/*! * axios-miniprogram-adapter 0.3.4 (https://github.com/bigMeow/axios-miniprogram-adapter) * API https://github.com/bigMeow/axios-miniprogram-adapter/blob/master/doc/api.md * Copyright 2018-2022 bigMeow. All Rights Reserved * Licensed under MIT (https://github.com/bigMeow/axios-miniprogram-adapter/blob/master/LICENSE) */ 'use strict'; var bind = function bind(fn, thisArg) { return function wrap() { var args = new Array(arguments.length); for (var i = 0; i < args.length; i++) { args[i] = arguments[i]; } return fn.apply(thisArg, args); }; }; /*global toString:true*/ // utils is a library of generic helper functions non-specific to axios var toString = Object.prototype.toString; /** * Determine if a value is an Array * * @param {Object} val The value to test * @returns {boolean} True if value is an Array, otherwise false */ function isArray(val) { return toString.call(val) === '[object Array]'; } /** * Determine if a value is undefined * * @param {Object} val The value to test * @returns {boolean} True if the value is undefined, otherwise false */ function isUndefined(val) { return typeof val === 'undefined'; } /** * Determine if a value is a Buffer * * @param {Object} val The value to test * @returns {boolean} True if value is a Buffer, otherwise false */ function isBuffer(val) { return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor) && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val); } /** * Determine if a value is an ArrayBuffer * * @param {Object} val The value to test * @returns {boolean} True if value is an ArrayBuffer, otherwise false */ function isArrayBuffer(val) { return toString.call(val) === '[object ArrayBuffer]'; } /** * Determine if a value is a FormData * * @param {Object} val The value to test * @returns {boolean} True if value is an FormData, otherwise false */ function isFormData(val) { return (typeof FormData !== 'undefined') && (val instanceof FormData); } /** * Determine if a value is a view on an ArrayBuffer * * @param {Object} val The value to test * @returns {boolean} True if value is a view on an ArrayBuffer, otherwise false */ function isArrayBufferView(val) { var result; if ((typeof ArrayBuffer !== 'undefined') && (ArrayBuffer.isView)) { result = ArrayBuffer.isView(val); } else { result = (val) && (val.buffer) && (val.buffer instanceof ArrayBuffer); } return result; } /** * Determine if a value is a String * * @param {Object} val The value to test * @returns {boolean} True if value is a String, otherwise false */ function isString(val) { return typeof val === 'string'; } /** * Determine if a value is a Number * * @param {Object} val The value to test * @returns {boolean} True if value is a Number, otherwise false */ function isNumber(val) { return typeof val === 'number'; } /** * Determine if a value is an Object * * @param {Object} val The value to test * @returns {boolean} True if value is an Object, otherwise false */ function isObject(val) { return val !== null && typeof val === 'object'; } /** * Determine if a value is a Date * * @param {Object} val The value to test * @returns {boolean} True if value is a Date, otherwise false */ function isDate(val) { return toString.call(val) === '[object Date]'; } /** * Determine if a value is a File * * @param {Object} val The value to test * @returns {boolean} True if value is a File, otherwise false */ function isFile(val) { return toString.call(val) === '[object File]'; } /** * Determine if a value is a Blob * * @param {Object} val The value to test * @returns {boolean} True if value is a Blob, otherwise false */ function isBlob(val) { return toString.call(val) === '[object Blob]'; } /** * Determine if a value is a Function * * @param {Object} val The value to test * @returns {boolean} True if value is a Function, otherwise false */ function isFunction(val) { return toString.call(val) === '[object Function]'; } /** * Determine if a value is a Stream * * @param {Object} val The value to test * @returns {boolean} True if value is a Stream, otherwise false */ function isStream(val) { return isObject(val) && isFunction(val.pipe); } /** * Determine if a value is a URLSearchParams object * * @param {Object} val The value to test * @returns {boolean} True if value is a URLSearchParams object, otherwise false */ function isURLSearchParams(val) { return typeof URLSearchParams !== 'undefined' && val instanceof URLSearchParams; } /** * Trim excess whitespace off the beginning and end of a string * * @param {String} str The String to trim * @returns {String} The String freed of excess whitespace */ function trim(str) { return str.replace(/^\s*/, '').replace(/\s*$/, ''); } /** * Determine if we're running in a standard browser environment * * This allows axios to run in a web worker, and react-native. * Both environments support XMLHttpRequest, but not fully standard globals. * * web workers: * typeof window -> undefined * typeof document -> undefined * * react-native: * navigator.product -> 'ReactNative' * nativescript * navigator.product -> 'NativeScript' or 'NS' */ function isStandardBrowserEnv() { if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' || navigator.product === 'NativeScript' || navigator.product === 'NS')) { return false; } return ( typeof window !== 'undefined' && typeof document !== 'undefined' ); } /** * Iterate over an Array or an Object invoking a function for each item. * * If `obj` is an Array callback will be called passing * the value, index, and complete array for each item. * * If 'obj' is an Object callback will be called passing * the value, key, and complete object for each property. * * @param {Object|Array} obj The object to iterate * @param {Function} fn The callback to invoke for each item */ function forEach(obj, fn) { // Don't bother if no value provided if (obj === null || typeof obj === 'undefined') { return; } // Force an array if not already something iterable if (typeof obj !== 'object') { /*eslint no-param-reassign:0*/ obj = [obj]; } if (isArray(obj)) { // Iterate over array values for (var i = 0, l = obj.length; i < l; i++) { fn.call(null, obj[i], i, obj); } } else { // Iterate over object keys for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { fn.call(null, obj[key], key, obj); } } } } /** * Accepts varargs expecting each argument to be an object, then * immutably merges the properties of each object and returns result. * * When multiple objects contain the same key the later object in * the arguments list will take precedence. * * Example: * * ```js * var result = merge({foo: 123}, {foo: 456}); * console.log(result.foo); // outputs 456 * ``` * * @param {Object} obj1 Object to merge * @returns {Object} Result of all merge properties */ function merge(/* obj1, obj2, obj3, ... */) { var result = {}; function assignValue(val, key) { if (typeof result[key] === 'object' && typeof val === 'object') { result[key] = merge(result[key], val); } else { result[key] = val; } } for (var i = 0, l = arguments.length; i < l; i++) { forEach(arguments[i], assignValue); } return result; } /** * Function equal to merge with the difference being that no reference * to original objects is kept. * * @see merge * @param {Object} obj1 Object to merge * @returns {Object} Result of all merge properties */ function deepMerge(/* obj1, obj2, obj3, ... */) { var result = {}; function assignValue(val, key) { if (typeof result[key] === 'object' && typeof val === 'object') { result[key] = deepMerge(result[key], val); } else if (typeof val === 'object') { result[key] = deepMerge({}, val); } else { result[key] = val; } } for (var i = 0, l = arguments.length; i < l; i++) { forEach(arguments[i], assignValue); } return result; } /** * Extends object a by mutably adding to it the properties of object b. * * @param {Object} a The object to be extended * @param {Object} b The object to copy properties from * @param {Object} thisArg The object to bind function to * @return {Object} The resulting value of object a */ function extend(a, b, thisArg) { forEach(b, function assignValue(val, key) { if (thisArg && typeof val === 'function') { a[key] = bind(val, thisArg); } else { a[key] = val; } }); return a; } var utils = { isArray: isArray, isArrayBuffer: isArrayBuffer, isBuffer: isBuffer, isFormData: isFormData, isArrayBufferView: isArrayBufferView, isString: isString, isNumber: isNumber, isObject: isObject, isUndefined: isUndefined, isDate: isDate, isFile: isFile, isBlob: isBlob, isFunction: isFunction, isStream: isStream, isURLSearchParams: isURLSearchParams, isStandardBrowserEnv: isStandardBrowserEnv, forEach: forEach, merge: merge, deepMerge: deepMerge, extend: extend, trim: trim }; /** * Update an Error with the specified config, error code, and response. * * @param {Error} error The error to update. * @param {Object} config The config. * @param {string} [code] The error code (for example, 'ECONNABORTED'). * @param {Object} [request] The request. * @param {Object} [response] The response. * @returns {Error} The error. */ var enhanceError = function enhanceError(error, config, code, request, response) { error.config = config; if (code) { error.code = code; } error.request = request; error.response = response; error.isAxiosError = true; error.toJSON = function() { return { // Standard message: this.message, name: this.name, // Microsoft description: this.description, number: this.number, // Mozilla fileName: this.fileName, lineNumber: this.lineNumber, columnNumber: this.columnNumber, stack: this.stack, // Axios config: this.config, code: this.code }; }; return error; }; /** * Create an Error with the specified message, config, error code, request and response. * * @param {string} message The error message. * @param {Object} config The config. * @param {string} [code] The error code (for example, 'ECONNABORTED'). * @param {Object} [request] The request. * @param {Object} [response] The response. * @returns {Error} The created error. */ var createError = function createError(message, config, code, request, response) { var error = new Error(message); return enhanceError(error, config, code, request, response); }; /** * Resolve or reject a Promise based on response status. * * @param {Function} resolve A function that resolves the promise. * @param {Function} reject A function that rejects the promise. * @param {object} response The response. */ var settle = function settle(resolve, reject, response) { var validateStatus = response.config.validateStatus; if (!validateStatus || validateStatus(response.status)) { resolve(response); } else { reject(createError( 'Request failed with status code ' + response.status, response.config, null, response.request, response )); } }; function encode(val) { return encodeURIComponent(val). replace(/%40/gi, '@'). replace(/%3A/gi, ':'). replace(/%24/g, '$'). replace(/%2C/gi, ','). replace(/%20/g, '+'). replace(/%5B/gi, '['). replace(/%5D/gi, ']'); } /** * Build a URL by appending params to the end * * @param {string} url The base of the url (e.g., http://www.google.com) * @param {object} [params] The params to be appended * @returns {string} The formatted url */ var buildURL = function buildURL(url, params, paramsSerializer) { /*eslint no-param-reassign:0*/ if (!params) { return url; } var serializedParams; if (paramsSerializer) { serializedParams = paramsSerializer(params); } else if (utils.isURLSearchParams(params)) { serializedParams = params.toString(); } else { var parts = []; utils.forEach(params, function serialize(val, key) { if (val === null || typeof val === 'undefined') { return; } if (utils.isArray(val)) { key = key + '[]'; } else { val = [val]; } utils.forEach(val, function parseValue(v) { if (utils.isDate(v)) { v = v.toISOString(); } else if (utils.isObject(v)) { v = JSON.stringify(v); } parts.push(encode(key) + '=' + encode(v)); }); }); serializedParams = parts.join('&'); } if (serializedParams) { var hashmarkIndex = url.indexOf('#'); if (hashmarkIndex !== -1) { url = url.slice(0, hashmarkIndex); } url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams; } return url; }; /** * Determines whether the specified URL is absolute * * @param {string} url The URL to test * @returns {boolean} True if the specified URL is absolute, otherwise false */ var isAbsoluteURL = function isAbsoluteURL(url) { // A URL is considered absolute if it begins with "://" or "//" (protocol-relative URL). // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed // by any combination of letters, digits, plus, period, or hyphen. return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url); }; /** * Creates a new URL by combining the specified URLs * * @param {string} baseURL The base URL * @param {string} relativeURL The relative URL * @returns {string} The combined URL */ var combineURLs = function combineURLs(baseURL, relativeURL) { return relativeURL ? baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '') : baseURL; }; /** * Creates a new URL by combining the baseURL with the requestedURL, * only when the requestedURL is not already an absolute URL. * If the requestURL is absolute, this function returns the requestedURL untouched. * * @param {string} baseURL The base URL * @param {string} requestedURL Absolute or relative URL to combine * @returns {string} The combined full path */ var buildFullPath = function buildFullPath(baseURL, requestedURL) { if (baseURL && !isAbsoluteURL(requestedURL)) { return combineURLs(baseURL, requestedURL); } return requestedURL; }; var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; // encoder function encoder(input) { var str = String(input); // initialize result and counter var block; var charCode; var idx = 0; var map = chars; var output = ''; for (; // if the next str index does not exist: // change the mapping table to "=" // check if d has no fractional digits str.charAt(idx | 0) || (map = '=', idx % 1); // "8 - idx % 1 * 8" generates the sequence 2, 4, 6, 8 output += map.charAt(63 & block >> 8 - idx % 1 * 8)) { charCode = str.charCodeAt(idx += 3 / 4); if (charCode > 0xFF) { throw new Error("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range."); } block = block << 8 | charCode; } return output; } var platFormName = "wechat" /* 微信 */; /** * 获取各个平台的请求函数 */ function getRequest() { switch (true) { case typeof wx === 'object': platFormName = "wechat" /* 微信 */; return wx.request.bind(wx); case typeof swan === 'object': platFormName = "baidu" /* 百度 */; return swan.request.bind(swan); case typeof dd === 'object': platFormName = "dd" /* 钉钉 */; // https://open.dingtalk.com/document/orgapp-client/send-network-requests return dd.httpRequest.bind(dd); case typeof my === 'object': /** * remark: * 支付宝客户端已不再维护 my.httpRequest,建议使用 my.request。另外,钉钉客户端尚不支持 my.request。若在钉钉客户端开发小程序,则需要使用 my.httpRequest。 * my.httpRequest的请求头默认值为{'content-type': 'application/x-www-form-urlencoded'}。 * my.request的请求头默认值为{'content-type': 'application/json'}。 * 还有个 dd.httpRequest */ platFormName = "alipay" /* 支付宝 */; return (my.request || my.httpRequest).bind(my); default: return wx.request.bind(wx); } } /** * 处理各平台返回的响应数据,抹平差异 * @param mpResponse * @param config axios处理过的请求配置对象 * @param request 小程序的调用发起请求时,传递给小程序api的实际配置 */ function transformResponse(mpResponse, config, mpRequestOption) { var headers = mpResponse.header || mpResponse.headers; var status = mpResponse.statusCode || mpResponse.status; var statusText = ''; if (status === 200) { statusText = 'OK'; } else if (status === 400) { statusText = 'Bad Request'; } var response = { data: mpResponse.data, status: status, statusText: statusText, headers: headers, config: config, request: mpRequestOption }; return response; } /** * 处理各平台返回的错误信息,抹平差异 * @param error 小程序api返回的错误对象 * @param reject 上层的promise reject 函数 * @param config */ function transformError(error, reject, config) { switch (platFormName) { case "wechat" /* 微信 */: if (error.errMsg.indexOf('request:fail abort') !== -1) { // Handle request cancellation (as opposed to a manual cancellation) reject(createError('Request aborted', config, 'ECONNABORTED', '')); } else if (error.errMsg.indexOf('timeout') !== -1) { // timeout reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', '')); } else { // NetWordError reject(createError('Network Error', config, null, '')); } break; case "dd" /* 钉钉 */: case "alipay" /* 支付宝 */: // https://docs.alipay.com/mini/api/network if ([14, 19].includes(error.error)) { reject(createError('Request aborted', config, 'ECONNABORTED', '', error)); } else if ([13].includes(error.error)) { // timeout reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', '', error)); } else { // NetWordError reject(createError('Network Error', config, null, '', error)); } break; case "baidu" /* 百度 */: // TODO error.errCode reject(createError('Network Error', config, null, '')); break; } } /** * 将axios的请求配置,转换成各个平台都支持的请求config * @param config */ function transformConfig(config) { var _a; if (["alipay" /* 支付宝 */, "dd" /* 钉钉 */].includes(platFormName)) { config.headers = config.header; delete config.header; if ("dd" /* 钉钉 */ === platFormName && config.method !== "GET" && ((_a = config.headers) === null || _a === void 0 ? void 0 : _a['Content-Type']) === 'application/json' && Object.prototype.toString.call(config.data) === '[object Object]') { // Content-Type为application/json时,data参数只支持json字符串,需要手动调用JSON.stringify进行序列化 config.data = JSON.stringify(config.data); } } return config; } var isJSONstr = function (str) { try { return typeof str === 'string' && str.length && (str = JSON.parse(str)) && Object.prototype.toString.call(str) === '[object Object]'; } catch (error) { return false; } }; function mpAdapter(config) { var request = getRequest(); return new Promise(function (resolve, reject) { var requestTask; var requestData = config.data; var requestHeaders = config.headers; // baidu miniprogram only support upperCase var requestMethod = (config.method && config.method.toUpperCase()) || 'GET'; // miniprogram network request config var mpRequestOption = { method: requestMethod, url: buildURL(buildFullPath(config.baseURL, config.url), config.params, config.paramsSerializer), timeout: config.timeout, // Listen for success success: function (mpResponse) { var response = transformResponse(mpResponse, config, mpRequestOption); settle(resolve, reject, response); }, // Handle request Exception fail: function (error) { transformError(error, reject, config); }, complete: function () { requestTask = undefined; } }; // HTTP basic authentication if (config.auth) { var _a = [config.auth.username || '', config.auth.password || ''], username = _a[0], password = _a[1]; requestHeaders.Authorization = 'Basic ' + encoder(username + ':' + password); } // Add headers to the request utils.forEach(requestHeaders, function setRequestHeader(val, key) { var _header = key.toLowerCase(); if ((typeof requestData === 'undefined' && _header === 'content-type') || _header === 'referer') { // Remove Content-Type if data is undefined // And the miniprogram document said that '设置请求的 header,header 中不能设置 Referer' delete requestHeaders[key]; } }); mpRequestOption.header = requestHeaders; // Add responseType to request if needed if (config.responseType) { mpRequestOption.responseType = config.responseType; } if (config.cancelToken) { // Handle cancellation config.cancelToken.promise.then(function onCanceled(cancel) { if (!requestTask) { return; } requestTask.abort(); reject(cancel); // Clean up request requestTask = undefined; }); } // Converting JSON strings to objects is handed over to the MiniPrograme if (isJSONstr(requestData)) { requestData = JSON.parse(requestData); } if (requestData !== undefined) { mpRequestOption.data = requestData; } requestTask = request(transformConfig(mpRequestOption)); }); } module.exports = mpAdapter; module.exports.default = mpAdapter;