keq 2.8.11 → 2.8.12
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 +2 -0
- package/dist/esm/src/core.js +4 -3
- package/dist/esm/src/util/create-response-proxy.d.ts +1 -1
- package/dist/esm/src/util/create-response-proxy.js +36 -6
- package/dist/esm/src/util/fork.d.ts +5 -0
- package/dist/esm/src/util/fork.js +76 -0
- package/dist/umd/src/core.js +5 -4
- package/dist/umd/src/util/create-response-proxy.d.ts +1 -1
- package/dist/umd/src/util/create-response-proxy.js +37 -7
- package/dist/umd/src/util/fork.d.ts +5 -0
- package/dist/umd/src/util/fork.js +91 -0
- package/package.json +11 -13
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
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.8.12](https://github.com/keq-request/keq/compare/v2.8.11...v2.8.12) (2025-12-16)
|
|
6
|
+
|
|
5
7
|
## [2.8.11](https://github.com/keq-request/keq/compare/v2.8.10...v2.8.11) (2025-06-05)
|
|
6
8
|
|
|
7
9
|
|
package/dist/esm/src/core.js
CHANGED
|
@@ -6,6 +6,7 @@ import { ABORT_PROPERTY, OUTPUT_PROPERTY } from './constant.js';
|
|
|
6
6
|
import { composeMiddleware } from './util/compose-middleware.js';
|
|
7
7
|
import { shallowClone } from './util/shallow-clone.js';
|
|
8
8
|
import { compileUrl } from './util/compile-url.js';
|
|
9
|
+
import { unwrap } from './util/fork.js';
|
|
9
10
|
/**
|
|
10
11
|
* @description Keq 核心 API,发送请求必要的原子化的API
|
|
11
12
|
*/
|
|
@@ -109,7 +110,7 @@ export class Core {
|
|
|
109
110
|
await middleware(ctx, async function emptyNext() { });
|
|
110
111
|
const output = ctx[OUTPUT_PROPERTY];
|
|
111
112
|
if (ctx.options.resolveWithFullResponse || ctx.options.resolveWith === 'response') {
|
|
112
|
-
return ctx.response;
|
|
113
|
+
return ctx.response?.clone();
|
|
113
114
|
}
|
|
114
115
|
const response = ctx.response;
|
|
115
116
|
if (!response) {
|
|
@@ -119,7 +120,7 @@ export class Core {
|
|
|
119
120
|
return await response.text();
|
|
120
121
|
}
|
|
121
122
|
else if (ctx.options.resolveWith === 'json') {
|
|
122
|
-
return await response.json();
|
|
123
|
+
return unwrap(await response.json());
|
|
123
124
|
}
|
|
124
125
|
else if (ctx.options.resolveWith === 'form-data') {
|
|
125
126
|
return await response.formData();
|
|
@@ -140,7 +141,7 @@ export class Core {
|
|
|
140
141
|
const contentType = response.headers.get('content-type') || '';
|
|
141
142
|
try {
|
|
142
143
|
if (contentType.includes('application/json')) {
|
|
143
|
-
return await response.json();
|
|
144
|
+
return unwrap(await response.json());
|
|
144
145
|
}
|
|
145
146
|
else if (contentType.includes('multipart/form-data')) {
|
|
146
147
|
return await response.formData();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function createResponseProxy(
|
|
1
|
+
export declare function createResponseProxy(response: Response): Response;
|
|
@@ -1,20 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { fork } from './fork.js';
|
|
2
|
+
const JsonCachePropertyKey = Symbol('KeqResponseProxyJsonCachePropertyKey');
|
|
3
|
+
const TextCachePropertyKey = Symbol('KeqResponseProxyTextCachePropertyKey');
|
|
4
|
+
export function createResponseProxy(response) {
|
|
5
|
+
return new Proxy(response, {
|
|
3
6
|
get(res, prop) {
|
|
4
7
|
if (typeof prop === 'string') {
|
|
5
|
-
if (
|
|
8
|
+
if (prop === 'json') {
|
|
9
|
+
return new Proxy(res[prop], {
|
|
10
|
+
apply() {
|
|
11
|
+
if (res[JsonCachePropertyKey]) {
|
|
12
|
+
return fork(res[JsonCachePropertyKey]);
|
|
13
|
+
}
|
|
14
|
+
return res.clone().json()
|
|
15
|
+
.then((body) => {
|
|
16
|
+
res[JsonCachePropertyKey] = body;
|
|
17
|
+
return fork(body);
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
else if (prop === 'text') {
|
|
23
|
+
return new Proxy(res[prop], {
|
|
24
|
+
apply() {
|
|
25
|
+
if (res[TextCachePropertyKey]) {
|
|
26
|
+
return fork(res[TextCachePropertyKey]);
|
|
27
|
+
}
|
|
28
|
+
return res.clone().text()
|
|
29
|
+
.then((body) => {
|
|
30
|
+
res[TextCachePropertyKey] = body;
|
|
31
|
+
return body;
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else if (['arrayBuffer', 'blob', 'buffer', 'formData'].includes(prop)) {
|
|
6
37
|
/**
|
|
7
38
|
* clone when invoking body, json, text, arrayBuffer, blob, buffer, formData
|
|
8
39
|
* to avoid time-consuming cloning
|
|
9
40
|
*/
|
|
10
41
|
return new Proxy(res[prop], {
|
|
11
42
|
apply(target, thisArg, argArray) {
|
|
12
|
-
|
|
13
|
-
return mirror[prop](...argArray);
|
|
43
|
+
return res.clone()[prop](...argArray);
|
|
14
44
|
},
|
|
15
45
|
});
|
|
16
46
|
}
|
|
17
|
-
if (prop === 'body') {
|
|
47
|
+
else if (prop === 'body') {
|
|
18
48
|
const mirror = res.clone();
|
|
19
49
|
return mirror.body;
|
|
20
50
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
2
|
+
import { klona } from 'klona/json';
|
|
3
|
+
const UnWrapPropertyKey = Symbol('UnWrapPropertyKey');
|
|
4
|
+
const ARRAY_MUTATORS = new Set([
|
|
5
|
+
'push',
|
|
6
|
+
'pop',
|
|
7
|
+
'shift',
|
|
8
|
+
'unshift',
|
|
9
|
+
'splice',
|
|
10
|
+
'sort',
|
|
11
|
+
'reverse',
|
|
12
|
+
'fill',
|
|
13
|
+
'copyWithin',
|
|
14
|
+
]);
|
|
15
|
+
function objectPath(obj, path) {
|
|
16
|
+
return path.reduce((o, k) => o[k], obj);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fork an json object, using copy-on-write strategy to avoid unnecessary deep cloning.
|
|
20
|
+
*/
|
|
21
|
+
export function fork(original) {
|
|
22
|
+
if (original === null || typeof original !== 'object') {
|
|
23
|
+
// primitive value, return directly
|
|
24
|
+
return original;
|
|
25
|
+
}
|
|
26
|
+
let current = original;
|
|
27
|
+
const ensureCopy = () => {
|
|
28
|
+
if (current === original) {
|
|
29
|
+
current = klona(original);
|
|
30
|
+
}
|
|
31
|
+
return current;
|
|
32
|
+
};
|
|
33
|
+
const createProxy = (path = []) => {
|
|
34
|
+
return new Proxy({}, {
|
|
35
|
+
get(_, prop) {
|
|
36
|
+
const target = objectPath(current, path);
|
|
37
|
+
if (prop === UnWrapPropertyKey)
|
|
38
|
+
return target;
|
|
39
|
+
const value = target[prop];
|
|
40
|
+
// return value directly if already copied
|
|
41
|
+
if (current !== original) {
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
// handle array mutator methods
|
|
45
|
+
if (Array.isArray(target) && ARRAY_MUTATORS.has(prop)) {
|
|
46
|
+
return new Proxy(value, {
|
|
47
|
+
apply(fn, thisArg, args) {
|
|
48
|
+
ensureCopy();
|
|
49
|
+
const t = objectPath(current, path);
|
|
50
|
+
return Reflect.apply(t[prop], t, args);
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
// 未复制,嵌套对象需要继续代理
|
|
55
|
+
if (typeof value === 'object' && value !== null) {
|
|
56
|
+
return createProxy([...path, prop]);
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
},
|
|
60
|
+
set(_, prop, value) {
|
|
61
|
+
ensureCopy();
|
|
62
|
+
objectPath(current, path)[prop] = value;
|
|
63
|
+
return true;
|
|
64
|
+
},
|
|
65
|
+
deleteProperty(_, prop) {
|
|
66
|
+
ensureCopy();
|
|
67
|
+
delete objectPath(current, path)[prop];
|
|
68
|
+
return true;
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
return createProxy();
|
|
73
|
+
}
|
|
74
|
+
export function unwrap(proxy) {
|
|
75
|
+
return proxy && proxy[UnWrapPropertyKey] ? proxy[UnWrapPropertyKey] : proxy;
|
|
76
|
+
}
|
package/dist/umd/src/core.js
CHANGED
|
@@ -7,7 +7,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
7
7
|
if (v !== undefined) module.exports = v;
|
|
8
8
|
}
|
|
9
9
|
else if (typeof define === "function" && define.amd) {
|
|
10
|
-
define(["require", "exports", "mitt", "./exception/exception.js", "./util/clone-body.js", "./constant.js", "./util/compose-middleware.js", "./util/shallow-clone.js", "./util/compile-url.js"], factory);
|
|
10
|
+
define(["require", "exports", "mitt", "./exception/exception.js", "./util/clone-body.js", "./constant.js", "./util/compose-middleware.js", "./util/shallow-clone.js", "./util/compile-url.js", "./util/fork.js"], factory);
|
|
11
11
|
}
|
|
12
12
|
})(function (require, exports) {
|
|
13
13
|
"use strict";
|
|
@@ -21,6 +21,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
const compose_middleware_js_1 = require("./util/compose-middleware.js");
|
|
22
22
|
const shallow_clone_js_1 = require("./util/shallow-clone.js");
|
|
23
23
|
const compile_url_js_1 = require("./util/compile-url.js");
|
|
24
|
+
const fork_js_1 = require("./util/fork.js");
|
|
24
25
|
/**
|
|
25
26
|
* @description Keq 核心 API,发送请求必要的原子化的API
|
|
26
27
|
*/
|
|
@@ -124,7 +125,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
124
125
|
await middleware(ctx, async function emptyNext() { });
|
|
125
126
|
const output = ctx[constant_js_1.OUTPUT_PROPERTY];
|
|
126
127
|
if (ctx.options.resolveWithFullResponse || ctx.options.resolveWith === 'response') {
|
|
127
|
-
return ctx.response;
|
|
128
|
+
return ctx.response?.clone();
|
|
128
129
|
}
|
|
129
130
|
const response = ctx.response;
|
|
130
131
|
if (!response) {
|
|
@@ -134,7 +135,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
134
135
|
return await response.text();
|
|
135
136
|
}
|
|
136
137
|
else if (ctx.options.resolveWith === 'json') {
|
|
137
|
-
return await response.json();
|
|
138
|
+
return (0, fork_js_1.unwrap)(await response.json());
|
|
138
139
|
}
|
|
139
140
|
else if (ctx.options.resolveWith === 'form-data') {
|
|
140
141
|
return await response.formData();
|
|
@@ -155,7 +156,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
155
156
|
const contentType = response.headers.get('content-type') || '';
|
|
156
157
|
try {
|
|
157
158
|
if (contentType.includes('application/json')) {
|
|
158
|
-
return await response.json();
|
|
159
|
+
return (0, fork_js_1.unwrap)(await response.json());
|
|
159
160
|
}
|
|
160
161
|
else if (contentType.includes('multipart/form-data')) {
|
|
161
162
|
return await response.formData();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function createResponseProxy(
|
|
1
|
+
export declare function createResponseProxy(response: Response): Response;
|
|
@@ -4,29 +4,59 @@
|
|
|
4
4
|
if (v !== undefined) module.exports = v;
|
|
5
5
|
}
|
|
6
6
|
else if (typeof define === "function" && define.amd) {
|
|
7
|
-
define(["require", "exports"], factory);
|
|
7
|
+
define(["require", "exports", "./fork.js"], factory);
|
|
8
8
|
}
|
|
9
9
|
})(function (require, exports) {
|
|
10
10
|
"use strict";
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.createResponseProxy = void 0;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
const fork_js_1 = require("./fork.js");
|
|
14
|
+
const JsonCachePropertyKey = Symbol('KeqResponseProxyJsonCachePropertyKey');
|
|
15
|
+
const TextCachePropertyKey = Symbol('KeqResponseProxyTextCachePropertyKey');
|
|
16
|
+
function createResponseProxy(response) {
|
|
17
|
+
return new Proxy(response, {
|
|
15
18
|
get(res, prop) {
|
|
16
19
|
if (typeof prop === 'string') {
|
|
17
|
-
if (
|
|
20
|
+
if (prop === 'json') {
|
|
21
|
+
return new Proxy(res[prop], {
|
|
22
|
+
apply() {
|
|
23
|
+
if (res[JsonCachePropertyKey]) {
|
|
24
|
+
return (0, fork_js_1.fork)(res[JsonCachePropertyKey]);
|
|
25
|
+
}
|
|
26
|
+
return res.clone().json()
|
|
27
|
+
.then((body) => {
|
|
28
|
+
res[JsonCachePropertyKey] = body;
|
|
29
|
+
return (0, fork_js_1.fork)(body);
|
|
30
|
+
});
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
else if (prop === 'text') {
|
|
35
|
+
return new Proxy(res[prop], {
|
|
36
|
+
apply() {
|
|
37
|
+
if (res[TextCachePropertyKey]) {
|
|
38
|
+
return (0, fork_js_1.fork)(res[TextCachePropertyKey]);
|
|
39
|
+
}
|
|
40
|
+
return res.clone().text()
|
|
41
|
+
.then((body) => {
|
|
42
|
+
res[TextCachePropertyKey] = body;
|
|
43
|
+
return body;
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else if (['arrayBuffer', 'blob', 'buffer', 'formData'].includes(prop)) {
|
|
18
49
|
/**
|
|
19
50
|
* clone when invoking body, json, text, arrayBuffer, blob, buffer, formData
|
|
20
51
|
* to avoid time-consuming cloning
|
|
21
52
|
*/
|
|
22
53
|
return new Proxy(res[prop], {
|
|
23
54
|
apply(target, thisArg, argArray) {
|
|
24
|
-
|
|
25
|
-
return mirror[prop](...argArray);
|
|
55
|
+
return res.clone()[prop](...argArray);
|
|
26
56
|
},
|
|
27
57
|
});
|
|
28
58
|
}
|
|
29
|
-
if (prop === 'body') {
|
|
59
|
+
else if (prop === 'body') {
|
|
30
60
|
const mirror = res.clone();
|
|
31
61
|
return mirror.body;
|
|
32
62
|
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
(function (factory) {
|
|
2
|
+
if (typeof module === "object" && typeof module.exports === "object") {
|
|
3
|
+
var v = factory(require, exports);
|
|
4
|
+
if (v !== undefined) module.exports = v;
|
|
5
|
+
}
|
|
6
|
+
else if (typeof define === "function" && define.amd) {
|
|
7
|
+
define(["require", "exports", "klona/json"], factory);
|
|
8
|
+
}
|
|
9
|
+
})(function (require, exports) {
|
|
10
|
+
"use strict";
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.unwrap = exports.fork = void 0;
|
|
13
|
+
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
14
|
+
const json_1 = require("klona/json");
|
|
15
|
+
const UnWrapPropertyKey = Symbol('UnWrapPropertyKey');
|
|
16
|
+
const ARRAY_MUTATORS = new Set([
|
|
17
|
+
'push',
|
|
18
|
+
'pop',
|
|
19
|
+
'shift',
|
|
20
|
+
'unshift',
|
|
21
|
+
'splice',
|
|
22
|
+
'sort',
|
|
23
|
+
'reverse',
|
|
24
|
+
'fill',
|
|
25
|
+
'copyWithin',
|
|
26
|
+
]);
|
|
27
|
+
function objectPath(obj, path) {
|
|
28
|
+
return path.reduce((o, k) => o[k], obj);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Fork an json object, using copy-on-write strategy to avoid unnecessary deep cloning.
|
|
32
|
+
*/
|
|
33
|
+
function fork(original) {
|
|
34
|
+
if (original === null || typeof original !== 'object') {
|
|
35
|
+
// primitive value, return directly
|
|
36
|
+
return original;
|
|
37
|
+
}
|
|
38
|
+
let current = original;
|
|
39
|
+
const ensureCopy = () => {
|
|
40
|
+
if (current === original) {
|
|
41
|
+
current = (0, json_1.klona)(original);
|
|
42
|
+
}
|
|
43
|
+
return current;
|
|
44
|
+
};
|
|
45
|
+
const createProxy = (path = []) => {
|
|
46
|
+
return new Proxy({}, {
|
|
47
|
+
get(_, prop) {
|
|
48
|
+
const target = objectPath(current, path);
|
|
49
|
+
if (prop === UnWrapPropertyKey)
|
|
50
|
+
return target;
|
|
51
|
+
const value = target[prop];
|
|
52
|
+
// return value directly if already copied
|
|
53
|
+
if (current !== original) {
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
// handle array mutator methods
|
|
57
|
+
if (Array.isArray(target) && ARRAY_MUTATORS.has(prop)) {
|
|
58
|
+
return new Proxy(value, {
|
|
59
|
+
apply(fn, thisArg, args) {
|
|
60
|
+
ensureCopy();
|
|
61
|
+
const t = objectPath(current, path);
|
|
62
|
+
return Reflect.apply(t[prop], t, args);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// 未复制,嵌套对象需要继续代理
|
|
67
|
+
if (typeof value === 'object' && value !== null) {
|
|
68
|
+
return createProxy([...path, prop]);
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
},
|
|
72
|
+
set(_, prop, value) {
|
|
73
|
+
ensureCopy();
|
|
74
|
+
objectPath(current, path)[prop] = value;
|
|
75
|
+
return true;
|
|
76
|
+
},
|
|
77
|
+
deleteProperty(_, prop) {
|
|
78
|
+
ensureCopy();
|
|
79
|
+
delete objectPath(current, path)[prop];
|
|
80
|
+
return true;
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
return createProxy();
|
|
85
|
+
}
|
|
86
|
+
exports.fork = fork;
|
|
87
|
+
function unwrap(proxy) {
|
|
88
|
+
return proxy && proxy[UnWrapPropertyKey] ? proxy[UnWrapPropertyKey] : proxy;
|
|
89
|
+
}
|
|
90
|
+
exports.unwrap = unwrap;
|
|
91
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keq",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.12",
|
|
4
4
|
"description": "Request API write by Typescript for flexibility, readability, and a low learning curve.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"request",
|
|
@@ -31,18 +31,9 @@
|
|
|
31
31
|
"type": "git",
|
|
32
32
|
"url": "git+https://github.com/keq-request/keq.git"
|
|
33
33
|
},
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "npm run clean && ./build/build.sh",
|
|
36
|
-
"clean": "rm -rf ./dist/*",
|
|
37
|
-
"dev": "npm run clean && ./build/watch.sh",
|
|
38
|
-
"prepare": "ts-patch install -s && is-ci || husky",
|
|
39
|
-
"prepublishOnly": "npm run build",
|
|
40
|
-
"release": "standard-version",
|
|
41
|
-
"release:alpha": "standard-version --prerelease alpha",
|
|
42
|
-
"test": "jest"
|
|
43
|
-
},
|
|
44
34
|
"dependencies": {
|
|
45
35
|
"fastq": "^1.17.1",
|
|
36
|
+
"klona": "^2.0.6",
|
|
46
37
|
"minimatch": "^9.0.4",
|
|
47
38
|
"mitt": "^3.0.1",
|
|
48
39
|
"ts-custom-error": "^3.3.1"
|
|
@@ -68,8 +59,15 @@
|
|
|
68
59
|
"typescript": "5.4.5",
|
|
69
60
|
"typescript-transform-paths": "^3.5.1"
|
|
70
61
|
},
|
|
71
|
-
"packageManager": "pnpm@9.15.1",
|
|
72
62
|
"engines": {
|
|
73
63
|
"node": ">=18.0.0"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "npm run clean && ./build/build.sh",
|
|
67
|
+
"clean": "rm -rf ./dist/*",
|
|
68
|
+
"dev": "npm run clean && ./build/watch.sh",
|
|
69
|
+
"release": "standard-version",
|
|
70
|
+
"release:alpha": "standard-version --prerelease alpha",
|
|
71
|
+
"test": "jest"
|
|
74
72
|
}
|
|
75
|
-
}
|
|
73
|
+
}
|