@nsnanocat/util 2.2.3 → 2.3.0

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 CHANGED
@@ -1,11 +1,11 @@
1
1
  # @nsnanocat/util
2
2
 
3
- 用于统一 Quantumult X / Loon / Shadowrocket / Node.js / Egern / Surge / Stash 脚本接口的通用工具库。
3
+ 用于统一 Quantumult X / Loon / Shadowrocket / Worker / Node.js / Egern / Surge / Stash 脚本接口的通用工具库。
4
4
 
5
5
  核心目标:
6
6
  - 统一不同平台的 HTTP、通知、持久化、结束脚本等调用方式。
7
7
  - 在一个脚本里尽量少写平台分支。
8
- - 提供一组可直接复用的 polyfill(`fetch` / `Storage` / `Console` / `Lodash`)。
8
+ - 提供一组可直接复用的 polyfill(`fetch` / `Storage` / `KV` / `Console` / `Lodash`)。
9
9
 
10
10
  ## 目录
11
11
  - [安装与导入](#安装与导入)
@@ -56,16 +56,17 @@ npm i @nsnanocat/util@latest
56
56
 
57
57
  ```js
58
58
  import {
59
- $app, // 当前平台名(如 "Surge" / "Loon" / "Quantumult X" / "Node.js")
59
+ $app, // 当前平台名(如 "Surge" / "Loon" / "Quantumult X" / "Worker" / "Node.js")
60
60
  $argument, // 已标准化的模块参数对象(导入包时自动处理字符串 -> 对象)
61
61
  done, // 统一结束脚本函数(内部自动适配各平台 $done 差异)
62
- fetch, // 统一 HTTP 请求函数(内部自动适配 $httpClient / $task / Node fetch)
62
+ fetch, // 统一 HTTP 请求函数(内部自动适配 $httpClient / $task / fetch)
63
63
  notification, // 统一通知函数(内部自动适配 $notify / $notification.post)
64
64
  time, // 时间格式化工具
65
65
  wait, // 延时等待工具(Promise)
66
66
  Console, // 统一日志工具(支持 logLevel)
67
67
  Lodash as _, // Lodash 建议按官方示例惯例使用 `_` 作为工具对象别名
68
- Storage, // 统一持久化存储接口(适配 $prefs / $persistentStore / 文件)
68
+ KV, // Cloudflare Workers KV 异步适配器(显式传入 namespace binding)
69
+ Storage, // 统一持久化存储接口(适配 $prefs / $persistentStore / 内存 / 文件)
69
70
  } from "@nsnanocat/util";
70
71
  ```
71
72
 
@@ -80,6 +81,7 @@ import {
80
81
  - `lib/wait.mjs`
81
82
  - `polyfill/Console.mjs`
82
83
  - `polyfill/fetch.mjs`
84
+ - `polyfill/KV.mjs`
83
85
  - `polyfill/Lodash.mjs`
84
86
  - `polyfill/StatusTexts.mjs`
85
87
  - `polyfill/Storage.mjs`
@@ -104,8 +106,9 @@ import {
104
106
  | `lib/notification.mjs` | `lib/app.mjs`, `polyfill/Console.mjs` | `$app`, `Console.group`, `Console.log`, `Console.groupEnd`, `Console.error` | 将通知参数映射到各平台通知接口并统一日志输出 |
105
107
  | `lib/runScript.mjs` | `polyfill/Console.mjs`, `polyfill/fetch.mjs`, `polyfill/Storage.mjs`, `polyfill/Lodash.mjs` | `Console.error`, `fetch`, `Storage.getItem`(`Lodash` 当前版本未实际调用) | 读取 BoxJS 配置并发起统一 HTTP 调用执行脚本 |
106
108
  | `getStorage.mjs` | `lib/argument.mjs`, `polyfill/Console.mjs`, `polyfill/Lodash.mjs`, `polyfill/Storage.mjs` | `Console.debug`, `Console.logLevel`, `Lodash.merge`, `Storage.getItem` | 先标准化 `$argument`,再合并默认配置/持久化配置/运行参数 |
107
- | `polyfill/Console.mjs` | `lib/app.mjs` | `$app` | 日志在 Node.js 与 iOS 脚本环境使用不同错误输出策略 |
109
+ | `polyfill/Console.mjs` | `lib/app.mjs` | `$app` | 日志在 Worker / Node.js 与 iOS 脚本环境使用不同错误输出策略 |
108
110
  | `polyfill/fetch.mjs` | `lib/app.mjs`, `polyfill/Lodash.mjs`, `polyfill/StatusTexts.mjs`, `polyfill/Console.mjs` | `$app`, `Lodash.set`, `StatusTexts`(`Console` 当前版本未实际调用) | 按平台选请求引擎并做参数映射、响应结构统一 |
111
+ | `polyfill/KV.mjs` | `lib/app.mjs`, `polyfill/Lodash.mjs`, `polyfill/Storage.mjs` | `$app`, `Lodash.get`, `Lodash.set`, `Lodash.unset`, `Storage` | 为 Cloudflare Workers KV 提供异步适配,并在非 Worker 平台回退到 `Storage` |
109
112
  | `polyfill/Storage.mjs` | `lib/app.mjs`, `polyfill/Lodash.mjs` | `$app`, `Lodash.get`, `Lodash.set`, `Lodash.unset` | 按平台选持久化后端并支持 `@key.path` 读写 |
110
113
  | `polyfill/Lodash.mjs` | 无 | 无 | 提供路径/合并等基础能力,被多个模块复用 |
111
114
  | `polyfill/StatusTexts.mjs` | 无 | 无 | 提供 HTTP 状态文案,供 `fetch/done` 使用 |
@@ -116,7 +119,7 @@ import {
116
119
  ### `lib/app.mjs` 与 `lib/environment.mjs`(平台识别与环境)
117
120
 
118
121
  #### `$app`
119
- - 类型:`"Quantumult X" | "Loon" | "Shadowrocket" | "Egern" | "Surge" | "Stash" | "Node.js" | undefined`
122
+ - 类型:`"Quantumult X" | "Loon" | "Shadowrocket" | "Egern" | "Surge" | "Stash" | "Worker" | "Node.js" | undefined`
120
123
  - 角色:核心模块。库内所有存在平台行为差异的模块都会先读取 `$app` 再分流(如 `done`、`notification`、`fetch`、`Storage`、`Console`、`environment`)。
121
124
  - 读取方式:
122
125
 
@@ -133,9 +136,10 @@ console.log(appName);
133
136
  4. 存在 `Egern` -> `Egern`
134
137
  5. 存在 `$environment` 且有 `surge-version` -> `Surge`
135
138
  6. 存在 `$environment` 且有 `stash-version` -> `Stash`
136
- 7. 存在 `process.versions.node` -> `Node.js`
137
- 8. 默认回落 -> `undefined`
138
- - 实现细节:内部使用 `'key' in globalThis` 检测平台标记,避免 `Object.keys(globalThis)` 漏掉不可枚举全局变量;因此在 Workers / Vercel 风格全局对象存在时,只要 `process.versions.node` 可用,仍会识别为 `Node.js`。
139
+ 7. 存在 `Cloudflare` -> `Worker`
140
+ 8. 存在 `process.versions.node` -> `Node.js`
141
+ 9. 默认回落 -> `undefined`
142
+ - 实现细节:内部使用 `'key' in globalThis` 检测平台标记,避免 `Object.keys(globalThis)` 漏掉不可枚举全局变量;当前 Worker 识别以 `Cloudflare` 全局标记为准。
139
143
 
140
144
  #### `$environment` / `environment()`
141
145
  - 路径:`lib/environment.mjs`(未从包主入口导出)
@@ -157,6 +161,7 @@ console.log(environment()); // 当前环境对象
157
161
  | Egern | 读取全局 `$environment`,再写入 `app` | `{ ..., app: "Egern" }` |
158
162
  | Loon | 读取全局 `$loon` 字符串并拆分 | `{ device, ios, "loon-version", app: "Loon" }` |
159
163
  | Quantumult X | 不读取额外环境字段,直接构造对象 | `{ app: "Quantumult X" }` |
164
+ | Worker | 直接构造对象 | `{ app: "Worker" }` |
160
165
  | Node.js | 读取 `process.env` 并写入 `process.env.app` | `{ ..., app: "Node.js" }` |
161
166
  | 其他 | 无 | `{}` |
162
167
 
@@ -194,7 +199,7 @@ console.log($argument); // { mode: "on", a: { b: "1" } }
194
199
 
195
200
  #### `done(object = {})`
196
201
  - 签名:`done(object?: object): void`
197
- - 作用:统一不同平台的脚本结束接口(`$done` / Node 退出)。
202
+ - 作用:统一不同平台的脚本结束接口(`$done` / Worker 日志结束 / Node 退出)。
198
203
 
199
204
  说明:下表描述的是各 App 原生接口差异与本库内部映射逻辑。调用方只需要按 `done` 的统一参数传值即可,不需要自己再写平台分支。
200
205
 
@@ -216,12 +221,14 @@ console.log($argument); // { mode: "on", a: { b: "1" } }
216
221
  | Egern | 不转换 | 透传 | 透传 | `$done(object)` |
217
222
  | Shadowrocket | 不转换 | 透传 | 透传 | `$done(object)` |
218
223
  | Quantumult X | 写入 `opts.policy` | `number` 会转 `HTTP/1.1 200 OK` 字符串 | 仅保留 `status/url/headers/body/bodyBytes`;`ArrayBuffer/TypedArray` 转 `bodyBytes` | `$done(object)` |
224
+ | Worker | 不适用 | 不适用 | 不适用 | 仅记录结束日志 |
219
225
  | Node.js | 不适用 | 不适用 | 不适用 | `process.exit(1)` |
220
226
 
221
227
  不可用/差异点:
222
228
  - `policy` 在 Egern / Shadowrocket 分支不做映射。
223
229
  - Quantumult X 会丢弃未在白名单内的字段。
224
230
  - Quantumult X 的 `status` 在部分场景要求完整状态行(如 `HTTP/1.1 200 OK`),本库会在传入数字状态码时自动拼接(依赖 `StatusTexts`)。
231
+ - Worker 不调用 `$done`,仅记录结束日志。
225
232
  - Node.js 不调用 `$done`,而是直接退出进程,且退出码固定为 `1`。
226
233
  - 未识别平台(`$app === undefined`)只记录结束日志,不会尝试调用 `$done` 或退出进程。
227
234
 
@@ -252,11 +259,13 @@ console.log($argument); // { mode: "on", a: { b: "1" } }
252
259
  | Shadowrocket | `$notification.post` | `{ openUrl: content }` | 走 Surge 分支的 action/url/text/media 字段 |
253
260
  | Loon | `$notification.post` | `{ openUrl: content }` | `openUrl`、`mediaUrl`(仅 http/https) |
254
261
  | Quantumult X | `$notify` | `{ "open-url": content }` | `open-url`、`media-url`(仅 http/https)、`update-pasteboard` |
262
+ | Worker | 不发送通知(非 iOS App 环境) | 无 | 无 |
255
263
  | Node.js | 不发送通知(非 iOS App 环境) | 无 | 无 |
256
264
 
257
265
  不可用/差异点:
258
266
  - `copy/update-pasteboard` 在 Loon 分支不会生效。
259
267
  - Loon / Quantumult X 对 `media` 仅接受网络 URL;Base64 媒体不会自动映射。
268
+ - Worker 不是 iOS App 脚本环境,不支持 iOS 通知行为;当前分支仅日志输出。
260
269
  - Node.js 不是 iOS App 脚本环境,不支持 iOS 通知行为;当前分支仅日志输出。
261
270
 
262
271
  ### `lib/time.mjs`
@@ -384,7 +393,7 @@ const store = getStorage("@my_box", ["YouTube", "Global"], database);
384
393
  - `timeout`
385
394
  - `policy`
386
395
  - `redirection` / `auto-redirect`
387
- - `auto-cookie`(仅 Node.js 分支识别;默认启用,传入 `false` / `0` / `-1` 可关闭)
396
+ - `auto-cookie`(Worker / Node.js 共享分支识别;默认启用,传入 `false` / `0` / `-1` 可关闭)
388
397
 
389
398
  说明:下表是各 App 原生 HTTP 接口的差异补充,以及本库 `fetch` 的内部映射方式。调用方使用统一入参即可。
390
399
 
@@ -398,6 +407,7 @@ const store = getStorage("@my_box", ["YouTube", "Global"], database);
398
407
  | Egern | `$httpClient[method]` | 秒 | 无专门映射 | `auto-redirect` | 同上 |
399
408
  | Shadowrocket | `$httpClient[method]` | 秒 | `headers.X-Surge-Proxy` | `auto-redirect` | 同上 |
400
409
  | Quantumult X | `$task.fetch` | 毫秒(内部乘 1000) | `opts.policy` | `opts.redirection` | `body(ArrayBuffer/TypedArray)` 转 `bodyBytes`;响应按 `Content-Type` 恢复到 `body` |
410
+ | Worker | `globalThis.fetch`(不存在时回退 `node-fetch`);共享 `auto-cookie` 处理 | 毫秒(内部乘 1000) | 无 | `redirect: follow/manual` | 返回 `body`(UTF-8 string) + `bodyBytes`(ArrayBuffer) |
401
411
  | Node.js | `globalThis.fetch`(不存在时回退 `node-fetch`);默认按需包裹 `fetch-cookie` | 毫秒(内部乘 1000) | 无 | `redirect: follow/manual` | 返回 `body`(UTF-8 string) + `bodyBytes`(ArrayBuffer) |
402
412
 
403
413
  返回对象(统一后)常见字段:
@@ -410,9 +420,9 @@ const store = getStorage("@my_box", ["YouTube", "Global"], database);
410
420
  - `bodyBytes`
411
421
 
412
422
  不可用/差异点:
413
- - `policy` 在 Surge / Egern / Node.js 分支没有额外适配逻辑。
423
+ - `policy` 在 Surge / Egern / Worker / Node.js 分支没有额外适配逻辑。
414
424
  - `redirection` 在部分平台会映射为 `auto-redirect` 或 `opts.redirection`。
415
- - Node.js 分支优先复用 `globalThis.fetch`;若不存在则回退到 `node-fetch`,并在 `auto-cookie` 未关闭时按需包裹 `fetch-cookie`。
425
+ - Worker / Node.js 共享基于 `fetch` 的请求分支;若 `globalThis.fetch` 不存在则回退到 `node-fetch`,并在 `auto-cookie` 未关闭时按需包裹 `fetch-cookie`。
416
426
  - 传入 `timeout` 时,`5` 和 `5000` 都会被接受;库会先将用户输入归一化,再按平台要求转换为秒或毫秒。
417
427
  - 返回结构是统一兼容结构,不等同于浏览器 `Response` 对象。
418
428
 
@@ -435,21 +445,24 @@ const store = getStorage("@my_box", ["YouTube", "Global"], database);
435
445
  #### `Storage.removeItem(keyName)`
436
446
  - Quantumult X:可用(`$prefs.removeValueForKey`)。
437
447
  - Surge:通过 `$persistentStore.write(null, keyName)` 删除。
448
+ - Worker:可用(仅删除内存缓存中的对应 key,不持久化)。
438
449
  - Node.js:可用(删除 `box.dat` 中对应 key 并落盘)。
439
450
  - Loon / Stash / Egern / Shadowrocket:返回 `false`。
440
451
 
441
452
  #### `Storage.clear()`
442
453
  - Quantumult X:可用(`$prefs.removeAllValues`)。
454
+ - Worker:可用(仅清空内存缓存,不持久化)。
443
455
  - Node.js:可用(清空 `box.dat` 并落盘)。
444
456
  - 其他平台:返回 `false`。
445
457
 
446
- #### Node.js 特性
458
+ #### Worker / Node.js 特性
459
+ - Worker:使用进程内内存缓存,不写文件。
447
460
  - 数据文件默认:`box.dat`。
448
461
  - 读取路径优先级:当前目录 -> `process.cwd()`。
449
462
 
450
463
  与 Web Storage 的行为差异:
451
464
  - 支持 `@key.path` 深路径读写(Web Storage 原生不支持)。
452
- - `removeItem/clear` 仅部分平台可用(目前为 Quantumult X、Node.js,以及 Surge 的 `removeItem`)。
465
+ - `removeItem/clear` 仅部分平台可用(目前为 Quantumult X、Worker、Node.js,以及 Surge 的 `removeItem`)。
453
466
  - `getItem` 会尝试 `JSON.parse`,`setItem` 写入对象会 `JSON.stringify`。
454
467
 
455
468
  平台后端映射:
@@ -458,8 +471,42 @@ const store = getStorage("@my_box", ["YouTube", "Global"], database);
458
471
  | --- | --- |
459
472
  | Surge / Loon / Stash / Egern / Shadowrocket | `$persistentStore.read/write` |
460
473
  | Quantumult X | `$prefs.valueForKey/setValueForKey` |
474
+ | Worker | 进程内内存缓存 |
461
475
  | Node.js | 本地 `box.dat` |
462
476
 
477
+ ### `polyfill/KV.mjs`
478
+
479
+ `KV` 是面向 Cloudflare Workers KV 的异步适配器:
480
+ - 调用方显式传入 namespace binding:`new KV(env.NAMESPACE)`
481
+ - Worker 分支直接调用 `namespace.get/put/delete/list`
482
+ - `get()` 不传 `type`,默认按 Cloudflare 行为读取字符串
483
+ - 非 Worker 平台会回退到 `Storage.getItem/setItem/removeItem`
484
+ - `list()` 仅支持 Worker,返回 Cloudflare KV 原生列举结果
485
+ - `clear()` 始终返回 `false`
486
+
487
+ #### `new KV(namespace)`
488
+ - `namespace` 需提供 `get(key)` / `put(key, value)` / `delete(key)`。
489
+
490
+ #### `await kv.getItem(keyName, defaultValue = null)`
491
+ - 支持普通 key。
492
+ - 支持路径 key:`@root.path.to.key`。
493
+ - 读取后会尝试 `JSON.parse`。
494
+
495
+ #### `await kv.setItem(keyName, keyValue)`
496
+ - 支持普通 key 与路径 key。
497
+ - `keyValue` 为对象时自动 `JSON.stringify`。
498
+
499
+ #### `await kv.removeItem(keyName)`
500
+ - 支持普通 key 与路径 key。
501
+
502
+ #### `await kv.list(options = {})`
503
+ - 仅支持 Worker。
504
+ - 透传 `prefix` / `limit` / `cursor` 到 `namespace.list(options)`。
505
+ - 返回 Cloudflare KV 的原生结果:`keys` / `list_complete` / `cursor`。
506
+
507
+ #### `await kv.clear()`
508
+ - 始终返回 `false`。
509
+
463
510
  ### `polyfill/Console.mjs`
464
511
 
465
512
  `Console` 是统一日志工具(静态类)。
@@ -507,7 +554,7 @@ console.log(Console.logLevel); // "WARN"
507
554
  | `count(label)` | `label?: string` | `void` | 计数并输出 |
508
555
  | `countReset(label)` | `label?: string` | `void` | 重置计数器 |
509
556
  | `debug(...msg)` | `...msg: any[]` | `void` | 仅 `DEBUG/ALL` 级别输出 |
510
- | `error(...msg)` | `...msg: any[]` | `void` | Node.js 优先输出 `stack` |
557
+ | `error(...msg)` | `...msg: any[]` | `void` | Worker / Node.js 优先输出 `stack` |
511
558
  | `exception(...msg)` | `...msg: any[]` | `void` | `error` 别名 |
512
559
  | `group(label)` | `label: string` | `void` | 压栈分组 |
513
560
  | `groupEnd()` | 无 | `void` | 出栈分组 |
@@ -519,7 +566,7 @@ console.log(Console.logLevel); // "WARN"
519
566
  | `warn(...msg)` | `...msg: any[]` | `void` | `WARN` 及以上 |
520
567
 
521
568
  平台差异:
522
- - Node.js 下 `error` 会优先打印 `Error.stack`。
569
+ - Worker / Node.js 下 `error` 会优先打印 `Error.stack`。
523
570
  - 其他平台统一加前缀符号输出(`❌/⚠️/ℹ️/🅱️`)。
524
571
 
525
572
  ### `polyfill/Lodash.mjs`
@@ -623,18 +670,19 @@ console.log(value); // 1
623
670
 
624
671
  说明:本节展示的是各平台原生脚本接口差异。实际在本库中,这些差异已由 `done`、`fetch`、`notification`、`Storage` 等模块做了统一适配。
625
672
 
626
- | 能力 | Quantumult X | Loon | Surge | Stash | Egern | Shadowrocket | Node.js |
627
- | --- | --- | --- | --- | --- | --- | --- | --- |
628
- | HTTP 请求 | `$task.fetch` | `$httpClient` | `$httpClient` | `$httpClient` | `$httpClient` | `$httpClient` | `fetch` |
629
- | 通知 | `$notify` | `$notification.post` | `$notification.post` | `$notification.post` | `$notification.post` | `$notification.post` | 无 |
630
- | 持久化 | `$prefs` | `$persistentStore` | `$persistentStore` | `$persistentStore` | `$persistentStore` | `$persistentStore` | `box.dat` |
631
- | 结束脚本 | `$done` | `$done` | `$done` | `$done` | `$done` | `$done` | `process.exit(1)` |
632
- | `removeItem/clear` | 可用 | 不可用 | `removeItem` 可用 / `clear` 不可用 | 不可用 | 不可用 | 不可用 | 可用 |
633
- | `policy` 注入(`fetch/done`) | `opts.policy` | `node` | `X-Surge-Policy`(done) | `X-Stash-Selected-Proxy` | 无专门映射 | `X-Surge-Proxy`(fetch) | 无 |
673
+ | 能力 | Quantumult X | Loon | Surge | Stash | Egern | Shadowrocket | Worker | Node.js |
674
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
675
+ | HTTP 请求 | `$task.fetch` | `$httpClient` | `$httpClient` | `$httpClient` | `$httpClient` | `$httpClient` | `fetch` | `fetch` |
676
+ | 通知 | `$notify` | `$notification.post` | `$notification.post` | `$notification.post` | `$notification.post` | `$notification.post` | 无 | 无 |
677
+ | 持久化 | `$prefs` | `$persistentStore` | `$persistentStore` | `$persistentStore` | `$persistentStore` | `$persistentStore` | 内存缓存 | `box.dat` |
678
+ | 结束脚本 | `$done` | `$done` | `$done` | `$done` | `$done` | `$done` | 仅日志 | `process.exit(1)` |
679
+ | `removeItem/clear` | 可用 | 不可用 | `removeItem` 可用 / `clear` 不可用 | 不可用 | 不可用 | 不可用 | 可用 | 可用 |
680
+ | `policy` 注入(`fetch/done`) | `opts.policy` | `node` | `X-Surge-Policy`(done) | `X-Stash-Selected-Proxy` | 无专门映射 | `X-Surge-Proxy`(fetch) | 无 | 无 |
634
681
 
635
682
  ## 已知限制与注意事项
636
683
 
637
684
  - `lib/argument.mjs` 为 `$argument` 标准化模块,`import` 时会按规则重写全局 `$argument`。
685
+ - `lib/done.mjs` 在 Worker 仅记录结束日志。
638
686
  - `lib/done.mjs` 在 Node.js 固定 `process.exit(1)`。
639
687
  - `Storage.removeItem("@a.b")` 分支存在未声明变量写入风险;如要大量使用路径删除,建议先本地验证。
640
688
  - `lib/runScript.mjs` 未从包主入口导出,需要按文件路径直接导入。
@@ -662,6 +710,9 @@ console.log(value); // 1
662
710
  - [crossutility/Quantumult-X - sample-fetch-opts-policy.js](https://raw.githubusercontent.com/crossutility/Quantumult-X/master/sample-fetch-opts-policy.js)
663
711
  - [crossutility/Quantumult-X - sample-rewrite-response-header.js](https://github.com/crossutility/Quantumult-X/raw/refs/heads/master/sample-rewrite-response-header.js)
664
712
 
713
+ ### Worker
714
+ - 以 `Cloudflare` 全局标记识别 Worker 运行时。
715
+
665
716
  ### Node.js
666
717
  - [Node.js Globals - fetch](https://nodejs.org/api/globals.html#fetch)
667
718
 
package/index.js CHANGED
@@ -6,6 +6,7 @@ export * from "./lib/time.mjs";
6
6
  export * from "./lib/wait.mjs";
7
7
  export * from "./polyfill/Console.mjs";
8
8
  export * from "./polyfill/fetch.mjs";
9
+ export * from "./polyfill/KV.mjs";
9
10
  export * from "./polyfill/Lodash.mjs";
10
11
  export * from "./polyfill/StatusTexts.mjs";
11
12
  export * from "./polyfill/Storage.mjs";
package/lib/app.mjs CHANGED
@@ -10,8 +10,9 @@
10
10
  * 4) `Egern` -> Egern
11
11
  * 5) `$environment["surge-version"]` -> Surge
12
12
  * 6) `$environment["stash-version"]` -> Stash
13
- * 7) `process.versions.node` -> Node.js
14
- * 8) 默认回落 -> undefined
13
+ * 7) `Cloudflare` -> Worker
14
+ * 8) `process.versions.node` -> Node.js
15
+ * 9) 默认回落 -> undefined
15
16
  * default fallback -> undefined
