@anjianshi/utils 1.2.6 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/env-node/env-reader.d.ts +12 -0
  2. package/env-node/env-reader.js +31 -0
  3. package/env-node/{logging.d.ts → logging/handlers.d.ts} +1 -6
  4. package/env-node/{logging.js → logging/handlers.js} +1 -12
  5. package/env-node/logging/index.d.ts +11 -0
  6. package/env-node/logging/index.js +14 -0
  7. package/env-node/typeorm/adapt-logging.d.ts +11 -0
  8. package/env-node/typeorm/adapt-logging.js +42 -0
  9. package/env-node/typeorm/index.d.ts +31 -0
  10. package/env-node/typeorm/index.js +39 -0
  11. package/lang/index.d.ts +1 -0
  12. package/lang/index.js +1 -0
  13. package/lang/may-success.d.ts +40 -0
  14. package/lang/may-success.js +27 -0
  15. package/lang/types.d.ts +12 -43
  16. package/lang/types.js +0 -13
  17. package/logging/index.js +1 -1
  18. package/md5.d.ts +30 -0
  19. package/md5.js +309 -0
  20. package/package.json +3 -1
  21. package/src/env-node/env-reader.ts +33 -0
  22. package/src/env-node/{logging.ts → logging/handlers.ts} +1 -21
  23. package/src/env-node/logging/index.ts +16 -0
  24. package/src/env-node/typeorm/adapt-logging.ts +54 -0
  25. package/src/env-node/typeorm/index.ts +62 -0
  26. package/src/lang/index.ts +1 -0
  27. package/src/lang/may-success.ts +57 -0
  28. package/src/lang/types.ts +14 -59
  29. package/src/logging/index.ts +1 -1
  30. package/src/md5.ts +319 -0
  31. package/src/url.ts +48 -55
  32. package/src/validators/array.ts +62 -0
  33. package/src/validators/base.ts +49 -0
  34. package/src/validators/boolean.ts +24 -0
  35. package/src/validators/factories.ts +47 -0
  36. package/src/validators/index.ts +9 -0
  37. package/src/validators/number.ts +43 -0
  38. package/src/validators/object.ts +70 -0
  39. package/src/validators/string.ts +55 -0
  40. package/url.d.ts +31 -15
  41. package/url.js +22 -28
  42. package/validators/array.d.ts +20 -0
  43. package/validators/array.js +44 -0
  44. package/validators/base.d.ts +26 -0
  45. package/validators/base.js +28 -0
  46. package/validators/boolean.d.ts +4 -0
  47. package/validators/boolean.js +28 -0
  48. package/validators/factories.d.ts +18 -0
  49. package/validators/factories.js +41 -0
  50. package/validators/index.d.ts +7 -0
  51. package/validators/index.js +7 -0
  52. package/validators/number.d.ts +15 -0
  53. package/validators/number.js +32 -0
  54. package/validators/object.d.ts +20 -0
  55. package/validators/object.js +52 -0
  56. package/validators/string.d.ts +17 -0
  57. package/validators/string.js +35 -0
package/src/lang/types.ts CHANGED
@@ -16,24 +16,16 @@ export function tuple<T extends unknown[]>(...elements: T) {
16
16
  return elements
17
17
  }
18
18
 
19
- /**
20
- * 将一个对象中的指定 key 设为必须的
21
- */
19
+ /** 将一个对象中的指定 key 设为必须的 */
22
20
  export type RequiredFields<T, K extends keyof T> = Omit<T, K> & Pick<Required<T>, K>
23
21
 
24
- /**
25
- * 将一个对象中的指定 key 设为非必须的
26
- */
22
+ /** 将一个对象中的指定 key 设为非必须的 */
27
23
  export type OptionalFields<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
28
24
 
29
- /**
30
- * 用 ReplaceT 中的字段定义代替 T 中的
31
- */
25
+ /** 用 ReplaceT 中的字段定义代替 T 中的 */
32
26
  export type ReplaceFields<T, ReplaceT> = Omit<T, keyof ReplaceT> & ReplaceT
33
27
 
34
- /**
35
- * 获取一个对象所有 value 的集合
36
- */
28
+ /** 获取一个对象所有 value 的集合 */
37
29
  export type ValueOf<T> = T extends { [_ in keyof T]: infer U } ? U : never
38
30
 
39
31
  /**
@@ -49,60 +41,25 @@ export type KnownKeys<T> = ValueOf<{
49
41
  [K in keyof T]: string extends K ? never : number extends K ? never : K
50
42
  }>
51
43
 
52
- /**
53
- * 排除对象中指定类型的项目
54
- */
44
+ /** 排除对象中指定类型的项目 */
55
45
  export type ExcludePropertiesOfType<T, ExcludeValueT> = Pick<
56
46
  T,
57
47
  { [K in keyof T]: T[K] extends ExcludeValueT ? never : K }[keyof T]
58
48
  >
59
49
 
