@jayfong/x-utils 1.0.0-202510061015 → 3.4.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.
Files changed (143) hide show
  1. package/lib/ai.d.mts +103 -0
  2. package/lib/ai.mjs +75 -0
  3. package/lib/cache.d.mts +464 -0
  4. package/lib/cache.mjs +531 -0
  5. package/lib/captcha.d.mts +259 -0
  6. package/lib/captcha.mjs +174 -0
  7. package/lib/case.d.mts +184 -0
  8. package/lib/case.mjs +120 -0
  9. package/lib/chinese.d.mts +57 -0
  10. package/lib/chinese.mjs +79 -0
  11. package/lib/cli.d.mts +52 -0
  12. package/lib/cli.mjs +62 -0
  13. package/lib/clipboard.d.mts +50 -0
  14. package/lib/clipboard.mjs +122 -0
  15. package/lib/cmd.d.mts +38 -0
  16. package/lib/cmd.mjs +52 -0
  17. package/lib/crawler.d.mts +330 -0
  18. package/lib/crawler.mjs +313 -0
  19. package/lib/crypto.d.mts +158 -0
  20. package/lib/crypto.mjs +213 -0
  21. package/lib/csv.d.mts +67 -0
  22. package/lib/csv.mjs +76 -0
  23. package/lib/data_set.d.mts +76 -0
  24. package/lib/data_set.mjs +104 -0
  25. package/lib/db_value.d.mts +213 -0
  26. package/lib/db_value.mjs +208 -0
  27. package/lib/dispose.d.mts +98 -0
  28. package/lib/dispose.mjs +107 -0
  29. package/lib/file_store.d.mts +167 -0
  30. package/lib/file_store.mjs +120 -0
  31. package/lib/fs.d.mts +111 -0
  32. package/lib/fs.mjs +127 -0
  33. package/lib/html.d.mts +103 -0
  34. package/lib/html.mjs +112 -0
  35. package/lib/json.d.mts +50 -0
  36. package/lib/json.mjs +74 -0
  37. package/lib/jwt.d.mts +128 -0
  38. package/lib/jwt.mjs +104 -0
  39. package/lib/mail.d.mts +125 -0
  40. package/lib/mail.mjs +108 -0
  41. package/lib/pay.d.mts +394 -0
  42. package/lib/pay.mjs +343 -0
  43. package/lib/puppeteer.d.mts +88 -0
  44. package/lib/puppeteer.mjs +219 -0
  45. package/lib/random.d.mts +71 -0
  46. package/lib/random.mjs +92 -0
  47. package/lib/redis.d.mts +32 -0
  48. package/lib/redis.mjs +24 -0
  49. package/lib/runner.d.mts +80 -0
  50. package/lib/runner.mjs +66 -0
  51. package/lib/sensitive_words.d.mts +82 -0
  52. package/lib/sensitive_words.mjs +73 -0
  53. package/lib/sms.d.mts +133 -0
  54. package/lib/sms.mjs +100 -0
  55. package/lib/task.d.mts +37 -0
  56. package/lib/task.mjs +43 -0
  57. package/lib/text.d.mts +84 -0
  58. package/lib/text.mjs +141 -0
  59. package/lib/yaml.d.mts +51 -0
  60. package/lib/yaml.mjs +64 -0
  61. package/package.json +82 -164
  62. package/_cjs/ai.js +0 -80
  63. package/_cjs/alipan.js +0 -173
  64. package/_cjs/cli.js +0 -51
  65. package/_cjs/clipboard.js +0 -65
  66. package/_cjs/cmd.js +0 -42
  67. package/_cjs/compressor.js +0 -47
  68. package/_cjs/crawler.js +0 -341
  69. package/_cjs/csv.js +0 -97
  70. package/_cjs/data_set.js +0 -115
  71. package/_cjs/env.js +0 -82
  72. package/_cjs/fs.js +0 -86
  73. package/_cjs/html.js +0 -62
  74. package/_cjs/http_server.js +0 -79
  75. package/_cjs/json.js +0 -49
  76. package/_cjs/ksb.js +0 -218
  77. package/_cjs/number.js +0 -39
  78. package/_cjs/ocr.js +0 -74
  79. package/_cjs/package.json +0 -3
  80. package/_cjs/path.js +0 -35
  81. package/_cjs/pinyin.js +0 -37
  82. package/_cjs/puppeteer.js +0 -271
  83. package/_cjs/runner.js +0 -88
  84. package/_cjs/task.js +0 -41
  85. package/_cjs/text.js +0 -136
  86. package/_cjs/unicode/lib_unicode.js +0 -58643
  87. package/_cjs/unicode/unicode.js +0 -441
  88. package/_cjs/upload.js +0 -101
  89. package/_cjs/yaml.js +0 -52
  90. package/ai.d.ts +0 -26
  91. package/ai.js +0 -60
  92. package/alipan.d.ts +0 -209
  93. package/alipan.js +0 -153
  94. package/cli.d.ts +0 -14
  95. package/cli.js +0 -21
  96. package/clipboard.d.ts +0 -6
  97. package/clipboard.js +0 -35
  98. package/cmd.d.ts +0 -5
  99. package/cmd.js +0 -22
  100. package/compressor.d.ts +0 -5
  101. package/compressor.js +0 -17
  102. package/crawler.d.ts +0 -162
  103. package/crawler.js +0 -332
  104. package/csv.d.ts +0 -14
  105. package/csv.js +0 -67
  106. package/data_set.d.ts +0 -22
  107. package/data_set.js +0 -85
  108. package/env.d.ts +0 -6
  109. package/env.js +0 -52
  110. package/fs.d.ts +0 -23
  111. package/fs.js +0 -56
  112. package/html.d.ts +0 -7
  113. package/html.js +0 -45
  114. package/http_server.d.ts +0 -13
  115. package/http_server.js +0 -49
  116. package/json.d.ts +0 -6
  117. package/json.js +0 -19
  118. package/ksb.d.ts +0 -37
  119. package/ksb.js +0 -198
  120. package/number.d.ts +0 -3
  121. package/number.js +0 -9
  122. package/ocr.d.ts +0 -17
  123. package/ocr.js +0 -54
  124. package/path.d.ts +0 -2
  125. package/path.js +0 -5
  126. package/pinyin.d.ts +0 -8
  127. package/pinyin.js +0 -17
  128. package/puppeteer.d.ts +0 -38
  129. package/puppeteer.js +0 -241
  130. package/runner.d.ts +0 -13
  131. package/runner.js +0 -58
  132. package/task.d.ts +0 -6
  133. package/task.js +0 -21
  134. package/text.d.ts +0 -63
  135. package/text.js +0 -119
  136. package/unicode/lib_unicode.d.ts +0 -4
  137. package/unicode/lib_unicode.js +0 -58623
  138. package/unicode/unicode.d.ts +0 -347
  139. package/unicode/unicode.js +0 -426
  140. package/upload.d.ts +0 -34
  141. package/upload.js +0 -71
  142. package/yaml.d.ts +0 -6
  143. package/yaml.js +0 -22
