keq 5.0.0-alpha.7 → 5.0.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +18 -0
- package/CHANGELOG.md +13 -0
- package/dist/context/execution-context.d.ts +24 -0
- package/dist/context/execution-context.d.ts.map +1 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/middleware-context.d.ts +9 -0
- package/dist/context/middleware-context.d.ts.map +1 -0
- package/dist/context/orchestrator-context.d.ts +10 -0
- package/dist/context/orchestrator-context.d.ts.map +1 -0
- package/dist/context/shared-context.d.ts +38 -0
- package/dist/context/shared-context.d.ts.map +1 -0
- package/dist/context/types/index.d.ts +6 -0
- package/dist/context/types/index.d.ts.map +1 -0
- package/dist/context/types/keq-context-data.d.ts +7 -0
- package/dist/context/types/keq-context-data.d.ts.map +1 -0
- package/dist/context/types/keq-context-emitter.d.ts +35 -0
- package/dist/context/types/keq-context-emitter.d.ts.map +1 -0
- package/dist/context/types/keq-context-options/index.d.ts +6 -0
- package/dist/context/types/keq-context-options/index.d.ts.map +1 -0
- package/dist/context/types/keq-context-options/keq-context-options.d.ts +5 -0
- package/dist/context/types/keq-context-options/keq-context-options.d.ts.map +1 -0
- package/dist/context/types/keq-context-options/keq-middleware-options.d.ts +33 -0
- package/dist/context/types/keq-context-options/keq-middleware-options.d.ts.map +1 -0
- package/dist/context/types/keq-context-options/keq-module-options.d.ts +5 -0
- package/dist/context/types/keq-context-options/keq-module-options.d.ts.map +1 -0
- package/dist/context/types/keq-context-options/keq-resolve-with-mode.d.ts +2 -0
- package/dist/context/types/keq-context-options/keq-resolve-with-mode.d.ts.map +1 -0
- package/dist/context/types/keq-context-options/keq-retry-options.d.ts +9 -0
- package/dist/context/types/keq-context-options/keq-retry-options.d.ts.map +1 -0
- package/dist/context/types/keq-context.d.ts +17 -0
- package/dist/context/types/keq-context.d.ts.map +1 -0
- package/dist/context/types/keq-global.d.ts +7 -0
- package/dist/context/types/keq-global.d.ts.map +1 -0
- package/dist/context/utils/index.d.ts +2 -0
- package/dist/context/utils/index.d.ts.map +1 -0
- package/dist/context/utils/watch-object.d.ts +5 -0
- package/dist/context/utils/watch-object.d.ts.map +1 -0
- package/dist/exception/abort.exception.d.ts +4 -0
- package/dist/exception/abort.exception.d.ts.map +1 -0
- package/dist/exception/exception.d.ts +5 -0
- package/dist/exception/exception.d.ts.map +1 -0
- package/dist/exception/index.d.ts +6 -0
- package/dist/exception/index.d.ts.map +1 -0
- package/dist/exception/request.exception.d.ts +7 -0
- package/dist/exception/request.exception.d.ts.map +1 -0
- package/dist/exception/timeout.exception.d.ts +5 -0
- package/dist/exception/timeout.exception.d.ts.map +1 -0
- package/dist/exception/type.exception.d.ts +5 -0
- package/dist/exception/type.exception.d.ts.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1496 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1439 -0
- package/dist/index.mjs.map +1 -0
- package/dist/middleware/index.d.ts +3 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/types/index.d.ts +3 -0
- package/dist/middleware/types/index.d.ts.map +1 -0
- package/dist/middleware/types/keq-middleware.d.ts +7 -0
- package/dist/middleware/types/keq-middleware.d.ts.map +1 -0
- package/dist/middleware/types/keq-next.d.ts +2 -0
- package/dist/middleware/types/keq-next.d.ts.map +1 -0
- package/dist/middleware/utils/compose-middleware.d.ts +7 -0
- package/dist/middleware/utils/compose-middleware.d.ts.map +1 -0
- package/dist/middleware/utils/index.d.ts +2 -0
- package/dist/middleware/utils/index.d.ts.map +1 -0
- package/dist/middlewares/fetch-middleware/index.d.ts +6 -0
- package/dist/middlewares/fetch-middleware/index.d.ts.map +1 -0
- package/dist/middlewares/flow-control-middleware/abort-flow-control-middleware.d.ts +3 -0
- package/dist/middlewares/flow-control-middleware/abort-flow-control-middleware.d.ts.map +1 -0
- package/dist/middlewares/flow-control-middleware/index.d.ts +4 -0
- package/dist/middlewares/flow-control-middleware/index.d.ts.map +1 -0
- package/dist/middlewares/flow-control-middleware/serial-flow-control-middleware.d.ts +3 -0
- package/dist/middlewares/flow-control-middleware/serial-flow-control-middleware.d.ts.map +1 -0
- package/dist/middlewares/flow-control-middleware/types/keq-flow-control.d.ts +8 -0
- package/dist/middlewares/flow-control-middleware/types/keq-flow-control.d.ts.map +1 -0
- package/dist/middlewares/index.d.ts +4 -0
- package/dist/middlewares/index.d.ts.map +1 -0
- package/dist/middlewares/timeout-middleware/index.d.ts +6 -0
- package/dist/middlewares/timeout-middleware/index.d.ts.map +1 -0
- package/dist/orchestrator/executor.d.ts +12 -0
- package/dist/orchestrator/executor.d.ts.map +1 -0
- package/dist/orchestrator/index.d.ts +3 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +13 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/types/keq-middleware-context.d.ts +4 -0
- package/dist/orchestrator/types/keq-middleware-context.d.ts.map +1 -0
- package/dist/request/core.d.ts +49 -0
- package/dist/request/core.d.ts.map +1 -0
- package/dist/request/create-request.d.ts +7 -0
- package/dist/request/create-request.d.ts.map +1 -0
- package/dist/request/index.d.ts +6 -0
- package/dist/request/index.d.ts.map +1 -0
- package/dist/request/keq.d.ts +115 -0
- package/dist/request/keq.d.ts.map +1 -0
- package/dist/request/request.d.ts +74 -0
- package/dist/request/request.d.ts.map +1 -0
- package/dist/request/types/api-schema/api-schema-select.d.ts +42 -0
- package/dist/request/types/api-schema/api-schema-select.d.ts.map +1 -0
- package/dist/request/types/api-schema/api-schema.d.ts +8 -0
- package/dist/request/types/api-schema/api-schema.d.ts.map +1 -0
- package/dist/request/types/api-schema/default-operation.d.ts +18 -0
- package/dist/request/types/api-schema/default-operation.d.ts.map +1 -0
- package/dist/request/types/api-schema/extract-method-operations.d.ts +31 -0
- package/dist/request/types/api-schema/extract-method-operations.d.ts.map +1 -0
- package/dist/request/types/api-schema/index.d.ts +6 -0
- package/dist/request/types/api-schema/index.d.ts.map +1 -0
- package/dist/request/types/api-schema/operation.d.ts +16 -0
- package/dist/request/types/api-schema/operation.d.ts.map +1 -0
- package/dist/request/types/content-type.d.ts +3 -0
- package/dist/request/types/content-type.d.ts.map +1 -0
- package/dist/request/types/index.d.ts +7 -0
- package/dist/request/types/index.d.ts.map +1 -0
- package/dist/request/types/keq-attachable-file.d.ts +2 -0
- package/dist/request/types/keq-attachable-file.d.ts.map +1 -0
- package/dist/request/types/keq-param-value.d.ts +3 -0
- package/dist/request/types/keq-param-value.d.ts.map +1 -0
- package/dist/request/types/keq-query-options.d.ts +7 -0
- package/dist/request/types/keq-query-options.d.ts.map +1 -0
- package/dist/request/types/keq-query-value.d.ts +7 -0
- package/dist/request/types/keq-query-value.d.ts.map +1 -0
- package/dist/request/utils/fix-content-type.d.ts +4 -0
- package/dist/request/utils/fix-content-type.d.ts.map +1 -0
- package/dist/request/utils/get-location-id.d.ts +2 -0
- package/dist/request/utils/get-location-id.d.ts.map +1 -0
- package/dist/request/utils/index.d.ts +6 -0
- package/dist/request/utils/index.d.ts.map +1 -0
- package/dist/request/utils/intelligent-parse-response.d.ts +2 -0
- package/dist/request/utils/intelligent-parse-response.d.ts.map +1 -0
- package/dist/request/utils/merge-keq-request-body.d.ts +3 -0
- package/dist/request/utils/merge-keq-request-body.d.ts.map +1 -0
- package/dist/request/utils/query-stringify.d.ts +3 -0
- package/dist/request/utils/query-stringify.d.ts.map +1 -0
- package/dist/request-init/index.d.ts +3 -0
- package/dist/request-init/index.d.ts.map +1 -0
- package/dist/request-init/request-init.d.ts +42 -0
- package/dist/request-init/request-init.d.ts.map +1 -0
- package/dist/request-init/types/index.d.ts +3 -0
- package/dist/request-init/types/index.d.ts.map +1 -0
- package/dist/request-init/types/keq-request-body.d.ts +2 -0
- package/dist/request-init/types/keq-request-body.d.ts.map +1 -0
- package/dist/request-init/types/keq-request-method.d.ts +2 -0
- package/dist/request-init/types/keq-request-method.d.ts.map +1 -0
- package/dist/request-init/utils/clone-body.d.ts +2 -0
- package/dist/request-init/utils/clone-body.d.ts.map +1 -0
- package/dist/request-init/utils/clone-headers.d.ts +2 -0
- package/dist/request-init/utils/clone-headers.d.ts.map +1 -0
- package/dist/request-init/utils/compile-url.d.ts +3 -0
- package/dist/request-init/utils/compile-url.d.ts.map +1 -0
- package/dist/request-init/utils/index.d.ts +6 -0
- package/dist/request-init/utils/index.d.ts.map +1 -0
- package/dist/request-init/utils/to-form-data.d.ts +2 -0
- package/dist/request-init/utils/to-form-data.d.ts.map +1 -0
- package/dist/request-init/utils/to-url-search-params.d.ts +2 -0
- package/dist/request-init/utils/to-url-search-params.d.ts.map +1 -0
- package/dist/router/index.d.ts +9 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/router/keq-host-route.d.ts +3 -0
- package/dist/router/keq-host-route.d.ts.map +1 -0
- package/dist/router/keq-location-route.d.ts +3 -0
- package/dist/router/keq-location-route.d.ts.map +1 -0
- package/dist/router/keq-method-route.d.ts +3 -0
- package/dist/router/keq-method-route.d.ts.map +1 -0
- package/dist/router/keq-module-route.d.ts +3 -0
- package/dist/router/keq-module-route.d.ts.map +1 -0
- package/dist/router/keq-pathname-route.d.ts +3 -0
- package/dist/router/keq-pathname-route.d.ts.map +1 -0
- package/dist/router/keq-router.d.ts +13 -0
- package/dist/router/keq-router.d.ts.map +1 -0
- package/dist/router/types/index.d.ts +2 -0
- package/dist/router/types/index.d.ts.map +1 -0
- package/dist/router/types/keq-route.d.ts +3 -0
- package/dist/router/types/keq-route.d.ts.map +1 -0
- package/dist/router/utils/compose-route.d.ts +3 -0
- package/dist/router/utils/compose-route.d.ts.map +1 -0
- package/dist/router/utils/index.d.ts +2 -0
- package/dist/router/utils/index.d.ts.map +1 -0
- package/dist/types/enabled-if-string-index.d.ts +24 -0
- package/dist/types/enabled-if-string-index.d.ts.map +1 -0
- package/dist/types/exclude-unknown-properties.d.ts +19 -0
- package/dist/types/exclude-unknown-properties.d.ts.map +1 -0
- package/dist/types/index-keys.d.ts +18 -0
- package/dist/types/index-keys.d.ts.map +1 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/literal-keys.d.ts +18 -0
- package/dist/types/literal-keys.d.ts.map +1 -0
- package/dist/types/loose-nested-like.d.ts +11 -0
- package/dist/types/loose-nested-like.d.ts.map +1 -0
- package/dist/types/string-index-value-of.d.ts +18 -0
- package/dist/types/string-index-value-of.d.ts.map +1 -0
- package/dist/utils/base64.d.ts +3 -0
- package/dist/utils/base64.d.ts.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/shallow-clone.d.ts +5 -0
- package/dist/utils/shallow-clone.d.ts.map +1 -0
- package/dist/utils/sleep.d.ts +2 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/validator/index.d.ts +2 -0
- package/dist/validator/index.d.ts.map +1 -0
- package/dist/validator/validator.d.ts +65 -0
- package/dist/validator/validator.d.ts.map +1 -0
- package/package.json +2 -3
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1439 @@
|
|
|
1
|
+
// src/exception/exception.ts
|
|
2
|
+
import { CustomError } from "ts-custom-error";
|
|
3
|
+
var Exception = class extends CustomError {
|
|
4
|
+
constructor(message) {
|
|
5
|
+
super(message);
|
|
6
|
+
Object.defineProperty(this, "name", { value: "KeqError" });
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// src/exception/type.exception.ts
|
|
11
|
+
var TypeException = class extends Exception {
|
|
12
|
+
constructor(msg) {
|
|
13
|
+
super(msg || "Invalid Type");
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/exception/abort.exception.ts
|
|
18
|
+
var AbortException = class _AbortException extends DOMException {
|
|
19
|
+
constructor(msg) {
|
|
20
|
+
super(msg, "AbortError");
|
|
21
|
+
Object.setPrototypeOf(this, _AbortException.prototype);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/exception/timeout.exception.ts
|
|
26
|
+
var TimeoutException = class extends AbortException {
|
|
27
|
+
constructor(msg) {
|
|
28
|
+
super(msg);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/exception/request.exception.ts
|
|
33
|
+
var RequestException = class extends Exception {
|
|
34
|
+
statusCode;
|
|
35
|
+
retry;
|
|
36
|
+
constructor(statusCode, message, retry = true) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.statusCode = statusCode;
|
|
39
|
+
this.retry = retry;
|
|
40
|
+
Object.defineProperty(this, "name", { value: "RequestException" });
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/utils/base64.ts
|
|
45
|
+
var base64Encode = globalThis.btoa || ((str) => Buffer.from(str).toString("base64"));
|
|
46
|
+
var base64Decode = globalThis.atob || ((str) => Buffer.from(str, "base64").toString("utf8"));
|
|
47
|
+
|
|
48
|
+
// src/validator/validator.ts
|
|
49
|
+
var Validator = class _Validator {
|
|
50
|
+
/**
|
|
51
|
+
* 检查是否为浏览器环境
|
|
52
|
+
*/
|
|
53
|
+
static isBrowser() {
|
|
54
|
+
return typeof window !== "undefined";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 检查是否为字符串
|
|
58
|
+
*/
|
|
59
|
+
static isString(value) {
|
|
60
|
+
return typeof value === "string";
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 检查是否为对象
|
|
64
|
+
*/
|
|
65
|
+
static isObject(value) {
|
|
66
|
+
return typeof value === "object" && value !== null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 检查是否为函数
|
|
70
|
+
*/
|
|
71
|
+
static isFunction(value) {
|
|
72
|
+
return typeof value === "function";
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 检查是否为 ArrayBuffer
|
|
76
|
+
*/
|
|
77
|
+
static isArrayBuffer(body) {
|
|
78
|
+
return body instanceof ArrayBuffer;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 检查是否为 ReadableStream
|
|
82
|
+
*/
|
|
83
|
+
static isReadableStream(body) {
|
|
84
|
+
return body instanceof ReadableStream;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 检查是否为 Blob 或 File 类对象
|
|
88
|
+
*/
|
|
89
|
+
static isBlob(value) {
|
|
90
|
+
if (value instanceof Blob) return true;
|
|
91
|
+
const names = ["Blob", "File"];
|
|
92
|
+
return _Validator.isObject(value) && _Validator.isString(value["type"]) && _Validator.isFunction(value["arrayBuffer"]) && _Validator.isFunction(value["stream"]) && _Validator.isFunction(value.constructor) && names.includes(value.constructor.name) && "size" in value;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 检查是否为 File
|
|
96
|
+
*/
|
|
97
|
+
static isFile(object) {
|
|
98
|
+
if (_Validator.isBrowser()) return object instanceof Blob;
|
|
99
|
+
return _Validator.isBlob(object);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 检查是否为 Buffer
|
|
103
|
+
*/
|
|
104
|
+
static isBuffer(obj) {
|
|
105
|
+
return _Validator.isBrowser() ? false : Buffer.isBuffer(obj);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 检查是否为 FormData
|
|
109
|
+
*/
|
|
110
|
+
static isFormData(object) {
|
|
111
|
+
if (object instanceof FormData) return true;
|
|
112
|
+
return _Validator.isObject(object) && _Validator.isFunction(object["append"]) && _Validator.isFunction(object["delete"]) && _Validator.isFunction(object["get"]) && _Validator.isFunction(object["getAll"]) && _Validator.isFunction(object["has"]) && _Validator.isFunction(object["set"]) && _Validator.isFunction(object["entries"]) && _Validator.isFunction(object["keys"]) && _Validator.isFunction(object["values"]);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 检查是否为 Headers
|
|
116
|
+
*/
|
|
117
|
+
static isHeaders(obj) {
|
|
118
|
+
if (obj instanceof Headers) return true;
|
|
119
|
+
if (_Validator.isObject(obj) && _Validator.isFunction(obj["forEach"]) && _Validator.isFunction(obj["get"]) && _Validator.isFunction(obj["has"]) && _Validator.isFunction(obj["set"]) && _Validator.isFunction(obj["append"]) && _Validator.isFunction(obj["delete"]) && _Validator.isFunction(obj["entries"]) && _Validator.isFunction(obj["keys"]) && _Validator.isFunction(obj["values"])) {
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 检查是否为 URLSearchParams
|
|
126
|
+
*/
|
|
127
|
+
static isUrlSearchParams(obj) {
|
|
128
|
+
if (obj instanceof URLSearchParams) return true;
|
|
129
|
+
return _Validator.isObject(obj) && _Validator.isFunction(obj["append"]) && _Validator.isFunction(obj["delete"]) && _Validator.isFunction(obj["entries"]) && _Validator.isFunction(obj["forEach"]) && _Validator.isFunction(obj["get"]) && _Validator.isFunction(obj["getAll"]) && _Validator.isFunction(obj["has"]) && _Validator.isFunction(obj["keys"]) && _Validator.isFunction(obj["set"]) && _Validator.isFunction(obj["values"]) && _Validator.isFunction(obj["sort"]) && _Validator.isFunction(obj["toString"]);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* 检查是否为合法的 Header 值
|
|
133
|
+
* 允许范围: HTAB(\t), 0x20-0x7E, 0x80-0xFF
|
|
134
|
+
* 禁止换行等控制字符
|
|
135
|
+
*/
|
|
136
|
+
static isHeaderValue(str) {
|
|
137
|
+
const regex = /^[\t\x20-\x7E\x80-\xFF]*$/;
|
|
138
|
+
return regex.test(String(str));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 检查是否为 BodyInit 类型
|
|
142
|
+
* BodyInit 包括: Blob | ArrayBuffer | TypedArray | DataView | FormData | URLSearchParams | ReadableStream | string
|
|
143
|
+
*/
|
|
144
|
+
static isBodyInit(value) {
|
|
145
|
+
if (value === null || value === void 0) return false;
|
|
146
|
+
return _Validator.isString(value) || _Validator.isBlob(value) || _Validator.isArrayBuffer(value) || _Validator.isFormData(value) || _Validator.isUrlSearchParams(value) || _Validator.isReadableStream(value) || _Validator.isBuffer(value) || ArrayBuffer.isView(value);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// src/utils/shallow-clone.ts
|
|
151
|
+
function shallowClone(obj) {
|
|
152
|
+
if (Array.isArray(obj)) {
|
|
153
|
+
return [...obj];
|
|
154
|
+
}
|
|
155
|
+
if (Validator.isObject(obj)) {
|
|
156
|
+
return { ...obj };
|
|
157
|
+
}
|
|
158
|
+
return obj;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// src/utils/sleep.ts
|
|
162
|
+
function sleep(ms) {
|
|
163
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/request-init/utils/clone-body.ts
|
|
167
|
+
function cloneBody(obj) {
|
|
168
|
+
if (Validator.isFormData(obj)) {
|
|
169
|
+
const formData = new FormData();
|
|
170
|
+
for (const [key, value] of obj.entries()) {
|
|
171
|
+
formData.append(key, value);
|
|
172
|
+
}
|
|
173
|
+
return formData;
|
|
174
|
+
}
|
|
175
|
+
if (Validator.isUrlSearchParams(obj)) {
|
|
176
|
+
const urlSearchParams = new URLSearchParams();
|
|
177
|
+
for (const [key, value] of obj.entries()) {
|
|
178
|
+
urlSearchParams.append(key, value);
|
|
179
|
+
}
|
|
180
|
+
return urlSearchParams;
|
|
181
|
+
}
|
|
182
|
+
if (Validator.isFile(obj)) {
|
|
183
|
+
return obj;
|
|
184
|
+
}
|
|
185
|
+
if (Validator.isBlob(obj)) {
|
|
186
|
+
return obj;
|
|
187
|
+
}
|
|
188
|
+
if (Validator.isBuffer(obj)) {
|
|
189
|
+
return obj;
|
|
190
|
+
}
|
|
191
|
+
if (obj === null) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (Array.isArray(obj)) {
|
|
195
|
+
return obj.map(cloneBody);
|
|
196
|
+
}
|
|
197
|
+
if (Validator.isObject(obj)) {
|
|
198
|
+
const cloned = {};
|
|
199
|
+
for (const key in obj) {
|
|
200
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
201
|
+
cloned[key] = cloneBody(obj[key]);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return cloned;
|
|
205
|
+
}
|
|
206
|
+
return obj;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/request-init/utils/clone-headers.ts
|
|
210
|
+
function cloneHeaders(headers) {
|
|
211
|
+
const cloned = new Headers();
|
|
212
|
+
for (const [key, value] of headers.entries()) {
|
|
213
|
+
cloned.append(key, value);
|
|
214
|
+
}
|
|
215
|
+
return cloned;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/request-init/utils/compile-url.ts
|
|
219
|
+
import { UriTemplateParser } from "@opendoc/uri-template";
|
|
220
|
+
function compileUrl(obj, routeParams) {
|
|
221
|
+
const url = new URL(typeof obj === "string" ? obj : obj.href);
|
|
222
|
+
url.pathname = new UriTemplateParser(
|
|
223
|
+
decodeURIComponent(url.pathname),
|
|
224
|
+
routeParams
|
|
225
|
+
).expand();
|
|
226
|
+
return url;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/request-init/utils/to-form-data.ts
|
|
230
|
+
function toFormData(body) {
|
|
231
|
+
const form = new FormData();
|
|
232
|
+
for (const [key, value] of Object.entries(body)) {
|
|
233
|
+
if (Array.isArray(value)) {
|
|
234
|
+
for (const v of value) {
|
|
235
|
+
form.append(key, v);
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
form.append(key, value);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return form;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// src/request-init/utils/to-url-search-params.ts
|
|
245
|
+
function toUrlSearchParams(body) {
|
|
246
|
+
const params = new URLSearchParams();
|
|
247
|
+
Object.entries(body).map(([key, value]) => {
|
|
248
|
+
if (Array.isArray(value)) {
|
|
249
|
+
for (const v of value) {
|
|
250
|
+
params.append(key, v);
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
params.append(key, value);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
return params;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// src/request-init/request-init.ts
|
|
260
|
+
var KeqRequestInit = class _KeqRequestInit {
|
|
261
|
+
url;
|
|
262
|
+
routeParams;
|
|
263
|
+
method;
|
|
264
|
+
headers;
|
|
265
|
+
body;
|
|
266
|
+
cache;
|
|
267
|
+
credentials;
|
|
268
|
+
integrity;
|
|
269
|
+
keepalive;
|
|
270
|
+
mode;
|
|
271
|
+
redirect;
|
|
272
|
+
referrer;
|
|
273
|
+
referrerPolicy;
|
|
274
|
+
__abortController__ = new AbortController();
|
|
275
|
+
constructor(options) {
|
|
276
|
+
this.url = new URL(options.url.href);
|
|
277
|
+
this.routeParams = shallowClone(options.routeParams);
|
|
278
|
+
this.method = options.method;
|
|
279
|
+
this.headers = cloneHeaders(options.headers);
|
|
280
|
+
this.body = cloneBody(options.body);
|
|
281
|
+
this.cache = options.cache;
|
|
282
|
+
this.credentials = options.credentials;
|
|
283
|
+
this.integrity = options.integrity;
|
|
284
|
+
this.keepalive = options.keepalive;
|
|
285
|
+
this.mode = options.mode;
|
|
286
|
+
this.redirect = options.redirect;
|
|
287
|
+
this.referrer = options.referrer;
|
|
288
|
+
this.referrerPolicy = options.referrerPolicy;
|
|
289
|
+
}
|
|
290
|
+
// the url merged routeParams
|
|
291
|
+
get __url__() {
|
|
292
|
+
return compileUrl(this.url, this.routeParams);
|
|
293
|
+
}
|
|
294
|
+
get signal() {
|
|
295
|
+
return this.__abortController__.signal;
|
|
296
|
+
}
|
|
297
|
+
abort(reason) {
|
|
298
|
+
if (!this.__abortController__.signal.aborted) {
|
|
299
|
+
this.__abortController__.abort(reason);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
clone() {
|
|
303
|
+
return new _KeqRequestInit({
|
|
304
|
+
url: this.url,
|
|
305
|
+
routeParams: this.routeParams,
|
|
306
|
+
method: this.method,
|
|
307
|
+
headers: this.headers,
|
|
308
|
+
body: this.body,
|
|
309
|
+
cache: this.cache,
|
|
310
|
+
credentials: this.credentials,
|
|
311
|
+
integrity: this.integrity,
|
|
312
|
+
keepalive: this.keepalive,
|
|
313
|
+
mode: this.mode,
|
|
314
|
+
redirect: this.redirect,
|
|
315
|
+
referrer: this.referrer,
|
|
316
|
+
referrerPolicy: this.referrerPolicy
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
getContentType() {
|
|
320
|
+
const contentType = this.headers.get("Content-Type");
|
|
321
|
+
if (contentType) return contentType;
|
|
322
|
+
if (!this.body) return void 0;
|
|
323
|
+
if (Validator.isFormData(this.body)) return "multipart/form-data";
|
|
324
|
+
if (Validator.isUrlSearchParams(this.body)) return "application/x-www-form-urlencoded";
|
|
325
|
+
if (Validator.isArrayBuffer(this.body)) return "application/octet-stream";
|
|
326
|
+
if (typeof this.body === "object") return "application/json";
|
|
327
|
+
return void 0;
|
|
328
|
+
}
|
|
329
|
+
toFetchBody(contentType) {
|
|
330
|
+
const body = this.body;
|
|
331
|
+
if (body === void 0) return;
|
|
332
|
+
if (body === null) return null;
|
|
333
|
+
if (Validator.isBodyInit(body)) return body;
|
|
334
|
+
if (!contentType || contentType === "application/json") {
|
|
335
|
+
return JSON.stringify(body);
|
|
336
|
+
}
|
|
337
|
+
if (contentType === "application/x-www-form-urlencoded") {
|
|
338
|
+
if (Array.isArray(body)) {
|
|
339
|
+
throw new Exception("request.body is an array, that cannot be serialized as application/x-www-form-urlencoded format");
|
|
340
|
+
}
|
|
341
|
+
return toUrlSearchParams(body);
|
|
342
|
+
}
|
|
343
|
+
if (contentType === "multipart/form-data") {
|
|
344
|
+
if (Array.isArray(body)) {
|
|
345
|
+
throw new Exception("FormData cannot send array");
|
|
346
|
+
}
|
|
347
|
+
return toFormData(body);
|
|
348
|
+
}
|
|
349
|
+
throw new Exception(`Cannot auto serialize request.body with Content-Type: ${contentType}`);
|
|
350
|
+
}
|
|
351
|
+
toFetchArguments() {
|
|
352
|
+
const contentType = this.getContentType();
|
|
353
|
+
const headers = cloneHeaders(this.headers);
|
|
354
|
+
if (contentType) headers.set("Content-Type", contentType);
|
|
355
|
+
const body = this.toFetchBody(contentType);
|
|
356
|
+
if (contentType === "multipart/form-data") {
|
|
357
|
+
headers.delete("Content-Type");
|
|
358
|
+
}
|
|
359
|
+
const requestInit = {
|
|
360
|
+
method: this.method.toUpperCase(),
|
|
361
|
+
headers,
|
|
362
|
+
body,
|
|
363
|
+
cache: this.cache,
|
|
364
|
+
credentials: this.credentials,
|
|
365
|
+
integrity: this.integrity,
|
|
366
|
+
keepalive: this.keepalive,
|
|
367
|
+
mode: this.mode,
|
|
368
|
+
redirect: this.redirect,
|
|
369
|
+
referrer: this.referrer,
|
|
370
|
+
referrerPolicy: this.referrerPolicy,
|
|
371
|
+
signal: this.signal
|
|
372
|
+
};
|
|
373
|
+
return [this.__url__.href, requestInit];
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
// src/context/middleware-context.ts
|
|
378
|
+
var KeqMiddlewareContext = class {
|
|
379
|
+
__executor__;
|
|
380
|
+
constructor(executor) {
|
|
381
|
+
this.__executor__ = executor;
|
|
382
|
+
}
|
|
383
|
+
get name() {
|
|
384
|
+
return this.__executor__.name;
|
|
385
|
+
}
|
|
386
|
+
get status() {
|
|
387
|
+
return this.__executor__.status;
|
|
388
|
+
}
|
|
389
|
+
get finished() {
|
|
390
|
+
return this.status === "fulfilled" || this.status === "rejected";
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
// src/context/orchestrator-context.ts
|
|
395
|
+
var KeqOrchestratorContext = class {
|
|
396
|
+
__middlewares__;
|
|
397
|
+
__orchestrator__;
|
|
398
|
+
constructor(orchestrator) {
|
|
399
|
+
this.__middlewares__ = orchestrator.executors.map((exe) => new KeqMiddlewareContext(exe));
|
|
400
|
+
this.__orchestrator__ = orchestrator;
|
|
401
|
+
}
|
|
402
|
+
get middlewares() {
|
|
403
|
+
return this.__middlewares__;
|
|
404
|
+
}
|
|
405
|
+
fork() {
|
|
406
|
+
return this.__orchestrator__.fork();
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// src/context/execution-context.ts
|
|
411
|
+
var KeqExecutionContext = class {
|
|
412
|
+
__orchestrator__;
|
|
413
|
+
__orchestrator_context__;
|
|
414
|
+
constructor(orchestrator) {
|
|
415
|
+
this.__orchestrator__ = orchestrator;
|
|
416
|
+
this.__orchestrator_context__ = new KeqOrchestratorContext(orchestrator);
|
|
417
|
+
}
|
|
418
|
+
get orchestration() {
|
|
419
|
+
return this.__orchestrator_context__;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* The unique identifier of the request's location in the code
|
|
423
|
+
*/
|
|
424
|
+
get locationId() {
|
|
425
|
+
return this.__orchestrator__.context.locationId;
|
|
426
|
+
}
|
|
427
|
+
get request() {
|
|
428
|
+
return this.__orchestrator__.context.request;
|
|
429
|
+
}
|
|
430
|
+
get global() {
|
|
431
|
+
return this.__orchestrator__.context.global;
|
|
432
|
+
}
|
|
433
|
+
get emitter() {
|
|
434
|
+
return this.__orchestrator__.context.emitter;
|
|
435
|
+
}
|
|
436
|
+
get options() {
|
|
437
|
+
return this.__orchestrator__.context.options;
|
|
438
|
+
}
|
|
439
|
+
// The result get by user if resolveWith is set to 'intelligent' or not set
|
|
440
|
+
set output(value) {
|
|
441
|
+
if (this.options.resolveWith && this.options.resolveWith !== "intelligent") {
|
|
442
|
+
console.warn(`The request is configured to resolve with ${this.options.resolveWith}, so setting context.output maybe no effect.`);
|
|
443
|
+
}
|
|
444
|
+
this.__orchestrator__.context.output = value;
|
|
445
|
+
}
|
|
446
|
+
// The original response
|
|
447
|
+
get res() {
|
|
448
|
+
return this.__orchestrator__.context.res;
|
|
449
|
+
}
|
|
450
|
+
// The original response
|
|
451
|
+
set res(value) {
|
|
452
|
+
this.__orchestrator__.context.res = value;
|
|
453
|
+
}
|
|
454
|
+
// The request response
|
|
455
|
+
get response() {
|
|
456
|
+
return this.__orchestrator__.context.response;
|
|
457
|
+
}
|
|
458
|
+
// The properties extends by middleware
|
|
459
|
+
get data() {
|
|
460
|
+
return this.__orchestrator__.context.data;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// src/context/shared-context.ts
|
|
465
|
+
import { klona } from "klona/full";
|
|
466
|
+
import mitt from "mitt";
|
|
467
|
+
|
|
468
|
+
// src/context/utils/watch-object.ts
|
|
469
|
+
function watchObject(obj, listeners) {
|
|
470
|
+
return new Proxy(obj, {
|
|
471
|
+
get(target, prop) {
|
|
472
|
+
if (prop in listeners) {
|
|
473
|
+
return new Proxy(target[prop], {
|
|
474
|
+
apply(target2, thisArg, argArray) {
|
|
475
|
+
const listener = listeners[prop];
|
|
476
|
+
listener(target2, thisArg, argArray);
|
|
477
|
+
return target2.apply(thisArg, argArray);
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
return target[prop];
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// src/context/shared-context.ts
|
|
487
|
+
var KeqSharedContext = class _KeqSharedContext {
|
|
488
|
+
__locationId__;
|
|
489
|
+
__request__;
|
|
490
|
+
__global__;
|
|
491
|
+
__emitter__;
|
|
492
|
+
__options__;
|
|
493
|
+
// The properties extends by middleware
|
|
494
|
+
__data__ = {};
|
|
495
|
+
__output__;
|
|
496
|
+
/**
|
|
497
|
+
* original response
|
|
498
|
+
*/
|
|
499
|
+
res;
|
|
500
|
+
constructor(options) {
|
|
501
|
+
this.__locationId__ = options.locationId;
|
|
502
|
+
this.__request__ = watchObject(new KeqRequestInit(options.request), {
|
|
503
|
+
abort: (target, thisArg, argArray) => {
|
|
504
|
+
this.__emitter__.emit("abort", { context: this, reason: argArray[0] });
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
this.__emitter__ = options.emitter || mitt();
|
|
508
|
+
this.__global__ = options.global;
|
|
509
|
+
this.__options__ = options.options ? shallowClone(options.options) : {};
|
|
510
|
+
this.__data__ = options.data || {};
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* The unique identifier of the request's location in the code
|
|
514
|
+
*/
|
|
515
|
+
get locationId() {
|
|
516
|
+
return this.__locationId__;
|
|
517
|
+
}
|
|
518
|
+
get request() {
|
|
519
|
+
return this.__request__;
|
|
520
|
+
}
|
|
521
|
+
get global() {
|
|
522
|
+
return this.__global__;
|
|
523
|
+
}
|
|
524
|
+
get emitter() {
|
|
525
|
+
return this.__emitter__;
|
|
526
|
+
}
|
|
527
|
+
get options() {
|
|
528
|
+
return this.__options__;
|
|
529
|
+
}
|
|
530
|
+
set output(value) {
|
|
531
|
+
this.__output__ = value;
|
|
532
|
+
}
|
|
533
|
+
get output() {
|
|
534
|
+
return this.__output__;
|
|
535
|
+
}
|
|
536
|
+
get response() {
|
|
537
|
+
if (!this.res) return void 0;
|
|
538
|
+
return new Proxy(this.res, {
|
|
539
|
+
get(res, prop) {
|
|
540
|
+
if (typeof prop === "string") {
|
|
541
|
+
if (["json", "text", "arrayBuffer", "blob", "buffer", "formData"].includes(prop)) {
|
|
542
|
+
return new Proxy(res[prop], {
|
|
543
|
+
apply(target, thisArg, argArray) {
|
|
544
|
+
const mirror = res.clone();
|
|
545
|
+
return mirror[prop](...argArray);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
if (prop === "body") {
|
|
550
|
+
const mirror = res.clone();
|
|
551
|
+
return mirror.body;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (typeof res[prop] === "function") {
|
|
555
|
+
return res[prop].bind(res);
|
|
556
|
+
}
|
|
557
|
+
return res[prop];
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
get data() {
|
|
562
|
+
return this.__data__;
|
|
563
|
+
}
|
|
564
|
+
clone() {
|
|
565
|
+
const context = new _KeqSharedContext({
|
|
566
|
+
locationId: this.__locationId__,
|
|
567
|
+
request: this.__request__.clone(),
|
|
568
|
+
global: this.__global__,
|
|
569
|
+
options: klona(this.__options__),
|
|
570
|
+
data: klona(this.__data__),
|
|
571
|
+
emitter: this.__emitter__
|
|
572
|
+
});
|
|
573
|
+
this.__emitter__.all.forEach((handler, type) => {
|
|
574
|
+
context.emitter.on(type, handler);
|
|
575
|
+
});
|
|
576
|
+
return context;
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
// src/orchestrator/executor.ts
|
|
581
|
+
var KeqMiddlewareExecutor = class _KeqMiddlewareExecutor {
|
|
582
|
+
constructor(middleware) {
|
|
583
|
+
this.middleware = middleware;
|
|
584
|
+
this.name = middleware.__keqMiddlewareName__ || middleware.name;
|
|
585
|
+
}
|
|
586
|
+
name;
|
|
587
|
+
status = "idle";
|
|
588
|
+
async execute(context, next) {
|
|
589
|
+
if (this.status !== "idle") {
|
|
590
|
+
throw new Exception(`Middleware "${this.name}" has already been executed.`);
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
this.status = "pending";
|
|
594
|
+
await context.emitter.emit("middleware:before", { context });
|
|
595
|
+
await this.middleware(context, next);
|
|
596
|
+
await context.emitter.emit("middleware:after", { context });
|
|
597
|
+
this.status = "fulfilled";
|
|
598
|
+
} catch (error) {
|
|
599
|
+
this.status = "rejected";
|
|
600
|
+
throw error;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
fork() {
|
|
604
|
+
const executor = new _KeqMiddlewareExecutor(this.middleware);
|
|
605
|
+
executor.status = this.status;
|
|
606
|
+
return executor;
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
// src/orchestrator/orchestrator.ts
|
|
611
|
+
var KeqMiddlewareOrchestrator = class _KeqMiddlewareOrchestrator {
|
|
612
|
+
status = "idle";
|
|
613
|
+
context;
|
|
614
|
+
executors = [];
|
|
615
|
+
constructor(context, middlewares = []) {
|
|
616
|
+
this.context = context;
|
|
617
|
+
this.executors = middlewares.map((mw) => new KeqMiddlewareExecutor(mw));
|
|
618
|
+
}
|
|
619
|
+
async run() {
|
|
620
|
+
const context = new KeqExecutionContext(this);
|
|
621
|
+
const dispatch = async (i) => {
|
|
622
|
+
if (i >= this.executors.length) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
const executor = this.executors[i];
|
|
626
|
+
await executor.execute(context, function next() {
|
|
627
|
+
return dispatch(i + 1);
|
|
628
|
+
});
|
|
629
|
+
};
|
|
630
|
+
await dispatch(0);
|
|
631
|
+
}
|
|
632
|
+
async execute() {
|
|
633
|
+
if (this.status !== "idle") {
|
|
634
|
+
throw new Exception("Orchestrator has already been executed.");
|
|
635
|
+
}
|
|
636
|
+
this.status = "pending";
|
|
637
|
+
try {
|
|
638
|
+
await this.run();
|
|
639
|
+
this.status = "fulfilled";
|
|
640
|
+
} catch (error) {
|
|
641
|
+
this.status = "rejected";
|
|
642
|
+
throw error;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
fork() {
|
|
646
|
+
const context = this.context.clone();
|
|
647
|
+
const middlewares = this.executors.filter((executor) => executor.status === "idle").map((executor) => executor.middleware);
|
|
648
|
+
const forkedOrchestrator = new _KeqMiddlewareOrchestrator(context, middlewares);
|
|
649
|
+
return forkedOrchestrator;
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
|
|
653
|
+
// src/request/utils/fix-content-type.ts
|
|
654
|
+
function fixContentType(contentType) {
|
|
655
|
+
if (["json", "xml"].includes(contentType)) {
|
|
656
|
+
return `application/${contentType}`;
|
|
657
|
+
} else if (["html", "css"].includes(contentType)) {
|
|
658
|
+
return `text/${contentType}`;
|
|
659
|
+
} else if (["form-data"].includes(contentType)) {
|
|
660
|
+
return `multipart/${contentType}`;
|
|
661
|
+
} else if (["jpeg", "bmp", "apng", "gif", "x-icon", "png", "webp", "tiff"].includes(contentType)) {
|
|
662
|
+
return `image/${contentType}`;
|
|
663
|
+
} else if (contentType === "form") {
|
|
664
|
+
return "application/x-www-form-urlencoded";
|
|
665
|
+
} else if (contentType === "svg") {
|
|
666
|
+
return "image/svg+xml";
|
|
667
|
+
}
|
|
668
|
+
return contentType;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/request/utils/get-location-id.ts
|
|
672
|
+
function getLocationId(depth = 0) {
|
|
673
|
+
const err = new Error();
|
|
674
|
+
if (!err.stack) return "";
|
|
675
|
+
const stackLine = err.stack.split("\n")[depth + 2];
|
|
676
|
+
return stackLine.trim();
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/request/utils/intelligent-parse-response.ts
|
|
680
|
+
async function intelligentParseResponse(response) {
|
|
681
|
+
if (!response) return void 0;
|
|
682
|
+
if (response.status === 204) {
|
|
683
|
+
return void 0;
|
|
684
|
+
}
|
|
685
|
+
const contentType = response.headers.get("content-type") || "";
|
|
686
|
+
try {
|
|
687
|
+
if (contentType.includes("application/json")) {
|
|
688
|
+
return await response.json();
|
|
689
|
+
} else if (contentType.includes("multipart/form-data")) {
|
|
690
|
+
return await response.formData();
|
|
691
|
+
} else if (contentType.includes("plain/text")) {
|
|
692
|
+
return await response.text();
|
|
693
|
+
}
|
|
694
|
+
} catch (e) {
|
|
695
|
+
console.warn("Failed to auto parse response body", e);
|
|
696
|
+
}
|
|
697
|
+
return void 0;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// src/request/utils/merge-keq-request-body.ts
|
|
701
|
+
function mergeKeqRequestBody(left, right) {
|
|
702
|
+
if (right === void 0) return left;
|
|
703
|
+
if (typeof right === "number") {
|
|
704
|
+
throw new TypeError("Cannot set request body to a number.");
|
|
705
|
+
}
|
|
706
|
+
if (left === null || right === null || Validator.isBuffer(right) || Validator.isArrayBuffer(right) || Validator.isBlob(right) || Validator.isReadableStream(right) || Validator.isBuffer(left) || Validator.isArrayBuffer(left) || Validator.isBlob(left) || Validator.isReadableStream(left) || Array.isArray(left) || Array.isArray(right) || typeof left !== "object" && left !== void 0 || typeof right !== "object") {
|
|
707
|
+
return Array.isArray(right) ? [...right] : right;
|
|
708
|
+
}
|
|
709
|
+
const result = left || {};
|
|
710
|
+
if (Validator.isUrlSearchParams(right)) {
|
|
711
|
+
const keys = right.keys();
|
|
712
|
+
for (const key of keys) {
|
|
713
|
+
const values = right.getAll(key);
|
|
714
|
+
if (values.length === 1) {
|
|
715
|
+
result[key] = values[0];
|
|
716
|
+
} else if (values.length > 1) {
|
|
717
|
+
result[key] = values;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
} else if (Validator.isFormData(right)) {
|
|
721
|
+
const keys = right.keys();
|
|
722
|
+
for (const key of keys) {
|
|
723
|
+
const values = right.getAll(key);
|
|
724
|
+
if (values.length === 1) {
|
|
725
|
+
result[key] = values[0];
|
|
726
|
+
} else if (values.length > 1) {
|
|
727
|
+
result[key] = values;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
} else if (typeof right === "object") {
|
|
731
|
+
for (const key in right) {
|
|
732
|
+
result[key] = right[key];
|
|
733
|
+
}
|
|
734
|
+
} else {
|
|
735
|
+
throw new Exception(`Not support request body type: ${typeof right}`);
|
|
736
|
+
}
|
|
737
|
+
return result;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// src/request/utils/query-stringify.ts
|
|
741
|
+
import qs from "qs";
|
|
742
|
+
function normalize(value, options) {
|
|
743
|
+
if (Array.isArray(value)) {
|
|
744
|
+
return value.map((v) => normalize(v, options));
|
|
745
|
+
}
|
|
746
|
+
if (typeof value === "bigint") {
|
|
747
|
+
return value.toString();
|
|
748
|
+
}
|
|
749
|
+
if (typeof value === "object" && value !== null) {
|
|
750
|
+
const obj = { ...value };
|
|
751
|
+
for (const key of Object.keys(obj)) {
|
|
752
|
+
obj[key] = normalize(obj[key], options);
|
|
753
|
+
}
|
|
754
|
+
return obj;
|
|
755
|
+
}
|
|
756
|
+
return value;
|
|
757
|
+
}
|
|
758
|
+
function queryStringify(query, options) {
|
|
759
|
+
const value = normalize(query, options);
|
|
760
|
+
const opts = {};
|
|
761
|
+
if (options?.allowDots !== void 0) {
|
|
762
|
+
opts.allowDots = options.allowDots;
|
|
763
|
+
}
|
|
764
|
+
if (options?.indices !== void 0) {
|
|
765
|
+
opts.indices = options.indices;
|
|
766
|
+
}
|
|
767
|
+
if (options?.arrayFormat === "pipe") {
|
|
768
|
+
opts.filter = (_, val) => {
|
|
769
|
+
if (Array.isArray(val)) return val.join("|");
|
|
770
|
+
return val;
|
|
771
|
+
};
|
|
772
|
+
} else if (options?.arrayFormat === "space") {
|
|
773
|
+
opts.filter = (_, val) => {
|
|
774
|
+
if (Array.isArray(val)) return val.join(" ");
|
|
775
|
+
return val;
|
|
776
|
+
};
|
|
777
|
+
} else if (options?.arrayFormat) {
|
|
778
|
+
opts.arrayFormat = options.arrayFormat;
|
|
779
|
+
}
|
|
780
|
+
return qs.stringify(value, opts);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// src/request/core.ts
|
|
784
|
+
var Core = class {
|
|
785
|
+
/**
|
|
786
|
+
* The unique identifier of the request's location in the code
|
|
787
|
+
*/
|
|
788
|
+
__locationId__;
|
|
789
|
+
requestPromise;
|
|
790
|
+
requestInit;
|
|
791
|
+
__listeners__ = {};
|
|
792
|
+
__global__;
|
|
793
|
+
__prepend_middlewares__ = [];
|
|
794
|
+
__append_middlewares__ = [];
|
|
795
|
+
__qs__;
|
|
796
|
+
get __middlewares__() {
|
|
797
|
+
return [...this.__prepend_middlewares__, ...this.__append_middlewares__];
|
|
798
|
+
}
|
|
799
|
+
__options__ = {
|
|
800
|
+
resolveWithFullResponse: false,
|
|
801
|
+
resolveWith: "intelligent"
|
|
802
|
+
};
|
|
803
|
+
constructor(url, options) {
|
|
804
|
+
this.__global__ = options.global || {};
|
|
805
|
+
this.__locationId__ = options.locationId;
|
|
806
|
+
this.__qs__ = options.qs;
|
|
807
|
+
this.requestInit = new KeqRequestInit({
|
|
808
|
+
method: "get",
|
|
809
|
+
headers: new Headers(),
|
|
810
|
+
routeParams: {},
|
|
811
|
+
body: void 0,
|
|
812
|
+
...options,
|
|
813
|
+
url: new URL(url.href)
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
prependMiddlewares(...middlewares) {
|
|
817
|
+
this.__prepend_middlewares__.push(...middlewares);
|
|
818
|
+
return this;
|
|
819
|
+
}
|
|
820
|
+
appendMiddlewares(...middlewares) {
|
|
821
|
+
this.__append_middlewares__.unshift(...middlewares);
|
|
822
|
+
return this;
|
|
823
|
+
}
|
|
824
|
+
use(...middlewares) {
|
|
825
|
+
return this.prependMiddlewares(...middlewares);
|
|
826
|
+
}
|
|
827
|
+
on(event, listener) {
|
|
828
|
+
this.__listeners__[event] = this.__listeners__[event] || [];
|
|
829
|
+
this.__listeners__[event].push(listener);
|
|
830
|
+
return this;
|
|
831
|
+
}
|
|
832
|
+
option(key, value = true) {
|
|
833
|
+
this.__options__[key] = value;
|
|
834
|
+
return this;
|
|
835
|
+
}
|
|
836
|
+
options(opts) {
|
|
837
|
+
for (const [key, value] of Object.entries(opts)) {
|
|
838
|
+
this.__options__[key] = value;
|
|
839
|
+
}
|
|
840
|
+
return this;
|
|
841
|
+
}
|
|
842
|
+
buildSharedContext() {
|
|
843
|
+
const coreContext = new KeqSharedContext({
|
|
844
|
+
locationId: this.__locationId__,
|
|
845
|
+
request: this.requestInit,
|
|
846
|
+
global: this.__global__,
|
|
847
|
+
options: this.__options__
|
|
848
|
+
});
|
|
849
|
+
for (const eventName in this.__listeners__) {
|
|
850
|
+
const listeners = this.__listeners__[eventName];
|
|
851
|
+
for (const listener of listeners) {
|
|
852
|
+
coreContext.emitter.on(eventName, listener);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return coreContext;
|
|
856
|
+
}
|
|
857
|
+
async run() {
|
|
858
|
+
let attempt;
|
|
859
|
+
while (true) {
|
|
860
|
+
const attemptWithDefault = attempt || 0;
|
|
861
|
+
const sharedContext = this.buildSharedContext();
|
|
862
|
+
const orchestrator = new KeqMiddlewareOrchestrator(sharedContext, this.__middlewares__);
|
|
863
|
+
if (attempt !== void 0) sharedContext.data.retry = { attempt };
|
|
864
|
+
if (attempt && attempt >= 1) sharedContext.emitter.emit("retry", { context: sharedContext });
|
|
865
|
+
let error = null;
|
|
866
|
+
try {
|
|
867
|
+
await orchestrator.execute();
|
|
868
|
+
} catch (err) {
|
|
869
|
+
error = err;
|
|
870
|
+
}
|
|
871
|
+
const retryOn = typeof sharedContext.options.retry?.on === "function" ? sharedContext.options.retry.on : (attempt2, error2) => {
|
|
872
|
+
if (error2 instanceof RequestException && error2.retry === false) return false;
|
|
873
|
+
return !!error2;
|
|
874
|
+
};
|
|
875
|
+
let retryTimes = sharedContext.options.retry?.times || 0;
|
|
876
|
+
if (retryTimes) {
|
|
877
|
+
if (retryTimes < 0) retryTimes = 0;
|
|
878
|
+
else if (!Number.isInteger(retryTimes)) retryTimes = Math.floor(retryTimes);
|
|
879
|
+
}
|
|
880
|
+
if (attemptWithDefault >= retryTimes || await retryOn(attemptWithDefault, error, sharedContext) === false) {
|
|
881
|
+
if (error) {
|
|
882
|
+
sharedContext.emitter.emit("error", { context: sharedContext });
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
885
|
+
return sharedContext;
|
|
886
|
+
}
|
|
887
|
+
const retryDelay = typeof sharedContext.options.retry?.delay === "function" ? await sharedContext.options.retry.delay(attemptWithDefault, error, sharedContext) : sharedContext.options.retry?.delay || 0;
|
|
888
|
+
if (retryDelay) await sleep(retryDelay);
|
|
889
|
+
attempt = attemptWithDefault + 1;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
async end() {
|
|
893
|
+
const coreContext = await this.run();
|
|
894
|
+
if (coreContext.options.resolveWith === "response") {
|
|
895
|
+
return coreContext.response;
|
|
896
|
+
}
|
|
897
|
+
const response = coreContext.response;
|
|
898
|
+
if (coreContext.options.resolveWith && coreContext.options.resolveWith !== "intelligent" && !response) {
|
|
899
|
+
throw new Exception([
|
|
900
|
+
`Unable to process the response with '${coreContext.options.resolveWith}'. Possible causes:`,
|
|
901
|
+
"1. The request was never initiated or sent",
|
|
902
|
+
"2. The request failed before a response was received."
|
|
903
|
+
].join("\n"));
|
|
904
|
+
}
|
|
905
|
+
if (coreContext.options.resolveWith === "text") {
|
|
906
|
+
return await response.text();
|
|
907
|
+
} else if (coreContext.options.resolveWith === "json") {
|
|
908
|
+
return await response.json();
|
|
909
|
+
} else if (coreContext.options.resolveWith === "form-data") {
|
|
910
|
+
return await response.formData();
|
|
911
|
+
} else if (coreContext.options.resolveWith === "blob") {
|
|
912
|
+
return await response.blob();
|
|
913
|
+
} else if (coreContext.options.resolveWith === "array-buffer") {
|
|
914
|
+
return await response.arrayBuffer();
|
|
915
|
+
}
|
|
916
|
+
const output = coreContext.output;
|
|
917
|
+
if (output !== void 0) {
|
|
918
|
+
return output;
|
|
919
|
+
}
|
|
920
|
+
return intelligentParseResponse(response);
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
|
924
|
+
* @param onfulfilled The callback to execute when the Promise is resolved.
|
|
925
|
+
* @param onrejected The callback to execute when the Promise is rejected.
|
|
926
|
+
* @returns A Promise for the completion of which ever callback is executed.
|
|
927
|
+
*/
|
|
928
|
+
then(onfulfilled, onrejected) {
|
|
929
|
+
if (!this.requestPromise) this.requestPromise = this.end();
|
|
930
|
+
return this.requestPromise.then(onfulfilled, onrejected);
|
|
931
|
+
}
|
|
932
|
+
catch(onrejected) {
|
|
933
|
+
return this.end().catch(onrejected);
|
|
934
|
+
}
|
|
935
|
+
finally(onfinally) {
|
|
936
|
+
return this.end().finally(onfinally);
|
|
937
|
+
}
|
|
938
|
+
};
|
|
939
|
+
|
|
940
|
+
// src/request/keq.ts
|
|
941
|
+
var Keq = class extends Core {
|
|
942
|
+
set(headersOrName, value) {
|
|
943
|
+
if (Validator.isHeaders(headersOrName)) {
|
|
944
|
+
headersOrName.forEach((value2, key) => {
|
|
945
|
+
this.requestInit.headers.set(key, value2);
|
|
946
|
+
});
|
|
947
|
+
} else if (typeof headersOrName === "string" && value) {
|
|
948
|
+
if (!Validator.isHeaderValue(value)) {
|
|
949
|
+
throw new Exception(`[Invalid header] Key: ${headersOrName} Value: ${value}`);
|
|
950
|
+
}
|
|
951
|
+
this.requestInit.headers.set(headersOrName, String(value));
|
|
952
|
+
} else if (typeof headersOrName === "object") {
|
|
953
|
+
for (const [key, value2] of Object.entries(headersOrName)) {
|
|
954
|
+
if (!Validator.isHeaderValue(value2)) {
|
|
955
|
+
throw new Exception(`[Invalid header] Key: ${key} Value: ${String(value2)}`);
|
|
956
|
+
}
|
|
957
|
+
this.requestInit.headers.set(key, String(value2));
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
return this;
|
|
961
|
+
}
|
|
962
|
+
headers(headersOrName, value) {
|
|
963
|
+
return this.set(headersOrName, value);
|
|
964
|
+
}
|
|
965
|
+
query(arg1, arg2, arg3) {
|
|
966
|
+
if (Validator.isObject(arg1)) {
|
|
967
|
+
const str = queryStringify(arg1, arg2 || this.__qs__);
|
|
968
|
+
for (const [k, v] of new URLSearchParams(str).entries()) {
|
|
969
|
+
for (const item of Array.isArray(v) ? v : [v]) {
|
|
970
|
+
this.requestInit.url.searchParams.append(k, item);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
this.requestInit.url.search = this.requestInit.url.searchParams.toString().replace(/\+/g, "%20");
|
|
974
|
+
return this;
|
|
975
|
+
}
|
|
976
|
+
if (typeof arg1 === "string") {
|
|
977
|
+
this.query({ [arg1]: arg2 }, arg3);
|
|
978
|
+
return this;
|
|
979
|
+
}
|
|
980
|
+
throw new TypeException("Invalid Arguments for .query()");
|
|
981
|
+
}
|
|
982
|
+
params(arg1, arg2) {
|
|
983
|
+
if (typeof arg1 === "string" && arg2 !== void 0) {
|
|
984
|
+
this.requestInit.routeParams[arg1] = arg2;
|
|
985
|
+
} else if (typeof arg1 === "object" && arg1 !== null) {
|
|
986
|
+
for (const k of Object.keys(arg1)) {
|
|
987
|
+
if (arg1[k]) this.requestInit.routeParams[k] = arg1[k];
|
|
988
|
+
}
|
|
989
|
+
} else {
|
|
990
|
+
throw new TypeError("Invalid Arguments for .params()");
|
|
991
|
+
}
|
|
992
|
+
return this;
|
|
993
|
+
}
|
|
994
|
+
/**
|
|
995
|
+
* Set request body
|
|
996
|
+
*/
|
|
997
|
+
body(value) {
|
|
998
|
+
this.requestInit.body = value;
|
|
999
|
+
return this;
|
|
1000
|
+
}
|
|
1001
|
+
type(contentType) {
|
|
1002
|
+
const type = fixContentType(contentType);
|
|
1003
|
+
this.set("content-type", type);
|
|
1004
|
+
return this;
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Http Basic Authentication
|
|
1008
|
+
*/
|
|
1009
|
+
auth(username, password) {
|
|
1010
|
+
this.set("Authorization", `Basic ${base64Encode(`${username}:${password}`)}`);
|
|
1011
|
+
return this;
|
|
1012
|
+
}
|
|
1013
|
+
setTypeIfEmpty(contentType) {
|
|
1014
|
+
if (!this.requestInit.headers.has("Content-Type")) void this.type(contentType);
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* set request body
|
|
1018
|
+
*/
|
|
1019
|
+
send(value) {
|
|
1020
|
+
this.requestInit.body = mergeKeqRequestBody(this.requestInit.body, value);
|
|
1021
|
+
if (Validator.isUrlSearchParams(value)) {
|
|
1022
|
+
this.setTypeIfEmpty("form");
|
|
1023
|
+
} else if (Validator.isFormData(value)) {
|
|
1024
|
+
this.setTypeIfEmpty("form-data");
|
|
1025
|
+
} else if (Validator.isBlob(value) || Validator.isReadableStream(value) || Validator.isArrayBuffer(value)) {
|
|
1026
|
+
} else if (typeof value === "object") {
|
|
1027
|
+
this.setTypeIfEmpty("json");
|
|
1028
|
+
}
|
|
1029
|
+
return this;
|
|
1030
|
+
}
|
|
1031
|
+
field(arg1, arg2) {
|
|
1032
|
+
if (typeof arg1 === "object") {
|
|
1033
|
+
this.requestInit.body = mergeKeqRequestBody(this.requestInit.body, arg1);
|
|
1034
|
+
} else if (arg2) {
|
|
1035
|
+
this.requestInit.body = mergeKeqRequestBody(this.requestInit.body, { [arg1]: arg2 });
|
|
1036
|
+
} else {
|
|
1037
|
+
throw new TypeException("Invalid arguments for .field()");
|
|
1038
|
+
}
|
|
1039
|
+
this.setTypeIfEmpty("form-data");
|
|
1040
|
+
return this;
|
|
1041
|
+
}
|
|
1042
|
+
attach(key, value, arg3 = "file") {
|
|
1043
|
+
const formData = new FormData();
|
|
1044
|
+
const appendFile = (file) => {
|
|
1045
|
+
if (Validator.isBlob(file)) {
|
|
1046
|
+
formData.append(key, file, arg3);
|
|
1047
|
+
} else if (Validator.isFile(file)) {
|
|
1048
|
+
formData.append(key, file);
|
|
1049
|
+
} else if (Validator.isBuffer(file)) {
|
|
1050
|
+
formData.append(key, new Blob([file]), arg3);
|
|
1051
|
+
} else {
|
|
1052
|
+
throw new TypeException("Invalid file type for .attach()");
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
if (Array.isArray(value)) {
|
|
1056
|
+
if (value.length === 0) {
|
|
1057
|
+
throw new TypeException("Empty array provided to .attach() is not valid");
|
|
1058
|
+
}
|
|
1059
|
+
for (const item of value) {
|
|
1060
|
+
appendFile(item);
|
|
1061
|
+
}
|
|
1062
|
+
} else {
|
|
1063
|
+
appendFile(value);
|
|
1064
|
+
}
|
|
1065
|
+
const files = formData.getAll(key);
|
|
1066
|
+
this.requestInit.body = mergeKeqRequestBody(
|
|
1067
|
+
this.requestInit.body,
|
|
1068
|
+
{
|
|
1069
|
+
[key]: files.length === 1 ? files[0] : files
|
|
1070
|
+
}
|
|
1071
|
+
);
|
|
1072
|
+
this.setTypeIfEmpty("form-data");
|
|
1073
|
+
return this;
|
|
1074
|
+
}
|
|
1075
|
+
/**
|
|
1076
|
+
*
|
|
1077
|
+
* @param retryTimes Max number of retries per call
|
|
1078
|
+
* @param retryDelay Initial value used to calculate the retry in milliseconds (This is still randomized following the randomization factor)
|
|
1079
|
+
* @param retryCallback Will be called after request failed
|
|
1080
|
+
*/
|
|
1081
|
+
retry(retryTimes, retryDelay = 0, retryOn = (attempt, error) => !!error) {
|
|
1082
|
+
this.option("retry", {
|
|
1083
|
+
times: retryTimes,
|
|
1084
|
+
delay: retryDelay,
|
|
1085
|
+
on: retryOn
|
|
1086
|
+
});
|
|
1087
|
+
return this;
|
|
1088
|
+
}
|
|
1089
|
+
redirect(mod) {
|
|
1090
|
+
this.requestInit.redirect = mod;
|
|
1091
|
+
return this;
|
|
1092
|
+
}
|
|
1093
|
+
credentials(mod) {
|
|
1094
|
+
this.requestInit.credentials = mod;
|
|
1095
|
+
return this;
|
|
1096
|
+
}
|
|
1097
|
+
mode(mod) {
|
|
1098
|
+
this.requestInit.mode = mod;
|
|
1099
|
+
return this;
|
|
1100
|
+
}
|
|
1101
|
+
flowControl(mode, signal) {
|
|
1102
|
+
const sig = signal ? signal : this.__locationId__;
|
|
1103
|
+
if (!sig) {
|
|
1104
|
+
throw new Exception("please set signal to .flowControl()");
|
|
1105
|
+
}
|
|
1106
|
+
const flowControl = {
|
|
1107
|
+
mode,
|
|
1108
|
+
signal: sig
|
|
1109
|
+
};
|
|
1110
|
+
this.option("flowControl", flowControl);
|
|
1111
|
+
return this;
|
|
1112
|
+
}
|
|
1113
|
+
timeout(milliseconds) {
|
|
1114
|
+
this.option("timeout", { millisecond: milliseconds });
|
|
1115
|
+
return this;
|
|
1116
|
+
}
|
|
1117
|
+
resolveWith(m2) {
|
|
1118
|
+
this.option("resolveWith", m2);
|
|
1119
|
+
return this;
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
1122
|
+
|
|
1123
|
+
// src/router/utils/compose-route.ts
|
|
1124
|
+
function composeRoute(routes) {
|
|
1125
|
+
if (!routes.length) {
|
|
1126
|
+
throw new Exception("At least one route");
|
|
1127
|
+
}
|
|
1128
|
+
return async (ctx) => {
|
|
1129
|
+
const results = await Promise.all(routes.map((route) => route(ctx)));
|
|
1130
|
+
return results.every((result) => result === true);
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
// src/router/keq-host-route.ts
|
|
1135
|
+
function keqHostRoute(host) {
|
|
1136
|
+
return (ctx) => ctx.request.url.host === host;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
// src/router/keq-location-route.ts
|
|
1140
|
+
function keqLocationRoute() {
|
|
1141
|
+
return (ctx) => Validator.isBrowser() && ctx.request.url.host === window.location.host;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// src/router/keq-method-route.ts
|
|
1145
|
+
function keqMethodRoute(method) {
|
|
1146
|
+
return (ctx) => ctx.request.method.toLowerCase() === method.toLowerCase();
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// src/router/keq-module-route.ts
|
|
1150
|
+
function keqModuleRoute(moduleName) {
|
|
1151
|
+
if (!moduleName) {
|
|
1152
|
+
throw new Exception("Module name should not be empty");
|
|
1153
|
+
}
|
|
1154
|
+
return (ctx) => ctx.options.module?.name === moduleName;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// src/router/keq-pathname-route.ts
|
|
1158
|
+
import * as m from "minimatch";
|
|
1159
|
+
function keqPathnameRoute(pathname) {
|
|
1160
|
+
return (ctx) => m.minimatch(ctx.request.url.pathname, pathname);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// src/middleware/utils/compose-middleware.ts
|
|
1164
|
+
function composeMiddleware(middlewares, options) {
|
|
1165
|
+
if (!middlewares.length) {
|
|
1166
|
+
throw new TypeException("At least one middleware");
|
|
1167
|
+
}
|
|
1168
|
+
const middleware = [...middlewares].reverse().reduce(function(prev, curr) {
|
|
1169
|
+
return async (ctx, next) => {
|
|
1170
|
+
await curr(ctx, () => prev(ctx, next));
|
|
1171
|
+
};
|
|
1172
|
+
});
|
|
1173
|
+
if (options?.name) {
|
|
1174
|
+
middleware.__keqMiddlewareName__ = options.name;
|
|
1175
|
+
}
|
|
1176
|
+
return middleware;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
// src/router/keq-router.ts
|
|
1180
|
+
var KeqRouter = class {
|
|
1181
|
+
constructor(middlewares = []) {
|
|
1182
|
+
this.middlewares = middlewares;
|
|
1183
|
+
}
|
|
1184
|
+
route(route, ...middlewares) {
|
|
1185
|
+
if (middlewares.length === 0) return this;
|
|
1186
|
+
const m2 = middlewares.length > 1 ? composeMiddleware(middlewares) : middlewares[0];
|
|
1187
|
+
this.middlewares.push(async function router(ctx, next) {
|
|
1188
|
+
if (route(ctx)) await m2(ctx, next);
|
|
1189
|
+
else await next();
|
|
1190
|
+
});
|
|
1191
|
+
return this;
|
|
1192
|
+
}
|
|
1193
|
+
host(host, ...middlewares) {
|
|
1194
|
+
return this.route(keqHostRoute(host), ...middlewares);
|
|
1195
|
+
}
|
|
1196
|
+
method(method, ...middlewares) {
|
|
1197
|
+
return this.route(keqMethodRoute(method), ...middlewares);
|
|
1198
|
+
}
|
|
1199
|
+
pathname(pathname, ...middlewares) {
|
|
1200
|
+
return this.route(keqPathnameRoute(pathname), ...middlewares);
|
|
1201
|
+
}
|
|
1202
|
+
location(...middlewares) {
|
|
1203
|
+
return this.route(keqLocationRoute(), ...middlewares);
|
|
1204
|
+
}
|
|
1205
|
+
module(moduleName, ...middlewares) {
|
|
1206
|
+
return this.route(keqModuleRoute(moduleName), ...middlewares);
|
|
1207
|
+
}
|
|
1208
|
+
};
|
|
1209
|
+
|
|
1210
|
+
// src/middlewares/timeout-middleware/index.ts
|
|
1211
|
+
function keqTimeoutMiddleware() {
|
|
1212
|
+
return async function timeoutMiddleware(ctx, next) {
|
|
1213
|
+
if (!ctx.options.timeout || ctx.options.timeout.millisecond <= 0) {
|
|
1214
|
+
await next();
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
const millisecond = ctx.options.timeout.millisecond;
|
|
1218
|
+
ctx.emitter.on("fetch:after", ({ context }) => {
|
|
1219
|
+
setTimeout(
|
|
1220
|
+
() => {
|
|
1221
|
+
const err = new TimeoutException(`keq request timeout(${millisecond}ms)`);
|
|
1222
|
+
context.request.abort(err);
|
|
1223
|
+
context.emitter.emit("timeout", { context });
|
|
1224
|
+
},
|
|
1225
|
+
millisecond
|
|
1226
|
+
);
|
|
1227
|
+
});
|
|
1228
|
+
await next();
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
// src/middlewares/flow-control-middleware/serial-flow-control-middleware.ts
|
|
1233
|
+
import * as fastq from "fastq";
|
|
1234
|
+
function keqSerialFlowControlMiddleware() {
|
|
1235
|
+
return async function serialFlowControlMiddleware(ctx, next) {
|
|
1236
|
+
if (!ctx.options.flowControl || ctx.options.flowControl.mode !== "serial") {
|
|
1237
|
+
await next();
|
|
1238
|
+
return;
|
|
1239
|
+
}
|
|
1240
|
+
const { signal } = ctx.options.flowControl;
|
|
1241
|
+
const key = typeof signal === "string" ? signal : signal(ctx);
|
|
1242
|
+
if (!ctx.global.serialFlowControl) ctx.global.serialFlowControl = {};
|
|
1243
|
+
if (!ctx.global.serialFlowControl[key]) {
|
|
1244
|
+
ctx.global.serialFlowControl[key] = fastq.promise(async (next2) => {
|
|
1245
|
+
await next2();
|
|
1246
|
+
}, 1);
|
|
1247
|
+
}
|
|
1248
|
+
const queue = ctx.global.serialFlowControl[key];
|
|
1249
|
+
await queue.push(next);
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/middlewares/flow-control-middleware/abort-flow-control-middleware.ts
|
|
1254
|
+
function keqAbortFlowControlMiddleware() {
|
|
1255
|
+
return async function abortFlowControlMiddleware(ctx, next) {
|
|
1256
|
+
if (!ctx.options.flowControl || ctx.options.flowControl.mode !== "abort") {
|
|
1257
|
+
await next();
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
const { signal } = ctx.options.flowControl;
|
|
1261
|
+
const key = typeof signal === "string" ? signal : signal(ctx);
|
|
1262
|
+
if (!ctx.global.abortFlowControl) ctx.global.abortFlowControl = {};
|
|
1263
|
+
const abort = ctx.global.abortFlowControl[key];
|
|
1264
|
+
if (abort) {
|
|
1265
|
+
const reason = new AbortException(`Previous request was aborted by AbortFlowControl with key "${key}"`);
|
|
1266
|
+
abort(reason);
|
|
1267
|
+
}
|
|
1268
|
+
const fn = ctx.request.abort.bind(ctx.request);
|
|
1269
|
+
ctx.global.abortFlowControl[key] = fn;
|
|
1270
|
+
try {
|
|
1271
|
+
await next();
|
|
1272
|
+
} finally {
|
|
1273
|
+
if (ctx.global.abortFlowControl[key] === fn) {
|
|
1274
|
+
ctx.global.abortFlowControl[key] = void 0;
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
// src/middlewares/flow-control-middleware/index.ts
|
|
1281
|
+
function keqFlowControlMiddleware() {
|
|
1282
|
+
return composeMiddleware(
|
|
1283
|
+
[keqSerialFlowControlMiddleware(), keqAbortFlowControlMiddleware()],
|
|
1284
|
+
{ name: "flowControlMiddleware" }
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// src/middlewares/fetch-middleware/index.ts
|
|
1289
|
+
function keqFetchMiddleware() {
|
|
1290
|
+
return async function fetchMiddleware(context) {
|
|
1291
|
+
const fetch = context.options.fetchAPI || globalThis.fetch;
|
|
1292
|
+
context.emitter.emit("fetch:before", { context });
|
|
1293
|
+
const response = await fetch(...context.request.toFetchArguments());
|
|
1294
|
+
context.emitter.emit("fetch:after", { context });
|
|
1295
|
+
context.res = response;
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// src/request/request.ts
|
|
1300
|
+
var KeqRequest = class {
|
|
1301
|
+
baseOrigin;
|
|
1302
|
+
qs;
|
|
1303
|
+
/**
|
|
1304
|
+
* share data between requests, used to implement flowControl
|
|
1305
|
+
* @description 跨请求共享数据,用于实现 flowControl的功能
|
|
1306
|
+
*/
|
|
1307
|
+
globals = {};
|
|
1308
|
+
preMiddlewares = [];
|
|
1309
|
+
postMiddlewares = [];
|
|
1310
|
+
constructor(options) {
|
|
1311
|
+
if (options?.baseOrigin) {
|
|
1312
|
+
this.baseOrigin = options.baseOrigin;
|
|
1313
|
+
} else {
|
|
1314
|
+
this.baseOrigin = Validator.isBrowser() ? location.origin : "http://127.0.0.1";
|
|
1315
|
+
}
|
|
1316
|
+
this.qs = options?.qs;
|
|
1317
|
+
this.postMiddlewares = options?.postMiddlewares || [
|
|
1318
|
+
keqFlowControlMiddleware(),
|
|
1319
|
+
keqTimeoutMiddleware(),
|
|
1320
|
+
keqFetchMiddleware()
|
|
1321
|
+
];
|
|
1322
|
+
this.preMiddlewares = options?.preMiddlewares || [];
|
|
1323
|
+
}
|
|
1324
|
+
__formatUrl__(url) {
|
|
1325
|
+
if (typeof url === "string") {
|
|
1326
|
+
return new URL(url, this.baseOrigin);
|
|
1327
|
+
}
|
|
1328
|
+
return new URL(url.href);
|
|
1329
|
+
}
|
|
1330
|
+
__fetch__(url, init, locationId) {
|
|
1331
|
+
const keq = new Keq(this.__formatUrl__(url), { ...init, locationId, global: this.globals, qs: this.qs });
|
|
1332
|
+
keq.appendMiddlewares(...this.postMiddlewares);
|
|
1333
|
+
keq.prependMiddlewares(...this.preMiddlewares);
|
|
1334
|
+
return keq;
|
|
1335
|
+
}
|
|
1336
|
+
fetch(url, init) {
|
|
1337
|
+
return this.__fetch__(
|
|
1338
|
+
url,
|
|
1339
|
+
init,
|
|
1340
|
+
getLocationId(1)
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
get(url) {
|
|
1344
|
+
return this.__fetch__(
|
|
1345
|
+
url,
|
|
1346
|
+
{ method: "get" },
|
|
1347
|
+
getLocationId(1)
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
put(url) {
|
|
1351
|
+
const locationId = getLocationId(1);
|
|
1352
|
+
const keq = new Keq(
|
|
1353
|
+
this.__formatUrl__(url),
|
|
1354
|
+
{
|
|
1355
|
+
method: "put",
|
|
1356
|
+
locationId,
|
|
1357
|
+
global: this.globals
|
|
1358
|
+
}
|
|
1359
|
+
);
|
|
1360
|
+
keq.appendMiddlewares(...this.postMiddlewares);
|
|
1361
|
+
keq.prependMiddlewares(...this.preMiddlewares);
|
|
1362
|
+
return keq;
|
|
1363
|
+
}
|
|
1364
|
+
delete(url) {
|
|
1365
|
+
return this.__fetch__(
|
|
1366
|
+
url,
|
|
1367
|
+
{ method: "delete" },
|
|
1368
|
+
getLocationId(1)
|
|
1369
|
+
);
|
|
1370
|
+
}
|
|
1371
|
+
del(url) {
|
|
1372
|
+
return this.delete(url);
|
|
1373
|
+
}
|
|
1374
|
+
post(url) {
|
|
1375
|
+
return this.__fetch__(
|
|
1376
|
+
url,
|
|
1377
|
+
{ method: "post" },
|
|
1378
|
+
getLocationId(1)
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
head(url) {
|
|
1382
|
+
return this.__fetch__(
|
|
1383
|
+
url,
|
|
1384
|
+
{ method: "head" },
|
|
1385
|
+
getLocationId(1)
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
patch(url) {
|
|
1389
|
+
return this.__fetch__(
|
|
1390
|
+
url,
|
|
1391
|
+
{ method: "patch" },
|
|
1392
|
+
getLocationId(1)
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
options(url) {
|
|
1396
|
+
return this.__fetch__(
|
|
1397
|
+
url,
|
|
1398
|
+
{ method: "options" },
|
|
1399
|
+
getLocationId(1)
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
use(firstMiddleware, ...middleware) {
|
|
1403
|
+
this.preMiddlewares.push(firstMiddleware, ...middleware);
|
|
1404
|
+
return this;
|
|
1405
|
+
}
|
|
1406
|
+
useRouter() {
|
|
1407
|
+
return new KeqRouter(this.preMiddlewares);
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
var request = new KeqRequest();
|
|
1411
|
+
|
|
1412
|
+
// src/request/create-request.ts
|
|
1413
|
+
function createRequest(options) {
|
|
1414
|
+
return new KeqRequest(options);
|
|
1415
|
+
}
|
|
1416
|
+
export {
|
|
1417
|
+
AbortException,
|
|
1418
|
+
Exception,
|
|
1419
|
+
Keq,
|
|
1420
|
+
KeqExecutionContext,
|
|
1421
|
+
KeqMiddlewareExecutor,
|
|
1422
|
+
KeqMiddlewareOrchestrator,
|
|
1423
|
+
KeqRequest,
|
|
1424
|
+
KeqRouter,
|
|
1425
|
+
KeqSharedContext,
|
|
1426
|
+
RequestException,
|
|
1427
|
+
TimeoutException,
|
|
1428
|
+
TypeException,
|
|
1429
|
+
composeMiddleware,
|
|
1430
|
+
composeRoute,
|
|
1431
|
+
createRequest,
|
|
1432
|
+
keqHostRoute,
|
|
1433
|
+
keqLocationRoute,
|
|
1434
|
+
keqMethodRoute,
|
|
1435
|
+
keqModuleRoute,
|
|
1436
|
+
keqPathnameRoute,
|
|
1437
|
+
request
|
|
1438
|
+
};
|
|
1439
|
+
//# sourceMappingURL=index.mjs.map
|