ph-utils 0.20.0 → 1.0.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/lib/index.js CHANGED
@@ -1,239 +1,1543 @@
1
+ import { a as toRgb, i as toHsv, n as rgbToHex, r as toHex, t as adjust } from "./color-CUZrwrjD.js";
2
+ //#region src/common.ts
1
3
  /**
2
- * node 和 web 通用的工具类
3
- */
4
+ * node 和 web 通用的基础工具函数
5
+ */
4
6
  /** 包含字母+数字的随机数字符 */
5
7
  const RANDOM_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
6
- /** 只包含字母的随机数字符 */
7
- const NUMBER_RANDOM_CHARTS = "0123456789";
8
- /**
9
- * 验证参数是否为空
10
- * @param str 待验证的参数
11
- * @param ignoreWhitespace 如果是字符串是否忽略空格(包括空白字符串以及[\r\t\n]之类的制表符),默认为true
12
- */
13
- export function isBlank(str, ignoreWhitespace = true) {
14
- if (str == null) {
15
- return true;
16
- }
17
- return ((ignoreWhitespace && typeof str === "string"
18
- ? str.trim().length
19
- : str.length) === 0);
20
- }
21
- /**
22
- * 屏蔽手机号,中间部分用 * 展示
23
- * @param mobile 待屏蔽的手机号
24
- * @returns 屏蔽后的手机号,例如:123 **** 1234
25
- */
26
- export function shieldMobile(mobile) {
27
- const x1 = Math.floor(mobile.length / 2);
28
- const x2 = Math.ceil(x1 / 2);
29
- const shields = [" "];
30
- for (let i = 0; i < x1 - 1; i++) {
31
- shields.push("*");
32
- }
33
- shields.push(" ");
34
- return (mobile.substring(0, x2) + shields.join("") + mobile.substring(x2 + x1 - 1));
35
- }
36
- /**
37
- * 验证参数是否是数字
38
- * @param str 待验证的字符串
39
- * @param numericParam 通过参数标记是否包含小数、正数
40
- * @param numericParam.isPositive 是否是正数, 默认: false
41
- * @param numericParam.isFloat 是否是小数, 默认: true
42
- * @returns true 是数字, false 不是数字
43
- */
44
- export function isNumeric(str, numericParam) {
45
- numericParam = { isPositive: false, isFloat: true, ...numericParam };
46
- const symbol = numericParam.isPositive ? "[+]?" : "[+-]?";
47
- const main = numericParam.isFloat ? "([0-9]*[.])?[0-9]+" : "[0-9]+";
48
- return new RegExp("^" + symbol + main + "$").test(str);
49
- }
50
- /**
51
- * 验证参数是否是Boolean 类型
52
- * @param str 待验证的字符串
53
- * @returns
54
- */
55
- export function isBoolean(str) {
56
- return ["true", "false"].indexOf(str) >= 0;
57
- }
58
- export function random(opts) {
59
- if (typeof opts === "object" && opts.min != null && opts.max != null) {
60
- const randomNum = Math.random();
61
- /* 生成两个数字之间的随机数(number) */
62
- const end = opts.hasEnd ? 1 : 0;
63
- const resRandom = randomNum * (opts.max - opts.min + end) + opts.min;
64
- return opts.isInteger !== false ? Math.floor(resRandom) : resRandom;
65
- }
66
- else {
67
- if (typeof opts === "object" && opts.length == null) {
68
- throw new Error("random_length_cannot_null");
69
- }
70
- let len = typeof opts === "object" ? opts.length : opts;
71
- /* 生成指定长度的随机数 */
72
- let chars = RANDOM_CHARS;
73
- if (typeof opts === "object" && opts.hasLetter === false) {
74
- chars = NUMBER_RANDOM_CHARTS;
75
- }
76
- const resRandom = Array.from({ length: len }, () => chars.charAt(random({ min: 0, max: chars.length - 1, hasEnd: true }))).join("");
77
- if (typeof opts === "object" &&
78
- opts.firstIsZero === false &&
79
- resRandom.indexOf("0") === 0) {
80
- return random(opts);
81
- }
82
- else {
83
- return resRandom;
84
- }
85
- }
86
- }
87
- /**
88
- * 带有错误名称标记的错误类型
89
- */
90
- export class BaseError extends Error {
91
- constructor() {
92
- if (arguments.length === 1) {
93
- super(arguments[0]);
94
- this.name = "BaseError";
95
- }
96
- else {
97
- super(arguments[1]);
98
- this.name = arguments[0];
99
- }
100
- }
101
- }
102
- /**
103
- * 将金额数字格式化为金额格式显示并且会保留两位小数[去除多余的位数,不是四舍五入,而是直接舍去] 1234523432.23 => 123,123,123.23
104
- * @param {number} number 待转换的金额数字
105
- * @return string
106
- */
107
- export function formatMoney(number) {
108
- if (typeof Intl.NumberFormat !== "undefined") {
109
- const formatter = new Intl.NumberFormat("zh-CN", {
110
- style: "decimal",
111
- maximumFractionDigits: 2,
112
- });
113
- return formatter.format(number);
114
- }
115
- else {
116
- number = number || 0;
117
- const negative = "";
118
- const base = String(parseInt(number, 10)); // 获取数字整数部分
119
- const mod = base.length > 3 ? base.length % 3 : 0;
120
- /*
121
- * 利用 正则前瞻 (?=) 将3位数字后面还紧跟一位数字的三位数字替换为 数字, 的形式
122
- */
123
- const numberStr = String(number);
124
- const usePrecision = numberStr.indexOf(".");
125
- let dotStr = usePrecision > 0 ? numberStr.slice(usePrecision + 1) : "00";
126
- dotStr = dotStr.length > 2 ? dotStr.slice(0, 2) : dotStr;
127
- return (negative +
128
- (mod ? base.slice(0, mod) + "," : "") +
129
- base.slice(mod).replace(/(\d{3})(?=\d)/g, "$1,") +
130
- "." +
131
- dotStr);
132
- }
133
- }
134
- /**
135
- * 将风格由大写风格转换为下划线风格: HelloWorld -> hello-world
136
- * @param name 命名, 例如: HelloWorld
137
- * @param connector 连接符, 默认为: _
138
- */
139
- export function snakeCaseStyle(name, connector = "-") {
140
- return name.replace(/([A-Z])/g, (match, p1, offset) => (offset > 0 ? connector : "") + match.toLowerCase());
141
- }
142
- /**
143
- * 对数字进行四舍五入处理
144
- * @param num 需要进行四舍五入的数字
145
- * @param precision 精度,默认为2,即保留小数点后两位
146
- * @param roundType 舍入类型,默认为0,提供三种取值:
147
- * 0: 标准四舍五入
148
- * 1: 向上取整
149
- * 2: 向下取整
150
- * @returns 返回经过指定方式舍入后的数字
151
- */
152
- export function round(num, precision = 2, roundType = 0) {
153
- // 计算精度因子,用于后续四舍五入计算
154
- const factor = Math.pow(10, precision);
155
- switch (roundType) {
156
- case 0:
157
- // 标准四舍五入
158
- return Math.round(num * factor) / factor;
159
- case 1:
160
- // 向上取整
161
- return Math.ceil(num * factor) / factor;
162
- case 2:
163
- // 向下取整
164
- return Math.floor(num * factor) / factor;
165
- default:
166
- // 如果传入的roundType不是预期的值,则直接返回原始数字
167
- return num;
168
- }
169
- }
170
- /**
171
- * 反转字符串
172
- */
173
- export function reverseStr(str) {
174
- return str.split("").reverse().join("");
175
- }
176
- function getObjKeyValue(data, key) {
177
- if (data == null)
178
- return null;
179
- return data[key];
180
- }
181
- /**
182
- * 嵌套的 json 指定 key 数据
183
- * @param data JSON格式数据
184
- * @param keys 待获取的数据 key, 可以通过 [.] 获取嵌套数据, 例如: a.b.c
185
- * @returns
186
- */
187
- export function getJSONValue(data, keystr) {
188
- if (data == null)
189
- return null;
190
- const keys = keystr.split(".");
191
- let res = data;
192
- for (const key of keys) {
193
- res = getObjKeyValue(res, key);
194
- if (res == null)
195
- break;
196
- }
197
- return res;
198
- }
199
- /**
200
- * 数据格式化主要用于数据类型转换
201
- * @param data 待转换数据类型的数据
202
- * @param config 转换配置
203
- * @returns
204
- */
205
- export function formatData(data, config) {
206
- const cfg = {
207
- numberFields: [],
208
- stringFields: [],
209
- formatter: {},
210
- ...config,
211
- };
212
- const res = {};
213
- for (const key in data) {
214
- let value = getJSONValue(data, key);
215
- let formater;
216
- if (key in cfg.formatter) {
217
- formater = cfg.formatter[key];
218
- }
219
- if (cfg.numberFields.includes(key)) {
220
- formater = "number";
221
- }
222
- if (cfg.stringFields.includes(key)) {
223
- formater = "string";
224
- }
225
- if (formater != null) {
226
- if (typeof formater == "function") {
227
- value = formater(value);
228
- }
229
- else if (formater == "number") {
230
- value = Number(value);
231
- }
232
- else if (formater == "string") {
233
- value = String(value);
234
- }
235
- }
236
- res[key] = value;
237
- }
238
- return res;
8
+ /** 只包含数字的随机数字符 */
9
+ const NUMBER_RANDOM_CHARS = "0123456789";
10
+ /**
11
+ * 验证参数是否为空
12
+ * @param str 待验证的参数
13
+ * @param ignoreWhitespace 如果是字符串是否忽略空格(包括空白字符串以及[\r\t\n]之类的制表符),默认为true
14
+ */
15
+ function isBlank(str, ignoreWhitespace = true) {
16
+ if (str == null) return true;
17
+ if (typeof str !== "string") return str.length === 0;
18
+ return ignoreWhitespace ? str.trim().length === 0 : str.length === 0;
19
+ }
20
+ /**
21
+ * 屏蔽手机号,中间部分用 * 展示(保持向后兼容)
22
+ * @param mobile 待屏蔽的手机号
23
+ * @param placeholder 遮掩字符,默认: '*'
24
+ * @returns 屏蔽后的手机号,例如:138****5678
25
+ */
26
+ function shieldMobile(mobile, placeholder = "*") {
27
+ return shieldString({
28
+ str: mobile,
29
+ startKeep: 3,
30
+ endKeep: 4,
31
+ placeholder,
32
+ addSpaces: false
33
+ });
34
+ }
35
+ /**
36
+ * 通用字符串遮掩函数
37
+ * @param options 配置参数
38
+ * @returns 遮掩后的字符串
39
+ *
40
+ * @example
41
+ * // 基本遮掩: 13812345678 -> 138****5678
42
+ * shieldString({ str: '13812345678' })
43
+ *
44
+ * @example
45
+ * // 限制长度: 64位字符串 -> 只显示前20位
46
+ * shieldString({
47
+ * str: '1234567890123456789012345678901234567890123456789012345678901234',
48
+ * startKeep: 8,
49
+ * endKeep: 8,
50
+ * maxLength: 20
51
+ * })
52
+ * // 结果: 12345678****87654321
53
+ *
54
+ * @example
55
+ * // 带省略号截断
56
+ * shieldString({
57
+ * str: '1234567890123456789012345678901234567890123456789012345678901234',
58
+ * startKeep: 8,
59
+ * endKeep: 8,
60
+ * maxLength: 15,
61
+ * showEllipsis: true
62
+ * })
63
+ * // 结果: 12345678...8765
64
+ */
65
+ function shieldString(options) {
66
+ const { str, startKeep = 3, endKeep = 4, placeholder = "*", addSpaces = false, customShield, maxLength, showEllipsis = true, ellipsis = "..." } = options;
67
+ let result;
68
+ if (customShield) result = customShield(str);
69
+ else if (str.length <= startKeep + endKeep) result = placeholder.repeat(str.length);
70
+ else {
71
+ const shieldCount = str.length - startKeep - endKeep;
72
+ const shieldStr = placeholder.repeat(shieldCount);
73
+ const startPart = str.slice(0, startKeep);
74
+ const endPart = str.slice(-endKeep);
75
+ result = addSpaces ? `${startPart} ${shieldStr} ${endPart}`.trim() : startPart + shieldStr + endPart;
76
+ }
77
+ if (maxLength != null && result.length > maxLength) {
78
+ if (!showEllipsis) return result.slice(0, maxLength);
79
+ const ellipsisLen = ellipsis.length;
80
+ if (maxLength <= ellipsisLen) return ellipsis.slice(0, maxLength);
81
+ const keepLen = maxLength - ellipsisLen;
82
+ const startKeepLen = Math.min(Math.floor(keepLen * .7), keepLen);
83
+ const endKeepLen = keepLen - startKeepLen;
84
+ if (startKeepLen < 3 && endKeepLen > 0) {
85
+ const startPart = result.slice(0, Math.min(startKeepLen, result.length));
86
+ const endPart = result.slice(-endKeepLen);
87
+ return startPart + ellipsis + endPart;
88
+ }
89
+ const startPart = result.slice(0, startKeepLen);
90
+ const endPart = result.slice(-endKeepLen);
91
+ return startPart + ellipsis + endPart;
92
+ }
93
+ return result;
94
+ }
95
+ /**
96
+ * 简化版:遮掩并限制长度
97
+ * @param str 待遮掩的字符串
98
+ * @param options 配置选项
99
+ * @returns 遮掩并截断后的字符串
100
+ *
101
+ * @example
102
+ * // 遮掩手机号并限制显示长度
103
+ * shieldWithLimit('13812345678', { maxLength: 8 }) // 138****78
104
+ *
105
+ * @example
106
+ * // 遮掩长字符串
107
+ * shieldWithLimit('12345678901234567890', {
108
+ * startKeep: 4,
109
+ * endKeep: 4,
110
+ * maxLength: 12
111
+ * }) // 1234****7890
112
+ */
113
+ function shieldWithLimit(str, options) {
114
+ return shieldString({
115
+ str,
116
+ ...options
117
+ });
118
+ }
119
+ /**
120
+ * 智能摘要:根据重要性自动调整保留位数
121
+ * @param str 待处理的字符串
122
+ * @param maxLength 最大输出长度
123
+ * @param placeholder 遮掩字符
124
+ * @returns 摘要后的字符串
125
+ *
126
+ * @example
127
+ * // 长字符串自动摘要
128
+ * smartSummary('12345678901234567890', 10) // 1234****90
129
+ *
130
+ * @example
131
+ * // 短字符串不变
132
+ * smartSummary('12345', 10) // 12345
133
+ */
134
+ function smartSummary(str, maxLength, placeholder = "*") {
135
+ if (str.length <= maxLength) return str;
136
+ if (maxLength < 3) return str.slice(0, maxLength);
137
+ const totalKeep = maxLength - placeholder.length;
138
+ const startKeep = Math.floor(totalKeep / 2);
139
+ const endKeep = totalKeep - startKeep;
140
+ return shieldString({
141
+ str,
142
+ startKeep: Math.max(1, startKeep),
143
+ endKeep: Math.max(1, endKeep),
144
+ placeholder,
145
+ maxLength,
146
+ showEllipsis: false
147
+ });
148
+ }
149
+ /**
150
+ * 屏蔽邮箱
151
+ * @param email 待屏蔽的邮箱
152
+ * @param placeholder 遮掩字符,默认: '*'
153
+ * @returns 屏蔽后的邮箱,例如:exa****@email.com
154
+ */
155
+ function shieldEmail(email, placeholder = "*") {
156
+ const [name, domain] = email.split("@");
157
+ if (!domain) return email;
158
+ const keepLength = Math.min(3, Math.floor(name.length / 2));
159
+ const shieldCount = name.length - keepLength;
160
+ const shieldStr = placeholder.repeat(Math.min(shieldCount, 4));
161
+ return `${name.slice(0, keepLength)}${shieldStr}@${domain}`;
162
+ }
163
+ /**
164
+ * 屏蔽身份证号
165
+ * @param idCard 待屏蔽的身份证号
166
+ * @param placeholder 遮掩字符,默认: '*'
167
+ * @returns 屏蔽后的身份证号,例如:110101********1234
168
+ */
169
+ function shieldIdCard(idCard, placeholder = "*") {
170
+ return shieldString({
171
+ str: idCard,
172
+ startKeep: 6,
173
+ endKeep: 4,
174
+ placeholder,
175
+ addSpaces: false
176
+ });
177
+ }
178
+ /**
179
+ * 屏蔽银行卡号(带空格格式化)
180
+ * @param bankCard 待屏蔽的银行卡号
181
+ * @param placeholder 遮掩字符,默认: '*'
182
+ * @returns 屏蔽后的银行卡号,例如:622848 **** **** 9018
183
+ */
184
+ function shieldBankCard(bankCard, placeholder = "*") {
185
+ return shieldString({
186
+ str: bankCard,
187
+ startKeep: 6,
188
+ endKeep: 4,
189
+ placeholder,
190
+ addSpaces: true
191
+ });
192
+ }
193
+ /**
194
+ * 屏蔽姓名
195
+ * @param name 待屏蔽的姓名
196
+ * @param placeholder 遮掩字符,默认: '*'
197
+ * @returns 屏蔽后的姓名,例如:张** 或 张*三
198
+ */
199
+ function shieldName(name, placeholder = "*") {
200
+ if (name.length <= 2) return name.charAt(0) + placeholder.repeat(name.length - 1);
201
+ return shieldString({
202
+ str: name,
203
+ startKeep: 1,
204
+ endKeep: 1,
205
+ placeholder,
206
+ addSpaces: false
207
+ });
208
+ }
209
+ /**
210
+ * 检测敏感信息并智能遮掩(支持长度控制)
211
+ * @param str 待检测的字符串
212
+ * @param placeholder 遮掩字符,默认: '*'
213
+ * @param maxLength 最大输出长度,默认: 不限制
214
+ * @returns 遮掩后的字符串,自动识别类型
215
+ *
216
+ * @example
217
+ * // 手机号:138****5678
218
+ * smartShield('13812345678')
219
+ *
220
+ * @example
221
+ * // 邮箱:exa****@email.com
222
+ * smartShield('example@email.com')
223
+ *
224
+ * @example
225
+ * // 身份证:110101********1234
226
+ * smartShield('110101199001011234')
227
+ *
228
+ * @example
229
+ * // 限制长度
230
+ * smartShield('13812345678', '*', 8) // 138****78
231
+ */
232
+ function smartShield(str, placeholder = "*", maxLength) {
233
+ const cleanStr = str.replace(/\s/g, "");
234
+ let result;
235
+ if (/^1\d{10}$/.test(cleanStr)) result = shieldMobile(cleanStr, placeholder);
236
+ else if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(cleanStr)) result = shieldEmail(cleanStr, placeholder);
237
+ else if (/^[1-9]\d{14}(\d{2}[\dXx])?$/.test(cleanStr)) result = shieldIdCard(cleanStr, placeholder);
238
+ else if (/^\d{16,19}$/.test(cleanStr)) result = shieldBankCard(cleanStr, placeholder);
239
+ else {
240
+ const keepLength = Math.floor(cleanStr.length / 3);
241
+ result = shieldString({
242
+ str: cleanStr,
243
+ startKeep: keepLength,
244
+ endKeep: keepLength,
245
+ placeholder,
246
+ addSpaces: false
247
+ });
248
+ }
249
+ if (maxLength != null && result.length > maxLength) return result.slice(0, maxLength);
250
+ return result;
251
+ }
252
+ /**
253
+ * 验证参数是否是数字
254
+ * @param str 待验证的字符串
255
+ * @param numericParam 通过参数标记是否包含小数、正数
256
+ * @param numericParam.isPositive 是否是正数, 默认: false
257
+ * @param numericParam.isFloat 是否是小数, 默认: true
258
+ * @returns true 是数字, false 不是数字
259
+ */
260
+ function isNumeric(str, numericParam) {
261
+ const { isPositive = false, isFloat = true } = numericParam ?? {};
262
+ return new RegExp(`^${isPositive ? "[+]?" : "[+-]?"}${isFloat ? "([0-9]*[.])?[0-9]+" : "[0-9]+"}$`).test(str);
263
+ }
264
+ /**
265
+ * 验证参数是否是Boolean 类型
266
+ * @param str 待验证的字符串
267
+ * @returns
268
+ */
269
+ function isBoolean(str) {
270
+ return ["true", "false"].includes(str);
271
+ }
272
+ function random(opts) {
273
+ if (typeof opts === "object" && opts !== null && "min" in opts && "max" in opts) {
274
+ const { min, max, hasEnd = false, isInteger = true } = opts;
275
+ const end = hasEnd ? 1 : 0;
276
+ const resRandom = Math.random() * (max - min + end) + min;
277
+ return isInteger ? Math.floor(resRandom) : resRandom;
278
+ }
279
+ const len = typeof opts === "object" ? opts.length : opts;
280
+ if (len == null) throw new Error("random_length_cannot_null");
281
+ const chars = typeof opts === "object" && opts.hasLetter === false ? NUMBER_RANDOM_CHARS : RANDOM_CHARS;
282
+ const charsLen = chars.length;
283
+ const generate = () => {
284
+ const result = [];
285
+ let i = 0;
286
+ while (i < len) {
287
+ result.push(chars[Math.floor(Math.random() * charsLen)]);
288
+ i++;
289
+ }
290
+ return result.join("");
291
+ };
292
+ const result = generate();
293
+ if (typeof opts === "object" && opts.firstIsZero === false && result.at(0) === "0") return random(opts);
294
+ return result;
295
+ }
296
+ /**
297
+ * 将金额数字格式化为金额格式显示并且会保留两位小数[去除多余的位数,不是四舍五入,而是直接舍去] 1234523432.23 => 123,123,123.23
298
+ * @param {number} number 待转换的金额数字
299
+ * @return string
300
+ */
301
+ function formatMoney(number) {
302
+ if (!Number.isFinite(number)) return "0.00";
303
+ return new Intl.NumberFormat("zh-CN", {
304
+ style: "decimal",
305
+ maximumFractionDigits: 2,
306
+ minimumFractionDigits: 2
307
+ }).format(number);
308
+ }
309
+ /**
310
+ * 将风格由大写风格转换为下划线风格: HelloWorld -> hello-world
311
+ * @param name 命名, 例如: HelloWorld
312
+ * @param connector 连接符, 默认为: _
313
+ */
314
+ function snakeCaseStyle(name, connector = "-") {
315
+ return name.replace(/([A-Z])/g, (match, p1, offset) => offset > 0 ? connector + match.toLowerCase() : match.toLowerCase());
316
+ }
317
+ /**
318
+ * 对数字进行四舍五入处理
319
+ * @param num 需要进行四舍五入的数字
320
+ * @param precision 精度,默认为2,即保留小数点后两位
321
+ * @param roundType 舍入类型,默认为0,提供三种取值:
322
+ * 0: 标准四舍五入
323
+ * 1: 向上取整
324
+ * 2: 向下取整
325
+ * @returns 返回经过指定方式舍入后的数字
326
+ */
327
+ function round(num, precision = 2, roundType = 0) {
328
+ if (!Number.isFinite(num)) return num;
329
+ const factor = 10 ** precision;
330
+ const roundFn = {
331
+ 0: Math.round,
332
+ 1: Math.ceil,
333
+ 2: Math.floor
334
+ }[roundType];
335
+ if (!roundFn) return num;
336
+ return roundFn(num * factor) / factor;
337
+ }
338
+ /**
339
+ * 反转字符串
340
+ */
341
+ function reverseStr(str) {
342
+ return str.split("").reverse().join("");
343
+ }
344
+ /**
345
+ * 嵌套的 json 指定 key 数据
346
+ * @param data JSON格式数据
347
+ * @param keys 待获取的数据 key, 可以通过 [.] 获取嵌套数据, 例如: a.b.c
348
+ * @returns
349
+ */
350
+ function getJSONValue(data, keystr) {
351
+ if (data == null) return null;
352
+ return keystr.split(".").reduce((acc, key) => acc?.[key] ?? null, data);
353
+ }
354
+ /**
355
+ * 数据格式化主要用于数据类型转换
356
+ * @param data 待转换数据类型的数据
357
+ * @param config 转换配置
358
+ * @returns
359
+ */
360
+ function formatData(data, config) {
361
+ if (data == null) return data;
362
+ const { numberFields = [], stringFields = [], formatter = {} } = config ?? {};
363
+ return Object.fromEntries(Object.entries(data).map(([key, value]) => {
364
+ let formater = formatter[key];
365
+ if (formater == null) {
366
+ if (numberFields.includes(key)) formater = "number";
367
+ else if (stringFields.includes(key)) formater = "string";
368
+ }
369
+ let newValue = value;
370
+ if (formater != null) {
371
+ if (typeof formater === "function") newValue = formater(value);
372
+ else if (formater === "number") newValue = Number(value);
373
+ else if (formater === "string") newValue = String(value);
374
+ }
375
+ return [key, newValue];
376
+ }));
377
+ }
378
+ //#endregion
379
+ //#region src/date.ts
380
+ /**
381
+ * node 和 web 端日期处理工具类
382
+ */
383
+ const REGEX_FORMAT = /yy(?:yy)?|([HMmds])\1?|(S)?/g;
384
+ const REGEX_PARSE = /^(\d{4})-?(\d{1,2})-?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d{1,3})?$/;
385
+ const ofArgs = {
386
+ start: [
387
+ 0,
388
+ 0,
389
+ 1,
390
+ 0,
391
+ 0,
392
+ 0,
393
+ 0
394
+ ],
395
+ end: [
396
+ 0,
397
+ 11,
398
+ -2,
399
+ 23,
400
+ 59,
401
+ 59,
402
+ 999
403
+ ]
404
+ };
405
+ const units = {
406
+ Date: [
407
+ "date",
408
+ "Date",
409
+ "day",
410
+ "Day",
411
+ "D",
412
+ "d"
413
+ ],
414
+ Month: [
415
+ "Month",
416
+ "month",
417
+ "m"
418
+ ],
419
+ FullYear: [
420
+ "Year",
421
+ "year",
422
+ "y"
423
+ ],
424
+ Hours: [
425
+ "Hours",
426
+ "hours",
427
+ "H"
428
+ ],
429
+ Minutes: [
430
+ "Minutes",
431
+ "Minute",
432
+ "minute",
433
+ "minutes",
434
+ "M"
435
+ ],
436
+ Seconds: [
437
+ "Seconds",
438
+ "seconds",
439
+ "Second",
440
+ "second",
441
+ "s"
442
+ ],
443
+ Milliseconds: [
444
+ "Milliseconds",
445
+ "Millisecond",
446
+ "milliseconds",
447
+ "illisecond",
448
+ "S"
449
+ ]
450
+ };
451
+ /**
452
+ * 不足位数, 前位补 0
453
+ * @param s 日期数字
454
+ * @param l 截取位数
455
+ * @returns {string} 补0后的日期数字
456
+ */
457
+ function p(s, l = 2) {
458
+ return `000${s}`.slice(l * -1);
459
+ }
460
+ /**
461
+ * 将单位转换为首字母大写, 例如:hours -> Hours
462
+ * @param unit hours
463
+ * @returns
464
+ */
465
+ function getUnit(unit) {
466
+ let period = null;
467
+ for (const [key, value] of Object.entries(units)) if (value.includes(unit)) {
468
+ period = key;
469
+ break;
470
+ }
471
+ if (period == null) throw new Error(`Invalid unit: ${unit}`);
472
+ return period;
473
+ }
474
+ /**
475
+ * 获取指定日期某个月的最后一天
476
+ * @param date 日期
477
+ * @param month 月份, 如果不传, 则当前月的最后一天
478
+ * @returns
479
+ */
480
+ function getLastDayOfMonth(date, month) {
481
+ const lastDate = new Date(date.getFullYear(), (month || date.getMonth()) + 1, 1);
482
+ lastDate.setDate(lastDate.getDate() - 1);
483
+ return lastDate.getDate();
484
+ }
485
+ /**
486
+ * 将日期格式化为指定形式的字符串
487
+ * @param date 日期
488
+ * @param pattern 格式化字符串 yyyy - 年, mm - 月, dd - 日, HH - 小时(24时制), MM - 分钟, ss - 秒, S - 毫秒, 默认: yyyy-mm-dd HH:MM:ss
489
+ */
490
+ function format(date, pattern = "yyyy-mm-dd HH:MM:ss") {
491
+ date = parse(date);
492
+ const d = date.getDate();
493
+ const y = date.getFullYear();
494
+ const m = date.getMonth();
495
+ const H = date.getHours();
496
+ const M = date.getMinutes();
497
+ const s = date.getSeconds();
498
+ const flags = {
499
+ yy: p(y),
500
+ yyyy: y,
501
+ m: m + 1,
502
+ mm: p(m + 1),
503
+ d,
504
+ dd: p(d),
505
+ H,
506
+ HH: p(H),
507
+ M,
508
+ MM: p(M),
509
+ s,
510
+ ss: p(s),
511
+ S: p(date.getMilliseconds(), 3)
512
+ };
513
+ if (pattern != null) return pattern.replace(REGEX_FORMAT, (flag) => {
514
+ if (flag in flags) return flags[flag];
515
+ return flag;
516
+ });
517
+ return String(date.getTime());
518
+ }
519
+ /**
520
+ * 将指定的参数解析为日期对象(Date)
521
+ * 参考 dayjs 实现, 也可以参考 https://github.com/nomiddlename/date-format
522
+ * @param date 待解析的日期参数
523
+ */
524
+ function parse(date) {
525
+ if (date == null) return /* @__PURE__ */ new Date();
526
+ if (typeof date === "string" && !/Z$/i.test(date)) {
527
+ const d = date.match(REGEX_PARSE);
528
+ if (d) return new Date(d[1], d[2] - 1, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, d[7] || 0);
529
+ }
530
+ if (typeof date === "number") return new Date(date <= 9999999999 ? date * 1e3 : date);
531
+ return new Date(date);
532
+ }
533
+ /**
534
+ * 设置日期的开始或者结束的点
535
+ * @param date 日期,能够被 parse 解析的日期
536
+ * @param unit 单位,Date[D]、Minute[M], 默认为 Date
537
+ * @param isEnd true则为 endOf
538
+ */
539
+ function dateOf(date, unit, isEnd = false) {
540
+ const periods = [
541
+ "Year",
542
+ "Month",
543
+ "Date",
544
+ "Hours",
545
+ "Minutes",
546
+ "Seconds",
547
+ "Milliseconds"
548
+ ];
549
+ let index = periods.indexOf(getUnit(unit || "Date"));
550
+ const clone = parse(date);
551
+ index++;
552
+ const setValues = ofArgs[isEnd === true ? "end" : "start"];
553
+ for (const len = periods.length; index < len; index++) {
554
+ let value = setValues[index];
555
+ if (value === -2) value = getLastDayOfMonth(clone);
556
+ Date.prototype["set" + periods[index]].apply(clone, [setValues[index]]);
557
+ }
558
+ return clone;
559
+ }
560
+ /**
561
+ * 设置日期的开始的点
562
+ * @param date 日期,能够被 parse 解析的日期
563
+ * @param unit 单位,Date[D]、Minute[M], 默认为 Date
564
+ * @returns
565
+ */
566
+ function startOf(date, unit) {
567
+ return dateOf(date, unit);
568
+ }
569
+ /**
570
+ * 设置日期的结束点
571
+ * @param date 日期,能够被 parse 解析的日期
572
+ * @param unit 单位,Date[D]、Minute[M], 默认为 Date
573
+ * @returns
574
+ */
575
+ function endOf(date, unit) {
576
+ return dateOf(date, unit, true);
577
+ }
578
+ /**
579
+ * 获取时间戳
580
+ * @param ctime 时间
581
+ * @param pre 精度, s - 精确到秒, ms - 精确到毫秒, 默认: s
582
+ * @returns
583
+ */
584
+ function timestamp(ctime, pre = "s") {
585
+ let tm = parse(ctime).getTime();
586
+ return pre === "s" ? Math.floor(tm / 1e3) : tm;
587
+ }
588
+ /**
589
+ * 日期加上指定时间后的日期
590
+ * @param date 指定的日期
591
+ * @param num 需要添加的数字, 如果这个参数传递一个小于0的数字,则就是日期减去相应的数字
592
+ * @param unit 需要添加的单位,date - 加减天数
593
+ * @param fmt 可选参数,如果传递了格式化的单位,则返回格式化后的日期, 格式化字符串 yyyy - 年, mm - 月, dd - 日, HH - 小时, MM - 分钟, ss - 秒
594
+ * @returns {Date | string} 如果传递了 fmt 参数,则返回 string,否则返回 Date
595
+ */
596
+ function add(date, num, unit, fmt) {
597
+ let sdate = /* @__PURE__ */ new Date();
598
+ if (date != null) sdate = parse(date);
599
+ unit = getUnit(unit);
600
+ let fn = "set" + unit;
601
+ let gn = "get" + unit;
602
+ let oldValue = Date.prototype[gn].apply(sdate);
603
+ Date.prototype[fn].apply(sdate, [oldValue + num]);
604
+ if (typeof fmt === "string") return format(sdate, fmt);
605
+ else return sdate;
606
+ }
607
+ //#endregion
608
+ //#region src/array.ts
609
+ /**
610
+ * 全局 Collator 单例缓存,避免重复创建
611
+ */
612
+ let _collator;
613
+ /**
614
+ * 获取 Intl.Collator 单例(支持数字感知的字符串比较,如 "a2" < "a10")
615
+ * 使用 ??= 运算符实现惰性初始化
616
+ */
617
+ function _getCollator() {
618
+ return _collator ?? (_collator = new Intl.Collator(void 0, { numeric: true }));
619
+ }
620
+ /**
621
+ * 数组排序(不修改原数组,返回新数组)
622
+ * @param arr 待排序数组
623
+ * @param order 排序方向: "asc" - 升序(默认), "desc" - 降序
624
+ * @param orderKey 如果数组元素是对象,指定按哪个字段排序;为 null 时按元素本身排序
625
+ * @returns 排序后的新数组
626
+ */
627
+ function order(arr, order = "asc", orderKey = null) {
628
+ const collator = _getCollator();
629
+ const sign = order === "asc" ? 1 : -1;
630
+ const get = orderKey == null ? (v) => v : (v) => v[orderKey];
631
+ return [...arr].sort((a, b) => {
632
+ const av = get(a);
633
+ const bv = get(b);
634
+ return (typeof av === "string" ? collator.compare(av, bv) : av < bv ? -1 : av > bv ? 1 : 0) * sign;
635
+ });
239
636
  }
