@nsnanocat/util 2.6.3 → 2.6.4

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
@@ -90,6 +90,7 @@ import {
90
90
  - `lib/environment.mjs`
91
91
  - `lib/runScript.mjs`
92
92
  - `getStorage.mjs`(薯条项目自用,仅当你的存储结构与薯条项目一致时再使用;请通过子路径 `@nsnanocat/util/getStorage.mjs` 导入)
93
+ - `polyfill/Storage.cjs`(Node.js / Worker CJS 入口;请通过子路径 `@nsnanocat/util/polyfill/Storage` 导入)
93
94
 
94
95
  ## 模块依赖关系
95
96
 
@@ -108,7 +109,7 @@ import {
108
109
  | `getStorage.mjs` | `lib/argument.mjs`, `polyfill/Console.mjs`, `polyfill/Lodash.mjs`, `polyfill/Storage.mjs` | `Console.debug`, `Console.logLevel`, `Lodash.merge`, `Storage.getItem` | 先标准化 `$argument`,再合并默认配置/持久化配置/运行参数 |
109
110
  | `polyfill/Console.mjs` | `lib/app.mjs` | `$app` | 日志在 Worker / Node.js 与 iOS 脚本环境使用不同错误输出策略 |
110
111
  | `polyfill/fetch.mjs` | `lib/app.mjs`, `polyfill/Lodash.mjs`, `polyfill/StatusTexts.mjs`, `polyfill/Console.mjs` | `$app`, `Lodash.set`, `StatusTexts`(`Console` 当前版本未实际调用) | 按平台选请求引擎并做参数映射、响应结构统一 |
111
- | `polyfill/Storage.mjs` | `lib/app.mjs`, `polyfill/Lodash.mjs` | `$app`, `Lodash.get`, `Lodash.set`, `Lodash.unset` | 按平台选持久化后端并支持 `@key.path` 读写 |
112
+ | `polyfill/Storage.mjs` | `lib/app.mjs`, `polyfill/Lodash.mjs` | `$app`, `Lodash.get`, `Lodash.set`, `Lodash.unset` | ESM 路径下按平台选持久化后端并支持 `@key.path` 读写(Node.js 请走 `Storage.cjs`) |
112
113
  | `polyfill/Lodash.mjs` | 无 | 无 | 提供路径/合并等基础能力,被多个模块复用 |
113
114
  | `polyfill/qs.mjs` | `polyfill/Lodash.mjs` | `Lodash.get`, `Lodash.set`, `Lodash.toPath` | 提供查询字符串与对象之间的解析/序列化能力 |
114
115
  | `polyfill/StatusTexts.mjs` | 无 | 无 | 提供 HTTP 状态文案,供 `fetch/done` 使用 |
@@ -434,7 +435,11 @@ Worker / Node.js 使用说明:
434
435
 
435
436
  ### `polyfill/Storage.mjs`
436
437
 
437
- `Storage` 是仿照 Web Storage 接口(`Storage`)设计的跨平台持久化适配器:
438
+ `Storage` 已拆分为 ESM / CJS 两条运行路径:
439
+ - `polyfill/Storage.mjs`:用于 iOS 脚本平台 + Worker
440
+ - `polyfill/Storage.cjs`:用于 Worker / Node.js(含 `box.dat` 文件读写)
441
+
442
+ `polyfill/Storage.mjs` 仍仿照 Web Storage 接口(`Storage`)设计:
438
443
  - 参考文档:https://developer.mozilla.org/en-US/docs/Web/API/Storage
439
444
  - 中文文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Storage
440
445
  - 目标:统一 VPN App 脚本环境中的持久化读写接口,并尽量贴近 Web Storage 行为
@@ -452,23 +457,31 @@ Worker / Node.js 使用说明:
452
457
  - Quantumult X:可用(`$prefs.removeValueForKey`)。
453
458
  - Surge:通过 `$persistentStore.write(null, keyName)` 删除。
454
459
  - Worker:可用(仅删除内存缓存中的对应 key,不持久化)。
455
- - Node.js:可用(删除 `box.dat` 中对应 key 并落盘)。
460
+ - Node.js:ESM 路径不支持,会提示改用 CJS 入口。
456
461
  - Loon / Stash / Egern / Shadowrocket:返回 `false`。
457
462
 
458
463
  #### `Storage.clear()`
459
464
  - Quantumult X:可用(`$prefs.removeAllValues`)。
460
465
  - Worker:可用(仅清空内存缓存,不持久化)。
461
- - Node.js:可用(清空 `box.dat` 并落盘)。
466
+ - Node.js:ESM 路径不支持,会提示改用 CJS 入口。
462
467
  - 其他平台:返回 `false`。
463
468
 
464
- #### Worker / Node.js 特性
469
+ #### Worker 特性(ESM)
465
470
  - Worker:使用进程内内存缓存,不写文件。
471
+
472
+ #### Node.js 特性(CJS)
466
473
  - 数据文件默认:`box.dat`。
467
474
  - 读取路径优先级:当前目录 -> `process.cwd()`。
468
475
 
476
+ Node.js 使用说明:
477
+ - 请通过 CJS 入口调用:`require("@nsnanocat/util").Storage` 或 `require("@nsnanocat/util/polyfill/Storage").Storage`
478
+ - CJS 的 `Storage` 分支支持 `box.dat` 读写与落盘
479
+
469
480
  与 Web Storage 的行为差异:
470
481
  - 支持 `@key.path` 深路径读写(Web Storage 原生不支持)。
471
- - `removeItem/clear` 仅部分平台可用(目前为 Quantumult X、Worker、Node.js,以及 Surge 的 `removeItem`)。
482
+ - `removeItem/clear` 仅部分平台可用:
483
+ - ESM 路径:Quantumult X、Worker,以及 Surge 的 `removeItem`
484
+ - CJS 路径:Worker、Node.js
472
485
  - `getItem` 会尝试 `JSON.parse`,`setItem` 写入对象会 `JSON.stringify`。
473
486
 
474
487
  平台后端映射:
@@ -477,8 +490,8 @@ Worker / Node.js 使用说明:
477
490
  | --- | --- |
478
491
  | Surge / Loon / Stash / Egern / Shadowrocket | `$persistentStore.read/write` |
479
492
  | Quantumult X | `$prefs.valueForKey/setValueForKey` |
480
- | Worker | 进程内内存缓存 |
481
- | Node.js | 本地 `box.dat` |
493
+ | Worker(ESM/CJS) | 进程内内存缓存 |
494
+ | Node.js(仅 CJS) | 本地 `box.dat` |
482
495
 
483
496
  ### `polyfill/Console.mjs`
484
497
 
package/getStorage.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import "./lib/argument.mjs";
2
2
  import { Console } from "./polyfill/Console.mjs";
3
3
  import { Lodash as _ } from "./polyfill/Lodash.mjs";
4
- import { Storage } from "./polyfill/Storage.js";
4
+ import { Storage } from "./polyfill/Storage.mjs";
5
5
 
6
6
  /**
7
7
  * 存储配置读取与合并结果。
package/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
 
3
3
  const { fetch } = require("./polyfill/fetch.cjs");
4
+ const { Storage } = require("./polyfill/Storage.cjs");
4
5
 
5
6
  module.exports = {
6
7
  fetch,
8
+ Storage,
7
9
  };
package/index.js CHANGED
@@ -9,7 +9,7 @@ export * from "./polyfill/fetch.mjs";
9
9
  export * from "./polyfill/Lodash.mjs";
10
10
  export * from "./polyfill/qs.mjs";
11
11
  export * from "./polyfill/StatusTexts.mjs";
12
- export * from "./polyfill/Storage.js";
12
+ export * from "./polyfill/Storage.mjs";
13
13
 
14
14
  /**
15
15
  * 已标准化的 `$argument` 快照。
package/package.json CHANGED
@@ -23,6 +23,10 @@
23
23
  "import": "./polyfill/fetch.mjs",
24
24
  "require": "./polyfill/fetch.cjs"
25
25
  },
26
+ "./polyfill/Storage": {
27
+ "import": "./polyfill/Storage.mjs",
28
+ "require": "./polyfill/Storage.cjs"
29
+ },
26
30
  "./*": "./*"
27
31
  },
28
32
  "types": "types/nsnanocat-util.d.ts",
@@ -59,5 +63,5 @@
59
63
  "registry": "https://registry.npmjs.org/",
60
64
  "access": "public"
61
65
  },
62
- "version": "2.6.3"
66
+ "version": "2.6.4"
63
67
  }
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+
3
+ const { Lodash: _ } = require("./Lodash.mjs");
4
+
5
+ /**
6
+ * 仅面向 Worker / Node.js 的持久化存储适配器(CJS 版本)。
7
+ * Persistent storage adapter for Worker / Node.js only (CJS version).
8
+ */
9
+ class Storage {
10
+ /**
11
+ * Worker / Node.js 环境下的内存数据缓存。
12
+ * In-memory data cache for Worker / Node.js runtime.
13
+ *
14
+ * @type {Record<string, any>|null}
15
+ */
16
+ static data = null;
17
+
18
+ /**
19
+ * Node.js 持久化文件名。
20
+ * Data file name used in Node.js.
21
+ *
22
+ * @type {string}
23
+ */
24
+ static dataFile = "box.dat";
25
+
26
+ /**
27
+ * `@key.path` 解析正则。
28
+ * Regex for `@key.path` parsing.
29
+ *
30
+ * @type {RegExp}
31
+ */
32
+ static #nameRegex = /^@(?<key>[^.]+)(?:\.(?<path>.*))?$/;
33
+
34
+ /**
35
+ * 读取存储值。
36
+ * Read value from persistent storage.
37
+ *
38
+ * @param {string} keyName 键名或路径键 / Key or path key.
39
+ * @param {*} [defaultValue=null] 默认值 / Default value when key is missing.
40
+ * @returns {*}
41
+ */
42
+ static getItem(keyName, defaultValue = null) {
43
+ let keyValue = defaultValue;
44
+ switch (keyName.startsWith("@")) {
45
+ case true: {
46
+ const { key, path } = keyName.match(Storage.#nameRegex)?.groups;
47
+ keyName = key;
48
+ let value = Storage.getItem(keyName, {});
49
+ if (typeof value !== "object") value = {};
50
+ keyValue = _.get(value, path);
51
+ try {
52
+ keyValue = JSON.parse(keyValue);
53
+ } catch {}
54
+ break;
55
+ }
56
+ default:
57
+ if (typeof process !== "undefined" && process.versions?.node) {
58
+ Storage.data = Storage.#loaddata(Storage.dataFile);
59
+ keyValue = Storage.data?.[keyName];
60
+ } else {
61
+ Storage.data = Storage.data ?? {};
62
+ keyValue = Storage.data[keyName];
63
+ }
64
+ try {
65
+ keyValue = JSON.parse(keyValue);
66
+ } catch {}
67
+ break;
68
+ }
69
+ return keyValue ?? defaultValue;
70
+ }
71
+
72
+ /**
73
+ * 写入存储值。
74
+ * Write value into persistent storage.
75
+ *
76
+ * @param {string} keyName 键名或路径键 / Key or path key.
77
+ * @param {*} keyValue 写入值 / Value to store.
78
+ * @returns {boolean}
79
+ */
80
+ static setItem(keyName = new String(), keyValue = new String()) {
81
+ let result = false;
82
+ switch (typeof keyValue) {
83
+ case "object":
84
+ keyValue = JSON.stringify(keyValue);
85
+ break;
86
+ default:
87
+ keyValue = String(keyValue);
88
+ break;
89
+ }
90
+ switch (keyName.startsWith("@")) {
91
+ case true: {
92
+ const { key, path } = keyName.match(Storage.#nameRegex)?.groups;
93
+ keyName = key;
94
+ let value = Storage.getItem(keyName, {});
95
+ if (typeof value !== "object") value = {};
96
+ _.set(value, path, keyValue);
97
+ result = Storage.setItem(keyName, value);
98
+ break;
99
+ }
100
+ default:
101
+ if (typeof process !== "undefined" && process.versions?.node) {
102
+ Storage.data = Storage.#loaddata(Storage.dataFile);
103
+ Storage.data[keyName] = keyValue;
104
+ Storage.#writedata(Storage.dataFile);
105
+ result = true;
106
+ } else {
107
+ Storage.data = Storage.data ?? {};
108
+ Storage.data[keyName] = keyValue;
109
+ result = true;
110
+ }
111
+ break;
112
+ }
113
+ return result;
114
+ }
115
+
116
+ /**
117
+ * 删除存储值。
118
+ * Remove value from persistent storage.
119
+ *
120
+ * @param {string} keyName 键名或路径键 / Key or path key.
121
+ * @returns {boolean}
122
+ */
123
+ static removeItem(keyName) {
124
+ let result = false;
125
+ switch (keyName.startsWith("@")) {
126
+ case true: {
127
+ const { key, path } = keyName.match(Storage.#nameRegex)?.groups;
128
+ keyName = key;
129
+ let value = Storage.getItem(keyName);
130
+ if (typeof value !== "object") value = {};
131
+ _.unset(value, path);
132
+ result = Storage.setItem(keyName, value);
133
+ break;
134
+ }
135
+ default:
136
+ if (typeof process !== "undefined" && process.versions?.node) {
137
+ Storage.data = Storage.#loaddata(Storage.dataFile);
138
+ delete Storage.data[keyName];
139
+ Storage.#writedata(Storage.dataFile);
140
+ result = true;
141
+ } else {
142
+ Storage.data = Storage.data ?? {};
143
+ delete Storage.data[keyName];
144
+ result = true;
145
+ }
146
+ break;
147
+ }
148
+ return result;
149
+ }
150
+
151
+ /**
152
+ * 清空存储。
153
+ * Clear storage.
154
+ *
155
+ * @returns {boolean}
156
+ */
157
+ static clear() {
158
+ if (typeof process !== "undefined" && process.versions?.node) {
159
+ Storage.data = Storage.#loaddata(Storage.dataFile);
160
+ Storage.data = {};
161
+ Storage.#writedata(Storage.dataFile);
162
+ return true;
163
+ }
164
+ Storage.data = {};
165
+ return true;
166
+ }
167
+
168
+ /**
169
+ * 从 Node.js 数据文件加载 JSON。
170
+ * Load JSON data from Node.js data file.
171
+ *
172
+ * @private
173
+ * @param {string} dataFile 数据文件名 / Data file name.
174
+ * @returns {Record<string, any>}
175
+ */
176
+ static #loaddata = dataFile => {
177
+ const fs = require("node:fs");
178
+ const path = require("node:path");
179
+ const curDirDataFilePath = path.resolve(dataFile);
180
+ const rootDirDataFilePath = path.resolve(process.cwd(), dataFile);
181
+ const isCurDirDataFile = fs.existsSync(curDirDataFilePath);
182
+ const isRootDirDataFile = !isCurDirDataFile && fs.existsSync(rootDirDataFilePath);
183
+ if (isCurDirDataFile || isRootDirDataFile) {
184
+ const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath;
185
+ try {
186
+ return JSON.parse(fs.readFileSync(datPath));
187
+ } catch {
188
+ return {};
189
+ }
190
+ }
191
+ return {};
192
+ };
193
+
194
+ /**
195
+ * 将内存数据写入 Node.js 数据文件。
196
+ * Persist in-memory data to Node.js data file.
197
+ *
198
+ * @private
199
+ * @param {string} [dataFile=this.dataFile] 数据文件名 / Data file name.
200
+ * @returns {void}
201
+ */
202
+ static #writedata = (dataFile = this.dataFile) => {
203
+ const fs = require("node:fs");
204
+ const path = require("node:path");
205
+ const curDirDataFilePath = path.resolve(dataFile);
206
+ const rootDirDataFilePath = path.resolve(process.cwd(), dataFile);
207
+ const isCurDirDataFile = fs.existsSync(curDirDataFilePath);
208
+ const isRootDirDataFile = !isCurDirDataFile && fs.existsSync(rootDirDataFilePath);
209
+ const jsondata = JSON.stringify(this.data);
210
+ if (isCurDirDataFile) {
211
+ fs.writeFileSync(curDirDataFilePath, jsondata);
212
+ } else if (isRootDirDataFile) {
213
+ fs.writeFileSync(rootDirDataFilePath, jsondata);
214
+ } else {
215
+ fs.writeFileSync(curDirDataFilePath, jsondata);
216
+ }
217
+ };
218
+ }
219
+
220
+ module.exports = { Storage };
@@ -82,7 +82,7 @@ export class Storage {
82
82
  keyValue = _.get(value, path);
83
83
  try {
84
84
  keyValue = JSON.parse(keyValue);
85
- } catch (e) {}
85
+ } catch {}
86
86
  break;
87
87
  }
88
88
  default:
@@ -102,16 +102,14 @@ export class Storage {
102
102
  keyValue = Storage.data[keyName];
103
103
  break;
104
104
  case "Node.js":
105
- Storage.data = Storage.#loaddata(Storage.dataFile);
106
- keyValue = Storage.data?.[keyName];
107
- break;
105
+ throw new Error(`${Storage.name}.getItem: ESM 版本不支持 Node.js,请改用 CJS 入口`);
108
106
  default:
109
107
  keyValue = Storage.data?.[keyName] || null;
110
108
  break;
111
109
  }
112
110
  try {
113
111
  keyValue = JSON.parse(keyValue);
114
- } catch (e) {
112
+ } catch {
115
113
  // do nothing
116
114
  }
117
115
  break;
@@ -165,11 +163,7 @@ export class Storage {
165
163
  result = true;
166
164
  break;
167
165
  case "Node.js":
168
- Storage.data = Storage.#loaddata(Storage.dataFile);
169
- Storage.data[keyName] = keyValue;
170
- Storage.#writedata(Storage.dataFile);
171
- result = true;
172
- break;
166
+ throw new Error(`${Storage.name}.setItem: ESM 版本不支持 Node.js,请改用 CJS 入口`);
173
167
  default:
174
168
  result = Storage.data?.[keyName] || null;
175
169
  break;
@@ -200,7 +194,7 @@ export class Storage {
200
194
  keyName = key;
201
195
  let value = Storage.getItem(keyName);
202
196
  if (typeof value !== "object") value = {};
203
- keyValue = _.unset(value, path);
197
+ _.unset(value, path);
204
198
  result = Storage.setItem(keyName, value);
205
199
  break;
206
200
  }
@@ -224,12 +218,7 @@ export class Storage {
224
218
  result = true;
225
219
  break;
226
220
  case "Node.js":
227
- // result = false;
228
- Storage.data = Storage.#loaddata(Storage.dataFile);
229
- delete Storage.data[keyName];
230
- Storage.#writedata(Storage.dataFile);
231
- result = true;
232
- break;
221
+ throw new Error(`${Storage.name}.removeItem: ESM 版本不支持 Node.js,请改用 CJS 入口`);
233
222
  default:
234
223
  result = false;
235
224
  break;
@@ -263,12 +252,7 @@ export class Storage {
263
252
  result = true;
264
253
  break;
265
254
  case "Node.js":
266
- // result = false;
267
- Storage.data = Storage.#loaddata(Storage.dataFile);
268
- Storage.data = {};
269
- Storage.#writedata(Storage.dataFile);
270
- result = true;
271
- break;
255
+ throw new Error(`${Storage.name}.clear: ESM 版本不支持 Node.js,请改用 CJS 入口`);
272
256
  default:
273
257
  result = false;
274
258
  break;
@@ -276,57 +260,4 @@ export class Storage {
276
260
  return result;
277
261
  }
278
262
 
279
- /**
280
- * 从 Node.js 数据文件加载 JSON。
281
- * Load JSON data from Node.js data file.
282
- *
283
- * @private
284
- * @param {string} dataFile 数据文件名 / Data file name.
285
- * @returns {Record<string, any>}
286
- */
287
- static #loaddata = dataFile => {
288
- if ($app === "Node.js") {
289
- this.fs = this.fs ? this.fs : require("fs");
290
- this.path = this.path ? this.path : require("path");
291
- const curDirDataFilePath = this.path.resolve(dataFile);
292
- const rootDirDataFilePath = this.path.resolve(process.cwd(), dataFile);
293
- const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath);
294
- const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath);
295
- if (isCurDirDataFile || isRootDirDataFile) {
296
- const datPath = isCurDirDataFile ? curDirDataFilePath : rootDirDataFilePath;
297
- try {
298
- return JSON.parse(this.fs.readFileSync(datPath));
299
- } catch (e) {
300
- return {};
301
- }
302
- } else return {};
303
- } else return {};
304
- };
305
-
306
- /**
307
- * 将内存数据写入 Node.js 数据文件。
308
- * Persist in-memory data to Node.js data file.
309
- *
310
- * @private
311
- * @param {string} [dataFile=this.dataFile] 数据文件名 / Data file name.
312
- * @returns {void}
313
- */
314
- static #writedata = (dataFile = this.dataFile) => {
315
- if ($app === "Node.js") {
316
- this.fs = this.fs ? this.fs : require("fs");
317
- this.path = this.path ? this.path : require("path");
318
- const curDirDataFilePath = this.path.resolve(dataFile);
319
- const rootDirDataFilePath = this.path.resolve(process.cwd(), dataFile);
320
- const isCurDirDataFile = this.fs.existsSync(curDirDataFilePath);
321
- const isRootDirDataFile = !isCurDirDataFile && this.fs.existsSync(rootDirDataFilePath);
322
- const jsondata = JSON.stringify(this.data);
323
- if (isCurDirDataFile) {
324
- this.fs.writeFileSync(curDirDataFilePath, jsondata);
325
- } else if (isRootDirDataFile) {
326
- this.fs.writeFileSync(rootDirDataFilePath, jsondata);
327
- } else {
328
- this.fs.writeFileSync(curDirDataFilePath, jsondata);
329
- }
330
- }
331
- };
332
263
  }
@@ -8,4 +8,4 @@ export type { Fetch, FetchRequest, FetchResponse } from "./fetch.d.ts";
8
8
  export { Lodash } from "./Lodash.mjs";
9
9
  export { qs } from "./qs.mjs";
10
10
  export { StatusTexts } from "./StatusTexts.mjs";
11
- export { Storage } from "./Storage.js";
11
+ export { Storage } from "./Storage.mjs";
package/polyfill/index.js CHANGED
@@ -3,4 +3,4 @@ export * from "./fetch.mjs";
3
3
  export * from "./Lodash.mjs";
4
4
  export * from "./qs.mjs";
5
5
  export * from "./StatusTexts.mjs";
6
- export * from "./Storage.js";
6
+ export * from "./Storage.mjs";