@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
package/src/number.ts ADDED
@@ -0,0 +1,236 @@
1
+ import { objectDefaults } from './object';
2
+ import { STRING_DICT } from './string';
3
+
4
+ export type NumberFixedOptions = {
5
+ /**
6
+ * 保留的小数位数
7
+ * @default 0
8
+ */
9
+ precision?: number;
10
+
11
+ /**
12
+ * 舍入方法,0 为四舍五入,1 为向上取整,-1 为向下取整
13
+ * @default 0
14
+ */
15
+ round?: 0 | 1 | -1;
16
+ };
17
+
18
+ /**
19
+ * 对数字进行精确小数位数处理并按规则舍入
20
+ * @param number 需要处理的原始数值
21
+ * @param options 可选配置参数
22
+ * @returns 处理后的数值(number类型)
23
+ * @example
24
+ * // 四舍五入示例
25
+ * numberFixed(3.1415, { precision: 2 }); // 3.14
26
+ * // 向上取整示例
27
+ * numberFixed(3.1415, { precision: 2, round: 1 }); // 3.15
28
+ * // 向下取整示例
29
+ * numberFixed(3.9999, { precision: 1, round: -1 }); // 3.9
30
+ */
31
+ export function numberFixed(number: number, options?: NumberFixedOptions) {
32
+ const { precision = 0, round = 0 } = options || {};
33
+ const scale = 10 ** precision;
34
+
35
+ if (round === 1) {
36
+ return Math.ceil(number * scale) / scale;
37
+ }
38
+
39
+ if (round === -1) {
40
+ return Math.floor(number * scale) / scale;
41
+ }
42
+
43
+ return Math.round(number * scale) / scale;
44
+ }
45
+
46
+ /**
47
+ * 生成指定范围内的随机整数
48
+ * @param {number} min - 随机数的最小值(包含)
49
+ * @param {number} max - 随机数的最大值(包含)
50
+ * @returns {number} - 生成的随机整数
51
+ * @example
52
+ * // 生成 1 到 10 之间的随机整数
53
+ * randomNumber(1, 10); // 可能返回 7
54
+ */
55
+ export function randomNumber(min: number, max: number): number {
56
+ const [minFinal, maxFinal] = min > max ? [max, min] : [min, max];
57
+ return Math.floor(Math.random() * (maxFinal - minFinal + 1) + minFinal);
58
+ }
59
+
60
+ /**
61
+ * 数字缩写选项
62
+ */
63
+ export type NumberAbbrOptions = {
64
+ /**
65
+ * 进制基数,用于计算单位进阶(如 1000 表示千进制)
66
+ * @default 1000
67
+ */
68
+ base?: number;
69
+
70
+ /**
71
+ * 数值保留的小数位数
72
+ * @default 0
73
+ */
74
+ precision?: number;
75
+ };
76
+
77
+ /**
78
+ * 将数字转换为带单位缩写的字符串表示
79
+ *
80
+ * @param {number} number - 需要转换的原始数值
81
+ * @param {Array<string>} units - 单位数组,按从小到大顺序排列(如['B','KB','MB']),不能为空
82
+ * @param {NumberAbbrOptions} [options] - 可选配置参数
83
+ * @returns {string} - 转换后的带单位字符串(如"1.2KB")
84
+ * @example
85
+ * // 基础用法
86
+ * numberAbbr(1500, ['', 'K', 'M'], { base: 1000 }); // "1.5K"
87
+ * @example
88
+ * // 自定义小数位
89
+ * numberAbbr(123456, ['B','KB','MB'], { precision: 1 }); // "0.1MB"
90
+ * @example
91
+ * // 处理不足基数的情况
92
+ * numberAbbr(500, ['B','KB']); // "500B"
93
+ */
94
+ export function numberAbbr(number: number, units: Array<string>, options?: NumberAbbrOptions): string {
95
+ const { base = 1000, precision = 0 } = options || {};
96
+ const { length } = units;
97
+
98
+ if (length === 0) throw new Error('数字单位组不能为空');
99
+
100
+ let numberFinal = number;
101
+ let step = 0;
102
+
103
+ while (numberFinal >= base && step < length - 1) {
104
+ numberFinal = numberFinal / base;
105
+ step++;
106
+ }
107
+
108
+ const value = numberFixed(numberFinal, { precision, round: -1 });
109
+ const unit = units[step];
110
+
111
+ return `${value}${unit}`;
112
+ }
113
+
114
+ /**
115
+ * 将文件大小转换为带单位缩写的字符串表示
116
+ *
117
+ * @param {number} number - 需要转换的文件大小数值
118
+ * @param {number} [precision=0] - 数值保留的小数位数
119
+ * @returns {string} - 转换后的带单位字符串(如"1.2KB")
120
+ * @example
121
+ * // 基础用法
122
+ * fileSizeAbbr(1024); // "1KB"
123
+ * @example
124
+ * // 自定义小数位
125
+ * fileSizeAbbr(123456, 1); // "0.1MB"
126
+ */
127
+ export function fileSizeAbbr(number: number, precision = 0) {
128
+ return numberAbbr(number, ['B', 'KB', 'MB', 'GB', 'TB'], {
129
+ base: 1024,
130
+ precision,
131
+ });
132
+ }
133
+
134
+ /**
135
+ * 将十进制数转换为指定进制的字符串表示
136
+ *
137
+ * @param {number | bigint} decimal - 需要转换的十进制数,可以是任意长度的数字或大整数
138
+ * @param {string} [dict] - 用于表示进制的字符字典,默认为数字、小写字母和大写字母的组合(62 进制)
139
+ * @returns {string} - 转换后的指定进制字符串
140
+ * @throws {Error} - 如果字符字典的长度小于 2,将抛出错误
141
+ * @example
142
+ * // 默认 62 进制
143
+ * numberConvert(123456789); // "8M0kX"
144
+ * @example
145
+ * // 自定义 16 进制
146
+ * numberConvert(255, '0123456789ABCDEF'); // "FF"
147
+ * @example
148
+ * // 处理大整数
149
+ * numberConvert(9007199254740991n); // "2gosa7pa2GV"
150
+ */
151
+ export function numberConvert(decimal: number | bigint, dict?: string): string {
152
+ const dictFinal = dict || STRING_DICT;
153
+
154
+ if (dictFinal.length < 2) throw new Error('进制转换字典长度不能小于 2');
155
+
156
+ let bigInt = BigInt(decimal);
157
+ const symbol = bigInt < 0n ? '-' : '';
158
+ bigInt = bigInt < 0n ? -bigInt : bigInt;
159
+ const result: Array<string> = [];
160
+ const { length } = dictFinal;
161
+ const bigLength = BigInt(length);
162
+ const calculate = (): void => {
163
+ const y = Number(bigInt % bigLength);
164
+
165
+ bigInt = bigInt / bigLength;
166
+ result.unshift(dictFinal[y]);
167
+
168
+ if (bigInt > 0) {
169
+ calculate();
170
+ }
171
+ };
172
+
173
+ calculate();
174
+
175
+ return symbol + result.join('');
176
+ }
177
+
178
+ /**
179
+ * 数字格式化配置选项
180
+ */
181
+ export type NumberFormatOptions = {
182
+ /**
183
+ * 分隔符字符,用于数字分隔
184
+ * @default ','
185
+ * @example 使用 '_' 分隔符时,123456 会格式化为 '123_456'
186
+ */
187
+ separator?: string;
188
+
189
+ /**
190
+ * 分隔步长,即每隔多少位添加分隔符
191
+ * @default 3
192
+ * @example 步长为 2 时,123456 会格式化为 '12,34,56'
193
+ */
194
+ step?: number;
195
+ };
196
+
197
+ /**
198
+ * 数字格式化
199
+ * @param [number] {number} 数字
200
+ * @param options {NumberFormatOptions} 格式化配置
201
+ * @returns {string} 分割后的字符串
202
+ * @example
203
+ * // 使用默认分隔符和步长
204
+ * numberFormat(123456.789); // => "123,456.789"
205
+ * // 自定义分隔符
206
+ * numberFormat(123456.789, '_'); // => "123_456.789"
207
+ * // 自定义步长
208
+ * numberFormat(123456.789, 2); // => "12,34,56.789"
209
+ * // 使用对象配置
210
+ * numberFormat(123456.789, { separator: '.', step: 4 }); // => "12.3456.789"
211
+ */
212
+ export function numberFormat(number: number, options: NumberFormatOptions): string;
213
+ export function numberFormat(number: number, separator: string): string;
214
+ export function numberFormat(number: number, step: number): string;
215
+ export function numberFormat(number: number): string;
216
+ export function numberFormat(number: number, options?: NumberFormatOptions | string | number) {
217
+ let optionsFinal: Required<NumberFormatOptions> = {
218
+ separator: ',',
219
+ step: 3,
220
+ };
221
+
222
+ if (typeof options === 'string') {
223
+ optionsFinal.separator = options;
224
+ } else if (typeof options === 'number') {
225
+ optionsFinal.step = options;
226
+ } else {
227
+ optionsFinal = objectDefaults(options || {}, optionsFinal) as Required<NumberFormatOptions>;
228
+ }
229
+
230
+ const { separator, step } = optionsFinal;
231
+ const arr = String(number).split('.');
232
+ const re = new RegExp(`(\\d)(?=(\\d{${step}})+(?!\\d))`, 'g');
233
+ const p1 = arr[0].replace(re, `$1${separator}`);
234
+
235
+ return p1 + (arr[1] ? `.${arr[1]}` : '');
236
+ }
@@ -1,4 +1,5 @@
1
- import { AnyObject, MaybePromise } from '../types';
1
+ import type { AnyObject, MaybePromise } from '@/types';
2
+
2
3
  /**
3
4
  * 遍历对象的每个键值对,并对每个键值对执行提供的回调函数。
4
5
  *
@@ -15,7 +16,17 @@ import { AnyObject, MaybePromise } from '../types';
15
16
  * console.log(results); // [['a', 1], ['b', 2], ['c', 3]]
16
17
  * ```
17
18
  */
