@nsnanocat/util 2.1.0 → 2.1.1
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 +601 -1
- package/getStorage.mjs +40 -5
- package/index.js +12 -1
- package/lib/app.mjs +13 -2
- package/lib/argument.mjs +18 -0
- 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 +87 -0
- package/polyfill/StatusTexts.mjs +17 -0
- package/polyfill/Storage.mjs +68 -29
- package/polyfill/fetch.mjs +61 -6
package/lib/app.mjs
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 当前运行平台名称。
|
|
3
|
+
* Current runtime platform name.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
+
* 识别顺序:
|
|
6
|
+
* Detection order:
|
|
7
|
+
* 1) `$task` -> Quantumult X
|
|
8
|
+
* 2) `$loon` -> Loon
|
|
9
|
+
* 3) `$rocket` -> Shadowrocket
|
|
10
|
+
* 4) `module` -> Node.js
|
|
11
|
+
* 5) `Egern` -> Egern
|
|
12
|
+
* 6) `$environment["surge-version"]` -> Surge
|
|
13
|
+
* 7) `$environment["stash-version"]` -> Stash
|
|
14
|
+
*
|
|
15
|
+
* @type {("Quantumult X" | "Loon" | "Shadowrocket" | "Node.js" | "Egern" | "Surge" | "Stash" | undefined)}
|
|
5
16
|
*/
|
|
6
17
|
export const $app = (() => {
|
|
7
18
|
const keys = Object.keys(globalThis);
|
package/lib/argument.mjs
CHANGED
|
@@ -1,6 +1,24 @@
|
|
|
1
1
|
import { Console } from "../polyfill/Console.mjs";
|
|
2
2
|
import { Lodash as _ } from "../polyfill/Lodash.mjs";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 统一 `$argument` 输入格式并展开深路径。
|
|
6
|
+
* Normalize `$argument` input format and expand deep paths.
|
|
7
|
+
*
|
|
8
|
+
* 平台差异:
|
|
9
|
+
* Platform differences:
|
|
10
|
+
* - Surge / Stash / Egern 常见为字符串参数: `a=1&b=2`
|
|
11
|
+
* - Surge / Stash / Egern usually pass string args: `a=1&b=2`
|
|
12
|
+
* - Loon 支持字符串和对象两种形态
|
|
13
|
+
* - Loon supports both string and object forms
|
|
14
|
+
* - Quantumult X / Shadowrocket 一般不提供 `$argument`
|
|
15
|
+
* - Quantumult X / Shadowrocket usually do not expose `$argument`
|
|
16
|
+
*
|
|
17
|
+
* 执行时机:
|
|
18
|
+
* Execution timing:
|
|
19
|
+
* - 该模块为即时执行模块,`import` 时立即处理全局 `$argument`
|
|
20
|
+
* - This module executes immediately and mutates global `$argument` on import
|
|
21
|
+
*/
|
|
4
22
|
(() => {
|
|
5
23
|
Console.debug("☑️ $argument");
|
|
6
24
|
switch (typeof $argument) {
|
package/lib/done.mjs
CHANGED
|
@@ -4,10 +4,30 @@ import { Lodash as _ } from "../polyfill/Lodash.mjs";
|
|
|
4
4
|
import { StatusTexts } from "../polyfill/StatusTexts.mjs";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* `done` 的统一入参结构。
|
|
8
|
+
* Unified `done` input payload.
|
|
8
9
|
*
|
|
9
|
-
* @
|
|
10
|
-
* @
|
|
10
|
+
* @typedef {object} DonePayload
|
|
11
|
+
* @property {number|string} [status] 响应状态码或状态行 / Response status code or status line.
|
|
12
|
+
* @property {string} [url] 响应 URL / Response URL.
|
|
13
|
+
* @property {Record<string, any>} [headers] 响应头 / Response headers.
|
|
14
|
+
* @property {string|ArrayBuffer|ArrayBufferView} [body] 响应体 / Response body.
|
|
15
|
+
* @property {ArrayBuffer} [bodyBytes] 二进制响应体 / Binary response body.
|
|
16
|
+
* @property {string} [policy] 指定策略名 / Preferred policy name.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 结束脚本执行并按平台转换参数。
|
|
21
|
+
* Complete script execution with platform-specific parameter mapping.
|
|
22
|
+
*
|
|
23
|
+
* 说明:
|
|
24
|
+
* Notes:
|
|
25
|
+
* - 这是调用入口,平台原生 `$done` 差异在内部处理
|
|
26
|
+
* - This is the call entry and native `$done` differences are handled internally
|
|
27
|
+
* - Node.js 不调用 `$done`,而是直接退出进程
|
|
28
|
+
* - Node.js does not call `$done`; it exits the process directly
|
|
29
|
+
*
|
|
30
|
+
* @param {DonePayload} [object={}] 统一响应对象 / Unified response object.
|
|
11
31
|
* @returns {void}
|
|
12
32
|
*/
|
|
13
33
|
export function done(object = {}) {
|
package/lib/environment.mjs
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import { $app } from "./app.mjs";
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* 标准化后的运行环境对象。
|
|
5
|
+
* Normalized runtime environment object.
|
|
6
|
+
*
|
|
7
|
+
* - Surge/Stash/Egern: 基于全局 `$environment` 并补充 `app`
|
|
8
|
+
* - Surge/Stash/Egern: based on global `$environment` with `app` patched
|
|
9
|
+
* - Loon: 解析 `$loon` 得到设备与版本信息
|
|
10
|
+
* - Loon: parse `$loon` into device/version fields
|
|
11
|
+
* - Quantumult X: 仅返回 `{ app: "Quantumult X" }`
|
|
12
|
+
* - Quantumult X: returns `{ app: "Quantumult X" }` only
|
|
13
|
+
* - Node.js: 复用 `process.env` 并写入 `process.env.app`
|
|
14
|
+
* - Node.js: reuses `process.env` and writes `process.env.app`
|
|
15
|
+
*
|
|
16
|
+
* @type {Record<string, any>}
|
|
17
|
+
*/
|
|
3
18
|
export const $environment = environment();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 获取标准化环境对象。
|
|
22
|
+
* Build and return the normalized environment object.
|
|
23
|
+
*
|
|
24
|
+
* @returns {Record<string, any>}
|
|
25
|
+
*/
|
|
4
26
|
export function environment() {
|
|
5
27
|
switch ($app) {
|
|
6
28
|
case "Surge":
|
package/lib/index.js
CHANGED
|
@@ -4,3 +4,15 @@ export * from "./done.mjs";
|
|
|
4
4
|
export * from "./notification.mjs";
|
|
5
5
|
export * from "./time.mjs";
|
|
6
6
|
export * from "./wait.mjs";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 已标准化的 `$argument` 快照。
|
|
10
|
+
* Normalized `$argument` snapshot.
|
|
11
|
+
*/
|
|
12
|
+
export const $argument = globalThis.$argument ?? {};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 兼容别名(建议优先使用 `$argument`)。
|
|
16
|
+
* Backward-compatible alias (prefer `$argument`).
|
|
17
|
+
*/
|
|
18
|
+
export const argument = $argument;
|
package/lib/notification.mjs
CHANGED
|
@@ -2,20 +2,41 @@ import { $app } from "./app.mjs";
|
|
|
2
2
|
import { Console } from "../polyfill/Console.mjs";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* 通知内容扩展参数。
|
|
6
|
+
* Extended notification content options.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* @typedef {object|string|number|boolean} NotificationContent
|
|
9
|
+
* @property {string} [open] 打开链接 / Open URL.
|
|
10
|
+
* @property {string} ["open-url"] 打开链接 (QuanX) / Open URL (QuanX).
|
|
11
|
+
* @property {string} [url] 打开链接 / Open URL.
|
|
12
|
+
* @property {string} [openUrl] 打开链接 (Loon/Shadowrocket) / Open URL (Loon/Shadowrocket).
|
|
13
|
+
* @property {string} [copy] 复制文本 / Copy text.
|
|
14
|
+
* @property {string} ["update-pasteboard"] 复制文本 (QuanX) / Copy text (QuanX).
|
|
15
|
+
* @property {string} [updatePasteboard] 复制文本 / Copy text.
|
|
16
|
+
* @property {string} [media] 媒体 URL 或 Base64 / Media URL or Base64.
|
|
17
|
+
* @property {string} ["media-url"] 媒体 URL / Media URL.
|
|
18
|
+
* @property {string} [mediaUrl] 媒体 URL / Media URL.
|
|
19
|
+
* @property {string} [mime] Base64 媒体 MIME / MIME type for Base64 media.
|
|
20
|
+
* @property {number} ["auto-dismiss"] 自动消失秒数 / Auto dismiss seconds.
|
|
21
|
+
* @property {string} [sound] 提示音 / Notification sound.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 发送系统通知并按平台适配参数格式。
|
|
26
|
+
* Send system notification with platform-specific payload mapping.
|
|
13
27
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
28
|
+
* 说明:
|
|
29
|
+
* Notes:
|
|
30
|
+
* - iOS App 平台调用 `$notification.post` 或 `$notify`
|
|
31
|
+
* - iOS app platforms call `$notification.post` or `$notify`
|
|
32
|
+
* - Node.js 不支持 iOS 通知接口,仅输出日志
|
|
33
|
+
* - Node.js does not support iOS notification APIs; it logs only
|
|
18
34
|
*
|
|
35
|
+
* @param {string} [title=`ℹ️ ${$app} 通知`] 标题 / Title.
|
|
36
|
+
* @param {string} [subtitle=""] 副标题 / Subtitle.
|
|
37
|
+
* @param {string} [body=""] 内容 / Message body.
|
|
38
|
+
* @param {NotificationContent} [content={}] 扩展参数 / Extended content options.
|
|
39
|
+
* @returns {void}
|
|
19
40
|
*/
|
|
20
41
|
export function notification(title = `ℹ️ ${$app} 通知`, subtitle = "", body = "", content = {}) {
|
|
21
42
|
const mutableContent = MutableContent(content);
|
|
@@ -39,6 +60,14 @@ export function notification(title = `ℹ️ ${$app} 通知`, subtitle = "", bod
|
|
|
39
60
|
Console.groupEnd();
|
|
40
61
|
}
|
|
41
62
|
|
|
63
|
+
/**
|
|
64
|
+
* 将统一通知参数转换为平台可识别字段。
|
|
65
|
+
* Convert normalized content into platform-recognized fields.
|
|
66
|
+
*
|
|
67
|
+
* @private
|
|
68
|
+
* @param {NotificationContent} content 通知扩展参数 / Extended content options.
|
|
69
|
+
* @returns {Record<string, any>}
|
|
70
|
+
*/
|
|
42
71
|
const MutableContent = content => {
|
|
43
72
|
const mutableContent = {};
|
|
44
73
|
switch (typeof content) {
|
package/lib/runScript.mjs
CHANGED
|
@@ -3,6 +3,27 @@ import { fetch } from "../polyfill/fetch.mjs";
|
|
|
3
3
|
import { Lodash as _ } from "../polyfill/Lodash.mjs";
|
|
4
4
|
import { Storage } from "../polyfill/Storage.mjs";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* 远程脚本执行选项。
|
|
8
|
+
* Remote script execution options.
|
|
9
|
+
*
|
|
10
|
+
* @typedef {object} RunScriptOptions
|
|
11
|
+
* @property {number} [timeout] 执行超时秒数 / Timeout in seconds.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 通过 BoxJS HTTP API 触发脚本执行。
|
|
16
|
+
* Trigger script execution through BoxJS HTTP API.
|
|
17
|
+
*
|
|
18
|
+
* 依赖键:
|
|
19
|
+
* Required keys:
|
|
20
|
+
* - `@chavy_boxjs_userCfgs.httpapi` (`password@host:port`)
|
|
21
|
+
* - `@chavy_boxjs_userCfgs.httpapi_timeout`
|
|
22
|
+
*
|
|
23
|
+
* @param {string} script 脚本文本 / Script source text.
|
|
24
|
+
* @param {RunScriptOptions} [runOpts] 运行选项 / Runtime options.
|
|
25
|
+
* @returns {Promise<void>}
|
|
26
|
+
*/
|
|
6
27
|
export async function runScript(script, runOpts) {
|
|
7
28
|
let httpapi = Storage.getItem("@chavy_boxjs_userCfgs.httpapi");
|
|
8
29
|
httpapi = httpapi?.replace?.(/\n/g, "")?.trim();
|
package/lib/time.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* [version of ISO8601]{@link https://262.ecma-international.org/5.1/#sec-15.9.1.15}
|
|
5
|
-
* 示例:time("yyyy-MM-dd qq HH:mm:ss.S") YYYY-MM-DDTHH:mm:ss.sssZ
|
|
6
|
-
* :time("yyyyMMddHHmmssS")
|
|
7
|
-
* YY:年 MM:月 dd:日 S:季 HH:时 m:分 ss:秒 sss:毫秒 Z:时区
|
|
8
|
-
* 其中y可选0-4位占位符、S可选0-1位占位符,其余可选0-2位占位符
|
|
9
|
-
* @param {string} format 格式化参数
|
|
10
|
-
* @param {number} ts 可选: 根据指定时间戳返回格式化日期
|
|
2
|
+
* 按模板格式化时间字符串。
|
|
3
|
+
* Format date/time into a template string.
|
|
11
4
|
*
|
|
5
|
+
* 支持占位符:
|
|
6
|
+
* Supported tokens:
|
|
7
|
+
* - `YY`, `yyyy`, `MM`, `dd`, `HH`, `mm`, `ss`, `sss`, `S`
|
|
8
|
+
*
|
|
9
|
+
* @param {string} format 格式模板 / Format template.
|
|
10
|
+
* @param {number} [ts] 可选时间戳 / Optional timestamp.
|
|
11
|
+
* @returns {string}
|
|
12
12
|
*/
|
|
13
13
|
export function time(format, ts) {
|
|
14
14
|
const date = ts ? new Date(ts) : new Date();
|
package/lib/wait.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 延时等待指定毫秒后继续执行。
|
|
3
|
+
* Wait for the given milliseconds before continuing.
|
|
3
4
|
*
|
|
4
|
-
* @
|
|
5
|
-
* @
|
|
6
|
-
* @returns {Promise<resolve>}
|
|
5
|
+
* @param {number} [delay=1000] 延迟毫秒 / Delay in milliseconds.
|
|
6
|
+
* @returns {Promise<void>}
|
|
7
7
|
*/
|
|
8
8
|
export function wait(delay = 1000) {
|
|
9
9
|
return new Promise(resolve => setTimeout(resolve, delay));
|
package/package.json
CHANGED
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,28 @@
|
|
|
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
|
+
*
|
|
13
|
+
* 参考:
|
|
14
|
+
* Reference:
|
|
15
|
+
* - https://www.lodashjs.com
|
|
16
|
+
* - https://lodash.com
|
|
17
|
+
*/
|
|
2
18
|
export class Lodash {
|
|
19
|
+
/**
|
|
20
|
+
* HTML 特殊字符转义。
|
|
21
|
+
* Escape HTML special characters.
|
|
22
|
+
*
|
|
23
|
+
* @param {string} string 输入文本 / Input text.
|
|
24
|
+
* @returns {string}
|
|
25
|
+
*/
|
|
3
26
|
static escape(string) {
|
|
4
27
|
const map = {
|
|
5
28
|
"&": "&",
|
|
@@ -11,6 +34,15 @@ export class Lodash {
|
|
|
11
34
|
return string.replace(/[&<>"']/g, m => map[m]);
|
|
12
35
|
}
|
|
13
36
|
|
|
37
|
+
/**
|
|
38
|
+
* 按路径读取对象值。
|
|
39
|
+
* Get object value by path.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} [object={}] 目标对象 / Target object.
|
|
42
|
+
* @param {string|string[]} [path=""] 路径 / Path.
|
|
43
|
+
* @param {*} [defaultValue=undefined] 默认值 / Default value.
|
|
44
|
+
* @returns {*}
|
|
45
|
+
*/
|
|
14
46
|
static get(object = {}, path = "", defaultValue = undefined) {
|
|
15
47
|
// translate array case to dot case, then split with .
|
|
16
48
|
// a[0].b -> a.0.b -> ['a', '0', 'b']
|
|
@@ -24,7 +56,9 @@ export class Lodash {
|
|
|
24
56
|
|
|
25
57
|
/**
|
|
26
58
|
* 递归合并源对象的自身可枚举属性到目标对象
|
|
59
|
+
* Recursively merge source enumerable properties into target object.
|
|
27
60
|
* @description 简化版 lodash.merge,用于合并配置对象
|
|
61
|
+
* @description A simplified lodash.merge for config merging.
|
|
28
62
|
*
|
|
29
63
|
* 适用情况:
|
|
30
64
|
* - 合并嵌套的配置/设置对象
|
|
@@ -41,8 +75,11 @@ export class Lodash {
|
|
|
41
75
|
* - 会修改原始目标对象 (mutates target)
|
|
42
76
|
*
|
|
43
77
|
* @param {object} object - 目标对象
|
|
78
|
+
* @param {object} object - Target object.
|
|
44
79
|
* @param {...object} sources - 源对象(可多个)
|
|
80
|
+
* @param {...object} sources - Source objects.
|
|
45
81
|
* @returns {object} 返回合并后的目标对象
|
|
82
|
+
* @returns {object} Merged target object.
|
|
46
83
|
* @example
|
|
47
84
|
* const target = { a: { b: 1 }, c: 2 };
|
|
48
85
|
* const source = { a: { d: 3 }, e: 4 };
|
|
@@ -99,8 +136,11 @@ export class Lodash {
|
|
|
99
136
|
|
|
100
137
|
/**
|
|
101
138
|
* 判断值是否为普通对象 (Plain Object)
|
|
139
|
+
* Check whether a value is a plain object.
|
|
102
140
|
* @param {*} value - 要检查的值
|
|
141
|
+
* @param {*} value - Value to check.
|
|
103
142
|
* @returns {boolean} 如果是普通对象返回 true
|
|
143
|
+
* @returns {boolean} Returns true when value is a plain object.
|
|
104
144
|
*/
|
|
105
145
|
static #isPlainObject(value) {
|
|
106
146
|
if (value === null || typeof value !== "object") return false;
|
|
@@ -108,24 +148,56 @@ export class Lodash {
|
|
|
108
148
|
return proto === null || proto === Object.prototype;
|
|
109
149
|
}
|
|
110
150
|
|
|
151
|
+
/**
|
|
152
|
+
* 删除对象指定路径并返回对象。
|
|
153
|
+
* Omit paths from object and return the same object.
|
|
154
|
+
*
|
|
155
|
+
* @param {object} [object={}] 目标对象 / Target object.
|
|
156
|
+
* @param {string|string[]} [paths=[]] 要删除的路径 / Paths to remove.
|
|
157
|
+
* @returns {object}
|
|
158
|
+
*/
|
|
111
159
|
static omit(object = {}, paths = []) {
|
|
112
160
|
if (!Array.isArray(paths)) paths = [paths.toString()];
|
|
113
161
|
paths.forEach(path => Lodash.unset(object, path));
|
|
114
162
|
return object;
|
|
115
163
|
}
|
|
116
164
|
|
|
165
|
+
/**
|
|
166
|
+
* 仅保留对象指定键(第一层)。
|
|
167
|
+
* Pick selected keys from object (top level only).
|
|
168
|
+
*
|
|
169
|
+
* @param {object} [object={}] 目标对象 / Target object.
|
|
170
|
+
* @param {string|string[]} [paths=[]] 需要保留的键 / Keys to keep.
|
|
171
|
+
* @returns {object}
|
|
172
|
+
*/
|
|
117
173
|
static pick(object = {}, paths = []) {
|
|
118
174
|
if (!Array.isArray(paths)) paths = [paths.toString()];
|
|
119
175
|
const filteredEntries = Object.entries(object).filter(([key, value]) => paths.includes(key));
|
|
120
176
|
return Object.fromEntries(filteredEntries);
|
|
121
177
|
}
|
|
122
178
|
|
|
179
|
+
/**
|
|
180
|
+
* 按路径写入对象值。
|
|
181
|
+
* Set object value by path.
|
|
182
|
+
*
|
|
183
|
+
* @param {object} object 目标对象 / Target object.
|
|
184
|
+
* @param {string|string[]} path 路径 / Path.
|
|
185
|
+
* @param {*} value 写入值 / Value.
|
|
186
|
+
* @returns {object}
|
|
187
|
+
*/
|
|
123
188
|
static set(object, path, value) {
|
|
124
189
|
if (!Array.isArray(path)) path = Lodash.toPath(path);
|
|
125
190
|
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
191
|
return object;
|
|
127
192
|
}
|
|
128
193
|
|
|
194
|
+
/**
|
|
195
|
+
* 将点路径或数组下标路径转换为数组。
|
|
196
|
+
* Convert dot/array-index path string into path segments.
|
|
197
|
+
*
|
|
198
|
+
* @param {string} value 路径字符串 / Path string.
|
|
199
|
+
* @returns {string[]}
|
|
200
|
+
*/
|
|
129
201
|
static toPath(value) {
|
|
130
202
|
return value
|
|
131
203
|
.replace(/\[(\d+)\]/g, ".$1")
|
|
@@ -133,6 +205,13 @@ export class Lodash {
|
|
|
133
205
|
.filter(Boolean);
|
|
134
206
|
}
|
|
135
207
|
|
|
208
|
+
/**
|
|
209
|
+
* HTML 实体反转义。
|
|
210
|
+
* Unescape HTML entities.
|
|
211
|
+
*
|
|
212
|
+
* @param {string} string 输入文本 / Input text.
|
|
213
|
+
* @returns {string}
|
|
214
|
+
*/
|
|
136
215
|
static unescape(string) {
|
|
137
216
|
const map = {
|
|
138
217
|
"&": "&",
|
|
@@ -144,6 +223,14 @@ export class Lodash {
|
|
|
144
223
|
return string.replace(/&|<|>|"|'/g, m => map[m]);
|
|
145
224
|
}
|
|
146
225
|
|
|
226
|
+
/**
|
|
227
|
+
* 删除对象路径对应的值。
|
|
228
|
+
* Remove value by object path.
|
|
229
|
+
*
|
|
230
|
+
* @param {object} [object={}] 目标对象 / Target object.
|
|
231
|
+
* @param {string|string[]} [path=""] 路径 / Path.
|
|
232
|
+
* @returns {boolean}
|
|
233
|
+
*/
|
|
147
234
|
static unset(object = {}, path = "") {
|
|
148
235
|
if (!Array.isArray(path)) path = Lodash.toPath(path);
|
|
149
236
|
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",
|