60
50
  /**
61
- * 排除对象的方法(仅保留属性)
51
+ * 生成不包含指定 key 的类型
52
+ * 与 Omit<T, Keys> 的区别是
62
53
  */
54
+ export type ExcludeKeys<T, ExcludeKeys> = Pick<
55
+ T,
56
+ { [K in keyof T]: K extends ExcludeKeys ? never : K }[keyof T]
57
+ >
58
+
59
+ /** 排除对象的方法(仅保留属性) */
63
60
  // eslint-disable-next-line @typescript-eslint/ban-types
64
61
  export type ExcluceMethods<T> = ExcludePropertiesOfType<T, Function>
65
62
 
66
- /**
67
- * 所有“可能失败”的操作都可使用此类型作为返回值
68
- */
69
- export interface Success<T = void> {
70
- success: true
71
- data: T
72
- }
73
- export interface Failed<ET = string> {
74
- success: false
75
- error: ET
76
- code?: number | string
77
- }
78
- export type MaySuccess<T = void, ET = string> = Success<T> | Failed<ET>
79
-
80
- function success(): Success
81
- function success<T>(data: T): Success<T>
82
- function success<T = void>(data?: T) {
83
- return { success: true, data }
84
- }
85
- export { success }
86
-
87
- export function failed<ET>(error: ET, code?: number | string): Failed<ET> {
88
- return { success: false, error, code }
89
- }
90
-
91
- /**
92
- * 若传入值为 success,格式化其 data;否则原样返回错误
93
- * 支持传入会返回 MaySuccess 的 Promise
94
- */
95
- function formatSuccess<T, ET, FT>(item: MaySuccess<T, ET>, formatter: (data: T) => FT): MaySuccess<FT, ET> // prettier-ignore
96
- function formatSuccess<T, ET, FT>(item: Promise<MaySuccess<T, ET>>, formatter: (data: T) => FT): Promise<MaySuccess<FT, ET>> // prettier-ignore
97
- function formatSuccess<T, ET, FT>(
98
- item: MaySuccess<T, ET> | Promise<MaySuccess<T, ET>>,
99
- formatter: (data: T) => FT
100
- ) {
101
- if ('then' in item) return item.then(finalItem => formatSuccess(finalItem, formatter))
102
- return item.success ? { ...item, data: formatter(item.data) } : item
103
- }
104
- export { formatSuccess }
105
-
106
63
  /**
107
64
  * 确认变量是否有值
108
65
  * 注意:空字符串和数字 0 也会判定为没有值
@@ -118,9 +75,7 @@ function truthy<T>(value: T | string | number | boolean | null | undefined) {
118
75
  }
119
76
  export { truthy }
120
77
 
121
- /**
122
- * 定义 JSON 数据
123
- */
78
+ /** 定义 JSON 数据 */
124
79
  export type JSONData = number | boolean | string | null | JSONData[] | { [key: string]: JSONData }
125
80
 
