@cloudcome/utils-core 1.1.0 → 1.1.1

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 (292) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/LICENSE +21 -0
  3. package/package.json +279 -1
  4. package/src/array.ts +312 -0
  5. package/src/async.ts +379 -0
  6. package/src/base64.ts +20 -0
  7. package/src/cache.ts +146 -0
  8. package/{dist/color/contrast.d.ts → src/color/contrast.ts} +12 -2
  9. package/src/color/distance.ts +28 -0
  10. package/src/color/helpers.ts +23 -0
  11. package/src/color/hex-hsl.ts +11 -0
  12. package/{dist/color/hex-hsv.d.ts → src/color/hex-hsv.ts} +11 -3
  13. package/{dist/color/hex-hwb.d.ts → src/color/hex-hwb.ts} +11 -3
  14. package/src/color/hex-rgb.ts +39 -0
  15. package/{dist/color/hsl-lighten.d.ts → src/color/hsl-lighten.ts} +7 -2
  16. package/{dist/color/hsv-brighten.d.ts → src/color/hsv-brighten.ts} +7 -2
  17. package/{dist/color/luminance.d.ts → src/color/luminance.ts} +9 -2
  18. package/{dist/color/mix.d.ts → src/color/mix.ts} +10 -2
  19. package/src/color/rgb-hsl.ts +53 -0
  20. package/{dist/color/rgb-hsv.d.ts → src/color/rgb-hsv.ts} +30 -3
  21. package/src/color/rgb-hwb.ts +56 -0
  22. package/{dist/color/rgb-lab.d.ts → src/color/rgb-lab.ts} +11 -3
  23. package/src/color/rgb-whiter.ts +22 -0
  24. package/src/color/rgb-xyz.ts +62 -0
  25. package/{dist/color/types.d.ts → src/color/types.ts} +12 -30
  26. package/{dist/color/xyz-lab.d.ts → src/color/xyz-lab.ts} +32 -3
  27. package/src/crypto/md5.mjs +357 -0
  28. package/src/crypto/sha1.mjs +300 -0
  29. package/src/crypto/sha256.mjs +310 -0
  30. package/src/crypto/sha512.mjs +459 -0
  31. package/{dist/crypto.d.ts → src/crypto.ts} +20 -4
  32. package/src/date/const.ts +6 -0
  33. package/src/date/core.ts +162 -0
  34. package/src/date/days.ts +51 -0
  35. package/{dist/date/is.d.ts → src/date/is.ts} +102 -8
  36. package/src/date/relative.ts +92 -0
  37. package/src/date/start-end.ts +246 -0
  38. package/src/date/timezone.ts +220 -0
  39. package/src/date/weeks.ts +100 -0
  40. package/src/dts/global.d.ts +27 -0
  41. package/src/easing.ts +166 -0
  42. package/src/emitter.ts +117 -0
  43. package/src/enum.ts +171 -0
  44. package/{dist/env.d.ts → src/env.ts} +30 -6
  45. package/{dist/error.d.ts → src/error.ts} +12 -3
  46. package/src/exception.ts +68 -0
  47. package/src/fn.ts +197 -0
  48. package/src/index.ts +1 -0
  49. package/src/number.ts +236 -0
  50. package/{dist/object/each.d.ts → src/object/each.ts} +23 -3
  51. package/src/object/get-set.ts +273 -0
  52. package/src/object/is.ts +128 -0
  53. package/src/object/merge.ts +180 -0
  54. package/{dist/object/process.d.ts → src/object/process.ts} +38 -4
  55. package/src/path.ts +188 -0
  56. package/{dist/promise.d.ts → src/promise.ts} +67 -6
  57. package/{dist/qs.d.ts → src/qs.ts} +60 -3
  58. package/src/regexp.ts +156 -0
  59. package/src/string.ts +146 -0
  60. package/src/time/from.ts +57 -0
  61. package/src/time/to.ts +106 -0
  62. package/{dist/timer.mjs → src/timer.ts} +124 -17
  63. package/{dist/tree.d.ts → src/tree.ts} +225 -41
  64. package/{dist/type.d.ts → src/type.ts} +96 -20
  65. package/{dist/types.d.ts → src/types.ts} +33 -12
  66. package/src/unique.ts +77 -0
  67. package/src/url.ts +93 -0
  68. package/src/version.ts +71 -0
  69. package/test/array.test.ts +332 -0
  70. package/test/async-real.test.ts +39 -0
  71. package/test/async.test.ts +375 -0
  72. package/test/base64.test.ts +32 -0
  73. package/test/cache.test.ts +83 -0
  74. package/test/color.test.ts +163 -0
  75. package/test/crypto.test.ts +34 -0
  76. package/test/date-tz.test.ts +206 -0
  77. package/test/date.test.ts +353 -0
  78. package/test/easing.test.ts +33 -0
  79. package/test/emitter.test.ts +71 -0
  80. package/test/enum.test.ts +113 -0
  81. package/test/env.test.ts +69 -0
  82. package/test/error.test.ts +58 -0
  83. package/test/exception.test.ts +43 -0
  84. package/test/fn.test.ts +263 -0
  85. package/test/helpers.ts +23 -0
  86. package/test/index.test.ts +6 -0
  87. package/test/number.test.ts +213 -0
  88. package/test/object.test.ts +309 -0
  89. package/test/path.test.ts +156 -0
  90. package/test/promise.test.ts +199 -0
  91. package/test/qs.test.ts +79 -0
  92. package/test/regexp.test.ts +97 -0
  93. package/test/string.test.ts +150 -0
  94. package/test/time.test.ts +214 -0
  95. package/test/timer.test.ts +114 -0
  96. package/test/tree.test.ts +348 -0
  97. package/test/type.test.ts +226 -0
  98. package/test/unique.test.ts +71 -0
  99. package/test/url.test.ts +136 -0
  100. package/test/version.test.ts +52 -0
  101. package/tsconfig.json +31 -0
  102. package/vite.config.mts +114 -0
  103. package/dist/array.cjs +0 -129
  104. package/dist/array.cjs.map +0 -1
  105. package/dist/array.d.ts +0 -171
  106. package/dist/array.mjs +0 -129
  107. package/dist/array.mjs.map +0 -1
  108. package/dist/async.cjs +0 -219
  109. package/dist/async.cjs.map +0 -1
  110. package/dist/async.d.ts +0 -137
  111. package/dist/async.mjs +0 -219
  112. package/dist/async.mjs.map +0 -1
  113. package/dist/base64.cjs +0 -16
  114. package/dist/base64.cjs.map +0 -1
  115. package/dist/base64.d.ts +0 -7
  116. package/dist/base64.mjs +0 -16
  117. package/dist/base64.mjs.map +0 -1
  118. package/dist/cache.cjs +0 -79
  119. package/dist/cache.cjs.map +0 -1
  120. package/dist/cache.d.ts +0 -90
  121. package/dist/cache.mjs +0 -79
  122. package/dist/cache.mjs.map +0 -1
  123. package/dist/color/distance.d.ts +0 -8
  124. package/dist/color/helpers.d.ts +0 -2
  125. package/dist/color/hex-hsl.d.ts +0 -3
  126. package/dist/color/hex-rgb.d.ts +0 -18
  127. package/dist/color/rgb-hsl.d.ts +0 -23
  128. package/dist/color/rgb-hwb.d.ts +0 -29
  129. package/dist/color/rgb-whiter.d.ts +0 -12
  130. package/dist/color/rgb-xyz.d.ts +0 -22
  131. package/dist/color.cjs +0 -250
  132. package/dist/color.cjs.map +0 -1
  133. package/dist/color.mjs +0 -250
  134. package/dist/color.mjs.map +0 -1
  135. package/dist/const.cjs +0 -14
  136. package/dist/const.cjs.map +0 -1
  137. package/dist/const.mjs +0 -15
  138. package/dist/const.mjs.map +0 -1
  139. package/dist/core.cjs +0 -250
  140. package/dist/core.cjs.map +0 -1
  141. package/dist/core.mjs +0 -251
  142. package/dist/core.mjs.map +0 -1
  143. package/dist/crypto/md5.d.mts +0 -1
  144. package/dist/crypto/sha1.d.mts +0 -1
  145. package/dist/crypto/sha256.d.mts +0 -1
  146. package/dist/crypto/sha512.d.mts +0 -1
  147. package/dist/crypto.cjs +0 -812
  148. package/dist/crypto.cjs.map +0 -1
  149. package/dist/crypto.mjs +0 -812
  150. package/dist/crypto.mjs.map +0 -1
  151. package/dist/date/const.d.ts +0 -6
  152. package/dist/date/core.d.ts +0 -52
  153. package/dist/date/days.d.ts +0 -23
  154. package/dist/date/relative.d.ts +0 -44
  155. package/dist/date/start-end.d.ts +0 -73
  156. package/dist/date/timezone.d.ts +0 -67
  157. package/dist/date/weeks.d.ts +0 -72
  158. package/dist/date.cjs +0 -239
  159. package/dist/date.cjs.map +0 -1
  160. package/dist/date.mjs +0 -241
  161. package/dist/date.mjs.map +0 -1
  162. package/dist/dict.cjs +0 -2
  163. package/dist/dict.cjs.map +0 -1
  164. package/dist/dict.mjs +0 -2
  165. package/dist/dict.mjs.map +0 -1
  166. package/dist/each.cjs +0 -18
  167. package/dist/each.cjs.map +0 -1
  168. package/dist/each.mjs +0 -19
  169. package/dist/each.mjs.map +0 -1
  170. package/dist/easing.cjs +0 -151
  171. package/dist/easing.cjs.map +0 -1
  172. package/dist/easing.d.ts +0 -46
  173. package/dist/easing.mjs +0 -151
  174. package/dist/easing.mjs.map +0 -1
  175. package/dist/emitter.cjs +0 -94
  176. package/dist/emitter.cjs.map +0 -1
  177. package/dist/emitter.d.ts +0 -68
  178. package/dist/emitter.mjs +0 -94
  179. package/dist/emitter.mjs.map +0 -1
  180. package/dist/enum.cjs +0 -58
  181. package/dist/enum.cjs.map +0 -1
  182. package/dist/enum.d.ts +0 -68
  183. package/dist/enum.mjs +0 -58
  184. package/dist/enum.mjs.map +0 -1
  185. package/dist/env.cjs +0 -28
  186. package/dist/env.cjs.map +0 -1
  187. package/dist/env.mjs +0 -28
  188. package/dist/env.mjs.map +0 -1
  189. package/dist/error.cjs +0 -12
  190. package/dist/error.cjs.map +0 -1
  191. package/dist/error.mjs +0 -12
  192. package/dist/error.mjs.map +0 -1
  193. package/dist/exception.cjs +0 -22
  194. package/dist/exception.cjs.map +0 -1
  195. package/dist/exception.d.ts +0 -31
  196. package/dist/exception.mjs +0 -22
  197. package/dist/exception.mjs.map +0 -1
  198. package/dist/fn.cjs +0 -76
  199. package/dist/fn.cjs.map +0 -1
  200. package/dist/fn.d.ts +0 -102
  201. package/dist/fn.mjs +0 -76
  202. package/dist/fn.mjs.map +0 -1
  203. package/dist/index.cjs +0 -5
  204. package/dist/index.cjs.map +0 -1
  205. package/dist/index.d.ts +0 -1
  206. package/dist/index.mjs +0 -5
  207. package/dist/index.mjs.map +0 -1
  208. package/dist/merge.cjs +0 -87
  209. package/dist/merge.cjs.map +0 -1
  210. package/dist/merge.mjs +0 -88
  211. package/dist/merge.mjs.map +0 -1
  212. package/dist/number.cjs +0 -11
  213. package/dist/number.cjs.map +0 -1
  214. package/dist/number.d.ts +0 -137
  215. package/dist/number.mjs +0 -11
  216. package/dist/number.mjs.map +0 -1
  217. package/dist/object/get-set.d.ts +0 -111
  218. package/dist/object/is.d.ts +0 -32
  219. package/dist/object/merge.d.ts +0 -72
  220. package/dist/object.cjs +0 -130
  221. package/dist/object.cjs.map +0 -1
  222. package/dist/object.mjs +0 -130
  223. package/dist/object.mjs.map +0 -1
  224. package/dist/path.cjs +0 -77
  225. package/dist/path.cjs.map +0 -1
  226. package/dist/path.d.ts +0 -82
  227. package/dist/path.mjs +0 -77
  228. package/dist/path.mjs.map +0 -1
  229. package/dist/promise.cjs +0 -62
  230. package/dist/promise.cjs.map +0 -1
  231. package/dist/promise.mjs +0 -62
  232. package/dist/promise.mjs.map +0 -1
  233. package/dist/qs.cjs +0 -47
  234. package/dist/qs.cjs.map +0 -1
  235. package/dist/qs.mjs +0 -47
  236. package/dist/qs.mjs.map +0 -1
  237. package/dist/regexp.cjs +0 -66
  238. package/dist/regexp.cjs.map +0 -1
  239. package/dist/regexp.d.ts +0 -65
  240. package/dist/regexp.mjs +0 -66
  241. package/dist/regexp.mjs.map +0 -1
  242. package/dist/string.cjs +0 -16
  243. package/dist/string.cjs.map +0 -1
  244. package/dist/string.d.ts +0 -80
  245. package/dist/string.mjs +0 -16
  246. package/dist/string.mjs.map +0 -1
  247. package/dist/string2.cjs +0 -147
  248. package/dist/string2.cjs.map +0 -1
  249. package/dist/string2.mjs +0 -148
  250. package/dist/string2.mjs.map +0 -1
  251. package/dist/time/from.d.ts +0 -14
  252. package/dist/time/to.d.ts +0 -38
  253. package/dist/time.cjs +0 -82
  254. package/dist/time.cjs.map +0 -1
  255. package/dist/time.mjs +0 -82
  256. package/dist/time.mjs.map +0 -1
  257. package/dist/timer.cjs +0 -119
  258. package/dist/timer.cjs.map +0 -1
  259. package/dist/timer.d.ts +0 -96
  260. package/dist/timer.mjs.map +0 -1
  261. package/dist/tree.cjs +0 -125
  262. package/dist/tree.cjs.map +0 -1
  263. package/dist/tree.mjs +0 -125
  264. package/dist/tree.mjs.map +0 -1
  265. package/dist/type.cjs +0 -78
  266. package/dist/type.cjs.map +0 -1
  267. package/dist/type.mjs +0 -78
  268. package/dist/type.mjs.map +0 -1
  269. package/dist/types.cjs +0 -2
  270. package/dist/types.cjs.map +0 -1
  271. package/dist/types.mjs +0 -2
  272. package/dist/types.mjs.map +0 -1
  273. package/dist/unique.cjs +0 -46
  274. package/dist/unique.cjs.map +0 -1
  275. package/dist/unique.d.ts +0 -22
  276. package/dist/unique.mjs +0 -46
  277. package/dist/unique.mjs.map +0 -1
  278. package/dist/url.cjs +0 -37
  279. package/dist/url.cjs.map +0 -1
  280. package/dist/url.d.ts +0 -53
  281. package/dist/url.mjs +0 -37
  282. package/dist/url.mjs.map +0 -1
  283. package/dist/version.cjs +0 -33
  284. package/dist/version.cjs.map +0 -1
  285. package/dist/version.d.ts +0 -32
  286. package/dist/version.mjs +0 -33
  287. package/dist/version.mjs.map +0 -1
  288. /package/{dist/color.d.ts → src/color.ts} +0 -0
  289. /package/{dist/date.d.ts → src/date.ts} +0 -0
  290. /package/{dist/dict.d.ts → src/dict.ts} +0 -0
  291. /package/{dist/object.d.ts → src/object.ts} +0 -0
  292. /package/{dist/time.d.ts → src/time.ts} +0 -0
