keq 2.7.3 → 2.7.5
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/CHANGELOG.md +14 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/src/core.js +1 -1
- package/dist/esm/src/create-request.js +7 -1
- package/dist/esm/src/is/is-blob.js +1 -0
- package/dist/esm/src/is/is-buffer.js +1 -0
- package/dist/esm/src/is/is-file.js +1 -0
- package/dist/esm/src/is/is-form-data.js +1 -0
- package/dist/esm/src/is/is-function.js +1 -0
- package/dist/esm/src/is/is-headers.js +1 -0
- package/dist/esm/src/is/is-object.js +1 -0
- package/dist/esm/src/is/is-url-search-params.js +1 -0
- package/dist/esm/src/keq.js +1 -1
- package/dist/esm/src/types/keq-context-request.d.ts +1 -1
- package/dist/esm/src/types/keq-context.js +1 -0
- package/dist/esm/src/types/keq-request.d.ts +1 -0
- package/dist/esm/src/util/assign-keq-request-body.js +1 -1
- package/dist/esm/src/util/clone-body.js +1 -0
- package/dist/esm/src/util/shallow-clone.js +1 -0
- package/dist/umd/package.json +1 -0
- package/dist/umd/src/core.js +1 -1
- package/dist/umd/src/create-request.js +7 -1
- package/dist/umd/src/is/is-blob.js +1 -0
- package/dist/umd/src/is/is-buffer.js +1 -0
- package/dist/umd/src/is/is-file.js +1 -0
- package/dist/umd/src/is/is-form-data.js +1 -0
- package/dist/umd/src/is/is-function.js +1 -0
- package/dist/umd/src/is/is-headers.js +1 -0
- package/dist/umd/src/is/is-object.js +1 -0
- package/dist/umd/src/is/is-url-search-params.js +1 -0
- package/dist/umd/src/keq.js +1 -1
- package/dist/umd/src/types/keq-context-request.d.ts +1 -1
- package/dist/umd/src/types/keq-context.js +1 -0
- package/dist/umd/src/types/keq-request.d.ts +1 -0
- package/dist/umd/src/util/assign-keq-request-body.js +1 -1
- package/dist/umd/src/util/clone-body.js +1 -0
- package/dist/umd/src/util/shallow-clone.js +1 -0
- package/package.json +15 -16
- package/.node-version +0 -1
- package/.vscode/settings.json +0 -5
- package/__tests__/node/abort.spec.ts +0 -110
- package/__tests__/node/request.ts +0 -51
- package/__tests__/node/resolve-with-mode.spec.ts +0 -48
- package/__tests__/node/retry.spec.ts +0 -35
- package/__tests__/node/send-request-with-body.spec.ts +0 -107
- package/__tests__/node/send-simple-request.spec.ts +0 -121
- package/__tests__/node/timeout.spec.ts +0 -48
- package/__tests__/setup.ts +0 -32
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
## [2.7.5](https://github.com/keq-request/keq/compare/v2.7.4...v2.7.5) (2024-09-10)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* cannot import esm ([e6c1cfb](https://github.com/keq-request/keq/commit/e6c1cfb3c283e7cd3d44cd7d60d30aaf7a380ecc))
|
|
11
|
+
|
|
12
|
+
## [2.7.4](https://github.com/keq-request/keq/compare/v2.7.3...v2.7.4) (2024-08-19)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Performance Improvements
|
|
16
|
+
|
|
17
|
+
* add options method support ([bfc7ae4](https://github.com/keq-request/keq/commit/bfc7ae45f4ad1e14bfca4ec7d6c599915424df43))
|
|
18
|
+
|
|
5
19
|
## [2.7.3](https://github.com/keq-request/keq/compare/v2.7.2...v2.7.3) (2024-08-13)
|
|
6
20
|
|
|
7
21
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type": "module"}
|
package/dist/esm/src/core.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import mitt from 'mitt';
|
|
2
3
|
import { Exception } from './exception/exception.js';
|
|
3
4
|
import { cloneBody } from './util/clone-body.js';
|
|
@@ -99,7 +100,6 @@ export class Core {
|
|
|
99
100
|
},
|
|
100
101
|
};
|
|
101
102
|
const middleware = composeMiddleware([...this.__prepend_middlewares__, ...this.__append_middlewares__]);
|
|
102
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
103
103
|
await middleware(ctx, async function emptyNext() { });
|
|
104
104
|
const output = ctx[OUTPUT_PROPERTY];
|
|
105
105
|
if (ctx.options.resolveWithFullResponse || ctx.options.resolveWith === 'response') {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { isBrowser } from './is/is-browser.js';
|
|
3
3
|
import { Keq } from './keq.js';
|
|
4
4
|
import { abortFlowControlMiddleware } from './middlewares/abort-flow-control-middleware.js';
|
|
@@ -91,6 +91,12 @@ export function createRequest(options) {
|
|
|
91
91
|
keq.prependMiddlewares(...prependMiddlewares);
|
|
92
92
|
return keq;
|
|
93
93
|
};
|
|
94
|
+
request.options = function (url) {
|
|
95
|
+
const keq = new Keq(formatUrl(url), { method: 'options' }, global);
|
|
96
|
+
keq.appendMiddlewares(...appendMiddlewares);
|
|
97
|
+
keq.prependMiddlewares(...prependMiddlewares);
|
|
98
|
+
return keq;
|
|
99
|
+
};
|
|
94
100
|
request.use = function use(middleware, ...middlewares) {
|
|
95
101
|
prependMiddlewares.push(middleware, ...middlewares);
|
|
96
102
|
return request;
|
package/dist/esm/src/keq.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import { Core } from './core.js';
|
|
2
3
|
import { Exception } from './exception/exception.js';
|
|
3
4
|
import { InvalidArgumentsExceptions } from './exception/invalid-arguments.exception.js';
|
|
@@ -206,7 +207,6 @@ export class Keq extends Core {
|
|
|
206
207
|
}
|
|
207
208
|
resolveWith(m) {
|
|
208
209
|
this.option('resolveWith', m);
|
|
209
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
210
210
|
return this;
|
|
211
211
|
}
|
|
212
212
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type KeqContextRequestMethod = 'get' | 'post' | 'put' | 'delete' | 'head' | 'patch';
|
|
1
|
+
export type KeqContextRequestMethod = 'get' | 'post' | 'put' | 'delete' | 'head' | 'patch' | 'options';
|
|
2
2
|
export type KeqContextRequestBody = object | Array<any> | string | undefined;
|
|
3
3
|
export interface KeqContextRequest {
|
|
4
4
|
url: URL;
|
|
@@ -25,6 +25,7 @@ export interface KeqRequest<OPERATIONS extends KeqOperations = KeqOperations> {
|
|
|
25
25
|
put: KeqRequestFn<FlattenOperations<OPERATIONS, 'put'>>;
|
|
26
26
|
patch: KeqRequestFn<FlattenOperations<OPERATIONS, 'patch'>>;
|
|
27
27
|
head: KeqRequestFn<FlattenOperations<OPERATIONS, 'head'>>;
|
|
28
|
+
options: KeqRequestFn<FlattenOperations<OPERATIONS, 'options'>>;
|
|
28
29
|
use(firstMiddleware: KeqMiddleware, ...middleware: KeqMiddleware[]): this;
|
|
29
30
|
useRouter(): KeqRouter;
|
|
30
31
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { Exception } from '../exception/exception.js';
|
|
3
3
|
import { OverwriteArrayBodyException } from '../exception/overwrite-array-body.exception.js';
|
|
4
4
|
import { isFormData } from '../is/is-form-data.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type": "commonjs"}
|
package/dist/umd/src/core.js
CHANGED
|
@@ -13,6 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
13
13
|
"use strict";
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.Core = void 0;
|
|
16
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
16
17
|
const mitt_1 = __importDefault(require("mitt"));
|
|
17
18
|
const exception_js_1 = require("./exception/exception.js");
|
|
18
19
|
const clone_body_js_1 = require("./util/clone-body.js");
|
|
@@ -114,7 +115,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
114
115
|
},
|
|
115
116
|
};
|
|
116
117
|
const middleware = (0, compose_middleware_js_1.composeMiddleware)([...this.__prepend_middlewares__, ...this.__append_middlewares__]);
|
|
117
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
118
118
|
await middleware(ctx, async function emptyNext() { });
|
|
119
119
|
const output = ctx[constant_js_1.OUTPUT_PROPERTY];
|
|
120
120
|
if (ctx.options.resolveWithFullResponse || ctx.options.resolveWith === 'response') {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.createRequest = void 0;
|
|
13
|
-
/* eslint-disable @typescript-eslint/no-
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14
14
|
const is_browser_js_1 = require("./is/is-browser.js");
|
|
15
15
|
const keq_js_1 = require("./keq.js");
|
|
16
16
|
const abort_flow_control_middleware_js_1 = require("./middlewares/abort-flow-control-middleware.js");
|
|
@@ -103,6 +103,12 @@
|
|
|
103
103
|
keq.prependMiddlewares(...prependMiddlewares);
|
|
104
104
|
return keq;
|
|
105
105
|
};
|
|
106
|
+
request.options = function (url) {
|
|
107
|
+
const keq = new keq_js_1.Keq(formatUrl(url), { method: 'options' }, global);
|
|
108
|
+
keq.appendMiddlewares(...appendMiddlewares);
|
|
109
|
+
keq.prependMiddlewares(...prependMiddlewares);
|
|
110
|
+
return keq;
|
|
111
|
+
};
|
|
106
112
|
request.use = function use(middleware, ...middlewares) {
|
|
107
113
|
prependMiddlewares.push(middleware, ...middlewares);
|
|
108
114
|
return request;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.isBlob = void 0;
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
14
|
const is_function_js_1 = require("./is-function.js");
|
|
14
15
|
const is_object_js_1 = require("./is-object.js");
|
|
15
16
|
const is_string_js_1 = require("./is-string.js");
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.isBuffer = void 0;
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
14
|
const is_browser_1 = require("./is-browser");
|
|
14
15
|
function isBuffer(obj) {
|
|
15
16
|
return (0, is_browser_1.isBrowser)() ? false : Buffer.isBuffer(obj);
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.isFile = void 0;
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
14
|
const is_blob_js_1 = require("./is-blob.js");
|
|
14
15
|
const is_browser_js_1 = require("./is-browser.js");
|
|
15
16
|
function isFile(object) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.isFormData = void 0;
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
14
|
const is_function_js_1 = require("./is-function.js");
|
|
14
15
|
const is_object_js_1 = require("./is-object.js");
|
|
15
16
|
function isFormData(object) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.isHeaders = void 0;
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
14
|
const is_function_js_1 = require("./is-function.js");
|
|
14
15
|
const is_object_js_1 = require("./is-object.js");
|
|
15
16
|
function isHeaders(obj) {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.isUrlSearchParams = void 0;
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
14
|
const is_function_js_1 = require("./is-function.js");
|
|
14
15
|
const is_object_js_1 = require("./is-object.js");
|
|
15
16
|
function isUrlSearchParams(obj) {
|
package/dist/umd/src/keq.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.Keq = void 0;
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
14
|
const core_js_1 = require("./core.js");
|
|
14
15
|
const exception_js_1 = require("./exception/exception.js");
|
|
15
16
|
const invalid_arguments_exception_js_1 = require("./exception/invalid-arguments.exception.js");
|
|
@@ -218,7 +219,6 @@
|
|
|
218
219
|
}
|
|
219
220
|
resolveWith(m) {
|
|
220
221
|
this.option('resolveWith', m);
|
|
221
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
222
222
|
return this;
|
|
223
223
|
}
|
|
224
224
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type KeqContextRequestMethod = 'get' | 'post' | 'put' | 'delete' | 'head' | 'patch';
|
|
1
|
+
export type KeqContextRequestMethod = 'get' | 'post' | 'put' | 'delete' | 'head' | 'patch' | 'options';
|
|
2
2
|
export type KeqContextRequestBody = object | Array<any> | string | undefined;
|
|
3
3
|
export interface KeqContextRequest {
|
|
4
4
|
url: URL;
|
|
@@ -25,6 +25,7 @@ export interface KeqRequest<OPERATIONS extends KeqOperations = KeqOperations> {
|
|
|
25
25
|
put: KeqRequestFn<FlattenOperations<OPERATIONS, 'put'>>;
|
|
26
26
|
patch: KeqRequestFn<FlattenOperations<OPERATIONS, 'patch'>>;
|
|
27
27
|
head: KeqRequestFn<FlattenOperations<OPERATIONS, 'head'>>;
|
|
28
|
+
options: KeqRequestFn<FlattenOperations<OPERATIONS, 'options'>>;
|
|
28
29
|
use(firstMiddleware: KeqMiddleware, ...middleware: KeqMiddleware[]): this;
|
|
29
30
|
useRouter(): KeqRouter;
|
|
30
31
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.assignKeqRequestBody = void 0;
|
|
13
|
-
/* eslint-disable @typescript-eslint/no-
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
14
14
|
const exception_js_1 = require("../exception/exception.js");
|
|
15
15
|
const overwrite_array_body_exception_js_1 = require("../exception/overwrite-array-body.exception.js");
|
|
16
16
|
const is_form_data_js_1 = require("../is/is-form-data.js");
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.cloneBody = void 0;
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
14
|
const is_buffer_js_1 = require("../is/is-buffer.js");
|
|
14
15
|
const is_blob_js_1 = require("../is/is-blob.js");
|
|
15
16
|
const is_file_js_1 = require("../is/is-file.js");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keq",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.5",
|
|
4
4
|
"description": "Request API write by Typescript for flexibility, readability, and a low learning curve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"request",
|
|
@@ -10,8 +10,19 @@
|
|
|
10
10
|
"nodejs",
|
|
11
11
|
"browser",
|
|
12
12
|
"middleware",
|
|
13
|
-
"middlewares"
|
|
13
|
+
"middlewares",
|
|
14
|
+
"axios",
|
|
15
|
+
"got"
|
|
14
16
|
],
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "Val.istar.Guo <val.istar.guo@gmail.com>",
|
|
19
|
+
"main": "dist/umd/src/index.js",
|
|
20
|
+
"module": "dist/esm/src/index.js",
|
|
21
|
+
"types": "dist/esm/src/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
"require": "./dist/umd/src/index.js",
|
|
24
|
+
"import": "./dist/esm/src/index.js"
|
|
25
|
+
},
|
|
15
26
|
"homepage": "https://github.com/keq-request/keq#readme",
|
|
16
27
|
"bugs": {
|
|
17
28
|
"url": "https://github.com/keq-request/keq/issues"
|
|
@@ -20,15 +31,6 @@
|
|
|
20
31
|
"type": "git",
|
|
21
32
|
"url": "git+https://github.com/keq-request/keq.git"
|
|
22
33
|
},
|
|
23
|
-
"license": "MIT",
|
|
24
|
-
"author": "Val.istar.Guo <val.istar.guo@gmail.com>",
|
|
25
|
-
"main": "dist/umd/src/index.js",
|
|
26
|
-
"module": "dist/esm/src/index.js",
|
|
27
|
-
"types": "dist/esm/src/index.d.ts",
|
|
28
|
-
"directories": {
|
|
29
|
-
"doc": "doc",
|
|
30
|
-
"test": "test"
|
|
31
|
-
},
|
|
32
34
|
"scripts": {
|
|
33
35
|
"build": "npm run clean && ./build/build.sh",
|
|
34
36
|
"clean": "rm -rf ./dist/*",
|
|
@@ -46,17 +48,14 @@
|
|
|
46
48
|
"ts-custom-error": "^3.3.1"
|
|
47
49
|
},
|
|
48
50
|
"devDependencies": {
|
|
49
|
-
"@buka/eslint-config": "^1.
|
|
51
|
+
"@buka/eslint-config": "^2.1.1",
|
|
50
52
|
"@commitlint/cli": "^19.3.0",
|
|
51
53
|
"@commitlint/config-conventional": "^19.2.2",
|
|
52
54
|
"@jest/globals": "^29.7.0",
|
|
53
|
-
"@rushstack/eslint-patch": "^1.10.3",
|
|
54
55
|
"@types/clone": "^2.1.4",
|
|
55
56
|
"@types/minimatch": "^5.1.2",
|
|
56
57
|
"@types/node": "^20.14.1",
|
|
57
|
-
"
|
|
58
|
-
"@typescript-eslint/parser": "^7.12.0",
|
|
59
|
-
"eslint": "^8.57.0",
|
|
58
|
+
"eslint": "^9.10.0",
|
|
60
59
|
"husky": "^9.0.11",
|
|
61
60
|
"is-ci": "^3.0.1",
|
|
62
61
|
"jest": "^29.7.0",
|
package/.node-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
v20.13.1
|
package/.vscode/settings.json
DELETED
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { expect, jest, test } from '@jest/globals'
|
|
2
|
-
import { request } from './request.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
test('abort flowController request', async () => {
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
7
|
-
const mockedFetch = jest.fn((input: RequestInfo | URL, init?: RequestInit) => new Promise((resolve, reject) => {
|
|
8
|
-
let finished = false
|
|
9
|
-
|
|
10
|
-
if (init?.signal) {
|
|
11
|
-
const signal = init.signal
|
|
12
|
-
signal.onabort = () => {
|
|
13
|
-
if (finished) return
|
|
14
|
-
finished = true
|
|
15
|
-
if (signal.reason) reject(signal.reason)
|
|
16
|
-
reject(new DOMException('AbortError', 'AbortError'))
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// sleet 500ms
|
|
21
|
-
setTimeout(
|
|
22
|
-
() => {
|
|
23
|
-
if (finished) return
|
|
24
|
-
finished = true
|
|
25
|
-
|
|
26
|
-
resolve(new Response(
|
|
27
|
-
JSON.stringify({ code: '200' }),
|
|
28
|
-
{
|
|
29
|
-
headers: {
|
|
30
|
-
'content-type': 'application/json',
|
|
31
|
-
},
|
|
32
|
-
}
|
|
33
|
-
))
|
|
34
|
-
},
|
|
35
|
-
100,
|
|
36
|
-
)
|
|
37
|
-
}))
|
|
38
|
-
|
|
39
|
-
async function abortRequest(): Promise<void> {
|
|
40
|
-
try {
|
|
41
|
-
await request
|
|
42
|
-
.get('http://test.com')
|
|
43
|
-
.option('fetchAPI', mockedFetch)
|
|
44
|
-
.flowControl('abort', 'test')
|
|
45
|
-
} catch (e) {
|
|
46
|
-
expect(e).toBeInstanceOf(DOMException)
|
|
47
|
-
expect((e as DOMException).name).toBe('AbortError')
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
void abortRequest()
|
|
52
|
-
|
|
53
|
-
await request
|
|
54
|
-
.get('http://test.com')
|
|
55
|
-
.option('fetchAPI', mockedFetch)
|
|
56
|
-
.flowControl('abort', 'test')
|
|
57
|
-
|
|
58
|
-
expect(mockedFetch).toBeCalledTimes(2)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
test('serial flowController request', async () => {
|
|
62
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
63
|
-
const mockedFetch = jest.fn((input: RequestInfo | URL, init?: RequestInit) => new Promise((resolve, reject) => {
|
|
64
|
-
let finished = false
|
|
65
|
-
|
|
66
|
-
if (init?.signal) {
|
|
67
|
-
const signal = init.signal
|
|
68
|
-
signal.onabort = () => {
|
|
69
|
-
if (finished) return
|
|
70
|
-
finished = true
|
|
71
|
-
if (signal.reason) reject(signal.reason)
|
|
72
|
-
reject(new DOMException('AbortError', 'AbortError'))
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// sleet 500ms
|
|
77
|
-
setTimeout(
|
|
78
|
-
() => {
|
|
79
|
-
if (finished) return
|
|
80
|
-
finished = true
|
|
81
|
-
|
|
82
|
-
resolve(new Response(
|
|
83
|
-
JSON.stringify({ code: '200' }),
|
|
84
|
-
{
|
|
85
|
-
headers: {
|
|
86
|
-
'content-type': 'application/json',
|
|
87
|
-
},
|
|
88
|
-
}
|
|
89
|
-
))
|
|
90
|
-
},
|
|
91
|
-
100,
|
|
92
|
-
)
|
|
93
|
-
}))
|
|
94
|
-
|
|
95
|
-
async function serialRequest(): Promise<void> {
|
|
96
|
-
await request
|
|
97
|
-
.get('http://test.com')
|
|
98
|
-
.option('fetchAPI', mockedFetch)
|
|
99
|
-
.flowControl('serial', 'test')
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
void serialRequest()
|
|
103
|
-
|
|
104
|
-
await request
|
|
105
|
-
.get('http://test.com')
|
|
106
|
-
.option('fetchAPI', mockedFetch)
|
|
107
|
-
.flowControl('abort', 'test')
|
|
108
|
-
|
|
109
|
-
expect(mockedFetch).toBeCalledTimes(2)
|
|
110
|
-
})
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/ban-types */
|
|
2
|
-
import { createRequest } from '~/index.js'
|
|
3
|
-
|
|
4
|
-
import type { KeqBaseOperation, KeqOperations } from '~/types/keq-operation.js'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
interface TestModule extends KeqOperations {
|
|
8
|
-
'http://test.com': {
|
|
9
|
-
get: {
|
|
10
|
-
requestParams: {}
|
|
11
|
-
requestQuery: {
|
|
12
|
-
q: string
|
|
13
|
-
}
|
|
14
|
-
requestHeaders: KeqBaseOperation['requestHeaders']
|
|
15
|
-
requestBody: {}
|
|
16
|
-
responseBody: {
|
|
17
|
-
data: 'test get'
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
post: {
|
|
21
|
-
requestParams: {}
|
|
22
|
-
requestQuery: {
|
|
23
|
-
q: string
|
|
24
|
-
}
|
|
25
|
-
requestHeaders: {
|
|
26
|
-
'x-test': 'test'
|
|
27
|
-
}
|
|
28
|
-
requestBody: {
|
|
29
|
-
id: number
|
|
30
|
-
name: string
|
|
31
|
-
file: Buffer
|
|
32
|
-
}
|
|
33
|
-
responseBody: {
|
|
34
|
-
data: 'test post'
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
'http://example.com': {
|
|
39
|
-
get: {
|
|
40
|
-
requestParams: {}
|
|
41
|
-
requestQuery: {}
|
|
42
|
-
requestHeaders: KeqBaseOperation['requestHeaders']
|
|
43
|
-
requestBody: {}
|
|
44
|
-
responseBody: {
|
|
45
|
-
data: 'example get'
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export const request = createRequest<TestModule>()
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { expect, test } from '@jest/globals'
|
|
2
|
-
import { request } from './request.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
test('resolve response body by text', async () => {
|
|
6
|
-
const result = await request
|
|
7
|
-
.get('http://test.com')
|
|
8
|
-
.auth('username', 'password')
|
|
9
|
-
.resolveWith('text')
|
|
10
|
-
|
|
11
|
-
expect(result).toEqual('{"code":"200"}')
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
test('resolve response body by json', async () => {
|
|
15
|
-
const result = await request
|
|
16
|
-
.get('http://test.com')
|
|
17
|
-
.auth('username', 'password')
|
|
18
|
-
.resolveWith<{ code: string }>('json')
|
|
19
|
-
|
|
20
|
-
expect(result).toEqual({ code: '200' })
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
test('resolve response body by blob', async () => {
|
|
24
|
-
const result = await request
|
|
25
|
-
.get('http://test.com')
|
|
26
|
-
.auth('username', 'password')
|
|
27
|
-
.resolveWith('blob')
|
|
28
|
-
|
|
29
|
-
expect(result).toBeInstanceOf(Blob)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
test('resolve response body by arrayBuffer', async () => {
|
|
33
|
-
const result = await request
|
|
34
|
-
.get('http://test.com')
|
|
35
|
-
.auth('username', 'password')
|
|
36
|
-
.resolveWith('array-buffer')
|
|
37
|
-
|
|
38
|
-
expect(result).toBeInstanceOf(ArrayBuffer)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
test('resolve response body', async () => {
|
|
42
|
-
const result = await request
|
|
43
|
-
.get('http://test.com')
|
|
44
|
-
.auth('username', 'password')
|
|
45
|
-
.resolveWith('response')
|
|
46
|
-
|
|
47
|
-
expect(result).toBeInstanceOf(Response)
|
|
48
|
-
})
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { expect, jest, test } from '@jest/globals'
|
|
2
|
-
import { KeqRetryOn } from '~/index.js'
|
|
3
|
-
import { request } from './request.js'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
test('send request retry twice', async () => {
|
|
7
|
-
const mockedFetch = jest.fn()
|
|
8
|
-
const retryOn = jest.fn<KeqRetryOn>(() => true)
|
|
9
|
-
const mockedListener = jest.fn()
|
|
10
|
-
|
|
11
|
-
await request
|
|
12
|
-
.get('http://test.com')
|
|
13
|
-
.retry(2, 10, retryOn)
|
|
14
|
-
.option('fetchAPI', mockedFetch)
|
|
15
|
-
.on('retry', mockedListener)
|
|
16
|
-
|
|
17
|
-
expect(mockedFetch.mock.calls).toHaveLength(3)
|
|
18
|
-
expect(mockedListener.mock.calls).toHaveLength(2)
|
|
19
|
-
|
|
20
|
-
expect(retryOn.mock.calls.length).toBe(2)
|
|
21
|
-
expect(retryOn.mock.calls[0][0]).toBe(0)
|
|
22
|
-
expect(retryOn.mock.calls[1][0]).toBe(1)
|
|
23
|
-
expect(retryOn.mock.calls[1][2].retry?.delay).toBe(10)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
test('send request retry once', async () => {
|
|
27
|
-
const mockedFetch = jest.fn()
|
|
28
|
-
|
|
29
|
-
await request
|
|
30
|
-
.get('http://test.com')
|
|
31
|
-
.retry(2, 0)
|
|
32
|
-
.option('fetchAPI', mockedFetch)
|
|
33
|
-
|
|
34
|
-
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
35
|
-
})
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from '@jest/globals'
|
|
2
|
-
import { Mock } from 'jest-mock'
|
|
3
|
-
import { request } from './request.js'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
describe('send multipart/form-data request', () => {
|
|
7
|
-
test('use FormData Class', async () => {
|
|
8
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
9
|
-
|
|
10
|
-
const formData = new FormData()
|
|
11
|
-
|
|
12
|
-
const resumeFile = new Blob(['test'], { type: 'text/plain' })
|
|
13
|
-
formData.append('name', 'John')
|
|
14
|
-
formData.append('resume', resumeFile, 'test.txt')
|
|
15
|
-
formData.append('friends', 'Tom')
|
|
16
|
-
formData.append('friends', 'Bob')
|
|
17
|
-
|
|
18
|
-
await request
|
|
19
|
-
.post('http://test.com')
|
|
20
|
-
.send(formData)
|
|
21
|
-
|
|
22
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
23
|
-
expect(init).toBeDefined()
|
|
24
|
-
|
|
25
|
-
const headers = init?.headers as Headers
|
|
26
|
-
|
|
27
|
-
// FormData 需要自动删除 Content-Type
|
|
28
|
-
expect(headers.get('Content-Type')).toBeNull()
|
|
29
|
-
|
|
30
|
-
const body = init?.body as FormData
|
|
31
|
-
|
|
32
|
-
expect(body.getAll('name')).toEqual(['John'])
|
|
33
|
-
expect(body.getAll('friends')).toEqual(['Tom', 'Bob'])
|
|
34
|
-
expect((body.get('resume') as File).name).toBe('test.txt')
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
test('use keq API', async () => {
|
|
38
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
39
|
-
|
|
40
|
-
await request
|
|
41
|
-
.post('http://test.com')
|
|
42
|
-
.field('name', 'John')
|
|
43
|
-
.field('friends', ['Tom', 'Bob'])
|
|
44
|
-
.field({ age: '12' })
|
|
45
|
-
.attach('file1', new Blob(['test'], { type: 'text/plain' }), 'file1.txt')
|
|
46
|
-
|
|
47
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
48
|
-
expect(init).toBeDefined()
|
|
49
|
-
|
|
50
|
-
const headers = init?.headers as Headers
|
|
51
|
-
|
|
52
|
-
// FormData 需要自动删除 Content-Type
|
|
53
|
-
expect(headers.get('Content-Type')).toBeNull()
|
|
54
|
-
|
|
55
|
-
const body = init?.body as FormData
|
|
56
|
-
|
|
57
|
-
expect(body.getAll('name')).toEqual(['John'])
|
|
58
|
-
expect(body.getAll('friends')).toEqual(['Tom', 'Bob'])
|
|
59
|
-
expect(body.getAll('age')).toEqual(['12'])
|
|
60
|
-
expect((body.get('file1') as File).name).toBe('file1.txt')
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
test('send application/json request', async () => {
|
|
66
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
67
|
-
|
|
68
|
-
const requestBody = {
|
|
69
|
-
name: 'John',
|
|
70
|
-
friends: ['Tom', 'Bob'],
|
|
71
|
-
}
|
|
72
|
-
await request
|
|
73
|
-
.post('http://test.com')
|
|
74
|
-
.send(requestBody)
|
|
75
|
-
|
|
76
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
77
|
-
expect(init).toBeDefined()
|
|
78
|
-
|
|
79
|
-
const headers = init?.headers as Headers
|
|
80
|
-
expect(headers.get('Content-Type')).toBe('application/json')
|
|
81
|
-
|
|
82
|
-
const body = init?.body as string
|
|
83
|
-
expect(body).toBe(JSON.stringify(requestBody))
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
test('send application/x-www-form-urlencoded request', async () => {
|
|
88
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
89
|
-
|
|
90
|
-
await request
|
|
91
|
-
.post('http://test.com')
|
|
92
|
-
.type('form')
|
|
93
|
-
.send({
|
|
94
|
-
name: 'John',
|
|
95
|
-
friends: ['Tom', 'Bob'],
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
99
|
-
expect(init).toBeDefined()
|
|
100
|
-
|
|
101
|
-
const headers = init?.headers as Headers
|
|
102
|
-
expect(headers.get('Content-Type')).toBe('application/x-www-form-urlencoded')
|
|
103
|
-
|
|
104
|
-
const body = init?.body as URLSearchParams
|
|
105
|
-
expect(body).toBeInstanceOf(URLSearchParams)
|
|
106
|
-
expect(body.toString()).toBe('name=John&friends=Tom&friends=Bob')
|
|
107
|
-
})
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import { expect, test } from '@jest/globals'
|
|
2
|
-
import { Mock } from 'jest-mock'
|
|
3
|
-
import { request } from './request.js'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
test('send get request', async () => {
|
|
7
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
8
|
-
|
|
9
|
-
await request
|
|
10
|
-
.get('http://test.com')
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
14
|
-
|
|
15
|
-
const url = mockedFetch.mock.calls[0][0]
|
|
16
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
17
|
-
|
|
18
|
-
expect(url).toBe('http://test.com/')
|
|
19
|
-
expect(init?.method).toBe('GET')
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
test('send post request', async () => {
|
|
24
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
25
|
-
|
|
26
|
-
await request
|
|
27
|
-
.post('http://test.com')
|
|
28
|
-
|
|
29
|
-
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
30
|
-
|
|
31
|
-
const url = mockedFetch.mock.calls[0][0]
|
|
32
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
33
|
-
|
|
34
|
-
expect(url).toBe('http://test.com/')
|
|
35
|
-
expect(init?.method).toBe('POST')
|
|
36
|
-
|
|
37
|
-
expect(init?.headers).toBeInstanceOf(Headers)
|
|
38
|
-
|
|
39
|
-
const headers = init?.headers as Headers
|
|
40
|
-
expect(headers.get('content-type')).toBeNull()
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
test('send put request', async () => {
|
|
44
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
45
|
-
|
|
46
|
-
await request
|
|
47
|
-
.put('http://test.com')
|
|
48
|
-
|
|
49
|
-
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
50
|
-
|
|
51
|
-
const url = mockedFetch.mock.calls[0][0]
|
|
52
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
53
|
-
|
|
54
|
-
expect(url).toBe('http://test.com/')
|
|
55
|
-
expect(init?.method).toBe('PUT')
|
|
56
|
-
|
|
57
|
-
expect(init?.headers).toBeInstanceOf(Headers)
|
|
58
|
-
|
|
59
|
-
const headers = init?.headers as Headers
|
|
60
|
-
expect(headers.get('content-type')).toBeNull()
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
test('send patch request', async () => {
|
|
64
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
65
|
-
|
|
66
|
-
await request
|
|
67
|
-
.patch('http://test.com')
|
|
68
|
-
|
|
69
|
-
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
70
|
-
|
|
71
|
-
const url = mockedFetch.mock.calls[0][0]
|
|
72
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
73
|
-
|
|
74
|
-
expect(url).toBe('http://test.com/')
|
|
75
|
-
expect(init?.method).toBe('PATCH')
|
|
76
|
-
|
|
77
|
-
expect(init?.headers).toBeInstanceOf(Headers)
|
|
78
|
-
|
|
79
|
-
const headers = init?.headers as Headers
|
|
80
|
-
expect(headers.get('content-type')).toBeNull()
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
test('send head request', async () => {
|
|
84
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
85
|
-
|
|
86
|
-
await request
|
|
87
|
-
.head('http://test.com')
|
|
88
|
-
|
|
89
|
-
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
90
|
-
|
|
91
|
-
const url = mockedFetch.mock.calls[0][0]
|
|
92
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
93
|
-
|
|
94
|
-
expect(url).toBe('http://test.com/')
|
|
95
|
-
expect(init?.method).toBe('HEAD')
|
|
96
|
-
|
|
97
|
-
expect(init?.headers).toBeInstanceOf(Headers)
|
|
98
|
-
|
|
99
|
-
const headers = init?.headers as Headers
|
|
100
|
-
expect(headers.get('content-type')).toBeNull()
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
test('send DELETE request', async () => {
|
|
104
|
-
const mockedFetch = global.fetch as Mock<typeof global.fetch>
|
|
105
|
-
|
|
106
|
-
await request
|
|
107
|
-
.del('http://test.com')
|
|
108
|
-
|
|
109
|
-
expect(mockedFetch.mock.calls).toHaveLength(1)
|
|
110
|
-
|
|
111
|
-
const url = mockedFetch.mock.calls[0][0]
|
|
112
|
-
const init = mockedFetch.mock.calls[0][1]
|
|
113
|
-
|
|
114
|
-
expect(url).toBe('http://test.com/')
|
|
115
|
-
expect(init?.method).toBe('DELETE')
|
|
116
|
-
|
|
117
|
-
expect(init?.headers).toBeInstanceOf(Headers)
|
|
118
|
-
|
|
119
|
-
const headers = init?.headers as Headers
|
|
120
|
-
expect(headers.get('content-type')).toBeNull()
|
|
121
|
-
})
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { expect, jest, test } from '@jest/globals'
|
|
2
|
-
import { request } from './request.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
test('timeout request', async () => {
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
7
|
-
const mockedFetch = jest.fn((input: RequestInfo | URL, init?: RequestInit) => new Promise((resolve, reject) => {
|
|
8
|
-
let finished = false
|
|
9
|
-
|
|
10
|
-
if (init?.signal) {
|
|
11
|
-
const signal = init.signal
|
|
12
|
-
signal.onabort = () => {
|
|
13
|
-
if (finished) return
|
|
14
|
-
finished = true
|
|
15
|
-
if (signal.reason) reject(signal.reason)
|
|
16
|
-
reject(new DOMException('AbortError', 'AbortError'))
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// sleet 500ms
|
|
21
|
-
setTimeout(
|
|
22
|
-
() => {
|
|
23
|
-
if (finished) return
|
|
24
|
-
finished = true
|
|
25
|
-
|
|
26
|
-
resolve(new Response(
|
|
27
|
-
JSON.stringify({ code: '200' }),
|
|
28
|
-
{
|
|
29
|
-
headers: {
|
|
30
|
-
'content-type': 'application/json',
|
|
31
|
-
},
|
|
32
|
-
}
|
|
33
|
-
))
|
|
34
|
-
},
|
|
35
|
-
500,
|
|
36
|
-
)
|
|
37
|
-
}))
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
await request
|
|
41
|
-
.get('http://test.com')
|
|
42
|
-
.option('fetchAPI', mockedFetch)
|
|
43
|
-
.timeout(100)
|
|
44
|
-
} catch (e) {
|
|
45
|
-
expect(e).toBeInstanceOf(DOMException)
|
|
46
|
-
expect((e as DOMException).name).toBe('AbortError')
|
|
47
|
-
}
|
|
48
|
-
})
|
package/__tests__/setup.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { afterAll, beforeAll, beforeEach, jest } from '@jest/globals'
|
|
2
|
-
// import { TextEncoder, TextDecoder } from 'util'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// https://github.com/inrupt/solid-client-authn-js/issues/1676
|
|
6
|
-
// global.TextEncoder = TextEncoder
|
|
7
|
-
// global.TextDecoder = TextDecoder as any
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
11
|
-
const mockedFetch = jest.fn((input: RequestInfo | URL, init?: RequestInit) => Promise.resolve(new Response(
|
|
12
|
-
JSON.stringify({ code: '200' }),
|
|
13
|
-
{
|
|
14
|
-
headers: {
|
|
15
|
-
'content-type': 'application/json',
|
|
16
|
-
},
|
|
17
|
-
}
|
|
18
|
-
)))
|
|
19
|
-
|
|
20
|
-
const unMockedFetch = global.fetch
|
|
21
|
-
|
|
22
|
-
beforeAll(() => {
|
|
23
|
-
global.fetch = mockedFetch as unknown as typeof fetch
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
afterAll(() => {
|
|
27
|
-
global.fetch = unMockedFetch
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
mockedFetch.mockClear()
|
|
32
|
-
})
|