16
17
  *
17
18
  * 说明:
@@ -19,10 +20,10 @@
19
20
  * - 使用 `'key' in globalThis`,避免 `Object.keys` 对不可枚举全局变量漏检。
20
21
  * - Use `'key' in globalThis` to avoid missing non-enumerable globals with `Object.keys`.
21
22
  *
22
- * @type {("Quantumult X" | "Loon" | "Shadowrocket" | "Egern" | "Surge" | "Stash" | "Node.js" | undefined)}
23
+ * @type {("Quantumult X" | "Loon" | "Shadowrocket" | "Egern" | "Surge" | "Stash" | "Worker" | "Node.js" | undefined)}
23
24
  */
24
25
  export const $app = (() => {
25
- const has = (key) => key in globalThis;
26
+ const has = key => key in globalThis;
26
27
  switch (true) {
27
28
  case has("$task"):
28
29
  return "Quantumult X";
@@ -36,6 +37,9 @@ export const $app = (() => {
36
37
  return "Surge";
37
38
  case Boolean(globalThis.$environment?.["stash-version"]):
38
39
  return "Stash";
40
+ case has("Cloudflare"):
41
+ //case has("ServiceWorkerGlobalScope") && has("self") && has("caches") && has("scheduler"):
42
+ return "Worker";
39
43
  case Boolean(globalThis.process?.versions?.node):
40
44
  return "Node.js";
41
45
  default:
package/lib/done.mjs CHANGED
@@ -24,6 +24,8 @@ import { StatusTexts } from "../polyfill/StatusTexts.mjs";
24
24
  * Notes:
25
25
  * - 这是调用入口,平台原生 `$done` 差异在内部处理
26
26
  * - This is the call entry and native `$done` differences are handled internally
27
+ * - Worker 不调用 `$done` 或退出进程,仅记录日志
28
+ * - Worker neither calls `$done` nor exits the process; it only logs
27
29
  * - Node.js 不调用 `$done`,而是直接退出进程
28
30
  * - Node.js does not call `$done`; it exits the process directly
29
31
  * - 未识别平台仅记录结束日志,不会强制退出
@@ -80,6 +82,9 @@ export function done(object = {}) {
80
82
  Console.log("🚩 执行结束!");
81
83
  $done(object);
82
84
  break;
85
+ case "Worker":
86
+ Console.log("🚩 执行结束!");
87
+ break;
83
88
  case "Node.js":
84
89
  Console.log("🚩 执行结束!");
85
90
  process.exit(1);
@@ -10,6 +10,8 @@ import { $app } from "./app.mjs";
10
10
  * - Loon: parse `$loon` into device/version fields
11
11
  * - Quantumult X: 仅返回 `{ app: "Quantumult X" }`
12
12
  * - Quantumult X: returns `{ app: "Quantumult X" }` only
13
+ * - Worker: 返回 `{ app: "Worker" }`
14
+ * - Worker: returns `{ app: "Worker" }`
13
15
  * - Node.js: 复用 `process.env` 并写入 `process.env.app`
14
16
  * - Node.js: reuses `process.env` and writes `process.env.app`
15
17
  *
@@ -47,6 +49,10 @@ export function environment() {
47
49
  return {
48
50
  app: "Quantumult X",
49
51
  };
52
+ case "Worker":
53
+ return {
54
+ app: "Worker",
55
+ };
50
56
  case "Node.js":
51
57
  process.env.app = "Node.js";
52
58
  return process.env;
@@ -29,6 +29,8 @@ import { Console } from "../polyfill/Console.mjs";
29
29
  * Notes:
30
30
  * - iOS App 平台调用 `$notification.post` 或 `$notify`
31
31
  * - iOS app platforms call `$notification.post` or `$notify`
32
+ * - Worker 不支持 iOS 通知接口,仅输出日志
33
+ * - Worker does not support iOS notification APIs; it logs only
32
34
  * - Node.js 不支持 iOS 通知接口,仅输出日志
33
35
  * - Node.js does not support iOS notification APIs; it logs only
34
36
  *
@@ -52,6 +54,7 @@ export function notification(title = `ℹ️ ${$app} 通知`, subtitle = "", bod
52
54
  case "Quantumult X":
53
55
  $notify(title, subtitle, body, mutableContent);
54
56
  break;
57
+ case "Worker":
55
58
  case "Node.js":
56
59
  break;
57
60
  }
@@ -90,6 +93,7 @@ const MutableContent = content => {
90
93
  case "Quantumult X":
91
94
  mutableContent["open-url"] = content;
92
95
  break;
96
+ case "Worker":
93
97
  case "Node.js":
94
98
  break;
95
99
  }
@@ -167,6 +171,7 @@ const MutableContent = content => {
167
171
  if (copyUrl) mutableContent["update-pasteboard"] = copyUrl;
168
172
  break;
169
173
  }
174
+ case "Worker":
170
175
  case "Node.js":
171
176
  break;
172
177
  }
package/package.json CHANGED
@@ -42,5 +42,5 @@
42
42
  "registry": "https://registry.npmjs.org/",
43
43
  "access": "public"
44
44
  },
45
- "version": "2.2.3"
45
+ "version": "2.3.0"
46
46
  }
@@ -1,8 +1,8 @@
1
1
  import { $app } from "../lib/app.mjs";
2
2
 
3
3
  /**
4
- * 统一日志工具,兼容各脚本平台与 Node.js。
5
- * Unified logger compatible with script platforms and Node.js.
4
+ * 统一日志工具,兼容各脚本平台、Worker Node.js。
5
+ * Unified logger compatible with script platforms, Worker, and Node.js.
6
6
  *
7
7
  * logLevel 用法:
8
8
  * logLevel usage:
@@ -99,8 +99,9 @@ export class Console {
99
99
  default:
100
100
  msg = msg.map(m => `❌ ${m}`);
101
101
  break;
102
+ case "Worker":
102
103
  case "Node.js":
103
- msg = msg.map(m => `❌ ${m.stack}`);
104
+ msg = msg.map(m => `❌ ${m?.stack ?? m}`);
104
105
  break;
105
106
  }
106
107
  Console.log(...msg);
@@ -0,0 +1,240 @@
1
+ import { $app } from "../lib/app.mjs";
2
+ import { Lodash as _ } from "./Lodash.mjs";
3
+ import { Storage } from "./Storage.mjs";
4
+
5
+ /**
6
+ * Cloudflare Workers KV 异步适配器。
7
+ * Async adapter for Cloudflare Workers KV.
8
+ *
9
+ * 设计目标:
10
+ * Design goal:
11
+ * - 提供与 `Storage` 接近的异步接口
12
+ * - Provide an async API close to `Storage`
13
+ * - 在 Worker 中使用显式传入的 KV namespace binding
14
+ * - Use an explicitly passed KV namespace binding in Workers
15
+ * - 在非 Worker 平台回退到 `Storage`
16
+ * - Fall back to `Storage` on non-Worker platforms
17
+ *
18
+ * 支持路径键:
19
+ * Supports path key:
20
+ * - `@root.path.to.value`
21
+ *
22
+ * @link https://developers.cloudflare.com/kv/get-started/#5-access-your-kv-namespace-from-your-worker
23
+ * @link https://developers.cloudflare.com/kv/api/read-key-value-pairs/
24
+ * @link https://developers.cloudflare.com/kv/api/write-key-value-pairs/
25
+ * @link https://developers.cloudflare.com/kv/api/delete-key-value-pairs/
26
+ * @link https://developers.cloudflare.com/kv/api/list-keys/
27
+ */
28
+ export class KV {
29
+ /**
30
+ * `@key.path` 解析正则。
31
+ * Regex for `@key.path` parsing.
32
+ *
33
+ * @type {RegExp}
34
+ */
35
+ static #nameRegex = /^@(?<key>[^.]+)(?:\.(?<path>.*))?$/;
36
+
37
+ /**
38
+ * Cloudflare KV namespace 绑定。
39
+ * Cloudflare KV namespace binding.
40
+ *
41
+ * @type {{ get(key: string): Promise<string|null>; put(key: string, value: string): Promise<void>; delete(key: string): Promise<void>; list?(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<{ keys: { name: string; expiration?: number; metadata?: object }[]; list_complete: boolean; cursor: string }> } | undefined}
42
+ */
43
+ namespace;
44
+
45
+ /**
46
+ * 创建 KV 适配器实例。
47
+ * Create a KV adapter instance.
48
+ *
49
+ * @param {{ get(key: string): Promise<string|null>; put(key: string, value: string): Promise<void>; delete(key: string): Promise<void>; list?(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<{ keys: { name: string; expiration?: number; metadata?: object }[]; list_complete: boolean; cursor: string }> } | null | undefined} namespace KV namespace 绑定 / KV namespace binding.
50
+ */
51
+ constructor(namespace) {
52
+ this.namespace = namespace ?? undefined;
53
+ }
54
+
55
+ /**
56
+ * 读取存储值。
57
+ * Read value from persistent storage.
58
+ *
59
+ * @param {string} keyName 键名或路径键 / Key or path key.
60
+ * @param {*} [defaultValue=null] 默认值 / Default value when key is missing.
61
+ * @returns {Promise<*>}
62
+ */
63
+ async getItem(keyName, defaultValue = null) {
64
+ let keyValue = defaultValue;
65
+ switch (keyName.startsWith("@")) {
66
+ case true: {
67
+ const { key, path } = keyName.match(KV.#nameRegex)?.groups ?? {};
68
+ keyName = key;
69
+ let value = await this.getItem(keyName, {});
70
+ if (typeof value !== "object" || value === null) value = {};
71
+ keyValue = _.get(value, path);
72
+ keyValue = KV.#deserialize(keyValue);
73
+ break;
74
+ }
75
+ default:
76
+ switch ($app) {
77
+ case "Worker":
78
+ keyValue = await this.#getNamespace().get(keyName);
79
+ break;
80
+ default:
81
+ keyValue = Storage.getItem(keyName, defaultValue);
82
+ break;
83
+ }
84
+ keyValue = KV.#deserialize(keyValue);
85
+ break;
86
+ }
87
+ return keyValue ?? defaultValue;
88
+ }
89
+
90
+ /**
91
+ * 写入存储值。
92
+ * Write value into persistent storage.
93
+ *
94
+ * @param {string} keyName 键名或路径键 / Key or path key.
95
+ * @param {*} keyValue 写入值 / Value to store.
96
+ * @returns {Promise<boolean>}
97
+ */
98
+ async setItem(keyName = new String(), keyValue = new String()) {
99
+ let result = false;
100
+ keyValue = KV.#serialize(keyValue);
101
+ switch (keyName.startsWith("@")) {
102
+ case true: {
103
+ const { key, path } = keyName.match(KV.#nameRegex)?.groups ?? {};
104
+ keyName = key;
105
+ let value = await this.getItem(keyName, {});
106
+ if (typeof value !== "object" || value === null) value = {};
107
+ _.set(value, path, keyValue);
108
+ result = await this.setItem(keyName, value);
109
+ break;
110
+ }
111
+ default:
112
+ switch ($app) {
113
+ case "Worker":
114
+ await this.#getNamespace().put(keyName, keyValue);
115
+ result = true;
116
+ break;
117
+ default:
118
+ result = Storage.setItem(keyName, keyValue);
119
+ break;
120
+ }
121
+ break;
122
+ }
123
+ return result;
124
+ }
125
+
126
+ /**
127
+ * 删除存储值。
128
+ * Remove value from persistent storage.
129
+ *
130
+ * @param {string} keyName 键名或路径键 / Key or path key.
131
+ * @returns {Promise<boolean>}
132
+ */
133
+ async removeItem(keyName) {
134
+ let result = false;
135
+ switch (keyName.startsWith("@")) {
136
+ case true: {
137
+ const { key, path } = keyName.match(KV.#nameRegex)?.groups ?? {};
138
+ keyName = key;
139
+ let value = await this.getItem(keyName);
140
+ if (typeof value !== "object" || value === null) value = {};
141
+ _.unset(value, path);
142
+ result = await this.setItem(keyName, value);
143
+ break;
144
+ }
145
+ default:
146
+ switch ($app) {
147
+ case "Worker":
148
+ await this.#getNamespace().delete(keyName);
149
+ result = true;
150
+ break;
151
+ default:
152
+ result = Storage.removeItem(keyName);
153
+ break;
154
+ }
155
+ break;
156
+ }
157
+ return result;
158
+ }
159
+
160
+ /**
161
+ * 清空存储。
162
+ * Clear storage.
163
+ *
164
+ * @returns {Promise<boolean>}
165
+ */
166
+ async clear() {
167
+ return false;
168
+ }
169
+
170
+ /**
171
+ * 列出命名空间中的键。
172
+ * List keys in the namespace.
173
+ *
174
+ * @param {{ prefix?: string; limit?: number; cursor?: string }} [options={}] 列举选项 / List options.
175
+ * @returns {Promise<{ keys: { name: string; expiration?: number; metadata?: object }[]; list_complete: boolean; cursor: string }>}
176
+ */
177
+ async list(options = {}) {
178
+ switch ($app) {
179
+ case "Worker": {
180
+ const namespace = this.#getNamespace();
181
+ if (typeof namespace.list !== "function") throw new TypeError("KV namespace binding with list() is required in Worker runtime.");
182
+ return await namespace.list(options);
183
+ }
184
+ default:
185
+ throw new TypeError("KV.list() is only supported in Worker runtime.");
186
+ }
187
+ }
188
+
189
+ /**
190
+ * 解析 Worker 所需的 namespace 绑定。
191
+ * Resolve the namespace binding required by Workers.
192
+ *
193
+ * @private
194
+ * @returns {{ get(key: string): Promise<string|null>; put(key: string, value: string): Promise<void>; delete(key: string): Promise<void>; list?(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<{ keys: { name: string; expiration?: number; metadata?: object }[]; list_complete: boolean; cursor: string }> }}
195
+ */
196
+ #getNamespace() {
197
+ if (
198
+ !this.namespace ||
199
+ typeof this.namespace.get !== "function" ||
200
+ typeof this.namespace.put !== "function" ||
201
+ typeof this.namespace.delete !== "function"
202
+ ) {
203
+ throw new TypeError("KV namespace binding is required in Worker runtime.");
204
+ }
205
+ return this.namespace;
206
+ }
207
+
208
+ /**
209
+ * 尝试将字符串反序列化为原始值。
210
+ * Try to deserialize a string into its original value.
211
+ *
212
+ * @private
213
+ * @param {*} value 原始值 / Raw value.
214
+ * @returns {*}
215
+ */
216
+ static #deserialize(value) {
217
+ try {
218
+ return JSON.parse(value);
219
+ } catch (e) {
220
+ return value;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * 规范化待写入的值。
226
+ * Normalize a value before persisting it.
227
+ *
228
+ * @private
229
+ * @param {*} value 原始值 / Raw value.
230
+ * @returns {string}
231
+ */
232
+ static #serialize(value) {
233
+ switch (typeof value) {
234
+ case "object":
235
+ return JSON.stringify(value);
236
+ default:
237
+ return String(value);
238
+ }
239
+ }
240
+ }
@@ -16,6 +16,8 @@ import { Lodash as _ } from "./Lodash.mjs";
16
16
  * Supported backends:
17
17
  * - Surge/Loon/Stash/Egern/Shadowrocket: `$persistentStore`
18
18
  * - Quantumult X: `$prefs`
19
+ * - Worker: 内存缓存(非持久化)
20
+ * - Worker: in-memory cache (non-persistent)
19
21
  * - Node.js: 本地 `box.dat`
20
22
  * - Node.js: local `box.dat`
21
23
  *
@@ -37,8 +39,8 @@ import { Lodash as _ } from "./Lodash.mjs";
37
39
  */
38
40
  export class Storage {
39
41
  /**
40
- * Node.js 环境下的内存数据缓存。
41
- * In-memory data cache for Node.js runtime.
42
+ * Worker / Node.js 环境下的内存数据缓存。
43
+ * In-memory data cache for Worker / Node.js runtime.
42
44
  *
43
45
  * @type {Record<string, any>|null}
44
46
  */
@@ -95,6 +97,10 @@ export class Storage {
95
97
  case "Quantumult X":
96
98
  keyValue = $prefs.valueForKey(keyName);
97
99
  break;
100
+ case "Worker":
101
+ Storage.data = Storage.data ?? {};
102
+ keyValue = Storage.data[keyName];
103
+ break;
98
104
  case "Node.js":
99
105
  Storage.data = Storage.#loaddata(Storage.dataFile);
100
106
  keyValue = Storage.data?.[keyName];
@@ -153,6 +159,11 @@ export class Storage {
153
159
  case "Quantumult X":
154
160
  result = $prefs.setValueForKey(keyValue, keyName);
155
161
  break;
162
+ case "Worker":
163
+ Storage.data = Storage.data ?? {};
164
+ Storage.data[keyName] = keyValue;
165
+ result = true;
166
+ break;
156
167
  case "Node.js":
157
168
  Storage.data = Storage.#loaddata(Storage.dataFile);
158
169
  Storage.data[keyName] = keyValue;
@@ -207,6 +218,11 @@ export class Storage {
207
218
  case "Quantumult X":
208
219
  result = $prefs.removeValueForKey(keyName);
209
220
  break;
221
+ case "Worker":
222
+ Storage.data = Storage.data ?? {};
223
+ delete Storage.data[keyName];
224
+ result = true;
225
+ break;
210
226
  case "Node.js":
211
227
  // result = false;
212
228
  Storage.data = Storage.#loaddata(Storage.dataFile);
@@ -224,8 +240,8 @@ export class Storage {
224
240
  }
225
241
 
226
242
  /**
227
- * 清空存储(仅 Quantumult X 支持)。
228
- * Clear storage (supported by Quantumult X only).
243
+ * 清空存储。
244
+ * Clear storage.
229
245
  *
230
246
  * @returns {boolean}
231
247
  */
@@ -242,6 +258,10 @@ export class Storage {
242
258
  case "Quantumult X":
243
259
  result = $prefs.removeAllValues();
244
260
  break;
261
+ case "Worker":
262
+ Storage.data = {};
263
+ result = true;
264
+ break;
245
265
  case "Node.js":
246
266
  // result = false;
247
267
  Storage.data = Storage.#loaddata(Storage.dataFile);
@@ -17,7 +17,7 @@ import { StatusTexts } from "./StatusTexts.mjs";
17
17
  * @property {string} [policy] 指定策略 / Preferred policy.
18
18
  * @property {boolean} [redirection] 是否跟随重定向 / Whether to follow redirects.
19
19
  * @property {boolean} ["auto-redirect"] 平台重定向字段 / Platform redirect flag.
20
- * @property {boolean|number|string} ["auto-cookie"] Node.js Cookie 开关 / Node.js Cookie toggle.
20
+ * @property {boolean|number|string} ["auto-cookie"] Worker / Node.js Cookie 开关 / Worker / Node.js Cookie toggle.
21
21
  * @property {Record<string, any>} [opts] 平台扩展字段 / Platform extension fields.
22
22
  */
23
23
 
@@ -43,13 +43,13 @@ import { StatusTexts } from "./StatusTexts.mjs";
43
43
  * Design goal:
44
44
  * - 仿照 Web API `fetch`(`Window.fetch`)接口设计
45
45
  * - Modeled after Web API `fetch` (`Window.fetch`)
46
- * - 统一 VPN App 与 Node.js 环境中的请求调用
47
- * - Unify request calls across VPN apps and Node.js
46
+ * - 统一 VPN App、Worker 与 Node.js 环境中的请求调用
47
+ * - Unify request calls across VPN apps, Worker, and Node.js
48
48
  *
49
49
  * 功能:
50
50
  * Features:
51
- * - 统一 Quantumult X / Loon / Surge / Stash / Egern / Shadowrocket / Node.js 请求接口
52
- * - Normalize request APIs across Quantumult X / Loon / Surge / Stash / Egern / Shadowrocket / Node.js
51
+ * - 统一 Quantumult X / Loon / Surge / Stash / Egern / Shadowrocket / Worker / Node.js 请求接口
52
+ * - Normalize request APIs across Quantumult X / Loon / Surge / Stash / Egern / Shadowrocket / Worker / Node.js
53
53
  * - 统一返回体字段(`ok/status/statusText/body/bodyBytes`)
54
54
  * - Normalize response fields (`ok/status/statusText/body/bodyBytes`)
55
55
  *
@@ -57,8 +57,10 @@ import { StatusTexts } from "./StatusTexts.mjs";
57
57
  * Known differences from Web `fetch`:
58
58
  * - 支持 `policy`、`auto-redirect` 等平台扩展字段
59
59
  * - Supports platform extension fields like `policy` and `auto-redirect`
60
- * - Node.js 分支默认启用 Cookie 透传,可通过 `auto-cookie` 关闭
61
- * - Node.js enables Cookie forwarding by default and can disable it via `auto-cookie`
60
+ * - Worker / Node.js 共享基于 `fetch` 的请求分支
61
+ * - Worker / Node.js share the `fetch`-based request branch
62
+ * - `auto-cookie` 在 Worker / Node.js 共享分支中识别
63
+ * - `auto-cookie` is recognized by the shared Worker / Node.js branch
62
64
  * - 非浏览器平台通过 `$httpClient/$task` 实现,不是原生 Fetch 实现
63
65
  * - Non-browser platforms use `$httpClient/$task` instead of native Fetch engine
64
66
  * - 返回结构包含 `statusCode/bodyBytes` 等兼容字段
@@ -117,6 +119,7 @@ export async function fetch(resource, options = {}) {
117
119
  switch ($app) {
118
120
  case "Loon":
119
121
  case "Quantumult X":
122
+ case "Worker":
120
123
  case "Node.js":
121
124
  // 这些平台要求毫秒,因此把秒重新换算为毫秒。
122
125
  // These platforms expect milliseconds, so convert seconds back to milliseconds.
@@ -238,9 +241,10 @@ export async function fetch(resource, options = {}) {
238
241
  }, resource.timeout);
239
242
  }),
240
243
  ]);
244
+ case "Worker":
241
245
  case "Node.js": {
242
- // Node.js 优先复用原生/宿主 `fetch`,缺失时再回退到 `node-fetch`。
243
- // Reuse host `fetch` in Node.js when available and fall back to `node-fetch` otherwise.
246
+ // Worker 复用宿主 `fetch`;Node.js 优先复用原生 `fetch`,缺失时再回退到 `node-fetch`。
247
+ // Worker reuses host `fetch`; Node.js reuses native `fetch` first and falls back to `node-fetch`.
244
248
  if (!globalThis.fetch) globalThis.fetch = require("node-fetch");
245
249
  switch (resource["auto-cookie"]) {
246
250
  case undefined:
@@ -261,8 +265,8 @@ export async function fetch(resource, options = {}) {
261
265
  case -1:
262
266
  break;
263
267
  }
264
- // 将通用字段映射到 Node.js Fetch 语义。
265
- // Map shared fields to Node.js Fetch semantics.
268
+ // 将通用字段映射到 Worker / Node.js Fetch 语义。
269
+ // Map shared fields to Worker / Node.js Fetch semantics.
266
270
  resource.redirect = resource.redirection ? "follow" : "manual";
267
271
  const { url, ...options } = resource;
268
272
  // 发起请求并归一化响应头、文本与二进制响应体。
package/polyfill/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./Console.mjs";
2
2
  export * from "./fetch.mjs";
3
+ export * from "./KV.mjs";
3
4
  export * from "./Lodash.mjs";
4
5
  export * from "./StatusTexts.mjs";
5
6
  export * from "./Storage.mjs";
@@ -0,0 +1,54 @@
1
+ declare module "@nsnanocat/util" {
2
+ export type AppName =
3
+ | "Quantumult X"
4
+ | "Loon"
5
+ | "Shadowrocket"
6
+ | "Egern"
7
+ | "Surge"
8
+ | "Stash"
9
+ | "Worker"
10
+ | "Node.js";
11
+
12
+ export const $app: AppName | undefined;
13
+ export const $argument: Record<string, unknown>;
14
+ export const argument: typeof $argument;
15
+
16
+ export interface DonePayload {
17
+ status?: number | string;
18
+ url?: string;
19
+ headers?: Record<string, unknown>;
20
+ body?: string | ArrayBuffer | ArrayBufferView;
21
+ bodyBytes?: ArrayBuffer;
22
+ policy?: string;
23
+ }
24
+
25
+ export function done(object?: DonePayload): void;
26
+
27
+ export interface NotificationContentObject {
28
+ open?: string;
29
+ "open-url"?: string;
30
+ url?: string;
31
+ openUrl?: string;
32
+ copy?: string;
33
+ "update-pasteboard"?: string;
34
+ updatePasteboard?: string;
35
+ media?: string;
36
+ "media-url"?: string;
37
+ mediaUrl?: string;
38
+ mime?: string;
39
+ "auto-dismiss"?: number;
40
+ sound?: string;
41
+ }
42
+
43
+ export type NotificationContent = NotificationContentObject | string | number | boolean;
44
+
45
+ export function notification(
46
+ title?: string,
47
+ subtitle?: string,
48
+ body?: string,
49
+ content?: NotificationContent,
50
+ ): void;
51
+
52
+ export function time(format: string, ts?: number): string;
53
+ export function wait(delay?: number): Promise<void>;
54
+ }
@@ -0,0 +1,4 @@
1
+ declare module "@nsnanocat/util/lib/environment.mjs" {
2
+ export const $environment: Record<string, unknown>;
3
+ export function environment(): Record<string, unknown>;
4
+ }
@@ -0,0 +1,29 @@
1
+ declare module "@nsnanocat/util" {
2
+ export interface FetchRequest {
3
+ url: string;
4
+ method?: string;
5
+ headers?: Record<string, unknown>;
6
+ body?: string | ArrayBuffer | ArrayBufferView | object;
7
+ bodyBytes?: ArrayBuffer;
8
+ timeout?: number | string;
9
+ policy?: string;
10
+ redirection?: boolean;
11
+ "auto-redirect"?: boolean;
12
+ "auto-cookie"?: boolean | number | string;
13
+ opts?: Record<string, unknown>;
14
+ [key: string]: unknown;
15
+ }
16
+
17
+ export interface FetchResponse {
18
+ ok: boolean;
19
+ status: number;
20
+ statusCode?: number;
21
+ statusText?: string;
22
+ headers?: Record<string, unknown>;
23
+ body?: string | ArrayBuffer;
24
+ bodyBytes?: ArrayBuffer;
25
+ [key: string]: unknown;
26
+ }
27
+
28
+ export function fetch(resource: FetchRequest | string, options?: Partial<FetchRequest>): Promise<FetchResponse>;
29
+ }
@@ -0,0 +1,22 @@
1
+ declare module "@nsnanocat/util/getStorage.mjs" {
2
+ export interface StorageProfile {
3
+ Settings: Record<string, unknown>;
4
+ Configs: Record<string, unknown>;
5
+ Caches: Record<string, unknown>;
6
+ }
7
+
8
+ export function traverseObject(
9
+ o: Record<string, unknown>,
10
+ c: (key: string, value: unknown) => unknown,
11
+ ): Record<string, unknown>;
12
+
13
+ export function string2number(string: string): string | number;
14
+
15
+ export function value2array(value: string | number | boolean | string[] | null | undefined): Array<string | number | boolean>;
16
+
17
+ export default function getStorage(
18
+ key: string,
19
+ names: string | string[] | Array<string | string[]>,
20
+ database: Record<string, unknown>,
21
+ ): StorageProfile;
22
+ }
@@ -0,0 +1,35 @@
1
+ declare module "@nsnanocat/util" {
2
+ export interface KVNamespaceLike {
3
+ get(key: string): Promise<string | null>;
4
+ put(key: string, value: string): Promise<void>;
5
+ delete(key: string): Promise<void>;
6
+ list?(options?: KVListOptions): Promise<KVListResult>;
7
+ }
8
+
9
+ export interface KVListOptions {
10
+ prefix?: string;
11
+ limit?: number;
12
+ cursor?: string;
13
+ }
14
+
15
+ export interface KVListKey {
16
+ name: string;
17
+ expiration?: number;
18
+ metadata?: Record<string, unknown>;
19
+ }
20
+
21
+ export interface KVListResult {
22
+ keys: KVListKey[];
23
+ list_complete: boolean;
24
+ cursor: string;
25
+ }
26
+
27
+ export class KV {
28
+ constructor(namespace?: KVNamespaceLike | null);
29
+ getItem<T = unknown>(keyName: string, defaultValue?: T): Promise<T>;
30
+ setItem(keyName: string, keyValue: unknown): Promise<boolean>;
31
+ removeItem(keyName: string): Promise<boolean>;
32
+ clear(): Promise<boolean>;
33
+ list(options?: KVListOptions): Promise<KVListResult>;
34
+ }
35
+ }
@@ -0,0 +1,34 @@
1
+ declare module "@nsnanocat/util" {
2
+ export class Console {
3
+ static clear(): void;
4
+ static count(label?: string): void;
5
+ static countReset(label?: string): void;
6
+ static debug(...msg: unknown[]): void;
7
+ static error(...msg: unknown[]): void;
8
+ static exception(...msg: unknown[]): void;
9
+ static group(label: string): number;
10
+ static groupEnd(): string | undefined;
11
+ static info(...msg: unknown[]): void;
12
+ static get logLevel(): "OFF" | "ERROR" | "WARN" | "INFO" | "DEBUG" | "ALL";
13
+ static set logLevel(level: number | string);
14
+ static log(...msg: unknown[]): void;
15
+ static time(label?: string): Map<string, number>;
16
+ static timeEnd(label?: string): boolean;
17
+ static timeLog(label?: string): void;
18
+ static warn(...msg: unknown[]): void;
19
+ }
20
+
21
+ export class Lodash {
22
+ static escape(string: string): string;
23
+ static get<T = unknown, D = undefined>(object?: Record<string, unknown>, path?: string | string[], defaultValue?: D): T | D;
24
+ static merge<T extends Record<string, unknown>>(object: T, ...sources: Array<Record<string, unknown> | null | undefined>): T;
25
+ static omit<T extends Record<string, unknown>>(object?: T, paths?: string | string[]): T;
26
+ static pick<T extends Record<string, unknown>, K extends keyof T>(object?: T, paths?: K | K[]): Pick<T, K>;
27
+ static set<T extends Record<string, unknown>>(object: T, path: string | string[], value: unknown): T;
28
+ static toPath(value: string): string[];
29
+ static unescape(string: string): string;
30
+ static unset(object?: Record<string, unknown>, path?: string | string[]): boolean;
31
+ }
32
+
33
+ export const StatusTexts: Record<number, string>;
34
+ }
@@ -0,0 +1,10 @@
1
+ declare module "@nsnanocat/util" {
2
+ export class Storage {
3
+ static data: Record<string, unknown> | null;
4
+ static dataFile: string;
5
+ static getItem<T = unknown>(keyName: string, defaultValue?: T): T;
6
+ static setItem(keyName: string, keyValue: unknown): boolean;
7
+ static removeItem(keyName: string): boolean;
8
+ static clear(): boolean;
9
+ }
10
+ }
@@ -1,151 +1,7 @@
1
- declare module "@nsnanocat/util" {
2
- export type AppName =
3
- | "Quantumult X"
4
- | "Loon"
5
- | "Shadowrocket"
6
- | "Egern"
7
- | "Surge"
8
- | "Stash"
9
- | "Node.js";
10
-
11
- export const $app: AppName | undefined;
12
- export const $argument: Record<string, unknown>;
13
- export const argument: typeof $argument;
14
-
15
- export interface DonePayload {
16
- status?: number | string;
17
- url?: string;
18
- headers?: Record<string, unknown>;
19
- body?: string | ArrayBuffer | ArrayBufferView;
20
- bodyBytes?: ArrayBuffer;
21
- policy?: string;
22
- }
23
-
24
- export function done(object?: DonePayload): void;
25
-
26
- export interface NotificationContentObject {
27
- open?: string;
28
- "open-url"?: string;
29
- url?: string;
30
- openUrl?: string;
31
- copy?: string;
32
- "update-pasteboard"?: string;
33
- updatePasteboard?: string;
34
- media?: string;
35
- "media-url"?: string;
36
- mediaUrl?: string;
37
- mime?: string;
38
- "auto-dismiss"?: number;
39
- sound?: string;
40
- }
41
-
42
- export type NotificationContent = NotificationContentObject | string | number | boolean;
43
-
44
- export function notification(
45
- title?: string,
46
- subtitle?: string,
47
- body?: string,
48
- content?: NotificationContent,
49
- ): void;
50
-
51
- export function time(format: string, ts?: number): string;
52
- export function wait(delay?: number): Promise<void>;
53
-
54
- export interface FetchRequest {
55
- url: string;
56
- method?: string;
57
- headers?: Record<string, unknown>;
58
- body?: string | ArrayBuffer | ArrayBufferView | object;
59
- bodyBytes?: ArrayBuffer;
60
- timeout?: number | string;
61
- policy?: string;
62
- redirection?: boolean;
63
- "auto-redirect"?: boolean;
64
- "auto-cookie"?: boolean | number | string;
65
- opts?: Record<string, unknown>;
66
- [key: string]: unknown;
67
- }
68
-
69
- export interface FetchResponse {
70
- ok: boolean;
71
- status: number;
72
- statusCode?: number;
73
- statusText?: string;
74
- headers?: Record<string, unknown>;
75
- body?: string | ArrayBuffer;
76
- bodyBytes?: ArrayBuffer;
77
- [key: string]: unknown;
78
- }
79
-
80
- export function fetch(resource: FetchRequest | string, options?: Partial<FetchRequest>): Promise<FetchResponse>;
81
-
82
- export class Console {
83
- static clear(): void;
84
- static count(label?: string): void;
85
- static countReset(label?: string): void;
86
- static debug(...msg: unknown[]): void;
87
- static error(...msg: unknown[]): void;
88
- static exception(...msg: unknown[]): void;
89
- static group(label: string): number;
90
- static groupEnd(): string | undefined;
91
- static info(...msg: unknown[]): void;
92
- static get logLevel(): "OFF" | "ERROR" | "WARN" | "INFO" | "DEBUG" | "ALL";
93
- static set logLevel(level: number | string);
94
- static log(...msg: unknown[]): void;
95
- static time(label?: string): Map<string, number>;
96
- static timeEnd(label?: string): boolean;
97
- static timeLog(label?: string): void;
98
- static warn(...msg: unknown[]): void;
99
- }
100
-
101
- export class Lodash {
102
- static escape(string: string): string;
103
- static get<T = unknown, D = undefined>(object?: Record<string, unknown>, path?: string | string[], defaultValue?: D): T | D;
104
- static merge<T extends Record<string, unknown>>(object: T, ...sources: Array<Record<string, unknown> | null | undefined>): T;
105
- static omit<T extends Record<string, unknown>>(object?: T, paths?: string | string[]): T;
106
- static pick<T extends Record<string, unknown>, K extends keyof T>(object?: T, paths?: K | K[]): Pick<T, K>;
107
- static set<T extends Record<string, unknown>>(object: T, path: string | string[], value: unknown): T;
108
- static toPath(value: string): string[];
109
- static unescape(string: string): string;
110
- static unset(object?: Record<string, unknown>, path?: string | string[]): boolean;
111
- }
112
-
113
- export const StatusTexts: Record<number, string>;
114
-
115
- export class Storage {
116
- static data: Record<string, unknown> | null;
117
- static dataFile: string;
118
- static getItem<T = unknown>(keyName: string, defaultValue?: T): T;
119
- static setItem(keyName: string, keyValue: unknown): boolean;
120
- static removeItem(keyName: string): boolean;
121
- static clear(): boolean;
122
- }
123
- }
124
-
125
- declare module "@nsnanocat/util/getStorage.mjs" {
126
- export interface StorageProfile {
127
- Settings: Record<string, unknown>;
128
- Configs: Record<string, unknown>;
129
- Caches: Record<string, unknown>;
130
- }
131
-
132
- export function traverseObject(
133
- o: Record<string, unknown>,
134
- c: (key: string, value: unknown) => unknown,
135
- ): Record<string, unknown>;
136
-
137
- export function string2number(string: string): string | number;
138
-
139
- export function value2array(value: string | number | boolean | string[] | null | undefined): Array<string | number | boolean>;
140
-
141
- export default function getStorage(
142
- key: string,
143
- names: string | string[] | Array<string | string[]>,
144
- database: Record<string, unknown>,
145
- ): StorageProfile;
146
- }
147
-
148
- declare module "@nsnanocat/util/lib/environment.mjs" {
149
- export const $environment: Record<string, unknown>;
150
- export function environment(): Record<string, unknown>;
151
- }
1
+ /// <reference path="./modules/core.d.ts" />
2
+ /// <reference path="./modules/fetch.d.ts" />
3
+ /// <reference path="./modules/polyfills.d.ts" />
4
+ /// <reference path="./modules/storage.d.ts" />
5
+ /// <reference path="./modules/kv.d.ts" />
6
+ /// <reference path="./modules/getStorage.d.ts" />
7
+ /// <reference path="./modules/environment.d.ts" />