@@ -0,0 +1,199 @@
1
+ import {
2
+ createMinDelayPromise,
3
+ isPromiseLike,
4
+ promiseDelay,
5
+ promiseShared,
6
+ promiseTimeout,
7
+ promiseWhen,
8
+ } from '@/promise';
9
+ import { describe, expect, it } from 'vitest';
10
+
11
+ describe('promiseDelay', () => {
12
+ it('应在指定时间后解决 Promise', async () => {
13
+ const startTime = Date.now();
14
+ await promiseDelay(100);
15
+ const endTime = Date.now();
16
+ expect(endTime - startTime).toBeGreaterThanOrEqual(0);
17
+ });
18
+
19
+ it('如果 ms 为 0,应立即解决 Promise', async () => {
20
+ const startTime = Date.now();
21
+ await promiseDelay(0);
22
+ const endTime = Date.now();
23
+ expect(endTime - startTime).toBeLessThan(10);
24
+ });
25
+
26
+ it('应在调用 abort 后解决 Promise', async () => {
27
+ const ctrl = new AbortController();
28
+ const startTime = Date.now();
29
+ const promise = promiseDelay(1000, ctrl);
30
+ ctrl.abort();
31
+ await promise;
32
+ const endTime = Date.now();
33
+ expect(endTime - startTime).toBeLessThan(1010);
34
+ });
35
+ });
36
+
37
+ describe('promiseTimeout', () => {
38
+ it('如果 Promise 在指定时间内解决,应返回其结果', async () => {
39
+ const result = await promiseTimeout(Promise.resolve('success'), 100);
40
+ expect(result).toBe('success');
41
+ });
42
+
43
+ it('如果 Promise 在指定时间内未解决,应抛出 "timeout" 错误', async () => {
44
+ await expect(promiseTimeout(promiseDelay(100), 0)).rejects.toThrow('timeout');
45
+ });
46
+
47
+ it('如果 Promise 在指定时间内恰好解决,应返回其结果', async () => {
48
+ const result = await promiseTimeout(Promise.resolve('success'), 10);
49
+ expect(result).toBe('success');
50
+ });
51
+ });
52
+
53
+ describe('promiseWhen', () => {
54
+ it('如果条件初始为真,应立即解决 Promise', async () => {
55
+ const startTime = Date.now();
56
+ await promiseWhen(() => true, 100);
57
+ const endTime = Date.now();
58
+ expect(endTime - startTime).toBeLessThan(10);
59
+ });
60
+
61
+ it('应在条件变为真后解决 Promise', async () => {
62
+ let conditionMet = false;
63
+ setTimeout(() => {
64
+ conditionMet = true;
65
+ }, 100);
66
+ const startTime = Date.now();
67
+ await promiseWhen(() => conditionMet, 10);
68
+ const endTime = Date.now();
69
+ expect(endTime - startTime).toBeGreaterThanOrEqual(100);
70
+ });
71
+
72
+ it('应使用较小的时间间隔检查条件', async () => {
73
+ let conditionMet = false;
74
+ setTimeout(() => {
75
+ conditionMet = true;
76
+ }, 50);
77
+ const startTime = Date.now();
78
+ await promiseWhen(() => conditionMet, 10);
79
+ const endTime = Date.now();
80
+ expect(endTime - startTime).toBeGreaterThanOrEqual(50);
81
+ });
82
+ });
83
+
84
+ describe('isPromiseLike', () => {
85
+ it('应正确判断 Promise 类型', async () => {
86
+ try {
87
+ const p1 = Promise.resolve();
88
+ expect(isPromiseLike(p1)).toBe(true);
89
+ await p1;
90
+
91
+ const p2 = Promise.reject();
92
+ expect(isPromiseLike(p2)).toBe(true);
93
+ // 这里需要执行掉,否会打印未捕获的 promise 错误
94
+ await p2;
95
+
96
+ const p3 = new Promise<void>((r) => r());
97
+ expect(isPromiseLike(p3)).toBe(true);
98
+ await p3;
99
+ } catch (cause) {
100
+ //
101
+ }
102
+ });
103
+
104
+ it('应正确判断 Promise 类似对象', () => {
105
+ // biome-ignore lint/suspicious/noThenProperty: <explanation>
106
+ expect(isPromiseLike({ then: () => {} })).toBe(true);
107
+ // biome-ignore lint/suspicious/noThenProperty: <explanation>
108
+ expect(isPromiseLike({ then: 'not a function' })).toBe(false);
109
+ expect(isPromiseLike({})).toBe(false);
110
+ expect(isPromiseLike(null)).toBe(false);
111
+ expect(isPromiseLike(undefined)).toBe(false);
112
+ expect(isPromiseLike('string')).toBe(false);
113
+ expect(isPromiseLike(42)).toBe(false);
114
+ expect(isPromiseLike(true)).toBe(false);
115
+ expect(isPromiseLike(Symbol('sym'))).toBe(false);
116
+ expect(isPromiseLike(BigInt(123))).toBe(false);
117
+ expect(isPromiseLike(Number.NaN)).toBe(false);
118
+ expect(isPromiseLike(new Error('error'))).toBe(false);
119
+ });
120
+ });
121
+
122
+ describe('sharedPromise', () => {
123
+ it('应共享原始 Promise 的成功状态', async () => {
124
+ const value = Math.random();
125
+ const { promise, resolve } = Promise.withResolvers();
126
+ const { promise: status, resolve: done } = Promise.withResolvers<void>();
127
+ const shared1 = promiseShared(promise);
128
+
129
+ setTimeout(async () => {
130
+ // 在完成之前共享
131
+ resolve(value);
132
+
133
+ // 在完成之后共享
134
+ await expect(promiseShared(promise)).resolves.toBe(value);
135
+ done();
136
+ }, 10);
137
+
138
+ await expect(shared1).resolves.toBe(value);
139
+ await expect(promise).resolves.toBe(value);
140
+
141
+ await expect(status).resolves.toBe(undefined);
142
+ });
143
+
144
+ it('应共享原始 Promise 的拒绝状态', async () => {
145
+ const value = Math.random();
146
+ const { promise, reject } = Promise.withResolvers();
147
+ const { promise: status, resolve: done } = Promise.withResolvers<void>();
148
+ const shared1 = promiseShared(promise);
149
+
150
+ setTimeout(async () => {
151
+ // 在完成之前共享
152
+ reject(value);
153
+
154
+ // 在完成之后共享
155
+ await expect(promiseShared(promise)).rejects.toBe(value);
156
+ done();
157
+ }, 10);
158
+
159
+ await expect(shared1).rejects.toBe(value);
160
+ await expect(promise).rejects.toBe(value);
161
+
162
+ await expect(status).resolves.toBe(undefined);
163
+ });
164
+ });
165
+
166
+ describe('createMinDelayPromise', () => {
167
+ it('当实际执行时间小于最小等待时间时,应等待剩余时间', async () => {
168
+ const minWait = 100;
169
+ const end = createMinDelayPromise(minWait);
170
+ const startTime = Date.now();
171
+ await promiseDelay(50); // 模拟操作耗时
172
+ await end();
173
+ const endTime = Date.now();
174
+ expect(endTime - startTime).toBeGreaterThanOrEqual(minWait - 10);
175
+ expect(endTime - startTime).toBeLessThanOrEqual(minWait + 10);
176
+ });
177
+
178
+ it('当实际执行时间大于最小等待时间时,应立即返回', async () => {
179
+ const minWait = 50;
180
+ const end = createMinDelayPromise(minWait);
181
+ const startTime = Date.now();
182
+ await promiseDelay(100); // 模拟操作耗时
183
+ await end();
184
+ const endTime = Date.now();
185
+ // 实际情况下,这个时间是接近 100,可能是 99,也有可能是 101
186
+ expect(endTime - startTime).toBeGreaterThanOrEqual(90);
187
+ expect(endTime - startTime).toBeLessThanOrEqual(110);
188
+ });
189
+
190
+ it('当最小等待时间为 0 时,应立即返回', async () => {
191
+ const minWait = 0;
192
+ const end = createMinDelayPromise(minWait);
193
+ const startTime = Date.now();
194
+ await end();
195
+ const endTime = Date.now();
196
+ expect(endTime - startTime).toBeLessThanOrEqual(10);
197
+ expect(endTime - startTime).toBeGreaterThanOrEqual(0);
198
+ });
199
+ });
@@ -0,0 +1,79 @@
1
+ import { dateFormat } from '@/date';
2
+ import { type QSReader, type QSWriter, qsParse, qsStringify } from '@/qs';
3
+ import { isArray, isBoolean, isDate, isFunction, isNull, isNumber, isString, isUndefined } from '@/type';
4
+ import { describe, expect, it } from 'vitest';
5
+
6
+ describe('qsParse', () => {
7
+ it('默认 reader', () => {
8
+ expect(qsParse('?a=1')).toEqual({ a: '1' });
9
+ expect(qsParse('?a=1&b=2')).toEqual({ a: '1', b: '2' });
10
+ expect(qsParse('?a=1&b=1&b=2')).toEqual({ a: '1', b: ['1', '2'] });
11
+ expect(qsParse('?a=1&a=2&b=1')).toEqual({ a: ['1', '2'], b: '1' });
12
+ expect(qsParse('?a=1&a=2&a=3&a=4')).toEqual({ a: ['1', '2', '3', '4'] });
13
+ expect(qsParse('xx?a=1&a=2&a=3&a=4')).toEqual({ a: ['1', '2', '3', '4'] });
14
+ expect(qsParse('xx??a=1&a=2&a=3&a=4')).toEqual({ a: ['1', '2', '3', '4'] });
15
+ });
16
+
17
+ it('自定义 reader', () => {
18
+ const qsReader: QSReader<Record<string, string | number | boolean | Array<string | number | boolean>>> = (
19
+ value,
20
+ key,
21
+ qsObject,
22
+ ) => {
23
+ if (value === '1') {
24
+ return 1;
25
+ }
26
+
27
+ if (value === 'true') {
28
+ return true;
29
+ }
30
+
31
+ if (value.includes(',')) {
32
+ if (Object.hasOwn(qsObject, key)) {
33
+ if (!isArray(qsObject[key])) qsObject[key] = [qsObject[key]];
34
+ } else {
35
+ qsObject[key] = [];
36
+ }
37
+
38
+ qsObject[key].push(...value.split(','));
39
+ return;
40
+ }
41
+
42
+ return value;
43
+ };
44
+
45
+ expect(qsParse('?a=1', qsReader)).toEqual({ a: 1 });
46
+ expect(qsParse('?a=1&b=2', qsReader)).toEqual({ a: 1, b: '2' });
47
+ expect(qsParse('?a=1&b=2&a=3', qsReader)).toEqual({ a: [1, '3'], b: '2' });
48
+ expect(qsParse('?a=1&b=2&a=3&b=4,5', qsReader)).toEqual({ a: [1, '3'], b: ['2', '4', '5'] });
49
+ expect(qsParse('?a=1&b=2&a=3&c=4,5', qsReader)).toEqual({ a: [1, '3'], b: '2', c: ['4', '5'] });
50
+ expect(qsParse('?x=true', qsReader)).toEqual({ x: true });
51
+ });
52
+ });
53
+
54
+ describe('qsStringify', () => {
55
+ it('qsStringify 默认 writer', () => {
56
+ const i = new Date(2020, 0, 1, 0, 0, 0, 0);
57
+ const query = { a: 1, b: [2, 3], c: '4', d: undefined, e: null, f: true, g: false, i };
58
+ const string = 'a=1&b=2&b=3&c=4&f=true&g=false&i=2019-12-31T16%3A00%3A00.000Z';
59
+ expect(qsStringify(query)).toBe(string);
60
+ });
61
+
62
+ it('qsStringify 复写 writer', () => {
63
+ const qsWriter: QSWriter = (value, key, qsObject) => {
64
+ if (isString(value)) return `string-${value}`;
65
+ if (isNumber(value)) return `number-${value}`;
66
+ if (isBoolean(value)) return `boolean-${value ? 'true' : 'false'}`;
67
+ if (isUndefined(value)) return 'undefined';
68
+ if (isNull(value)) return 'null';
69
+ if (isDate(value)) return `date-${dateFormat(value, 'YYYY-MM-DD HH:mm:ss')}`;
70
+ return null;
71
+ };
72
+ const i = new Date(2020, 0, 1, 0, 0, 0, 0);
73
+ const query = { a: 1, b: [2, 3], c: '4', d: undefined, e: null, f: true, g: false, i };
74
+ const string =
75
+ 'a=number-1&b=number-2&b=number-3&c=string-4&d=undefined&e=null&' +
76
+ 'f=boolean-true&g=boolean-false&i=date-2020-01-01+00%3A00%3A00';
77
+ expect(qsStringify(query, qsWriter)).toBe(string);
78
+ });
79
+ });
@@ -0,0 +1,97 @@
1
+ import {
2
+ isDigit,
3
+ isEmail,
4
+ isFloat,
5
+ isIDNo,
6
+ isIPV4,
7
+ isInteger,
8
+ isNumerical,
9
+ isPhone,
10
+ isURL,
11
+ regexpEscape,
12
+ } from '@/regexp';
13
+ import { expect, test } from 'vitest';
14
+
15
+ test('reEscape', () => {
16
+ const str = 'a*';
17
+ const re = new RegExp(regexpEscape(str), 'i');
18
+
19
+ expect(re.source).toEqual('a\\*');
20
+ expect(re.test('a*')).toEqual(true);
21
+ expect(re.test('A*')).toEqual(true);
22
+ });
23
+
24
+ test('isURL', () => {
25
+ expect(isURL('')).toBe(false);
26
+ expect(isURL('http://aba.com')).toBe(true);
27
+ expect(isURL("http://aba.com:8080/'1@!.+%a?a(x)")).toBe(true);
28
+ expect(isURL("http://192.168.0.1/'1@!.+%a?a(x)")).toBe(true);
29
+ expect(isURL("http://192.168.0.1:8080/'1@!.+%a?a(x)")).toBe(true);
30
+ });
31
+
32
+ test('isEmail', () => {
33
+ expect(isEmail('')).toBe(false);
34
+ expect(isEmail('http://aba.com')).toBe(false);
35
+ expect(isEmail('http@aba.com')).toBe(true);
36
+ expect(isEmail('ht.tp@aba.com')).toBe(true);
37
+ });
38
+
39
+ test('isPhone', () => {
40
+ expect(isPhone('')).toBe(false);
41
+ expect(isPhone('19912345678')).toBe(true);
42
+ });
43
+
44
+ test('isIPV4', () => {
45
+ expect(isIPV4('')).toBe(false);
46
+ expect(isIPV4('11.11.111.11')).toBe(true);
47
+ expect(isIPV4('222.233.455.11')).toBe(false);
48
+ });
49
+
50
+ test('isIdNo', () => {
51
+ // http://id.8684.cn/
52
+ expect(isIDNo('350213197706189461')).toBe(true);
53
+ expect(isIDNo('350213197706189462')).toBe(false);
54
+ });
55
+
56
+ test('isInteger', () => {
57
+ expect(isInteger('1')).toBe(true);
58
+ expect(isInteger('-1')).toBe(true);
59
+ expect(isInteger('12300')).toBe(true);
60
+ expect(isInteger('-12300')).toBe(true);
61
+ expect(isInteger('0')).toBe(true);
62
+ expect(isInteger('0.0')).toBe(false);
63
+ expect(isInteger('0.1')).toBe(false);
64
+ expect(isInteger('-0')).toBe(false);
65
+ expect(isInteger('-0123')).toBe(false);
66
+ expect(isInteger('0123')).toBe(false);
67
+ });
68
+
69
+ test('isFloat', () => {
70
+ expect(isFloat('0')).toBe(false);
71
+ expect(isFloat('0.1')).toBe(true);
72
+ expect(isFloat('1.1')).toBe(true);
73
+ expect(isFloat('1.01')).toBe(true);
74
+ expect(isFloat('10.01')).toBe(true);
75
+ expect(isFloat('-10.01')).toBe(true);
76
+ expect(isFloat('010.01')).toBe(false);
77
+ expect(isFloat('10.010')).toBe(true);
78
+ expect(isFloat('10.0')).toBe(true);
79
+ expect(isFloat('10.')).toBe(false);
80
+ expect(isFloat('10')).toBe(false);
81
+ });
82
+
83
+ test('.isNumrical', () => {
84
+ expect(isNumerical('0')).toBe(true);
85
+ expect(isNumerical('0.0')).toBe(true);
86
+ expect(isNumerical('0.1')).toBe(true);
87
+ expect(isNumerical('1.1')).toBe(true);
88
+ expect(isNumerical('-1.1')).toBe(true);
89
+ });
90
+
91
+ test('isDigit', () => {
92
+ expect(isDigit('1')).toBe(true);
93
+ expect(isDigit('12')).toBe(true);
94
+ expect(isDigit('012')).toBe(true);
95
+ expect(isDigit('+012')).toBe(false);
96
+ expect(isDigit('-012')).toBe(false);
97
+ });
@@ -0,0 +1,150 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { randomString, randomUUID4, stringCamelCase, stringFormat, stringKebabCase, stringify } from '../src/string';
3
+
4
+ describe('stringCamelCase', () => {
5
+ it('应将字符串转换为驼峰命名', () => {
6
+ expect(stringCamelCase('hello world')).toBe('helloWorld');
7
+ expect(stringCamelCase('hello_world')).toBe('helloWorld');
8
+ expect(stringCamelCase('hello-world')).toBe('helloWorld');
9
+ });
10
+
11
+ it('如果 bigger 为 true,应将首字母大写', () => {
12
+ expect(stringCamelCase('hello world', true)).toBe('HelloWorld');
13
+ expect(stringCamelCase('hello_world', true)).toBe('HelloWorld');
14
+ expect(stringCamelCase('hello-world', true)).toBe('HelloWorld');
15
+ });
16
+ });
17
+
18
+ describe('stringKebabCase', () => {
19
+ it('应将字符串转换为默认分隔符的短横线命名', () => {
20
+ expect(stringKebabCase('helloWorld')).toBe('hello-world');
21
+ expect(stringKebabCase('HelloWorld')).toBe('-hello-world');
22
+ });
23
+
24
+ it('应将字符串转换为自定义分隔符的短横线命名', () => {
25
+ expect(stringKebabCase('helloWorld', '_')).toBe('hello_world');
26
+ expect(stringKebabCase('HelloWorld', '_')).toBe('_hello_world');
27
+ });
28
+ });
29
+
30
+ describe('randomString', () => {
31
+ it('应生成指定长度的随机字符串', () => {
32
+ const result = randomString(10);
33
+ expect(result.length).toBe(10);
34
+ });
35
+
36
+ it('应使用自定义字符字典生成随机字符串', () => {
37
+ const dict = 'ABCDEF';
38
+ const result = randomString(8, dict);
39
+ expect(result).toHaveLength(8);
40
+ for (const char of result) {
41
+ expect(dict).toContain(char);
42
+ }
43
+ });
44
+ });
45
+
46
+ describe('stringFormat', () => {
47
+ it('应支持索引方式格式化字符串', () => {
48
+ const result = stringFormat('你好 {0}!我的名字是 {1}。', '张三', '李四');
49
+ expect(result).toBe('你好 张三!我的名字是 李四。');
50
+ });
51
+
52
+ it('应支持对象方式格式化字符串', () => {
53
+ const result = stringFormat('{greet}!我的名字是 {name}。', { greet: '你好', name: '王五' });
54
+ expect(result).toBe('你好!我的名字是 王五。');
55
+ });
56
+
57
+ it('应支持带回退值的对象方式格式化字符串', () => {
58
+ const result = stringFormat('{greet}!我的名字是 {name}。', { greet: '你好' }, '未知');
59
+ expect(result).toBe('你好!我的名字是 未知。');
60
+ });
61
+
62
+ it('应支持带回退函数的对象方式格式化字符串', () => {
63
+ const result = stringFormat('{greet}!我的名字是 {name}。', { greet: '你好' }, (key) => `默认${key}`);
64
+ expect(result).toBe('你好!我的名字是 默认name。');
65
+ });
66
+
67
+ it('应处理未找到的键值对', () => {
68
+ const result = stringFormat('{greet}!我的名字是 {name}。', { greet: '你好' });
69
+ expect(result).toBe('你好!我的名字是 name。');
70
+ });
71
+
72
+ it('应处理空字符串', () => {
73
+ const result = stringFormat('', { greet: '你好' });
74
+ expect(result).toBe('');
75
+ });
76
+
77
+ it('应处理未传递参数的情况', () => {
78
+ const result = stringFormat('{greet}!我的名字是 {name}。');
79
+ expect(result).toBe('greet!我的名字是 name。');
80
+ });
81
+
82
+ it('边界,值为 falsy 值', () => {
83
+ const result = stringFormat('{days}天{hours}时{minutes}分{seconds}秒', {
84
+ days: 0,
85
+ hours: 1,
86
+ minutes: 2,
87
+ seconds: 3,
88
+ });
89
+ expect(result).toBe('0天1时2分3秒');
90
+ });
91
+ });
92
+
93
+ describe('randomUUID4', () => {
94
+ it('验证长度', () => {
95
+ for (let i = 0; i < 10; i++) {
96
+ const uuid = randomUUID4();
97
+
98
+ // 验证长度
99
+ expect(uuid.length).toBe(36);
100
+ }
101
+ });
102
+
103
+ it('验证分隔符位置', () => {
104
+ for (let i = 0; i < 10; i++) {
105
+ const uuid = randomUUID4();
106
+
107
+ // 验证分隔符位置
108
+ expect(uuid[8]).toBe('-');
109
+ expect(uuid[13]).toBe('-');
110
+ expect(uuid[18]).toBe('-');
111
+ expect(uuid[23]).toBe('-');
112
+ }
113
+ });
114
+
115
+ it('验证版本号为 4', () => {
116
+ for (let i = 0; i < 10; i++) {
117
+ const uuid = randomUUID4();
118
+
119
+ // 验证版本号为 4
120
+ expect(uuid[14]).toBe('4');
121
+ }
122
+ });
123
+
124
+ it('验证变体', () => {
125
+ for (let i = 0; i < 10; i++) {
126
+ const uuid = randomUUID4();
127
+
128
+ // 验证变体符合 RFC 4122 (第19位为 '8', '9', 'a', 或 'b')
129
+ const variantChar = uuid[19];
130
+ expect(['8', '9', 'a', 'b']).toContain(variantChar);
131
+ }
132
+ });
133
+ });
134
+
135
+ describe('stringify', () => {
136
+ it('应将 null 转换为空字符串', () => {
137
+ expect(stringify(null)).toBe('');
138
+ });
139
+
140
+ it('应将 undefined 转换为空字符串', () => {
141
+ expect(stringify(undefined)).toBe('');
142
+ });
143
+
144
+ it('应将其他值转换为字符串', () => {
145
+ expect(stringify(123)).toBe('123');
146
+ expect(stringify(true)).toBe('true');
147
+ expect(stringify({})).toBe('[object Object]');
148
+ expect(stringify('hello')).toBe('hello');
149
+ });
150
+ });