| 'use strict' | ||
| const { settle, createError } = require('../axios-modules') | ||
| const statuses = require('../static/statuses') | ||
| const getUrl = require('../core/getUrl') | ||
| module.exports = function wxMiniprogramAdapter(config) { | ||
| const url = getUrl(config.baseURL, config.url, config.params) | ||
| return new Promise(function dispatchWxMiniprogramRequest(resolve, reject) { | ||
| let request = wx.request({ | ||
| url: url, | ||
| data: config.data, | ||
| header: config.headers, | ||
| timeout: typeof config.timeout === 'number' && config.timeout > 0 ? config.timeout : undefined, | ||
| method: config.method.toUpperCase(), | ||
| success(res) { | ||
| const response = { | ||
| data: res.data, | ||
| status: res.statusCode, | ||
| statusText: statuses[res.statusCode] || 'UnKnown', | ||
| headers: res.header, | ||
| config, | ||
| request, | ||
| } | ||
| settle(resolve, reject, response) | ||
| }, | ||
| fail() { | ||
| reject(createError('Network Error', config, null, request)) | ||
| request = null | ||
| }, | ||
| }) | ||
| }) | ||
| } |
| 'use strict' | ||
| /** | ||
| * 当前文件用于引入原生 `axios` 的方法,所有需要使用 `axios` 源代码的方法均从此文件引入 | ||
| * | ||
| * 说明: | ||
| * 1. 为保持代码简略,部分方法略有修改,但未改变原意。 | ||
| */ | ||
| function createError(message, config, code, request, response) { | ||
| const error = new Error(message) | ||
| error.config = config | ||
| error.request = request | ||
| error.response = response | ||
| error.isAxiosError = true | ||
| if (code) { | ||
| error.code = code | ||
| } | ||
| error.toJSON = function toJSON() { | ||
| return { | ||
| message: this.message, | ||
| config: this.config, | ||
| code: this.code, | ||
| } | ||
| } | ||
| } | ||
| function settle(resolve, reject, response) { | ||
| const validateStatus = response.config.validateStatus | ||
| if (!response.status || !validateStatus || validateStatus(response.status)) { | ||
| resolve(response) | ||
| } else { | ||
| reject(createError('Request failed with status code ' + response.status, response.config, null, response.request, response)) | ||
| } | ||
| } | ||
| module.exports = { createError, settle } |
| 'use strict' | ||
| function isWxMiniprogram() { | ||
| return typeof wx === 'object' && typeof wx.request === 'function' | ||
| } | ||
| /** | ||
| * 获取默认请求适配器 | ||
| * | ||
| * 说明: | ||
| * 1. 仅处理附加的适配器 | ||
| * 2. `xhr` 和 `nodejs` 的适配器不处理,由 `axios` 接管 | ||
| */ | ||
| module.exports = function getDefaultAdapter() { | ||
| let adapter | ||
| if (isWxMiniprogram()) { | ||
| adapter = require('../adapters/wx-miniprogram') | ||
| } | ||
| return adapter | ||
| } |
| 'use strict' | ||
| function getType(val) { | ||
| return Object.prototype.toString.call(val).slice(8, -1).toLowerCase() | ||
| } | ||
| /** | ||
| * 重写 `axios` 默认的 `paramsSerializer` 方法,增加以下逻辑: | ||
| * | ||
| * 1. 按键名字典排序 | ||
| */ | ||
| module.exports = function paramsSerializer(params) { | ||
| const parts = [] | ||
| const basicType = ['number', 'string', 'boolean'] | ||
| const keys = Object.keys(params) | ||
| keys.forEach((key) => { | ||
| const val = params[key] | ||
| const type = getType(val) | ||
| if (basicType.includes(type)) { | ||
| parts.push(`${key}=${String(val)}`) | ||
| } else if (type === 'array') { | ||
| val.forEach((item) => { | ||
| if (basicType.includes(getType(item))) { | ||
| parts.push(`${key + '[]'}=${String(item)}`) | ||
| } | ||
| }) | ||
| } else if (type === 'date') { | ||
| parts.push(`${key}=${val.toISOString()}`) | ||
| } else if (type === 'object') { | ||
| parts.push(`${key}=${JSON.stringify(val)}`) | ||
| } else { | ||
| // 其余均不处理 | ||
| } | ||
| }) | ||
| parts.sort() | ||
| return parts.join('&') | ||
| } |
| 'use strict' | ||
| const getDefaultAdapter = require('./core/default-adapter') | ||
| const defaults = { | ||
| adapter: getDefaultAdapter(), | ||
| } | ||
| module.exports = defaults |
| 'use strict' | ||
| const CryptoJS = require('crypto-js') | ||
| const getUrl = require('../core/getUrl') | ||
| /** 放在 `config.headers` 中但是非正常请求头字段的字段 */ | ||
| const axiosExtHeader = ['common', 'delete', 'get', 'head', 'patch', 'post', 'put'] | ||
| /** | ||
| * 规格化请求头 | ||
| * 1. 将字段名变为小写 | ||
| * 2. 去除重复的字段 | ||
| */ | ||
| function normalizeHeaders(headers) { | ||
| if (typeof headers !== 'object') { | ||
| return {} | ||
| } else { | ||
| return Object.keys(headers).reduce((result, field) => { | ||
| console.log('field: ', field) | ||
| const lowerField = field.toLowerCase() | ||
| if (!result[lowerField] && headers[field] && !axiosExtHeader.includes(field)) { | ||
| result[lowerField] = headers[field] | ||
| } | ||
| return result | ||
| }, {}) | ||
| } | ||
| } | ||
| /** | ||
| * 获取参与签名的请求头字段 | ||
| * @param {object} headers 请求头 | ||
| * @returns {string[]} | ||
| */ | ||
| function getSignHeaderKeys(headers) { | ||
| /** 不参与 Header 签名的请求头 */ | ||
| const EXCLUDE_SIGN_HEADERS = ['x-ca-signature', 'x-xa-signature-headers', 'accept', 'content-md5', 'content-type', 'date'] | ||
| const signHeaderKeys = [] | ||
| Object.keys(headers).forEach((field) => { | ||
| if (!EXCLUDE_SIGN_HEADERS.includes(field)) { | ||
| signHeaderKeys.push(field) | ||
| } | ||
| }) | ||
| return signHeaderKeys.sort() | ||
| } | ||
| function getSignedHeadersString(signHeaderKeys, headers) { | ||
| const list = [] | ||
| for (let i = 0; i < signHeaderKeys.length; i++) { | ||
| const key = signHeaderKeys[i] | ||
| const value = headers[key] | ||
| list.push(key + ':' + (value ? value : '')) | ||
| } | ||
| return list.join('\n') | ||
| } | ||
| function getPathAndParams(url) { | ||
| const urlRaw = url.replace('https://', '').replace('http://', '') | ||
| return urlRaw.substr(urlRaw.indexOf('/')) | ||
| } | ||
| function md5(content) { | ||
| return CryptoJS.MD5(content).toString(CryptoJS.enc.Base64) | ||
| } | ||
| function buildStringToSign(method, headers, signedHeadersString, pathAndParams) { | ||
| const lf = '\n' | ||
| const list = [method.toUpperCase(), lf] | ||
| const arr = ['accept', 'content-md5', 'content-type', 'date'] | ||
| for (let i = 0; i < arr.length; i++) { | ||
| const key = arr[i] | ||
| if (headers[key]) { | ||
| list.push(headers[key]) | ||
| } | ||
| list.push(lf) | ||
| } | ||
| if (signedHeadersString) { | ||
| list.push(signedHeadersString) | ||
| list.push(lf) | ||
| } | ||
| if (pathAndParams) { | ||
| list.push(pathAndParams) | ||
| } | ||
| return list.join('') | ||
| } | ||
| /** | ||
| * 阿里云 API 网关签名加密 | ||
| * @see https://help.aliyun.com/document_detail/29475.html | ||
| */ | ||
| module.exports = function aliyunApigwSignatureInterceptor(config) { | ||
| // 未配置签名参数,则直接跳过 | ||
| if (typeof config.signature !== 'object') { | ||
| return config | ||
| } | ||
| const { key, secret, debug } = config.signature | ||
| if (!(key && secret)) { | ||
| throw new Error('配置了 `signature` 参数但是未配置 `key` 或 `secret`') | ||
| } | ||
| const headers = normalizeHeaders(config.headers) | ||
| // 给请求头添加一些要求添加的字段 | ||
| headers['x-ca-key'] = key | ||
| headers['x-ca-timestamp'] = Date.now() | ||
| headers['accept'] = headers['accept'] || '*/*' | ||
| headers['content-type'] = headers['content-type'] || 'application/json' | ||
| // 该请求头要求为一个随机字符串,理论上使用 `UUID` 更好,为了少引入依赖,使用这种方法 | ||
| headers['x-ca-nonce'] = CryptoJS.MD5(Date.now().toString() + Math.random() * 10000).toString(CryptoJS.enc.Hex) | ||
| /** 允许包含 `body` 的请求方法 */ | ||
| const bodyValidMethod = ['post', 'put', 'patch'] | ||
| if (bodyValidMethod.includes(config.method.toLowerCase())) { | ||
| if (config.data) { | ||
| headers['content-md5'] = md5(JSON.stringify(config.data)) | ||
| } else { | ||
| console.warn(`当前请求方法为 \`${config.method}\` 但是未配置请求数据(\`data\`)`) | ||
| } | ||
| } else { | ||
| if (config.data) { | ||
| console.warn('如果要提交请求数据 `data`,请使用以下请求方法:`post`, `put`, `patch` ') | ||
| } | ||
| } | ||
| const signHeaderKeys = getSignHeaderKeys(headers) | ||
| headers['x-ca-signature-headers'] = signHeaderKeys.join(',') | ||
| const url = getUrl(config.baseURL, config.url, config.params) | ||
| const pathAndParams = getPathAndParams(url) | ||
| const signedHeadersString = getSignedHeadersString(signHeaderKeys, headers) | ||
| const stringToSign = buildStringToSign(config.method, headers, signedHeadersString, pathAndParams) | ||
| headers['x-ca-signature'] = CryptoJS.HmacSHA256(stringToSign, secret).toString(CryptoJS.enc.Base64) | ||
| if (debug) { | ||
| console.info(`当前签名字符串:\`${stringToSign.replace(/\n/g, '#')}\``) | ||
| } | ||
| config.headers = headers | ||
| return config | ||
| } |
| 'use strict' | ||
| const getDefaultAdapter = require('../core/default-adapter') | ||
| const mockAdapter = require('../adapters/mock') | ||
| module.exports = function coreInterceptor(config) { | ||
| /** | ||
| * 处理 `mock` 参数,优先级高于 `adapter` 参数 | ||
| * | ||
| * 说明: | ||
| * 1. 只要存在有效的 `mock` 参数,则挂载 `mockAdapter` 适配器 | ||
| */ | ||
| if (typeof config.mock === 'object' && Object.keys(config.mock).length > 0) { | ||
| config.adapter = mockAdapter | ||
| } | ||
| /** | ||
| * `adapter` 参数为空,则挂载对应的请求适配器 | ||
| */ | ||
| if (!config.adapter) { | ||
| config.adapter = getDefaultAdapter() | ||
| } | ||
| return config | ||
| } |
+4
-0
| # 版本更新说明 | ||
| ## v0.5.0 - 2021.06.18 | ||
| 1. 重构版本 | ||
| ## v0.4.1 - 2021.05.07 | ||
@@ -4,0 +8,0 @@ |
+2
-1
| 'use strict' | ||
| const jshttp = require('./lib/jshttp.js') | ||
| const jshttp = require('./lib/jshttp') | ||
| module.exports = jshttp | ||
@@ -5,0 +6,0 @@ |
+48
-13
| 'use strict' | ||
| const { settle, createError } = require('../axios-modules') | ||
| const statuses = require('../static/statuses') | ||
| /** | ||
| * 用于 Mock 数据,未发送真实请求 | ||
| * 用于客户端 Mock 请求数据,并未发送真实的 HTTP 请求。 | ||
| */ | ||
| /** 文档地址 */ | ||
| const docUrl = 'https://www.npmjs.com/package/jshttp#使用' | ||
| module.exports = function mockAdapter(config) { | ||
@@ -11,21 +17,50 @@ const { mock } = config | ||
| if (typeof mock !== 'object') { | ||
| throw new Error(`配置项 \`mock\` 要求是一个 \`object\` 类型的值,但是你设置的 \`mock\` = \`${mock}\` 为 \`${typeof mock}\` 类型`) | ||
| throw new Error(`配置项 \`mock\` 要求是一个 \`object\` 类型的值,详情请查看文档 ${docUrl}`) | ||
| } | ||
| const { status, statusText, headers, data, error } = mock | ||
| // 处理参数,所有参数均为选填 | ||
| const status = mock.status || 200 | ||
| const statusText = mock.statusText || statuses[status] || 'UnKnown' | ||
| const headers = mock.headers || {} | ||
| const data = mock.data | ||
| const delay = (mock.delay && parseInt(mock.delay, 10)) || 1 | ||
| const error = mock.error | ||
| return new Promise((resolve, reject) => { | ||
| /** 各个平台的 `request` 均不同,这个无法模拟,只能模拟一些基础属性 */ | ||
| let request = mock.request || { | ||
| method: config.method, | ||
| headers: config.headers, | ||
| data: config.data, | ||
| } | ||
| // 超时时间,用于模拟请求超时 | ||
| const timeout = config.timeout || 0 | ||
| return new Promise(function dispatchMockRequest(resolve, reject) { | ||
| /** 请求是否完成 */ | ||
| let isRequestFinished = false | ||
| // 模拟请求超时的 `ontimeout` 事件 | ||
| if (timeout > 0) { | ||
| setTimeout(() => { | ||
| if (!isRequestFinished) { | ||
| const timeoutErrorMessage = config.timeoutErrorMessage || `timeout of ${timeout} ms exceeded` | ||
| reject(createError(timeoutErrorMessage, config, 'ECONNABORTED', request)) | ||
| request = null | ||
| } | ||
| }, timeout) | ||
| } | ||
| // 模拟正常请求流程 | ||
| setTimeout(() => { | ||
| isRequestFinished = true | ||
| if (error) { | ||
| reject(new Error(error)) | ||
| // 模拟发生错误的 `onerror` 事件 | ||
| reject(createError('Network Error', config, null, request)) | ||
| request = null | ||
| } else { | ||
| resolve({ | ||
| status: status || 200, | ||
| statusText: statusText || '', | ||
| headers: headers || {}, | ||
| data: data, | ||
| config: config, | ||
| request: Object.create(null), | ||
| }) | ||
| // 请求成功情况 | ||
| const response = { data, status, statusText, headers, config, request } | ||
| settle(resolve, reject, response) | ||
| } | ||
@@ -32,0 +67,0 @@ }, delay) |
+3
-11
| 'use strict' | ||
| const paramsSerializer = require('./params-serializer') | ||
| function isHttpUrl(url) { | ||
@@ -45,13 +47,3 @@ if (url && typeof url === 'string' && (url.startsWith('http://') || url.startsWith('https://'))) { | ||
| function encodeQs(obj) { | ||
| const arr = [] | ||
| Object.keys(obj) | ||
| .sort() | ||
| .forEach((name) => { | ||
| const value = obj[name].toString() | ||
| if (value && typeof value === 'string' && value !== '[object Object]') { | ||
| // 兼容数组,name=['a','b'] 会转变为 name=a,b | ||
| arr.push(`${name}=${value}`) | ||
| } | ||
| }) | ||
| return arr.join('&') | ||
| return paramsSerializer(obj) | ||
| } | ||
@@ -58,0 +50,0 @@ |
+13
-20
| 'use strict' | ||
| const Application = require('./core/applocation.js') | ||
| const getDefaults = require('./config/defaults.js') | ||
| const axios = require('axios') | ||
| const defaults = require('./defaults') | ||
| const coreInterceptor = require('./interceptors/core-interceptor') | ||
| const aliyunApigwSignatureInterceptor = require('./interceptors/aliyun-apigw-signature') | ||
| function createInstance(defaultConfig) { | ||
| const instance = new Application(defaultConfig) | ||
| function create(defaultConfig) { | ||
| const instance = axios.create(defaultConfig) | ||
| instance.interceptors.request.use(aliyunApigwSignatureInterceptor) | ||
| instance.interceptors.request.use(coreInterceptor) | ||
| return instance | ||
| } | ||
| function request(...args) { | ||
| return instance.request(...args) | ||
| } | ||
| const jshttp = create(defaults) | ||
| jshttp.create = create | ||
| // 挂载常用方法 | ||
| const methods = ['get', 'post', 'delete', 'put', 'patch'] | ||
| methods.forEach((method) => { | ||
| request[method] = function (...args) { | ||
| return request(method.toUpperCase(), ...args) | ||
| } | ||
| }) | ||
| return request | ||
| } | ||
| module.exports = createInstance(getDefaults()) | ||
| module.exports.create = createInstance | ||
| module.exports = jshttp |
@@ -0,0 +0,0 @@ 'use strict' |
+0
-0
@@ -0,0 +0,0 @@ MIT License |
+7
-2
| { | ||
| "name": "jshttp", | ||
| "version": "0.4.2", | ||
| "description": "能在任何 Javascript 环境使用的 HTTP 请求库,支持 Node.js,浏览器,微信小程序,支付宝小程序,百度小程序 ...", | ||
| "version": "0.5.0", | ||
| "description": "基于 Axios 的 HTTP 请求库,内置了一些常用功能,支持在任何 Javascript 环境下运行", | ||
| "main": "index.js", | ||
@@ -14,2 +14,3 @@ "scripts": { | ||
| "keywords": [ | ||
| "axios", | ||
| "request", | ||
@@ -30,3 +31,7 @@ "http", | ||
| "mocha": "^8.4.0" | ||
| }, | ||
| "dependencies": { | ||
| "axios": "^0.21.1", | ||
| "crypto-js": "^4.0.0" | ||
| } | ||
| } |
+6
-167
@@ -16,3 +16,3 @@ # jshttp | ||
| `JsHttp` 是一个基于 `Promise` 的 `Javascript` HTTP 请求库,支持在任何 `Javascript` 环境中发起 HTTP 请求(目前已支持 `Node.js`,`浏览器`,各大平台小程序,包含`微信`、`QQ`、`支付宝`、`字节跳动`等平台),在各个环境提供完全一致 `API`。同时集成了强大的 **中间件** 功能,可以自由灵活地对请求和响应进行操作。 | ||
| `JsHttp` 是一个基于 `Axios` 的 `Javascript` HTTP 请求库,支持在任何 `Javascript` 环境中发起 HTTP 请求(目前已支持 `Node.js`,`浏览器`,各大平台小程序,包含`微信`、`QQ`、`支付宝`、`字节跳动`等平台),在各个环境提供完全一致 `API`。 | ||
@@ -31,11 +31,2 @@ ## 目录 | ||
| - [配置项](#配置项) | ||
| - [进阶](#进阶) | ||
| - [默认值](#默认值) | ||
| - [设置默认值的方式](#设置默认值的方式) | ||
| - [默认值合并方式](#默认值合并方式) | ||
| - [中间件](#中间件) | ||
| - [中间件简介](#中间件简介) | ||
| - [中间件实现](#中间件实现) | ||
| - [使用中间件](#使用中间件) | ||
| - [中间件顺序](#中间件顺序) | ||
| - [作者](#作者) | ||
@@ -51,2 +42,4 @@ - [参与](#参与) | ||
| 另外,常用的 HTTP 请求库 `Axios` 本身已经非常成熟了,因此笔者基于 `Axios` 进行开发,拓展了 `Axios` 原生的 `adapter` 和 `interceptor` 来完成对应功能。 | ||
| ### 特点 | ||
@@ -57,6 +50,5 @@ | ||
| 1. 支持所有的 `Javascript` 环境(也就是说,只要你在用 `Javascript` 写代码,你可以可以使用 `JsHttp` 来发起请求)。 | ||
| 2. 兼容 `Axios` 参数,可以从 `Axios` 无缝迁移到 `JsHttp`。 | ||
| 3. 强大的中间件功能,可以非常方便灵活地操作请求和响应(中间件逻辑参考了 `Koa`,特此说明)。 | ||
| 4. 内置了一些中间件,例如请求内容签名、格式转换等,进行简单配置后就可以直接使用。 | ||
| 5. 全中文错误提示,更利于开发和调试。 | ||
| 2. 完全兼容 `Axios` 参数,可以从 `Axios` 无缝迁移到 `JsHttp`。 | ||
| 3. 内置了一些中间件,例如请求内容签名、格式转换等,进行简单配置后就可以直接使用。 | ||
| 4. 全中文错误提示,更利于开发和调试。 | ||
@@ -319,155 +311,2 @@ ## 安装 | ||
| ## 进阶 | ||
| ### 默认值 | ||
| #### 设置默认值的方式 | ||
| 在实际开发中,你可能会希望自定义一些默认值,例如往往后端请求地址是固定的,不希望每次请求时都配置 `baseURL`,你希望能够将其设定为全局的默认值,`jshttp` 提供了两种方式来修改默认值。 | ||
| 比较简单的方式为直接修改 `jshttp.defaults[attribute]` 的值,例如: | ||
| ```javascript | ||
| jshttp.defaults.baseURL = 'https://api.inlym.com/ | ||
| ``` | ||
| 其他的配置项也可以用同样的方式去设置。 | ||
| 另一种更为复杂的情况是,在一个前端项目中,需要配置多套不同的默认值,这种情况建议使用 `jshttp.create(defaultConfig)` 方法去创建一个新的实例,例如: | ||
| ```javascript | ||
| const request1 = jshttp.create({ | ||
| baseURL: 'https://api1.inlym.com', | ||
| responseType: 'text', | ||
| }) | ||
| const request2 = jshttp.create({ | ||
| baseURL: 'https://api2.inlym.com', | ||
| responseType: 'text', | ||
| timeout: 2000, | ||
| }) | ||
| ``` | ||
| 对于通过以上方式创建的 `request1` 和 `request2`,你可以像使用 `jshttp` 一样去使用它们。 | ||
| #### 默认值合并方式 | ||
| 实际上,存在 3 组配置项: | ||
| 1. `jshttp` 内置的全局默认值 | ||
| 2. 实例的自定义默认值 | ||
| 3. 本次请求的配置项 | ||
| 3 组配置项的优先级由低到高,在发起请求前会将配置项合并,生成最终的请求配置项,但实际上,各个配置项的合并方式还有有一些不一样的,对于除 `headers` 和 `params` 以外的其他配置项,更高优先级直接覆盖低优先级中的配置项。 | ||
| 对于 `headers` 和 `params` 这 2 个配置项,合并方式略有不同,采用 **合并** 而不是 **覆盖**(对象内同名字段采用 **覆盖**),例如: | ||
| ```javascript | ||
| jshttp.defaults.headers = { | ||
| 'header-a': 'aaa', | ||
| 'header-b': 'bbb', | ||
| } | ||
| const config = { | ||
| headers: { | ||
| 'header-b': 'BBB', | ||
| 'header-c': 'ccc', | ||
| } | ||
| } | ||
| // 使用 `jshttp(config)` 发起请求时,实际合成的 `headers` 为 | ||
| { | ||
| 'header-a': 'aaa', | ||
| 'header-b': 'BBB', | ||
| 'header-c': 'ccc', | ||
| } | ||
| ``` | ||
| ### 中间件 | ||
| #### 中间件简介 | ||
| `jshttp` 的中间件是与 `Axios` 的中间件采用了完全不同的实现方式,在实现逻辑上借鉴了 `Koa` 的中间件实现方式,你可以将其称之为 **洋葱模型**。在 `ctx` 变量上挂载了以下对象: | ||
| 1. `config` - 配置项 | ||
| 2. `adapter` - 请求适配器 | ||
| 3. `response` - 响应(在适配器发送请求成功后创建) | ||
| 你可以通过操作以上挂载的变量来实现中间件功能,中间件函数既可以是一个标准的异步函数,也可以是同步函数(会被封装为异步函数)。 | ||
| #### 中间件实现 | ||
| 下面演示几个中间件实现。 | ||
| 示例 1:(模拟常见的请求签名逻辑)对 `headers` 签名,并使用 `x-ca-signature-headers` 字段记录被签名的字段,使用 `x-ca-signature` 记录签名内容: | ||
| ```javascript | ||
| // 备注:只是用来演示中间件写法,并不是严谨的签名实现方法 | ||
| function sign(ctx) { | ||
| // 从 `ctx` 中获取 `headers` | ||
| const requestHeaders = ctx.config.headers | ||
| const fields = Object.keys(requestHeaders) | ||
| const valueList = fields.map((field) => { | ||
| return requestHeaders[field] | ||
| }) | ||
| requestHeaders['x-ca-signature-headers'] = fields.join(',') | ||
| requestHeaders['x-ca-signature'] = md5(valueList.join('\n')) | ||
| } | ||
| ``` | ||
| 示例 2:微信小程序请求查询字符串加上 `code` 作为鉴权方式 | ||
| ```javascript | ||
| // 第一步:封装获取 `code` 的方法为 `Promise` | ||
| function getCode() { | ||
| return new Promise((resolve) => { | ||
| wx.login({ | ||
| success(res) { | ||
| resolve(res.code) | ||
| }, | ||
| }) | ||
| }) | ||
| } | ||
| // 第二步:在中间件中使用,需要用到 `await` | ||
| async function appendCode(ctx, next) { | ||
| const code = await getCode() | ||
| ctx.config.params.code = code | ||
| await next() | ||
| } | ||
| ``` | ||
| #### 使用中间件 | ||
| 中间件的配置和其他配置项一样,既可以定义为默认配置项,也可以只放在本次请求中使用。 | ||
| 使用方式 1:仅在本次请求中使用中间件,在配置项 `middleware` 中设置就行了 | ||
| ```javascript | ||
| jshttp({ | ||
| // ... 其他配置项 | ||
| middleware: [appendCode, sign], | ||
| }) | ||
| ``` | ||
| 使用方式 2:添加到默认配置项 | ||
| `middleware` 配置项定义为默认配置项的方式和其他参数完全一致,除此之外,还可以使用 `jshttp.use(fn)` 方法添加一个新的中间件到中间件列表中(本质上只是对列表进行了一个 `push` 操作) | ||
| #### 中间件顺序 | ||
| 少数中间件对于执行顺序是有要求的,例如 **签名中间件** 就应该放在所有的中间件之后,在发送请求之前,`jshttp` 内置提供了定义中间件执行顺序的方法,在以上说明的任何放置中间件的地方,可以替换为以下对象,定义中间件顺序: | ||
| ```javascript | ||
| // 以上述的 `sign` 中间件为例 | ||
| { | ||
| fn: sign, | ||
| order: 9999, | ||
| } | ||
| ``` | ||
| 使用普通方式添加中间件或者未定义 `order` 参数的中间件,将自动将 `order` 设置为 `100`,最终会将所有中间件按 `order` 升序执行。 | ||
| ## 作者 | ||
@@ -474,0 +313,0 @@ |
| 'use strict' | ||
| /** | ||
| * 生成一个小程序适配器 | ||
| * @param {object} options 构造适配器的配置项 | ||
| * | ||
| * 说明: | ||
| * 1. 由于平台的小程序的 `request` 方法大同小异,因此没必要各个单独写,只需要兼容一下参数就可以了 | ||
| * 2. 适配参数,然后输出一个函数 | ||
| * | ||
| * 小程序平台: | ||
| * 1. 微信小程序 | ||
| * @see https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html | ||
| * | ||
| * 2. QQ 小程序 | ||
| * @see https://q.qq.com/wiki/develop/miniprogram/API/network/network_request.html | ||
| * | ||
| * 3. 支付宝小程序 | ||
| * @see https://opendocs.alipay.com/mini/api/owycmh | ||
| * | ||
| * 4. 字节跳动小程序 | ||
| * @see https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/api/network/http/tt-request | ||
| */ | ||
| const getUrl = require('../core/getUrl.js') | ||
| /** | ||
| * 将配置项中的 `responseType` 参数转换为小程序请求所需的 `dataType` 和 `dataType` 参数 | ||
| * @param {string} responseType 响应数据类型 | ||
| */ | ||
| function convertResponseType(responseType) { | ||
| if (typeof my === 'object') { | ||
| if (responseType === undefined || responseType === 'json') { | ||
| return { dataType: 'JSON' } | ||
| } else { | ||
| return { dataType: responseType } | ||
| } | ||
| } else { | ||
| if (responseType === undefined || responseType === 'json') { | ||
| return { | ||
| dataType: 'json', | ||
| responseType: 'text', | ||
| } | ||
| } else if (['arraybuffer', 'blob', 'stream'].includes(responseType)) { | ||
| return { | ||
| dataType: 'arraybuffer', | ||
| responseType: 'arraybuffer', | ||
| } | ||
| } else { | ||
| return { | ||
| dataType: 'string', | ||
| responseType: 'text', | ||
| } | ||
| } | ||
| } | ||
| } | ||
| module.exports = function createMiniAdapter(options) { | ||
| /** 请求头字段名称,默认 `headers` */ | ||
| const requestHeaders = (options.alias && options.alias.requestHeaders) || 'headers' | ||
| /** 响应头字段名称,默认 `headers` */ | ||
| const responseHeaders = (options.alias && options.alias.responseHeaders) || 'headers' | ||
| /** 响应码字段名称,默认 `statusCode` */ | ||
| const statusCode = (options.alias && options.alias.statusCode) || 'statusCode' | ||
| return function miniAdapter(config) { | ||
| const requestOptions = { | ||
| method: config.method, | ||
| url: getUrl(config.baseURL, config.url, config.params), | ||
| timeout: config.timeout, | ||
| [requestHeaders]: config.headers, | ||
| } | ||
| /** 不应该有请求体的请求方法 */ | ||
| const NO_BODY_METHODS = ['GET', 'HEAD', 'OPTIONS'] | ||
| if (!NO_BODY_METHODS.includes(config.method) && config !== undefined) { | ||
| requestOptions.data = config.data | ||
| } | ||
| /** | ||
| * 备注: | ||
| * 笔者认为,当请求方法为 `GET` 时,将 `data` 插入到 `quertstirng` 中,这种做法很怪异!非常容易造成难以预料的问题! | ||
| * 因此在 `jshttp` 中,直接屏蔽了这种做法。 | ||
| */ | ||
| /** 附加的参数 */ | ||
| const extraConfigKeys = ['enableHttp2', 'enableQuic', 'enableCache'] | ||
| extraConfigKeys.forEach((key) => { | ||
| if (config[key]) { | ||
| requestOptions[key] = config[key] | ||
| } | ||
| }) | ||
| return new Promise((resolve, reject) => { | ||
| const request = options.platform.request( | ||
| Object.assign({}, convertResponseType(config.responseType), requestOptions, { | ||
| success(res) { | ||
| resolve({ | ||
| status: res[statusCode], | ||
| headers: res[responseHeaders], | ||
| data: res.data, | ||
| config: config, | ||
| request: request, | ||
| }) | ||
| }, | ||
| fail(res) { | ||
| if (!res) { | ||
| reject(new Error('请求错误,发起请求失败!')) | ||
| } else { | ||
| if (typeof res === 'object' && typeof res.errMsg === 'string') { | ||
| reject(new Error('请求失败,错误原因:' + res.errMsg)) | ||
| } | ||
| } | ||
| }, | ||
| }) | ||
| ) | ||
| }) | ||
| } | ||
| } |
| 'use strict' | ||
| /** | ||
| * 支付宝小程序 | ||
| * @see https://opendocs.alipay.com/mini/api/owycmh | ||
| */ | ||
| const createMiniAdapter = require('./mini-base.js') | ||
| const options = { | ||
| /** 所在平台简称 */ | ||
| name: 'my', | ||
| /** 对应的全局变量 */ | ||
| platform: my, | ||
| /** 支持的请求方法 */ | ||
| methods: ['GET', 'POST', 'PUT', 'DELETE'], | ||
| /** 参数别名 */ | ||
| alias: { | ||
| requestHeaders: 'headers', | ||
| responseHeaders: 'headers', | ||
| statusCode: 'status', | ||
| }, | ||
| } | ||
| module.exports = createMiniAdapter(options) |
| 'use strict' | ||
| /** | ||
| * QQ 小程序 | ||
| * @see https://q.qq.com/wiki/develop/miniprogram/API/network/network_request.html | ||
| */ | ||
| const createMiniAdapter = require('./mini-base.js') | ||
| const options = { | ||
| /** 所在平台简称 */ | ||
| name: 'qq', | ||
| /** 对应的全局变量 */ | ||
| platform: qq, | ||
| /** 支持的请求方法 */ | ||
| methods: ['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'], | ||
| /** 参数别名 */ | ||
| alias: { | ||
| requestHeaders: 'header', | ||
| responseHeaders: 'header', | ||
| statusCode: 'statusCode', | ||
| }, | ||
| } | ||
| module.exports = createMiniAdapter(options) |
| 'use strict' | ||
| /** | ||
| * 字节跳动小程序 | ||
| * @see https://microapp.bytedance.com/docs/zh-CN/mini-app/develop/api/network/http/tt-request | ||
| */ | ||
| const createMiniAdapter = require('./mini-base.js') | ||
| const options = { | ||
| /** 所在平台简称 */ | ||
| name: 'tt', | ||
| /** 对应的全局变量 */ | ||
| platform: tt, | ||
| /** 支持的请求方法 | ||
| * | ||
| * | ||
| * GET/POST/OPTIONS/PUT/HEAD/DELETE | ||
| */ | ||
| methods: ['GET', 'HEAD', 'POST', 'OPTIONS', 'PUT', 'DELETE'], | ||
| /** 参数别名 */ | ||
| alias: { | ||
| requestHeaders: 'header', | ||
| responseHeaders: 'header', | ||
| statusCode: 'statusCode', | ||
| }, | ||
| } | ||
| module.exports = createMiniAdapter(options) |
| 'use strict' | ||
| const createMiniAdapter = require('./mini-base.js') | ||
| const options = { | ||
| /** 所在平台简称 */ | ||
| name: 'wx', | ||
| /** 对应的全局变量 */ | ||
| platform: wx, | ||
| /** 支持的请求方法 */ | ||
| methods: ['OPTIONS', 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'], | ||
| /** 参数别名 */ | ||
| alias: { | ||
| requestHeaders: 'header', | ||
| responseHeaders: 'header', | ||
| statusCode: 'statusCode', | ||
| }, | ||
| } | ||
| module.exports = createMiniAdapter(options) |
| 'use strict' | ||
| /** | ||
| * Node.js HTTP | ||
| * @see https://nodejs.org/dist/latest-v14.x/docs/api/http.html#http_http_request_options_callback | ||
| */ | ||
| const http = require('http') | ||
| const https = require('https') | ||
| const getUrl = require('../core/getUrl.js') | ||
| const packageInfo = require('../../package.json') | ||
| /** | ||
| * Node.js 发起请求适配器 | ||
| * @param {object} config 配置项 | ||
| * | ||
| * 说明: | ||
| * 1. 本期版本统一不支持 proxy | ||
| * 2. 后续版本支持自定义 agent | ||
| */ | ||
| module.exports = function nodeAdapter(config) { | ||
| const url = getUrl(config.baseURL, config.url, config.params) | ||
| // 根据 `http` 或 `https` 协议选用对应的模块 | ||
| let httpModule = null | ||
| if (url.startsWith('http://')) { | ||
| httpModule = http | ||
| } else if (url.startsWith('https://')) { | ||
| httpModule = https | ||
| } else { | ||
| throw new Error('请求地址请以 `http://` 或 `https://` 开头!') | ||
| } | ||
| const { headers, method } = config | ||
| // 处理 `data` | ||
| let requestData = null | ||
| let contentType = '' | ||
| if (typeof config.data === 'object') { | ||
| requestData = JSON.stringify(config.data) | ||
| contentType = 'application/json' | ||
| } else if (typeof config.data === 'string') { | ||
| requestData = config.data | ||
| contentType = 'text/plain' | ||
| } | ||
| const options = { | ||
| method: method, | ||
| headers: headers, | ||
| } | ||
| if (config.timeout && config.timeout > 0) { | ||
| options.timeout = config.timeout | ||
| } | ||
| return new Promise((resolve, reject) => { | ||
| const request = httpModule.request(url, options, function handleResponse(response) { | ||
| const responseBufferList = [] | ||
| response.on('data', function handleResponseData(buf) { | ||
| responseBufferList.push(buf) | ||
| }) | ||
| response.on('end', function handleResponseEnd() { | ||
| let responseData = Buffer.concat(responseBufferList) | ||
| if (config.responseType !== 'arraybuffer') { | ||
| responseData = responseData.toString(config.responseEncoding) | ||
| } | ||
| resolve({ | ||
| status: response.statusCode, | ||
| statusText: response.statusMessage, | ||
| headers: response.headers, | ||
| data: responseData, | ||
| config: config, | ||
| request: request, | ||
| }) | ||
| }) | ||
| }) | ||
| // 监听 `error` 事件 | ||
| request.on('error', function handleRequestError(err) { | ||
| reject(new Error(`请求时发生错误,触发 \`error\` 事件,以下是错误原因:${err}`)) | ||
| }) | ||
| request.on('timeout', function handleRequestTimeout() { | ||
| /** | ||
| * 请求超时时,需要手动中止请求 | ||
| * @see https://nodejs.org/dist/latest-v14.x/docs/api/http.html#http_event_timeout | ||
| * | ||
| * `request.abort()` 方法已启用,改用 `request.destroy()` | ||
| * @see https://nodejs.org/dist/latest-v14.x/docs/api/http.html#http_request_abort | ||
| */ | ||
| request.destroy() | ||
| reject(new Error(`请求已超时(你设置的超时时间为 \`timeout: ${config.timeout}\`,单位:ms)`)) | ||
| }) | ||
| // 附加一些请求头 | ||
| if (!request.getHeader('User-Agent')) { | ||
| request.setHeader('User-Agent', `JsHttp/${packageInfo.version}`) | ||
| } | ||
| if (!request.getHeader('Content-Type') && contentType) { | ||
| request.setHeader('Content-Type', contentType) | ||
| } | ||
| request.end(requestData) | ||
| }) | ||
| } |
| 'use strict' | ||
| const getUrl = require('../core/getUrl.js') | ||
| /** | ||
| * XMLHttpRequest | ||
| * @see https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest | ||
| */ | ||
| /** | ||
| * 获取并解析响应头 | ||
| * @param {XMLHttpRequest} xhr | ||
| * @returns {object} 解析成对象的响应头 | ||
| * | ||
| * 调用 `XMLHttpRequest.getAllResponseHeaders()` 获取的响应头,是以 `CRLF` 分割的字符串,需要手工解析 | ||
| * @see https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/getAllResponseHeaders | ||
| */ | ||
| function getHeaders(xhr) { | ||
| /** | ||
| * 关于 `CRLF` | ||
| * @see https://developer.mozilla.org/zh-CN/docs/Glossary/CRLF | ||
| */ | ||
| const CRLF = '\r\n' | ||
| // 裸响应头,以 `CRLF` 分割的字符串 | ||
| const rawHeaders = xhr.getAllResponseHeaders() | ||
| // 以分隔符拆分响应头字符串 | ||
| const headerList = rawHeaders.split(CRLF) | ||
| // 获取响应头字段名列表 | ||
| const fieldList = headerList.map((v) => { | ||
| return v.split(':')[0] | ||
| }) | ||
| const headers = {} | ||
| fieldList.forEach((field) => { | ||
| if (field) { | ||
| headers[field] = xhr.getResponseHeader(field) | ||
| } | ||
| }) | ||
| return headers | ||
| } | ||
| /** | ||
| * 检查指定请求头是否被禁用修改(仅提示) | ||
| * @param {string} field 请求头字段(请求首部) | ||
| * | ||
| * 部分请求头不能在代码中通过编程的方式进行修改,应该由由浏览器控制 | ||
| * @see https://developer.mozilla.org/zh-CN/docs/Glossary/Forbidden_header_name | ||
| */ | ||
| function warnIfHeaderNameForbidden(field) { | ||
| const forbiddenHeaderNames = [ | ||
| 'Accept-Charset', | ||
| 'Accept-Encoding', | ||
| 'Access-Control-Request-Headers', | ||
| 'Access-Control-Request-Method', | ||
| 'Connection', | ||
| 'Content-Length', | ||
| 'Cookie', | ||
| 'Cookie2', | ||
| 'Date', | ||
| 'DNT', | ||
| 'Expect', | ||
| 'Host', | ||
| 'Keep-Alive', | ||
| 'Origin', | ||
| 'Referer', | ||
| 'TE', | ||
| 'Trailer', | ||
| 'Transfer-Encoding', | ||
| 'Upgrade', | ||
| 'Via', | ||
| ] | ||
| const forbiddenHeaderNamePrefix = ['Proxy-', 'Sec-'] | ||
| forbiddenHeaderNames.forEach((key) => { | ||
| if (key.toUpperCase() === field.toUpperCase()) { | ||
| const message = `请求头 \`${field}\` 不允许自主设置,应当由浏览器自动设置,建议删除该请求头!详见文档 https://developer.mozilla.org/zh-CN/docs/Glossary/Forbidden_header_name` | ||
| console.error(message) | ||
| } | ||
| }) | ||
| forbiddenHeaderNamePrefix.forEach((pre) => { | ||
| if (field.toUpperCase().startsWith(pre.toUpperCase())) { | ||
| const message = `以 \`${pre}\` 开头的请求头不允许自主设置,应当由浏览器自动设置,建议删除 \`${field}\` 请求头!详见文档 https://developer.mozilla.org/zh-CN/docs/Glossary/Forbidden_header_name` | ||
| console.error(message) | ||
| } | ||
| }) | ||
| } | ||
| /** | ||
| * 检查响应数据类型(即 `responseType` 值)是否合规 | ||
| * @param {string} responseType 响应数据的类型 | ||
| * | ||
| * @see https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/responseType | ||
| */ | ||
| function throwErrorIfInvalidResponseType(responseType) { | ||
| const valid = ['', 'arraybuffer', 'blob', 'document', 'json', 'text'] | ||
| if (!valid.includes(responseType)) { | ||
| throw new Error(`你设置的 \`responseType\` 为 \`${responseType}\` 不是一个有效的值!`) | ||
| } | ||
| } | ||
| module.exports = function xhrAdapter(config) { | ||
| const url = getUrl(config.baseURL, config.url, config.params) | ||
| return new Promise((resolve, reject) => { | ||
| const xhr = new XMLHttpRequest() | ||
| xhr.open(config.method, url, true) | ||
| // 设置超时时间 | ||
| if (config.timeout > 0) { | ||
| xhr.timeout = config.timeout | ||
| } | ||
| // 设置响应值类型 | ||
| throwErrorIfInvalidResponseType(config.responseType) | ||
| xhr.responseType = config.responseType | ||
| // 设置 `withCredentials` | ||
| if (config.withCredentials) { | ||
| xhr.withCredentials = true | ||
| } | ||
| // 监听发生请求错误 | ||
| xhr.onerror = function handleRequestError() { | ||
| reject(new Error('请求时发生错误,触发 `error` 事件')) | ||
| } | ||
| /** | ||
| * 监听请求完成,`onload` 相比 `onreadystatechange` 更智能一些 | ||
| * @see https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequestEventTarget/onload | ||
| */ | ||
| xhr.onload = function handleRequestLoad() { | ||
| const responseHeaders = getHeaders(xhr) | ||
| resolve({ | ||
| status: xhr.status, | ||
| statusText: xhr.statusText, | ||
| headers: responseHeaders, | ||
| data: xhr.response, | ||
| config: config, | ||
| request: xhr, | ||
| }) | ||
| } | ||
| // 监听请求超时 | ||
| xhr.ontimeout = function handleRequestTimeout() { | ||
| reject(new Error(`请求已超时(你设置的超时时间为 \`timeout: ${config.timeout}\`,单位:ms)`)) | ||
| } | ||
| // 处理请求数据 | ||
| let requestData = config.data || null | ||
| /** | ||
| * 如果请求方法是 GET 或者 HEAD,则应将请求主体设置为 null | ||
| * @see https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/send | ||
| */ | ||
| if (['GET', 'HEAD'].includes(config.method)) { | ||
| requestData = null | ||
| } | ||
| if (requestData && typeof requestData === 'object') { | ||
| requestData = JSON.stringify(requestData) | ||
| } | ||
| // 最终处理请求头,逐个设置 | ||
| Object.keys(config.headers).forEach((field) => { | ||
| warnIfHeaderNameForbidden(field) | ||
| xhr.setRequestHeader(field, config.headers[field]) | ||
| }) | ||
| xhr.send(requestData) | ||
| }) | ||
| } |
| 'use strict' | ||
| const methods = require('../static/methods.js') | ||
| /** | ||
| * 支持不定长度的参数形式,合成一个参数对象 | ||
| * | ||
| * 格式演示: | ||
| * 1. `('get', '/path.to?name=mark', { baseURL: 'https://www.jshttp.com' })` | ||
| * 2. `('/path.to?name=mark')` | ||
| * 3. `({ baseURL: 'https://www.jshttp.com', method: 'post'})` | ||
| * 4. `({ baseURL: 'https://www.jshttp.com' }, 'get', '/path.to?name=mark',)` | ||
| */ | ||
| module.exports = function buildConfig(...args) { | ||
| if (args.length === 0) { | ||
| throw new Error('请求配置项 `config` 不能为空') | ||
| } | ||
| // 首先生成一个对象列表 | ||
| const configList = args.map((value) => { | ||
| if (typeof value === 'object' && value !== null) { | ||
| return value | ||
| } else if (typeof value === 'string') { | ||
| if (methods.includes(value.toUpperCase())) { | ||
| return { method: value.toUpperCase() } | ||
| } else { | ||
| return { url: value } | ||
| } | ||
| } | ||
| }) | ||
| const config = {} | ||
| Object.assign(config, ...configList) | ||
| return config | ||
| } |
| 'use strict' | ||
| const getAdapter = require('../core/getAdapter.js') | ||
| const defaultAdapter = getAdapter() | ||
| /** | ||
| * 当前文件放置 [ 默认配置 ] | ||
| * | ||
| * 用途说明: | ||
| * 1. 生成实例时,作为默认配置 | ||
| * 2. 开发者可以从当前文件查看支持的参数,以及含义 | ||
| * 3. 未避免全局污染,封装为函数 | ||
| */ | ||
| module.exports = function getDefaults() { | ||
| return { | ||
| /*********************************** 基本类型值配置 **********************************/ | ||
| /** | ||
| * 请求方法 | ||
| * | ||
| * 常用值: | ||
| * 1. `GET` | ||
| * 2. `POST` | ||
| * 3. `PUT` | ||
| * 4. `DELETE` | ||
| * 5. `HEAD` | ||
| */ | ||
| method: 'GET', | ||
| /** | ||
| * 请求基础地址 | ||
| * | ||
| * 说明: | ||
| * 1. 不为空时要求以 `http://` 或 `https://` 开头 | ||
| * 2. 配置项 `url` 以 `http://` 或 `https://` 开头时,配置项 `baseURL` 无效 | ||
| * 3. 配置项 `url` 为普通路径时(即以非 `http://` 或 `https://` 开头),将两者连接合成请求地址 | ||
| */ | ||
| baseURL: undefined, | ||
| /** | ||
| * 请求地址 | ||
| * | ||
| * 说明: | ||
| * 1. 可单独存在,或者和 `baseURL` 拼接合成请求地址 | ||
| * 2. 和 `baseURL` 不能同时为空 | ||
| */ | ||
| url: undefined, | ||
| /** | ||
| * 请求的数据类型 | ||
| * | ||
| * 合法值: | ||
| * 1. `json` | ||
| * 2. `text` | ||
| * 3. `form` | ||
| * 4. `arraybuffer` | ||
| */ | ||
| requestType: 'json', | ||
| /** | ||
| * 响应的数据类型 | ||
| * | ||
| * 合法值: | ||
| * 1. `json` - 全部适用 | ||
| * 2. `arraybuffer` - 全部适用 | ||
| * 3. `text` - 全部适用 | ||
| * 4. `blob` - 仅用于 `XMLHttpRequest` | ||
| * 5. `document` - 仅用于 `XMLHttpRequest` | ||
| * 6. `stream` - 仅用于 `Node.js` | ||
| */ | ||
| responseType: 'json', | ||
| /** | ||
| * 响应的字符编码 | ||
| * | ||
| * 合法值: | ||
| * 'utf8', 'utf-8', 'utf16le', 'latin1', 'base64', 'hex', 'ascii', 'binary', 'ucs2' | ||
| */ | ||
| responseEncoding: 'utf8', | ||
| /** | ||
| * 请求超时时间 | ||
| * | ||
| * 说明: | ||
| * 1. `0` 表示无超时时间 | ||
| * 2. 单位:毫秒(ms) | ||
| */ | ||
| timeout: 0, | ||
| /** | ||
| * 重试次数 | ||
| * | ||
| * 说明: | ||
| * 1. `0` 表示不重试 | ||
| */ | ||
| retry: 0, | ||
| /** | ||
| * 是否开启调试模式 | ||
| * | ||
| * 开启后,包含以下功能: | ||
| * 1. 打印请求日志 | ||
| */ | ||
| debug: false, | ||
| /** | ||
| * 用于打印日志的函数 | ||
| * @param {object} content 日志内容对象 | ||
| * @param {number} content.status 响应状态码 | ||
| * @param {string} content.url 请求地址 | ||
| * @param {object} content.config 配置项 | ||
| * @param {number} content.startTime 发起请求任务时间(时间戳) | ||
| * @param {number} content.endTime 结束请求任务时间(时间戳) | ||
| * @param {number} content.requestTime 本次发起请求时间(时间戳) | ||
| * @param {number} content.responseTime 本次结束响应时间(时间戳) | ||
| * @param {string} content.startTimeText 发起请求任务时间(文本格式) | ||
| * @param {string} content.endTimeText 结束请求任务时间(文本格式) | ||
| * @param {string} content.requestTimeText 本次发起请求时间(文本格式) | ||
| * @param {string} content.responseTimeText 本次结束响应时间(文本格式) | ||
| */ | ||
| logging: function logging(content) { | ||
| const message = `${content.startTimeText} ${content.responseTime - content.requestTime}ms ${content.config.method} ${content.url} ${content.status}` | ||
| console.log(message) | ||
| }, | ||
| /*********************************** 对象类配置 **********************************/ | ||
| /** | ||
| * 请求头 | ||
| */ | ||
| headers: {}, | ||
| /** | ||
| * 请求参数,最终以 `name=mark&age=19` 的形式合并到 `url` 中 | ||
| */ | ||
| params: {}, | ||
| /** | ||
| * mock 响应对象 | ||
| */ | ||
| mock: undefined, | ||
| /*********************************** 函数类配置 **********************************/ | ||
| /** | ||
| * 请求适配器,将最终使用该适配器发送请求 | ||
| */ | ||
| adapter: defaultAdapter, | ||
| /** | ||
| * 是否是正常的响应状态码,函数返回为 `false` 将直接报错 | ||
| * @param {number} status 响应状态码 | ||
| * @returns {boolean} | ||
| */ | ||
| validateStatus: function validateStatus(status) { | ||
| return typeof status === 'number' && status >= 200 && status < 300 | ||
| }, | ||
| /*********************************** 其他配置 **********************************/ | ||
| /** | ||
| * 请求数据 | ||
| */ | ||
| data: undefined, | ||
| /** | ||
| * 用户自定义中间件列表 | ||
| */ | ||
| middleware: [], | ||
| /** | ||
| * 响应的返回内容项 | ||
| * | ||
| * 合法值: | ||
| * 1. `status` | ||
| * 2. `statusText` | ||
| * 3. `headers` | ||
| * 4. `data` | ||
| * 5. `config` | ||
| * 6. `request` | ||
| * | ||
| * 支持别名,格式: | ||
| * ```js | ||
| * { | ||
| * item: 'status, | ||
| * alias: 'statusCode' | ||
| * } | ||
| * ``` | ||
| */ | ||
| responseItems: ['status', 'statusText', 'headers', 'data'], | ||
| } | ||
| } |
| 'use strict' | ||
| const base = require('./defaults.js') | ||
| /** | ||
| * 合并配置项 | ||
| * @param {object} base 基础配置项,包含所有可配置键名 | ||
| * @param {object} defaults 实例的默认配置项 | ||
| * @param {object} comtom 本次请求设置的配置项 | ||
| * | ||
| * 说明: | ||
| * 1. 优先级:`comtom` > `defaults` > `base` | ||
| */ | ||
| module.exports = function mergeConfig(base, defaults, custom) { | ||
| /** | ||
| * 第一类键名 | ||
| * | ||
| * 说明: | ||
| * 1. 更高优先级存在值,则直接覆盖低优先级配置 | ||
| */ | ||
| const firstTypeKeys = [ | ||
| 'method', | ||
| 'baseURL', | ||
| 'url', | ||
| 'requestType', | ||
| 'responseType', | ||
| 'responseEncoding', | ||
| 'timeout', | ||
| 'retry', | ||
| 'mock', | ||
| 'adapter', | ||
| 'data', | ||
| 'middleware', | ||
| 'responseItems', | ||
| 'validateStatus', | ||
| 'sign', | ||
| 'debug', | ||
| 'logging', | ||
| ] | ||
| /** | ||
| * 第二类键名 | ||
| * | ||
| * 说明: | ||
| * 1. 更高优先级存在对应值,不会覆盖低优先级,而是做合并处理 | ||
| * 2. 对应值类型应为 `object` | ||
| * 3. 对象内同名键,更高优先级覆盖低优先级 | ||
| */ | ||
| const secondTypeKeys = ['headers', 'params'] | ||
| firstTypeKeys.forEach((key) => { | ||
| custom[key] = custom[key] || defaults[key] || base[key] | ||
| }) | ||
| secondTypeKeys.forEach((key) => { | ||
| custom[key] = Object.assign({}, base[key], defaults[key] || {}, custom[key] || {}) | ||
| }) | ||
| return custom | ||
| } |
| 'use strict' | ||
| const methods = require('../static/methods.js') | ||
| /** | ||
| * 检验一个值是否是指定的类型,如果不是则报错 | ||
| * @param {*} value 要检验的数据 | ||
| * @param {*} type 类型 | ||
| * @param {*} name 名称 | ||
| */ | ||
| function throwErrorIfWrongType(name, value, type) { | ||
| if (typeof value !== type) { | ||
| const message = `配置项 \`${name}\` 要求是一个 \`${type}\` 类型的值,但是你设置的 \`${name}\` = \`${value}\` 为 \`${typeof value}\` 类型` | ||
| throw new Error(message) | ||
| } | ||
| } | ||
| /** | ||
| * 指定的值如果没有在列表中,则报错 | ||
| * @param {*} value 要检验的数据 | ||
| * @param {*} list 列表 | ||
| * @param {*} name 名称 | ||
| */ | ||
| function throwErrorIfNotInclude(name, value, list) { | ||
| if (!list.includes(value)) { | ||
| const message = | ||
| `配置项 \`${name}\` 仅支持以下值,你设置的 \`${name}\` = \`${value}\` 不在此范围内:\n` + | ||
| list | ||
| .map((value, index) => { | ||
| return `${index + 1}. \`${value}\`\n` | ||
| }) | ||
| .join('') | ||
| throw new Error(message) | ||
| } | ||
| } | ||
| /** | ||
| * 整理参数(对原对象对校验和调整) | ||
| * @param {object} config 配置项 | ||
| * | ||
| * 说明: | ||
| * 1. 将已知的参数做一个 `数据校验` 和规范化,未知数据原封不动保留 | ||
| */ | ||
| module.exports = function normalizeConfig(config) { | ||
| throwErrorIfWrongType('config', config, 'object') | ||
| /** | ||
| * 配置项 `method` | ||
| * | ||
| * 规则: | ||
| * 1. 大写字符串 | ||
| * 2. 在指定范围内 | ||
| */ | ||
| if (config.method !== undefined) { | ||
| throwErrorIfWrongType('method', config.method, 'string') | ||
| config.method = config.method.toUpperCase() | ||
| throwErrorIfNotInclude('method', config.method, methods) | ||
| } | ||
| /** | ||
| * 配置项 `baseURL` | ||
| * | ||
| * 规则: | ||
| * 1. 字符串 | ||
| * 2. 以 `http://` 或 `https://` 开头 | ||
| */ | ||
| if (config.baseURL !== undefined) { | ||
| throwErrorIfWrongType('baseURL', config.baseURL, 'string') | ||
| if (!config.baseURL.startsWith('http://') && !config.baseURL.startsWith('https://')) { | ||
| throw new Error('配置项 `baseURL` 应该以 `http://` 或 `https://` 开头,例如 `https://www.jshttp.cn/`') | ||
| } | ||
| } | ||
| /** | ||
| * 配置项 `url` | ||
| * | ||
| * 规则: | ||
| * 1. 字符串 | ||
| */ | ||
| if (config.url !== undefined) { | ||
| if (typeof config.url !== 'string') { | ||
| throw new Error('配置项 `url` 应该是一个字符串类型,可以是绝对路径或相对路径,例如 `https://www.jshttp.cn/` 或 `/api`') | ||
| } | ||
| } | ||
| /** | ||
| * 配置项 `requestType` | ||
| * | ||
| * 规则: | ||
| * 1. 字符串 | ||
| * | ||
| * 合法值: | ||
| * 1. `json` - 默认 | ||
| * 2. `text` | ||
| * 3. `form` | ||
| * 4. `arraybuffer` | ||
| */ | ||
| if (config.requestType !== undefined) { | ||
| throwErrorIfWrongType('requestType', config.requestType, 'string') | ||
| const validList = ['json', 'text', 'form', 'arraybuffer'] | ||
| throwErrorIfNotInclude('requestType', config.requestType, validList) | ||
| } | ||
| /** | ||
| * 配置项 `responseType` | ||
| * | ||
| * 规则: | ||
| * 1. 字符串 | ||
| * | ||
| * 合法值: | ||
| * 1. `json` - 全部适用 | ||
| * 2. `arraybuffer` - 全部适用 | ||
| * 3. `text` - 全部适用 | ||
| * 4. `blob` - 仅用于 `XMLHttpRequest` | ||
| * 5. `document` - 仅用于 `XMLHttpRequest` | ||
| * 6. `stream` - 仅用于 `Node.js` | ||
| */ | ||
| if (config.responseType !== undefined) { | ||
| throwErrorIfWrongType('responseType', config.responseType, 'string') | ||
| const validList = ['arraybuffer', 'blob', 'document', 'json', 'text', 'stream'] | ||
| throwErrorIfNotInclude('responseType', config.responseType, validList) | ||
| } | ||
| /** | ||
| * 配置项 `responseEncoding` | ||
| * | ||
| * 规则: | ||
| * 1. 字符串 | ||
| * | ||
| * 合法值: | ||
| * 1. `utf8` (或 `utf-8`) | ||
| * 2. `base64` | ||
| * 3. `hex` | ||
| * 4. `ascii` | ||
| * 5. `binary` | ||
| * 6. `ucs2` | ||
| * 7. `latin1` | ||
| * 8. `utf16le` | ||
| */ | ||
| if (config.responseEncoding !== undefined) { | ||
| throwErrorIfWrongType('responseEncoding', config.responseEncoding, 'string') | ||
| const validList = ['utf8', 'utf-8', 'utf16le', 'latin1', 'base64', 'hex', 'ascii', 'binary', 'ucs2'] | ||
| throwErrorIfNotInclude('responseEncoding', config.responseEncoding, validList) | ||
| } | ||
| /** | ||
| * 配置项 `timeout` | ||
| * | ||
| * 规则: | ||
| * 1. 数字 | ||
| */ | ||
| if (config.timeout !== undefined) { | ||
| throwErrorIfWrongType('timeout', config.timeout, 'number') | ||
| const timeout = parseInt(config.timeout, 10) | ||
| config.timeout = timeout > 0 ? timeout : 0 | ||
| } | ||
| /** | ||
| * 配置项 `retry` | ||
| * | ||
| * 规则: | ||
| * 1. 非负整数 | ||
| */ | ||
| if (config.retry !== undefined) { | ||
| if (!(Number.isInteger(config.retry) && config.retry >= 0)) { | ||
| throw new Error('配置项 `retry` 要求是一个非负整数(0,1,2,...)') | ||
| } | ||
| } | ||
| /** | ||
| * 配置项 `headers` | ||
| * | ||
| * 规则: | ||
| * 1. 对象 | ||
| */ | ||
| if (config.headers !== undefined) { | ||
| throwErrorIfWrongType('headers', config.headers, 'object') | ||
| } | ||
| /** | ||
| * 配置项 `params` | ||
| * | ||
| * 规则: | ||
| * 1. 对象 | ||
| */ | ||
| if (config.params !== undefined) { | ||
| throwErrorIfWrongType('params', config.params, 'object') | ||
| } | ||
| /** | ||
| * 配置项 `mock` | ||
| * | ||
| * 规则: | ||
| * 1. 对象 | ||
| */ | ||
| if (config.mock !== undefined) { | ||
| throwErrorIfWrongType('mock', config.mock, 'object') | ||
| } | ||
| /** | ||
| * 配置项 `adapter` | ||
| * | ||
| * 规则: | ||
| * 1. 函数 | ||
| */ | ||
| if (config.adapter !== undefined) { | ||
| throwErrorIfWrongType('adapter', config.adapter, 'function') | ||
| } | ||
| /** | ||
| * 配置项 `validateStatus` | ||
| * | ||
| * 规则: | ||
| * 1. 函数 | ||
| */ | ||
| if (config.validateStatus !== undefined) { | ||
| throwErrorIfWrongType('validateStatus', config.validateStatus, 'function') | ||
| } | ||
| /** | ||
| * 配置项 `middleware` | ||
| * | ||
| * 规则: | ||
| * 1. 数组 | ||
| * 2. 子项为函数或对象 | ||
| */ | ||
| if (config.middleware !== undefined) { | ||
| if (typeof config.middleware !== 'object' || typeof config.middleware.length !== 'number') { | ||
| throw new Error('配置项 `middleware` 要求是一个数组!') | ||
| } | ||
| config.middleware.forEach(function checkMiddlewareItemType(item) { | ||
| if (typeof item !== 'function' && typeof item !== 'object') { | ||
| throw new Error('配置项 `middleware` 的列表元素要求是一个函数或对象,请检查你的设置!') | ||
| } | ||
| }) | ||
| } | ||
| /** | ||
| * 配置项 `responseItems` | ||
| * | ||
| * 规则: | ||
| * 1. 数组 | ||
| */ | ||
| if (config.responseItems !== undefined) { | ||
| if (typeof config.responseItems !== 'object' || typeof config.responseItems.length !== 'number') { | ||
| throw new Error('配置项 `responseItems` 要求是一个数组!') | ||
| } | ||
| if (config.responseItems.length === 0) { | ||
| throw new Error('配置项 `responseItems` 应至少包含一个元素,可选值:`status`, `statusText`, `headers`, `data`, `config`, `request`') | ||
| } | ||
| const valid = ['status', 'statusText', 'headers', 'data', 'config', 'request'] | ||
| config.responseItems.forEach(function checkResponseItemInclude(item) { | ||
| if (!valid.includes(item)) { | ||
| throw new Error( | ||
| '配置项 `responseItems` 的可选值:`status`, `statusText`, `headers`, `data`, `config`, `request`,你可以选择一项或多项,你选择的以下值不在可选值内:' + | ||
| ` \`${item}\` ` | ||
| ) | ||
| } | ||
| }) | ||
| } | ||
| /** | ||
| * 配置项 `debug` | ||
| * | ||
| * 合法值: | ||
| * 1. `true` | ||
| * 2. `false` | ||
| */ | ||
| if (config.debug !== undefined) { | ||
| if (config.debug !== true && config.debug !== false) { | ||
| throw new Error('配置项 `debug` 的可选值:`true` 或 `false`') | ||
| } | ||
| } | ||
| /** | ||
| * 配置项 `logging` | ||
| * | ||
| * 规则: | ||
| * 1. 是一个函数 | ||
| */ | ||
| if (config.logging !== undefined) { | ||
| throwErrorIfWrongType('logging', config.logging, 'function') | ||
| } | ||
| /** | ||
| * 配置项 `data` | ||
| * | ||
| * 规则: | ||
| * 1. 对 `data` 无校验 | ||
| */ | ||
| if (config.data !== undefined) { | ||
| // 空 | ||
| } | ||
| return config | ||
| } |
| 'use strict' | ||
| const buildConfig = require('../config/buildConfig.js') | ||
| const initialize = require('../lifetime/initialize.js') | ||
| const dispatch = require('../lifetime/dispatch.js') | ||
| const mergeConfig = require('../config/mergeConfig.js') | ||
| const getDefaults = require('../config/defaults.js') | ||
| const normalizeConfig = require('../config/normalizeConfig.js') | ||
| const Context = require('./context.js') | ||
| const getAliyunApigwSignatureMiddleware = require('../middleware/aliyunApigwSignature.js') | ||
| const MIDDLEWARE = Symbol('application#middleware') | ||
| const baseConfig = getDefaults() | ||
| module.exports = class Application { | ||
| constructor(defaultConfig) { | ||
| this.defaults = normalizeConfig(defaultConfig) | ||
| /** | ||
| * 用户自定义中间件列表 | ||
| * | ||
| * @param {function} [].fn 函数 | ||
| * @param {number} [].order 函数排序,用于在生成最终函数时,将列表重排(按数字升序排列) | ||
| */ | ||
| this[MIDDLEWARE] = this.defaults.middleware || [] | ||
| } | ||
| /** | ||
| * 添加一个默认自定义中间件 | ||
| * @param {function|object} obj 中间件 | ||
| */ | ||
| use(obj) { | ||
| this[MIDDLEWARE]['push'](obj) | ||
| } | ||
| /** | ||
| * 根据配置,计算需要加入的签名中间件 | ||
| */ | ||
| getSignMiddleware(config) { | ||
| if (config.sign && typeof config.sign === 'object' && !config.sign.disable) { | ||
| if (!(config.sign.key && config.sign.secret)) { | ||
| throw new Error('签名中间件开启时,需要同时配置 `sign.key` 和 `sign.secret` 参数!') | ||
| } | ||
| if (!config.sign.type || config.sign.type === 'aliyun') { | ||
| return getAliyunApigwSignatureMiddleware(config.sign) | ||
| } | ||
| } | ||
| } | ||
| createPipeline(configMiddleware) { | ||
| // 整理格式统一为 `{fn:function;order:number;}` | ||
| const unifiedList = configMiddleware.map((value) => { | ||
| if (typeof value === 'object') { | ||
| const { fn, order } = value | ||
| if (typeof fn === 'function' && typeof order === 'number') { | ||
| return { fn, order } | ||
| } | ||
| } else if (typeof value === 'function') { | ||
| return { fn: value, order: 100 } | ||
| } | ||
| throw new Error('中间件格式错误') | ||
| }) | ||
| // 对中间件重新排序(按照 `order` 升序排列) | ||
| unifiedList.sort((a, b) => { | ||
| return a.order - b.order | ||
| }) | ||
| // 获取中间件函数列表 | ||
| const middleware = unifiedList.map((value) => { | ||
| return value.fn | ||
| }) | ||
| middleware.push(dispatch) | ||
| middleware.unshift(initialize) | ||
| return function (context, next) { | ||
| let index = -1 | ||
| function dispatch(i) { | ||
| if (i <= index) return Promise.reject(new Error('next() 被执行了多次!')) | ||
| index = i | ||
| let fn = middleware[i] | ||
| if (i === middleware.length) { | ||
| fn = next | ||
| } | ||
| if (!fn) { | ||
| return Promise.resolve() | ||
| } | ||
| try { | ||
| return Promise.resolve(fn(context, dispatch.bind(null, i + 1))) | ||
| } catch (err) { | ||
| return Promise.reject(err) | ||
| } | ||
| } | ||
| return dispatch(0) | ||
| } | ||
| } | ||
| /** | ||
| * 发起请求(包含重试逻辑) | ||
| */ | ||
| async request(...args) { | ||
| // 整理参数,生成标准格式的 `config` | ||
| const customConfig = buildConfig(...args) | ||
| const config = mergeConfig(baseConfig, this.defaults, normalizeConfig(customConfig)) | ||
| // 加入 `签名中间件` | ||
| const signFn = this.getSignMiddleware(config) | ||
| if (signFn) { | ||
| config.middleware.push({ fn: signFn, order: 9999 }) | ||
| } | ||
| /** 最大重试次数 */ | ||
| let retry = config.retry | ||
| /** 已重试次数 */ | ||
| let retries = -1 | ||
| /** 检验成功请求函数 */ | ||
| const validateStatus = config.validateStatus | ||
| /** 响应结果列表 */ | ||
| const responses = [] | ||
| while (retry >= 0) { | ||
| retry-- | ||
| retries++ | ||
| const result = await this.execSingleRequest(config, retries) | ||
| responses.push(result) | ||
| if (validateStatus(result.status)) { | ||
| // 返回响应结果 | ||
| const { responseItems } = config | ||
| const output = {} | ||
| responseItems.forEach((key) => { | ||
| if (typeof key === 'string') { | ||
| output[key] = result[key] | ||
| } else if (typeof key === 'object') { | ||
| const { item, alias } = key | ||
| output[alias] = result[item] | ||
| } else { | ||
| throw new Error('配置项 `responseItems` 格式错误') | ||
| } | ||
| }) | ||
| if (responseItems.includes('responses')) { | ||
| output.responses = responses | ||
| } | ||
| return output | ||
| } | ||
| } | ||
| throw new Error(responses[responses.length - 1]) | ||
| } | ||
| /** | ||
| * 执行单次请求 | ||
| */ | ||
| async execSingleRequest(config, retries) { | ||
| const context = new Context(config) | ||
| context.retries = retries | ||
| const fn = this.createPipeline(config.middleware) | ||
| return await fn(context) | ||
| } | ||
| } |
| 'use strict' | ||
| const getUrl = require('./getUrl.js') | ||
| const mockAdapter = require('../adapters/mock.js') | ||
| const STATE = Symbol('context#state') | ||
| module.exports = class Context { | ||
| constructor(config) { | ||
| // 检查请求头 | ||
| this.checkHeadersUnique(config.headers) | ||
| this.config = config | ||
| this.adapter = config.mock && typeof config.mock === 'object' ? mockAdapter : config.adapter | ||
| /** 公共存储空间 */ | ||
| this[STATE] = {} | ||
| } | ||
| /** | ||
| * 以指定名称存储当前时间 | ||
| * @param {string} name 名称 | ||
| */ | ||
| setTime(name) { | ||
| /** | ||
| * 辅助函数:对数字补零到指定位数 | ||
| * @param {number} number 被格式化的数字 | ||
| * @param {number} digit 格式化的位数 | ||
| * @returns {string} | ||
| */ | ||
| function zerofill(number, digit = 2) { | ||
| const zero = '0' | ||
| for (let i = 0; i < digit; i++) { | ||
| if (number < Math.pow(10, i + 1)) { | ||
| const str = zero.repeat(digit - i - 1) + number.toString() | ||
| return str | ||
| } | ||
| } | ||
| return number | ||
| } | ||
| const time = new Date() | ||
| const year = time.getFullYear() | ||
| const month = zerofill(time.getMonth()) | ||
| const day = zerofill(time.getDate()) | ||
| const hour = zerofill(time.getHours()) | ||
| const minute = zerofill(time.getMinutes()) | ||
| const second = zerofill(time.getSeconds()) | ||
| const millisecond = zerofill(time.getMilliseconds(), 3) | ||
| const str = `${year}-${month}-${day} ${hour}:${minute}:${second}.${millisecond}` | ||
| this[STATE][name] = time.getTime() | ||
| this[STATE][name + 'Text'] = str | ||
| } | ||
| get state() { | ||
| return this[STATE] | ||
| } | ||
| /** | ||
| * 用于在中间件中快速获取 `url` | ||
| */ | ||
| get url() { | ||
| return getUrl(this.config.baseURL, this.config.url, this.config.params) | ||
| } | ||
| /** | ||
| * 检查请求头是否存在同名字段 | ||
| * @param {object} headers 请求头 | ||
| * | ||
| * 例如:`content-type` 和 `Content-Type` 为同名字段 | ||
| */ | ||
| checkHeadersUnique(headers) { | ||
| const keys = Object.keys(headers) | ||
| for (let i = 0; i < keys.length - 1; i++) { | ||
| for (let j = i + 1; j < keys.length; j++) { | ||
| if (keys[i].toUpperCase() === keys[j].toUpperCase()) { | ||
| throw new Error(`请求头中的 \`${keys[i]}\` 和 \`${keys[j]}\` 同名冲突,请检查对应的请求头来源,最多只保留一个!`) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| getHeader(field) { | ||
| if (typeof field !== 'string') { | ||
| return '' | ||
| } | ||
| Object.keys(this.config.headers).forEach((key) => { | ||
| if (key.toUpperCase() === field.toUpperCase()) { | ||
| return this.config.headers[key] | ||
| } | ||
| }) | ||
| } | ||
| removeHeader(field) { | ||
| if (typeof field !== 'string') { | ||
| return '' | ||
| } | ||
| Object.keys(this.config.headers).forEach((key) => { | ||
| if (key.toUpperCase() === field.toUpperCase()) { | ||
| delete this.config.headers[key] | ||
| } | ||
| }) | ||
| } | ||
| setHeader(field, value) { | ||
| this.removeHeader(field) | ||
| this.config.headers[field] = value | ||
| } | ||
| getParam(field) { | ||
| return this.config.params[field] | ||
| } | ||
| setParam(field, value) { | ||
| this.config.params[field] = value | ||
| } | ||
| /** | ||
| * 合成打印日志所需的内容 | ||
| * @returns {object} content 日志内容对象 | ||
| * @returns {number} content.status 响应状态码 | ||
| * @returns {string} content.url 请求地址 | ||
| * @returns {object} content.config 配置项 | ||
| * @returns {number} content.startTime 发起请求任务时间(时间戳) | ||
| * @returns {number} content.endTime 结束请求任务时间(时间戳) | ||
| * @returns {number} content.requestTime 本次发起请求时间(时间戳) | ||
| * @returns {number} content.responseTime 本次结束响应时间(时间戳) | ||
| * @returns {string} content.startTimeText 发起请求任务时间(文本格式) | ||
| * @returns {string} content.endTimeText 结束请求任务时间(文本格式) | ||
| * @returns {string} content.requestTimeText 本次发起请求时间(文本格式) | ||
| * @returns {string} content.responseTimeText 本次结束响应时间(文本格式) | ||
| */ | ||
| getLoggingContent() { | ||
| const ctx = this | ||
| return { | ||
| status: ctx.response.status, | ||
| url: ctx.url, | ||
| config: ctx.config, | ||
| startTime: ctx.state.startTime, | ||
| endTime: ctx.state.endTime, | ||
| requestTime: ctx.state.requestTime, | ||
| responseTime: ctx.state.responseTime, | ||
| startTimeText: ctx.state.startTimeText, | ||
| endTimeText: ctx.state.endTimeText, | ||
| requestTimeText: ctx.state.requestTimeText, | ||
| responseTimeText: ctx.state.responseTimeText, | ||
| } | ||
| } | ||
| } |
| 'use strict' | ||
| /** | ||
| * 根据环境变量,判断所处的环境类型,选择适配器 | ||
| * | ||
| * 备注: | ||
| * 1. `require` 语句不能在顶上提前使用,非特定环境引入模块将会无法运行而报错,只能在确定所处环境后引入 | ||
| */ | ||
| module.exports = function getAdapter() { | ||
| // 浏览器环境(各小程序环境把) | ||
| /** | ||
| * 普通浏览器环境、React Native 环境 | ||
| * | ||
| * 说明: | ||
| * 1. 各 `小程序` 环境把 `XMLHttpRequest` 直接禁用了,即 | ||
| * ```js | ||
| * typeof XMLHttpRequest === 'undefined' // true | ||
| * ``` | ||
| */ | ||
| if (typeof XMLHttpRequest === 'function') { | ||
| return require('../adapters/xhr.js') | ||
| } | ||
| /** | ||
| * Node.js 环境 | ||
| */ | ||
| if (typeof process === 'object' && typeof Buffer === 'function') { | ||
| return require('../adapters/node.js') | ||
| } | ||
| /** | ||
| * 微信小程序 | ||
| */ | ||
| if (typeof wx === 'object' && typeof wx.request === 'function') { | ||
| return require('../adapters/mini-wx.js') | ||
| } | ||
| /** | ||
| * 支付宝小程序 | ||
| */ | ||
| if (typeof my === 'object' && typeof my.request === 'function') { | ||
| return require('../adapters/mini-my.js') | ||
| } | ||
| /** | ||
| * QQ 小程序 | ||
| */ | ||
| if (typeof qq === 'object' && typeof qq.request === 'function') { | ||
| return require('../adapters/mini-qq.js') | ||
| } | ||
| /** | ||
| * 字节跳动小程序 | ||
| */ | ||
| if (typeof tt === 'object' && typeof tt.request === 'function') { | ||
| return require('../adapters/mini-tt.js') | ||
| } | ||
| throw new Error('暂时无法识别当前的 Javascript 环境,请使用自定义适配器 `config.adapter` 或联系作者添加当前环境适配器!') | ||
| } |
| 'use strict' | ||
| const statuses = require('../static/statuses.js') | ||
| const STATUS = Symbol('response#status') | ||
| const STATUS_TEXT = Symbol('response#statusText') | ||
| const HEADERS = Symbol('response#headers') | ||
| const DATA = Symbol('response#data') | ||
| module.exports = class Response { | ||
| constructor(adapterRes) { | ||
| this.config = adapterRes.config | ||
| this.status = adapterRes.status | ||
| this.statusText = adapterRes.statusText | ||
| this.headers = adapterRes.headers | ||
| this.data = adapterRes.data | ||
| this.request = adapterRes.request | ||
| } | ||
| get status() { | ||
| return this[STATUS] | ||
| } | ||
| set status(value) { | ||
| const code = parseInt(value, 10) | ||
| if (isNaN(code) || code < 100 || code > 599) { | ||
| throw new Error('响应状态码的有效范围是 100 ~ 599,当前为 ' + value) | ||
| } | ||
| this[STATUS] = code | ||
| } | ||
| get statusText() { | ||
| return this[STATUS_TEXT] || statuses[this.status] | ||
| } | ||
| set statusText(value) { | ||
| if (!value || typeof value !== 'string') { | ||
| return | ||
| } | ||
| this[STATUS_TEXT] = value | ||
| } | ||
| get headers() { | ||
| return this[HEADERS] || {} | ||
| } | ||
| set headers(value) { | ||
| if (typeof value === 'object') { | ||
| this[HEADERS] = value | ||
| } | ||
| } | ||
| get data() { | ||
| return this[DATA] || null | ||
| } | ||
| set data(value) { | ||
| if (typeof value === 'string' && this.config.responseType === 'json') { | ||
| try { | ||
| const json = JSON.parse(value) | ||
| this[DATA] = json | ||
| } catch (e) { | ||
| this[DATA] = value | ||
| } | ||
| } else { | ||
| this[DATA] = value | ||
| } | ||
| } | ||
| } |
| 'use strict' | ||
| const Response = require('../core/response.js') | ||
| /** | ||
| * 将请求适配器封装为中间件格式 | ||
| */ | ||
| module.exports = async function dispatch(ctx) { | ||
| // 记录发起请求时间 | ||
| ctx.setTime('requestTime') | ||
| const result = await ctx.adapter(ctx.config) | ||
| // 记录完成请求时间 | ||
| ctx.setTime('responseTime') | ||
| ctx.response = new Response(result) | ||
| ctx.request = result.request | ||
| } |
| 'use strict' | ||
| /** | ||
| * 初始化函数,用于放在第一个中间件 | ||
| */ | ||
| module.exports = async function initialize(ctx, next) { | ||
| // 记录本次请求任务开始时间 | ||
| ctx.setTime('startTime') | ||
| await next() | ||
| // 记录本次请求任务结束时间 | ||
| ctx.setTime('endTime') | ||
| // 打印日志 | ||
| if (ctx.config.debug && typeof ctx.config.logging === 'function') { | ||
| const content = ctx.getLoggingContent() | ||
| ctx.config.logging(content) | ||
| } | ||
| return { | ||
| status: ctx.response.status, | ||
| statusText: ctx.response.statusText, | ||
| headers: ctx.response.headers, | ||
| data: ctx.response.data, | ||
| request: ctx.response.request, | ||
| url: ctx.url, | ||
| config: ctx.config, | ||
| } | ||
| } |
| 'use strict' | ||
| const CryptoJS = require('../min/crypto-js.min.js') | ||
| /** | ||
| * 获取参与签名的请求头字段 | ||
| * @param {object} headers 请求头 | ||
| * @returns {string[]} | ||
| */ | ||
| function getSignHeaderKeys(headers) { | ||
| /** 不参与 Header 签名的请求头 */ | ||
| const EXCLUDE_SIGN_HEADERS = ['x-ca-signature', 'x-xa-signature-headers', 'accept', 'content-md5', 'content-type', 'date'] | ||
| const signHeaderKeys = [] | ||
| Object.keys(headers).forEach((field) => { | ||
| if (!EXCLUDE_SIGN_HEADERS.includes(field)) { | ||
| signHeaderKeys.push(field) | ||
| } | ||
| }) | ||
| return signHeaderKeys.sort() | ||
| } | ||
| function getSignedHeadersString(signHeaderKeys, headers) { | ||
| const list = [] | ||
| for (let i = 0; i < signHeaderKeys.length; i++) { | ||
| const key = signHeaderKeys[i] | ||
| const value = headers[key] | ||
| list.push(key + ':' + (value ? value : '')) | ||
| } | ||
| return list.join('\n') | ||
| } | ||
| function getPathAndParams(url) { | ||
| const urlRaw = url.replace('https://', '').replace('http://', '') | ||
| return urlRaw.substr(urlRaw.indexOf('/')) | ||
| } | ||
| function md5(content) { | ||
| return CryptoJS.MD5(content).toString(CryptoJS.enc.Base64) | ||
| } | ||
| function buildStringToSign(method, headers, signedHeadersString, pathAndParams) { | ||
| const lf = '\n' | ||
| const list = [method.toUpperCase(), lf] | ||
| const arr = ['accept', 'content-md5', 'content-type', 'date'] | ||
| for (let i = 0; i < arr.length; i++) { | ||
| const key = arr[i] | ||
| if (headers[key]) { | ||
| list.push(headers[key]) | ||
| } | ||
| list.push(lf) | ||
| } | ||
| if (signedHeadersString) { | ||
| list.push(signedHeadersString) | ||
| list.push(lf) | ||
| } | ||
| if (pathAndParams) { | ||
| list.push(pathAndParams) | ||
| } | ||
| return list.join('') | ||
| } | ||
| /** | ||
| * 阿里云 API 网关签名加密 | ||
| * @see https://help.aliyun.com/document_detail/29475.html | ||
| */ | ||
| module.exports = function getAliyunApigwSignatureMiddleware(options) { | ||
| const { key, secret, debug } = options | ||
| const appKey = key | ||
| const appSecret = secret | ||
| return async function aliyunApigwSignature(ctx, next) { | ||
| const originHeaders = ctx.config.headers | ||
| const url = ctx.url | ||
| // 处理请求头 | ||
| const headers = {} | ||
| // 将请求头字段全部处理成 `小写` | ||
| Object.keys(originHeaders).forEach((field) => { | ||
| if (originHeaders[field]) { | ||
| headers[field.toLowerCase()] = originHeaders[field] | ||
| } | ||
| }) | ||
| // 给请求头添加一些要求添加的字段 | ||
| headers['x-ca-key'] = appKey | ||
| headers['x-ca-timestamp'] = Date.now() | ||
| headers['accept'] = headers['accept'] || '*/*' | ||
| headers['content-type'] = headers['content-type'] || 'application/json' | ||
| // 该请求头要求为一个随机字符串,理论上使用 `UUID` 更好,为了少引入依赖,使用这种方法 | ||
| headers['x-ca-nonce'] = CryptoJS.MD5(Date.now().toString() + Math.random() * 10000).toString(CryptoJS.enc.Hex) | ||
| if (ctx.config.method !== 'GET' && ctx.config.data) { | ||
| headers['content-md5'] = md5(JSON.stringify(ctx.config.data)) | ||
| } | ||
| const signHeaderKeys = getSignHeaderKeys(headers) | ||
| headers['x-ca-signature-headers'] = signHeaderKeys.join(',') | ||
| const pathAndParams = getPathAndParams(url) | ||
| const signedHeadersString = getSignedHeadersString(signHeaderKeys, headers) | ||
| const stringToSign = buildStringToSign(ctx.config.method, headers, signedHeadersString, pathAndParams) | ||
| headers['x-ca-signature'] = CryptoJS.HmacSHA256(stringToSign, appSecret).toString(CryptoJS.enc.Base64) | ||
| if (debug) { | ||
| console.log(`当前签名字符串:\`${stringToSign.replace(/\n/g, '#')}\``) | ||
| } | ||
| ctx.config.headers = headers | ||
| await next() | ||
| } | ||
| } |
| !function(t,e){"object"==typeof exports?module.exports=exports=e():"function"==typeof define&&define.amd?define([],e):t.CryptoJS=e()}(this,function(){var h,t,e,r,i,n,f,o,s,c,a,l,d,m,x,b,H,z,A,u,p,_,v,y,g,B,w,k,S,C,D,E,R,M,F,P,W,O,I,U,K,X,L,j,N,T,q,Z,V,G,J,$,Q,Y,tt,et,rt,it,nt,ot,st,ct,at,ht,lt,ft,dt,ut,pt,_t,vt,yt,gt,Bt,wt,kt,St,bt=bt||function(l){var t;if("undefined"!=typeof window&&window.crypto&&(t=window.crypto),!t&&"undefined"!=typeof window&&window.msCrypto&&(t=window.msCrypto),!t&&"undefined"!=typeof global&&global.crypto&&(t=global.crypto),!t&&"function"==typeof require)try{t=require("crypto")}catch(t){}function i(){if(t){if("function"==typeof t.getRandomValues)try{return t.getRandomValues(new Uint32Array(1))[0]}catch(t){}if("function"==typeof t.randomBytes)try{return t.randomBytes(4).readInt32LE()}catch(t){}}throw new Error("Native crypto module could not be used to get secure random number.")}var r=Object.create||function(t){var e;return n.prototype=t,e=new n,n.prototype=null,e};function n(){}var e={},o=e.lib={},s=o.Base={extend:function(t){var e=r(this);return t&&e.mixIn(t),e.hasOwnProperty("init")&&this.init!==e.init||(e.init=function(){e.$super.init.apply(this,arguments)}),(e.init.prototype=e).$super=this,e},create:function(){var t=this.extend();return t.init.apply(t,arguments),t},init:function(){},mixIn:function(t){for(var e in t)t.hasOwnProperty(e)&&(this[e]=t[e]);t.hasOwnProperty("toString")&&(this.toString=t.toString)},clone:function(){return this.init.prototype.extend(this)}},f=o.WordArray=s.extend({init:function(t,e){t=this.words=t||[],this.sigBytes=null!=e?e:4*t.length},toString:function(t){return(t||a).stringify(this)},concat:function(t){var e=this.words,r=t.words,i=this.sigBytes,n=t.sigBytes;if(this.clamp(),i%4)for(var o=0;o<n;o++){var s=r[o>>>2]>>>24-o%4*8&255;e[i+o>>>2]|=s<<24-(i+o)%4*8}else for(o=0;o<n;o+=4)e[i+o>>>2]=r[o>>>2];return this.sigBytes+=n,this},clamp:function(){var t=this.words,e=this.sigBytes;t[e>>>2]&=4294967295<<32-e%4*8,t.length=l.ceil(e/4)},clone:function(){var t=s.clone.call(this);return t.words=this.words.slice(0),t},random:function(t){for(var e=[],r=0;r<t;r+=4)e.push(i());return new f.init(e,t)}}),c=e.enc={},a=c.Hex={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],n=0;n<r;n++){var o=e[n>>>2]>>>24-n%4*8&255;i.push((o>>>4).toString(16)),i.push((15&o).toString(16))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i<e;i+=2)r[i>>>3]|=parseInt(t.substr(i,2),16)<<24-i%8*4;return new f.init(r,e/2)}},h=c.Latin1={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],n=0;n<r;n++){var o=e[n>>>2]>>>24-n%4*8&255;i.push(String.fromCharCode(o))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i<e;i++)r[i>>>2]|=(255&t.charCodeAt(i))<<24-i%4*8;return new f.init(r,e)}},d=c.Utf8={stringify:function(t){try{return decodeURIComponent(escape(h.stringify(t)))}catch(t){throw new Error("Malformed UTF-8 data")}},parse:function(t){return h.parse(unescape(encodeURIComponent(t)))}},u=o.BufferedBlockAlgorithm=s.extend({reset:function(){this._data=new f.init,this._nDataBytes=0},_append:function(t){"string"==typeof t&&(t=d.parse(t)),this._data.concat(t),this._nDataBytes+=t.sigBytes},_process:function(t){var e,r=this._data,i=r.words,n=r.sigBytes,o=this.blockSize,s=n/(4*o),c=(s=t?l.ceil(s):l.max((0|s)-this._minBufferSize,0))*o,a=l.min(4*c,n);if(c){for(var h=0;h<c;h+=o)this._doProcessBlock(i,h);e=i.splice(0,c),r.sigBytes-=a}return new f.init(e,a)},clone:function(){var t=s.clone.call(this);return t._data=this._data.clone(),t},_minBufferSize:0}),p=(o.Hasher=u.extend({cfg:s.extend(),init:function(t){this.cfg=this.cfg.extend(t),this.reset()},reset:function(){u.reset.call(this),this._doReset()},update:function(t){return this._append(t),this._process(),this},finalize:function(t){return t&&this._append(t),this._doFinalize()},blockSize:16,_createHelper:function(r){return function(t,e){return new r.init(e).finalize(t)}},_createHmacHelper:function(r){return function(t,e){return new p.HMAC.init(r,e).finalize(t)}}}),e.algo={});return e}(Math);function mt(t,e,r){return t^e^r}function xt(t,e,r){return t&e|~t&r}function Ht(t,e,r){return(t|~e)^r}function zt(t,e,r){return t&r|e&~r}function At(t,e,r){return t^(e|~r)}function Ct(t,e){return t<<e|t>>>32-e}function Dt(t,e,r,i){var n,o=this._iv;o?(n=o.slice(0),this._iv=void 0):n=this._prevBlock,i.encryptBlock(n,0);for(var s=0;s<r;s++)t[e+s]^=n[s]}function Et(t){if(255==(t>>24&255)){var e=t>>16&255,r=t>>8&255,i=255&t;255===e?(e=0,255===r?(r=0,255===i?i=0:++i):++r):++e,t=0,t+=e<<16,t+=r<<8,t+=i}else t+=1<<24;return t}function Rt(){for(var t=this._X,e=this._C,r=0;r<8;r++)ft[r]=e[r];e[0]=e[0]+1295307597+this._b|0,e[1]=e[1]+3545052371+(e[0]>>>0<ft[0]>>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0<ft[1]>>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0<ft[2]>>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0<ft[3]>>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0<ft[4]>>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0<ft[5]>>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0<ft[6]>>>0?1:0)|0,this._b=e[7]>>>0<ft[7]>>>0?1:0;for(r=0;r<8;r++){var i=t[r]+e[r],n=65535&i,o=i>>>16,s=((n*n>>>17)+n*o>>>15)+o*o,c=((4294901760&i)*i|0)+((65535&i)*i|0);dt[r]=s^c}t[0]=dt[0]+(dt[7]<<16|dt[7]>>>16)+(dt[6]<<16|dt[6]>>>16)|0,t[1]=dt[1]+(dt[0]<<8|dt[0]>>>24)+dt[7]|0,t[2]=dt[2]+(dt[1]<<16|dt[1]>>>16)+(dt[0]<<16|dt[0]>>>16)|0,t[3]=dt[3]+(dt[2]<<8|dt[2]>>>24)+dt[1]|0,t[4]=dt[4]+(dt[3]<<16|dt[3]>>>16)+(dt[2]<<16|dt[2]>>>16)|0,t[5]=dt[5]+(dt[4]<<8|dt[4]>>>24)+dt[3]|0,t[6]=dt[6]+(dt[5]<<16|dt[5]>>>16)+(dt[4]<<16|dt[4]>>>16)|0,t[7]=dt[7]+(dt[6]<<8|dt[6]>>>24)+dt[5]|0}function Mt(){for(var t=this._X,e=this._C,r=0;r<8;r++)wt[r]=e[r];e[0]=e[0]+1295307597+this._b|0,e[1]=e[1]+3545052371+(e[0]>>>0<wt[0]>>>0?1:0)|0,e[2]=e[2]+886263092+(e[1]>>>0<wt[1]>>>0?1:0)|0,e[3]=e[3]+1295307597+(e[2]>>>0<wt[2]>>>0?1:0)|0,e[4]=e[4]+3545052371+(e[3]>>>0<wt[3]>>>0?1:0)|0,e[5]=e[5]+886263092+(e[4]>>>0<wt[4]>>>0?1:0)|0,e[6]=e[6]+1295307597+(e[5]>>>0<wt[5]>>>0?1:0)|0,e[7]=e[7]+3545052371+(e[6]>>>0<wt[6]>>>0?1:0)|0,this._b=e[7]>>>0<wt[7]>>>0?1:0;for(r=0;r<8;r++){var i=t[r]+e[r],n=65535&i,o=i>>>16,s=((n*n>>>17)+n*o>>>15)+o*o,c=((4294901760&i)*i|0)+((65535&i)*i|0);kt[r]=s^c}t[0]=kt[0]+(kt[7]<<16|kt[7]>>>16)+(kt[6]<<16|kt[6]>>>16)|0,t[1]=kt[1]+(kt[0]<<8|kt[0]>>>24)+kt[7]|0,t[2]=kt[2]+(kt[1]<<16|kt[1]>>>16)+(kt[0]<<16|kt[0]>>>16)|0,t[3]=kt[3]+(kt[2]<<8|kt[2]>>>24)+kt[1]|0,t[4]=kt[4]+(kt[3]<<16|kt[3]>>>16)+(kt[2]<<16|kt[2]>>>16)|0,t[5]=kt[5]+(kt[4]<<8|kt[4]>>>24)+kt[3]|0,t[6]=kt[6]+(kt[5]<<16|kt[5]>>>16)+(kt[4]<<16|kt[4]>>>16)|0,t[7]=kt[7]+(kt[6]<<8|kt[6]>>>24)+kt[5]|0}return h=bt.lib.WordArray,bt.enc.Base64={stringify:function(t){var e=t.words,r=t.sigBytes,i=this._map;t.clamp();for(var n=[],o=0;o<r;o+=3)for(var s=(e[o>>>2]>>>24-o%4*8&255)<<16|(e[o+1>>>2]>>>24-(o+1)%4*8&255)<<8|e[o+2>>>2]>>>24-(o+2)%4*8&255,c=0;c<4&&o+.75*c<r;c++)n.push(i.charAt(s>>>6*(3-c)&63));var a=i.charAt(64);if(a)for(;n.length%4;)n.push(a);return n.join("")},parse:function(t){var e=t.length,r=this._map,i=this._reverseMap;if(!i){i=this._reverseMap=[];for(var n=0;n<r.length;n++)i[r.charCodeAt(n)]=n}var o=r.charAt(64);if(o){var s=t.indexOf(o);-1!==s&&(e=s)}return function(t,e,r){for(var i=[],n=0,o=0;o<e;o++)if(o%4){var s=r[t.charCodeAt(o-1)]<<o%4*2,c=r[t.charCodeAt(o)]>>>6-o%4*2,a=s|c;i[n>>>2]|=a<<24-n%4*8,n++}return h.create(i,n)}(t,e,i)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="},function(l){var t=bt,e=t.lib,r=e.WordArray,i=e.Hasher,n=t.algo,H=[];!function(){for(var t=0;t<64;t++)H[t]=4294967296*l.abs(l.sin(t+1))|0}();var o=n.MD5=i.extend({_doReset:function(){this._hash=new r.init([1732584193,4023233417,2562383102,271733878])},_doProcessBlock:function(t,e){for(var r=0;r<16;r++){var i=e+r,n=t[i];t[i]=16711935&(n<<8|n>>>24)|4278255360&(n<<24|n>>>8)}var o=this._hash.words,s=t[e+0],c=t[e+1],a=t[e+2],h=t[e+3],l=t[e+4],f=t[e+5],d=t[e+6],u=t[e+7],p=t[e+8],_=t[e+9],v=t[e+10],y=t[e+11],g=t[e+12],B=t[e+13],w=t[e+14],k=t[e+15],S=o[0],m=o[1],x=o[2],b=o[3];S=z(S,m,x,b,s,7,H[0]),b=z(b,S,m,x,c,12,H[1]),x=z(x,b,S,m,a,17,H[2]),m=z(m,x,b,S,h,22,H[3]),S=z(S,m,x,b,l,7,H[4]),b=z(b,S,m,x,f,12,H[5]),x=z(x,b,S,m,d,17,H[6]),m=z(m,x,b,S,u,22,H[7]),S=z(S,m,x,b,p,7,H[8]),b=z(b,S,m,x,_,12,H[9]),x=z(x,b,S,m,v,17,H[10]),m=z(m,x,b,S,y,22,H[11]),S=z(S,m,x,b,g,7,H[12]),b=z(b,S,m,x,B,12,H[13]),x=z(x,b,S,m,w,17,H[14]),S=A(S,m=z(m,x,b,S,k,22,H[15]),x,b,c,5,H[16]),b=A(b,S,m,x,d,9,H[17]),x=A(x,b,S,m,y,14,H[18]),m=A(m,x,b,S,s,20,H[19]),S=A(S,m,x,b,f,5,H[20]),b=A(b,S,m,x,v,9,H[21]),x=A(x,b,S,m,k,14,H[22]),m=A(m,x,b,S,l,20,H[23]),S=A(S,m,x,b,_,5,H[24]),b=A(b,S,m,x,w,9,H[25]),x=A(x,b,S,m,h,14,H[26]),m=A(m,x,b,S,p,20,H[27]),S=A(S,m,x,b,B,5,H[28]),b=A(b,S,m,x,a,9,H[29]),x=A(x,b,S,m,u,14,H[30]),S=C(S,m=A(m,x,b,S,g,20,H[31]),x,b,f,4,H[32]),b=C(b,S,m,x,p,11,H[33]),x=C(x,b,S,m,y,16,H[34]),m=C(m,x,b,S,w,23,H[35]),S=C(S,m,x,b,c,4,H[36]),b=C(b,S,m,x,l,11,H[37]),x=C(x,b,S,m,u,16,H[38]),m=C(m,x,b,S,v,23,H[39]),S=C(S,m,x,b,B,4,H[40]),b=C(b,S,m,x,s,11,H[41]),x=C(x,b,S,m,h,16,H[42]),m=C(m,x,b,S,d,23,H[43]),S=C(S,m,x,b,_,4,H[44]),b=C(b,S,m,x,g,11,H[45]),x=C(x,b,S,m,k,16,H[46]),S=D(S,m=C(m,x,b,S,a,23,H[47]),x,b,s,6,H[48]),b=D(b,S,m,x,u,10,H[49]),x=D(x,b,S,m,w,15,H[50]),m=D(m,x,b,S,f,21,H[51]),S=D(S,m,x,b,g,6,H[52]),b=D(b,S,m,x,h,10,H[53]),x=D(x,b,S,m,v,15,H[54]),m=D(m,x,b,S,c,21,H[55]),S=D(S,m,x,b,p,6,H[56]),b=D(b,S,m,x,k,10,H[57]),x=D(x,b,S,m,d,15,H[58]),m=D(m,x,b,S,B,21,H[59]),S=D(S,m,x,b,l,6,H[60]),b=D(b,S,m,x,y,10,H[61]),x=D(x,b,S,m,a,15,H[62]),m=D(m,x,b,S,_,21,H[63]),o[0]=o[0]+S|0,o[1]=o[1]+m|0,o[2]=o[2]+x|0,o[3]=o[3]+b|0},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;e[i>>>5]|=128<<24-i%32;var n=l.floor(r/4294967296),o=r;e[15+(64+i>>>9<<4)]=16711935&(n<<8|n>>>24)|4278255360&(n<<24|n>>>8),e[14+(64+i>>>9<<4)]=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8),t.sigBytes=4*(e.length+1),this._process();for(var s=this._hash,c=s.words,a=0;a<4;a++){var h=c[a];c[a]=16711935&(h<<8|h>>>24)|4278255360&(h<<24|h>>>8)}return s},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t}});function z(t,e,r,i,n,o,s){var c=t+(e&r|~e&i)+n+s;return(c<<o|c>>>32-o)+e}function A(t,e,r,i,n,o,s){var c=t+(e&i|r&~i)+n+s;return(c<<o|c>>>32-o)+e}function C(t,e,r,i,n,o,s){var c=t+(e^r^i)+n+s;return(c<<o|c>>>32-o)+e}function D(t,e,r,i,n,o,s){var c=t+(r^(e|~i))+n+s;return(c<<o|c>>>32-o)+e}t.MD5=i._createHelper(o),t.HmacMD5=i._createHmacHelper(o)}(Math),e=(t=bt).lib,r=e.WordArray,i=e.Hasher,n=t.algo,f=[],o=n.SHA1=i.extend({_doReset:function(){this._hash=new r.init([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],n=r[1],o=r[2],s=r[3],c=r[4],a=0;a<80;a++){if(a<16)f[a]=0|t[e+a];else{var h=f[a-3]^f[a-8]^f[a-14]^f[a-16];f[a]=h<<1|h>>>31}var l=(i<<5|i>>>27)+c+f[a];l+=a<20?1518500249+(n&o|~n&s):a<40?1859775393+(n^o^s):a<60?(n&o|n&s|o&s)-1894007588:(n^o^s)-899497514,c=s,s=o,o=n<<30|n>>>2,n=i,i=l}r[0]=r[0]+i|0,r[1]=r[1]+n|0,r[2]=r[2]+o|0,r[3]=r[3]+s|0,r[4]=r[4]+c|0},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[14+(64+i>>>9<<4)]=Math.floor(r/4294967296),e[15+(64+i>>>9<<4)]=r,t.sigBytes=4*e.length,this._process(),this._hash},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t}}),t.SHA1=i._createHelper(o),t.HmacSHA1=i._createHmacHelper(o),function(n){var t=bt,e=t.lib,r=e.WordArray,i=e.Hasher,o=t.algo,s=[],B=[];!function(){function t(t){for(var e=n.sqrt(t),r=2;r<=e;r++)if(!(t%r))return;return 1}function e(t){return 4294967296*(t-(0|t))|0}for(var r=2,i=0;i<64;)t(r)&&(i<8&&(s[i]=e(n.pow(r,.5))),B[i]=e(n.pow(r,1/3)),i++),r++}();var w=[],c=o.SHA256=i.extend({_doReset:function(){this._hash=new r.init(s.slice(0))},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],n=r[1],o=r[2],s=r[3],c=r[4],a=r[5],h=r[6],l=r[7],f=0;f<64;f++){if(f<16)w[f]=0|t[e+f];else{var d=w[f-15],u=(d<<25|d>>>7)^(d<<14|d>>>18)^d>>>3,p=w[f-2],_=(p<<15|p>>>17)^(p<<13|p>>>19)^p>>>10;w[f]=u+w[f-7]+_+w[f-16]}var v=i&n^i&o^n&o,y=(i<<30|i>>>2)^(i<<19|i>>>13)^(i<<10|i>>>22),g=l+((c<<26|c>>>6)^(c<<21|c>>>11)^(c<<7|c>>>25))+(c&a^~c&h)+B[f]+w[f];l=h,h=a,a=c,c=s+g|0,s=o,o=n,n=i,i=g+(y+v)|0}r[0]=r[0]+i|0,r[1]=r[1]+n|0,r[2]=r[2]+o|0,r[3]=r[3]+s|0,r[4]=r[4]+c|0,r[5]=r[5]+a|0,r[6]=r[6]+h|0,r[7]=r[7]+l|0},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[14+(64+i>>>9<<4)]=n.floor(r/4294967296),e[15+(64+i>>>9<<4)]=r,t.sigBytes=4*e.length,this._process(),this._hash},clone:function(){var t=i.clone.call(this);return t._hash=this._hash.clone(),t}});t.SHA256=i._createHelper(c),t.HmacSHA256=i._createHmacHelper(c)}(Math),function(){var n=bt.lib.WordArray,t=bt.enc;t.Utf16=t.Utf16BE={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],n=0;n<r;n+=2){var o=e[n>>>2]>>>16-n%4*8&65535;i.push(String.fromCharCode(o))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i<e;i++)r[i>>>1]|=t.charCodeAt(i)<<16-i%2*16;return n.create(r,2*e)}};function s(t){return t<<8&4278255360|t>>>8&16711935}t.Utf16LE={stringify:function(t){for(var e=t.words,r=t.sigBytes,i=[],n=0;n<r;n+=2){var o=s(e[n>>>2]>>>16-n%4*8&65535);i.push(String.fromCharCode(o))}return i.join("")},parse:function(t){for(var e=t.length,r=[],i=0;i<e;i++)r[i>>>1]|=s(t.charCodeAt(i)<<16-i%2*16);return n.create(r,2*e)}}}(),function(){if("function"==typeof ArrayBuffer){var t=bt.lib.WordArray,n=t.init;(t.init=function(t){if(t instanceof ArrayBuffer&&(t=new Uint8Array(t)),(t instanceof Int8Array||"undefined"!=typeof Uint8ClampedArray&&t instanceof Uint8ClampedArray||t instanceof Int16Array||t instanceof Uint16Array||t instanceof Int32Array||t instanceof Uint32Array||t instanceof Float32Array||t instanceof Float64Array)&&(t=new Uint8Array(t.buffer,t.byteOffset,t.byteLength)),t instanceof Uint8Array){for(var e=t.byteLength,r=[],i=0;i<e;i++)r[i>>>2]|=t[i]<<24-i%4*8;n.call(this,r,e)}else n.apply(this,arguments)}).prototype=t}}(),Math,c=(s=bt).lib,a=c.WordArray,l=c.Hasher,d=s.algo,m=a.create([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13]),x=a.create([5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]),b=a.create([11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6]),H=a.create([8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]),z=a.create([0,1518500249,1859775393,2400959708,2840853838]),A=a.create([1352829926,1548603684,1836072691,2053994217,0]),u=d.RIPEMD160=l.extend({_doReset:function(){this._hash=a.create([1732584193,4023233417,2562383102,271733878,3285377520])},_doProcessBlock:function(t,e){for(var r=0;r<16;r++){var i=e+r,n=t[i];t[i]=16711935&(n<<8|n>>>24)|4278255360&(n<<24|n>>>8)}var o,s,c,a,h,l,f,d,u,p,_,v=this._hash.words,y=z.words,g=A.words,B=m.words,w=x.words,k=b.words,S=H.words;l=o=v[0],f=s=v[1],d=c=v[2],u=a=v[3],p=h=v[4];for(r=0;r<80;r+=1)_=o+t[e+B[r]]|0,_+=r<16?mt(s,c,a)+y[0]:r<32?xt(s,c,a)+y[1]:r<48?Ht(s,c,a)+y[2]:r<64?zt(s,c,a)+y[3]:At(s,c,a)+y[4],_=(_=Ct(_|=0,k[r]))+h|0,o=h,h=a,a=Ct(c,10),c=s,s=_,_=l+t[e+w[r]]|0,_+=r<16?At(f,d,u)+g[0]:r<32?zt(f,d,u)+g[1]:r<48?Ht(f,d,u)+g[2]:r<64?xt(f,d,u)+g[3]:mt(f,d,u)+g[4],_=(_=Ct(_|=0,S[r]))+p|0,l=p,p=u,u=Ct(d,10),d=f,f=_;_=v[1]+c+u|0,v[1]=v[2]+a+p|0,v[2]=v[3]+h+l|0,v[3]=v[4]+o+f|0,v[4]=v[0]+s+d|0,v[0]=_},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;e[i>>>5]|=128<<24-i%32,e[14+(64+i>>>9<<4)]=16711935&(r<<8|r>>>24)|4278255360&(r<<24|r>>>8),t.sigBytes=4*(e.length+1),this._process();for(var n=this._hash,o=n.words,s=0;s<5;s++){var c=o[s];o[s]=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8)}return n},clone:function(){var t=l.clone.call(this);return t._hash=this._hash.clone(),t}}),s.RIPEMD160=l._createHelper(u),s.HmacRIPEMD160=l._createHmacHelper(u),p=bt.lib.Base,_=bt.enc.Utf8,bt.algo.HMAC=p.extend({init:function(t,e){t=this._hasher=new t.init,"string"==typeof e&&(e=_.parse(e));var r=t.blockSize,i=4*r;e.sigBytes>i&&(e=t.finalize(e)),e.clamp();for(var n=this._oKey=e.clone(),o=this._iKey=e.clone(),s=n.words,c=o.words,a=0;a<r;a++)s[a]^=1549556828,c[a]^=909522486;n.sigBytes=o.sigBytes=i,this.reset()},reset:function(){var t=this._hasher;t.reset(),t.update(this._iKey)},update:function(t){return this._hasher.update(t),this},finalize:function(t){var e=this._hasher,r=e.finalize(t);return e.reset(),e.finalize(this._oKey.clone().concat(r))}}),y=(v=bt).lib,g=y.Base,B=y.WordArray,w=v.algo,k=w.SHA1,S=w.HMAC,C=w.PBKDF2=g.extend({cfg:g.extend({keySize:4,hasher:k,iterations:1}),init:function(t){this.cfg=this.cfg.extend(t)},compute:function(t,e){for(var r=this.cfg,i=S.create(r.hasher,t),n=B.create(),o=B.create([1]),s=n.words,c=o.words,a=r.keySize,h=r.iterations;s.length<a;){var l=i.update(e).finalize(o);i.reset();for(var f=l.words,d=f.length,u=l,p=1;p<h;p++){u=i.finalize(u),i.reset();for(var _=u.words,v=0;v<d;v++)f[v]^=_[v]}n.concat(l),c[0]++}return n.sigBytes=4*a,n}}),v.PBKDF2=function(t,e,r){return C.create(r).compute(t,e)},E=(D=bt).lib,R=E.Base,M=E.WordArray,F=D.algo,P=F.MD5,W=F.EvpKDF=R.extend({cfg:R.extend({keySize:4,hasher:P,iterations:1}),init:function(t){this.cfg=this.cfg.extend(t)},compute:function(t,e){for(var r,i=this.cfg,n=i.hasher.create(),o=M.create(),s=o.words,c=i.keySize,a=i.iterations;s.length<c;){r&&n.update(r),r=n.update(t).finalize(e),n.reset();for(var h=1;h<a;h++)r=n.finalize(r),n.reset();o.concat(r)}return o.sigBytes=4*c,o}}),D.EvpKDF=function(t,e,r){return W.create(r).compute(t,e)},I=(O=bt).lib.WordArray,U=O.algo,K=U.SHA256,X=U.SHA224=K.extend({_doReset:function(){this._hash=new I.init([3238371032,914150663,812702999,4144912697,4290775857,1750603025,1694076839,3204075428])},_doFinalize:function(){var t=K._doFinalize.call(this);return t.sigBytes-=4,t}}),O.SHA224=K._createHelper(X),O.HmacSHA224=K._createHmacHelper(X),L=bt.lib,j=L.Base,N=L.WordArray,(T=bt.x64={}).Word=j.extend({init:function(t,e){this.high=t,this.low=e}}),T.WordArray=j.extend({init:function(t,e){t=this.words=t||[],this.sigBytes=null!=e?e:8*t.length},toX32:function(){for(var t=this.words,e=t.length,r=[],i=0;i<e;i++){var n=t[i];r.push(n.high),r.push(n.low)}return N.create(r,this.sigBytes)},clone:function(){for(var t=j.clone.call(this),e=t.words=this.words.slice(0),r=e.length,i=0;i<r;i++)e[i]=e[i].clone();return t}}),function(d){var t=bt,e=t.lib,u=e.WordArray,i=e.Hasher,l=t.x64.Word,r=t.algo,C=[],D=[],E=[];!function(){for(var t=1,e=0,r=0;r<24;r++){C[t+5*e]=(r+1)*(r+2)/2%64;var i=(2*t+3*e)%5;t=e%5,e=i}for(t=0;t<5;t++)for(e=0;e<5;e++)D[t+5*e]=e+(2*t+3*e)%5*5;for(var n=1,o=0;o<24;o++){for(var s=0,c=0,a=0;a<7;a++){if(1&n){var h=(1<<a)-1;h<32?c^=1<<h:s^=1<<h-32}128&n?n=n<<1^113:n<<=1}E[o]=l.create(s,c)}}();var R=[];!function(){for(var t=0;t<25;t++)R[t]=l.create()}();var n=r.SHA3=i.extend({cfg:i.cfg.extend({outputLength:512}),_doReset:function(){for(var t=this._state=[],e=0;e<25;e++)t[e]=new l.init;this.blockSize=(1600-2*this.cfg.outputLength)/32},_doProcessBlock:function(t,e){for(var r=this._state,i=this.blockSize/2,n=0;n<i;n++){var o=t[e+2*n],s=t[e+2*n+1];o=16711935&(o<<8|o>>>24)|4278255360&(o<<24|o>>>8),s=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),(x=r[n]).high^=s,x.low^=o}for(var c=0;c<24;c++){for(var a=0;a<5;a++){for(var h=0,l=0,f=0;f<5;f++){h^=(x=r[a+5*f]).high,l^=x.low}var d=R[a];d.high=h,d.low=l}for(a=0;a<5;a++){var u=R[(a+4)%5],p=R[(a+1)%5],_=p.high,v=p.low;for(h=u.high^(_<<1|v>>>31),l=u.low^(v<<1|_>>>31),f=0;f<5;f++){(x=r[a+5*f]).high^=h,x.low^=l}}for(var y=1;y<25;y++){var g=(x=r[y]).high,B=x.low,w=C[y];l=w<32?(h=g<<w|B>>>32-w,B<<w|g>>>32-w):(h=B<<w-32|g>>>64-w,g<<w-32|B>>>64-w);var k=R[D[y]];k.high=h,k.low=l}var S=R[0],m=r[0];S.high=m.high,S.low=m.low;for(a=0;a<5;a++)for(f=0;f<5;f++){var x=r[y=a+5*f],b=R[y],H=R[(a+1)%5+5*f],z=R[(a+2)%5+5*f];x.high=b.high^~H.high&z.high,x.low=b.low^~H.low&z.low}x=r[0];var A=E[c];x.high^=A.high,x.low^=A.low}},_doFinalize:function(){var t=this._data,e=t.words,r=(this._nDataBytes,8*t.sigBytes),i=32*this.blockSize;e[r>>>5]|=1<<24-r%32,e[(d.ceil((1+r)/i)*i>>>5)-1]|=128,t.sigBytes=4*e.length,this._process();for(var n=this._state,o=this.cfg.outputLength/8,s=o/8,c=[],a=0;a<s;a++){var h=n[a],l=h.high,f=h.low;l=16711935&(l<<8|l>>>24)|4278255360&(l<<24|l>>>8),f=16711935&(f<<8|f>>>24)|4278255360&(f<<24|f>>>8),c.push(f),c.push(l)}return new u.init(c,o)},clone:function(){for(var t=i.clone.call(this),e=t._state=this._state.slice(0),r=0;r<25;r++)e[r]=e[r].clone();return t}});t.SHA3=i._createHelper(n),t.HmacSHA3=i._createHmacHelper(n)}(Math),function(){var t=bt,e=t.lib.Hasher,r=t.x64,i=r.Word,n=r.WordArray,o=t.algo;function s(){return i.create.apply(i,arguments)}var mt=[s(1116352408,3609767458),s(1899447441,602891725),s(3049323471,3964484399),s(3921009573,2173295548),s(961987163,4081628472),s(1508970993,3053834265),s(2453635748,2937671579),s(2870763221,3664609560),s(3624381080,2734883394),s(310598401,1164996542),s(607225278,1323610764),s(1426881987,3590304994),s(1925078388,4068182383),s(2162078206,991336113),s(2614888103,633803317),s(3248222580,3479774868),s(3835390401,2666613458),s(4022224774,944711139),s(264347078,2341262773),s(604807628,2007800933),s(770255983,1495990901),s(1249150122,1856431235),s(1555081692,3175218132),s(1996064986,2198950837),s(2554220882,3999719339),s(2821834349,766784016),s(2952996808,2566594879),s(3210313671,3203337956),s(3336571891,1034457026),s(3584528711,2466948901),s(113926993,3758326383),s(338241895,168717936),s(666307205,1188179964),s(773529912,1546045734),s(1294757372,1522805485),s(1396182291,2643833823),s(1695183700,2343527390),s(1986661051,1014477480),s(2177026350,1206759142),s(2456956037,344077627),s(2730485921,1290863460),s(2820302411,3158454273),s(3259730800,3505952657),s(3345764771,106217008),s(3516065817,3606008344),s(3600352804,1432725776),s(4094571909,1467031594),s(275423344,851169720),s(430227734,3100823752),s(506948616,1363258195),s(659060556,3750685593),s(883997877,3785050280),s(958139571,3318307427),s(1322822218,3812723403),s(1537002063,2003034995),s(1747873779,3602036899),s(1955562222,1575990012),s(2024104815,1125592928),s(2227730452,2716904306),s(2361852424,442776044),s(2428436474,593698344),s(2756734187,3733110249),s(3204031479,2999351573),s(3329325298,3815920427),s(3391569614,3928383900),s(3515267271,566280711),s(3940187606,3454069534),s(4118630271,4000239992),s(116418474,1914138554),s(174292421,2731055270),s(289380356,3203993006),s(460393269,320620315),s(685471733,587496836),s(852142971,1086792851),s(1017036298,365543100),s(1126000580,2618297676),s(1288033470,3409855158),s(1501505948,4234509866),s(1607167915,987167468),s(1816402316,1246189591)],xt=[];!function(){for(var t=0;t<80;t++)xt[t]=s()}();var c=o.SHA512=e.extend({_doReset:function(){this._hash=new n.init([new i.init(1779033703,4089235720),new i.init(3144134277,2227873595),new i.init(1013904242,4271175723),new i.init(2773480762,1595750129),new i.init(1359893119,2917565137),new i.init(2600822924,725511199),new i.init(528734635,4215389547),new i.init(1541459225,327033209)])},_doProcessBlock:function(t,e){for(var r=this._hash.words,i=r[0],n=r[1],o=r[2],s=r[3],c=r[4],a=r[5],h=r[6],l=r[7],f=i.high,d=i.low,u=n.high,p=n.low,_=o.high,v=o.low,y=s.high,g=s.low,B=c.high,w=c.low,k=a.high,S=a.low,m=h.high,x=h.low,b=l.high,H=l.low,z=f,A=d,C=u,D=p,E=_,R=v,M=y,F=g,P=B,W=w,O=k,I=S,U=m,K=x,X=b,L=H,j=0;j<80;j++){var N,T,q=xt[j];if(j<16)T=q.high=0|t[e+2*j],N=q.low=0|t[e+2*j+1];else{var Z=xt[j-15],V=Z.high,G=Z.low,J=(V>>>1|G<<31)^(V>>>8|G<<24)^V>>>7,$=(G>>>1|V<<31)^(G>>>8|V<<24)^(G>>>7|V<<25),Q=xt[j-2],Y=Q.high,tt=Q.low,et=(Y>>>19|tt<<13)^(Y<<3|tt>>>29)^Y>>>6,rt=(tt>>>19|Y<<13)^(tt<<3|Y>>>29)^(tt>>>6|Y<<26),it=xt[j-7],nt=it.high,ot=it.low,st=xt[j-16],ct=st.high,at=st.low;T=(T=(T=J+nt+((N=$+ot)>>>0<$>>>0?1:0))+et+((N+=rt)>>>0<rt>>>0?1:0))+ct+((N+=at)>>>0<at>>>0?1:0),q.high=T,q.low=N}var ht,lt=P&O^~P&U,ft=W&I^~W&K,dt=z&C^z&E^C&E,ut=A&D^A&R^D&R,pt=(z>>>28|A<<4)^(z<<30|A>>>2)^(z<<25|A>>>7),_t=(A>>>28|z<<4)^(A<<30|z>>>2)^(A<<25|z>>>7),vt=(P>>>14|W<<18)^(P>>>18|W<<14)^(P<<23|W>>>9),yt=(W>>>14|P<<18)^(W>>>18|P<<14)^(W<<23|P>>>9),gt=mt[j],Bt=gt.high,wt=gt.low,kt=X+vt+((ht=L+yt)>>>0<L>>>0?1:0),St=_t+ut;X=U,L=K,U=O,K=I,O=P,I=W,P=M+(kt=(kt=(kt=kt+lt+((ht=ht+ft)>>>0<ft>>>0?1:0))+Bt+((ht=ht+wt)>>>0<wt>>>0?1:0))+T+((ht=ht+N)>>>0<N>>>0?1:0))+((W=F+ht|0)>>>0<F>>>0?1:0)|0,M=E,F=R,E=C,R=D,C=z,D=A,z=kt+(pt+dt+(St>>>0<_t>>>0?1:0))+((A=ht+St|0)>>>0<ht>>>0?1:0)|0}d=i.low=d+A,i.high=f+z+(d>>>0<A>>>0?1:0),p=n.low=p+D,n.high=u+C+(p>>>0<D>>>0?1:0),v=o.low=v+R,o.high=_+E+(v>>>0<R>>>0?1:0),g=s.low=g+F,s.high=y+M+(g>>>0<F>>>0?1:0),w=c.low=w+W,c.high=B+P+(w>>>0<W>>>0?1:0),S=a.low=S+I,a.high=k+O+(S>>>0<I>>>0?1:0),x=h.low=x+K,h.high=m+U+(x>>>0<K>>>0?1:0),H=l.low=H+L,l.high=b+X+(H>>>0<L>>>0?1:0)},_doFinalize:function(){var t=this._data,e=t.words,r=8*this._nDataBytes,i=8*t.sigBytes;return e[i>>>5]|=128<<24-i%32,e[30+(128+i>>>10<<5)]=Math.floor(r/4294967296),e[31+(128+i>>>10<<5)]=r,t.sigBytes=4*e.length,this._process(),this._hash.toX32()},clone:function(){var t=e.clone.call(this);return t._hash=this._hash.clone(),t},blockSize:32});t.SHA512=e._createHelper(c),t.HmacSHA512=e._createHmacHelper(c)}(),Z=(q=bt).x64,V=Z.Word,G=Z.WordArray,J=q.algo,$=J.SHA512,Q=J.SHA384=$.extend({_doReset:function(){this._hash=new G.init([new V.init(3418070365,3238371032),new V.init(1654270250,914150663),new V.init(2438529370,812702999),new V.init(355462360,4144912697),new V.init(1731405415,4290775857),new V.init(2394180231,1750603025),new V.init(3675008525,1694076839),new V.init(1203062813,3204075428)])},_doFinalize:function(){var t=$._doFinalize.call(this);return t.sigBytes-=16,t}}),q.SHA384=$._createHelper(Q),q.HmacSHA384=$._createHmacHelper(Q),bt.lib.Cipher||function(){var t=bt,e=t.lib,r=e.Base,a=e.WordArray,i=e.BufferedBlockAlgorithm,n=t.enc,o=(n.Utf8,n.Base64),s=t.algo.EvpKDF,c=e.Cipher=i.extend({cfg:r.extend(),createEncryptor:function(t,e){return this.create(this._ENC_XFORM_MODE,t,e)},createDecryptor:function(t,e){return this.create(this._DEC_XFORM_MODE,t,e)},init:function(t,e,r){this.cfg=this.cfg.extend(r),this._xformMode=t,this._key=e,this.reset()},reset:function(){i.reset.call(this),this._doReset()},process:function(t){return this._append(t),this._process()},finalize:function(t){return t&&this._append(t),this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(i){return{encrypt:function(t,e,r){return h(e).encrypt(i,t,e,r)},decrypt:function(t,e,r){return h(e).decrypt(i,t,e,r)}}}});function h(t){return"string"==typeof t?w:g}e.StreamCipher=c.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var l,f=t.mode={},d=e.BlockCipherMode=r.extend({createEncryptor:function(t,e){return this.Encryptor.create(t,e)},createDecryptor:function(t,e){return this.Decryptor.create(t,e)},init:function(t,e){this._cipher=t,this._iv=e}}),u=f.CBC=((l=d.extend()).Encryptor=l.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize;p.call(this,t,e,i),r.encryptBlock(t,e),this._prevBlock=t.slice(e,e+i)}}),l.Decryptor=l.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=t.slice(e,e+i);r.decryptBlock(t,e),p.call(this,t,e,i),this._prevBlock=n}}),l);function p(t,e,r){var i,n=this._iv;n?(i=n,this._iv=void 0):i=this._prevBlock;for(var o=0;o<r;o++)t[e+o]^=i[o]}var _=(t.pad={}).Pkcs7={pad:function(t,e){for(var r=4*e,i=r-t.sigBytes%r,n=i<<24|i<<16|i<<8|i,o=[],s=0;s<i;s+=4)o.push(n);var c=a.create(o,i);t.concat(c)},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},v=(e.BlockCipher=c.extend({cfg:c.cfg.extend({mode:u,padding:_}),reset:function(){var t;c.reset.call(this);var e=this.cfg,r=e.iv,i=e.mode;this._xformMode==this._ENC_XFORM_MODE?t=i.createEncryptor:(t=i.createDecryptor,this._minBufferSize=1),this._mode&&this._mode.__creator==t?this._mode.init(this,r&&r.words):(this._mode=t.call(i,this,r&&r.words),this._mode.__creator=t)},_doProcessBlock:function(t,e){this._mode.processBlock(t,e)},_doFinalize:function(){var t,e=this.cfg.padding;return this._xformMode==this._ENC_XFORM_MODE?(e.pad(this._data,this.blockSize),t=this._process(!0)):(t=this._process(!0),e.unpad(t)),t},blockSize:4}),e.CipherParams=r.extend({init:function(t){this.mixIn(t)},toString:function(t){return(t||this.formatter).stringify(this)}})),y=(t.format={}).OpenSSL={stringify:function(t){var e=t.ciphertext,r=t.salt;return(r?a.create([1398893684,1701076831]).concat(r).concat(e):e).toString(o)},parse:function(t){var e,r=o.parse(t),i=r.words;return 1398893684==i[0]&&1701076831==i[1]&&(e=a.create(i.slice(2,4)),i.splice(0,4),r.sigBytes-=16),v.create({ciphertext:r,salt:e})}},g=e.SerializableCipher=r.extend({cfg:r.extend({format:y}),encrypt:function(t,e,r,i){i=this.cfg.extend(i);var n=t.createEncryptor(r,i),o=n.finalize(e),s=n.cfg;return v.create({ciphertext:o,key:r,iv:s.iv,algorithm:t,mode:s.mode,padding:s.padding,blockSize:t.blockSize,formatter:i.format})},decrypt:function(t,e,r,i){return i=this.cfg.extend(i),e=this._parse(e,i.format),t.createDecryptor(r,i).finalize(e.ciphertext)},_parse:function(t,e){return"string"==typeof t?e.parse(t,this):t}}),B=(t.kdf={}).OpenSSL={execute:function(t,e,r,i){i=i||a.random(8);var n=s.create({keySize:e+r}).compute(t,i),o=a.create(n.words.slice(e),4*r);return n.sigBytes=4*e,v.create({key:n,iv:o,salt:i})}},w=e.PasswordBasedCipher=g.extend({cfg:g.cfg.extend({kdf:B}),encrypt:function(t,e,r,i){var n=(i=this.cfg.extend(i)).kdf.execute(r,t.keySize,t.ivSize);i.iv=n.iv;var o=g.encrypt.call(this,t,e,n.key,i);return o.mixIn(n),o},decrypt:function(t,e,r,i){i=this.cfg.extend(i),e=this._parse(e,i.format);var n=i.kdf.execute(r,t.keySize,t.ivSize,e.salt);return i.iv=n.iv,g.decrypt.call(this,t,e,n.key,i)}})}(),bt.mode.CFB=((Y=bt.lib.BlockCipherMode.extend()).Encryptor=Y.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize;Dt.call(this,t,e,i,r),this._prevBlock=t.slice(e,e+i)}}),Y.Decryptor=Y.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=t.slice(e,e+i);Dt.call(this,t,e,i,r),this._prevBlock=n}}),Y),bt.mode.ECB=((tt=bt.lib.BlockCipherMode.extend()).Encryptor=tt.extend({processBlock:function(t,e){this._cipher.encryptBlock(t,e)}}),tt.Decryptor=tt.extend({processBlock:function(t,e){this._cipher.decryptBlock(t,e)}}),tt),bt.pad.AnsiX923={pad:function(t,e){var r=t.sigBytes,i=4*e,n=i-r%i,o=r+n-1;t.clamp(),t.words[o>>>2]|=n<<24-o%4*8,t.sigBytes+=n},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},bt.pad.Iso10126={pad:function(t,e){var r=4*e,i=r-t.sigBytes%r;t.concat(bt.lib.WordArray.random(i-1)).concat(bt.lib.WordArray.create([i<<24],1))},unpad:function(t){var e=255&t.words[t.sigBytes-1>>>2];t.sigBytes-=e}},bt.pad.Iso97971={pad:function(t,e){t.concat(bt.lib.WordArray.create([2147483648],1)),bt.pad.ZeroPadding.pad(t,e)},unpad:function(t){bt.pad.ZeroPadding.unpad(t),t.sigBytes--}},bt.mode.OFB=(et=bt.lib.BlockCipherMode.extend(),rt=et.Encryptor=et.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=this._iv,o=this._keystream;n&&(o=this._keystream=n.slice(0),this._iv=void 0),r.encryptBlock(o,0);for(var s=0;s<i;s++)t[e+s]^=o[s]}}),et.Decryptor=rt,et),bt.pad.NoPadding={pad:function(){},unpad:function(){}},it=bt.lib.CipherParams,nt=bt.enc.Hex,bt.format.Hex={stringify:function(t){return t.ciphertext.toString(nt)},parse:function(t){var e=nt.parse(t);return it.create({ciphertext:e})}},function(){var t=bt,e=t.lib.BlockCipher,r=t.algo,h=[],l=[],f=[],d=[],u=[],p=[],_=[],v=[],y=[],g=[];!function(){for(var t=[],e=0;e<256;e++)t[e]=e<128?e<<1:e<<1^283;var r=0,i=0;for(e=0;e<256;e++){var n=i^i<<1^i<<2^i<<3^i<<4;n=n>>>8^255&n^99,h[r]=n;var o=t[l[n]=r],s=t[o],c=t[s],a=257*t[n]^16843008*n;f[r]=a<<24|a>>>8,d[r]=a<<16|a>>>16,u[r]=a<<8|a>>>24,p[r]=a;a=16843009*c^65537*s^257*o^16843008*r;_[n]=a<<24|a>>>8,v[n]=a<<16|a>>>16,y[n]=a<<8|a>>>24,g[n]=a,r?(r=o^t[t[t[c^o]]],i^=t[t[i]]):r=i=1}}();var B=[0,1,2,4,8,16,32,64,128,27,54],i=r.AES=e.extend({_doReset:function(){if(!this._nRounds||this._keyPriorReset!==this._key){for(var t=this._keyPriorReset=this._key,e=t.words,r=t.sigBytes/4,i=4*(1+(this._nRounds=6+r)),n=this._keySchedule=[],o=0;o<i;o++)o<r?n[o]=e[o]:(a=n[o-1],o%r?6<r&&o%r==4&&(a=h[a>>>24]<<24|h[a>>>16&255]<<16|h[a>>>8&255]<<8|h[255&a]):(a=h[(a=a<<8|a>>>24)>>>24]<<24|h[a>>>16&255]<<16|h[a>>>8&255]<<8|h[255&a],a^=B[o/r|0]<<24),n[o]=n[o-r]^a);for(var s=this._invKeySchedule=[],c=0;c<i;c++){o=i-c;if(c%4)var a=n[o];else a=n[o-4];s[c]=c<4||o<=4?a:_[h[a>>>24]]^v[h[a>>>16&255]]^y[h[a>>>8&255]]^g[h[255&a]]}}},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._keySchedule,f,d,u,p,h)},decryptBlock:function(t,e){var r=t[e+1];t[e+1]=t[e+3],t[e+3]=r,this._doCryptBlock(t,e,this._invKeySchedule,_,v,y,g,l);r=t[e+1];t[e+1]=t[e+3],t[e+3]=r},_doCryptBlock:function(t,e,r,i,n,o,s,c){for(var a=this._nRounds,h=t[e]^r[0],l=t[e+1]^r[1],f=t[e+2]^r[2],d=t[e+3]^r[3],u=4,p=1;p<a;p++){var _=i[h>>>24]^n[l>>>16&255]^o[f>>>8&255]^s[255&d]^r[u++],v=i[l>>>24]^n[f>>>16&255]^o[d>>>8&255]^s[255&h]^r[u++],y=i[f>>>24]^n[d>>>16&255]^o[h>>>8&255]^s[255&l]^r[u++],g=i[d>>>24]^n[h>>>16&255]^o[l>>>8&255]^s[255&f]^r[u++];h=_,l=v,f=y,d=g}_=(c[h>>>24]<<24|c[l>>>16&255]<<16|c[f>>>8&255]<<8|c[255&d])^r[u++],v=(c[l>>>24]<<24|c[f>>>16&255]<<16|c[d>>>8&255]<<8|c[255&h])^r[u++],y=(c[f>>>24]<<24|c[d>>>16&255]<<16|c[h>>>8&255]<<8|c[255&l])^r[u++],g=(c[d>>>24]<<24|c[h>>>16&255]<<16|c[l>>>8&255]<<8|c[255&f])^r[u++];t[e]=_,t[e+1]=v,t[e+2]=y,t[e+3]=g},keySize:8});t.AES=e._createHelper(i)}(),function(){var t=bt,e=t.lib,n=e.WordArray,r=e.BlockCipher,i=t.algo,h=[57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4],l=[14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32],f=[1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28],d=[{0:8421888,268435456:32768,536870912:8421378,805306368:2,1073741824:512,1342177280:8421890,1610612736:8389122,1879048192:8388608,2147483648:514,2415919104:8389120,2684354560:33280,2952790016:8421376,3221225472:32770,3489660928:8388610,3758096384:0,4026531840:33282,134217728:0,402653184:8421890,671088640:33282,939524096:32768,1207959552:8421888,1476395008:512,1744830464:8421378,2013265920:2,2281701376:8389120,2550136832:33280,2818572288:8421376,3087007744:8389122,3355443200:8388610,3623878656:32770,3892314112:514,4160749568:8388608,1:32768,268435457:2,536870913:8421888,805306369:8388608,1073741825:8421378,1342177281:33280,1610612737:512,1879048193:8389122,2147483649:8421890,2415919105:8421376,2684354561:8388610,2952790017:33282,3221225473:514,3489660929:8389120,3758096385:32770,4026531841:0,134217729:8421890,402653185:8421376,671088641:8388608,939524097:512,1207959553:32768,1476395009:8388610,1744830465:2,2013265921:33282,2281701377:32770,2550136833:8389122,2818572289:514,3087007745:8421888,3355443201:8389120,3623878657:0,3892314113:33280,4160749569:8421378},{0:1074282512,16777216:16384,33554432:524288,50331648:1074266128,67108864:1073741840,83886080:1074282496,100663296:1073758208,117440512:16,134217728:540672,150994944:1073758224,167772160:1073741824,184549376:540688,201326592:524304,218103808:0,234881024:16400,251658240:1074266112,8388608:1073758208,25165824:540688,41943040:16,58720256:1073758224,75497472:1074282512,92274688:1073741824,109051904:524288,125829120:1074266128,142606336:524304,159383552:0,176160768:16384,192937984:1074266112,209715200:1073741840,226492416:540672,243269632:1074282496,260046848:16400,268435456:0,285212672:1074266128,301989888:1073758224,318767104:1074282496,335544320:1074266112,352321536:16,369098752:540688,385875968:16384,402653184:16400,419430400:524288,436207616:524304,452984832:1073741840,469762048:540672,486539264:1073758208,503316480:1073741824,520093696:1074282512,276824064:540688,293601280:524288,310378496:1074266112,327155712:16384,343932928:1073758208,360710144:1074282512,377487360:16,394264576:1073741824,411041792:1074282496,427819008:1073741840,444596224:1073758224,461373440:524304,478150656:0,494927872:16400,511705088:1074266128,528482304:540672},{0:260,1048576:0,2097152:67109120,3145728:65796,4194304:65540,5242880:67108868,6291456:67174660,7340032:67174400,8388608:67108864,9437184:67174656,10485760:65792,11534336:67174404,12582912:67109124,13631488:65536,14680064:4,15728640:256,524288:67174656,1572864:67174404,2621440:0,3670016:67109120,4718592:67108868,5767168:65536,6815744:65540,7864320:260,8912896:4,9961472:256,11010048:67174400,12058624:65796,13107200:65792,14155776:67109124,15204352:67174660,16252928:67108864,16777216:67174656,17825792:65540,18874368:65536,19922944:67109120,20971520:256,22020096:67174660,23068672:67108868,24117248:0,25165824:67109124,26214400:67108864,27262976:4,28311552:65792,29360128:67174400,30408704:260,31457280:65796,32505856:67174404,17301504:67108864,18350080:260,19398656:67174656,20447232:0,21495808:65540,22544384:67109120,23592960:256,24641536:67174404,25690112:65536,26738688:67174660,27787264:65796,28835840:67108868,29884416:67109124,30932992:67174400,31981568:4,33030144:65792},{0:2151682048,65536:2147487808,131072:4198464,196608:2151677952,262144:0,327680:4198400,393216:2147483712,458752:4194368,524288:2147483648,589824:4194304,655360:64,720896:2147487744,786432:2151678016,851968:4160,917504:4096,983040:2151682112,32768:2147487808,98304:64,163840:2151678016,229376:2147487744,294912:4198400,360448:2151682112,425984:0,491520:2151677952,557056:4096,622592:2151682048,688128:4194304,753664:4160,819200:2147483648,884736:4194368,950272:4198464,1015808:2147483712,1048576:4194368,1114112:4198400,1179648:2147483712,1245184:0,1310720:4160,1376256:2151678016,1441792:2151682048,1507328:2147487808,1572864:2151682112,1638400:2147483648,1703936:2151677952,1769472:4198464,1835008:2147487744,1900544:4194304,1966080:64,2031616:4096,1081344:2151677952,1146880:2151682112,1212416:0,1277952:4198400,1343488:4194368,1409024:2147483648,1474560:2147487808,1540096:64,1605632:2147483712,1671168:4096,1736704:2147487744,1802240:2151678016,1867776:4160,1933312:2151682048,1998848:4194304,2064384:4198464},{0:128,4096:17039360,8192:262144,12288:536870912,16384:537133184,20480:16777344,24576:553648256,28672:262272,32768:16777216,36864:537133056,40960:536871040,45056:553910400,49152:553910272,53248:0,57344:17039488,61440:553648128,2048:17039488,6144:553648256,10240:128,14336:17039360,18432:262144,22528:537133184,26624:553910272,30720:536870912,34816:537133056,38912:0,43008:553910400,47104:16777344,51200:536871040,55296:553648128,59392:16777216,63488:262272,65536:262144,69632:128,73728:536870912,77824:553648256,81920:16777344,86016:553910272,90112:537133184,94208:16777216,98304:553910400,102400:553648128,106496:17039360,110592:537133056,114688:262272,118784:536871040,122880:0,126976:17039488,67584:553648256,71680:16777216,75776:17039360,79872:537133184,83968:536870912,88064:17039488,92160:128,96256:553910272,100352:262272,104448:553910400,108544:0,112640:553648128,116736:16777344,120832:262144,124928:537133056,129024:536871040},{0:268435464,256:8192,512:270532608,768:270540808,1024:268443648,1280:2097152,1536:2097160,1792:268435456,2048:0,2304:268443656,2560:2105344,2816:8,3072:270532616,3328:2105352,3584:8200,3840:270540800,128:270532608,384:270540808,640:8,896:2097152,1152:2105352,1408:268435464,1664:268443648,1920:8200,2176:2097160,2432:8192,2688:268443656,2944:270532616,3200:0,3456:270540800,3712:2105344,3968:268435456,4096:268443648,4352:270532616,4608:270540808,4864:8200,5120:2097152,5376:268435456,5632:268435464,5888:2105344,6144:2105352,6400:0,6656:8,6912:270532608,7168:8192,7424:268443656,7680:270540800,7936:2097160,4224:8,4480:2105344,4736:2097152,4992:268435464,5248:268443648,5504:8200,5760:270540808,6016:270532608,6272:270540800,6528:270532616,6784:8192,7040:2105352,7296:2097160,7552:0,7808:268435456,8064:268443656},{0:1048576,16:33555457,32:1024,48:1049601,64:34604033,80:0,96:1,112:34603009,128:33555456,144:1048577,160:33554433,176:34604032,192:34603008,208:1025,224:1049600,240:33554432,8:34603009,24:0,40:33555457,56:34604032,72:1048576,88:33554433,104:33554432,120:1025,136:1049601,152:33555456,168:34603008,184:1048577,200:1024,216:34604033,232:1,248:1049600,256:33554432,272:1048576,288:33555457,304:34603009,320:1048577,336:33555456,352:34604032,368:1049601,384:1025,400:34604033,416:1049600,432:1,448:0,464:34603008,480:33554433,496:1024,264:1049600,280:33555457,296:34603009,312:1,328:33554432,344:1048576,360:1025,376:34604032,392:33554433,408:34603008,424:0,440:34604033,456:1049601,472:1024,488:33555456,504:1048577},{0:134219808,1:131072,2:134217728,3:32,4:131104,5:134350880,6:134350848,7:2048,8:134348800,9:134219776,10:133120,11:134348832,12:2080,13:0,14:134217760,15:133152,2147483648:2048,2147483649:134350880,2147483650:134219808,2147483651:134217728,2147483652:134348800,2147483653:133120,2147483654:133152,2147483655:32,2147483656:134217760,2147483657:2080,2147483658:131104,2147483659:134350848,2147483660:0,2147483661:134348832,2147483662:134219776,2147483663:131072,16:133152,17:134350848,18:32,19:2048,20:134219776,21:134217760,22:134348832,23:131072,24:0,25:131104,26:134348800,27:134219808,28:134350880,29:133120,30:2080,31:134217728,2147483664:131072,2147483665:2048,2147483666:134348832,2147483667:133152,2147483668:32,2147483669:134348800,2147483670:134217728,2147483671:134219808,2147483672:134350880,2147483673:134217760,2147483674:134219776,2147483675:0,2147483676:133120,2147483677:2080,2147483678:131104,2147483679:134350848}],u=[4160749569,528482304,33030144,2064384,129024,8064,504,2147483679],o=i.DES=r.extend({_doReset:function(){for(var t=this._key.words,e=[],r=0;r<56;r++){var i=h[r]-1;e[r]=t[i>>>5]>>>31-i%32&1}for(var n=this._subKeys=[],o=0;o<16;o++){var s=n[o]=[],c=f[o];for(r=0;r<24;r++)s[r/6|0]|=e[(l[r]-1+c)%28]<<31-r%6,s[4+(r/6|0)]|=e[28+(l[r+24]-1+c)%28]<<31-r%6;s[0]=s[0]<<1|s[0]>>>31;for(r=1;r<7;r++)s[r]=s[r]>>>4*(r-1)+3;s[7]=s[7]<<5|s[7]>>>27}var a=this._invSubKeys=[];for(r=0;r<16;r++)a[r]=n[15-r]},encryptBlock:function(t,e){this._doCryptBlock(t,e,this._subKeys)},decryptBlock:function(t,e){this._doCryptBlock(t,e,this._invSubKeys)},_doCryptBlock:function(t,e,r){this._lBlock=t[e],this._rBlock=t[e+1],p.call(this,4,252645135),p.call(this,16,65535),_.call(this,2,858993459),_.call(this,8,16711935),p.call(this,1,1431655765);for(var i=0;i<16;i++){for(var n=r[i],o=this._lBlock,s=this._rBlock,c=0,a=0;a<8;a++)c|=d[a][((s^n[a])&u[a])>>>0];this._lBlock=s,this._rBlock=o^c}var h=this._lBlock;this._lBlock=this._rBlock,this._rBlock=h,p.call(this,1,1431655765),_.call(this,8,16711935),_.call(this,2,858993459),p.call(this,16,65535),p.call(this,4,252645135),t[e]=this._lBlock,t[e+1]=this._rBlock},keySize:2,ivSize:2,blockSize:2});function p(t,e){var r=(this._lBlock>>>t^this._rBlock)&e;this._rBlock^=r,this._lBlock^=r<<t}function _(t,e){var r=(this._rBlock>>>t^this._lBlock)&e;this._lBlock^=r,this._rBlock^=r<<t}t.DES=r._createHelper(o);var s=i.TripleDES=r.extend({_doReset:function(){var t=this._key.words;if(2!==t.length&&4!==t.length&&t.length<6)throw new Error("Invalid key length - 3DES requires the key length to be 64, 128, 192 or >192.");var e=t.slice(0,2),r=t.length<4?t.slice(0,2):t.slice(2,4),i=t.length<6?t.slice(0,2):t.slice(4,6);this._des1=o.createEncryptor(n.create(e)),this._des2=o.createEncryptor(n.create(r)),this._des3=o.createEncryptor(n.create(i))},encryptBlock:function(t,e){this._des1.encryptBlock(t,e),this._des2.decryptBlock(t,e),this._des3.encryptBlock(t,e)},decryptBlock:function(t,e){this._des3.decryptBlock(t,e),this._des2.encryptBlock(t,e),this._des1.decryptBlock(t,e)},keySize:6,ivSize:2,blockSize:2});t.TripleDES=r._createHelper(s)}(),function(){var t=bt,e=t.lib.StreamCipher,r=t.algo,i=r.RC4=e.extend({_doReset:function(){for(var t=this._key,e=t.words,r=t.sigBytes,i=this._S=[],n=0;n<256;n++)i[n]=n;n=0;for(var o=0;n<256;n++){var s=n%r,c=e[s>>>2]>>>24-s%4*8&255;o=(o+i[n]+c)%256;var a=i[n];i[n]=i[o],i[o]=a}this._i=this._j=0},_doProcessBlock:function(t,e){t[e]^=n.call(this)},keySize:8,ivSize:0});function n(){for(var t=this._S,e=this._i,r=this._j,i=0,n=0;n<4;n++){r=(r+t[e=(e+1)%256])%256;var o=t[e];t[e]=t[r],t[r]=o,i|=t[(t[e]+t[r])%256]<<24-8*n}return this._i=e,this._j=r,i}t.RC4=e._createHelper(i);var o=r.RC4Drop=i.extend({cfg:i.cfg.extend({drop:192}),_doReset:function(){i._doReset.call(this);for(var t=this.cfg.drop;0<t;t--)n.call(this)}});t.RC4Drop=e._createHelper(o)}(),bt.mode.CTRGladman=(ot=bt.lib.BlockCipherMode.extend(),st=ot.Encryptor=ot.extend({processBlock:function(t,e){var r,i=this._cipher,n=i.blockSize,o=this._iv,s=this._counter;o&&(s=this._counter=o.slice(0),this._iv=void 0),0===((r=s)[0]=Et(r[0]))&&(r[1]=Et(r[1]));var c=s.slice(0);i.encryptBlock(c,0);for(var a=0;a<n;a++)t[e+a]^=c[a]}}),ot.Decryptor=st,ot),at=(ct=bt).lib.StreamCipher,ht=ct.algo,lt=[],ft=[],dt=[],ut=ht.Rabbit=at.extend({_doReset:function(){for(var t=this._key.words,e=this.cfg.iv,r=0;r<4;r++)t[r]=16711935&(t[r]<<8|t[r]>>>24)|4278255360&(t[r]<<24|t[r]>>>8);var i=this._X=[t[0],t[3]<<16|t[2]>>>16,t[1],t[0]<<16|t[3]>>>16,t[2],t[1]<<16|t[0]>>>16,t[3],t[2]<<16|t[1]>>>16],n=this._C=[t[2]<<16|t[2]>>>16,4294901760&t[0]|65535&t[1],t[3]<<16|t[3]>>>16,4294901760&t[1]|65535&t[2],t[0]<<16|t[0]>>>16,4294901760&t[2]|65535&t[3],t[1]<<16|t[1]>>>16,4294901760&t[3]|65535&t[0]];for(r=this._b=0;r<4;r++)Rt.call(this);for(r=0;r<8;r++)n[r]^=i[r+4&7];if(e){var o=e.words,s=o[0],c=o[1],a=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),h=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8),l=a>>>16|4294901760&h,f=h<<16|65535&a;n[0]^=a,n[1]^=l,n[2]^=h,n[3]^=f,n[4]^=a,n[5]^=l,n[6]^=h,n[7]^=f;for(r=0;r<4;r++)Rt.call(this)}},_doProcessBlock:function(t,e){var r=this._X;Rt.call(this),lt[0]=r[0]^r[5]>>>16^r[3]<<16,lt[1]=r[2]^r[7]>>>16^r[5]<<16,lt[2]=r[4]^r[1]>>>16^r[7]<<16,lt[3]=r[6]^r[3]>>>16^r[1]<<16;for(var i=0;i<4;i++)lt[i]=16711935&(lt[i]<<8|lt[i]>>>24)|4278255360&(lt[i]<<24|lt[i]>>>8),t[e+i]^=lt[i]},blockSize:4,ivSize:2}),ct.Rabbit=at._createHelper(ut),bt.mode.CTR=(pt=bt.lib.BlockCipherMode.extend(),_t=pt.Encryptor=pt.extend({processBlock:function(t,e){var r=this._cipher,i=r.blockSize,n=this._iv,o=this._counter;n&&(o=this._counter=n.slice(0),this._iv=void 0);var s=o.slice(0);r.encryptBlock(s,0),o[i-1]=o[i-1]+1|0;for(var c=0;c<i;c++)t[e+c]^=s[c]}}),pt.Decryptor=_t,pt),yt=(vt=bt).lib.StreamCipher,gt=vt.algo,Bt=[],wt=[],kt=[],St=gt.RabbitLegacy=yt.extend({_doReset:function(){for(var t=this._key.words,e=this.cfg.iv,r=this._X=[t[0],t[3]<<16|t[2]>>>16,t[1],t[0]<<16|t[3]>>>16,t[2],t[1]<<16|t[0]>>>16,t[3],t[2]<<16|t[1]>>>16],i=this._C=[t[2]<<16|t[2]>>>16,4294901760&t[0]|65535&t[1],t[3]<<16|t[3]>>>16,4294901760&t[1]|65535&t[2],t[0]<<16|t[0]>>>16,4294901760&t[2]|65535&t[3],t[1]<<16|t[1]>>>16,4294901760&t[3]|65535&t[0]],n=this._b=0;n<4;n++)Mt.call(this);for(n=0;n<8;n++)i[n]^=r[n+4&7];if(e){var o=e.words,s=o[0],c=o[1],a=16711935&(s<<8|s>>>24)|4278255360&(s<<24|s>>>8),h=16711935&(c<<8|c>>>24)|4278255360&(c<<24|c>>>8),l=a>>>16|4294901760&h,f=h<<16|65535&a;i[0]^=a,i[1]^=l,i[2]^=h,i[3]^=f,i[4]^=a,i[5]^=l,i[6]^=h,i[7]^=f;for(n=0;n<4;n++)Mt.call(this)}},_doProcessBlock:function(t,e){var r=this._X;Mt.call(this),Bt[0]=r[0]^r[5]>>>16^r[3]<<16,Bt[1]=r[2]^r[7]>>>16^r[5]<<16,Bt[2]=r[4]^r[1]>>>16^r[7]<<16,Bt[3]=r[6]^r[3]>>>16^r[1]<<16;for(var i=0;i<4;i++)Bt[i]=16711935&(Bt[i]<<8|Bt[i]>>>24)|4278255360&(Bt[i]<<24|Bt[i]>>>8),t[e+i]^=Bt[i]},blockSize:4,ivSize:2}),vt.RabbitLegacy=yt._createHelper(St),bt.pad.ZeroPadding={pad:function(t,e){var r=4*e;t.clamp(),t.sigBytes+=r-(t.sigBytes%r||r)},unpad:function(t){var e=t.words,r=t.sigBytes-1;for(r=t.sigBytes-1;0<=r;r--)if(e[r>>>2]>>>24-r%4*8&255){t.sigBytes=r+1;break}}},bt}); |
| 'use strict' | ||
| /** | ||
| * 所有 HTTP 请求方法合法值 | ||
| * From http.METHODS | ||
| * @see https://nodejs.org/api/http.html#http_http_methods | ||
| */ | ||
| const ALL_METHODS = [ | ||
| 'ACL', | ||
| 'BIND', | ||
| 'CHECKOUT', | ||
| 'CONNECT', | ||
| 'COPY', | ||
| 'DELETE', | ||
| 'GET', | ||
| 'HEAD', | ||
| 'LINK', | ||
| 'LOCK', | ||
| 'M-SEARCH', | ||
| 'MERGE', | ||
| 'MKACTIVITY', | ||
| 'MKCALENDAR', | ||
| 'MKCOL', | ||
| 'MOVE', | ||
| 'NOTIFY', | ||
| 'OPTIONS', | ||
| 'PATCH', | ||
| 'POST', | ||
| 'PROPFIND', | ||
| 'PROPPATCH', | ||
| 'PURGE', | ||
| 'PUT', | ||
| 'REBIND', | ||
| 'REPORT', | ||
| 'SEARCH', | ||
| 'SOURCE', | ||
| 'SUBSCRIBE', | ||
| 'TRACE', | ||
| 'UNBIND', | ||
| 'UNLINK', | ||
| 'UNLOCK', | ||
| 'UNSUBSCRIBE', | ||
| ] | ||
| /** | ||
| * 常用方法 | ||
| */ | ||
| const COMMON_METHODS = ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'] | ||
| module.exports = ALL_METHODS |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1
-50%1
-50%27715
-76.78%2
Infinity%16
-44.83%464
-72.96%326
-33.06%+ Added
+ Added
+ Added
+ Added
+ Added