@@ -0,0 +1,104 @@
1
+ import { XCsv } from "./csv.mjs";
2
+ import { XDispose } from "./dispose.mjs";
3
+ import { XJson } from "./json.mjs";
4
+ import { XYaml } from "./yaml.mjs";
5
+ import { keyBy, keysStrict, pickStrict } from "vtils";
6
+ import { v } from "vtils/vae";
7
+ import * as path from "node:path";
8
+ import fs from "fs-extra";
9
+ // src/data_set.ts
10
+ /**
11
+ * 数据集实例,负责将记录数组映射到 `storeFile` 并做类型转换。
12
+ *
13
+ * @template T 数据集中的单条记录类型。
14
+ * @template K `T` 中作为主键的字段名。
15
+ */
16
+ var XDataSet = class {
17
+ options;
18
+ /** 存储格式,由 `storeFile` 扩展名推导得出。 */
19
+ storeType;
20
+ /** 记录结构的 vae schema 形状(字段名 → 字段 schema)。 */
21
+ dataShapeSchema;
22
+ /** 由 `dataShapeSchema` 组合得到的对象 schema,用于整体转换与校验。 */
23
+ dataSchema;
24
+ /** 根据 schema 推导出的 CSV 解析/序列化配置。 */
25
+ csvOptions;
26
+ /** 以 `dataKey` 字段值为键的记录映射,值为去掉主键后的记录。 */
27
+ currentMap;
28
+ /**
29
+ * 将 `currentMap` 还原为记录数组。
30
+ *
31
+ * 每条记录会按 schema 定义的字段顺序重组,并执行一次 schema 转换,
32
+ * 保证返回结构与 `dataSchema` 声明一致。
33
+ */
34
+ get current() {
35
+ const orderedDataFieldNames = keysStrict(this.dataShapeSchema);
36
+ return keysStrict(this.currentMap).map((dataKeyValue) => {
37
+ const data = {
38
+ ...this.currentMap[dataKeyValue],
39
+ [this.options.dataKey]: dataKeyValue
40
+ };
41
+ return this.dataSchema.cast(pickStrict(data, orderedDataFieldNames));
42
+ });
43
+ }
44
+ /**
45
+ * @param options 数据集配置,详见 {@link XDataSetOptions}。
46
+ *
47
+ * 构造函数会先根据 `storeFile` 扩展名确定存储格式,再基于 `dataSchema`
48
+ * 生成 CSV 所需的字段类型转换配置。若未设置 `noAutoWrite`,则启动定时器
49
+ * 周期性调用 {@link write},并在进程退出时做最后一次写入。
50
+ */
51
+ constructor(options) {
52
+ this.options = options;
53
+ this.storeType = options.storeFile.endsWith(".csv") ? "csv" : options.storeFile.endsWith(".yaml") || options.storeFile.endsWith(".yml") ? "yaml" : "json";
54
+ this.dataShapeSchema = options.dataSchema(v);
55
+ this.dataSchema = v.object(this.dataShapeSchema);
56
+ this.csvOptions = {
57
+ arrayDelimiter: options.csvArrayDelimiter,
58
+ arrayFieldNames: keysStrict(this.dataShapeSchema).filter((key) => this.dataShapeSchema[key].options.type === "array"),
59
+ objectFieldNames: keysStrict(this.dataShapeSchema).filter((key) => this.dataShapeSchema[key].options.type === "object"),
60
+ booleanFieldNames: keysStrict(this.dataShapeSchema).filter((key) => this.dataShapeSchema[key].options.type === "boolean"),
61
+ dateFieldNames: keysStrict(this.dataShapeSchema).filter((key) => this.dataShapeSchema[key].options.type === "date")
62
+ };
63
+ if (!options.noAutoWrite) {
64
+ const timer = setInterval(() => {
65
+ this.write();
66
+ }, options.autoWriteInterval || 5e3);
67
+ XDispose.exit.add(async () => {
68
+ clearInterval(timer);
69
+ await this.write();
70
+ });
71
+ }
72
+ }
73
+ /**
74
+ * 从 `storeFile` 读取数据集。
75
+ *
76
+ * - 文件不存在或读取/解析失败时返回空数组(不会抛错);
77
+ * - 读取成功后对每条记录执行 schema 转换,并重建 `currentMap`。
78
+ *
79
+ * @returns 读取并转换后的记录数组。
80
+ */
81
+ async read() {
82
+ let dataSet = [];
83
+ try {
84
+ dataSet = await (this.storeType === "csv" ? XCsv.read(this.options.storeFile, this.csvOptions) : this.storeType === "yaml" ? XYaml.read(this.options.storeFile) : XJson.read(this.options.storeFile));
85
+ } catch {}
86
+ dataSet = dataSet.map((data) => this.dataSchema.cast(data));
87
+ this.currentMap = keyBy(dataSet, (data) => data[this.options.dataKey]);
88
+ return dataSet;
89
+ }
90
+ /**
91
+ * 将数据集写入 `storeFile`。
92
+ *
93
+ * 写入前会确保父目录存在;未显式传入 `dataSet` 时使用 `current` 作为数据源。
94
+ *
95
+ * @param dataSet 要写入的记录数组,默认取 `current`。
96
+ */
97
+ async write(dataSet = this.current) {
98
+ console.log(`💥 写入 ${this.options.storeFile}`);
99
+ await fs.ensureDir(path.dirname(this.options.storeFile));
100
+ await (this.storeType === "csv" ? XCsv.write(this.options.storeFile, dataSet, this.csvOptions) : this.storeType === "yaml" ? XYaml.write(this.options.storeFile, dataSet) : XJson.write(this.options.storeFile, dataSet));
101
+ console.log(`✅ 写入 ${this.options.storeFile}`);
102
+ }
103
+ };
104
+ export { XDataSet };
@@ -0,0 +1,213 @@
1
+ /**
2
+ * `XDbValue.SimpleArray` 的配置项。
3
+ */
4
+ export interface XDbValueSimpleArrayOptions {
5
+ /**
6
+ * 元素之间的分隔符。
7
+ *
8
+ * @default ','
9
+ */
10
+ separator?: string;
11
+ /**
12
+ * 存储值的统一前后缀(成对包裹在序列化结果两侧)。
13
+ *
14
+ * 例如传入 `"` 后,`['a', 'b']` 会被写为 `"a,b"`。
15
+ * 读取时仅当值同时以该前后缀开头和结尾时才会被剥离,否则按原始文本解析,
16
+ * 避免误伤没有前后缀的历史数据。
17
+ *
18
+ * @default ''
19
+ */
20
+ affix?: string;
21
+ /**
22
+ * 是否将数组元素转换为数字。
23
+ *
24
+ * @default false
25
+ */
26
+ number?: boolean;
27
+ }
28
+ /**
29
+ * `XDbValue.JsonObject` 的转换钩子配置。
30
+ *
31
+ * @template T 对象值的类型。
32
+ */
33
+ export interface XDbValueJsonObjectOptions<T> {
34
+ /**
35
+ * 可选的转换钩子,分别在写入(add)、读取(get)、更新(update)时对值做预处理。
36
+ */
37
+ transform?: {
38
+ /** 写入(add)前对值做转换。 */
39
+ add?: (value: T) => T;
40
+ /** 读取(get)后对值做转换。 */
41
+ get?: (value: T) => T;
42
+ /** 更新(update)前对值做转换。 */
43
+ update?: (value: T) => T;
44
+ };
45
+ }
46
+ /**
47
+ * 数据库字段值的序列化与反序列化工具。
48
+ *
49
+ * 适用于把数组或对象以字符串形式存储到数据库字段(如 VARCHAR / TEXT)的场景,
50
+ * 内置三种存储格式:
51
+ * - {@link XDbValue.SimpleArray}:以分隔符连接的数组,支持前后缀与数字模式;
52
+ * - {@link XDbValue.JsonArray}:JSON 数组;
53
+ * - {@link XDbValue.JsonObject}:JSON 对象,支持读写转换钩子。
54
+ *
55
+ * 每种格式都提供 `add`(写入)、`get`(读取)、`update`(更新)三个方法:
56
+ * - `add` 返回序列化后的字符串,入参非法时使用默认值兜底;
57
+ * - `get` 将存储字符串反序列化为目标类型,空值返回空数组或空对象;
58
+ * - `update` 返回用于更新的字符串,入参非法时返回 `undefined`,便于调用方跳过该字段。
59
+ *
60
+ * @example
61
+ * const sa = new XDbValue.SimpleArray();
62
+ * sa.add(['a', 'b']);
63
+ * // 'a,b'
64
+ * sa.get('a,b');
65
+ * // ['a', 'b']
66
+ */
67
+ export declare class XDbValue {
68
+ /**
69
+ * 分隔符数组格式:`'a,b'` ↔ `['a', 'b']`。
70
+ *
71
+ * @template T 数组元素类型,仅支持 `string` 或 `number`。
72
+ */
73
+ static SimpleArray: {
74
+ new <T extends string | number = string>(options?: XDbValueSimpleArrayOptions): {
75
+ /** 元素之间的分隔符。 */
76
+ separator: string;
77
+ /** 存储值的统一前后缀,空字符串表示不包裹。 */
78
+ affix: string;
79
+ /** 是否将数组元素转换为数字。 */
80
+ number: boolean;
81
+ /**
82
+ * 将数组序列化为适合存储的字符串。
83
+ *
84
+ * - `value` 不是数组时,使用 `defaultValue` 兜底;
85
+ * - 过滤掉 `null`、`undefined` 与空字符串元素;
86
+ * - 结果非空且设置了 `affix` 时在首尾包裹前后缀;结果为空时返回空字符串(不包裹)。
87
+ *
88
+ * @param value 要序列化的数组。
89
+ * @param defaultValue `value` 非法时使用的兜底数组。
90
+ * @returns 序列化后的字符串。
91
+ *
92
+ * @example
93
+ * const sa = new XDbValue.SimpleArray();
94
+ * sa.add(['a', 'b']);
95
+ * // 'a,b'
96
+ */
97
+ add: (value?: T[], defaultValue?: T[]) => string;
98
+ /**
99
+ * 将存储字符串反序列化为数组。
100
+ *
101
+ * - 值为空时返回空数组;
102
+ * - 设置了 `affix` 时,仅当值同时以该前后缀开头和结尾才会剥离,否则按原始文本解析;
103
+ * - 按 `separator` 分割并过滤掉空字符串;
104
+ * - `number` 为 `true` 时,将每个元素转换为数字。
105
+ *
106
+ * @param value 存储字符串。
107
+ * @returns 反序列化后的数组。
108
+ *
109
+ * @example
110
+ * const sa = new XDbValue.SimpleArray();
111
+ * sa.get('a,b');
112
+ * // ['a', 'b']
113
+ */
114
+ get: (value?: string) => T[];
115
+ /**
116
+ * 将数组序列化为用于更新的字符串。
117
+ *
118
+ * 与 {@link add} 的区别在于:`value` 不是数组时返回 `undefined`,
119
+ * 便于调用方在更新数据库时跳过该字段;`value` 是数组但过滤后为空时返回空字符串。
120
+ *
121
+ * @param value 要序列化的数组。
122
+ * @returns 序列化后的字符串;`value` 不是数组时返回 `undefined`。
123
+ */
124
+ update: (value?: T[]) => string | undefined;
125
+ };
126
+ };
127
+ /**
128
+ * JSON 数组格式:`'[{"a":1}]'` ↔ `[{ a: 1 }]`。
129
+ *
130
+ * @template T 数组元素类型。
131
+ */
132
+ static JsonArray: {
133
+ new <T = string>(): {
134
+ /**
135
+ * 将数组序列化为 JSON 字符串。
136
+ *
137
+ * @param value 要序列化的数组。
138
+ * @param defaultValue `value` 非法时使用的兜底数组。
139
+ * @returns JSON 字符串。
140
+ */
141
+ add: (value?: T[], defaultValue?: T[]) => string;
142
+ /**
143
+ * 将 JSON 字符串反序列化为数组。
144
+ *
145
+ * - 值为空时返回空数组;
146
+ * - 值不是合法 JSON 时抛出 `SyntaxError`。
147
+ *
148
+ * @param value JSON 字符串。
149
+ * @returns 反序列化后的数组。
150
+ */
151
+ get: (value?: string) => T[];
152
+ /**
153
+ * 将数组序列化为用于更新的 JSON 字符串。
154
+ *
155
+ * @param value 要序列化的数组。
156
+ * @returns JSON 字符串;`value` 不是数组时返回 `undefined`。
157
+ */
158
+ update: (value?: T[]) => string | undefined;
159
+ };
160
+ };
161
+ /**
162
+ * JSON 对象格式:`'{"name":"x"}'` ↔ `{ name: 'x' }`。
163
+ *
164
+ * 相比 {@link XDbValue.JsonArray},额外支持 {@link XDbValueJsonObjectOptions.transform}
165
+ * 转换钩子,适合在读写时统一处理日期、默认值等字段。
166
+ *
167
+ * @template T 对象值的类型。
168
+ */
169
+ static JsonObject: {
170
+ new <T>(options?: XDbValueJsonObjectOptions<T>): {
171
+ /** 构造时传入的转换钩子配置。 */
172
+ options?: XDbValueJsonObjectOptions<T>;
173
+ /** 是否配置了 add 转换钩子。 */
174
+ hasTransformAdd: boolean;
175
+ /** 是否配置了 get 转换钩子。 */
176
+ hasTransformGet: boolean;
177
+ /** 是否配置了 update 转换钩子。 */
178
+ hasTransformUpdate: boolean;
179
+ /**
180
+ * 将对象序列化为 JSON 字符串。
181
+ *
182
+ * - 配置 `transform.add` 时,先对值做转换;
183
+ * - `value` 不是普通对象时,使用 `defaultValue` 兜底。
184
+ *
185
+ * @param value 要序列化的对象。
186
+ * @param defaultValue `value` 非法时使用的兜底对象。
187
+ * @returns JSON 字符串。
188
+ */
189
+ add: (value?: T, defaultValue?: T) => string;
190
+ /**
191
+ * 将 JSON 字符串反序列化为对象。
192
+ *
193
+ * - 值为空时返回空对象 `{}`;
194
+ * - 值不是合法 JSON 时抛出 `SyntaxError`;
195
+ * - 配置 `transform.get` 时对结果做转换,空值解析出的 `{}` 同样会经过该钩子。
196
+ *
197
+ * @param value JSON 字符串。
198
+ * @returns 反序列化后的对象。
199
+ */
200
+ get: (value?: string) => T;
201
+ /**
202
+ * 将对象序列化为用于更新的 JSON 字符串。
203
+ *
204
+ * - 配置 `transform.update` 时,先对值做转换;
205
+ * - `value` 不是普通对象时返回 `undefined`。
206
+ *
207
+ * @param value 要序列化的对象。
208
+ * @returns JSON 字符串;`value` 不是普通对象时返回 `undefined`。
209
+ */
210
+ update: (value?: T) => string | undefined;
211
+ };
212
+ };
213
+ }
@@ -0,0 +1,208 @@
1
+ import { isPlainObject } from "vtils";
2
+ // src/db_value.ts
3
+ /**
4
+ * 数据库字段值的序列化与反序列化工具。
5
+ *
6
+ * 适用于把数组或对象以字符串形式存储到数据库字段(如 VARCHAR / TEXT)的场景,
7
+ * 内置三种存储格式:
8
+ * - {@link XDbValue.SimpleArray}:以分隔符连接的数组,支持前后缀与数字模式;
9
+ * - {@link XDbValue.JsonArray}:JSON 数组;
10
+ * - {@link XDbValue.JsonObject}:JSON 对象,支持读写转换钩子。
11
+ *
12
+ * 每种格式都提供 `add`(写入)、`get`(读取)、`update`(更新)三个方法:
13
+ * - `add` 返回序列化后的字符串,入参非法时使用默认值兜底;
14
+ * - `get` 将存储字符串反序列化为目标类型,空值返回空数组或空对象;
15
+ * - `update` 返回用于更新的字符串,入参非法时返回 `undefined`,便于调用方跳过该字段。
16
+ *
17
+ * @example
18
+ * const sa = new XDbValue.SimpleArray();
19
+ * sa.add(['a', 'b']);
20
+ * // 'a,b'
21
+ * sa.get('a,b');
22
+ * // ['a', 'b']
23
+ */
24
+ var XDbValue = class {
25
+ /**
26
+ * 分隔符数组格式:`'a,b'` ↔ `['a', 'b']`。
27
+ *
28
+ * @template T 数组元素类型,仅支持 `string` 或 `number`。
29
+ */
30
+ static SimpleArray = class {
31
+ /** 元素之间的分隔符。 */
32
+ separator;
33
+ /** 存储值的统一前后缀,空字符串表示不包裹。 */
34
+ affix;
35
+ /** 是否将数组元素转换为数字。 */
36
+ number;
37
+ /**
38
+ * @param options 配置项,详见 {@link XDbValueSimpleArrayOptions}。
39
+ */
40
+ constructor(options) {
41
+ this.separator = options?.separator || ",";
42
+ this.affix = options?.affix || "";
43
+ this.number = !!options?.number;
44
+ }
45
+ /**
46
+ * 将数组序列化为适合存储的字符串。
47
+ *
48
+ * - `value` 不是数组时,使用 `defaultValue` 兜底;
49
+ * - 过滤掉 `null`、`undefined` 与空字符串元素;
50
+ * - 结果非空且设置了 `affix` 时在首尾包裹前后缀;结果为空时返回空字符串(不包裹)。
51
+ *
52
+ * @param value 要序列化的数组。
53
+ * @param defaultValue `value` 非法时使用的兜底数组。
54
+ * @returns 序列化后的字符串。
55
+ *
56
+ * @example
57
+ * const sa = new XDbValue.SimpleArray();
58
+ * sa.add(['a', 'b']);
59
+ * // 'a,b'
60
+ */
61
+ add = (value, defaultValue = []) => {
62
+ const text = (!value || !Array.isArray(value) ? defaultValue : value.filter((v) => v != null && v !== "")).join(this.separator);
63
+ return !text || !this.affix ? text : `${this.affix}${text}${this.affix}`;
64
+ };
65
+ /**
66
+ * 将存储字符串反序列化为数组。
67
+ *
68
+ * - 值为空时返回空数组;
69
+ * - 设置了 `affix` 时,仅当值同时以该前后缀开头和结尾才会剥离,否则按原始文本解析;
70
+ * - 按 `separator` 分割并过滤掉空字符串;
71
+ * - `number` 为 `true` 时,将每个元素转换为数字。
72
+ *
73
+ * @param value 存储字符串。
74
+ * @returns 反序列化后的数组。
75
+ *
76
+ * @example
77
+ * const sa = new XDbValue.SimpleArray();
78
+ * sa.get('a,b');
79
+ * // ['a', 'b']
80
+ */
81
+ get = (value) => {
82
+ if (!value) return [];
83
+ const res = (this.affix && value.startsWith(this.affix) && value.endsWith(this.affix) ? value.slice(this.affix.length, -this.affix.length) : value).split(this.separator).filter((v) => v !== "");
84
+ return this.number ? res.map(Number) : res;
85
+ };
86
+ /**
87
+ * 将数组序列化为用于更新的字符串。
88
+ *
89
+ * 与 {@link add} 的区别在于:`value` 不是数组时返回 `undefined`,
90
+ * 便于调用方在更新数据库时跳过该字段;`value` 是数组但过滤后为空时返回空字符串。
91
+ *
92
+ * @param value 要序列化的数组。
93
+ * @returns 序列化后的字符串;`value` 不是数组时返回 `undefined`。
94
+ */
95
+ update = (value) => {
96
+ const text = !value || !Array.isArray(value) ? void 0 : value.filter((v) => v != null && v !== "").join(this.separator);
97
+ return !text || !this.affix ? text : `${this.affix}${text}${this.affix}`;
98
+ };
99
+ };
100
+ /**
101
+ * JSON 数组格式:`'[{"a":1}]'` ↔ `[{ a: 1 }]`。
102
+ *
103
+ * @template T 数组元素类型。
104
+ */
105
+ static JsonArray = class {
106
+ /**
107
+ * 将数组序列化为 JSON 字符串。
108
+ *
109
+ * @param value 要序列化的数组。
110
+ * @param defaultValue `value` 非法时使用的兜底数组。
111
+ * @returns JSON 字符串。
112
+ */
113
+ add = (value, defaultValue = []) => {
114
+ return JSON.stringify(!value || !Array.isArray(value) ? defaultValue : value);
115
+ };
116
+ /**
117
+ * 将 JSON 字符串反序列化为数组。
118
+ *
119
+ * - 值为空时返回空数组;
120
+ * - 值不是合法 JSON 时抛出 `SyntaxError`。
121
+ *
122
+ * @param value JSON 字符串。
123
+ * @returns 反序列化后的数组。
124
+ */
125
+ get = (value) => {
126
+ return !value ? [] : JSON.parse(value);
127
+ };
128
+ /**
129
+ * 将数组序列化为用于更新的 JSON 字符串。
130
+ *
131
+ * @param value 要序列化的数组。
132
+ * @returns JSON 字符串;`value` 不是数组时返回 `undefined`。
133
+ */
134
+ update = (value) => {
135
+ return !value || !Array.isArray(value) ? void 0 : JSON.stringify(value);
136
+ };
137
+ };
138
+ /**
139
+ * JSON 对象格式:`'{"name":"x"}'` ↔ `{ name: 'x' }`。
140
+ *
141
+ * 相比 {@link XDbValue.JsonArray},额外支持 {@link XDbValueJsonObjectOptions.transform}
142
+ * 转换钩子,适合在读写时统一处理日期、默认值等字段。
143
+ *
144
+ * @template T 对象值的类型。
145
+ */
146
+ static JsonObject = class {
147
+ /** 构造时传入的转换钩子配置。 */
148
+ options;
149
+ /** 是否配置了 add 转换钩子。 */
150
+ hasTransformAdd = false;
151
+ /** 是否配置了 get 转换钩子。 */
152
+ hasTransformGet = false;
153
+ /** 是否配置了 update 转换钩子。 */
154
+ hasTransformUpdate = false;
155
+ /**
156
+ * @param options 转换钩子配置,详见 {@link XDbValueJsonObjectOptions}。
157
+ */
158
+ constructor(options) {
159
+ this.options = options;
160
+ this.hasTransformAdd = !!options?.transform?.add;
161
+ this.hasTransformGet = !!options?.transform?.get;
162
+ this.hasTransformUpdate = !!options?.transform?.update;
163
+ }
164
+ /**
165
+ * 将对象序列化为 JSON 字符串。
166
+ *
167
+ * - 配置 `transform.add` 时,先对值做转换;
168
+ * - `value` 不是普通对象时,使用 `defaultValue` 兜底。
169
+ *
170
+ * @param value 要序列化的对象。
171
+ * @param defaultValue `value` 非法时使用的兜底对象。
172
+ * @returns JSON 字符串。
173
+ */
174
+ add = (value, defaultValue = {}) => {
175
+ if (value && this.hasTransformAdd) value = this.options.transform.add(value);
176
+ return JSON.stringify(!value || !isPlainObject(value) ? defaultValue : value);
177
+ };
178
+ /**
179
+ * 将 JSON 字符串反序列化为对象。
180
+ *
181
+ * - 值为空时返回空对象 `{}`;
182
+ * - 值不是合法 JSON 时抛出 `SyntaxError`;
183
+ * - 配置 `transform.get` 时对结果做转换,空值解析出的 `{}` 同样会经过该钩子。
184
+ *
185
+ * @param value JSON 字符串。
186
+ * @returns 反序列化后的对象。
187
+ */
188
+ get = (value) => {
189
+ let result = !value ? {} : JSON.parse(value);
190
+ if (result && this.hasTransformGet) result = this.options.transform.get(result);
191
+ return result;
192
+ };
193
+ /**
194
+ * 将对象序列化为用于更新的 JSON 字符串。
195
+ *
196
+ * - 配置 `transform.update` 时,先对值做转换;
197
+ * - `value` 不是普通对象时返回 `undefined`。
198
+ *
199
+ * @param value 要序列化的对象。
200
+ * @returns JSON 字符串;`value` 不是普通对象时返回 `undefined`。
201
+ */
202
+ update = (value) => {
203
+ if (value && this.hasTransformUpdate) value = this.options.transform.update(value);
204
+ return !value || !isPlainObject(value) ? void 0 : JSON.stringify(value);
205
+ };
206
+ };
207
+ };
208
+ export { XDbValue };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `XDispose` 的构造配置。
3
+ */
4
+ export interface XDisposeOptions {
5
+ /**
6
+ * 是否在进程退出时自动调用 `dispose()` 进行资源清理。
7
+ *
8
+ * - 默认 `false`:不注册退出钩子,仅支持手动调用 `dispose()` 或通过
9
+ * `await using` 触发 `Symbol.asyncDispose`;
10
+ * - 为 `true` 时:通过 `asyncExitHook` 注册异步退出钩子,进程因
11
+ * `SIGINT`、`SIGTERM`、`beforeExit` 等场景退出时自动执行清理,
12
+ * 清理总时长最多等待 3000ms,超时后由退出钩子强制结束进程。
13
+ */
14
+ disposeOnExit?: boolean;
15
+ }
16
+ /**
17
+ * 资源释放管理器。
18
+ *
19
+ * 用于集中管理一组同步/异步清理函数,并在退出时统一、可靠地释放,
20
+ * 适用于数据库连接、文件句柄、定时器、子进程等资源的生命周期管理。
21
+ *
22
+ * 主要行为:
23
+ * - 通过 `add()` 按注册顺序追加清理函数;
24
+ * - `dispose()` 幂等:首次调用按注册顺序依次执行全部清理函数,执行后
25
+ * 清空列表,之后再次调用直接返回,不会重复执行;
26
+ * - 清理函数逐个执行,单个抛错不会中断后续清理,全部执行完后将错误
27
+ * 以 `AggregateError` 汇总抛出(无错误则不抛);
28
+ * - 实现 `Symbol.asyncDispose`,可直接配合 `await using` 使用;
29
+ * - 开启 `disposeOnExit` 时自动注册进程退出钩子,退出时调用 `dispose()`。
30
+ *
31
+ * @example
32
+ * const dispose = new XDispose({ disposeOnExit: true });
33
+ *
34
+ * dispose.add(() => db.close());
35
+ * dispose.add(async () => {
36
+ * await fs.rm(tmpDir, { recursive: true, force: true });
37
+ * });
38
+ *
39
+ * // 进程退出时自动清理,也可手动触发
40
+ * await dispose.dispose();
41
+ */
42
+ export declare class XDispose {
43
+ private options?;
44
+ /** 已注册的清理函数列表,按注册顺序存放。 */
45
+ private disposes;
46
+ /** 是否已完成清理,保证 `dispose()` 幂等。 */
47
+ private disposed;
48
+ /**
49
+ * 创建资源释放管理器。
50
+ *
51
+ * @param options 构造配置;`disposeOnExit` 为 `true` 时立即注册
52
+ * 进程退出钩子(见 {@link XDisposeOptions})。
53
+ */
54
+ constructor(options?: XDisposeOptions | undefined);
55
+ /**
56
+ * 注册一个清理函数。
57
+ *
58
+ * 清理函数支持同步或异步返回值,`dispose()` 执行时会 `await` 其结果。
59
+ * 已调用过 `dispose()` 后再注册会抛错,因为此时资源已释放完毕。
60
+ *
61
+ * @param fn 清理函数,按注册顺序在 `dispose()` 中依次执行。
62
+ *
63
+ * @example
64
+ * dispose.add(() => server.close());
65
+ */
66
+ add(fn: () => any): void;
67
+ /**
68
+ * 释放所有已注册的资源。
69
+ *
70
+ * 幂等:首次调用按注册顺序依次执行全部清理函数并清空列表,之后的
71
+ * 调用直接返回。单个清理函数抛错不会中断后续执行,全部执行完后若
72
+ * 存在错误则以 `AggregateError` 汇总抛出。
73
+ *
74
+ * @returns 全部清理函数成功执行后 resolve;存在失败时 reject,
75
+ * 错误为聚合了全部失败项的 `AggregateError`。
76
+ */
77
+ dispose(): Promise<void>;
78
+ /**
79
+ * `await using` 支持。
80
+ *
81
+ * 等价于调用 `dispose()`,用于在 `using` 语句作用域结束时自动释放。
82
+ */
83
+ [Symbol.asyncDispose](): Promise<void>;
84
+ /**
85
+ * 进程级共享的释放管理器实例。
86
+ *
87
+ * 该实例已开启 `disposeOnExit`,进程退出时会自动执行注册到其上的全部
88
+ * 清理函数。适用于不希望手动创建并持有 `XDispose` 实例的场景,直接
89
+ * 通过 `XDispose.exit.add(...)` 注册清理函数即可,进程退出时统一释放。
90
+ *
91
+ * 注意:作为进程级单例,该实例被 `dispose()` 后无法再注册清理函数,
92
+ * 通常无需手动调用,进程退出时由退出钩子自动处理。
93
+ *
94
+ * @example
95
+ * XDispose.exit.add(() => db.close());
96
+ */
97
+ static exit: XDispose;
98
+ }