126
81
  /**
@@ -44,7 +44,7 @@ export class Logger {
44
44
 
45
45
  static getRealLevel(raw: LogLevel | string) {
46
46
  if (typeof raw === 'string') {
47
- raw = raw.toLocaleLowerCase()
47
+ raw = raw.toLowerCase()
48
48
  if (logLevelMap[raw] === undefined) throw new Error('Not supported log level: ' + raw)
49
49
  return logLevelMap[raw]!
50
50
  }
package/src/md5.ts ADDED
@@ -0,0 +1,319 @@
1
+ /* eslint-disable no-multi-assign, @typescript-eslint/restrict-plus-operands */
2
+ /**
3
+ * MD5 算法来自:https://github.com/emn178/js-md5
4
+ */
5
+
6
+ export function md5(content: string | ArrayBuffer) {
7
+ const md5 = new MD5()
8
+ md5.update(content)
9
+ return md5.hex()
10
+ }
11
+
12
+ // -------------------------------------------------------------------
13
+
14
+ /**
15
+ * 使用方法:
16
+ * const md5 = new MD5()
17
+ * md5.update(xxx) // 对于大文件,可以拆分开,多次调用 md5.update()
18
+ * const hash = md5.hex()
19
+ */
20
+ export class MD5 {
21
+ private readonly buffer8: Uint8Array
22
+ private readonly blocks: Uint32Array
23
+
24
+ private h0 = 0
25
+ private h1 = 0
26
+ private h2 = 0
27
+ private h3 = 0
28
+ private start = 0
29
+ private bytes = 0
30
+ private hBytes = 0
31
+ private lastByteIndex = 0
32
+
33
+ private finalized = false
34
+ private hashed = false
35
+ private first = true
36
+
37
+ constructor() {
38
+ const buffer = new ArrayBuffer(68)
39
+ this.buffer8 = new Uint8Array(buffer)
40
+ this.blocks = new Uint32Array(buffer)
41
+ }
42
+
43
+ update(message: string | number[] | Uint8Array | ArrayBuffer) {
44
+ if (this.finalized) return
45
+
46
+ if (message instanceof ArrayBuffer) {
47
+ message = new Uint8Array(message)
48
+ }
49
+ if (typeof message !== 'string' && !Array.isArray(message) && !ArrayBuffer.isView(message)) {
50
+ throw new Error('input is invalid type')
51
+ }
52
+
53
+ const length = message.length
54
+ const { blocks, buffer8 } = this
55
+
56
+ let code: number
57
+ let index = 0
58
+ let i: number
59
+
60
+ while (index < length) {
61
+ if (this.hashed) {
62
+ this.hashed = false
63
+ blocks[0] = blocks[16]!
64
+ // prettier-ignore
65
+ blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] =
66
+ blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] =
67
+ blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0
68
+ }
69
+
70
+ if (typeof message !== 'string') {
71
+ for (i = this.start; index < length && i < 64; ++index) {
72
+ buffer8[i++] = message[index]!
73
+ }
74
+ } else {
75
+ for (i = this.start; index < length && i < 64; ++index) {
76
+ code = message.charCodeAt(index)
77
+ if (code < 0x80) {
78
+ buffer8[i++] = code
79
+ } else if (code < 0x800) {
80
+ buffer8[i++] = 0xc0 | (code >> 6)
81
+ buffer8[i++] = 0x80 | (code & 0x3f)
82
+ } else if (code < 0xd800 || code >= 0xe000) {
83
+ buffer8[i++] = 0xe0 | (code >> 12)
84
+ buffer8[i++] = 0x80 | ((code >> 6) & 0x3f)
85
+ buffer8[i++] = 0x80 | (code & 0x3f)
86
+ } else {
87
+ code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff))
88
+ buffer8[i++] = 0xf0 | (code >> 18)
89
+ buffer8[i++] = 0x80 | ((code >> 12) & 0x3f)
90
+ buffer8[i++] = 0x80 | ((code >> 6) & 0x3f)
91
+ buffer8[i++] = 0x80 | (code & 0x3f)
92
+ }
93
+ }
94
+ }
95
+ this.lastByteIndex = i
96
+ this.bytes += i - this.start
97
+ if (i >= 64) {
98
+ this.start = i - 64
99
+ this.hash()
100
+ this.hashed = true
101
+ } else {
102
+ this.start = i
103
+ }
104
+ }
105
+ if (this.bytes > 4294967295) {
106
+ this.hBytes += (this.bytes / 4294967296) << 0
107
+ this.bytes = this.bytes % 4294967296
108
+ }
109
+ }
110
+
111
+ private finalize() {
112
+ if (this.finalized) return
113
+ this.finalized = true
114
+ const { blocks } = this
115
+ const i = this.lastByteIndex
116
+ const EXTRA = [128, 32768, 8388608, -2147483648]
117
+ blocks[i >> 2] |= EXTRA[i & 3]!
118
+ if (i >= 56) {
119
+ if (!this.hashed) this.hash()
120
+ blocks[0] = blocks[16]!
121
+ // prettier-ignore
122
+ blocks[16] = blocks[1] = blocks[2] = blocks[3] = blocks[4] = blocks[5] =
123
+ blocks[6] = blocks[7] = blocks[8] = blocks[9] = blocks[10] =
124
+ blocks[11] = blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0
125
+ }
126
+ blocks[14] = this.bytes << 3
127
+ blocks[15] = (this.hBytes << 3) | (this.bytes >>> 29)
128
+ this.hash()
129
+ }
130
+
131
+ private hash() {
132
+ let a: number, b: number, c: number, d: number, bc: number, da: number
133
+ const { blocks } = this
134
+
135
+ if (this.first) {
136
+ a = blocks[0]! - 680876937
137
+ a = (((a << 7) | (a >>> 25)) - 271733879) << 0
138
+ d = (-1732584194 ^ (a & 2004318071)) + blocks[1]! - 117830708
139
+ d = (((d << 12) | (d >>> 20)) + a) << 0
140
+ c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2]! - 1126478375
141
+ c = (((c << 17) | (c >>> 15)) + d) << 0
142
+ b = (a ^ (c & (d ^ a))) + blocks[3]! - 1316259209
143
+ b = (((b << 22) | (b >>> 10)) + c) << 0
144
+ } else {
145
+ a = this.h0
146
+ b = this.h1
147
+ c = this.h2
148
+ d = this.h3
149
+ a += (d ^ (b & (c ^ d))) + blocks[0]! - 680876936
150
+ a = (((a << 7) | (a >>> 25)) + b) << 0
151
+ d += (c ^ (a & (b ^ c))) + blocks[1]! - 389564586
152
+ d = (((d << 12) | (d >>> 20)) + a) << 0
153
+ c += (b ^ (d & (a ^ b))) + blocks[2]! + 606105819
154
+ c = (((c << 17) | (c >>> 15)) + d) << 0
155
+ b += (a ^ (c & (d ^ a))) + blocks[3]! - 1044525330
156
+ b = (((b << 22) | (b >>> 10)) + c) << 0
157
+ }
158
+
159
+ a += (d ^ (b & (c ^ d))) + blocks[4]! - 176418897
160
+ a = (((a << 7) | (a >>> 25)) + b) << 0
161
+ d += (c ^ (a & (b ^ c))) + blocks[5]! + 1200080426
162
+ d = (((d << 12) | (d >>> 20)) + a) << 0
163
+ c += (b ^ (d & (a ^ b))) + blocks[6]! - 1473231341
164
+ c = (((c << 17) | (c >>> 15)) + d) << 0
165
+ b += (a ^ (c & (d ^ a))) + blocks[7]! - 45705983
166
+ b = (((b << 22) | (b >>> 10)) + c) << 0
167
+ a += (d ^ (b & (c ^ d))) + blocks[8]! + 1770035416
168
+ a = (((a << 7) | (a >>> 25)) + b) << 0
169
+ d += (c ^ (a & (b ^ c))) + blocks[9]! - 1958414417
170
+ d = (((d << 12) | (d >>> 20)) + a) << 0
171
+ c += (b ^ (d & (a ^ b))) + blocks[10]! - 42063
172
+ c = (((c << 17) | (c >>> 15)) + d) << 0
173
+ b += (a ^ (c & (d ^ a))) + blocks[11]! - 1990404162
174
+ b = (((b << 22) | (b >>> 10)) + c) << 0
175
+ a += (d ^ (b & (c ^ d))) + blocks[12]! + 1804603682
176
+ a = (((a << 7) | (a >>> 25)) + b) << 0
177
+ d += (c ^ (a & (b ^ c))) + blocks[13]! - 40341101
178
+ d = (((d << 12) | (d >>> 20)) + a) << 0
179
+ c += (b ^ (d & (a ^ b))) + blocks[14]! - 1502002290
180
+ c = (((c << 17) | (c >>> 15)) + d) << 0
181
+ b += (a ^ (c & (d ^ a))) + blocks[15]! + 1236535329
182
+ b = (((b << 22) | (b >>> 10)) + c) << 0
183
+ a += (c ^ (d & (b ^ c))) + blocks[1]! - 165796510
184
+ a = (((a << 5) | (a >>> 27)) + b) << 0
185
+ d += (b ^ (c & (a ^ b))) + blocks[6]! - 1069501632
186
+ d = (((d << 9) | (d >>> 23)) + a) << 0
187
+ c += (a ^ (b & (d ^ a))) + blocks[11]! + 643717713
188
+ c = (((c << 14) | (c >>> 18)) + d) << 0
189
+ b += (d ^ (a & (c ^ d))) + blocks[0]! - 373897302
190
+ b = (((b << 20) | (b >>> 12)) + c) << 0
191
+ a += (c ^ (d & (b ^ c))) + blocks[5]! - 701558691
192
+ a = (((a << 5) | (a >>> 27)) + b) << 0
193
+ d += (b ^ (c & (a ^ b))) + blocks[10]! + 38016083
194
+ d = (((d << 9) | (d >>> 23)) + a) << 0
195
+ c += (a ^ (b & (d ^ a))) + blocks[15]! - 660478335
196
+ c = (((c << 14) | (c >>> 18)) + d) << 0
197
+ b += (d ^ (a & (c ^ d))) + blocks[4]! - 405537848
198
+ b = (((b << 20) | (b >>> 12)) + c) << 0
199
+ a += (c ^ (d & (b ^ c))) + blocks[9]! + 568446438
200
+ a = (((a << 5) | (a >>> 27)) + b) << 0
201
+ d += (b ^ (c & (a ^ b))) + blocks[14]! - 1019803690
202
+ d = (((d << 9) | (d >>> 23)) + a) << 0
203
+ c += (a ^ (b & (d ^ a))) + blocks[3]! - 187363961
204
+ c = (((c << 14) | (c >>> 18)) + d) << 0
205
+ b += (d ^ (a & (c ^ d))) + blocks[8]! + 1163531501
206
+ b = (((b << 20) | (b >>> 12)) + c) << 0
207
+ a += (c ^ (d & (b ^ c))) + blocks[13]! - 1444681467
208
+ a = (((a << 5) | (a >>> 27)) + b) << 0
209
+ d += (b ^ (c & (a ^ b))) + blocks[2]! - 51403784
210
+ d = (((d << 9) | (d >>> 23)) + a) << 0
211
+ c += (a ^ (b & (d ^ a))) + blocks[7]! + 1735328473
212
+ c = (((c << 14) | (c >>> 18)) + d) << 0
213
+ b += (d ^ (a & (c ^ d))) + blocks[12]! - 1926607734
214
+ b = (((b << 20) | (b >>> 12)) + c) << 0
215
+ bc = b ^ c
216
+ a += (bc ^ d) + blocks[5]! - 378558
217
+ a = (((a << 4) | (a >>> 28)) + b) << 0
218
+ d += (bc ^ a) + blocks[8]! - 2022574463
219
+ d = (((d << 11) | (d >>> 21)) + a) << 0
220
+ da = d ^ a
221
+ c += (da ^ b) + blocks[11]! + 1839030562
222
+ c = (((c << 16) | (c >>> 16)) + d) << 0
223
+ b += (da ^ c) + blocks[14]! - 35309556
224
+ b = (((b << 23) | (b >>> 9)) + c) << 0
225
+ bc = b ^ c
226
+ a += (bc ^ d) + blocks[1]! - 1530992060
227
+ a = (((a << 4) | (a >>> 28)) + b) << 0
228
+ d += (bc ^ a) + blocks[4]! + 1272893353
229
+ d = (((d << 11) | (d >>> 21)) + a) << 0
230
+ da = d ^ a
231
+ c += (da ^ b) + blocks[7]! - 155497632
232
+ c = (((c << 16) | (c >>> 16)) + d) << 0
233
+ b += (da ^ c) + blocks[10]! - 1094730640
234
+ b = (((b << 23) | (b >>> 9)) + c) << 0
235
+ bc = b ^ c
236
+ a += (bc ^ d) + blocks[13]! + 681279174
237
+ a = (((a << 4) | (a >>> 28)) + b) << 0
238
+ d += (bc ^ a) + blocks[0]! - 358537222
239
+ d = (((d << 11) | (d >>> 21)) + a) << 0
240
+ da = d ^ a
241
+ c += (da ^ b) + blocks[3]! - 722521979
242
+ c = (((c << 16) | (c >>> 16)) + d) << 0
243
+ b += (da ^ c) + blocks[6]! + 76029189
244
+ b = (((b << 23) | (b >>> 9)) + c) << 0
245
+ bc = b ^ c
246
+ a += (bc ^ d) + blocks[9]! - 640364487
247
+ a = (((a << 4) | (a >>> 28)) + b) << 0
248
+ d += (bc ^ a) + blocks[12]! - 421815835
249
+ d = (((d << 11) | (d >>> 21)) + a) << 0
250
+ da = d ^ a
251
+ c += (da ^ b) + blocks[15]! + 530742520
252
+ c = (((c << 16) | (c >>> 16)) + d) << 0
253
+ b += (da ^ c) + blocks[2]! - 995338651
254
+ b = (((b << 23) | (b >>> 9)) + c) << 0
255
+ a += (c ^ (b | ~d)) + blocks[0]! - 198630844
256
+ a = (((a << 6) | (a >>> 26)) + b) << 0
257
+ d += (b ^ (a | ~c)) + blocks[7]! + 1126891415
258
+ d = (((d << 10) | (d >>> 22)) + a) << 0
259
+ c += (a ^ (d | ~b)) + blocks[14]! - 1416354905
260
+ c = (((c << 15) | (c >>> 17)) + d) << 0
261
+ b += (d ^ (c | ~a)) + blocks[5]! - 57434055
262
+ b = (((b << 21) | (b >>> 11)) + c) << 0
263
+ a += (c ^ (b | ~d)) + blocks[12]! + 1700485571
264
+ a = (((a << 6) | (a >>> 26)) + b) << 0
265
+ d += (b ^ (a | ~c)) + blocks[3]! - 1894986606
266
+ d = (((d << 10) | (d >>> 22)) + a) << 0
267
+ c += (a ^ (d | ~b)) + blocks[10]! - 1051523
268
+ c = (((c << 15) | (c >>> 17)) + d) << 0
269
+ b += (d ^ (c | ~a)) + blocks[1]! - 2054922799
270
+ b = (((b << 21) | (b >>> 11)) + c) << 0
271
+ a += (c ^ (b | ~d)) + blocks[8]! + 1873313359
272
+ a = (((a << 6) | (a >>> 26)) + b) << 0
273
+ d += (b ^ (a | ~c)) + blocks[15]! - 30611744
274
+ d = (((d << 10) | (d >>> 22)) + a) << 0
275
+ c += (a ^ (d | ~b)) + blocks[6]! - 1560198380
276
+ c = (((c << 15) | (c >>> 17)) + d) << 0
277
+ b += (d ^ (c | ~a)) + blocks[13]! + 1309151649
278
+ b = (((b << 21) | (b >>> 11)) + c) << 0
279
+ a += (c ^ (b | ~d)) + blocks[4]! - 145523070
280
+ a = (((a << 6) | (a >>> 26)) + b) << 0
281
+ d += (b ^ (a | ~c)) + blocks[11]! - 1120210379
282
+ d = (((d << 10) | (d >>> 22)) + a) << 0
283
+ c += (a ^ (d | ~b)) + blocks[2]! + 718787259
284
+ c = (((c << 15) | (c >>> 17)) + d) << 0
285
+ b += (d ^ (c | ~a)) + blocks[9]! - 343485551
286
+ b = (((b << 21) | (b >>> 11)) + c) << 0
287
+
288
+ if (this.first) {
289
+ this.h0 = (a + 1732584193) << 0
290
+ this.h1 = (b - 271733879) << 0
291
+ this.h2 = (c - 1732584194) << 0
292
+ this.h3 = (d + 271733878) << 0
293
+ this.first = false
294
+ } else {
295
+ this.h0 = (this.h0 + a) << 0
296
+ this.h1 = (this.h1 + b) << 0
297
+ this.h2 = (this.h2 + c) << 0
298
+ this.h3 = (this.h3 + d) << 0
299
+ }
300
+ }
301
+
302
+ hex() {
303
+ this.finalize()
304
+
305
+ const { h0, h1, h2, h3 } = this
306
+ const HEX_CHARS = '0123456789abcdef'.split('')
307
+ return (
308
+ // prettier-ignore
309
+ // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
310
+ HEX_CHARS[(h0 >> 4) & 0x0f] + HEX_CHARS[h0 & 0x0f]! + HEX_CHARS[(h0 >> 12) & 0x0f] + HEX_CHARS[(h0 >> 8) & 0x0f] + HEX_CHARS[(h0 >> 20) & 0x0f] +
311
+ HEX_CHARS[(h0 >> 16) & 0x0f] + HEX_CHARS[(h0 >> 28) & 0x0f] + HEX_CHARS[(h0 >> 24) & 0x0f] + HEX_CHARS[(h1 >> 4) & 0x0f] + HEX_CHARS[h1 & 0x0f] +
312
+ HEX_CHARS[(h1 >> 12) & 0x0f] + HEX_CHARS[(h1 >> 8) & 0x0f] + HEX_CHARS[(h1 >> 20) & 0x0f] + HEX_CHARS[(h1 >> 16) & 0x0f] + HEX_CHARS[(h1 >> 28) & 0x0f] +
313
+ HEX_CHARS[(h1 >> 24) & 0x0f] + HEX_CHARS[(h2 >> 4) & 0x0f] + HEX_CHARS[h2 & 0x0f] + HEX_CHARS[(h2 >> 12) & 0x0f] + HEX_CHARS[(h2 >> 8) & 0x0f] +
314
+ HEX_CHARS[(h2 >> 20) & 0x0f] + HEX_CHARS[(h2 >> 16) & 0x0f] + HEX_CHARS[(h2 >> 28) & 0x0f] + HEX_CHARS[(h2 >> 24) & 0x0f] + HEX_CHARS[(h3 >> 4) & 0x0f] +
315
+ HEX_CHARS[h3 & 0x0f] + HEX_CHARS[(h3 >> 12) & 0x0f] + HEX_CHARS[(h3 >> 8) & 0x0f] + HEX_CHARS[(h3 >> 20) & 0x0f] + HEX_CHARS[(h3 >> 16) & 0x0f] +
316
+ HEX_CHARS[(h3 >> 28) & 0x0f] + HEX_CHARS[(h3 >> 24) & 0x0f]
317
+ )
318
+ }
319
+ }
package/src/url.ts CHANGED
@@ -9,47 +9,43 @@
9
9
  */