637
+ /**
638
+ * 获取集合大小(兼容 Set 和 Array)
639
+ * @param v Set 或 Array
640
+ * @returns 元素个数
641
+ */
642
+ function _size(v) {
643
+ return v instanceof Set ? v.size : v.length;
644
+ }
645
+ /**
646
+ * 将 Set 或 Array 统一转为 Set(如果已经是 Set 则直接返回,避免重复创建)
647
+ * @param v Set 或 Array
648
+ * @returns Set 实例
649
+ */
650
+ function _toSet(v) {
651
+ return v instanceof Set ? v : new Set(v);
652
+ }
653
+ /**
654
+ * 检测对象上是否存在可用的原生方法(ES2025 Set 新方法检测)
655
+ * @param obj 目标对象
656
+ * @param name 方法名
657
+ * @returns 是否为可调用的函数
658
+ */
659
+ function _hasNative(obj, name) {
660
+ return typeof obj[name] === "function";
661
+ }
662
+ /**
663
+ * 返回所有集合交集的元素组成的新集合
664
+ * - 以第一个参数的类型决定返回 Set 还是 Array
665
+ * - 支持混合传入 Set 和 Array
666
+ * - 优先使用原生 ES2025 Set.prototype.intersection
667
+ * @param arrs 多个集合/数组
668
+ * @returns 交集结果(Set 或 Array)
669
+ */
670
+ function intersection(...arrs) {
671
+ const isSet = arrs[0] instanceof Set;
672
+ let acc = isSet ? new Set(arrs[0]) : [...arrs[0]];
673
+ for (let i = 1; i < arrs.length && _size(acc) > 0; i++) {
674
+ const cur = arrs[i];
675
+ if (_size(cur) === 0) return isSet ? /* @__PURE__ */ new Set() : [];
676
+ if (isSet) {
677
+ const accSet = acc;
678
+ const curSet = _toSet(cur);
679
+ acc = _hasNative(accSet, "intersection") ? accSet.intersection(curSet) : _intersectFallback(accSet, curSet);
680
+ } else {
681
+ const curSet = _toSet(cur);
682
+ acc = acc.filter((x) => curSet.has(x));
683
+ }
684
+ }
685
+ return acc;
686
+ }
687
+ /**
688
+ * 交集回退实现(不支持原生 intersection 时使用)
689
+ * 性能优化:始终遍历较小的集合,减少迭代次数
690
+ * @param a 集合 A
691
+ * @param b 集合 B
692
+ * @returns 交集 Set
693
+ */
694
+ function _intersectFallback(a, b) {
695
+ const [smaller, larger] = a.size <= b.size ? [a, b] : [b, a];
696
+ const res = /* @__PURE__ */ new Set();
697
+ for (const v of smaller) if (larger.has(v)) res.add(v);
698
+ return res;
699
+ }
700
+ /**
701
+ * 返回第一个集合中有但后续所有集合中没有的元素组成的新集合
702
+ * - 优先使用原生 ES2025 Set.prototype.difference
703
+ * - 数组模式使用 Set.has 替代 includes,性能从 O(n×m) 降至 O(n+m)
704
+ * @param arrs 多个集合/数组
705
+ * @returns 差集结果(Set 或 Array)
706
+ */
707
+ function difference(...arrs) {
708
+ const isSet = arrs[0] instanceof Set;
709
+ let acc = isSet ? new Set(arrs[0]) : [...arrs[0]];
710
+ for (let i = 1; i < arrs.length && _size(acc) > 0; i++) {
711
+ const curSet = _toSet(arrs[i]);
712
+ if (isSet) {
713
+ const accSet = acc;
714
+ acc = _hasNative(accSet, "difference") ? accSet.difference(curSet) : _filterSet(accSet, (v) => !curSet.has(v));
715
+ } else acc = acc.filter((v) => !curSet.has(v));
716
+ }
717
+ return acc;
718
+ }
719
+ /**
720
+ * Set 过滤辅助函数:返回满足条件的新 Set
721
+ * @param set 源集合
722
+ * @param predicate 过滤条件函数
723
+ * @returns 过滤后的新 Set
724
+ */
725
+ function _filterSet(set, predicate) {
726
+ const res = /* @__PURE__ */ new Set();
727
+ for (const v of set) if (predicate(v)) res.add(v);
728
+ return res;
729
+ }
730
+ /**
731
+ * 返回多个集合的并集
732
+ * - 优先使用原生 ES2025 Set.prototype.union
733
+ * - Set 模式自动去重;Array 模式保持原有行为(拼接,不去重)
734
+ * @param arrs 多个集合/数组
735
+ * @returns 并集结果(Set 或 Array)
736
+ */
737
+ function union(...arrs) {
738
+ const isSet = arrs[0] instanceof Set;
739
+ let acc = isSet ? new Set(arrs[0]) : [...arrs[0]];
740
+ for (let i = 1; i < arrs.length; i++) {
741
+ const cur = arrs[i];
742
+ if (isSet) {
743
+ const accSet = acc;
744
+ const curSet = _toSet(cur);
745
+ acc = _hasNative(accSet, "union") ? accSet.union(curSet) : /* @__PURE__ */ new Set([...accSet, ...curSet]);
746
+ } else acc = [...acc, ...cur];
747
+ }
748
+ return acc;
749
+ }
750
+ /**
751
+ * 对称差:返回只存在于其中一个集合中的元素(不同时存在于两个集合中的元素)
752
+ * - 优先使用原生 ES2025 Set.prototype.symmetricDifference
753
+ * - 数组模式使用 Set.has 替代 includes,性能大幅提升
754
+ * @param arrs 多个集合/数组
755
+ * @returns 对称差结果(Set 或 Array)
756
+ */
757
+ function symmetricDifference(...arrs) {
758
+ const isSet = arrs[0] instanceof Set;
759
+ let acc = isSet ? new Set(arrs[0]) : [...arrs[0]];
760
+ for (let i = 1; i < arrs.length; i++) {
761
+ const cur = arrs[i];
762
+ if (isSet) {
763
+ const accSet = acc;
764
+ const curSet = _toSet(cur);
765
+ acc = _hasNative(accSet, "symmetricDifference") ? accSet.symmetricDifference(curSet) : _symDiffSetFallback(accSet, curSet);
766
+ } else acc = _symDiffArrayFallback(acc, cur);
767
+ }
768
+ return acc;
769
+ }
770
+ /**
771
+ * Set 对称差回退实现
772
+ * 逻辑:A△B = (A-B) ∪ (B-A)
773
+ * @param a 集合 A
774
+ * @param b 集合 B
775
+ * @returns 对称差 Set
776
+ */
777
+ function _symDiffSetFallback(a, b) {
778
+ const res = /* @__PURE__ */ new Set();
779
+ for (const v of a) if (!b.has(v)) res.add(v);
780
+ for (const v of b) if (!a.has(v)) res.add(v);
781
+ return res;
782
+ }
783
+ /**
784
+ * 数组对称差回退实现
785
+ * 性能优化:将两个数组都转为 Set,查找从 O(n) 降至 O(1)
786
+ * @param a 数组 A
787
+ * @param b 数组 B
788
+ * @returns 对称差数组
789
+ */
790
+ function _symDiffArrayFallback(a, b) {
791
+ const aSet = new Set(a);
792
+ const bSet = new Set(b);
793
+ const res = [];
794
+ for (const v of a) if (!bSet.has(v)) res.push(v);
795
+ for (const v of b) if (!aSet.has(v)) res.push(v);
796
+ return res;
797
+ }
798
+ /**
799
+ * 判断 a1 是否是 a2 的子集(a1 的所有元素是否都在 a2 中)
800
+ * - 优先使用原生 ES2025 Set.prototype.isSubsetOf
801
+ * - 数组模式使用 Set.has 替代 includes,性能优化
802
+ * @param a1 待判断集合(可能是子集)
803
+ * @param a2 目标集合(可能是超集)
804
+ * @returns a1 ⊆ a2 时返回 true
805
+ */
806
+ function isSubsetOf(a1, a2) {
807
+ if (a1 instanceof Set && _hasNative(a1, "isSubsetOf")) return a1.isSubsetOf(_toSet(a2));
808
+ const set2 = _toSet(a2);
809
+ for (const item of a1) if (!set2.has(item)) return false;
810
+ return true;
811
+ }
812
+ /**
813
+ * 判断 a1 是否是 a2 的超集(a2 的所有元素是否都在 a1 中)
814
+ * 逻辑等价于:a2 是 a1 的子集
815
+ * @param a1 待判断集合(可能是超集)
816
+ * @param a2 目标集合(可能是子集)
817
+ * @returns a1 ⊇ a2 时返回 true
818
+ */
819
+ function isSupersetOf(a1, a2) {
820
+ return isSubsetOf(a2, a1);
821
+ }
822
+ /**
823
+ * 判断两个集合是否没有公共元素(不相交)
824
+ * - 优先使用原生 ES2025 Set.prototype.isDisjointFrom
825
+ * - 数组模式使用 Set.has 替代 includes,性能优化
826
+ * @param a1 集合 1
827
+ * @param a2 集合 2
828
+ * @returns 两集合无交集时返回 true
829
+ */
830
+ function isDisjointFrom(a1, a2) {
831
+ if (a1 instanceof Set && _hasNative(a1, "isDisjointFrom")) return a1.isDisjointFrom(_toSet(a2));
832
+ const set2 = _toSet(a2);
833
+ for (const item of a1) if (set2.has(item)) return false;
834
+ return true;
835
+ }
836
+ //#endregion
837
+ //#region src/id.ts
838
+ /** 雪花ID, 推荐在全局构造一个对象用于生成id */
839
+ var SnowflakeID = class SnowflakeID {
840
+ /**
841
+ * 构造函数
842
+ *
843
+ * @param machineId 机器标识,默认为1
844
+ * @param epoch 时间戳起始值,默认为1288834974657
845
+ * @param maxClockTolerance 允许的最大时钟回拨毫秒数,默认为5
846
+ */
847
+ constructor(machineId = 1, epoch = 1288834974657, maxClockTolerance = SnowflakeID.DEFAULT_MAX_CLOCK_TOLERANCE) {
848
+ this._version = 0;
849
+ if (!Number.isSafeInteger(machineId) || machineId < 0 || machineId > SnowflakeID.MAX_MACHINE_ID) throw new Error(`machineId must be a safe integer between 0 and ${SnowflakeID.MAX_MACHINE_ID}, got ${machineId}`);
850
+ if (!Number.isSafeInteger(epoch) || epoch < 0 || epoch > Date.now()) throw new Error(`epoch must be a non-negative safe integer not later than the current time, got ${epoch}`);
851
+ if (!Number.isSafeInteger(maxClockTolerance) || maxClockTolerance < 0) throw new Error(`maxClockTolerance must be a non-negative safe integer, got ${maxClockTolerance}`);
852
+ this.machineId = BigInt(machineId);
853
+ this.epoch = BigInt(epoch);
854
+ this.maxClockTolerance = maxClockTolerance;
855
+ const stateKey = `${this.epoch}:${this.machineId}`;
856
+ let state = SnowflakeID.STATES.get(stateKey);
857
+ if (state == null) {
858
+ state = {
859
+ lastTimestamp: 0n,
860
+ sequence: 0n
861
+ };
862
+ SnowflakeID.STATES.set(stateKey, state);
863
+ }
864
+ this._state = state;
865
+ }
866
+ get version() {
867
+ return this._version;
868
+ }
869
+ set version(version) {
870
+ if (!Number.isInteger(version) || version < 0 || version > 9) throw new Error(`version must be an integer between 0 and 9, got ${version}`);
871
+ this._version = version;
872
+ }
873
+ /**
874
+ * 生成雪花ID
875
+ *
876
+ * @returns 返回生成的唯一ID字符串
877
+ * @throws 如果时钟回拨超过容差阈值,抛出错误
878
+ */
879
+ generate() {
880
+ let cTimestamp = BigInt(Date.now());
881
+ if (cTimestamp < this._state.lastTimestamp) {
882
+ const drift = Number(this._state.lastTimestamp - cTimestamp);
883
+ if (drift > this.maxClockTolerance) throw new Error(`Clock moved backwards by ${drift}ms (tolerance: ${this.maxClockTolerance}ms)`);
884
+ cTimestamp = this._state.lastTimestamp;
885
+ }
886
+ let sequence;
887
+ if (cTimestamp === this._state.lastTimestamp) {
888
+ sequence = this._state.sequence + 1n & SnowflakeID.SEQUENCE_MASK;
889
+ if (sequence === 0n) cTimestamp += 1n;
890
+ } else sequence = 0n;
891
+ const timeDiff = cTimestamp - this.epoch;
892
+ if (timeDiff < 0n || timeDiff > SnowflakeID.TIMESTAMP_MASK) throw new Error(`Timestamp offset must be between 0 and ${SnowflakeID.TIMESTAMP_MASK}, got ${timeDiff}`);
893
+ this._state.lastTimestamp = cTimestamp;
894
+ this._state.sequence = sequence;
895
+ const idstr = (timeDiff << SnowflakeID.SHIFT_TIMESTAMP | this.machineId << SnowflakeID.SHIFT_MACHINE | sequence).toString().padStart(19, "0");
896
+ return `${this.version}${idstr}`;
897
+ }
898
+ parse(snowflakeID, epoch, includeVersion = true) {
899
+ if (typeof snowflakeID !== "string" || !/^\d+$/.test(snowflakeID)) throw new Error("snowflakeID must be a non-empty decimal string");
900
+ let version;
901
+ if (includeVersion) {
902
+ if (snowflakeID.length < 2) throw new Error("snowflakeID must contain a version and an ID value");
903
+ version = snowflakeID.substring(0, 1);
904
+ snowflakeID = snowflakeID.substring(1);
905
+ }
906
+ const epochTime = epoch ?? Number(this.epoch);
907
+ if (!Number.isSafeInteger(epochTime) || epochTime < 0) throw new Error(`epoch must be a non-negative safe integer, got ${epochTime}`);
908
+ const id = BigInt(snowflakeID);
909
+ if (id > SnowflakeID.MAX_ID) throw new Error(`snowflakeID value exceeds the 63-bit Snowflake range`);
910
+ const timeDiff = id >> SnowflakeID.SHIFT_TIMESTAMP & SnowflakeID.TIMESTAMP_MASK;
911
+ const flowTime = Number(timeDiff) + epochTime;
912
+ if (!Number.isSafeInteger(flowTime)) throw new Error("Parsed timestamp exceeds the JavaScript safe integer range");
913
+ const machineId = id >> SnowflakeID.SHIFT_MACHINE & SnowflakeID.MACHINE_MASK;
914
+ const sequence = id & SnowflakeID.SEQUENCE_MASK;
915
+ return {
916
+ value: snowflakeID,
917
+ timeOffset: timeDiff,
918
+ timestamp: flowTime,
919
+ machineId,
920
+ sequence,
921
+ epoch: epochTime,
922
+ version
923
+ };
924
+ }
925
+ };
926
+ SnowflakeID.SEQUENCE_MASK = 4095n;
927
+ SnowflakeID.TIMESTAMP_MASK = 2199023255551n;
928
+ SnowflakeID.MACHINE_MASK = 1023n;
929
+ SnowflakeID.SHIFT_TIMESTAMP = 22n;
930
+ SnowflakeID.SHIFT_MACHINE = 12n;
931
+ SnowflakeID.MAX_MACHINE_ID = 1023;
932
+ SnowflakeID.DEFAULT_MAX_CLOCK_TOLERANCE = 5;
933
+ SnowflakeID.MAX_ID = (1n << 63n) - 1n;
934
+ SnowflakeID.STATES = /* @__PURE__ */ new Map();
935
+ /** 将uuid转换为更简单的唯一标记id */
936
+ var ShortUUID = class {
937
+ /**
938
+ * 构造函数,用于初始化字母表
939
+ * @param {string} [alphabet] - 可选参数,用于指定自定义字母表
940
+ * 如果提供了alphabet参数,则将其设置为实例的字母表属性
941
+ * 如果未提供alphabet参数,则使用默认值
942
+ */
943
+ constructor(alphabet) {
944
+ this.alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
945
+ if (alphabet) this.alphabet = alphabet;
946
+ }
947
+ /**
948
+ * 将UUID字符串进行编码处理
949
+ * @param uuid - 需要编码的UUID字符串
950
+ * @returns 编码后的字符串
951
+ */
952
+ encode(uuid, alphabet) {
953
+ const uuidInt = this._uuidHexToInt(uuid);
954
+ return this._intToString(uuidInt, alphabet);
955
+ }
956
+ /**
957
+ * 解码短UUID字符串,将其转换为UUID整数和十六进制格式
958
+ * @param shortUUID - 需要解码的短UUID字符串
959
+ * @returns 返回包含UUID整数和十六进制格式的对象
960
+ */
961
+ decode(shortUUID, alphabet) {
962
+ const uuidInt = this._stringToInt(shortUUID, alphabet);
963
+ return {
964
+ uuidInt,
965
+ uuid: this._uuidIntToHex(uuidInt)
966
+ };
967
+ }
968
+ _stringToInt(str, alphabet) {
969
+ if (!alphabet) alphabet = this.alphabet;
970
+ const alphabetlen = BigInt(alphabet.length);
971
+ let result = BigInt(0);
972
+ let multiplier = BigInt(1);
973
+ const strlen = str.length;
974
+ for (let i = strlen - 1; i >= 0; i--) {
975
+ const char = str[i];
976
+ const index = alphabet.indexOf(char);
977
+ if (index === -1) throw new Error(`Character "${char}" not found in alphabet`);
978
+ result += BigInt(index) * multiplier;
979
+ multiplier *= alphabetlen;
980
+ }
981
+ return result;
982
+ }
983
+ _intToString(uuidInt, alphabet) {
984
+ if (!alphabet) alphabet = this.alphabet;
985
+ const alphabetlen = BigInt(alphabet.length);
986
+ let num = uuidInt;
987
+ const result = [];
988
+ while (num) {
989
+ const index = Number(num % alphabetlen);
990
+ num = num / alphabetlen;
991
+ result.push(alphabet.charAt(index));
992
+ }
993
+ return result.reverse().join("");
994
+ }
995
+ _uuidHexToInt(uuid) {
996
+ const uuidHex = uuid.replaceAll("-", "");
997
+ return BigInt(`0x${uuidHex}`);
998
+ }
999
+ _uuidIntToHex(uuidInt) {
1000
+ const uuidHexStr = uuidInt.toString(16);
1001
+ if (!/^[0-9a-fA-F]{32}$/.test(uuidHexStr)) throw new Error("Invalid UUID string (must be 32 hex characters)");
1002
+ return [
1003
+ uuidHexStr.slice(0, 8),
1004
+ uuidHexStr.slice(8, 12),
1005
+ uuidHexStr.slice(12, 16),
1006
+ uuidHexStr.slice(16, 20),
1007
+ uuidHexStr.slice(20)
1008
+ ].join("-");
1009
+ }
1010
+ };
1011
+ //#endregion
1012
+ //#region src/crypto.ts
1013
+ /**
1014
+ * 将原始的二进制数据转换为 Hex String
1015
+ * @param bf 待转换的原始数据
1016
+ * @param upper 是否需要转换为大写
1017
+ * @returns
1018
+ */
1019
+ function bufferToHex(bf, upper = false) {
1020
+ const u8Array = bf instanceof Uint8Array ? bf : new Uint8Array(bf);
1021
+ return Array.from(u8Array).map((b) => {
1022
+ let hx = b.toString(16).padStart(2, "0");
1023
+ return upper === true ? hx.toUpperCase() : hx;
1024
+ }).join("");
1025
+ }
1026
+ /**
1027
+ * 将16进制的数据转换为 UInt8Array
1028
+ * @param data 16进制的数据
1029
+ * @returns
1030
+ */
1031
+ function hexToBuffer(data) {
1032
+ const len = data.length / 2;
1033
+ const uint8Array = new Uint8Array(len);
1034
+ for (let i = 0; i < len; i++) {
1035
+ const byteHex = data.substring(i * 2, i * 2 + 2);
1036
+ const byte = parseInt(byteHex.toLocaleLowerCase(), 16);
1037
+ uint8Array[i] = byte;
1038
+ }
1039
+ return uint8Array;
1040
+ }
1041
+ /**
1042
+ * 将原始数据转换为 Base64 编码
1043
+ * @param bf 待转换的原始数据
1044
+ * @returns
1045
+ */
1046
+ function bufferToBase64(bf) {
1047
+ const u8Array = bf instanceof Uint8Array ? bf : new Uint8Array(bf);
1048
+ const hashArray = Array.from(u8Array);
1049
+ return globalThis.btoa(String.fromCharCode.apply(null, hashArray));
1050
+ }
1051
+ /**
1052
+ * 将 Base64 转换为 UInt8Array 数据
1053
+ * @param data
1054
+ * @returns
1055
+ */
1056
+ function base64ToBuffer(data) {
1057
+ return new Uint8Array(globalThis.atob(data).split("").map((char) => char.charCodeAt(0)));
1058
+ }
1059
+ /**
1060
+ * SHA 哈希算法
1061
+ * @param message 待进行 hash 的数据
1062
+ * @param upper 是否转换为大写, 默认为: false
1063
+ * @param algorithm hash算法, 支持: SHA-1、SHA-256、SHA-384、SHA-512; 默认为: SHA-256
1064
+ * @returns
1065
+ */
1066
+ async function sha(message, upper = false, algorithm = "SHA-256") {
1067
+ let msgBuffer = message;
1068
+ if (typeof message === "string") msgBuffer = new TextEncoder().encode(message);
1069
+ return bufferToHex(await globalThis.crypto.subtle.digest(algorithm || "SHA-256", msgBuffer), upper);
1070
+ }
1071
+ /**
1072
+ * 哈希算法
1073
+ * @param message 待进行 hash 的数据
1074
+ * @param upper 是否转换为大写, 默认为: false
1075
+ * @param algorithm hash算法, 支持: SHA-1、SHA-256、SHA-384、SHA-512; 默认为: SHA-256
1076
+ * @returns
1077
+ */
1078
+ async function hash(message, upper = false, algorithm = "SHA-256") {
1079
+ return sha(message, upper, algorithm);
1080
+ }
1081
+ /**
1082
+ * 使用 HMAC 算法计算消息的哈希值
1083
+ * @param message - 需要计算哈希的消息字符串
1084
+ * @param secret - 用于生成 HMAC 的密钥
1085
+ * @param algorithm - HMAC 使用的哈希算法,默认为 "SHA-256"
1086
+ * @param upper - 是否将结果转换为大写,默认为 false
1087
+ * @returns 返回十六进制格式的 HMAC 哈希值
1088
+ */
1089
+ async function hmacHash(message, secret, algorithm = "SHA-256", upper = false) {
1090
+ const encoder = new TextEncoder();
1091
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
1092
+ name: "HMAC",
1093
+ hash: { name: algorithm }
1094
+ }, false, ["sign"]);
1095
+ return bufferToHex(await crypto.subtle.sign("HMAC", key, encoder.encode(message)), upper);
1096
+ }
1097
+ function parseRsaKey(pem) {
1098
+ const pemHeader = "-----BEGIN PUBLIC KEY-----";
1099
+ const pemFooter = "-----END PUBLIC KEY-----";
1100
+ if (pem.indexOf(pemHeader) !== -1 && pem.indexOf(pemFooter) !== -1) pem = pem.substring(26, pem.indexOf(pemFooter)).trim();
1101
+ return pem;
1102
+ }
1103
+ /**
1104
+ * 导入上下文密钥
1105
+ * @param key 导入的密钥
1106
+ * @param algorithmName 算法名称
1107
+ * @param usages 该密钥可以用于哪些函数使用
1108
+ * @returns
1109
+ */
1110
+ async function importKey(key, algorithmName, usages, encoding = "hex") {
1111
+ let name = "AES-CBC";
1112
+ if (algorithmName == null) name = "AES-CBC";
1113
+ else if (algorithmName.toUpperCase() === "AES") name = "AES-CBC";
1114
+ else if (algorithmName.toUpperCase() === "RSA") name = "RSA-OAEP";
1115
+ name = name.toUpperCase();
1116
+ if (usages == null || usages.length === 0) usages = ["encrypt"];
1117
+ let format = "raw";
1118
+ let algorithm = { name };
1119
+ if (name.includes("RSA")) {
1120
+ format = "spki";
1121
+ algorithm.hash = { name: "SHA-256" };
1122
+ key = parseRsaKey(key);
1123
+ }
1124
+ const keyBuf = encoding === "base64" ? base64ToBuffer(key) : hexToBuffer(key);
1125
+ return Promise.all([globalThis.crypto.subtle.importKey(format, keyBuf, algorithm, false, usages), Promise.resolve({ name })]);
1126
+ }
1127
+ /**
1128
+ * 加密
1129
+ * @param algorithm 算法参数, 算法名称、向量等
1130
+ * @param key 算法密钥
1131
+ * @param message 待加密的数据
1132
+ * @param encode 解密后返回数据格式
1133
+ * @returns
1134
+ */
1135
+ async function encrypt(algorithm, key, message, encode = "hex") {
1136
+ if (typeof message === "string") message = new TextEncoder().encode(message);
1137
+ const encrypted = await globalThis.crypto.subtle.encrypt(algorithm, key, message);
1138
+ if (encode === "hex") return bufferToHex(encrypted);
1139
+ else if (encode === "hexUpper") return bufferToHex(encrypted, true);
1140
+ else if (encode === "base64") return bufferToBase64(encrypted);
1141
+ else return encrypted;
1142
+ }
1143
+ /**
1144
+ * 数据解密
1145
+ * @param algorithm 解密算法
1146
+ * @param key 解密密钥
1147
+ * @param message 加密后的数据
1148
+ * @returns
1149
+ */
1150
+ async function decrypt(algorithm, key, message) {
1151
+ const decrypted = await globalThis.crypto.subtle.decrypt(algorithm, key, message);
1152
+ return new TextDecoder("utf-8").decode(decrypted);
1153
+ }
1154
+ /**
1155
+ * AES 加密
1156
+ * @param message 待加密的数据
1157
+ * @param key 加解密密钥
1158
+ * @param encode 加密后的数据转换的形式, hex - 转换为16进制字符串, hexUpper - 转换为16进制且大写, base64 - 转换为 base64 形式
1159
+ * @param iv 加解密向量
1160
+ * @returns [加密数据,向量]
1161
+ */
1162
+ async function aesEncrypt(message, key, encode = "hex", iv = null) {
1163
+ let ciphertext = "";
1164
+ let resIv = "";
1165
+ const [cryptoKey, algorithm] = await importKey(key, "aes", ["encrypt"]);
1166
+ if (iv == null) iv = globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
1167
+ else if (typeof iv === "string") iv = hexToBuffer(iv);
1168
+ ciphertext = await encrypt({
1169
+ ...algorithm,
1170
+ iv
1171
+ }, cryptoKey, message, encode);
1172
+ resIv = bufferToHex(iv);
1173
+ return {
1174
+ ciphertext,
1175
+ iv: resIv,
1176
+ key
1177
+ };
1178
+ }
1179
+ /**
1180
+ * 根据加密后的数据类型使用对应函数最终转换为 UInt8Array
1181
+ * @param message 原始加密后的数据
1182
+ * @param type 类型: hex | base64
1183
+ * @returns
1184
+ */
1185
+ function parseEncryptData(message, type) {
1186
+ let input;
1187
+ if (typeof message === "string") {
1188
+ if (type === "hex" || type === "hexUpper") input = hexToBuffer(message);
1189
+ else input = base64ToBuffer(message);
1190
+ } else input = message;
1191
+ return input;
1192
+ }
1193
+ /**
1194
+ * AES 解密
1195
+ * @param message 加密后的数据
1196
+ * @param key 解密密钥
1197
+ * @param iv 向量
1198
+ * @param encode 加密后数据的形式: hex | base64
1199
+ * @returns
1200
+ */
1201
+ async function aesDecrypt(message, key, iv, encode = "hex") {
1202
+ const [cryptoKey, algorithm] = await importKey(key, "aes", ["decrypt"]);
1203
+ const input = parseEncryptData(message, encode);
1204
+ return await decrypt({
1205
+ ...algorithm,
1206
+ iv: hexToBuffer(iv)
1207
+ }, cryptoKey, input);
1208
+ }
1209
+ /**
1210
+ * RSA 加密
1211
+ * @param key 公钥
1212
+ * @param message 待加密数据
1213
+ * @param encode 返回类型
1214
+ * @returns
1215
+ */
1216
+ async function rsaEncrypt(message, publicKey, encode = "hex") {
1217
+ const [cryptoKey, algorithm] = await importKey(publicKey, "rsa", ["encrypt"], "base64");
1218
+ return await encrypt(algorithm, cryptoKey, message, encode);
1219
+ }
1220
+ /**
1221
+ * RSA 解密
1222
+ * @param key 私钥, 根据私钥解密
1223
+ * @param message 加密后的数据
1224
+ * @param encode 加密后的数据形式
1225
+ * @returns
1226
+ */
1227
+ async function rsaDecrypt(privateKey, message, encode = "hex") {
1228
+ const [cryptoKey, algorithm] = await importKey(privateKey, "rsa", ["decrypt"]);
1229
+ return await decrypt({ ...algorithm }, cryptoKey, parseEncryptData(message, encode));
1230
+ }
1231
+ //#endregion
1232
+ //#region src/base-codec.ts
1233
+ /** 通用进制编码解码器,兼容 Node.js 和浏览器环境 */
1234
+ const BASE62_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
1235
+ const BASE32_ALPHABET = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ";
1236
+ var BaseCodec = class {
1237
+ constructor(name, alphabet) {
1238
+ this.alphabet = alphabet;
1239
+ this.base = BigInt(alphabet.length);
1240
+ this.name = name;
1241
+ }
1242
+ intToBytes(value) {
1243
+ if (value < 0n) throw new RangeError("Only supports non-negative integers");
1244
+ if (value === 0n) return new Uint8Array([0]);
1245
+ const bytes = [];
1246
+ while (value > 0n) {
1247
+ bytes.unshift(Number(value & 255n));
1248
+ value >>= 8n;
1249
+ }
1250
+ return new Uint8Array(bytes);
1251
+ }
1252
+ bytesToInt(bytes) {
1253
+ let value = 0n;
1254
+ for (const byte of bytes) value = value << 8n | BigInt(byte);
1255
+ return value;
1256
+ }
1257
+ concatBytes(...arrays) {
1258
+ const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);
1259
+ const result = new Uint8Array(totalLength);
1260
+ let offset = 0;
1261
+ for (const arr of arrays) {
1262
+ result.set(arr, offset);
1263
+ offset += arr.length;
1264
+ }
1265
+ return result;
1266
+ }
1267
+ bytesToUtf8(bytes) {
1268
+ return new TextDecoder().decode(bytes);
1269
+ }
1270
+ utf8ToBytes(str) {
1271
+ return new TextEncoder().encode(str);
1272
+ }
1273
+ bytesToHex(bytes) {
1274
+ let hex = "";
1275
+ for (const byte of bytes) hex += byte.toString(16).padStart(2, "0");
1276
+ return hex;
1277
+ }
1278
+ encodeFromBytes(input) {
1279
+ if (input.length === 0) return "";
1280
+ let leadingZeros = 0;
1281
+ while (leadingZeros < input.length && input[leadingZeros] === 0) leadingZeros++;
1282
+ let value = this.bytesToInt(input);
1283
+ let encoded = "";
1284
+ while (value > 0n) {
1285
+ encoded = this.alphabet.charAt(Number(value % this.base)) + encoded;
1286
+ value /= this.base;
1287
+ }
1288
+ return this.alphabet.charAt(0).repeat(leadingZeros) + encoded;
1289
+ }
1290
+ decodeToBytes(input) {
1291
+ if (input.length === 0) return /* @__PURE__ */ new Uint8Array(0);
1292
+ let leadingZeros = 0;
1293
+ while (leadingZeros < input.length && input.charAt(leadingZeros) === this.alphabet.charAt(0)) leadingZeros++;
1294
+ let value = 0n;
1295
+ for (const char of input.slice(leadingZeros)) {
1296
+ const index = this.alphabet.indexOf(char);
1297
+ if (index === -1) throw new TypeError(`Invalid ${this.name} character: ${char}`);
1298
+ value = value * this.base + BigInt(index);
1299
+ }
1300
+ const valueBytes = value === 0n ? /* @__PURE__ */ new Uint8Array(0) : this.intToBytes(value);
1301
+ return this.concatBytes(new Uint8Array(leadingZeros), valueBytes);
1302
+ }
1303
+ /** 将非负 bigint 编码为字符串 */
1304
+ encodeFromInt(value) {
1305
+ return this.encodeFromBytes(this.intToBytes(value));
1306
+ }
1307
+ /** 先将字符串转换为 UTF-8 字节,再编码 */
1308
+ encodeFromStr(value) {
1309
+ return this.encodeFromBytes(this.utf8ToBytes(value));
1310
+ }
1311
+ /** 根据参数类型将 bigint 或字符串编码 */
1312
+ encode(value) {
1313
+ return typeof value === "bigint" ? this.encodeFromInt(value) : this.encodeFromStr(value);
1314
+ }
1315
+ /** 解码为非负 bigint */
1316
+ decodeToInt(value) {
1317
+ return this.bytesToInt(this.decodeToBytes(value));
1318
+ }
1319
+ /** 解码为 UTF-8 字符串 */
1320
+ decodeToStr(value) {
1321
+ return this.bytesToUtf8(this.decodeToBytes(value));
1322
+ }
1323
+ /** 默认解码为 UTF-8 字符串 */
1324
+ decode(value) {
1325
+ return this.decodeToStr(value);
1326
+ }
1327
+ /** 将字节数组转换为十六进制字符串 */
1328
+ bufferToHex(value) {
1329
+ return this.bytesToHex(value);
1330
+ }
1331
+ };
1332
+ var Base32 = class extends BaseCodec {
1333
+ constructor() {
1334
+ super("Base32", BASE32_ALPHABET);
1335
+ }
1336
+ };
1337
+ var Base62 = class extends BaseCodec {
1338
+ constructor() {
1339
+ super("Base62", BASE62_ALPHABET);
1340
+ }
1341
+ };
1342
+ //#endregion
1343
+ //#region src/validator.ts
1344
+ /**
1345
+ * 数据验证器
1346
+ */
1347
+ const defaultMsgs = {
1348
+ mobile: "请输入正确的手机号",
1349
+ same: "两次输入不一致",
1350
+ required: "%s为必填字段",
1351
+ default: "请输入正确的数据"
1352
+ };
1353
+ const ruleRegexs = {
1354
+ /** 验证跟其余数据相等的正则,一般用于验证再次输入密码 */
1355
+ same: /^same:(.+)$/i,
1356
+ /** 验证手机号的正则表达式 */
1357
+ mobile: /^1[3456789]\d{9}$/,
1358
+ /** 非空验证的正则表达式 */
1359
+ required: /\S/
1360
+ };
1361
+ const ruleFns = {
1362
+ /** 验证相等 */
1363
+ same(val1, val2) {
1364
+ return val2 === val1;
1365
+ },
1366
+ /** 正则匹配 */
1367
+ pattern(regex, val) {
1368
+ if (val == null) return false;
1369
+ return regex.test(String(val));
1370
+ }
1371
+ };
1372
+ var ValidateError = class extends Error {
1373
+ constructor(detail, key, message) {
1374
+ super(message);
1375
+ this.name = "ValidateError";
1376
+ this.key = key;
1377
+ this.detail = detail;
1378
+ }
1379
+ };
1380
+ /**
1381
+ * 数据验证器
1382
+ */
1383
+ var Validator = class {
1384
+ /**
1385
+ * 构造数据验证转换器
1386
+ *
1387
+ * See {@link https://gitee.com/towardly/ph/wikis/utils/validator|Validator文档}.
1388
+ *
1389
+ * @param schemas 配置验证转换规则
1390
+ *
1391
+ * @example
1392
+ *
1393
+ * const validator = new Validator([
1394
+ * { key: 'mobile', rules: ['required', 'mobile'] },
1395
+ * { key: 'code': rules: /^\d{6}$/, message: '请输入正确的验证码' },
1396
+ * { key: 'confirmPassword', rules: ['required', 'same:password'] }
1397
+ * ])
1398
+ * // 验证某一个字段
1399
+ * validator.validateKey().then(res => {})
1400
+ */
1401
+ constructor(schemas) {
1402
+ this.setSchemas(schemas);
1403
+ }
1404
+ addSchemas(schemas) {
1405
+ for (let schema of schemas) this.addSchema(schema);
1406
+ }
1407
+ addSchema(schema) {
1408
+ if (schema.key in this.rules) this.rules[schema.key] = [...this.rules[schema.key], ...this._parseSchemaRules(schema)];
1409
+ else this.rules[schema.key] = this._parseSchemaRules(schema);
1410
+ }
1411
+ setSchema(schema) {
1412
+ this.rules[schema.key] = this._parseSchemaRules(schema);
1413
+ }
1414
+ setSchemas(schemas) {
1415
+ this.rules = {};
1416
+ for (let schema of schemas) this.rules[schema.key] = this._parseSchemaRules(schema);
1417
+ }
1418
+ removeSchema(key) {
1419
+ delete this.rules[key];
1420
+ }
1421
+ hasSchema(key) {
1422
+ return key in this.rules;
1423
+ }
1424
+ /**
1425
+ * 进行数据验证
1426
+ * @param data 待验证的数据
1427
+ * @param all 是否全部验证, false - 只要验证错误一个则停止验证
1428
+ * @returns
1429
+ */
1430
+ validate(data, all = false) {
1431
+ const detail = {};
1432
+ let currentKey = void 0;
1433
+ let currentMessage = void 0;
1434
+ for (let key in this.rules) {
1435
+ let errMsg = this._validateRule(this.rules[key], data[key], data);
1436
+ if (errMsg !== "") {
1437
+ errMsg = errMsg.replace("%s", key);
1438
+ detail[key] = errMsg;
1439
+ currentKey = key;
1440
+ currentMessage = errMsg;
1441
+ if (!all) break;
1442
+ }
1443
+ }
1444
+ if (currentKey == null) return true;
1445
+ throw new ValidateError(detail, currentKey, currentMessage);
1446
+ }
1447
+ /**
1448
+ * 只验证指定 key 的数据格式
1449
+ * @param key 指定待验证的 key
1450
+ * @param value 待验证的数据
1451
+ * @param data 原始数据,当验证确认密码时需要使用
1452
+ */
1453
+ validateKey(key, value, data) {
1454
+ let keyRules = this.rules[key];
1455
+ if (keyRules == null) return {
1456
+ key,
1457
+ value
1458
+ };
1459
+ let errMsg = this._validateRule(keyRules, value, data);
1460
+ if (errMsg !== "") {
1461
+ errMsg = errMsg.replace("%s", key);
1462
+ throw new ValidateError({ [key]: errMsg }, key, errMsg);
1463
+ }
1464
+ return {
1465
+ key,
1466
+ value
1467
+ };
1468
+ }
1469
+ _validateRule(rules, value, data) {
1470
+ let errMsg = "";
1471
+ for (let rule of rules) {
1472
+ if (!rule) continue;
1473
+ if (rule.rule === "required") {
1474
+ if (value == null || !ruleFns.pattern(ruleRegexs.required, value)) errMsg = rule.message;
1475
+ } else if (typeof rule.rule === "function") {
1476
+ if (!rule.rule(value)) errMsg = rule.message;
1477
+ } else if (rule.sameKey != null) {
1478
+ if (data != null) {
1479
+ if (!ruleFns.same(value, data[rule.sameKey])) errMsg = rule.message;
1480
+ }
1481
+ } else if (rule && !ruleFns.pattern(rule.rule, value)) errMsg = rule.message;
1482
+ if (errMsg !== "") break;
1483
+ }
1484
+ return errMsg;
1485
+ }
1486
+ _parseSchemaRules(schema) {
1487
+ let rules = [];
1488
+ let rule = schema.rules;
1489
+ if (schema.required === true) rules.push(...this._parseStringRule("required", schema.message));
1490
+ if (rule != null) {
1491
+ if (typeof rule === "string") rules.push(...this._parseStringRule(rule, schema.message));
1492
+ else if (rule instanceof Array) for (let ruleItem of rule) if (typeof ruleItem === "string") rules.push(...this._parseStringRule(ruleItem, schema.message));
1493
+ else if (ruleItem instanceof RegExp || typeof ruleItem === "function") rules.push({
1494
+ rule: ruleItem,
1495
+ message: schema.message || defaultMsgs["default"]
1496
+ });
1497
+ else {
1498
+ const emessage = ruleItem.message || schema.message || defaultMsgs["default"];
1499
+ if (typeof ruleItem.rule === "string") rules.push(...this._parseStringRule(ruleItem.rule, emessage));
1500
+ else rules.push({
1501
+ rule: ruleItem.rule,
1502
+ message: emessage
1503
+ });
1504
+ }
1505
+ else rules.push({
1506
+ rule,
1507
+ message: defaultMsgs["default"]
1508
+ });
1509
+ }
1510
+ return rules;
1511
+ }
1512
+ _parseStringRule(rule, ruleErrMsg) {
1513
+ let rules = [];
1514
+ let trule = rule.split("|");
1515
+ for (let r of trule) {
1516
+ let message = ruleErrMsg;
1517
+ let rrule = null;
1518
+ let sameKey;
1519
+ if (r === "required") {
1520
+ rrule = "required";
1521
+ message = message || ruleErrMsg || defaultMsgs.required;
1522
+ } else if (ruleRegexs.same.test(r)) {
1523
+ let m = r.match(ruleRegexs.same);
1524
+ if (m != null) {
1525
+ rrule = ruleRegexs.same;
1526
+ sameKey = m[1];
1527
+ message = message || defaultMsgs["same"];
1528
+ }
1529
+ } else if (Object.hasOwn(ruleRegexs, r)) {
1530
+ rrule = ruleRegexs[r];
1531
+ message = message || defaultMsgs[r];
1532
+ }
1533
+ if (rrule) rules.push({
1534
+ rule: rrule,
1535
+ message,
1536
+ sameKey
1537
+ });
1538
+ }
1539
+ return rules;
1540
+ }
1541
+ };
1542
+ //#endregion
1543
+ export { Base32, Base62, ShortUUID, SnowflakeID, Validator, add, adjust, aesDecrypt, aesEncrypt, bufferToHex, dateOf, difference, endOf, format, formatData, formatMoney, getJSONValue, hash, hmacHash, intersection, isBlank, isBoolean, isDisjointFrom, isNumeric, isSubsetOf, isSupersetOf, order, parse, random, reverseStr, rgbToHex, round, rsaDecrypt, rsaEncrypt, sha, shieldBankCard, shieldEmail, shieldIdCard, shieldMobile, shieldName, shieldString, shieldWithLimit, smartShield, smartSummary, snakeCaseStyle, startOf, symmetricDifference, timestamp, toHex, toHsv, toRgb, union };