18
- export declare function objectEach<O extends AnyObject, K extends keyof O & (string | number)>(obj: O, iterator: (this: O, val: O[K], key: K) => false | unknown): void;
19
+ export function objectEach<O extends AnyObject, K extends keyof O & (string | number)>(
20
+ obj: O,
21
+ iterator: (this: O, val: O[K], key: K) => false | unknown,
22
+ ): void {
23
+ for (const [key, val] of Object.entries(obj)) {
24
+ if (iterator.call(obj, val as O[K], key as K) === false) {
25
+ break;
26
+ }
27
+ }
28
+ }
29
+
19
30
  /**
20
31
  * 异步遍历对象的每个键值对,并对每个键值对执行提供的回调函数。
21
32
  *
@@ -33,4 +44,13 @@ export declare function objectEach<O extends AnyObject, K extends keyof O & (str
33
44
  * console.log(results); // [['a', 1], ['b', 2], ['c', 3]]
34
45
  * ```
35
46
  */
36
- export declare function objectEachAsync<O extends AnyObject, K extends keyof O & (string | number)>(obj: O, iterator: (this: O, val: O[K], key: K) => MaybePromise<false | unknown>): Promise<void>;
47
+ export async function objectEachAsync<O extends AnyObject, K extends keyof O & (string | number)>(
48
+ obj: O,
49
+ iterator: (this: O, val: O[K], key: K) => MaybePromise<false | unknown>,
50
+ ): Promise<void> {
51
+ for (const [key, val] of Object.entries(obj)) {
52
+ if ((await iterator.call(obj, val as O[K], key as K)) === false) {
53
+ break;
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,273 @@
1
+ import { isArray, isObject, isUndefined } from '@/type';
2
+ import type { AnyArray, AnyObject } from '@/types';
3
+
4
+ // @ref https://stackoverflow.com/a/67609485
5
+
6
+ type Idx<T, K> = K extends keyof T
7
+ ? T[K]
8
+ : number extends keyof T
9
+ ? K extends `${number}`
10
+ ? T[number]
11
+ : never
12
+ : never;
13
+
14
+ type Join<K, P> = K extends string | number
15
+ ? P extends string | number
16
+ ? `${K}${'' extends P ? '' : '.'}${P}`
17
+ : never
18
+ : never;
19
+
20
+ type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]];
21
+
22
+ export type ObjectPath<O, D extends number = 4> = [D] extends [never]
23
+ ? never
24
+ : O extends object
25
+ ? {
26
+ [K in keyof O]-?: K extends string | number ? `${K}` | Join<K, ObjectPath<O[K], Prev[D]>> : never;
27
+ }[keyof O]
28
+ : '';
29
+
30
+ export type ObjectLeafPath<O, D extends number = 4> = [D] extends [never]
31
+ ? never
32
+ : O extends object
33
+ ? {
34
+ [K in keyof O]-?: K extends string | number
35
+ ? O[K] extends string | number
36
+ ? `${K}` | Join<K, ObjectLeafPath<O[K], Prev[D]>>
37
+ : Join<K, ObjectLeafPath<O[K], Prev[D]>>
38
+ : never;
39
+ }[keyof O]
40
+ : '';
41
+
42
+ export type ObjectPathValue<O, P extends ObjectPath<O, 4>> = P extends `${infer Key}.${infer Rest}`
43
+ ? Rest extends ObjectPath<Idx<O, Key>, 4>
44
+ ? ObjectPathValue<Idx<O, Key>, Rest>
45
+ : never
46
+ : Idx<O, P>;
47
+
48
+ function pathToKeys(path: string | string[]) {
49
+ // 下文用到该数组时会进行修改操作,因此复制一份
50
+ if (isArray(path)) return [...path];
51
+
52
+ let pathFinal = path.replace(/\[(\w+)\]/g, '.$1');
53
+ pathFinal = pathFinal.replace(/^\./, '');
54
+ return pathFinal.split('.');
55
+ }
56
+
57
+ function isObjectOrArray(v: unknown) {
58
+ return isObject(v) || isArray(v);
59
+ }
60
+
61
+ /**
62
+ * 表示对象节点的信息。
63
+ *
64
+ * @template V - 键值的类型。
65
+ */
66
+ export type TObjectNode<V = unknown | undefined> = {
67
+ /**
68
+ * 当前节点的父级对象。
69
+ */
70
+ parent: unknown | undefined;
71
+
72
+ /**
73
+ * 当前节点的键名路径。
74
+ */
75
+ keys: string[];
76
+
77
+ /**
78
+ * 当前节点的键名。
79
+ */
80
+ key: string | undefined;
81
+
82
+ /**
83
+ * 当前节点的键值。
84
+ */
85
+ value: V;
86
+ };
87
+
88
+ /**
89
+ * 根据属性路径获取属性值
90
+ * @param {O} obj
91
+ * @param {string | string[] | P} path
92
+ * @returns {TObjectNode<O>}
93
+ * 根据属性路径获取属性值。
94
+ *
95
+ * @template O - 目标对象的类型。
96
+ * @template P - 属性路径的类型。
97
+ * @param {O} obj - 要操作的目标对象。
98
+ * @param {P | string | string[]} path - 属性路径,可以是字符串或字符串数组。支持点分隔符(如 "a.b.c")或数组形式(如 ["a", "b", "c"])。
99
+ * @returns {TObjectNode<O>} 返回一个包含父级、键名路径、键名和键值的对象节点。
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const obj = { a: { b: { c: 42 } } };
104
+ * const result = objectGet(obj, 'a.b.c');
105
+ * console.log(result.value); // 输出 42
106
+ * ```
107
+ */
108
+ export function objectGet<O extends AnyObject, P extends ObjectPath<O>>(
109
+ obj: O,
110
+ path: P | string | string[],
111
+ ): TObjectNode<O> {
112
+ const keys = pathToKeys(path);
113
+ const lastKey = keys.pop();
114
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
115
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
116
+ let parent: any = obj;
117
+ const keysFinal: string[] = [];
118
+
119
+ for (let i = 0; i < keys.length; i++) {
120
+ const key = keys[i];
121
+
122
+ keysFinal.push(key);
123
+ if (!isObjectOrArray(parent)) break;
124
+
125
+ // @ts-ignore
126
+ parent = parent[key];
127
+ }
128
+
129
+ return {
130
+ parent: parent,
131
+ keys: keysFinal,
132
+ key: lastKey,
133
+ // @ts-ignore
134
+ value: isObjectOrArray(parent) && lastKey ? parent[lastKey] : undefined,
135
+ };
136
+ }
137
+
138
+ // /**
139
+ // * 根据路径获取对象叶子节点值
140
+ // * @param {O} obj
141
+ // * @param {P} path
142
+ // * @returns {ObjectNode<O>}
143
+ // */
144
+ // export function objectLeaf<O extends AnyObject, P extends ObjectLeafPath<O>>(obj: O, path: P) {
145
+ // return objectGet(obj, path);
146
+ // }
147
+
148
+ /**
149
+ * 配置选项,用于控制 `objectSet` 的行为。
150
+ *
151
+ * @template O - 目标对象的类型。
152
+ */
153
+ export type TObjectSetOptions<O extends AnyObject> = {
154
+ /**
155
+ * 在设置值之前调用的钩子函数。
156
+ * 如果返回 `false`,则阻止设置值。
157
+ *
158
+ * @param {TObjectNode<O> & { key: string }} node - 当前节点信息。
159
+ * @returns {boolean | undefined | void} 返回 `false` 时阻止设置值。
160
+ */
161
+ // biome-ignore lint/suspicious/noConfusingVoidType: <explanation>
162
+ beforeSet(node: TObjectNode<O> & { key: string }): boolean | undefined | void;
163
+
164
+ /**
165
+ * 当遇到未定义的中间节点时调用的钩子函数。
166
+ * 返回值将用于创建中间节点。
167
+ *
168
+ * @param {TObjectNode<O>} node - 当前节点信息。
169
+ * @returns {AnyObject | AnyArray | undefined | void} 返回值将用于创建中间节点。
170
+ */
171
+ // biome-ignore lint/suspicious/noConfusingVoidType: <explanation>
172
+ undefinedSet(node: TObjectNode<O>): AnyObject | AnyArray | undefined | void;
173
+ };
174
+
175
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
177
+ const defaultObjectSetOptions: TObjectSetOptions<any> = {
178
+ beforeSet: () => true,
179
+ undefinedSet: () => ({}),
180
+ };
181
+
182
+ /**
183
+ * 根据属性路径设置属性值
184
+ * @param {AnyObject} obj
185
+ * @param {string} path
186
+ * @param {V} val
187
+ * @param {Partial<TObjectSetOptions<O>>} options
188
+ * @returns {TObjectNode<O, V>}
189
+ * 根据属性路径设置属性值。
190
+ *
191
+ * @template O - 目标对象的类型。
192
+ * @template V - 要设置的值的类型。
193
+ * @param {O} obj - 要操作的目标对象。
194
+ * @param {string | string[]} path - 属性路径,可以是字符串或字符串数组。支持点分隔符(如 "a.b.c")或数组形式(如 ["a", "b", "c"])。
195
+ * @param {V} val - 要设置的值。
196
+ * @param {Partial<TObjectSetOptions<O>>} [options] - 可选配置项,用于控制设置行为。
197
+ * @returns {TObjectNode<V>} 返回一个包含父级、键名路径、键名和键值的对象节点。
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const obj = {};
202
+ * objectSet(obj, 'a.b.c', 42);
203
+ * console.log(obj); // 输出 { a: { b: { c: 42 } } }
204
+ *
205
+ * objectSet(obj, 'a.b.c', 100, {
206
+ * beforeSet: (node) => node.key === 'c',
207
+ * });
208
+ * console.log(obj); // 输出 { a: { b: { c: 100 } } }
209
+ * ```
210
+ */
211
+ export function objectSet<O extends AnyObject, V>(
212
+ obj: O,
213
+ path: string | string[],
214
+ val: V,
215
+ options?: Partial<TObjectSetOptions<O>>,
216
+ ): TObjectNode<V> {
217
+ const { beforeSet, undefinedSet } = Object.assign({}, defaultObjectSetOptions, options);
218
+ const keys = pathToKeys(path);
219
+ const lastKey = keys.pop();
220
+ let parent = obj;
221
+ let stopped = false;
222
+ const keysFinal: string[] = [];
223
+
224
+ for (const key of keys) {
225
+ let val = parent[key];
226
+ keysFinal.push(key);
227
+
228
+ if (isUndefined(val)) {
229
+ const seted = undefinedSet({
230
+ parent: parent,
231
+ keys: keysFinal,
232
+ key: key,
233
+ value: val,
234
+ });
235
+
236
+ if (!seted) {
237
+ stopped = true;
238
+ break;
239
+ }
240
+
241
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
242
+ // @ts-ignore
243
+ val = parent[key] = seted;
244
+ }
245
+
246
+ // @ts-ignore
247
+ parent = val;
248
+ }
249
+
250
+ if (!stopped && !isUndefined(lastKey)) {
251
+ keysFinal.push(lastKey);
252
+
253
+ if (
254
+ beforeSet({
255
+ parent: parent,
256
+ keys: keysFinal,
257
+ key: lastKey,
258
+ value: parent[lastKey],
259
+ })
260
+ ) {
261
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
262
+ // @ts-ignore
263
+ parent[lastKey] = val;
264
+ }
265
+ }
266
+
267
+ return {
268
+ keys: keysFinal,
269
+ parent: parent,
270
+ key: lastKey,
271
+ value: val,
272
+ };
273
+ }
@@ -0,0 +1,128 @@
1
+ import { isArray, isObject, isString, typeIs } from '@/type';
2
+ import type { AnyArray, AnyFunction, AnyObject } from '@/types';
3
+
4
+ /**
5
+ * 检查一个对象是否为空对象(不包含任何自有属性,包括符号属性)。
6
+ *
7
+ * @param obj - 要检查的对象
8
+ * @returns 如果对象没有自有属性(包括符号属性)则返回 true,否则返回 false
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * isEmptyObject({}); // true
13
+ * isEmptyObject({ a: 1 }); // false
14
+ * isEmptyObject(Object.create(null)); // true
15
+ * isEmptyObject({ [Symbol('key')]: 'value' }); // false
16
+ * ```
17
+ */
18
+ export function isEmptyObject(obj: AnyObject): boolean {
19
+ return Object.getOwnPropertyNames(obj).length === 0 && Object.getOwnPropertySymbols(obj).length === 0;
20
+ }
21
+
22
+ /**
23
+ * 检查一个对象是否为纯对象(通过对象字面量或Object构造函数创建,而非其他构造函数的实例)。
24
+ *
25
+ * @param obj - 要检查的对象
26
+ * @returns 如果是纯对象则返回 true,否则返回 false
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * isPlainObject({}); // true
31
+ * isPlainObject(Object.create(null)); // true
32
+ * isPlainObject(new Date()); // false
33
+ * isPlainObject([]); // false
34
+ * isPlainObject(() => {}); // false
35
+ * ```
36
+ */
37
+ export function isPlainObject(obj: AnyObject): boolean {
38
+ const proto: unknown = Object.getPrototypeOf(obj);
39
+
40
+ // 对象无原型
41
+ if (!proto) return true;
42
+
43
+ // 是否对象直接实例
44
+ return proto === Object.prototype;
45
+ }
46
+
47
+ // 移除,原因是,定义对象尽可能的使用 type 关键字即可避开此问题
48
+ // /**
49
+ // * 精确对象,常用于联合类型判断
50
+ // * 相关 bug:https://l.ydr.me/Zp88vFKc
51
+ // */
52
+ // // biome-ignore lint/suspicious/noExplicitAny: <explanation>
53
+ // export type ExactObject<T = any> = T extends AnyFunction
54
+ // ? never
55
+ // : T extends AnyArray
56
+ // ? never
57
+ // : T extends object
58
+ // ? T
59
+ // : never;
60
+ //
61
+ // /**
62
+ // * 检查值是否为精确接口对象
63
+ // * @param object - 传入对象,必须是一个对象与其他类型的联合
64
+ // * @returns 如果值为对象则返回 true,否则返回 false
65
+ // * @example
66
+ // * ```typescript
67
+ // * type Id = string | string[] | (() => string);
68
+ // *
69
+ // * interface Cache {
70
+ // * id?: Id;
71
+ // * }
72
+ // *
73
+ // * type Share = {
74
+ // * id?: Id;
75
+ // * }
76
+ // *
77
+ // * interface Options {
78
+ // * cache?: Id | Cache;
79
+ // * share?: Id | Share;
80
+ // * }
81
+ // *
82
+ // * function test(options: Options) {
83
+ // * // string | string[] | (() => string) | Cache | undefined
84
+ // * const cache = options.cache;
85
+ // *
86
+ // * // Cache
87
+ // * // 需要使用
88
+ // * if (isExactObject(cache)) {
89
+ // * cache.id;
90
+ // * }
91
+ // * // string[]
92
+ // * else if (isArray(cache)) {
93
+ // * cache.push();
94
+ // * }
95
+ // * // string
96
+ // * else if (isString(cache)) {
97
+ // * cache.charCodeAt(0);
98
+ // * }
99
+ // * // (() => string) | undefined
100
+ // * else {
101
+ // * cache?.();
102
+ // * }
103
+ // *
104
+ // * // string | string[] | (() => string) | Share | undefined
105
+ // * const share = options.share;
106
+ // *
107
+ // * // Share
108
+ // * if (isObject(share)) {
109
+ // * share.id;
110
+ // * }
111
+ // * // string[]
112
+ // * else if (isArray(share)) {
113
+ // * share.push();
114
+ // * }
115
+ // * // string
116
+ // * else if (isString(share)) {
117
+ // * share.charCodeAt(0);
118
+ // * }
119
+ // * // (() => string) | undefined
120
+ // * else {
121
+ // * share?.();
122
+ // * }
123
+ // * }
124
+ // * ```
125
+ // */
126
+ // export function isExactObject<T>(object: T): object is ExactObject<T> {
127
+ // return typeIs(object) === 'object';
128
+ // }