10
10
  import isPlainObject from 'lodash/isPlainObject.js'
11
11
 
12
- /*
13
- 从字符串中解析出 query 对象
14
-
15
- array:
16
- - 若开启,支持解析 a[]=1&a[]=2 格式的参数,会解析成一个数组 { a: ['1', '2'] }
17
- - 否则把 `a[]` 整体当做一个参数名 { 'a[]': '2' }
18
-
19
- strict:
20
- 是否开启“严格模式”(默认不开启)。
21
- 在非严格模式下,会做很多兼容处理:
22
- 1. 支持直接传入 query string(a=1&b=2);严格模式下,则需在开头补充一个 ?(?a=1&b=2)
23
- 2. hash 里的内容也会被解析,以兼容拼接错误的 URL(把 query 拼到了 hash 后面)。
24
- 3. 出现多个 ? 符号时,会把 ? 也当做 & 分隔符(index.html?a=1&b=2?c=3)
25
-
26
- 小技巧:
27
- 在非严格模式下,如果明确只想解析 hash 或 search 里的内容,可以传入 location.search / hash 而不是传入整个 location.href
28
-
29
- 此函数不会对 query 值进行 decode,需自定处理
30
- */
31
- function parseQuery(url: string, options?: { array?: false, strict?: boolean }): Record<string, string> // prettier-ignore
32
- function parseQuery(url: string, options: { array: true, strict?: boolean }): Record<string, string | string[]> // prettier-ignore
12
+ /**
13
+ * 从 URL 中解析出 query 对象
14
+ * 注意:不带 ? 号的纯 query 内容需手动加上 ? 再传入。
15
+ *
16
+ * [array]
17
+ * 是否把重复出现的 key 保存为数组(默认不开启)
18
+ * a=1&a=2 => { a: [1,2] }
19
+ *
20
+ * [loose]
21
+ * 是否开启“宽松模式”(默认不开启)
22
+ * 1. hash 里的内容也会被解析,以兼容拼接错误的 URL(把 query 拼到了 hash 后面)。
23
+ * 2. 出现多个 ? 符号时,会把 ? 也当做 & 分隔符(index.html?a=1&b=2?c=3)
24
+ *
25
+ * [decode]
26
+ * 是否对 query 值进行 decode(默认开启)
27
+ */
28
+ function parseQuery(url: string, options?: { array?: false, loose?: boolean, decode?: boolean }): Record<string, string> // prettier-ignore
29
+ function parseQuery(url: string, options: { array: true, loose?: boolean, decode?: boolean }): Record<string, string | string[]> // prettier-ignore
33
30
  function parseQuery(
34
31
  url: string,
35
- options?: { array?: boolean; strict?: boolean }
32
+ options?: { array?: boolean; loose?: boolean; decode?: boolean }
36
33
  ): Record<string, string | string[]> {
37
- if (!url) return {}
38
- const { array = false, strict = false } = options ?? {}
34
+ const { array = false, loose = false, decode = true } = options ?? {}
39
35
 
40
- const queryString = strict
41
- ? (/^[^?#]*\?(.+?)(\?|#|$)/.exec(url) ?? ['', ''])[1] // 排除 hash、重复的 ? 符号
42
- : url
36
+ // 正常状态下,将仅剩 a=1&b=1(即不会再有 ? 和 #);loose 模型下,可能为 a=1&b=2#c=3?d=4
37
+ const queryString = (loose ? /(\?|#)(.+)/ : /(\?)(.+?)(#|$)/).exec(url)?.[2] ?? ''
38
+ if (!queryString) return {}
43
39
 
44
40
  const query: { [name: string]: string | string[] } = {}
45
41
  const reg = /([^#?&]*)=([^#?&]*)/g
46
42
  let re = reg.exec(queryString)
47
43
  while (re) {
48
- const [name, value] = [re[1]!, re[2]!]
49
- if (name.endsWith('[]') && array) {
50
- const realName = name.slice(0, -2)
51
- if (Array.isArray(query[realName])) (query[realName] as string[]).push(value)
52
- else query[realName] = [value]
44
+ const [name, rawValue] = [re[1]!, re[2]!] as [string, string]
45
+ const value = decode ? safeDecode(rawValue) : rawValue
46
+ if (array && query[name] !== undefined) {
47
+ const prev = query[name]!
48
+ query[name] = Array.isArray(prev) ? [...prev, value] : [prev, value]
53
49
  } else {
54
50
  query[name] = value
55
51
  }
@@ -59,35 +55,35 @@ function parseQuery(
59
55
  }
60
56
  export { parseQuery }
61
57
 
62
- /**
63
- * 取 query 中指定参数的值
64
- * - 参数存在,返回参数值(可能是空字符串);不存在返回 null
65
- * - 解析 query 固定基于 parseQuery() 的 { array: false, strict: false } 规则
66
- * - 和 parseQuery() 一样,url 可以根据需要传 location.href/search/hash
67
- */
68
- export function getQueryParam(name: string, url: string): string | null {
69
- const query = parseQuery(url)
70
- return typeof query[name] === 'string' ? query[name]! : null
71
- }
72
-
73
58
  /**
74
59
  * 把对象合并成 query string。
75
- * 支持字符串、数值、布尔值(不建议,考虑用 0 和 1 代替),数组会转换成 name[]=value 的格式
60
+ * - 支持字符串、数值、布尔值、数组。
61
+ * - 布尔值会替换成 0 和 1。
62
+ * - 数组会多次赋值:{ a: [1,2,3] } => 'a=1&a=2&a=3',不支持嵌套数组
63
+ * - encode 为 true 时会对 value 执行 encodeURIComponent(默认为 true)
76
64
  */
77
65
  type StringifyVal = string | number | boolean
78
- export function stringifyQuery(obj: { [key: string]: StringifyVal | StringifyVal[] | undefined }) {
66
+ type StringifyQuery = { [key: string]: StringifyVal | StringifyVal[] | undefined }
67
+ export function stringifyQuery(obj: StringifyQuery, encode = true) {
79
68
  if (!isPlainObject(obj)) return ''
80
69
  return (
81
70
  Object.entries(obj)
82
71
  // 过滤值为 undefined 的项目,使其完全不出现在最终的 query 中
83
72
  .filter((entry): entry is [string, StringifyVal | StringifyVal[]] => entry[1] !== undefined)
84
- .map(([name, value]) => stringifyQueryItem(name, value))
73
+ .map(([name, value]) => stringifyQueryItem(name, value, encode))
85
74
  .join('&')
86
75
  )
87
76
  }
88
- function stringifyQueryItem(name: string, value: StringifyVal | StringifyVal[]): string {
77
+ function stringifyQueryItem(
78
+ name: string,
79
+ value: StringifyVal | StringifyVal[],
80
+ encode: boolean
81
+ ): string {
89
82
  if (Array.isArray(value))
90
- return value.map(subValue => stringifyQueryItem(`${name}[]`, subValue)).join('&')
83
+ return value.map(subValue => stringifyQueryItem(name, subValue, encode)).join('&')
84
+ if (typeof value === 'boolean') value = value ? '1' : '0'
85
+ if (typeof value === 'number') value = value.toString()
86
+ if (encode) value = encodeURIComponent(value)
91
87
  return `${name}=${value}`
92
88
  }
93
89
 
@@ -96,6 +92,7 @@ function stringifyQueryItem(name: string, value: StringifyVal | StringifyVal[]):
96
92
  *
97
93
  * bare 为 true,则 search 不带 '?',hash 不带 '#'
98
94
  * 否则和 location.search / hash 一样
95
+ * (默认为 true)
99
96
  */
100
97
  export function splitUrl(url: string, bare = true): { base: string; search: string; hash: string } {
101
98
  let hashIndex = url.indexOf('#')
@@ -114,16 +111,12 @@ export function splitUrl(url: string, bare = true): { base: string; search: stri
114
111
  }
115
112
 
116
113
  /**
117
- * 把指定 query 和 hash 内容合并到 url 上
114
+ * query 和 hash 内容合并到 url 上
118
115
  *
119
116
  * query object 与现有 search 合并,替换同名项(值为数组的,用新数组代替老的,不会合并数组)
120
- * hash string url 已有 hash,会用此值代替。带不带开头的 '?' 皆可。
117
+ * hash string 带不带开头的 '#' 皆可。会代替 url 已有的 hash
121
118
  */
122
- export function combineUrl(
123
- origUrl: string,
124
- query: Record<string, string | string[]> = {},
125
- hash: string = ''
126
- ) {
119
+ export function combineUrl(origUrl: string, query: StringifyQuery = {}, hash: string = '') {
127
120
  if (hash.startsWith('#')) hash = hash.slice(1)
128
121
 
129
122
  // 拆分原 url 的 search、hash
@@ -0,0 +1,62 @@
1
+ import { success, failed } from '../lang/index.js'
2
+ import { Validator } from './base.js'
3
+
4
+ /** 验证元素数量任意、元素类型相同的数组 */
5
+ export type ArrayOptions = {
6
+ /** 验证数组各元素 */
7
+ item: Validator
8
+ /** 数组最小长度 */
9
+ min?: number
10
+ /** 数组最大长度 */
11
+ max?: number
12
+ /** 是否对数组元素进行去重 @defaults false */
13
+ unique?: boolean
14
+ }
15
+
16
+ /** 验证元素数量固定、类型可以不同的数组 */
17
+ export type TupleOptions = {
18
+ /** 验证数组各元素(validator 与元素一一对应) */
19
+ tuple: Validator[]
20
+ }
21
+
22
+ export class ArrayValidator extends Validator<ArrayOptions | TupleOptions> {
23
+ validate(field: string, value: unknown) {
24
+ const superResult = super.validate(field, value)
25
+ if (!superResult.success) return superResult
26
+
27
+ value = superResult.data
28
+ if (value === null || value === undefined) return superResult
29
+ const opt = this.options
30
+
31
+ if (!Array.isArray(value)) return failed(`${field} should be an array`)
32
+
33
+ let formatted = []
34
+ if ('item' in opt) {
35
+ if (typeof opt.min === 'number' && value.length < opt.min)
36
+ return failed(`array ${field}'s length should >= ${opt.min}`)
37
+
38
+ if (typeof opt.max === 'number' && value.length > opt.max)
39
+ return failed(`array ${field}'s length should <= ${opt.max}`)
40
+
41
+ for (let i = 0; i < value.length; i++) {
42
+ const itemResult = opt.item.validate(`${field}[${i}]`, value[i])
43
+ if (itemResult.success) formatted.push(itemResult.data)
44
+ else return itemResult
45
+ }
46
+
47
+ if (opt.unique === true) formatted = [...new Set(formatted)]
48
+ } else {
49
+ if (value.length > opt.tuple.length)
50
+ return failed(`${field} should be a tuple with ${opt.tuple.length} items`)
51
+
52
+ // 这种情况不能遍历 value,因为它的长度可能小于 opt.tuple
53
+ for (let i = 0; i < opt.tuple.length; i++) {
54
+ const itemResult = opt.tuple[i]!.validate(`${field}[${i}]`, value[i])
55
+ if (itemResult.success) formatted.push(itemResult.data)
56
+ else return itemResult
57
+ }
58
+ }
59
+
60
+ return success(formatted)
61
+ }
62
+ }