first commit

This commit is contained in:
2026-09-08 20:28:54 +08:00
commit 2ada8d3d5a
37380 changed files with 4886169 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
//支付宝小程序适配器
const statusList = {
11: '无权跨域',
12: '网络出错',
13: '超时',
14: '解码失败',
19: 'HTTP错误'
}
module.exports = function (request, responseCallback) {
let con = {
method: request.method,
url: request.url,
dataType: 'text',
headers: request.headers,
data: request.body || {},
timeout: request.timeout || 20000,
success(res) {
responseCallback({
statusCode: res.status,
responseText: res.data,
headers: res.headers
})
},
fail(res) {
responseCallback({
statusCode: res.status || 0,
responseText: res.data,
headers: res.headers,
errMsg: statusList[res.status] || ""
})
}
}
my.httpRequest(con)
}
+23
View File
@@ -0,0 +1,23 @@
let handleImgBase64Data=require("../utils/ImgBase64Handler")
let adapter;
if (window.dsBridge) {
adapter = function (request, responseCallBack) {
dsBridge.call("onAjaxRequest", request, function (responseData) {
responseData = JSON.parse(responseData);
if(request.responseType==="stream") {
handleImgBase64Data(responseData);
}
responseCallBack(responseData)
})
}
} else {
console.error("dsBridge is not exist!")
}
//build环境定义全局变量
KEEP("cdn||cdn-min", () => {
window.dsbAdapter = adapter
})
module.exports = adapter;
+22
View File
@@ -0,0 +1,22 @@
//hap adapter
module.exports = function (fetch) {
return function (request, responseCallback) {
request.data = request.body;
request.header = request.headers;
request.complete = (ret) => {
if (typeof ret !== "object") {
ret = {
code: 0,
msg: ret
}
}
responseCallback({
statusCode: ret.code || 0,
responseText: ret.data,
headers: ret.headers,
statusMessage: ret.msg
})
}
fetch.fetch(request)
}
}
+42
View File
@@ -0,0 +1,42 @@
let pkg = require('./../../package.json');
let req = require("request");
module.exports = function (request, responseCallBack) {
//自动管理cookie
let config = {jar: true, proxy: request.proxy}
let rq = req.defaults(config)
let headers = request.headers;
if (!headers['User-Agent'] && !headers['user-agent']) {
headers['User-Agent'] = 'fly/' + pkg.version;
}
//支持gzip
request.gzip = true;
if (request.hasOwnProperty("timeout") && request.timeout < 1) {
//use request lib default timeout
delete request.timeout;
}
let ret = {
statusCode: 0
}
if (request.responseType === "stream") {
delete request.responseType;
request.encoding=null;
}
rq(request, function (error, response, body) {
if (error) {
ret.statusMessage = error.message
} else {
ret.statusCode = response.statusCode
ret.responseText = body;
ret.headers = response.headers;
ret.statusMessage = response.statusMessage;
ret.response = response;
}
responseCallBack(ret)
})
}
+32
View File
@@ -0,0 +1,32 @@
let handleImgBase64Data=require("../utils/ImgBase64Handler")
// Reference from https://github.com/marcuswestin/WebViewJavascriptBridge
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
let WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
function adapter(request, responseCallBack) {
setupWebViewJavascriptBridge(function (bridge) {
bridge.callHandler("onAjaxRequest",request,(responseData)=>{
responseData = JSON.parse(responseData);
if(request.responseType==="stream") {
handleImgBase64Data(responseData);
}
responseCallBack(responseData)
})
})
}
//build环境定义全局变量
KEEP("cdn||cdn-min", () => {
window.wjsbAdapter= adapter
})
module.exports = adapter;
+22
View File
@@ -0,0 +1,22 @@
//weex adapter
let stream = weex.requireModule('stream');
module.exports = function (request, responseCallback) {
if (!request.body) {
delete request.body;
}
stream.fetch(request, function (res) {
if (res.ok) {
responseCallback({
statusCode: res.status,
responseText: res.data,
headers: res.headers,
statusMessage: res.statusText
})
} else {
responseCallback({
statusCode: res.status || 0,
statusMessage: res.statusText
})
}
});
}
+26
View File
@@ -0,0 +1,26 @@
//微信小程序适配器
module.exports=function(request, responseCallback) {
let con = {
method: request.method,
url: request.url,
dataType: request.dataType || undefined,
header: request.headers,
data: request.body||{},
responseType: request.responseType || 'text',
success(res) {
responseCallback({
statusCode: res.statusCode,
responseText: res.data,
headers: res.header,
statusMessage: res.errMsg
})
},
fail(res) {
responseCallback({
statusCode: res.statusCode||0,
statusMessage: res.errMsg
})
}
}
wx.request(con)
}
+8
View File
@@ -0,0 +1,8 @@
//支付宝小程序入口
const _Fly = require("./fly")
const EngineWrapper = require("./engine-wrapper")
const adapter = require("./adapter/ap")
const aliPayEngine = EngineWrapper(adapter)
module.exports = function (engine) {
return new _Fly(engine || aliPayEngine);
}
+181
View File
@@ -0,0 +1,181 @@
/*
* author: wendu
* email: 824783146@qq.com
**/
let util = require('./utils/utils')
let isBrowser = typeof document !== "undefined";
//EngineWrapper can help generating a http engine quickly through a adapter
function EngineWrapper(adapter) {
class AjaxEngine {
constructor() {
this.requestHeaders = {};
this.readyState = 0;
this.timeout = 0;// 0 stands for no timeout
this.responseURL = "";
this.responseHeaders = {};
}
_call(name) {
this[name] && this[name].apply(this, [].splice.call(arguments, 1))
}
_changeReadyState(state) {
this.readyState = state;
this._call("onreadystatechange")
}
open(method, url) {
this.method = method;
if (!url) {
url = location.href;
} else {
url = util.trim(url);
if (url.indexOf("http") !== 0) {
// Normalize the request url
if (isBrowser) {
let t = document.createElement("a");
t.href = url;
url = t.href;
}
}
}
this.responseURL = url;
this._changeReadyState(1)
}
send(arg) {
arg = arg || null;
let self = this;
if (adapter) {
let request = {
method: self.method,
url: self.responseURL,
headers: self.requestHeaders || {},
body: arg
}
util.merge(request, self._options || {})
if (request.method === "GET") {
request.body = null;
}
self._changeReadyState(3)
let timer;
self.timeout = self.timeout || 0;
if (self.timeout > 0) {
timer = setTimeout(() => {
if (self.readyState === 3) {
this._call("onloadend");
self._changeReadyState(0);
self._call("ontimeout");
}
}, self.timeout);
}
request.timeout = self.timeout;
adapter(request, function (response) {
function getAndDelete(key) {
let t = response[key]
delete response[key]
return t;
}
// If the request has already timeout, return
if (self.readyState !== 3) return;
clearTimeout(timer)
// Make sure the type of status is integer
self.status = getAndDelete("statusCode") - 0;
let responseText = getAndDelete("responseText")
let statusMessage = getAndDelete("statusMessage")
// Network error, set the status code 0
if (!self.status) {
self.statusText = responseText;
self._call("onerror", {msg: statusMessage});
} else {
// Parsing the response headers to array in a object, because
// there may be multiple values with the same header name
let responseHeaders = getAndDelete("headers")
let headers = {};
for (let field in responseHeaders) {
let value = responseHeaders[field];
let key = field.toLowerCase();
// Is array
if (typeof value === "object") {
headers[key] = value;
} else {
headers[key] = headers[key] || []
headers[key].push(value)
}
}
let cookies = headers["set-cookie"];
if (isBrowser && cookies) {
cookies.forEach((e) => {
// Remove the http-Only property of the cookie
// so that JavaScript can operate it.
document.cookie = e.replace(/;\s*httpOnly/ig, "")
})
}
self.responseHeaders = headers;
// Set the fields of engine from response
self.statusText = statusMessage || "";
self.response = self.responseText = responseText;
self._response = response;
self._changeReadyState(4);
self._call("onload");
}
self._call("onloadend");
});
} else {
console.error("Ajax require adapter")
}
}
setRequestHeader(key, value) {
this.requestHeaders[util.trim(key)] = value;
}
getResponseHeader(key) {
return (this.responseHeaders[key.toLowerCase()] || "").toString() || null
}
getAllResponseHeaders() {
let str = "";
for (let key in this.responseHeaders) {
str += key + ":" + this.getResponseHeader(key) + "\r\n";
}
return str || null;
}
abort(msg) {
this._changeReadyState(0)
this._call("onerror", {msg});
this._call("onloadend");
}
static setAdapter(requestAdapter) {
adapter = requestAdapter
}
}
return AjaxEngine;
}
// learn more about keep-loader: https://github.com/wendux/keep-loader
KEEP("cdn||cdn-min", () => {
// This code block will be removed besides the "CDN" and "cdn-min" build environment
window.EngineWrapper = EngineWrapper
})
module.exports = EngineWrapper;
+369
View File
@@ -0,0 +1,369 @@
let utils = require('./utils/utils');
let isBrowser = typeof document !== "undefined";
class Fly {
constructor(engine) {
this.engine = engine || XMLHttpRequest;
this.default = this //For typeScript
/**
* Add lock/unlock API for interceptor.
*
* Once an request/response interceptor is locked, the incoming request/response
* will be added to a queue before they enter the interceptor, they will not be
* continued until the interceptor is unlocked.
*
* @param [interceptor] either is interceptors.request or interceptors.response
*/
function wrap(interceptor) {
let resolve;
let reject;
function _clear() {
interceptor.p = resolve = reject = null;
}
utils.merge(interceptor, {
lock() {
if (!resolve) {
interceptor.p = new Promise((_resolve, _reject) => {
resolve = _resolve
reject = _reject;
})
}
},
unlock() {
if (resolve) {
resolve()
_clear();
}
},
clear() {
if (reject) {
reject("cancel");
_clear();
}
}
})
}
let interceptors = this.interceptors = {
response: {
use(handler, onerror) {
this.handler = handler;
this.onerror = onerror;
}
},
request: {
use(handler) {
this.handler = handler;
}
}
}
let irq = interceptors.request;
let irp = interceptors.response;
wrap(irp);
wrap(irq);
this.config = {
method: "GET",
baseURL: "",
headers: {},
timeout: 0,
params: {}, // Default Url params
parseJson: true, // Convert response data to JSON object automatically.
withCredentials: false
}
}
request(url, data, options) {
let engine = new this.engine;
let contentType = "Content-Type";
let contentTypeLowerCase = contentType.toLowerCase();
let interceptors = this.interceptors;
let requestInterceptor = interceptors.request;
let responseInterceptor = interceptors.response;
let requestInterceptorHandler = requestInterceptor.handler;
let promise = new Promise((resolve, reject) => {
if (utils.isObject(url)) {
options = url;
url = options.url;
}
options = options || {};
options.headers = options.headers || {};
function isPromise(p) {
// some polyfill implementation of Promise may be not standard,
// so, we test by duck-typing
return p && p.then && p.catch
}
/**
* If the request/response interceptor has been locked
* the new request/response will enter a queue. otherwise, it will be performed directly.
* @param [promise] if the promise exist, means the interceptor is locked.
* @param [callback]
*/
function enqueueIfLocked(promise, callback) {
if (promise) {
promise.then(() => {
callback()
})
} else {
callback()
}
}
// make the http request
function makeRequest(options) {
data = options.body;
// Normalize the request url
url = utils.trim(options.url);
let baseUrl = utils.trim(options.baseURL || "");
if (!url && isBrowser && !baseUrl) url = location.href;
if (url.indexOf("http") !== 0) {
let isAbsolute = url[0] === "/";
if (!baseUrl && isBrowser) {
let arr = location.pathname.split("/");
arr.pop();
baseUrl = location.protocol + "//" + location.host + (isAbsolute ? "" : arr.join("/"))
}
if (baseUrl[baseUrl.length - 1] !== "/") {
baseUrl += "/"
}
url = baseUrl + (isAbsolute ? url.substr(1) : url)
if (isBrowser) {
// Normalize the url which contains the ".." or ".", such as
// "http://xx.com/aa/bb/../../xx" to "http://xx.com/xx" .
let t = document.createElement("a");
t.href = url;
url = t.href;
}
}
let responseType = utils.trim(options.responseType || "")
let needQuery = ["GET", "HEAD", "DELETE", "OPTION"].indexOf(options.method) !== -1;
let dataType = utils.type(data);
let params = options.params || {};
// merge url params when the method is "GET" (data is object)
if (needQuery && dataType === "object") {
params = utils.merge(data, params)
}
// encode params to String
params = utils.formatParams(params);
// save url params
let _params = [];
if (params) {
_params.push(params);
}
// Add data to url params when the method is "GET" (data is String)
if (needQuery && data && dataType === "string") {
_params.push(data);
}
// make the final url
if (_params.length > 0) {
url += (url.indexOf("?") === -1 ? "?" : "&") + _params.join("&");
}
engine.open(options.method, url);
// try catch for ie >=9
try {
engine.withCredentials = !!options.withCredentials;
engine.timeout = options.timeout || 0;
if (responseType !== "stream") {
engine.responseType = responseType
}
} catch (e) {
}
let customContentType = options.headers[contentType] || options.headers[contentTypeLowerCase];
// default content type
let _contentType = "application/x-www-form-urlencoded";
// If the request data is json object, transforming it to json string,
// and set request content-type to "json". In browser, the data will
// be sent as RequestBody instead of FormData
if (utils.trim((customContentType || "").toLowerCase()) === _contentType) {
data = utils.formatParams(data);
} else if (!utils.isFormData(data) && ["object", "array"].indexOf(utils.type(data)) !== -1) {
_contentType = 'application/json;charset=utf-8'
data = JSON.stringify(data);
}
//If user doesn't set content-type, set default.
if (!(customContentType || needQuery)) {
options.headers[contentType] = _contentType;
}
for (let k in options.headers) {
if (k === contentType && utils.isFormData(data)) {
// Delete the content-type, Let the browser set it
delete options.headers[k];
} else {
try {
// In browser environment, some header fields are readonly,
// write will cause the exception .
engine.setRequestHeader(k, options.headers[k])
} catch (e) {
}
}
}
function onresult(handler, data, type) {
enqueueIfLocked(responseInterceptor.p, function () {
if (handler) {
//如果失败,添加请求信息
if (type) {
data.request = options;
}
let ret = handler.call(responseInterceptor, data, Promise)
data = ret === undefined ? data : ret;
}
if (!isPromise(data)) {
data = Promise[type === 0 ? "resolve" : "reject"](data)
}
data.then(d => {
resolve(d)
}).catch((e) => {
reject(e)
})
})
}
function onerror(e) {
e.engine = engine;
onresult(responseInterceptor.onerror, e, -1)
}
function Err(msg, status) {
this.message = msg
this.status = status;
}
engine.onload = () => {
try {
// The xhr of IE9 has not response field
let response = engine.response || engine.responseText;
if (response && options.parseJson && (engine.getResponseHeader(contentType) || "").indexOf("json") !== -1
// Some third engine implementation may transform the response text to json object automatically,
// so we should test the type of response before transforming it
&& !utils.isObject(response)) {
response = JSON.parse(response);
}
let headers = engine.responseHeaders;
// In browser
if (!headers) {
headers = {};
let items = (engine.getAllResponseHeaders() || "").split("\r\n");
items.pop();
items.forEach((e) => {
if (!e) return;
let key = e.split(":")[0]
headers[key] = engine.getResponseHeader(key)
})
}
let status = engine.status
let statusText = engine.statusText
let data = {data: response, headers, status, statusText};
// The _response filed of engine is set in adapter which be called in engine-wrapper.js
utils.merge(data, engine._response)
if ((status >= 200 && status < 300) || status === 304) {
data.engine = engine;
data.request = options;
onresult(responseInterceptor.handler, data, 0)
} else {
let e = new Err(statusText, status);
e.response = data
onerror(e)
}
} catch (e) {
onerror(new Err(e.msg, engine.status))
}
}
engine.onerror = (e) => {
onerror(new Err(e.msg || "Network Error", 0))
}
engine.ontimeout = () => {
onerror(new Err(`timeout [ ${engine.timeout}ms ]`, 1))
}
engine._options = options;
setTimeout(() => {
engine.send(needQuery ? null : data)
}, 0)
}
enqueueIfLocked(requestInterceptor.p, () => {
utils.merge(options, JSON.parse(JSON.stringify(this.config)));
let headers = options.headers;
headers[contentType] = headers[contentType] || headers[contentTypeLowerCase] || "";
delete headers[contentTypeLowerCase]
options.body = data || options.body;
url = utils.trim(url || "");
options.method = options.method.toUpperCase();
options.url = url;
let ret = options;
if (requestInterceptorHandler) {
ret = requestInterceptorHandler.call(requestInterceptor, options, Promise) || options;
}
if (!isPromise(ret)) {
ret = Promise.resolve(ret)
}
ret.then((d) => {
//if options continue
if (d === options) {
makeRequest(d)
} else {
resolve(d)
}
}, (err) => {
reject(err)
})
})
})
promise.engine = engine;
return promise;
}
all(promises) {
return Promise.all(promises)
}
spread(callback) {
return function (arr) {
return callback.apply(null, arr);
}
}
}
//For typeScript
Fly.default = Fly;
["get", "post", "put", "patch", "head", "delete"].forEach(e => {
Fly.prototype[e] = function (url, data, option) {
return this.request(url, data, utils.merge({method: e}, option))
}
});
["lock", "unlock", "clear"].forEach(e => {
Fly.prototype[e] = function () {
this.interceptors.request[e]();
}
});
// Learn more about keep-loader: https://github.com/wendux/keep-loader
KEEP("cdn||cdn-min", () => {
// This code block will be removed besides the "CDN" and "cdn-min" build environment
window.fly = new Fly;
window.Fly = Fly;
})
module.exports = Fly;
+8
View File
@@ -0,0 +1,8 @@
//quick app entry
const Fly = require("./fly")
const EngineWrapper = require("./engine-wrapper")
const adapter = require("./adapter/hap")
module.exports = function (fetch) {
let hapEngine = EngineWrapper(adapter(fetch))
return new Fly(hapEngine);
}
+3
View File
@@ -0,0 +1,3 @@
## Notice
The source of this folder just for node environment.
+43
View File
@@ -0,0 +1,43 @@
let Fly = require("../../dist/npm/fly")
let EngineWrapper = require("../../dist/npm/engine-wrapper")
let adapter = require("../adapter/node")
let utils = require("../utils/utils")
let nodeEngine = EngineWrapper(adapter)
let fs = require("fs")
let path= require("path")
let request=require("request")
let rq = request.defaults({jar: true})
Object.assign(Fly.prototype,{
// Http plugin
"$http":rq ,
// File download API
download(url, savePath, params = null,options) {
return this.request(url, params,utils.merge({responseType: "stream"},options))
.then(d => {
return new Promise((resolve,reject)=> {
fs.writeFile(savePath, d.data,(err)=>{
if(!err) {
resolve({size:d.data.length,path:path.resolve(savePath)})
}else{
// Failed to save file
err.status=2
reject(err)
}
})
})
}).catch(e=>{
return Promise.reject(e)
})
},
// File upload API
upload(url,formData,options) {
return this.post(url,null,utils.merge({formData},options))
}
})
module.exports = function (engine) {
return new Fly(engine||nodeEngine)
}
+13
View File
@@ -0,0 +1,13 @@
/**
* Add base64 tag, like data:image/img;base64
* @param responseData
*/
module.exports = function handleImgBase64Data(responseData) {
let headers = responseData.headers || {};
let contentType = (headers["content-type"] || headers["Content-Type"] || "")
.toString().toLowerCase();
if (contentType.indexOf("image") !== -1
&& responseData.responseText.indexOf("base64") === -1) {
responseData.responseText = `data:${contentType};base64,` + responseData.responseText;
}
}
+77
View File
@@ -0,0 +1,77 @@
module.exports = {
type(ob) {
return Object.prototype.toString.call(ob).slice(8, -1).toLowerCase()
},
isObject(ob, real) {
if (real) {
return this.type(ob) === "object"
} else {
return ob && typeof ob === 'object'
}
},
isFormData(val) {
return (typeof FormData !== 'undefined') && (val instanceof FormData);
},
trim(str) {
return str.replace(/(^\s*)|(\s*$)/g, '');
},
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, ']');
},
formatParams(data) {
let str = "";
let first = true;
let that = this;
if (!this.isObject(data)) {
return data;
}
function _encode(sub, path) {
let encode = that.encode;
let type = that.type(sub);
if (type == "array") {
sub.forEach(function (e, i) {
if (!that.isObject(e)) i = "";
_encode(e, path + `%5B${i}%5D`);
});
} else if (type == "object") {
for (let key in sub) {
if (path) {
_encode(sub[key], path + "%5B" + encode(key) + "%5D");
} else {
_encode(sub[key], encode(key));
}
}
} else {
if (!first) {
str += "&";
}
first = false;
str += path + "=" + encode(sub);
}
}
_encode(data, "");
return str;
},
// Do not overwrite existing attributes
merge(a, b) {
for (let key in b) {
if (!a.hasOwnProperty(key)) {
a[key] = b[key];
} else if (this.isObject(b[key], 1) && this.isObject(a[key], 1)) {
this.merge(a[key], b[key])
}
}
return a;
}
}
+8
View File
@@ -0,0 +1,8 @@
//weex entry
const Fly = require("./fly")
const EngineWrapper = require("./engine-wrapper")
const adapter = require("./adapter/weex")
const weexEngine = EngineWrapper(adapter)
module.exports = function (engine) {
return new Fly(engine || weexEngine);
}
+8
View File
@@ -0,0 +1,8 @@
//微信小程序入口
const _Fly = require("./fly")
const EngineWrapper = require("./engine-wrapper")
const adapter = require("./adapter/wx")
const wxEngine = EngineWrapper(adapter)
module.exports=function (engine) {
return new _Fly(engine || wxEngine);
}