@nsnanocat/util 2.1.0 → 2.1.2
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/README.md +653 -1
- package/getStorage.mjs +76 -27
- package/index.js +12 -1
- package/lib/app.mjs +13 -2
- package/lib/argument.mjs +38 -8
- package/lib/done.mjs +23 -3
- package/lib/environment.mjs +22 -0
- package/lib/index.js +12 -0
- package/lib/notification.mjs +40 -11
- package/lib/runScript.mjs +21 -0
- package/lib/time.mjs +9 -9
- package/lib/wait.mjs +4 -4
- package/package.json +1 -1
- package/polyfill/Console.mjs +124 -0
- package/polyfill/Lodash.mjs +109 -0
- package/polyfill/StatusTexts.mjs +17 -0
- package/polyfill/Storage.mjs +76 -29
- package/polyfill/fetch.mjs +61 -6
package/polyfill/Console.mjs
CHANGED
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
import { $app } from "../lib/app.mjs";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* 统一日志工具,兼容各脚本平台与 Node.js。
|
|
5
|
+
* Unified logger compatible with script platforms and Node.js.
|
|
6
|
+
*
|
|
7
|
+
* logLevel 用法:
|
|
8
|
+
* logLevel usage:
|
|
9
|
+
* - 可读: `Console.logLevel` 返回 `OFF|ERROR|WARN|INFO|DEBUG|ALL`
|
|
10
|
+
* - Read: `Console.logLevel` returns `OFF|ERROR|WARN|INFO|DEBUG|ALL`
|
|
11
|
+
* - 可写: 数字 `0~5` 或字符串 `off/error/warn/info/debug/all`
|
|
12
|
+
* - Write: number `0~5` or string `off/error/warn/info/debug/all`
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* Console.logLevel = "debug";
|
|
16
|
+
* Console.debug("only shown when level >= DEBUG");
|
|
17
|
+
* Console.logLevel = 2; // WARN
|
|
18
|
+
*/
|
|
3
19
|
export class Console {
|
|
4
20
|
static #counts = new Map([]);
|
|
5
21
|
static #groups = [];
|
|
6
22
|
static #times = new Map([]);
|
|
7
23
|
|
|
24
|
+
/**
|
|
25
|
+
* 清空控制台(当前为空实现)。
|
|
26
|
+
* Clear console (currently a no-op).
|
|
27
|
+
*
|
|
28
|
+
* @returns {void}
|
|
29
|
+
*/
|
|
8
30
|
static clear = () => {};
|
|
9
31
|
|
|
32
|
+
/**
|
|
33
|
+
* 增加计数器并打印当前值。
|
|
34
|
+
* Increment counter and print the current value.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} [label="default"] 计数器名称 / Counter label.
|
|
37
|
+
* @returns {void}
|
|
38
|
+
*/
|
|
10
39
|
static count = (label = "default") => {
|
|
11
40
|
switch (Console.#counts.has(label)) {
|
|
12
41
|
case true:
|
|
@@ -19,6 +48,13 @@ export class Console {
|
|
|
19
48
|
Console.log(`${label}: ${Console.#counts.get(label)}`);
|
|
20
49
|
};
|
|
21
50
|
|
|
51
|
+
/**
|
|
52
|
+
* 重置计数器。
|
|
53
|
+
* Reset a counter.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} [label="default"] 计数器名称 / Counter label.
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
22
58
|
static countReset = (label = "default") => {
|
|
23
59
|
switch (Console.#counts.has(label)) {
|
|
24
60
|
case true:
|
|
@@ -31,12 +67,26 @@ export class Console {
|
|
|
31
67
|
}
|
|
32
68
|
};
|
|
33
69
|
|
|
70
|
+
/**
|
|
71
|
+
* 输出调试日志。
|
|
72
|
+
* Print debug logs.
|
|
73
|
+
*
|
|
74
|
+
* @param {...any} msg 日志内容 / Log messages.
|
|
75
|
+
* @returns {void}
|
|
76
|
+
*/
|
|
34
77
|
static debug = (...msg) => {
|
|
35
78
|
if (Console.#level < 4) return;
|
|
36
79
|
msg = msg.map(m => `🅱️ ${m}`);
|
|
37
80
|
Console.log(...msg);
|
|
38
81
|
};
|
|
39
82
|
|
|
83
|
+
/**
|
|
84
|
+
* 输出错误日志。
|
|
85
|
+
* Print error logs.
|
|
86
|
+
*
|
|
87
|
+
* @param {...any} msg 日志内容 / Log messages.
|
|
88
|
+
* @returns {void}
|
|
89
|
+
*/
|
|
40
90
|
static error(...msg) {
|
|
41
91
|
if (Console.#level < 1) return;
|
|
42
92
|
switch ($app) {
|
|
@@ -56,12 +106,39 @@ export class Console {
|
|
|
56
106
|
Console.log(...msg);
|
|
57
107
|
}
|
|
58
108
|
|
|
109
|
+
/**
|
|
110
|
+
* `error` 的别名。
|
|
111
|
+
* Alias of `error`.
|
|
112
|
+
*
|
|
113
|
+
* @param {...any} msg 日志内容 / Log messages.
|
|
114
|
+
* @returns {void}
|
|
115
|
+
*/
|
|
59
116
|
static exception = (...msg) => Console.error(...msg);
|
|
60
117
|
|
|
118
|
+
/**
|
|
119
|
+
* 进入日志分组。
|
|
120
|
+
* Enter a log group.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} label 分组名 / Group label.
|
|
123
|
+
* @returns {number}
|
|
124
|
+
*/
|
|
61
125
|
static group = label => Console.#groups.unshift(label);
|
|
62
126
|
|
|
127
|
+
/**
|
|
128
|
+
* 退出日志分组。
|
|
129
|
+
* Exit the latest log group.
|
|
130
|
+
*
|
|
131
|
+
* @returns {*}
|
|
132
|
+
*/
|
|
63
133
|
static groupEnd = () => Console.#groups.shift();
|
|
64
134
|
|
|
135
|
+
/**
|
|
136
|
+
* 输出信息日志。
|
|
137
|
+
* Print info logs.
|
|
138
|
+
*
|
|
139
|
+
* @param {...any} msg 日志内容 / Log messages.
|
|
140
|
+
* @returns {void}
|
|
141
|
+
*/
|
|
65
142
|
static info(...msg) {
|
|
66
143
|
if (Console.#level < 3) return;
|
|
67
144
|
msg = msg.map(m => `ℹ️ ${m}`);
|
|
@@ -70,6 +147,12 @@ export class Console {
|
|
|
70
147
|
|
|
71
148
|
static #level = 3;
|
|
72
149
|
|
|
150
|
+
/**
|
|
151
|
+
* 获取日志级别文本。
|
|
152
|
+
* Get current log level text.
|
|
153
|
+
*
|
|
154
|
+
* @returns {"OFF"|"ERROR"|"WARN"|"INFO"|"DEBUG"|"ALL"}
|
|
155
|
+
*/
|
|
73
156
|
static get logLevel() {
|
|
74
157
|
switch (Console.#level) {
|
|
75
158
|
case 0:
|
|
@@ -88,6 +171,12 @@ export class Console {
|
|
|
88
171
|
}
|
|
89
172
|
}
|
|
90
173
|
|
|
174
|
+
/**
|
|
175
|
+
* 设置日志级别。
|
|
176
|
+
* Set current log level.
|
|
177
|
+
*
|
|
178
|
+
* @param {number|string} level 级别值 / Level value.
|
|
179
|
+
*/
|
|
91
180
|
static set logLevel(level) {
|
|
92
181
|
switch (typeof level) {
|
|
93
182
|
case "string":
|
|
@@ -130,6 +219,13 @@ export class Console {
|
|
|
130
219
|
}
|
|
131
220
|
}
|
|
132
221
|
|
|
222
|
+
/**
|
|
223
|
+
* 输出通用日志。
|
|
224
|
+
* Print generic logs.
|
|
225
|
+
*
|
|
226
|
+
* @param {...any} msg 日志内容 / Log messages.
|
|
227
|
+
* @returns {void}
|
|
228
|
+
*/
|
|
133
229
|
static log = (...msg) => {
|
|
134
230
|
if (Console.#level === 0) return;
|
|
135
231
|
msg = msg.map(log => {
|
|
@@ -157,16 +253,44 @@ export class Console {
|
|
|
157
253
|
console.log(msg.join("\n"));
|
|
158
254
|
};
|
|
159
255
|
|
|
256
|
+
/**
|
|
257
|
+
* 开始计时。
|
|
258
|
+
* Start timer.
|
|
259
|
+
*
|
|
260
|
+
* @param {string} [label="default"] 计时器名称 / Timer label.
|
|
261
|
+
* @returns {Map<string, number>}
|
|
262
|
+
*/
|
|
160
263
|
static time = (label = "default") => Console.#times.set(label, Date.now());
|
|
161
264
|
|
|
265
|
+
/**
|
|
266
|
+
* 结束计时并移除计时器。
|
|
267
|
+
* End timer and remove it.
|
|
268
|
+
*
|
|
269
|
+
* @param {string} [label="default"] 计时器名称 / Timer label.
|
|
270
|
+
* @returns {boolean}
|
|
271
|
+
*/
|
|
162
272
|
static timeEnd = (label = "default") => Console.#times.delete(label);
|
|
163
273
|
|
|
274
|
+
/**
|
|
275
|
+
* 输出当前计时器耗时。
|
|
276
|
+
* Print elapsed time for a timer.
|
|
277
|
+
*
|
|
278
|
+
* @param {string} [label="default"] 计时器名称 / Timer label.
|
|
279
|
+
* @returns {void}
|
|
280
|
+
*/
|
|
164
281
|
static timeLog = (label = "default") => {
|
|
165
282
|
const time = Console.#times.get(label);
|
|
166
283
|
if (time) Console.log(`${label}: ${Date.now() - time}ms`);
|
|
167
284
|
else Console.warn(`Timer "${label}" doesn’t exist`);
|
|
168
285
|
};
|
|
169
286
|
|
|
287
|
+
/**
|
|
288
|
+
* 输出警告日志。
|
|
289
|
+
* Print warning logs.
|
|
290
|
+
*
|
|
291
|
+
* @param {...any} msg 日志内容 / Log messages.
|
|
292
|
+
* @returns {void}
|
|
293
|
+
*/
|
|
170
294
|
static warn(...msg) {
|
|
171
295
|
if (Console.#level < 2) return;
|
|
172
296
|
msg = msg.map(m => `⚠️ ${m}`);
|
package/polyfill/Lodash.mjs
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
/* https://www.lodashjs.com */
|
|
2
|
+
/**
|
|
3
|
+
* 轻量 Lodash 工具集。
|
|
4
|
+
* Lightweight Lodash-like utilities.
|
|
5
|
+
*
|
|
6
|
+
* 说明:
|
|
7
|
+
* Notes:
|
|
8
|
+
* - 这是 Lodash 的“部分方法”简化实现,不等价于完整 Lodash
|
|
9
|
+
* - This is a simplified subset, not a full Lodash implementation
|
|
10
|
+
* - 各方法语义可参考 Lodash 官方文档
|
|
11
|
+
* - Method semantics can be referenced from official Lodash docs
|
|
12
|
+
* - 导入时建议使用 `Lodash as _`,遵循 lodash 官方示例惯例
|
|
13
|
+
* - Use `Lodash as _` when importing, following official lodash example convention
|
|
14
|
+
*
|
|
15
|
+
* 参考:
|
|
16
|
+
* Reference:
|
|
17
|
+
* - https://www.lodashjs.com
|
|
18
|
+
* - https://lodash.com
|
|
19
|
+
*/
|
|
2
20
|
export class Lodash {
|
|
21
|
+
/**
|
|
22
|
+
* HTML 特殊字符转义。
|
|
23
|
+
* Escape HTML special characters.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} string 输入文本 / Input text.
|
|
26
|
+
* @returns {string}
|
|
27
|
+
* @see {@link https://lodash.com/docs/#escape lodash.escape}
|
|
28
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.escape lodash.escape (中文)}
|
|
29
|
+
*/
|
|
3
30
|
static escape(string) {
|
|
4
31
|
const map = {
|
|
5
32
|
"&": "&",
|
|
@@ -11,6 +38,17 @@ export class Lodash {
|
|
|
11
38
|
return string.replace(/[&<>"']/g, m => map[m]);
|
|
12
39
|
}
|
|
13
40
|
|
|
41
|
+
/**
|
|
42
|
+
* 按路径读取对象值。
|
|
43
|
+
* Get object value by path.
|
|
44
|
+
*
|
|
45
|
+
* @param {object} [object={}] 目标对象 / Target object.
|
|
46
|
+
* @param {string|string[]} [path=""] 路径 / Path.
|
|
47
|
+
* @param {*} [defaultValue=undefined] 默认值 / Default value.
|
|
48
|
+
* @returns {*}
|
|
49
|
+
* @see {@link https://lodash.com/docs/#get lodash.get}
|
|
50
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.get lodash.get (中文)}
|
|
51
|
+
*/
|
|
14
52
|
static get(object = {}, path = "", defaultValue = undefined) {
|
|
15
53
|
// translate array case to dot case, then split with .
|
|
16
54
|
// a[0].b -> a.0.b -> ['a', '0', 'b']
|
|
@@ -24,7 +62,9 @@ export class Lodash {
|
|
|
24
62
|
|
|
25
63
|
/**
|
|
26
64
|
* 递归合并源对象的自身可枚举属性到目标对象
|
|
65
|
+
* Recursively merge source enumerable properties into target object.
|
|
27
66
|
* @description 简化版 lodash.merge,用于合并配置对象
|
|
67
|
+
* @description A simplified lodash.merge for config merging.
|
|
28
68
|
*
|
|
29
69
|
* 适用情况:
|
|
30
70
|
* - 合并嵌套的配置/设置对象
|
|
@@ -41,8 +81,13 @@ export class Lodash {
|
|
|
41
81
|
* - 会修改原始目标对象 (mutates target)
|
|
42
82
|
*
|
|
43
83
|
* @param {object} object - 目标对象
|
|
84
|
+
* @param {object} object - Target object.
|
|
44
85
|
* @param {...object} sources - 源对象(可多个)
|
|
86
|
+
* @param {...object} sources - Source objects.
|
|
45
87
|
* @returns {object} 返回合并后的目标对象
|
|
88
|
+
* @returns {object} Merged target object.
|
|
89
|
+
* @see {@link https://lodash.com/docs/#merge lodash.merge}
|
|
90
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.merge lodash.merge (中文)}
|
|
46
91
|
* @example
|
|
47
92
|
* const target = { a: { b: 1 }, c: 2 };
|
|
48
93
|
* const source = { a: { d: 3 }, e: 4 };
|
|
@@ -99,8 +144,13 @@ export class Lodash {
|
|
|
99
144
|
|
|
100
145
|
/**
|
|
101
146
|
* 判断值是否为普通对象 (Plain Object)
|
|
147
|
+
* Check whether a value is a plain object.
|
|
102
148
|
* @param {*} value - 要检查的值
|
|
149
|
+
* @param {*} value - Value to check.
|
|
103
150
|
* @returns {boolean} 如果是普通对象返回 true
|
|
151
|
+
* @returns {boolean} Returns true when value is a plain object.
|
|
152
|
+
* @see {@link https://lodash.com/docs/#isPlainObject lodash.isPlainObject}
|
|
153
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.isPlainObject lodash.isPlainObject (中文)}
|
|
104
154
|
*/
|
|
105
155
|
static #isPlainObject(value) {
|
|
106
156
|
if (value === null || typeof value !== "object") return false;
|
|
@@ -108,24 +158,64 @@ export class Lodash {
|
|
|
108
158
|
return proto === null || proto === Object.prototype;
|
|
109
159
|
}
|
|
110
160
|
|
|
161
|
+
/**
|
|
162
|
+
* 删除对象指定路径并返回对象。
|
|
163
|
+
* Omit paths from object and return the same object.
|
|
164
|
+
*
|
|
165
|
+
* @param {object} [object={}] 目标对象 / Target object.
|
|
166
|
+
* @param {string|string[]} [paths=[]] 要删除的路径 / Paths to remove.
|
|
167
|
+
* @returns {object}
|
|
168
|
+
* @see {@link https://lodash.com/docs/#omit lodash.omit}
|
|
169
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.omit lodash.omit (中文)}
|
|
170
|
+
*/
|
|
111
171
|
static omit(object = {}, paths = []) {
|
|
112
172
|
if (!Array.isArray(paths)) paths = [paths.toString()];
|
|
113
173
|
paths.forEach(path => Lodash.unset(object, path));
|
|
114
174
|
return object;
|
|
115
175
|
}
|
|
116
176
|
|
|
177
|
+
/**
|
|
178
|
+
* 仅保留对象指定键(第一层)。
|
|
179
|
+
* Pick selected keys from object (top level only).
|
|
180
|
+
*
|
|
181
|
+
* @param {object} [object={}] 目标对象 / Target object.
|
|
182
|
+
* @param {string|string[]} [paths=[]] 需要保留的键 / Keys to keep.
|
|
183
|
+
* @returns {object}
|
|
184
|
+
* @see {@link https://lodash.com/docs/#pick lodash.pick}
|
|
185
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.pick lodash.pick (中文)}
|
|
186
|
+
*/
|
|
117
187
|
static pick(object = {}, paths = []) {
|
|
118
188
|
if (!Array.isArray(paths)) paths = [paths.toString()];
|
|
119
189
|
const filteredEntries = Object.entries(object).filter(([key, value]) => paths.includes(key));
|
|
120
190
|
return Object.fromEntries(filteredEntries);
|
|
121
191
|
}
|
|
122
192
|
|
|
193
|
+
/**
|
|
194
|
+
* 按路径写入对象值。
|
|
195
|
+
* Set object value by path.
|
|
196
|
+
*
|
|
197
|
+
* @param {object} object 目标对象 / Target object.
|
|
198
|
+
* @param {string|string[]} path 路径 / Path.
|
|
199
|
+
* @param {*} value 写入值 / Value.
|
|
200
|
+
* @returns {object}
|
|
201
|
+
* @see {@link https://lodash.com/docs/#set lodash.set}
|
|
202
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.set lodash.set (中文)}
|
|
203
|
+
*/
|
|
123
204
|
static set(object, path, value) {
|
|
124
205
|
if (!Array.isArray(path)) path = Lodash.toPath(path);
|
|
125
206
|
path.slice(0, -1).reduce((previousValue, currentValue, currentIndex) => (Object(previousValue[currentValue]) === previousValue[currentValue] ? previousValue[currentValue] : (previousValue[currentValue] = /^\d+$/.test(path[currentIndex + 1]) ? [] : {})), object)[path[path.length - 1]] = value;
|
|
126
207
|
return object;
|
|
127
208
|
}
|
|
128
209
|
|
|
210
|
+
/**
|
|
211
|
+
* 将点路径或数组下标路径转换为数组。
|
|
212
|
+
* Convert dot/array-index path string into path segments.
|
|
213
|
+
*
|
|
214
|
+
* @param {string} value 路径字符串 / Path string.
|
|
215
|
+
* @returns {string[]}
|
|
216
|
+
* @see {@link https://lodash.com/docs/#toPath lodash.toPath}
|
|
217
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.toPath lodash.toPath (中文)}
|
|
218
|
+
*/
|
|
129
219
|
static toPath(value) {
|
|
130
220
|
return value
|
|
131
221
|
.replace(/\[(\d+)\]/g, ".$1")
|
|
@@ -133,6 +223,15 @@ export class Lodash {
|
|
|
133
223
|
.filter(Boolean);
|
|
134
224
|
}
|
|
135
225
|
|
|
226
|
+
/**
|
|
227
|
+
* HTML 实体反转义。
|
|
228
|
+
* Unescape HTML entities.
|
|
229
|
+
*
|
|
230
|
+
* @param {string} string 输入文本 / Input text.
|
|
231
|
+
* @returns {string}
|
|
232
|
+
* @see {@link https://lodash.com/docs/#unescape lodash.unescape}
|
|
233
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.unescape lodash.unescape (中文)}
|
|
234
|
+
*/
|
|
136
235
|
static unescape(string) {
|
|
137
236
|
const map = {
|
|
138
237
|
"&": "&",
|
|
@@ -144,6 +243,16 @@ export class Lodash {
|
|
|
144
243
|
return string.replace(/&|<|>|"|'/g, m => map[m]);
|
|
145
244
|
}
|
|
146
245
|
|
|
246
|
+
/**
|
|
247
|
+
* 删除对象路径对应的值。
|
|
248
|
+
* Remove value by object path.
|
|
249
|
+
*
|
|
250
|
+
* @param {object} [object={}] 目标对象 / Target object.
|
|
251
|
+
* @param {string|string[]} [path=""] 路径 / Path.
|
|
252
|
+
* @returns {boolean}
|
|
253
|
+
* @see {@link https://lodash.com/docs/#unset lodash.unset}
|
|
254
|
+
* @see {@link https://www.lodashjs.com/docs/lodash.unset lodash.unset (中文)}
|
|
255
|
+
*/
|
|
147
256
|
static unset(object = {}, path = "") {
|
|
148
257
|
if (!Array.isArray(path)) path = Lodash.toPath(path);
|
|
149
258
|
const result = path.reduce((previousValue, currentValue, currentIndex) => {
|
package/polyfill/StatusTexts.mjs
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 状态码文本映射表。
|
|
3
|
+
* HTTP status code to status text map.
|
|
4
|
+
*
|
|
5
|
+
* 主要用途:
|
|
6
|
+
* Primary usage:
|
|
7
|
+
* - 为 Quantumult X 的 `$done` 状态行拼接提供状态文本
|
|
8
|
+
* - Provide status text for Quantumult X `$done` status-line composition
|
|
9
|
+
* - QX 在部分场景要求 `status` 为完整状态行(如 `HTTP/1.1 200 OK`)
|
|
10
|
+
* - QX may require full status line (e.g. `HTTP/1.1 200 OK`) in some cases
|
|
11
|
+
*
|
|
12
|
+
* 参考:
|
|
13
|
+
* Reference:
|
|
14
|
+
* - https://github.com/crossutility/Quantumult-X/raw/refs/heads/master/sample-rewrite-response-header.js
|
|
15
|
+
*
|
|
16
|
+
* @type {Record<number, string>}
|
|
17
|
+
*/
|
|
1
18
|
export const StatusTexts = {
|
|
2
19
|
100: "Continue",
|
|
3
20
|
101: "Switching Protocols",
|
package/polyfill/Storage.mjs
CHANGED
|
@@ -2,36 +2,70 @@ import { $app } from "../lib/app.mjs";
|
|
|
2
2
|
import { Lodash as _ } from "./Lodash.mjs";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* 跨平台持久化存储适配器。
|
|
6
|
+
* Cross-platform persistent storage adapter.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* 设计目标:
|
|
9
|
+
* Design goal:
|
|
10
|
+
* - 仿照 Web Storage (`Storage`) 接口设计
|
|
11
|
+
* - Modeled after Web Storage (`Storage`) interface
|
|
12
|
+
* - 统一 VPN App 脚本环境中的持久化读写接口
|
|
13
|
+
* - Unify persistence APIs across VPN app script environments
|
|
14
|
+
*
|
|
15
|
+
* 支持后端:
|
|
16
|
+
* Supported backends:
|
|
17
|
+
* - Surge/Loon/Stash/Egern/Shadowrocket: `$persistentStore`
|
|
18
|
+
* - Quantumult X: `$prefs`
|
|
19
|
+
* - Node.js: 本地 `box.dat`
|
|
20
|
+
* - Node.js: local `box.dat`
|
|
21
|
+
*
|
|
22
|
+
* 支持路径键:
|
|
23
|
+
* Supports path key:
|
|
24
|
+
* - `@root.path.to.value`
|
|
25
|
+
*
|
|
26
|
+
* 与 Web Storage 的已知差异:
|
|
27
|
+
* Known differences from Web Storage:
|
|
28
|
+
* - 支持 `@key.path` 深路径读写(Web Storage 原生不支持)
|
|
29
|
+
* - Supports `@key.path` deep-path access (not native in Web Storage)
|
|
30
|
+
* - `removeItem/clear` 并非所有平台都可用
|
|
31
|
+
* - `removeItem/clear` are not available on every platform
|
|
32
|
+
* - 读取时会尝试 `JSON.parse`,写入对象会 `JSON.stringify`
|
|
33
|
+
* - Reads try `JSON.parse`, writes stringify objects
|
|
34
|
+
*
|
|
35
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Storage
|
|
36
|
+
* @link https://developer.mozilla.org/zh-CN/docs/Web/API/Storage
|
|
11
37
|
*/
|
|
12
38
|
export class Storage {
|
|
13
39
|
/**
|
|
14
|
-
*
|
|
40
|
+
* Node.js 环境下的内存数据缓存。
|
|
41
|
+
* In-memory data cache for Node.js runtime.
|
|
15
42
|
*
|
|
16
|
-
* @
|
|
17
|
-
* @type {file}
|
|
43
|
+
* @type {Record<string, any>|null}
|
|
18
44
|
*/
|
|
19
45
|
static data = null;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Node.js 持久化文件名。
|
|
49
|
+
* Data file name used in Node.js.
|
|
50
|
+
*
|
|
51
|
+
* @type {string}
|
|
52
|
+
*/
|
|
20
53
|
static dataFile = "box.dat";
|
|
54
|
+
|
|
21
55
|
/**
|
|
22
|
-
*
|
|
56
|
+
* `@key.path` 解析正则。
|
|
57
|
+
* Regex for `@key.path` parsing.
|
|
23
58
|
*
|
|
24
|
-
* @
|
|
25
|
-
* @type {regexp}
|
|
59
|
+
* @type {RegExp}
|
|
26
60
|
*/
|
|
27
61
|
static #nameRegex = /^@(?<key>[^.]+)(?:\.(?<path>.*))?$/;
|
|
28
62
|
|
|
29
63
|
/**
|
|
30
|
-
*
|
|
64
|
+
* 读取存储值。
|
|
65
|
+
* Read value from persistent storage.
|
|
31
66
|
*
|
|
32
|
-
* @
|
|
33
|
-
* @param {
|
|
34
|
-
* @param {*} [defaultValue]
|
|
67
|
+
* @param {string} keyName 键名或路径键 / Key or path key.
|
|
68
|
+
* @param {*} [defaultValue=null] 默认值 / Default value when key is missing.
|
|
35
69
|
* @returns {*}
|
|
36
70
|
*/
|
|
37
71
|
static getItem(keyName, defaultValue = null) {
|
|
@@ -80,11 +114,11 @@ export class Storage {
|
|
|
80
114
|
}
|
|
81
115
|
|
|
82
116
|
/**
|
|
83
|
-
*
|
|
117
|
+
* 写入存储值。
|
|
118
|
+
* Write value into persistent storage.
|
|
84
119
|
*
|
|
85
|
-
* @
|
|
86
|
-
* @param {
|
|
87
|
-
* @param {*} keyValue
|
|
120
|
+
* @param {string} keyName 键名或路径键 / Key or path key.
|
|
121
|
+
* @param {*} keyValue 写入值 / Value to store.
|
|
88
122
|
* @returns {boolean}
|
|
89
123
|
*/
|
|
90
124
|
static setItem(keyName = new String(), keyValue = new String()) {
|
|
@@ -135,10 +169,16 @@ export class Storage {
|
|
|
135
169
|
}
|
|
136
170
|
|
|
137
171
|
/**
|
|
138
|
-
*
|
|
172
|
+
* 删除存储值。
|
|
173
|
+
* Remove value from persistent storage.
|
|
139
174
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
175
|
+
* 平台说明:
|
|
176
|
+
* Platform notes:
|
|
177
|
+
* - Quantumult X: `$prefs.removeValueForKey`
|
|
178
|
+
* - Surge: 通过 `$persistentStore.write(null, keyName)` 删除
|
|
179
|
+
* - 其余平台当前返回 `false`
|
|
180
|
+
*
|
|
181
|
+
* @param {string} keyName 键名或路径键 / Key or path key.
|
|
142
182
|
* @returns {boolean}
|
|
143
183
|
*/
|
|
144
184
|
static removeItem(keyName) {
|
|
@@ -156,6 +196,8 @@ export class Storage {
|
|
|
156
196
|
default:
|
|
157
197
|
switch ($app) {
|
|
158
198
|
case "Surge":
|
|
199
|
+
result = $persistentStore.write(null, keyName);
|
|
200
|
+
break;
|
|
159
201
|
case "Loon":
|
|
160
202
|
case "Stash":
|
|
161
203
|
case "Egern":
|
|
@@ -178,9 +220,9 @@ export class Storage {
|
|
|
178
220
|
}
|
|
179
221
|
|
|
180
222
|
/**
|
|
181
|
-
*
|
|
223
|
+
* 清空存储(仅 Quantumult X 支持)。
|
|
224
|
+
* Clear storage (supported by Quantumult X only).
|
|
182
225
|
*
|
|
183
|
-
* @static
|
|
184
226
|
* @returns {boolean}
|
|
185
227
|
*/
|
|
186
228
|
static clear() {
|
|
@@ -207,10 +249,12 @@ export class Storage {
|
|
|
207
249
|
}
|
|
208
250
|
|
|
209
251
|
/**
|
|
210
|
-
*
|
|
252
|
+
* 从 Node.js 数据文件加载 JSON。
|
|
253
|
+
* Load JSON data from Node.js data file.
|
|
211
254
|
*
|
|
212
|
-
* @
|
|
213
|
-
* @
|
|
255
|
+
* @private
|
|
256
|
+
* @param {string} dataFile 数据文件名 / Data file name.
|
|
257
|
+
* @returns {Record<string, any>}
|
|
214
258
|
*/
|
|
215
259
|
static #loaddata = dataFile => {
|
|
216
260
|
if ($app === "Node.js") {
|
|
@@ -232,9 +276,12 @@ export class Storage {
|
|
|
232
276
|
};
|
|
233
277
|
|
|
234
278
|
/**
|
|
235
|
-
*
|
|
279
|
+
* 将内存数据写入 Node.js 数据文件。
|
|
280
|
+
* Persist in-memory data to Node.js data file.
|
|
236
281
|
*
|
|
237
|
-
* @
|
|
282
|
+
* @private
|
|
283
|
+
* @param {string} [dataFile=this.dataFile] 数据文件名 / Data file name.
|
|
284
|
+
* @returns {void}
|
|
238
285
|
*/
|
|
239
286
|
static #writedata = (dataFile = this.dataFile) => {
|
|
240
287
|
if ($app === "Node.js") {
|
package/polyfill/fetch.mjs
CHANGED
|
@@ -4,14 +4,69 @@ import { Lodash as _ } from "./Lodash.mjs";
|
|
|
4
4
|
import { StatusTexts } from "./StatusTexts.mjs";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* 统一请求参数。
|
|
8
|
+
* Unified request payload.
|
|
8
9
|
*
|
|
9
|
-
* @
|
|
10
|
-
* @
|
|
10
|
+
* @typedef {object} FetchRequest
|
|
11
|
+
* @property {string} url 请求地址 / Request URL.
|
|
12
|
+
* @property {string} [method] 请求方法 / HTTP method.
|
|
13
|
+
* @property {Record<string, any>} [headers] 请求头 / Request headers.
|
|
14
|
+
* @property {string|ArrayBuffer|ArrayBufferView|object} [body] 请求体 / Request body.
|
|
15
|
+
* @property {ArrayBuffer} [bodyBytes] 二进制请求体 / Binary request body.
|
|
16
|
+
* @property {number|string} [timeout] 超时(秒或毫秒)/ Timeout (seconds or milliseconds).
|
|
17
|
+
* @property {string} [policy] 指定策略 / Preferred policy.
|
|
18
|
+
* @property {boolean} [redirection] 是否跟随重定向 / Whether to follow redirects.
|
|
19
|
+
* @property {boolean} ["auto-redirect"] 平台重定向字段 / Platform redirect flag.
|
|
20
|
+
* @property {Record<string, any>} [opts] 平台扩展字段 / Platform extension fields.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 统一响应结构。
|
|
25
|
+
* Unified response payload.
|
|
26
|
+
*
|
|
27
|
+
* @typedef {object} FetchResponse
|
|
28
|
+
* @property {boolean} ok 请求是否成功 / Whether request is successful.
|
|
29
|
+
* @property {number} status 状态码 / HTTP status code.
|
|
30
|
+
* @property {number} [statusCode] 状态码别名 / Status code alias.
|
|
31
|
+
* @property {string} [statusText] 状态文本 / HTTP status text.
|
|
32
|
+
* @property {Record<string, any>} [headers] 响应头 / Response headers.
|
|
33
|
+
* @property {string|ArrayBuffer} [body] 响应体 / Response body.
|
|
34
|
+
* @property {ArrayBuffer} [bodyBytes] 二进制响应体 / Binary response body.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 跨平台 `fetch` 适配层。
|
|
39
|
+
* Cross-platform `fetch` adapter.
|
|
40
|
+
*
|
|
41
|
+
* 设计目标:
|
|
42
|
+
* Design goal:
|
|
43
|
+
* - 仿照 Web API `fetch`(`Window.fetch`)接口设计
|
|
44
|
+
* - Modeled after Web API `fetch` (`Window.fetch`)
|
|
45
|
+
* - 统一 VPN App 与 Node.js 环境中的请求调用
|
|
46
|
+
* - Unify request calls across VPN apps and Node.js
|
|
47
|
+
*
|
|
48
|
+
* 功能:
|
|
49
|
+
* Features:
|
|
50
|
+
* - 统一 Quantumult X / Loon / Surge / Stash / Egern / Shadowrocket / Node.js 请求接口
|
|
51
|
+
* - Normalize request APIs across Quantumult X / Loon / Surge / Stash / Egern / Shadowrocket / Node.js
|
|
52
|
+
* - 统一返回体字段(`ok/status/statusText/body/bodyBytes`)
|
|
53
|
+
* - Normalize response fields (`ok/status/statusText/body/bodyBytes`)
|
|
54
|
+
*
|
|
55
|
+
* 与 Web `fetch` 的已知差异:
|
|
56
|
+
* Known differences from Web `fetch`:
|
|
57
|
+
* - 支持 `policy`、`auto-redirect` 等平台扩展字段
|
|
58
|
+
* - Supports platform extension fields like `policy` and `auto-redirect`
|
|
59
|
+
* - 非浏览器平台通过 `$httpClient/$task` 实现,不是原生 Fetch 实现
|
|
60
|
+
* - Non-browser platforms use `$httpClient/$task` instead of native Fetch engine
|
|
61
|
+
* - 返回结构包含 `statusCode/bodyBytes` 等兼容字段
|
|
62
|
+
* - Response includes compatibility fields like `statusCode/bodyBytes`
|
|
63
|
+
*
|
|
64
|
+
* @link https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch
|
|
65
|
+
* @link https://developer.mozilla.org/zh-CN/docs/Web/API/Window/fetch
|
|
11
66
|
* @async
|
|
12
|
-
* @param {
|
|
13
|
-
* @param {
|
|
14
|
-
* @returns {Promise<
|
|
67
|
+
* @param {FetchRequest|string} resource 请求对象或 URL / Request object or URL string.
|
|
68
|
+
* @param {Partial<FetchRequest>} [options={}] 追加参数 / Extra options.
|
|
69
|
+
* @returns {Promise<FetchResponse>}
|
|
15
70
|
*/
|
|
16
71
|
export async function fetch(resource, options = {}) {
|
|
17
72
|
// 初始化参数
|