@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,158 @@
1
+ /**
2
+ * 常用加解密与摘要工具。
3
+ *
4
+ * 提供 Base64 / Base64URL 编解码、常见哈希摘要(MD5、SHA-1、
5
+ * SHA-256、SHA-512),以及 AES-128-ECB 对称加解密能力,所有方法
6
+ * 均为静态方法,可直接通过 `XCrypto.xxx()` 调用。
7
+ *
8
+ * @example
9
+ * XCrypto.md5('123456') // 'e10adc3949ba59abbe56e057f20f883e'
10
+ * XCrypto.base64UrlEncode('{"userId":"doit"}') // 'eyJ1c2VySWQiOiJkb2l0In0'
11
+ *
12
+ * @remarks
13
+ * - MD5 与 SHA-1 已被视为不安全算法,仅建议用于校验等非安全场景;
14
+ * - AES 采用 ECB 模式,相同明文加密结果固定,不适用于需要语义安全的
15
+ * 场景(建议改用 CBC / GCM 等带随机 IV 的模式);
16
+ * - 加解密密钥会自动规范化为 16 字节(不足补零、超出截断)。
17
+ */
18
+ export declare class XCrypto {
19
+ /**
20
+ * 将文本编码为 Base64 字符串。
21
+ *
22
+ * @param text 需要编码的原始文本。
23
+ * @returns Base64 编码后的字符串。
24
+ *
25
+ * @example
26
+ * XCrypto.base64Encode('{"userId":"doit"}') // 'eyJ1c2VySWQiOiJkb2l0In0='
27
+ * XCrypto.base64Encode('🐲') // '8J+Qsg=='
28
+ */
29
+ static base64Encode(text: string): string;
30
+ /**
31
+ * 将 Base64 字符串解码为 UTF-8 文本。
32
+ *
33
+ * @param text Base64 编码的字符串。
34
+ * @returns 解码后的 UTF-8 文本。
35
+ *
36
+ * @example
37
+ * XCrypto.base64Decode('eyJ1c2VySWQiOiJkb2l0In0=') // '{"userId":"doit"}'
38
+ * XCrypto.base64Decode('8J+Qsg==') // '🐲'
39
+ */
40
+ static base64Decode(text: string): string;
41
+ /**
42
+ * 将文本编码为 URL 安全的 Base64(Base64URL)字符串。
43
+ *
44
+ * 与标准 Base64 的区别:`+` 替换为 `-`、`/` 替换为 `_`,并去除
45
+ * 末尾的 `=` 填充,使其可直接用于 URL 查询参数等场景。
46
+ *
47
+ * @param text 需要编码的原始文本。
48
+ * @returns Base64URL 编码后的字符串。
49
+ *
50
+ * @example
51
+ * XCrypto.base64UrlEncode('{"userId":"doit"}') // 'eyJ1c2VySWQiOiJkb2l0In0'
52
+ * XCrypto.base64UrlEncode('🐲') // '8J-Qsg'
53
+ */
54
+ static base64UrlEncode(text: string): string;
55
+ /**
56
+ * 将 Base64URL 字符串解码为 UTF-8 文本。
57
+ *
58
+ * @param text Base64URL 编码的字符串。
59
+ * @returns 解码后的 UTF-8 文本。
60
+ *
61
+ * @example
62
+ * XCrypto.base64UrlDecode('eyJ1c2VySWQiOiJkb2l0In0') // '{"userId":"doit"}'
63
+ * XCrypto.base64UrlDecode('8J-Qsg') // '🐲'
64
+ */
65
+ static base64UrlDecode(text: string): string;
66
+ /**
67
+ * 计算文本的 MD5 摘要。
68
+ *
69
+ * @param text 待摘要的文本。
70
+ * @returns 32 位小写十六进制字符串。
71
+ *
72
+ * @example
73
+ * XCrypto.md5('123456') // 'e10adc3949ba59abbe56e057f20f883e'
74
+ */
75
+ static md5(text: string): string;
76
+ /**
77
+ * 计算文本的 SHA-1 摘要。
78
+ *
79
+ * @param text 待摘要的文本。
80
+ * @returns 40 位小写十六进制字符串。
81
+ *
82
+ * @example
83
+ * XCrypto.sha1('123456') // '7c4a8d09ca3762af61e59520943dc26494f8941b'
84
+ */
85
+ static sha1(text: string): string;
86
+ /**
87
+ * 计算文本的 SHA-256 摘要。
88
+ *
89
+ * @param text 待摘要的文本。
90
+ * @returns 64 位小写十六进制字符串。
91
+ *
92
+ * @example
93
+ * XCrypto.sha256('123456') // '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'
94
+ */
95
+ static sha256(text: string): string;
96
+ /**
97
+ * 计算文本的 SHA-512 摘要。
98
+ *
99
+ * @param text 待摘要的文本。
100
+ * @returns 128 位小写十六进制字符串。
101
+ *
102
+ * @example
103
+ * XCrypto.sha512('123456') // 'ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413'
104
+ */
105
+ static sha512(text: string): string;
106
+ /**
107
+ * 使用 AES-128-ECB(PKCS7 填充)对文本进行加密。
108
+ *
109
+ * @param text 需要加密的文本。
110
+ * @param key 加密密钥,会自动规范化为 16 字节(不足补零、超出截断)。
111
+ * @returns Base64 编码的密文。
112
+ *
113
+ * @example
114
+ * XCrypto.aesEncrypt('{"userId":"doit"}', 'hj7Wer8V3epukzV7')
115
+ * // 'fCH/NJMbVFVDjL73irkG40HP19muCpVcrrUrx22GuEc='
116
+ */
117
+ static aesEncrypt(text: string, key: string): string;
118
+ /**
119
+ * 使用 AES-128-ECB(PKCS7 填充)对文本进行解密。
120
+ *
121
+ * @param text Base64 编码的密文。
122
+ * @param key 解密密钥,需与加密时使用的密钥一致,会自动规范化为 16 字节。
123
+ * @returns 解密后的 UTF-8 文本。
124
+ *
125
+ * @example
126
+ * XCrypto.aesDecrypt('fCH/NJMbVFVDjL73irkG40HP19muCpVcrrUrx22GuEc=', 'hj7Wer8V3epukzV7')
127
+ * // '{"userId":"doit"}'
128
+ */
129
+ static aesDecrypt(text: string, key: string): string;
130
+ /**
131
+ * 将对象序列化为 JSON 后使用 AES 加密。
132
+ *
133
+ * 适用于传输或存储敏感结构化数据前的加密处理。
134
+ *
135
+ * @param payload 需要加密的任意数据,内部会先 `JSON.stringify` 序列化。
136
+ * @param key 加密密钥,会自动规范化为 16 字节(不足补零、超出截断)。
137
+ * @returns Base64 编码的密文。
138
+ *
139
+ * @example
140
+ * XCrypto.aesEncryptJson({ userId: 'doit' }, 'hj7Wer8V3epukzV7')
141
+ * // 'fCH/NJMbVFVDjL73irkG40HP19muCpVcrrUrx22GuEc='
142
+ */
143
+ static aesEncryptJson(payload: unknown, key: string): string;
144
+ /**
145
+ * 解密文本并将结果解析为 JSON 对象。
146
+ *
147
+ * @param text Base64 编码的密文。
148
+ * @param key 解密密钥,需与加密时使用的密钥一致。
149
+ * @returns 解密并解析后的 JSON 对象,类型由泛型 `T` 指定。
150
+ *
151
+ * @example
152
+ * XCrypto.aesDecryptJson<{ userId: string }>(
153
+ * 'fCH/NJMbVFVDjL73irkG40HP19muCpVcrrUrx22GuEc=',
154
+ * 'hj7Wer8V3epukzV7',
155
+ * ) // { userId: 'doit' }
156
+ */
157
+ static aesDecryptJson<T>(text: string, key: string): T;
158
+ }
package/lib/crypto.mjs ADDED
@@ -0,0 +1,213 @@
1
+ import crypto from "node:crypto";
2
+ // src/crypto.ts
3
+ /**
4
+ * 将任意长度的密钥规范化为 AES-128 所需的 16 字节密钥。
5
+ *
6
+ * 规则:密钥先按 UTF-8 编码为字节序列;不足 16 字节时在右侧补 0,
7
+ * 超过 16 字节时截取前 16 字节。
8
+ *
9
+ * 对于纯 ASCII 密钥,结果与 `key.substring(0, 16)` 完全一致;
10
+ * 同时修复了原实现按字符截取导致多字节字符密钥字节数超长而抛错的问题。
11
+ */
12
+ function resolveAesKey(key) {
13
+ return Buffer.concat([Buffer.from(key, "utf8"), Buffer.alloc(16)]).subarray(0, 16);
14
+ }
15
+ /**
16
+ * 计算文本的十六进制摘要。
17
+ *
18
+ * @param text 待摘要的文本。
19
+ * @param algorithm 摘要算法名称(如 md5、sha1、sha256、sha512)。
20
+ * @returns 小写十六进制形式的摘要字符串。
21
+ */
22
+ function digest(text, algorithm) {
23
+ return crypto.createHash(algorithm).update(text, "utf8").digest("hex");
24
+ }
25
+ /**
26
+ * 常用加解密与摘要工具。
27
+ *
28
+ * 提供 Base64 / Base64URL 编解码、常见哈希摘要(MD5、SHA-1、
29
+ * SHA-256、SHA-512),以及 AES-128-ECB 对称加解密能力,所有方法
30
+ * 均为静态方法,可直接通过 `XCrypto.xxx()` 调用。
31
+ *
32
+ * @example
33
+ * XCrypto.md5('123456') // 'e10adc3949ba59abbe56e057f20f883e'
34
+ * XCrypto.base64UrlEncode('{"userId":"doit"}') // 'eyJ1c2VySWQiOiJkb2l0In0'
35
+ *
36
+ * @remarks
37
+ * - MD5 与 SHA-1 已被视为不安全算法,仅建议用于校验等非安全场景;
38
+ * - AES 采用 ECB 模式,相同明文加密结果固定,不适用于需要语义安全的
39
+ * 场景(建议改用 CBC / GCM 等带随机 IV 的模式);
40
+ * - 加解密密钥会自动规范化为 16 字节(不足补零、超出截断)。
41
+ */
42
+ var XCrypto = class XCrypto {
43
+ /**
44
+ * 将文本编码为 Base64 字符串。
45
+ *
46
+ * @param text 需要编码的原始文本。
47
+ * @returns Base64 编码后的字符串。
48
+ *
49
+ * @example
50
+ * XCrypto.base64Encode('{"userId":"doit"}') // 'eyJ1c2VySWQiOiJkb2l0In0='
51
+ * XCrypto.base64Encode('🐲') // '8J+Qsg=='
52
+ */
53
+ static base64Encode(text) {
54
+ return Buffer.from(text, "utf8").toString("base64");
55
+ }
56
+ /**
57
+ * 将 Base64 字符串解码为 UTF-8 文本。
58
+ *
59
+ * @param text Base64 编码的字符串。
60
+ * @returns 解码后的 UTF-8 文本。
61
+ *
62
+ * @example
63
+ * XCrypto.base64Decode('eyJ1c2VySWQiOiJkb2l0In0=') // '{"userId":"doit"}'
64
+ * XCrypto.base64Decode('8J+Qsg==') // '🐲'
65
+ */
66
+ static base64Decode(text) {
67
+ return Buffer.from(text, "base64").toString("utf8");
68
+ }
69
+ /**
70
+ * 将文本编码为 URL 安全的 Base64(Base64URL)字符串。
71
+ *
72
+ * 与标准 Base64 的区别:`+` 替换为 `-`、`/` 替换为 `_`,并去除
73
+ * 末尾的 `=` 填充,使其可直接用于 URL 查询参数等场景。
74
+ *
75
+ * @param text 需要编码的原始文本。
76
+ * @returns Base64URL 编码后的字符串。
77
+ *
78
+ * @example
79
+ * XCrypto.base64UrlEncode('{"userId":"doit"}') // 'eyJ1c2VySWQiOiJkb2l0In0'
80
+ * XCrypto.base64UrlEncode('🐲') // '8J-Qsg'
81
+ */
82
+ static base64UrlEncode(text) {
83
+ return Buffer.from(text, "utf8").toString("base64url");
84
+ }
85
+ /**
86
+ * 将 Base64URL 字符串解码为 UTF-8 文本。
87
+ *
88
+ * @param text Base64URL 编码的字符串。
89
+ * @returns 解码后的 UTF-8 文本。
90
+ *
91
+ * @example
92
+ * XCrypto.base64UrlDecode('eyJ1c2VySWQiOiJkb2l0In0') // '{"userId":"doit"}'
93
+ * XCrypto.base64UrlDecode('8J-Qsg') // '🐲'
94
+ */
95
+ static base64UrlDecode(text) {
96
+ return Buffer.from(text, "base64url").toString("utf8");
97
+ }
98
+ /**
99
+ * 计算文本的 MD5 摘要。
100
+ *
101
+ * @param text 待摘要的文本。
102
+ * @returns 32 位小写十六进制字符串。
103
+ *
104
+ * @example
105
+ * XCrypto.md5('123456') // 'e10adc3949ba59abbe56e057f20f883e'
106
+ */
107
+ static md5(text) {
108
+ return digest(text, "md5");
109
+ }
110
+ /**
111
+ * 计算文本的 SHA-1 摘要。
112
+ *
113
+ * @param text 待摘要的文本。
114
+ * @returns 40 位小写十六进制字符串。
115
+ *
116
+ * @example
117
+ * XCrypto.sha1('123456') // '7c4a8d09ca3762af61e59520943dc26494f8941b'
118
+ */
119
+ static sha1(text) {
120
+ return digest(text, "sha1");
121
+ }
122
+ /**
123
+ * 计算文本的 SHA-256 摘要。
124
+ *
125
+ * @param text 待摘要的文本。
126
+ * @returns 64 位小写十六进制字符串。
127
+ *
128
+ * @example
129
+ * XCrypto.sha256('123456') // '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'
130
+ */
131
+ static sha256(text) {
132
+ return digest(text, "sha256");
133
+ }
134
+ /**
135
+ * 计算文本的 SHA-512 摘要。
136
+ *
137
+ * @param text 待摘要的文本。
138
+ * @returns 128 位小写十六进制字符串。
139
+ *
140
+ * @example
141
+ * XCrypto.sha512('123456') // 'ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413'
142
+ */
143
+ static sha512(text) {
144
+ return digest(text, "sha512");
145
+ }
146
+ /**
147
+ * 使用 AES-128-ECB(PKCS7 填充)对文本进行加密。
148
+ *
149
+ * @param text 需要加密的文本。
150
+ * @param key 加密密钥,会自动规范化为 16 字节(不足补零、超出截断)。
151
+ * @returns Base64 编码的密文。
152
+ *
153
+ * @example
154
+ * XCrypto.aesEncrypt('{"userId":"doit"}', 'hj7Wer8V3epukzV7')
155
+ * // 'fCH/NJMbVFVDjL73irkG40HP19muCpVcrrUrx22GuEc='
156
+ */
157
+ static aesEncrypt(text, key) {
158
+ const cipher = crypto.createCipheriv("aes-128-ecb", resolveAesKey(key), null);
159
+ let encrypted = cipher.update(text, "utf8", "base64");
160
+ encrypted += cipher.final("base64");
161
+ return encrypted;
162
+ }
163
+ /**
164
+ * 使用 AES-128-ECB(PKCS7 填充)对文本进行解密。
165
+ *
166
+ * @param text Base64 编码的密文。
167
+ * @param key 解密密钥,需与加密时使用的密钥一致,会自动规范化为 16 字节。
168
+ * @returns 解密后的 UTF-8 文本。
169
+ *
170
+ * @example
171
+ * XCrypto.aesDecrypt('fCH/NJMbVFVDjL73irkG40HP19muCpVcrrUrx22GuEc=', 'hj7Wer8V3epukzV7')
172
+ * // '{"userId":"doit"}'
173
+ */
174
+ static aesDecrypt(text, key) {
175
+ const decipher = crypto.createDecipheriv("aes-128-ecb", resolveAesKey(key), null);
176
+ let decrypted = decipher.update(text, "base64", "utf8");
177
+ decrypted += decipher.final("utf8");
178
+ return decrypted;
179
+ }
180
+ /**
181
+ * 将对象序列化为 JSON 后使用 AES 加密。
182
+ *
183
+ * 适用于传输或存储敏感结构化数据前的加密处理。
184
+ *
185
+ * @param payload 需要加密的任意数据,内部会先 `JSON.stringify` 序列化。
186
+ * @param key 加密密钥,会自动规范化为 16 字节(不足补零、超出截断)。
187
+ * @returns Base64 编码的密文。
188
+ *
189
+ * @example
190
+ * XCrypto.aesEncryptJson({ userId: 'doit' }, 'hj7Wer8V3epukzV7')
191
+ * // 'fCH/NJMbVFVDjL73irkG40HP19muCpVcrrUrx22GuEc='
192
+ */
193
+ static aesEncryptJson(payload, key) {
194
+ return XCrypto.aesEncrypt(JSON.stringify(payload), key);
195
+ }
196
+ /**
197
+ * 解密文本并将结果解析为 JSON 对象。
198
+ *
199
+ * @param text Base64 编码的密文。
200
+ * @param key 解密密钥,需与加密时使用的密钥一致。
201
+ * @returns 解密并解析后的 JSON 对象,类型由泛型 `T` 指定。
202
+ *
203
+ * @example
204
+ * XCrypto.aesDecryptJson<{ userId: string }>(
205
+ * 'fCH/NJMbVFVDjL73irkG40HP19muCpVcrrUrx22GuEc=',
206
+ * 'hj7Wer8V3epukzV7',
207
+ * ) // { userId: 'doit' }
208
+ */
209
+ static aesDecryptJson(text, key) {
210
+ return JSON.parse(XCrypto.aesDecrypt(text, key));
211
+ }
212
+ };
213
+ export { XCrypto };
package/lib/csv.d.mts ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * CSV 解析/序列化的配置项。
3
+ *
4
+ * 这些字段用于在 CSV 与对象之间做更强的类型转换:
5
+ * - `arrayFieldNames`:按分隔符拆分/组装数组字段;
6
+ * - `objectFieldNames`:将 JSON 字符串恢复为对象,或写出为字符串;
7
+ * - `booleanFieldNames`:将 `1/0` 这类值恢复为布尔值;
8
+ * - `dateFieldNames`:将日期字符串转换为 `Date` 对象,写出时按指定格式格式化。
9
+ */
10
+ export interface XCsvParseOptions {
11
+ /** 需要按 `arrayDelimiter` 拆分为数组的字段名列表。 */
12
+ arrayFieldNames?: string[];
13
+ /** 数组字段的分隔符;仅在 `arrayFieldNames` 生效时使用。 */
14
+ arrayDelimiter?: string;
15
+ /** 需要 JSON 解析为对象的字段名列表。 */
16
+ objectFieldNames?: string[];
17
+ /** 需要转换为布尔值的字段名列表;CSV 中常见 `1/0` 形式。 */
18
+ booleanFieldNames?: string[];
19
+ /** 需要转换为 `Date` 的字段名列表。 */
20
+ dateFieldNames?: string[];
21
+ }
22
+ /**
23
+ * CSV 序列化配置与解析配置相同,因此直接复用同一组字段定义。
24
+ */
25
+ export interface XCsvStringifyOptions extends XCsvParseOptions {}
26
+ /**
27
+ * CSV 数据的通用读写工具。
28
+ *
29
+ * 设计目标:
30
+ * - 让对象字段在 CSV 与 JavaScript/TypeScript 之间可互转;
31
+ * - 在常见场景中自动处理数组、对象、布尔值和日期;
32
+ * - 提供与文件系统统一的 `read` / `write` 能力,方便配置和报表场景使用。
33
+ */
34
+ export declare class XCsv {
35
+ /**
36
+ * 解析 CSV 文本为对象数组。
37
+ *
38
+ * 当启用 `arrayFieldNames` / `objectFieldNames` / `booleanFieldNames` /
39
+ * `dateFieldNames` 这些选项时,会对相应字段执行类型转换;否则直接返回
40
+ * 原始解析结果。
41
+ */
42
+ static parse<T>(value: string, options?: XCsvParseOptions): Promise<T[]>;
43
+ /**
44
+ * 将对象数组序列化为 CSV 文本。
45
+ *
46
+ * 对于数组、对象、布尔和日期字段,会在输出前做统一转换:
47
+ * - 数组字段以 `arrayDelimiter` 拼接;
48
+ * - 对象字段使用 `JSON.stringify`;
49
+ * - 布尔字段输出 `1/0`;
50
+ * - 日期字段按 `yyyy-mm-dd hh:ii:ss` 输出。
51
+ */
52
+ static stringify(value: any[], options?: XCsvStringifyOptions): Promise<string>;
53
+ /**
54
+ * 读取 CSV 文件并立即解析为对象数组。
55
+ *
56
+ * 传入的 `options` 会应用到 `parse` 中,因此开发者可以用同一套规则在
57
+ * 文件读取和内存对象转换两种场景中保持行为一致。
58
+ */
59
+ static read<T>(file: string, options?: XCsvParseOptions): Promise<T[]>;
60
+ /**
61
+ * 将对象数组写入 CSV 文件。
62
+ *
63
+ * 这里会先走 `stringify`,再将结果写到目标文件。调用方可传入相同的
64
+ * `XCsvStringifyOptions` 以控制数组、对象、布尔和日期字段输出格式。
65
+ */
66
+ static write(file: string, value: any, options?: XCsvStringifyOptions): Promise<void>;
67
+ }
package/lib/csv.mjs ADDED
@@ -0,0 +1,76 @@
1
+ import { mapValues } from "vtils";
2
+ import * as fs from "node:fs";
3
+ import * as csv from "csv";
4
+ import { formatDate } from "vtils/date";
5
+ // src/csv.ts
6
+ /**
7
+ * CSV 数据的通用读写工具。
8
+ *
9
+ * 设计目标:
10
+ * - 让对象字段在 CSV 与 JavaScript/TypeScript 之间可互转;
11
+ * - 在常见场景中自动处理数组、对象、布尔值和日期;
12
+ * - 提供与文件系统统一的 `read` / `write` 能力,方便配置和报表场景使用。
13
+ */
14
+ var XCsv = class XCsv {
15
+ /**
16
+ * 解析 CSV 文本为对象数组。
17
+ *
18
+ * 当启用 `arrayFieldNames` / `objectFieldNames` / `booleanFieldNames` /
19
+ * `dateFieldNames` 这些选项时,会对相应字段执行类型转换;否则直接返回
20
+ * 原始解析结果。
21
+ */
22
+ static async parse(value, options = {}) {
23
+ const res = [];
24
+ for await (const item of csv.parse(value, { columns: true })) res.push(options.objectFieldNames?.length || options.booleanFieldNames?.length || options.dateFieldNames?.length || options.arrayFieldNames?.length && options.arrayDelimiter ? mapValues(item, (v, k) => {
25
+ if (options.arrayFieldNames.includes(k)) return v.split(options.arrayDelimiter).filter(Boolean);
26
+ if (options.objectFieldNames?.includes(k)) return v ? JSON.parse(v) : {};
27
+ if (options.booleanFieldNames?.includes(k)) {
28
+ if (v === "" || v == null) return;
29
+ return v === true || v === "1" || v === 1 || v === "true";
30
+ }
31
+ if (options.dateFieldNames?.includes(k)) return v ? new Date(v) : void 0;
32
+ return v;
33
+ }) : item);
34
+ return res;
35
+ }
36
+ /**
37
+ * 将对象数组序列化为 CSV 文本。
38
+ *
39
+ * 对于数组、对象、布尔和日期字段,会在输出前做统一转换:
40
+ * - 数组字段以 `arrayDelimiter` 拼接;
41
+ * - 对象字段使用 `JSON.stringify`;
42
+ * - 布尔字段输出 `1/0`;
43
+ * - 日期字段按 `yyyy-mm-dd hh:ii:ss` 输出。
44
+ */
45
+ static async stringify(value, options = {}) {
46
+ let res = "";
47
+ for await (const line of csv.stringify(options.objectFieldNames?.length || options.booleanFieldNames?.length || options.dateFieldNames?.length || options.arrayFieldNames?.length && options.arrayDelimiter ? value.map((item) => mapValues(item, (v, k) => {
48
+ if (options.arrayFieldNames.includes(k)) return v ? v.join(options.arrayDelimiter) : "";
49
+ if (options.objectFieldNames?.includes(k)) return JSON.stringify(v || {});
50
+ if (options.booleanFieldNames?.includes(k)) return v ? "1" : "0";
51
+ if (options.dateFieldNames?.includes(k)) return v ? formatDate(v, "yyyy-mm-dd hh:ii:ss") : v;
52
+ return v;
53
+ })) : value, { header: true })) res += line;
54
+ return res;
55
+ }
56
+ /**
57
+ * 读取 CSV 文件并立即解析为对象数组。
58
+ *
59
+ * 传入的 `options` 会应用到 `parse` 中,因此开发者可以用同一套规则在
60
+ * 文件读取和内存对象转换两种场景中保持行为一致。
61
+ */
62
+ static async read(file, options = {}) {
63
+ const value = await fs.promises.readFile(file, "utf8");
64
+ return XCsv.parse(value, options);
65
+ }
66
+ /**
67
+ * 将对象数组写入 CSV 文件。
68
+ *
69
+ * 这里会先走 `stringify`,再将结果写到目标文件。调用方可传入相同的
70
+ * `XCsvStringifyOptions` 以控制数组、对象、布尔和日期字段输出格式。
71
+ */
72
+ static async write(file, value, options = {}) {
73
+ await fs.promises.writeFile(file, await XCsv.stringify(value, options));
74
+ }
75
+ };
76
+ export { XCsv };
@@ -0,0 +1,76 @@
1
+ import { VaeObjectSchemaShapeOf, v } from "vtils/vae";
2
+ import { OmitStrict } from "vtils/types";
3
+ /**
4
+ * 基于本地文件(JSON / YAML / CSV)的轻量数据集存储。
5
+ *
6
+ * `XDataSet` 将一组可通过 vae 校验的对象持久化为单个文件,并提供以下能力:
7
+ * - 以 `dataKey` 字段作为主键,内存中以 `Map` 形式维护记录,便于快速定位与去重;
8
+ * - 通过 `dataSchema` 在读写时对数据做类型转换与校验;
9
+ * - 根据文件扩展名自动选择 JSON、YAML 或 CSV 读写方式;
10
+ * - 可选地按固定间隔自动落盘,并在进程退出前做最后一次写入。
11
+ */
12
+ export interface XDataSetOptions<T extends Record<string, any>, K extends keyof T> {
13
+ /** 数据文件路径;扩展名决定存储格式:`.csv`、`.yaml`/`.yml`,其余按 JSON 处理。 */
14
+ storeFile: string;
15
+ /** 记录结构的 vae schema,入参为 `v`,返回各字段 schema;用于读写时的类型转换与校验。 */
16
+ dataSchema: (_: typeof v) => VaeObjectSchemaShapeOf<T>;
17
+ /** 作为记录主键的字段名,`currentMap` 以该字段值作为键。 */
18
+ dataKey: K;
19
+ /** CSV 存储时数组字段的分隔符。 */
20
+ csvArrayDelimiter?: string;
21
+ /** 自动写入间隔(毫秒),仅在未关闭自动写入时生效。 @default 5000 */
22
+ autoWriteInterval?: number;
23
+ /** 是否关闭自动写入。 @default false */
24
+ noAutoWrite?: boolean;
25
+ }
26
+ /**
27
+ * 数据集实例,负责将记录数组映射到 `storeFile` 并做类型转换。
28
+ *
29
+ * @template T 数据集中的单条记录类型。
30
+ * @template K `T` 中作为主键的字段名。
31
+ */
32
+ export declare class XDataSet<T extends Record<string, any>, K extends keyof T> {
33
+ private options;
34
+ /** 存储格式,由 `storeFile` 扩展名推导得出。 */
35
+ private storeType;
36
+ /** 记录结构的 vae schema 形状(字段名 → 字段 schema)。 */
37
+ private dataShapeSchema;
38
+ /** 由 `dataShapeSchema` 组合得到的对象 schema,用于整体转换与校验。 */
39
+ private dataSchema;
40
+ /** 根据 schema 推导出的 CSV 解析/序列化配置。 */
41
+ private csvOptions;
42
+ /** 以 `dataKey` 字段值为键的记录映射,值为去掉主键后的记录。 */
43
+ currentMap: Record<T[K], OmitStrict<T, K>>;
44
+ /**
45
+ * 将 `currentMap` 还原为记录数组。
46
+ *
47
+ * 每条记录会按 schema 定义的字段顺序重组,并执行一次 schema 转换,
48
+ * 保证返回结构与 `dataSchema` 声明一致。
49
+ */
50
+ get current(): T[];
51
+ /**
52
+ * @param options 数据集配置,详见 {@link XDataSetOptions}。
53
+ *
54
+ * 构造函数会先根据 `storeFile` 扩展名确定存储格式,再基于 `dataSchema`
55
+ * 生成 CSV 所需的字段类型转换配置。若未设置 `noAutoWrite`,则启动定时器
56
+ * 周期性调用 {@link write},并在进程退出时做最后一次写入。
57
+ */
58
+ constructor(options: XDataSetOptions<T, K>);
59
+ /**
60
+ * 从 `storeFile` 读取数据集。
61
+ *
62
+ * - 文件不存在或读取/解析失败时返回空数组(不会抛错);
63
+ * - 读取成功后对每条记录执行 schema 转换,并重建 `currentMap`。
64
+ *
65
+ * @returns 读取并转换后的记录数组。
66
+ */
67
+ read(): Promise<T[]>;
68
+ /**
69
+ * 将数据集写入 `storeFile`。
70
+ *
71
+ * 写入前会确保父目录存在;未显式传入 `dataSet` 时使用 `current` 作为数据源。
72
+ *
73
+ * @param dataSet 要写入的记录数组,默认取 `current`。
74
+ */
75
+ write(dataSet?: T[]): Promise<void>;
76
+ }