@movk/core 1.0.3 → 1.2.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/dist/index.d.ts CHANGED
@@ -1,1790 +1,2444 @@
1
- import { MaybeRefOrGetter, VNode, Ref, Component, DefineComponent } from 'vue';
2
- import { z } from 'zod/v4';
1
+ import { Ref, VNode, MaybeRefOrGetter, Component, DefineComponent } from 'vue';
2
+
3
+ type StorageType = 'localStorage' | 'sessionStorage';
4
+ interface StorageConfig<T = unknown> {
5
+ key: string;
6
+ defaultValue: T;
7
+ prefix: string;
8
+ storage: StorageType;
9
+ }
10
+ type StorageConfigInput<T = unknown> = Partial<Omit<StorageConfig<T>, 'key' | 'defaultValue'>> & {
11
+ key: string;
12
+ defaultValue: T;
13
+ };
14
+ interface AppStorageReturn<T> {
15
+ state: Ref<T>;
16
+ getItem: () => T;
17
+ setItem: (value: T) => void;
18
+ removeItem: () => void;
19
+ }
20
+
21
+ /**
22
+ * 应用存储管理的组合式函数,支持 localStorage 和 sessionStorage
23
+ *
24
+ * @category Composables
25
+ * @param config 存储配置对象
26
+ * @returns 存储管理对象,包含响应式状态和操作方法
27
+ * @example
28
+ * ```ts
29
+ * // 创建存储管理实例
30
+ * const { state, setItem, getItem, removeItem } = useAppStorage({
31
+ * key: 'user-preferences',
32
+ * defaultValue: {
33
+ * theme: 'light',
34
+ * language: 'zh-CN',
35
+ * fontSize: 16
36
+ * },
37
+ * storage: 'localStorage',
38
+ * prefix: 'app'
39
+ * })
40
+ *
41
+ * // 使用响应式状态
42
+ * console.log(state.value.theme) // 'light'
43
+ *
44
+ * // 更新设置
45
+ * setItem({
46
+ * theme: 'dark',
47
+ * language: 'en-US',
48
+ * fontSize: 18
49
+ * })
50
+ * ```
51
+ */
52
+ declare function useAppStorage<T = unknown>(config: StorageConfigInput<T>): AppStorageReturn<T>;
53
+
54
+ /**
55
+ * 复制文本到剪贴板的组合式函数
56
+ *
57
+ * @category Composables
58
+ * @param text 要复制的文本内容
59
+ * @returns 复制是否成功的Promise
60
+ * @example
61
+ * ```ts
62
+ * // 复制简单文本
63
+ * const copyText = async () => {
64
+ * const success = await useCopyCode('Hello, World!')
65
+ * if (success) {
66
+ * console.log('复制成功')
67
+ * } else {
68
+ * console.log('复制失败')
69
+ * }
70
+ * }
71
+ *
72
+ * // 复制代码块
73
+ * const copyCodeBlock = async () => {
74
+ * const code = `
75
+ * function hello() {
76
+ * console.log('Hello, World!')
77
+ * }
78
+ * `
79
+ * const success = await useCopyCode(code)
80
+ * if (success) {
81
+ * // 显示复制成功提示
82
+ * showNotification('代码已复制到剪贴板')
83
+ * }
84
+ * }
85
+ *
86
+ * // 在点击事件中使用
87
+ * const handleCopy = () => {
88
+ * useCopyCode(document.getElementById('code').textContent)
89
+ * }
90
+ * ```
91
+ */
92
+ declare function useCopyCode(text: string): Promise<boolean>;
93
+
94
+ /**
95
+ * 将SVG字符串转换为PNG格式的Blob对象
96
+ *
97
+ * @category File
98
+ * @param svg SVG字符串
99
+ * @returns PNG格式的Blob对象
100
+ * @throws 当SVG无效或转换失败时抛出错误
101
+ * @example
102
+ * ```ts
103
+ * const svgString = '<svg width="100" height="100"><circle cx="50" cy="50" r="40" fill="red"/></svg>'
104
+ *
105
+ * try {
106
+ * const pngBlob = await convertSvgToPng(svgString)
107
+ * const url = URL.createObjectURL(pngBlob)
108
+ *
109
+ * // 用于下载或显示
110
+ * const img = document.createElement('img')
111
+ * img.src = url
112
+ * document.body.appendChild(img)
113
+ * } catch (error) {
114
+ * console.error('SVG转换失败:', error)
115
+ * }
116
+ * ```
117
+ */
118
+ declare function convertSvgToPng(svg: string): Promise<Blob>;
119
+
120
+ /**
121
+ * 从响应头中提取文件名
122
+ *
123
+ * @category File
124
+ * @param headers 响应头对象
125
+ * @param fallbackName 默认文件名
126
+ * @returns 提取的文件名
127
+ * @example
128
+ * ```ts
129
+ * // 从响应头中提取文件名
130
+ * const headers = new Headers({
131
+ * 'content-disposition': 'attachment; filename="report.pdf"'
132
+ * })
133
+ * const filename = extractFilename(headers, 'download')
134
+ * console.log(filename) // 'report.pdf'
135
+ *
136
+ * // 处理编码的文件名
137
+ * const encodedHeaders = new Headers({
138
+ * 'content-disposition': 'attachment; filename*=UTF-8\'\'%E6%8A%A5%E5%91%8A.pdf'
139
+ * })
140
+ * const encodedFilename = extractFilename(encodedHeaders)
141
+ * console.log(encodedFilename) // '报告.pdf'
142
+ * ```
143
+ */
144
+ declare function extractFilename(headers?: Headers, fallbackName?: string): string;
145
+
146
+ /**
147
+ * 格式化文件大小,将字节数转换为可读的文件大小字符串
148
+ *
149
+ * @category File
150
+ * @param bytes 文件大小(字节)
151
+ * @returns 格式化后的文件大小字符串
152
+ * @example
153
+ * ```ts
154
+ * console.log(formatFileSize(1024)) // '1 KB'
155
+ * console.log(formatFileSize(1536)) // '1.5 KB'
156
+ * console.log(formatFileSize(1048576)) // '1 MB'
157
+ * console.log(formatFileSize(1073741824)) // '1 GB'
158
+ *
159
+ * // 处理边界情况
160
+ * console.log(formatFileSize(0)) // '0 Bytes'
161
+ * console.log(formatFileSize(-100)) // '0 Bytes'
162
+ * ```
163
+ */
164
+ declare function formatFileSize(bytes: number): string;
165
+
166
+ /**
167
+ * 替换SVG文件中的currentColor为指定颜色
168
+ *
169
+ * @category File
170
+ * @param path SVG文件路径
171
+ * @param color 替换的颜色值,不提供则返回原始SVG
172
+ * @returns 处理后的SVG字符串
173
+ * @throws 当文件获取失败或SVG无效时抛出错误
174
+ * @example
175
+ * ```ts
176
+ * // 获取并替换SVG中的currentColor
177
+ * try {
178
+ * const svgContent = await replaceCurrentColor('/icons/star.svg', '#ff0000')
179
+ * const container = document.createElement('div')
180
+ * container.innerHTML = svgContent
181
+ * document.body.appendChild(container)
182
+ * } catch (error) {
183
+ * console.error('SVG处理失败:', error)
184
+ * }
185
+ *
186
+ * // 只获取SVG内容,不替换颜色
187
+ * const originalSvg = await replaceCurrentColor('/icons/star.svg')
188
+ * ```
189
+ */
190
+ declare function replaceCurrentColor(path: string, color?: string): Promise<string>;
191
+
192
+ /**
193
+ * 触发浏览器下载文件
194
+ *
195
+ * @category File
196
+ * @param blob 文件数据
197
+ * @param filename 文件名
198
+ * @example
199
+ * ```ts
200
+ * // 下载文本文件
201
+ * const textBlob = new Blob(['Hello, World!'], { type: 'text/plain' })
202
+ * triggerDownload(textBlob, 'hello.txt')
203
+ *
204
+ * // 下载JSON数据
205
+ * const data = { name: 'John', age: 30 }
206
+ * const jsonBlob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
207
+ * triggerDownload(jsonBlob, 'data.json')
208
+ *
209
+ * // 下载图片
210
+ * const canvas = document.createElement('canvas')
211
+ * canvas.toBlob((blob) => {
212
+ * if (blob) {
213
+ * triggerDownload(blob, 'image.png')
214
+ * }
215
+ * })
216
+ * ```
217
+ */
218
+ declare function triggerDownload(blob: Blob, filename: string): void;
219
+
220
+ /**
221
+ * 深拷贝任意 JavaScript 值。
222
+ *
223
+ * - 优先使用原生 `structuredClone`(若可用),覆盖 `Map`/`Set`/`TypedArray`/`ArrayBuffer` 等内建类型。
224
+ * - 对不支持 `structuredClone` 的环境,使用回退实现:
225
+ * - 支持循环引用(`WeakMap` 记忆化)。
226
+ * - 保留原型与属性描述符(含 getter/setter),复制 symbol 键。
227
+ * - 内建类型专项处理:`Date`/`RegExp`/`Map`/`Set`/`ArrayBuffer`/`TypedArray`/`URL`/`Error`。
228
+ *
229
+ * @category Object
230
+ * @typeParam T 拷贝值的类型
231
+ * @param obj 要被深拷贝的值
232
+ * @param cache 内部使用的 `WeakMap`(循环引用记忆化),一般不需要传入
233
+ * @returns 新的深拷贝值,与输入值结构等价、引用独立
234
+ *
235
+ * @example
236
+ * ```ts
237
+ * const source = { a: 1, d: new Date(), m: new Map([[1, { x: 2 }]]) }
238
+ * const cloned = deepClone(source)
239
+ * cloned !== source // true
240
+ * cloned.d !== source.d // true
241
+ * cloned.m !== source.m // true
242
+ * cloned.m.get(1) !== source.m.get(1) // true
243
+ * ```
244
+ *
245
+ * @remarks
246
+ * 若对象包含不可克隆资源(如带有原生句柄的自定义对象),请在外层进行自定义序列化逻辑或为该类型添加专用分支。
247
+ */
248
+ declare function deepClone<T>(obj: T, cache?: WeakMap<object, any>): T;
249
+
250
+ type UnknownObject = Record<string, unknown>;
251
+ type AnyObject = Record<string, any>;
252
+ /**
253
+ * Vue 渲染相关文本/节点类型: 可为字符串、`VNode` 或返回 `VNode` 的函数。
254
+ * @example
255
+ * // 在渲染 API 中允许三种形态:
256
+ * // - '标题'
257
+ * // - h('div', '标题') 产生的 VNode
258
+ * // - () => h('div', '标题') 的惰性渲染函数
259
+ */
260
+ type StringOrVNode = string | VNode | (() => VNode);
261
+ /**
262
+ * 合并两个对象类型,U 中的属性会覆盖 T 中的属性
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * type T = { a: number, c: string }
267
+ * type U = { a: string, b: boolean }
268
+ * type M = Merge<T, U> // { a: string, b: boolean, c: string }
269
+ * ```
270
+ */
271
+ type Merge<T, U> = Omit<T, keyof U> & U;
272
+ /**
273
+ * 判断类型 T 是否为纯对象类型
274
+ * 纯对象是指普通的对象字面量,排除数组、函数、Date 等特殊对象类型
275
+ * @example
276
+ * ```ts
277
+ * type Test1 = IsPlainObject<{ a: number }> // true
278
+ * type Test2 = IsPlainObject<string[]> // false
279
+ * type Test3 = IsPlainObject<() => void> // false
280
+ * type Test4 = IsPlainObject<Date> // false
281
+ * type Test5 = IsPlainObject<string> // false
282
+ * type Test6 = IsPlainObject<null> // false
283
+ * ```
284
+ */
285
+ type IsPlainObject<T> = NonNullable<T> extends Record<string, any> ? NonNullable<T> extends any[] ? false : NonNullable<T> extends (...args: any[]) => any ? false : NonNullable<T> extends Date ? false : true : false;
286
+
287
+ /**
288
+ * 递归将对象类型 `T` 的所有属性变为可选(深可选)。
289
+ * @typeParam T - 源对象类型
290
+ * @example
291
+ * // type Src = { a: { b: number } }
292
+ * // type R = DeepPartial<Src>
293
+ * // 结果: R 为 { a?: { b?: number | undefined } | undefined }
294
+ */
295
+ type DeepPartial<T> = {
296
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P] | undefined;
297
+ };
298
+ /** 递归深度计数器,用于限制类型递归层数 */
299
+ type MergeDepth = [never, 0, 1, 2, 3, 4];
300
+ /**
301
+ * 递归合并两个对象类型,`U` 中的属性优先级高于 `T`。
302
+ * 仅对双方都是纯对象的属性做深度递归,其余类型直接取 `U` 的值。
303
+ *
304
+ * @typeParam T - 基础对象类型
305
+ * @typeParam U - 覆盖对象类型
306
+ * @typeParam D - 递归深度限制,默认为 4
307
+ *
308
+ * @example
309
+ * ```ts
310
+ * type A = { a: { b: number; c: string }; d: boolean }
311
+ * type B = { a: { b: string; e: number }; f: Date }
312
+ * type R = DeepMerge<A, B>
313
+ * // { a: { b: string; c: string; e: number }; d: boolean; f: Date }
314
+ * ```
315
+ */
316
+ type DeepMerge<T, U, D extends number = 4> = [D] extends [never] ? T & U : {
317
+ [K in keyof T | keyof U]: K extends keyof U ? K extends keyof T ? IsPlainObject<T[K]> extends true ? IsPlainObject<U[K]> extends true ? DeepMerge<NonNullable<T[K]>, NonNullable<U[K]>, MergeDepth[D]> : U[K] : U[K] : U[K] : K extends keyof T ? T[K] : never;
318
+ };
319
+
320
+ /**
321
+ * 深度控制类型,用于限制类型递归的深度
322
+ * 防止类型计算超出 TypeScript 的递归限制
323
+ */
324
+ type Depth = [never, 0, 1, 2, 3, 4];
325
+ /**
326
+ * 当 `MaybeObject` 为对象时,返回键 `Key` 对应的属性类型; 否则为 `never`。
327
+ * @typeParam MaybeObject - 可能为对象的类型
328
+ * @typeParam Key - 目标键名(string)
329
+ * @example
330
+ * // type Obj = { id: number }
331
+ * // type R1 = GetObjectField<Obj, 'id'> // 结果: number
332
+ * // type R2 = GetObjectField<string, 'id'> // 结果: never
333
+ */
334
+ type GetObjectField<MaybeObject, Key extends string> = MaybeObject extends Record<string, any> ? MaybeObject[Key] : never;
335
+ /**
336
+ * 提取对象的嵌套键,支持点语法路径
337
+ *
338
+ * @template T 源对象类型
339
+ * @template D 递归深度,默认为2
340
+ * @example
341
+ * ```ts
342
+ * type User = {
343
+ * name: string
344
+ * address: {
345
+ * city: string
346
+ * country: string
347
+ * }
348
+ * }
349
+ * type Keys = NestedKeys<User> // 'name' | 'address' | 'address.city' | 'address.country'
350
+ * ```
351
+ */
352
+ type NestedKeys<T, D extends number = 2> = [D] extends [never] ? never : {
353
+ [K in keyof T & string]: IsPlainObject<T[K]> extends true ? K | `${K}.${NestedKeys<NonNullable<T[K]>, Depth[D]>}` : K;
354
+ }[keyof T & string];
355
+ /**
356
+ * 提取对象中所有纯对象字段的键(包括嵌套的),支持点语法路径
357
+ *
358
+ * @template T 源对象类型
359
+ * @template D 递归深度,默认为2
360
+ * @example
361
+ * ```ts
362
+ * type User = {
363
+ * name: string
364
+ * age: number
365
+ * address: {
366
+ * city: string
367
+ * location: {
368
+ * lat: number
369
+ * lng: number
370
+ * }
371
+ * }
372
+ * }
373
+ * type ObjectKeys = ObjectFieldKeys<User> // 'address' | 'address.location'
374
+ * ```
375
+ */
376
+ type ObjectFieldKeys<T, D extends number = 2> = [D] extends [never] ? never : {
377
+ [K in keyof T & string]: IsPlainObject<T[K]> extends true ? K | `${K}.${ObjectFieldKeys<NonNullable<T[K]>, Depth[D]>}` : never;
378
+ }[keyof T & string];
379
+ /**
380
+ * 提取对象中所有非对象字段的键
381
+ * 排除纯对象字段,只保留原始类型字段的键
382
+ *
383
+ * @template T 源对象类型
384
+ * @example
385
+ * ```ts
386
+ * type User = {
387
+ * name: string
388
+ * age: number
389
+ * address: {
390
+ * city: string
391
+ * }
392
+ * }
393
+ * type NonObjectKeys = NonObjectFieldKeys<User> // 'name' | 'age' | 'address.city'
394
+ * ```
395
+ */
396
+ type NonObjectFieldKeys<T> = Exclude<NestedKeys<T>, ObjectFieldKeys<T>>;
397
+ /**
398
+ * 提取对象中所有数组字段的键(包括嵌套的),支持点语法路径
399
+ *
400
+ * @template T 源对象类型
401
+ * @template D 递归深度,默认为2
402
+ * @example
403
+ * ```ts
404
+ * type User = {
405
+ * name: string
406
+ * tags: string[]
407
+ * posts: Array<{ title: string }>
408
+ * profile: {
409
+ * hobbies: string[]
410
+ * }
411
+ * }
412
+ * type ArrayKeys = ArrayFieldKeys<User> // 'tags' | 'posts' | 'profile.hobbies'
413
+ * ```
414
+ */
415
+ type ArrayFieldKeys<T, D extends number = 2> = [D] extends [never] ? never : {
416
+ [K in keyof T & string]: NonNullable<T[K]> extends any[] ? K : IsPlainObject<T[K]> extends true ? `${K}.${ArrayFieldKeys<NonNullable<T[K]>, Depth[D]>}` : never;
417
+ }[keyof T & string];
418
+ /**
419
+ * 根据路径字符串提取对象属性的类型,支持点语法和嵌套对象
420
+ * @example GetFieldValue<User, 'tags'> // string[]
421
+ * @example GetFieldValue<User, 'profile.bio'> // string
422
+ */
423
+ type GetFieldValue<T, P extends string> = P extends keyof T ? T[P] : P extends `${infer K}.${infer Rest}` ? K extends keyof T ? T[K] extends undefined ? undefined : GetFieldValue<NonNullable<T[K]>, Rest> : unknown : unknown;
424
+
425
+ /**
426
+ * 依据键名从对象类型 `T` 中剔除键 `K`。
427
+ * @typeParam T - 源对象类型
428
+ * @typeParam K - 要剔除的键(必须来自 `keyof T`)
429
+ * @example
430
+ * // type User = { id: string; name: string; age: number }
431
+ * // type R = OmitByKey<User, 'age'>
432
+ * // 结果: R 为 { id: string; name: string }
433
+ */
434
+ type OmitByKey<T, K extends keyof T> = {
435
+ [P in keyof T as P extends K ? never : P]: T[P];
436
+ };
437
+ /**
438
+ * 依据键名从对象类型 `T` 中挑选键 `K`。
439
+ * @typeParam T - 源对象类型
440
+ * @typeParam K - 要保留的键(必须来自 `keyof T`)
441
+ * @example
442
+ * // type User = { id: string; name: string; age: number }
443
+ * // type R = PickByKey<User, 'id' | 'name'>
444
+ * // 结果: R 为 { id: string; name: string }
445
+ */
446
+ type PickByKey<T, K extends keyof T> = {
447
+ [P in keyof T as P extends K ? P : never]: T[P];
448
+ };
449
+ /**
450
+ * 基于映射表 `Mapping` 对对象类型 `T` 的键进行重命名。
451
+ * 未在映射表中的键保持原名; 映射值为 `PropertyKey`(string/number/symbol)。
452
+ * @typeParam T - 源对象类型
453
+ * @typeParam Mapping - 旧键到新键名的映射
454
+ * @example
455
+ * // type Src = { a: number; b: string }
456
+ * // type R = RenameKeys<Src, { a: 'id' }>
457
+ * // 结果: R 为 { id: number; b: string }
458
+ */
459
+ type RenameKeys<T, Mapping extends {
460
+ [K in keyof T]?: PropertyKey;
461
+ }> = {
462
+ [K in keyof T as K extends keyof Mapping ? Exclude<Mapping[K], undefined> : K]: T[K];
463
+ };
464
+ /**
465
+ * 将对象类型 `T` 中的键 `K` 标记为必填(移除可选修饰)。
466
+ * @typeParam T - 源对象类型
467
+ * @typeParam K - 设为必填的键
468
+ * @example
469
+ * // type User = { id: string; name?: string }
470
+ * // type R = RequiredByKeys<User, 'name'>
471
+ * // 结果: R['name'] 为必填的 string
472
+ */
473
+ type RequiredByKeys<T, K extends keyof T> = T & {
474
+ [P in K]-?: T[P];
475
+ };
476
+ /**
477
+ * 将对象类型 `T` 中的键 `K` 标记为可选。
478
+ * @typeParam T - 源对象类型
479
+ * @typeParam K - 设为可选的键
480
+ * @example
481
+ * // type User = { id: string; name: string }
482
+ * // type R = PartialByKeys<User, 'name'>
483
+ * // 结果: R['name'] 为可选(可能为 undefined)
484
+ */
485
+ type PartialByKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
486
+ /**
487
+ * 将对象类型 `T` 中的键 `K` 标记为只读(浅只读)。
488
+ * @typeParam T - 源对象类型
489
+ * @typeParam K - 设为只读的键
490
+ * @example
491
+ * // type User = { id: string; name: string }
492
+ * // type R = ReadonlyByKeys<User, 'id'>
493
+ * // 结果: R['id'] 不可被重新赋值
494
+ */
495
+ type ReadonlyByKeys<T, K extends keyof T> = T & {
496
+ readonly [P in K]: T[P];
497
+ };
498
+ /**
499
+ * 取消对象类型 `T` 中键 `K` 的只读限制,使其可写(浅层)。
500
+ * @typeParam T - 源对象类型
501
+ * @typeParam K - 取消只读的键
502
+ * @example
503
+ * // type User = { readonly id: string; name: string }
504
+ * // type R = MutableByKeys<User, 'id'>
505
+ * // 结果: R['id'] 变为可写
506
+ */
507
+ type MutableByKeys<T, K extends keyof T> = {
508
+ -readonly [P in K]: T[P];
509
+ } & Omit<T, K>;
510
+
511
+ /**
512
+ * 将联合类型 `U` 转换为交叉类型,用于合并联合成员的属性。
513
+ * @typeParam U - 联合类型
514
+ * @example
515
+ * // type U = { a: number } | { b: string }
516
+ * // type R = UnionToIntersection<U>
517
+ * // 结果: R 为 { a: number } & { b: string }
518
+ */
519
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? {
520
+ [K in keyof I]: I[K];
521
+ } : never;
522
+ /**
523
+ * 若对象 `T` 在键 `K` 处的类型为元组,则提取其首个元素类型,否则为 `never`。
524
+ * @typeParam T - 具有元组属性的对象类型
525
+ * @typeParam K - 属性键
526
+ * @example
527
+ * // type Cfg = { params: [id: string, flag?: boolean] }
528
+ * // type R = FirstParam<Cfg, 'params'>
529
+ * // 结果: R 为 string
530
+ */
531
+ type FirstParam<T, K extends keyof T> = T[K] extends [infer P, ...any[]] ? P : never;
532
+ /**
533
+ * 从函数类型中提取首个参数类型; 若 `T` 非函数类型,则为 `undefined`。
534
+ * @typeParam T - 函数类型
535
+ * @example
536
+ * // type Fn = (x: number, y: string) => void
537
+ * // type R = FirstParameter<Fn>
538
+ * // 结果: R 为 number; 若 T 非函数,则为 undefined
539
+ */
540
+ type FirstParameter<T> = T extends (arg: infer P, ...args: any[]) => any ? P : undefined;
541
+
542
+ /**
543
+ * 数组合并策略
544
+ * - `'concat'` : 将 source 数组拼接在 target 数组之后(默认)
545
+ * - `'replace'` : 用 source 数组整体替换 target 数组
546
+ * - `'unique'` : 拼接后去重(基于 SameValueZero 比较)
547
+ */
548
+ type ArrayMergeStrategy = 'concat' | 'replace' | 'unique';
549
+ /**
550
+ * null/undefined 处理策略
551
+ * - `'skip'` : 忽略 source 中的 null/undefined,保留 target 中的值(默认)
552
+ * - `'override'` : 允许 source 中的 null/undefined 覆盖 target 中的值
553
+ */
554
+ type NullHandlingStrategy = 'skip' | 'override';
555
+ /**
556
+ * 自定义合并函数。
557
+ *
558
+ * @param key 当前正在处理的键(string 或 Symbol)
559
+ * @param targetVal target 中该键的当前值
560
+ * @param sourceVal source 中该键的值
561
+ * @param path 从根对象到当前层级的键路径
562
+ * @returns 返回合并结果;返回 `undefined` 则交由默认逻辑处理
563
+ */
564
+ type CustomMerger = (key: string | symbol, targetVal: unknown, sourceVal: unknown, path: ReadonlyArray<string | symbol>) => unknown;
565
+ /**
566
+ * deepMerge 配置选项
567
+ */
568
+ interface DeepMergeOptions {
569
+ /** 数组合并策略,默认 `'concat'` */
570
+ arrayStrategy?: ArrayMergeStrategy;
571
+ /** null/undefined 处理策略,默认 `'skip'` */
572
+ nullHandling?: NullHandlingStrategy;
573
+ /** 自定义合并函数,返回 `undefined` 则交由默认逻辑处理 */
574
+ customMerger?: CustomMerger;
575
+ }
576
+ /**
577
+ * 递归地将多个 source 对象深度合并为一个新对象。
578
+ *
579
+ * - 后面的 source 优先级更高,会覆盖前面的同名属性
580
+ * - 双方都是纯对象的属性会递归合并,而非覆盖
581
+ * - 数组合并策略、null 处理和自定义合并函数均可配置
582
+ * - 支持 Symbol 键,防止原型污染(跳过 `__proto__` 和 `constructor`)
583
+ * - 不修改任何输入对象
584
+ *
585
+ * @category Object
586
+ * @typeParam T 合并结果的对象类型
587
+ * @param sources 要合并的源对象数组,后面的对象优先级更高
588
+ * @param options 合并行为配置项(可选)
589
+ * @returns 合并后的新对象
590
+ *
591
+ * @example
592
+ * ```ts
593
+ * const defaults = { theme: 'light', pagination: { page: 1, size: 10 } }
594
+ * const userConfig = { pagination: { size: 20 }, debug: true }
595
+ * const result = deepMerge([defaults, userConfig])
596
+ * // => { theme: 'light', pagination: { page: 1, size: 20 }, debug: true }
597
+ * ```
598
+ *
599
+ * @example
600
+ * ```ts
601
+ * // 数组去重合并
602
+ * const result = deepMerge(
603
+ * [{ tags: ['a', 'b'] }, { tags: ['b', 'c'] }],
604
+ * { arrayStrategy: 'unique' },
605
+ * )
606
+ * // => { tags: ['a', 'b', 'c'] }
607
+ * ```
608
+ */
609
+ declare function deepMerge<T extends AnyObject>(sources: T[], options?: DeepMergeOptions): T;
610
+ /**
611
+ * 创建一个预绑定配置的 deepMerge 函数。
612
+ *
613
+ * @category Object
614
+ * @param options 合并行为配置项
615
+ * @returns 预配置的 deepMerge 函数
616
+ *
617
+ * @example
618
+ * ```ts
619
+ * const mergeReplace = createDeepMerge({ arrayStrategy: 'replace' })
620
+ * const result = mergeReplace([{ tags: ['a'] }, { tags: ['b'] }])
621
+ * // => { tags: ['b'] }
622
+ * ```
623
+ */
624
+ declare function createDeepMerge(options: DeepMergeOptions): <T extends AnyObject>(sources: T[]) => T;
625
+
626
+ /**
627
+ * 从对象中排除指定的键,返回新对象
628
+ *
629
+ * @category Object
630
+ * @param obj 源对象
631
+ * @param keys 要排除的键数组
632
+ * @returns 排除指定键后的新对象
633
+ * @example
634
+ * ```ts
635
+ * const user = {
636
+ * id: 1,
637
+ * name: 'John',
638
+ * password: 'secret',
639
+ * email: 'john@example.com'
640
+ * }
641
+ *
642
+ * const publicUser = omit(user, ['password'])
643
+ * console.log(publicUser) // { id: 1, name: 'John', email: 'john@example.com' }
644
+ *
645
+ * const basicInfo = omit(user, ['password', 'email'])
646
+ * console.log(basicInfo) // { id: 1, name: 'John' }
647
+ * ```
648
+ */
649
+ declare function omit<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): OmitByKey<T, K>;
650
+
651
+ /**
652
+ * 从对象中排除值为undefined的键
653
+ *
654
+ * @category Object
655
+ * @param obj 源对象
656
+ * @returns 排除undefined值后的新对象
657
+ * @example
658
+ * ```ts
659
+ * const data = {
660
+ * name: 'John',
661
+ * age: undefined,
662
+ * city: 'New York',
663
+ * country: undefined
664
+ * }
665
+ *
666
+ * const cleaned = omitUndefined(data)
667
+ * console.log(cleaned) // { name: 'John', city: 'New York' }
668
+ *
669
+ * // 用于API请求前清理数据
670
+ * const requestData = omitUndefined({
671
+ * title: 'Post Title',
672
+ * content: 'Post content',
673
+ * tags: undefined,
674
+ * published: true
675
+ * })
676
+ * ```
677
+ */
678
+ declare function omitUndefined<T extends AnyObject>(obj: T): Partial<T>;
679
+
680
+ /**
681
+ * 从对象中选择指定的键,返回新对象
682
+ *
683
+ * @category Object
684
+ * @param obj 源对象
685
+ * @param keys 要选择的键数组
686
+ * @returns 只包含指定键的新对象
687
+ * @example
688
+ * ```ts
689
+ * const user = {
690
+ * id: 1,
691
+ * name: 'John',
692
+ * email: 'john@example.com',
693
+ * password: 'secret',
694
+ * createdAt: '2023-01-01',
695
+ * updatedAt: '2023-01-15'
696
+ * }
697
+ *
698
+ * const publicInfo = pick(user, ['id', 'name', 'email'])
699
+ * console.log(publicInfo) // { id: 1, name: 'John', email: 'john@example.com' }
700
+ *
701
+ * const basicInfo = pick(user, ['id', 'name'])
702
+ * console.log(basicInfo) // { id: 1, name: 'John' }
703
+ * ```
704
+ */
705
+ declare function pick<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): PickByKey<T, K>;
706
+
707
+ /**
708
+ * 将对象按指定键分离为两个对象
709
+ *
710
+ * @category Object
711
+ * @param obj 源对象
712
+ * @param keys 要分离的键数组
713
+ * @returns 包含picked和omitted两个对象的结果
714
+ * @example
715
+ * ```ts
716
+ * const user = {
717
+ * id: 1,
718
+ * name: 'John',
719
+ * email: 'john@example.com',
720
+ * password: 'secret',
721
+ * role: 'admin'
722
+ * }
723
+ *
724
+ * const { picked, omitted } = separate(user, ['id', 'name'])
725
+ * console.log(picked) // { id: 1, name: 'John' }
726
+ * console.log(omitted) // { email: 'john@example.com', password: 'secret', role: 'admin' }
727
+ *
728
+ * // 用于分离敏感信息
729
+ * const { picked: publicData, omitted: privateData } = separate(user, ['id', 'name', 'email'])
730
+ * ```
731
+ */
732
+ declare function separate<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): {
733
+ picked: PickByKey<T, K>;
734
+ omitted: OmitByKey<T, K>;
735
+ };
3
736
 
4
737
  /**
5
- * 统一同步/异步返回类型
6
- * @typeParam T - 类型
738
+ * 将对象按多分组键集合进行分离(浅层),返回各分组与 others
739
+ *
740
+ * - 键冲突策略:先到先得。若同一键出现在多个分组中,则归入第一个匹配到的分组
741
+ * - 仅处理对象自有的浅层键,不解析深层路径
742
+ * - 分组中包含不存在于对象的键将被忽略
743
+ *
744
+ * @category Object
745
+ * @param obj 源对象
746
+ * @param groups 分组映射,如 { a: ['x', 'y'], b: ['z'] }
747
+ * @returns 一个对象,包含每个分组的子对象以及 others(其余未被分组捕获的键)
7
748
  * @example
8
- * // type T = string | Promise<string>
9
- * // type R = ApiAwaitable<T>
10
- * // 结果:R string | Promise<string>
749
+ * ```ts
750
+ * const options = { id: 1, name: 'John', email: 'a@b.com', role: 'admin' }
751
+ * const { a, b, others } = separateMany(options, { a: ['id'], b: ['name'] as const })
752
+ * // a: { id: 1 }
753
+ * // b: { name: 'John' }
754
+ * // others: { email: 'a@b.com', role: 'admin' }
755
+ * ```
11
756
  */
12
- type ApiAwaitable<T> = T | Promise<T>;
757
+ declare function separateMany<T extends AnyObject, M extends Record<string, readonly (keyof T)[]>>(obj: T, groups: M): {
758
+ [P in keyof M]: PickByKey<T, M[P][number]>;
759
+ } & {
760
+ others: OmitByKey<T, M[keyof M][number]>;
761
+ };
762
+
763
+ type PathSegment = string | number;
764
+ type PathSegments = PathSegment[];
765
+ type PathInput = string | PathSegments;
13
766
  /**
14
- * 提取Promise类型
15
- * @typeParam T - 类型
767
+ * 将路径字符串解析为片段数组。
768
+ *
769
+ * - 支持点语法与方括号语法混用
770
+ * - 引号键支持单/双引号与反斜杠转义
771
+ * - 方括号内未引号的非负整数字面量解析为 number 段
772
+ * - 点语法中的纯数字段保持字符串(不转为索引)
773
+ *
774
+ * @category Path
775
+ * @param path 路径字符串或片段数组
776
+ * @returns 解析后的片段数组
16
777
  * @example
17
- * // type T = Promise<string>
18
- * // type R = ApiUnwrapPromise<T>
19
- * // 结果:R 为 string
778
+ * ```ts
779
+ * toPath('a.b[0].c') // ['a', 'b', 0, 'c']
780
+ * toPath("a['x.y']") // ['a', 'x.y']
781
+ * ```
20
782
  */
21
- type ApiUnwrapPromise<T> = T extends Promise<infer U> ? U : T;
783
+ declare function toPath(path: PathInput): PathSegments;
784
+
22
785
  /**
23
- * 提取函数返回类型
24
- * @typeParam TFn - 函数类型
786
+ * 读取对象指定路径的值。
787
+ *
788
+ * - 若取值结果为 undefined,则返回 defaultValue
789
+ * - 若取值结果为 null,则直接返回 null(不触发默认值)
790
+ * - 传入空路径时返回 object 本身
791
+ *
792
+ * @category Path
793
+ * @param object 源对象
794
+ * @param path 路径字符串或片段数组
795
+ * @param defaultValue 结果为 undefined 时返回的默认值
796
+ * @returns 读取到的值或默认值
25
797
  * @example
26
- * // type Fn = (x: number, y: string) => Promise<string>
27
- * // type R = ApiAwaitedReturn<Fn>
28
- * // 结果:R string
798
+ * ```ts
799
+ * const obj = { a: { b: { c: 1, d: undefined }, e: null }, arr: [{ x: 9 }] }
800
+ * getPath(obj, 'a.b.c') // 1
801
+ * getPath(obj, 'a.b.d', 42) // 42(d 为 undefined,使用默认值)
802
+ * getPath(obj, 'a.e', 100) // null(null 不触发默认值)
803
+ * getPath(obj, 'arr[0].x') // 9
804
+ * getPath(obj, '') // 返回 obj 本身
805
+ * ```
806
+ */
807
+ declare function getPath<T, D = undefined>(object: T, path: PathInput, defaultValue?: D): unknown | D;
808
+
809
+ /**
810
+ * 将片段数组序列化为路径字符串。
811
+ *
812
+ * 规则:
813
+ * - 合法标识符段使用点拼接(a.b.c)
814
+ * - 数字段转为索引([0])
815
+ * - 其它需要转义的键使用方括号引号(['x.y']),并转义 \\ 与 '\''
816
+ *
817
+ * @category Path
818
+ * @param segments 路径片段数组
819
+ * @returns 路径字符串
820
+ * @example
821
+ * ```ts
822
+ * const p = joinPath(['a', 'x.y', 0, 'space key'])
823
+ * // p: "a['x.y'][0]['space key']"
824
+ * // 与解析往返:toPath(p) => ['a', 'x.y', 0, 'space key']
825
+ * ```
826
+ */
827
+ declare function joinPath(segments: (string | number)[]): string;
828
+
829
+ /**
830
+ * 在对象指定路径写入值。缺失路径会被自动创建:
831
+ * - 下一段为 number(索引)时创建数组
832
+ * - 下一段为 string(属性)时创建对象
833
+ *
834
+ * 若中途遇到非容器类型(如字符串/数值/布尔),会被替换为正确的容器以继续写入。
835
+ *
836
+ * @category Path
837
+ * @param object 目标对象(原地修改并返回同一引用)
838
+ * @param path 路径字符串或片段数组
839
+ * @param value 要写入的值
840
+ * @returns 原对象(已修改)
841
+ * @example
842
+ * ```ts
843
+ * const obj: any = {}
844
+ * setPath(obj, 'a.b[0].c', 7)
845
+ * // obj => { a: { b: [{ c: 7 }] } }
846
+ *
847
+ * setPath(obj, 'a.b[2].d', 8)
848
+ * // 数组自动扩容到长度 3
849
+ * // obj.a.b[2] => { d: 8 }
850
+ *
851
+ * setPath(obj, 'a.0.b', 1) // 点语法数字键保持为字符串键
852
+ * // obj => { a: { 0: { b: 1 } } }
853
+ * setPath(obj, 'a[0].b', 2) // 索引用方括号
854
+ * // obj.a[0].b => 2
855
+ * ```
856
+ */
857
+ declare function setPath<T extends Record<string, any>>(object: T, path: PathInput, value: unknown): T;
858
+
859
+ /**
860
+ * 生成字符串的简单哈希值
861
+ *
862
+ * @category Helpers
863
+ * @param str 待哈希的字符串
864
+ * @returns 32位哈希值转换为36进制字符串
865
+ * @example
866
+ * ```ts
867
+ * const hash1 = simpleHash('hello world')
868
+ * console.log(hash1) // 'nf5xd4'
869
+ *
870
+ * const hash2 = simpleHash('hello world')
871
+ * console.log(hash1 === hash2) // true,相同字符串产生相同哈希
872
+ *
873
+ * const hash3 = simpleHash('hello world!')
874
+ * console.log(hash1 === hash3) // false,不同字符串产生不同哈希
875
+ * ```
876
+ */
877
+ declare function simpleHash(str: string): string;
878
+
879
+ /**
880
+ * 生成随机UUID字符串
881
+ *
882
+ * @category Helpers
883
+ * @returns 符合UUID v4格式的随机字符串
884
+ * @example
885
+ * ```ts
886
+ * const id1 = getRandomUUID()
887
+ * console.log(id1) // 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
888
+ *
889
+ * const id2 = getRandomUUID()
890
+ * console.log(id2) // 'f47ac10b-58cc-4372-a567-0e02b2c3d480'
891
+ *
892
+ * // 用于生成唯一标识符
893
+ * const componentId = `component-${getRandomUUID()}`
894
+ * ```
895
+ */
896
+ declare function getRandomUUID(): string;
897
+
898
+ /**
899
+ * 将对象的键名转换为kebab-case格式
900
+ *
901
+ * @category Object
902
+ * @param obj 待转换的对象
903
+ * @param deep 是否深度转换嵌套对象,默认为false
904
+ * @returns 转换后的对象
905
+ * @example
906
+ * ```ts
907
+ * const obj = {
908
+ * firstName: 'John',
909
+ * lastName: 'Doe',
910
+ * userInfo: {
911
+ * birthDate: '1990-01-01',
912
+ * phoneNumber: '123-456-7890'
913
+ * }
914
+ * }
915
+ *
916
+ * const converted = convertToKebabCase(obj)
917
+ * console.log(converted)
918
+ * // {
919
+ * // 'first-name': 'John',
920
+ * // 'last-name': 'Doe',
921
+ * // 'user-info': { birthDate: '1990-01-01', phoneNumber: '123-456-7890' }
922
+ * // }
923
+ *
924
+ * const deepConverted = convertToKebabCase(obj, true)
925
+ * console.log(deepConverted)
926
+ * // {
927
+ * // 'first-name': 'John',
928
+ * // 'last-name': 'Doe',
929
+ * // 'user-info': { 'birth-date': '1990-01-01', 'phone-number': '123-456-7890' }
930
+ * // }
931
+ * ```
932
+ */
933
+ declare function convertToKebabCase<T extends AnyObject>(obj: T, deep?: boolean): T;
934
+
935
+ /**
936
+ * 将字符串转换为驼峰命名格式(第一个单词小写,后续单词首字母大写)。
937
+ *
938
+ * @category String
939
+ * @param str 要转换的字符串
940
+ * @returns 驼峰命名格式的字符串
941
+ * @example
942
+ * ```ts
943
+ * camelCase('First Name') // 'firstName'
944
+ * camelCase('first_name') // 'firstName'
945
+ * camelCase('first-name') // 'firstName'
946
+ * camelCase('XMLHttpRequest') // 'xmlHttpRequest'
947
+ * ```
948
+ */
949
+ declare function camelCase(str: string): string;
950
+
951
+ /**
952
+ * 将字符串首字母大写,其余字母小写。
953
+ *
954
+ * @category String
955
+ * @param str 要转换的字符串
956
+ * @returns 首字母大写的字符串
957
+ * @example
958
+ * ```ts
959
+ * capitalize('hello') // 'Hello'
960
+ * capitalize('HELLO') // 'Hello'
961
+ * capitalize('hello world') // 'Hello world'
962
+ * ```
963
+ */
964
+ declare function capitalize(str: string): string;
965
+
966
+ /**
967
+ * 将字符串转换为短横线命名格式(kebab-case)。
968
+ *
969
+ * @category String
970
+ * @param str 要转换的字符串
971
+ * @returns 短横线命名格式的字符串
972
+ * @example
973
+ * ```ts
974
+ * kebabCase('firstName') // 'first-name'
975
+ * kebabCase('First Name') // 'first-name'
976
+ * kebabCase('first_name') // 'first-name'
977
+ * kebabCase('XMLHttpRequest') // 'xml-http-request'
978
+ * ```
979
+ */
980
+ declare function kebabCase(str: string): string;
981
+
982
+ /**
983
+ * 将字符串转换为小写格式,单词之间用空格分隔。
984
+ *
985
+ * @category String
986
+ * @param str 要转换的字符串
987
+ * @returns 小写格式的字符串
988
+ * @example
989
+ * ```ts
990
+ * lowerCase('firstName') // 'first name'
991
+ * lowerCase('First_Name') // 'first name'
992
+ * lowerCase('FIRST-NAME') // 'first name'
993
+ * lowerCase('XMLHttpRequest') // 'xml http request'
994
+ * ```
29
995
  */
30
- type ApiAwaitedReturn<TFn> = TFn extends (...args: any[]) => ApiAwaitable<infer R> ? R : never;
996
+ declare function lowerCase(str: string): string;
31
997
 
32
998
  /**
33
- * 提供字符串字面量提示的同时允许任意字符串
34
- * 在 IDE 中提供 T 类型的自动补全提示,但不限制只能使用这些值
999
+ * 将字符串首字母小写,其余字母保持原样。
35
1000
  *
1001
+ * @category String
1002
+ * @param str 要转换的字符串
1003
+ * @returns 首字母小写的字符串
36
1004
  * @example
37
1005
  * ```ts
38
- * type Color = Suggest<'red' | 'blue' | 'green'>
39
- *
40
- * // IDE 会提示 'red', 'blue', 'green',但也可以使用其他字符串
41
- * const color1: Color = 'red' // 有提示
42
- * const color2: Color = 'yellow' // 也可以,虽然没有提示
1006
+ * lowerFirst('Hello') // 'hello'
1007
+ * lowerFirst('HELLO') // 'hELLO'
1008
+ * lowerFirst('Hello World') // 'hello World'
43
1009
  * ```
44
1010
  */
45
- type Suggest<T extends string> = T | (string & {});
1011
+ declare function lowerFirst(str: string): string;
1012
+
46
1013
  /**
47
- * 响应式值类型 - 基于 Vue 的 `MaybeRefOrGetter` 扩展,额外支持上下文回调
48
- *
49
- * @template T - 值类型
50
- * @template CTX - 上下文类型(用于回调函数)
1014
+ * 将字符串转换为帕斯卡命名格式(PascalCase,每个单词首字母大写)。
51
1015
  *
1016
+ * @category String
1017
+ * @param str 要转换的字符串
1018
+ * @returns 帕斯卡命名格式的字符串
52
1019
  * @example
53
1020
  * ```ts
54
- * const value: ReactiveValue<boolean> = ref(false)
55
- * const getter: ReactiveValue<string> = () => name.value
56
- * const computed: ReactiveValue<number> = computed(() => count.value)
57
- * const withContext: ReactiveValue<boolean, Context> = (ctx) => ctx.value > 0
1021
+ * pascalCase('firstName') // 'FirstName'
1022
+ * pascalCase('first_name') // 'FirstName'
1023
+ * pascalCase('first-name') // 'FirstName'
1024
+ * pascalCase('XMLHttpRequest') // 'XmlHttpRequest'
58
1025
  * ```
59
1026
  */
60
- type ReactiveValue<T, CTX = never> = [CTX] extends [never] ? MaybeRefOrGetter<T> : MaybeRefOrGetter<T> | ((ctx: CTX) => T);
61
- type StripNullable<T> = T extends null | undefined ? never : T;
1027
+ declare function pascalCase(str: string): string;
62
1028
 
63
- type UnknownObject = Record<string, unknown>;
64
- type AnyObject = Record<string, any>;
65
1029
  /**
66
- * 依据键名从对象类型 `T` 中剔除键 `K`。
67
- * @typeParam T - 源对象类型
68
- * @typeParam K - 要剔除的键(必须来自 `keyof T`)
1030
+ * 将字符串转换为下划线命名格式(snake_case)。
1031
+ *
1032
+ * @category String
1033
+ * @param str 要转换的字符串
1034
+ * @returns 下划线命名格式的字符串
69
1035
  * @example
70
- * // type User = { id: string; name: string; age: number }
71
- * // type R = OmitByKey<User, 'age'>
72
- * // 结果:R { id: string; name: string }
1036
+ * ```ts
1037
+ * snakeCase('firstName') // 'first_name'
1038
+ * snakeCase('First Name') // 'first_name'
1039
+ * snakeCase('first-name') // 'first_name'
1040
+ * snakeCase('XMLHttpRequest') // 'xml_http_request'
1041
+ * ```
73
1042
  */
74
- type OmitByKey<T, K extends keyof T> = {
75
- [P in keyof T as P extends K ? never : P]: T[P];
76
- };
1043
+ declare function snakeCase(str: string): string;
1044
+
77
1045
  /**
78
- * 依据键名从对象类型 `T` 中挑选键 `K`。
79
- * @typeParam T - 源对象类型
80
- * @typeParam K - 要保留的键(必须来自 `keyof T`)
1046
+ * 将字符串转换为Start Case格式(每个单词首字母大写,用空格分隔)。
1047
+ *
1048
+ * @category String
1049
+ * @param str 要转换的字符串
1050
+ * @returns Start Case格式的字符串
81
1051
  * @example
82
- * // type User = { id: string; name: string; age: number }
83
- * // type R = PickByKey<User, 'id' | 'name'>
84
- * // 结果:R 为 { id: string; name: string }
1052
+ * ```ts
1053
+ * startCase('firstName') // 'First Name'
1054
+ * startCase('first_name') // 'First Name'
1055
+ * startCase('first-name') // 'First Name'
1056
+ * startCase('XMLHttpRequest') // 'XML Http Request'
1057
+ * ```
85
1058
  */
86
- type PickByKey<T, K extends keyof T> = {
87
- [P in keyof T as P extends K ? P : never]: T[P];
88
- };
1059
+ declare function startCase(str: string): string;
1060
+
89
1061
  /**
90
- * 基于映射表 `Mapping` 对对象类型 `T` 的键进行重命名。
91
- * 未在映射表中的键保持原名;映射值为 `PropertyKey`(string/number/symbol)。
92
- * @typeParam T - 源对象类型
93
- * @typeParam Mapping - 旧键到新键名的映射
1062
+ * 将字符串转换为大写格式,单词之间用空格分隔。
1063
+ *
1064
+ * @category String
1065
+ * @param str 要转换的字符串
1066
+ * @returns 大写格式的字符串
94
1067
  * @example
95
- * // type Src = { a: number; b: string }
96
- * // type R = RenameKeys<Src, { a: 'id' }>
97
- * // 结果:R 为 { id: number; b: string }
1068
+ * ```ts
1069
+ * upperCase('firstName') // 'FIRST NAME'
1070
+ * upperCase('first_name') // 'FIRST NAME'
1071
+ * upperCase('first-name') // 'FIRST NAME'
1072
+ * upperCase('XMLHttpRequest') // 'XML HTTP REQUEST'
1073
+ * ```
98
1074
  */
99
- type RenameKeys<T, Mapping extends {
100
- [K in keyof T]?: PropertyKey;
101
- }> = {
102
- [K in keyof T as K extends keyof Mapping ? Exclude<Mapping[K], undefined> : K]: T[K];
103
- };
1075
+ declare function upperCase(str: string): string;
1076
+
104
1077
  /**
105
- * 将对象类型 `T` 中的键 `K` 标记为必填(移除可选修饰)。
106
- * @typeParam T - 源对象类型
107
- * @typeParam K - 设为必填的键
1078
+ * 将字符串首字母大写,其余字母保持原样。
1079
+ *
1080
+ * @category String
1081
+ * @param str 要转换的字符串
1082
+ * @returns 首字母大写的字符串
108
1083
  * @example
109
- * // type User = { id: string; name?: string }
110
- * // type R = RequiredByKeys<User, 'name'>
111
- * // 结果:R['name'] 为必填的 string
1084
+ * ```ts
1085
+ * upperFirst('hello') // 'Hello'
1086
+ * upperFirst('hELLO') // 'HELLO'
1087
+ * upperFirst('hello world') // 'Hello world'
1088
+ * ```
112
1089
  */
113
- type RequiredByKeys<T, K extends keyof T> = T & {
114
- [P in K]-?: T[P];
115
- };
1090
+ declare function upperFirst(str: string): string;
1091
+
116
1092
  /**
117
- * 将对象类型 `T` 中的键 `K` 标记为可选。
118
- * @typeParam T - 源对象类型
119
- * @typeParam K - 设为可选的键
1093
+ * 将字符串分解为单词数组。支持camelCase、snake_case、kebab-case等各种命名风格。
1094
+ *
1095
+ * @category String
1096
+ * @param str 要分解的字符串
1097
+ * @returns 单词数组
120
1098
  * @example
121
- * // type User = { id: string; name: string }
122
- * // type R = PartialByKeys<User, 'name'>
123
- * // 结果:R['name'] 为可选(可能为 undefined)
1099
+ * ```ts
1100
+ * words('helloWorld') // ['hello', 'World']
1101
+ * words('hello_world') // ['hello', 'world']
1102
+ * words('hello-world') // ['hello', 'world']
1103
+ * words('XMLHttpRequest') // ['XML', 'Http', 'Request']
1104
+ * ```
124
1105
  */
125
- type PartialByKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
1106
+ declare function words(str: string): string[];
1107
+
126
1108
  /**
127
- * 将对象类型 `T` 中的键 `K` 标记为只读(浅只读)。
128
- * @typeParam T - 源对象类型
129
- * @typeParam K - 设为只读的键
130
- * @example
131
- * // type User = { id: string; name: string }
132
- * // type R = ReadonlyByKeys<User, 'id'>
133
- * // 结果:R['id'] 不可被重新赋值
1109
+ * 树节点类型定义
1110
+ *
1111
+ * @template T 节点数据类型
134
1112
  */
135
- type ReadonlyByKeys<T, K extends keyof T> = T & {
136
- readonly [P in K]: T[P];
1113
+ type TreeNode<T = any> = T & {
1114
+ children?: TreeNode<T>[];
1115
+ [key: string]: any;
137
1116
  };
138
1117
  /**
139
- * 取消对象类型 `T` 中键 `K` 的只读限制,使其可写(浅层)。
140
- * @typeParam T - 源对象类型
141
- * @typeParam K - 取消只读的键
142
- * @example
143
- * // type User = { readonly id: string; name: string }
144
- * // type R = MutableByKeys<User, 'id'>
145
- * // 结果:R['id'] 变为可写
146
- */
147
- type MutableByKeys<T, K extends keyof T> = {
148
- -readonly [P in K]: T[P];
149
- } & Omit<T, K>;
150
- /**
151
- * 将联合类型 `U` 转换为交叉类型,用于合并联合成员的属性。
152
- * @typeParam U - 联合类型
153
- * @example
154
- * // type U = { a: number } | { b: string }
155
- * // type R = UnionToIntersection<U>
156
- * // 结果:R 为 { a: number } & { b: string }
1118
+ * 树形配置类型
157
1119
  */
158
- type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? {
159
- [K in keyof I]: I[K];
160
- } : never;
1120
+ interface TreeConfig {
1121
+ id: string;
1122
+ pid: string;
1123
+ children: string;
1124
+ }
161
1125
  /**
162
- * 若对象 `T` 在键 `K` 处的类型为元组,则提取其首个元素类型,否则为 `never`。
163
- * @typeParam T - 具有元组属性的对象类型
164
- * @typeParam K - 属性键
165
- * @example
166
- * // type Cfg = { params: [id: string, flag?: boolean] }
167
- * // type R = FirstParam<Cfg, 'params'>
168
- * // 结果:R 为 string
1126
+ * 树形配置输入类型
169
1127
  */
170
- type FirstParam<T, K extends keyof T> = T[K] extends [infer P, ...any[]] ? P : never;
1128
+ type TreeConfigInput = Partial<TreeConfig>;
171
1129
  /**
172
- * 从函数类型中提取首个参数类型;若 `T` 非函数类型,则为 `undefined`。
173
- * @typeParam T - 函数类型
174
- * @example
175
- * // type Fn = (x: number, y: string) => void
176
- * // type R = FirstParameter<Fn>
177
- * // 结果:R 为 number;若 T 非函数,则为 undefined
1130
+ * 树统计信息类型
178
1131
  */
179
- type FirstParameter<T> = T extends (arg: infer P, ...args: any[]) => any ? P : undefined;
1132
+ interface TreeStats {
1133
+ total: number;
1134
+ leaves: number;
1135
+ depth: number;
1136
+ branches: number;
1137
+ }
180
1138
  /**
181
- * 递归将对象类型 `T` 的所有属性变为可选(深可选)。
182
- * @typeParam T - 源对象类型
183
- * @example
184
- * // type Src = { a: { b: number } }
185
- * // type R = DeepPartial<Src>
186
- * // 结果:R 为 { a?: { b?: number | undefined } | undefined }
1139
+ * 树节点谓词函数类型
1140
+ *
1141
+ * @template T 节点数据类型
1142
+ * @param params 包含节点信息的对象参数
1143
+ * @param params.node 当前节点
1144
+ * @param params.depth 节点深度(从0开始)
1145
+ * @param params.path 从根节点到当前节点的路径数组
1146
+ * @param params.index 节点在同级节点中的索引
1147
+ * @returns 是否满足条件
187
1148
  */
188
- type DeepPartial<T> = {
189
- [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P] | undefined;
190
- };
1149
+ type TreePredicate<T = any> = (params: {
1150
+ node: TreeNode<T>;
1151
+ depth: number;
1152
+ path: readonly TreeNode<T>[];
1153
+ index: number;
1154
+ }) => boolean;
191
1155
  /**
192
- * 当 `MaybeObject` 为对象时,返回键 `Key` 对应的属性类型;否则为 `never`。
193
- * @typeParam MaybeObject - 可能为对象的类型
194
- * @typeParam Key - 目标键名(string)
195
- * @example
196
- * // type Obj = { id: number }
197
- * // type R1 = GetObjectField<Obj, 'id'> // 结果:number
198
- * // type R2 = GetObjectField<string, 'id'> // 结果:never
1156
+ * 树节点转换函数类型
1157
+ *
1158
+ * @template T 源节点数据类型
1159
+ * @template R 目标节点数据类型
1160
+ * @param params 包含节点信息的对象参数
1161
+ * @param params.node 当前节点
1162
+ * @param params.depth 节点深度(从0开始)
1163
+ * @param params.path 从根节点到当前节点的路径数组
1164
+ * @param params.index 节点在同级节点中的索引
1165
+ * @returns 转换后的节点数据
199
1166
  */
200
- type GetObjectField<MaybeObject, Key extends string> = MaybeObject extends Record<string, any> ? MaybeObject[Key] : never;
1167
+ type TreeTransformer<T = any, R = any> = (params: {
1168
+ node: TreeNode<T>;
1169
+ depth: number;
1170
+ path: readonly TreeNode<T>[];
1171
+ index: number;
1172
+ }) => R;
201
1173
  /**
202
- * Vue 渲染相关文本/节点类型:可为字符串、`VNode` 或返回 `VNode` 的函数。
203
- * @example
204
- * // 在渲染 API 中允许三种形态:
205
- * // - '标题'
206
- * // - h('div', '标题') 产生的 VNode
207
- * // - () => h('div', '标题') 的惰性渲染函数
1174
+ * 树节点访问函数类型
1175
+ *
1176
+ * @template T 节点数据类型
1177
+ * @param params 包含节点信息的对象参数
1178
+ * @param params.node 当前节点
1179
+ * @param params.depth 节点深度(从0开始)
1180
+ * @param params.path 从根节点到当前节点的路径数组
1181
+ * @param params.index 节点在同级节点中的索引
1182
+ * @returns 返回false可以终止遍历或跳过子节点
208
1183
  */
209
- type StringOrVNode = string | VNode | (() => VNode);
1184
+ type TreeVisitor<T = any> = (params: {
1185
+ node: TreeNode<T>;
1186
+ depth: number;
1187
+ path: readonly TreeNode<T>[];
1188
+ index: number;
1189
+ }) => void | boolean;
1190
+
210
1191
  /**
211
- * 合并两个对象类型,U 中的属性会覆盖 T 中的属性
1192
+ * 从扁平数组创建树形结构
212
1193
  *
1194
+ * @category Tree
1195
+ * @param list 扁平数组数据
1196
+ * @param config 树形配置选项
1197
+ * @returns 树形结构数组
213
1198
  * @example
214
1199
  * ```ts
215
- * type T = { a: number, c: string }
216
- * type U = { a: string, b: boolean }
217
- * type M = Merge<T, U> // { a: string, b: boolean, c: string }
1200
+ * const flatData = [
1201
+ * { id: '1', name: '部门1', parentId: null },
1202
+ * { id: '2', name: '部门1-1', parentId: '1' },
1203
+ * { id: '3', name: '部门1-2', parentId: '1' },
1204
+ * { id: '4', name: '部门1-1-1', parentId: '2' }
1205
+ * ]
1206
+ *
1207
+ * const tree = fromList(flatData, {
1208
+ * id: 'id',
1209
+ * pid: 'parentId',
1210
+ * children: 'children'
1211
+ * })
1212
+ *
1213
+ * console.log(tree) // 转换为树形结构
218
1214
  * ```
219
1215
  */
220
- type Merge<T, U> = Omit<T, keyof U> & U;
1216
+ declare function fromList<T = any>(list: T[], config?: TreeConfigInput): TreeNode<T>[];
221
1217
  /**
222
- * 判断类型 T 是否为纯对象类型
223
- * 纯对象是指普通的对象字面量,排除数组、函数、Date 等特殊对象类型
1218
+ * 将树形结构转换为扁平数组
1219
+ *
1220
+ * @category Tree
1221
+ * @param tree 树形结构(单个节点或节点数组)
1222
+ * @param config 树形配置选项
1223
+ * @returns 扁平数组
224
1224
  * @example
225
1225
  * ```ts
226
- * type Test1 = IsPlainObject<{ a: number }> // true
227
- * type Test2 = IsPlainObject<string[]> // false
228
- * type Test3 = IsPlainObject<() => void> // false
229
- * type Test4 = IsPlainObject<Date> // false
230
- * type Test5 = IsPlainObject<string> // false
231
- * type Test6 = IsPlainObject<null> // false
1226
+ * const tree = [
1227
+ * {
1228
+ * id: '1',
1229
+ * name: '根节点',
1230
+ * children: [
1231
+ * { id: '2', name: '子节点1', children: [] },
1232
+ * { id: '3', name: '子节点2', children: [] }
1233
+ * ]
1234
+ * }
1235
+ * ]
1236
+ *
1237
+ * const flatList = toList(tree)
1238
+ * console.log(flatList) // [{ id: '1', name: '根节点' }, { id: '2', name: '子节点1' }, ...]
232
1239
  * ```
233
1240
  */
234
- type IsPlainObject<T> = NonNullable<T> extends Record<string, any> ? NonNullable<T> extends any[] ? false : NonNullable<T> extends (...args: any[]) => any ? false : NonNullable<T> extends Date ? false : true : false;
235
- /**
236
- * 深度控制类型,用于限制类型递归的深度
237
- * 防止类型计算超出 TypeScript 的递归限制
238
- */
239
- type Depth = [never, 0, 1, 2, 3, 4];
1241
+ declare function toList<T = any>(tree: TreeNode<T> | TreeNode<T>[], config?: TreeConfigInput): T[];
240
1242
  /**
241
- * 提取对象的嵌套键,支持点语法路径
1243
+ * 估算树形结构的节点数量
242
1244
  *
243
- * @template T 源对象类型
244
- * @template D 递归深度,默认为2
1245
+ * @category Tree
1246
+ * @param tree 树形结构(单个节点或节点数组)
1247
+ * @param config 树形配置选项
1248
+ * @returns 节点总数量
245
1249
  * @example
246
1250
  * ```ts
247
- * type User = {
248
- * name: string
249
- * address: {
250
- * city: string
251
- * country: string
1251
+ * const tree = [
1252
+ * {
1253
+ * id: '1',
1254
+ * name: '根节点',
1255
+ * children: [
1256
+ * { id: '2', name: '子节点1', children: [] },
1257
+ * { id: '3', name: '子节点2', children: [] }
1258
+ * ]
252
1259
  * }
253
- * }
254
- * type Keys = NestedKeys<User> // 'name' | 'address' | 'address.city' | 'address.country'
1260
+ * ]
1261
+ *
1262
+ * const size = estimateSize(tree)
1263
+ * console.log(size) // 3
255
1264
  * ```
256
1265
  */
257
- type NestedKeys<T, D extends number = 2> = [D] extends [never] ? never : {
258
- [K in keyof T & string]: IsPlainObject<T[K]> extends true ? K | `${K}.${NestedKeys<NonNullable<T[K]>, Depth[D]>}` : K;
259
- }[keyof T & string];
1266
+ declare function estimateSize<T = any>(tree: TreeNode<T> | TreeNode<T>[], config?: TreeConfigInput): number;
1267
+
260
1268
  /**
261
- * 提取对象中所有纯对象字段的键(包括嵌套的),支持点语法路径
1269
+ * 在指定节点前插入新节点
262
1270
  *
263
- * @template T 源对象类型
264
- * @template D 递归深度,默认为2
1271
+ * @category Tree
1272
+ * @param tree 树形结构数组
1273
+ * @param targetId 目标节点的ID
1274
+ * @param newNode 要插入的新节点数据
1275
+ * @param config 树形配置选项
1276
+ * @returns 是否成功插入
265
1277
  * @example
266
1278
  * ```ts
267
- * type User = {
268
- * name: string
269
- * age: number
270
- * address: {
271
- * city: string
272
- * location: {
273
- * lat: number
274
- * lng: number
275
- * }
1279
+ * const tree = [
1280
+ * {
1281
+ * id: '1',
1282
+ * name: '节点1',
1283
+ * children: [
1284
+ * { id: '2', name: '节点2', children: [] }
1285
+ * ]
276
1286
  * }
277
- * }
278
- * type ObjectKeys = ObjectFieldKeys<User> // 'address' | 'address.location'
1287
+ * ]
1288
+ *
1289
+ * const success = insertBefore(tree, '2', { id: '1.5', name: '新节点' })
1290
+ * console.log(success) // true
279
1291
  * ```
280
1292
  */
281
- type ObjectFieldKeys<T, D extends number = 2> = [D] extends [never] ? never : {
282
- [K in keyof T & string]: IsPlainObject<T[K]> extends true ? K | `${K}.${ObjectFieldKeys<NonNullable<T[K]>, Depth[D]>}` : never;
283
- }[keyof T & string];
1293
+ declare function insertBefore<T = any>(tree: TreeNode<T>[], targetId: string, newNode: T, config?: TreeConfigInput): boolean;
284
1294
  /**
285
- * 提取对象中所有非对象字段的键
286
- * 排除纯对象字段,只保留原始类型字段的键
1295
+ * 在指定节点后插入新节点
287
1296
  *
288
- * @template T 源对象类型
1297
+ * @category Tree
1298
+ * @param tree 树形结构数组
1299
+ * @param targetId 目标节点的ID
1300
+ * @param newNode 要插入的新节点数据
1301
+ * @param config 树形配置选项
1302
+ * @returns 是否成功插入
289
1303
  * @example
290
1304
  * ```ts
291
- * type User = {
292
- * name: string
293
- * age: number
294
- * address: {
295
- * city: string
1305
+ * const tree = [
1306
+ * {
1307
+ * id: '1',
1308
+ * name: '节点1',
1309
+ * children: [
1310
+ * { id: '2', name: '节点2', children: [] }
1311
+ * ]
296
1312
  * }
297
- * }
298
- * type NonObjectKeys = NonObjectFieldKeys<User> // 'name' | 'age' | 'address.city'
1313
+ * ]
1314
+ *
1315
+ * const success = insertAfter(tree, '2', { id: '3', name: '新节点' })
1316
+ * console.log(success) // true
299
1317
  * ```
300
1318
  */
301
- type NonObjectFieldKeys<T> = Exclude<NestedKeys<T>, ObjectFieldKeys<T>>;
1319
+ declare function insertAfter<T = any>(tree: TreeNode<T>[], targetId: string, newNode: T, config?: TreeConfigInput): boolean;
302
1320
  /**
303
- * 提取对象中所有数组字段的键(包括嵌套的),支持点语法路径
1321
+ * 从树中删除指定节点
304
1322
  *
305
- * @template T 源对象类型
306
- * @template D 递归深度,默认为2
1323
+ * @category Tree
1324
+ * @param tree 树形结构数组
1325
+ * @param targetId 要删除的节点ID
1326
+ * @param config 树形配置选项
1327
+ * @returns 被删除的节点,未找到时返回undefined
307
1328
  * @example
308
1329
  * ```ts
309
- * type User = {
310
- * name: string
311
- * tags: string[]
312
- * posts: Array<{ title: string }>
313
- * profile: {
314
- * hobbies: string[]
1330
+ * const tree = [
1331
+ * {
1332
+ * id: '1',
1333
+ * name: '根节点',
1334
+ * children: [
1335
+ * { id: '2', name: '子节点', children: [] }
1336
+ * ]
315
1337
  * }
316
- * }
317
- * type ArrayKeys = ArrayFieldKeys<User> // 'tags' | 'posts' | 'profile.hobbies'
1338
+ * ]
1339
+ *
1340
+ * const removed = remove(tree, '2')
1341
+ * console.log(removed?.name) // '子节点'
318
1342
  * ```
319
1343
  */
320
- type ArrayFieldKeys<T, D extends number = 2> = [D] extends [never] ? never : {
321
- [K in keyof T & string]: NonNullable<T[K]> extends any[] ? K : IsPlainObject<T[K]> extends true ? `${K}.${ArrayFieldKeys<NonNullable<T[K]>, Depth[D]>}` : never;
322
- }[keyof T & string];
323
- /**
324
- * 根据路径字符串提取对象属性的类型,支持点语法和嵌套对象
325
- * @example GetFieldValue<User, 'tags'> // string[]
326
- * @example GetFieldValue<User, 'profile.bio'> // string
327
- */
328
- type GetFieldValue<T, P extends string> = P extends keyof T ? T[P] : P extends `${infer K}.${infer Rest}` ? K extends keyof T ? T[K] extends undefined ? undefined : GetFieldValue<NonNullable<T[K]>, Rest> : unknown : unknown;
329
-
330
- declare const StorageTypeSchema: z.ZodEnum<{
331
- localStorage: "localStorage";
332
- sessionStorage: "sessionStorage";
333
- }>;
334
- type StorageType = z.infer<typeof StorageTypeSchema>;
335
- declare function createStorageConfigSchema<T = unknown>(schema: z.ZodType<T>): z.ZodObject<{
336
- key: z.ZodString;
337
- schema: z.ZodCustom<z.ZodType<T, unknown, z.core.$ZodTypeInternals<T, unknown>>, z.ZodType<T, unknown, z.core.$ZodTypeInternals<T, unknown>>>;
338
- defaultValue: z.ZodCustom<T, T>;
339
- prefix: z.ZodDefault<z.ZodString>;
340
- storage: z.ZodDefault<z.ZodEnum<{
341
- localStorage: "localStorage";
342
- sessionStorage: "sessionStorage";
343
- }>>;
344
- }, z.core.$strip>;
345
- type StorageConfig<T = unknown> = z.infer<ReturnType<typeof createStorageConfigSchema<T>>>;
346
- type StorageConfigInput<T = unknown> = z.input<ReturnType<typeof createStorageConfigSchema<T>>>;
347
- interface AppStorageReturn<T> {
348
- state: Ref<T>;
349
- getItem: () => T;
350
- setItem: (value: T) => void;
351
- removeItem: () => void;
352
- }
353
-
354
- /**
355
- * vue-component-type-helpers
356
- * Copy from https://github.com/vuejs/language-tools/tree/master/packages/component-type-helpers
357
- */
358
- type IsComponent = StringOrVNode | Component | DefineComponent | ((...args: any[]) => any);
359
- type ComponentType<T> = T extends new (...args: any) => {} ? 1 : T extends (...args: any) => any ? 2 : 0;
360
- type ComponentProps<T> = T extends new (...args: any) => {
361
- $props: infer P;
362
- } ? NonNullable<P> : T extends (props: infer P, ...args: any) => any ? P : {};
363
- type ComponentSlots<T> = T extends new (...args: any) => {
364
- $slots: infer S;
365
- } ? NonNullable<S> : T extends (props: any, ctx: {
366
- slots: infer S;
367
- attrs: any;
368
- emit: any;
369
- }, ...args: any) => any ? NonNullable<S> : {};
370
- type ComponentAttrs<T> = T extends new (...args: any) => {
371
- $attrs: infer A;
372
- } ? NonNullable<A> : T extends (props: any, ctx: {
373
- slots: any;
374
- attrs: infer A;
375
- emit: any;
376
- }, ...args: any) => any ? NonNullable<A> : {};
377
- type ComponentEmit<T> = T extends new (...args: any) => {
378
- $emit: infer E;
379
- } ? NonNullable<E> : T extends (props: any, ctx: {
380
- slots: any;
381
- attrs: any;
382
- emit: infer E;
383
- }, ...args: any) => any ? NonNullable<E> : {};
384
- type ComponentExposed<T> = T extends new (...args: any) => infer E ? E : T extends (props: any, ctx: any, expose: (exposed: infer E) => any, ...args: any) => any ? NonNullable<E> : {};
1344
+ declare function remove<T = any>(tree: TreeNode<T>[], targetId: string, config?: TreeConfigInput): TreeNode<T> | undefined;
385
1345
 
386
1346
  /**
387
- * 应用存储管理的组合式函数,支持localStorage和sessionStorage
1347
+ * 查找树中第一个满足条件的节点
388
1348
  *
389
- * @category Composables
390
- * @param config 存储配置对象
391
- * @returns 存储管理对象,包含响应式状态和操作方法
1349
+ * @category Tree
1350
+ * @param tree 树形结构(单个节点或节点数组)
1351
+ * @param predicate 查找条件函数
1352
+ * @param config 树形配置选项
1353
+ * @returns 匹配的节点;未找到时返回undefined
392
1354
  * @example
393
1355
  * ```ts
394
- * import { z } from 'zod/v4'
395
- *
396
- * // 定义用户偏好设置的schema
397
- * const userPrefsSchema = z.object({
398
- * theme: z.enum(['light', 'dark']),
399
- * language: z.string(),
400
- * fontSize: z.number().min(12).max(24)
401
- * })
402
- *
403
- * // 创建存储管理实例
404
- * const { state, setItem, getItem, removeItem } = useAppStorage({
405
- * key: 'user-preferences',
406
- * defaultValue: {
407
- * theme: 'light',
408
- * language: 'zh-CN',
409
- * fontSize: 16
410
- * },
411
- * schema: userPrefsSchema,
412
- * storage: 'localStorage',
413
- * prefix: 'app'
414
- * })
415
- *
416
- * // 使用响应式状态
417
- * console.log(state.value.theme) // 'light'
1356
+ * const tree = [
1357
+ * {
1358
+ * id: '1',
1359
+ * name: '部门1',
1360
+ * children: [
1361
+ * { id: '2', name: '部门1-1', children: [] }
1362
+ * ]
1363
+ * }
1364
+ * ]
418
1365
  *
419
- * // 更新设置
420
- * setItem({
421
- * theme: 'dark',
422
- * language: 'en-US',
423
- * fontSize: 18
424
- * })
1366
+ * const result = find(tree, ({ node }) => node.name === '部门1-1')
1367
+ * console.log(result?.id) // '2'
425
1368
  * ```
426
1369
  */
427
- declare function useAppStorage<T = unknown>(config: StorageConfigInput<T>): AppStorageReturn<T>;
428
-
1370
+ declare function find<T = any>(tree: TreeNode<T> | TreeNode<T>[], predicate: TreePredicate<T>, config?: TreeConfigInput): TreeNode<T> | undefined;
429
1371
  /**
430
- * 复制文本到剪贴板的组合式函数
1372
+ * 查找树中所有满足条件的节点
431
1373
  *
432
- * @category Composables
433
- * @param text 要复制的文本内容
434
- * @returns 复制是否成功的Promise
1374
+ * @category Tree
1375
+ * @param tree 树形结构(单个节点或节点数组)
1376
+ * @param predicate 查找条件函数
1377
+ * @param config 树形配置选项
1378
+ * @returns 所有匹配的节点数组
435
1379
  * @example
436
1380
  * ```ts
437
- * // 复制简单文本
438
- * const copyText = async () => {
439
- * const success = await useCopyCode('Hello, World!')
440
- * if (success) {
441
- * console.log('复制成功')
442
- * } else {
443
- * console.log('复制失败')
444
- * }
445
- * }
446
- *
447
- * // 复制代码块
448
- * const copyCodeBlock = async () => {
449
- * const code = `
450
- * function hello() {
451
- * console.log('Hello, World!')
452
- * }
453
- * `
454
- * const success = await useCopyCode(code)
455
- * if (success) {
456
- * // 显示复制成功提示
457
- * showNotification('代码已复制到剪贴板')
1381
+ * const tree = [
1382
+ * {
1383
+ * id: '1',
1384
+ * type: 'folder',
1385
+ * name: '根目录',
1386
+ * children: [
1387
+ * { id: '2', type: 'file', name: '文件1', children: [] },
1388
+ * { id: '3', type: 'file', name: '文件2', children: [] }
1389
+ * ]
458
1390
  * }
459
- * }
1391
+ * ]
460
1392
  *
461
- * // 在点击事件中使用
462
- * const handleCopy = () => {
463
- * useCopyCode(document.getElementById('code').textContent)
464
- * }
1393
+ * const files = findAll(tree, ({ node }) => node.type === 'file')
1394
+ * console.log(files.length) // 2
465
1395
  * ```
466
1396
  */
467
- declare function useCopyCode(text: string): Promise<boolean>;
468
-
1397
+ declare function findAll<T = any>(tree: TreeNode<T> | TreeNode<T>[], predicate: TreePredicate<T>, config?: TreeConfigInput): TreeNode<T>[];
469
1398
  /**
470
- * 数组去重,返回去除重复元素后的新数组
1399
+ * 根据ID查找树中的节点
471
1400
  *
472
- * @category Array
473
- * @param arr 待去重的数组
474
- * @returns 去重后的新数组
1401
+ * @category Tree
1402
+ * @param tree 树形结构(单个节点或节点数组)
1403
+ * @param id 要查找的节点ID
1404
+ * @param config 树形配置选项
1405
+ * @returns 匹配的节点;未找到时返回undefined
475
1406
  * @example
476
1407
  * ```ts
477
- * const numbers = [1, 2, 2, 3, 3, 4]
478
- * const uniqueNumbers = unique(numbers)
479
- * console.log(uniqueNumbers) // [1, 2, 3, 4]
1408
+ * const tree = [
1409
+ * {
1410
+ * id: '1',
1411
+ * name: '根节点',
1412
+ * children: [
1413
+ * { id: '2', name: '子节点', children: [] }
1414
+ * ]
1415
+ * }
1416
+ * ]
480
1417
  *
481
- * const strings = ['a', 'b', 'a', 'c']
482
- * const uniqueStrings = unique(strings)
483
- * console.log(uniqueStrings) // ['a', 'b', 'c']
1418
+ * const result = findById(tree, '2')
1419
+ * console.log(result?.name) // '子节点'
484
1420
  * ```
485
1421
  */
486
- declare function unique<T>(arr: T[]): T[];
1422
+ declare function findById<T = any>(tree: TreeNode<T> | TreeNode<T>[], id: string, config?: TreeConfigInput): TreeNode<T> | undefined;
1423
+
487
1424
  /**
488
- * 将数组分割成指定大小的块
1425
+ * 过滤树形结构,保留满足条件的节点及其祖先和后代
489
1426
  *
490
- * @category Array
491
- * @param arr 待分割的数组
492
- * @param size 每个块的大小
493
- * @returns 分割后的二维数组
1427
+ * @category Tree
1428
+ * @param tree 树形结构(单个节点或节点数组)
1429
+ * @param predicate 过滤条件函数,接收对象参数 {node, depth, path, index}
1430
+ * @param config 树形配置选项
1431
+ * @returns 过滤后的树形结构数组
494
1432
  * @example
495
1433
  * ```ts
496
- * const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
497
- * const chunks = chunk(numbers, 3)
498
- * console.log(chunks) // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
1434
+ * const tree = [
1435
+ * {
1436
+ * id: '1',
1437
+ * type: 'folder',
1438
+ * name: '根目录',
1439
+ * children: [
1440
+ * { id: '2', type: 'file', name: '文档.txt', children: [] },
1441
+ * { id: '3', type: 'folder', name: '子目录', children: [
1442
+ * { id: '4', type: 'file', name: '图片.jpg', children: [] }
1443
+ * ] }
1444
+ * ]
1445
+ * }
1446
+ * ]
499
1447
  *
500
- * const names = ['Alice', 'Bob', 'Charlie', 'David', 'Eve']
501
- * const pairs = chunk(names, 2)
502
- * console.log(pairs) // [['Alice', 'Bob'], ['Charlie', 'David'], ['Eve']]
1448
+ * const filtered = filter(tree, ({ node }) => node.type === 'file')
1449
+ * // 返回包含所有文件节点及其父级路径的树结构
503
1450
  * ```
504
1451
  */
505
- declare function chunk<T>(arr: T[], size: number): T[][];
1452
+ declare function filter<T = any>(tree: TreeNode<T> | TreeNode<T>[], predicate: TreePredicate<T>, config?: TreeConfigInput): TreeNode<T>[];
506
1453
  /**
507
- * 数组扁平化,将嵌套数组展平到指定深度
1454
+ * 转换树形结构,将每个节点转换为新的结构
508
1455
  *
509
- * @category Array
510
- * @param arr 待扁平化的数组
511
- * @param depth 扁平化深度,默认为1
512
- * @returns 扁平化后的数组
1456
+ * @category Tree
1457
+ * @param tree 树形结构(单个节点或节点数组)
1458
+ * @param transformer 节点转换函数,接收对象参数 {node, depth, path, index}
1459
+ * @param config 树形配置选项
1460
+ * @returns 转换后的树形结构数组
513
1461
  * @example
514
1462
  * ```ts
515
- * const nested = [1, [2, 3], [4, [5, 6]]]
516
- * const flat1 = flatten(nested)
517
- * console.log(flat1) // [1, 2, 3, 4, [5, 6]]
1463
+ * const tree = [
1464
+ * {
1465
+ * id: '1',
1466
+ * name: '部门1',
1467
+ * children: [
1468
+ * { id: '2', name: '部门1-1', children: [] }
1469
+ * ]
1470
+ * }
1471
+ * ]
518
1472
  *
519
- * const flat2 = flatten(nested, 2)
520
- * console.log(flat2) // [1, 2, 3, 4, 5, 6]
1473
+ * const transformed = transform(tree, ({ node, depth }) => ({
1474
+ * key: node.id,
1475
+ * title: node.name,
1476
+ * level: depth
1477
+ * }))
1478
+ * // 转换为新的数据结构
521
1479
  * ```
522
1480
  */
523
- declare function flatten<T>(arr: T[], depth?: number): any[];
1481
+ declare function transform<T = any, R = any>(tree: TreeNode<T> | TreeNode<T>[], transformer: TreeTransformer<T, R>, config?: TreeConfigInput): TreeNode<R>[];
524
1482
 
525
1483
  /**
526
- * 防抖函数,在指定时间内多次触发只执行最后一次
1484
+ * 遍历树形结构的每个节点
527
1485
  *
528
- * @category Async
529
- * @param func 需要防抖的函数
530
- * @param wait 防抖延迟时间(毫秒)
531
- * @returns 防抖处理后的函数
1486
+ * @category Tree
1487
+ * @param tree 树形结构(单个节点或节点数组)
1488
+ * @param visitor 访问者函数,接收对象参数 {node, depth, path, index},返回false可以跳过子节点的遍历
1489
+ * @param config 树形配置选项
532
1490
  * @example
533
1491
  * ```ts
534
- * const debouncedSearch = debounce((query: string) => {
535
- * console.log('搜索:', query)
536
- * }, 300)
1492
+ * const tree = [
1493
+ * {
1494
+ * id: '1',
1495
+ * name: '根节点',
1496
+ * children: [
1497
+ * { id: '2', name: '子节点', children: [] }
1498
+ * ]
1499
+ * }
1500
+ * ]
537
1501
  *
538
- * // 连续调用,只有最后一次会执行
539
- * debouncedSearch('a')
540
- * debouncedSearch('ab')
541
- * debouncedSearch('abc') // 只有这次会在300ms后执行
1502
+ * forEach(tree, ({ node, depth }) => {
1503
+ * console.log(`${' '.repeat(depth * 2)}${node.name}`)
1504
+ * // 输出缩进的树结构
1505
+ * })
542
1506
  * ```
543
1507
  */
544
- declare function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void;
1508
+ declare function forEach<T = any>(tree: TreeNode<T> | TreeNode<T>[], visitor: TreeVisitor<T>, config?: TreeConfigInput): void;
545
1509
 
546
1510
  /**
547
- * 延迟执行函数,返回一个在指定时间后resolve的Promise
1511
+ * 获取树形结构的统计信息
548
1512
  *
549
- * @category Async
550
- * @param ms 延迟时间(毫秒)
551
- * @returns 延迟Promise
1513
+ * @category Tree
1514
+ * @param tree 树形结构(单个节点或节点数组)
1515
+ * @param config 树形配置选项
1516
+ * @returns 树的统计信息,包含总节点数、叶子节点数、最大深度和分支节点数
552
1517
  * @example
553
1518
  * ```ts
554
- * // 延迟1秒后继续执行
555
- * await sleep(1000)
556
- * console.log('1秒后执行')
1519
+ * const tree = [
1520
+ * {
1521
+ * id: '1',
1522
+ * name: '根节点',
1523
+ * children: [
1524
+ * { id: '2', name: '子节点1', children: [] },
1525
+ * { id: '3', name: '子节点2', children: [
1526
+ * { id: '4', name: '孙节点', children: [] }
1527
+ * ] }
1528
+ * ]
1529
+ * }
1530
+ * ]
557
1531
  *
558
- * // 在异步函数中使用
559
- * async function delayedOperation() {
560
- * console.log('开始')
561
- * await sleep(500)
562
- * console.log('500ms后执行')
563
- * }
1532
+ * const stats = getStats(tree)
1533
+ * console.log(stats) // { total: 4, leaves: 2, depth: 3, branches: 2 }
564
1534
  * ```
565
1535
  */
566
- declare function sleep(ms: number): Promise<void>;
1536
+ declare function getStats<T = any>(tree: TreeNode<T> | TreeNode<T>[], config?: TreeConfigInput): TreeStats;
567
1537
  /**
568
- * 可取消的延迟函数,返回Promise和取消函数
1538
+ * 验证树形结构的有效性
569
1539
  *
570
- * @category Async
571
- * @param ms 延迟时间(毫秒)
572
- * @returns 包含Promise和取消函数的对象
1540
+ * @category Tree
1541
+ * @param tree 树形结构(单个节点或节点数组)
1542
+ * @param config 树形配置选项
1543
+ * @returns 验证结果,包含是否有效和错误信息数组
573
1544
  * @example
574
1545
  * ```ts
575
- * const { promise, cancel } = sleepWithCancel(5000)
576
- *
577
- * // 在另一个地方取消延迟
578
- * setTimeout(() => {
579
- * cancel() // 取消延迟
580
- * }, 2000)
1546
+ * const tree = [
1547
+ * {
1548
+ * id: '1',
1549
+ * name: '根节点',
1550
+ * children: [
1551
+ * { id: '2', name: '子节点', children: [] }
1552
+ * ]
1553
+ * }
1554
+ * ]
581
1555
  *
582
- * try {
583
- * await promise
584
- * console.log('5秒后执行')
585
- * } catch (error) {
586
- * console.log('延迟被取消')
587
- * }
1556
+ * const result = validate(tree)
1557
+ * console.log(result.isValid) // true
1558
+ * console.log(result.errors) // []
588
1559
  * ```
589
1560
  */
590
- declare function sleepWithCancel(ms: number): {
591
- promise: Promise<void>;
592
- cancel: () => void;
1561
+ declare function validate<T = any>(tree: TreeNode<T> | TreeNode<T>[], config?: TreeConfigInput): {
1562
+ isValid: boolean;
1563
+ errors: string[];
593
1564
  };
594
1565
 
595
1566
  /**
596
- * 节流函数,在指定时间内多次触发只执行第一次
1567
+ * 树形数据结构操作工具类
597
1568
  *
598
- * @category Async
599
- * @param func 需要节流的函数
600
- * @param limit 节流时间间隔(毫秒)
601
- * @returns 节流处理后的函数
602
- * @example
603
- * ```ts
604
- * const throttledScroll = throttle((event: Event) => {
605
- * console.log('滚动事件处理')
606
- * }, 100)
1569
+ * 提供了一系列操作树形数据的静态方法,包括:
1570
+ * - 查找:find, findAll, findById
1571
+ * - 转换:fromList, toList, transform
1572
+ * - 过滤:filter
1573
+ * - 遍历:forEach
1574
+ * - 统计:estimateSize, getStats
1575
+ * - 修改:insertBefore, insertAfter, remove
1576
+ * - 验证:validate
607
1577
  *
608
- * // 监听滚动事件,每100ms最多执行一次
609
- * window.addEventListener('scroll', throttledScroll)
610
- * ```
611
- */
612
- declare function throttle<T extends (...args: any[]) => any>(func: T, limit: number): (...args: Parameters<T>) => void;
613
-
614
- /**
615
- * 将SVG字符串转换为PNG格式的Blob对象
1578
+ * 所有使用谓词函数或访问函数的方法都采用对象解构参数格式:
1579
+ * `({ node, depth, path, index }) => boolean`
616
1580
  *
617
- * @category File
618
- * @param svg SVG字符串
619
- * @returns PNG格式的Blob对象
620
- * @throws 当SVG无效或转换失败时抛出错误
621
1581
  * @example
622
1582
  * ```ts
623
- * const svgString = '<svg width="100" height="100"><circle cx="50" cy="50" r="40" fill="red"/></svg>'
1583
+ * // 1. 从扁平数组创建树形结构
1584
+ * const departments = [
1585
+ * { id: '1', name: '技术部', parentId: null },
1586
+ * { id: '2', name: '前端组', parentId: '1' },
1587
+ * { id: '3', name: '后端组', parentId: '1' },
1588
+ * { id: '4', name: 'UI 组', parentId: '2' },
1589
+ * { id: '5', name: '测试组', parentId: '2' }
1590
+ * ]
624
1591
  *
625
- * try {
626
- * const pngBlob = await convertSvgToPng(svgString)
627
- * const url = URL.createObjectURL(pngBlob)
1592
+ * const tree = Tree.fromList(departments, {
1593
+ * id: 'id',
1594
+ * pid: 'parentId',
1595
+ * children: 'children'
1596
+ * })
628
1597
  *
629
- * // 用于下载或显示
630
- * const img = document.createElement('img')
631
- * img.src = url
632
- * document.body.appendChild(img)
633
- * } catch (error) {
634
- * console.error('SVG转换失败:', error)
1598
+ * // 2. 查找节点
1599
+ * const frontend = Tree.find(tree, ({ node }) => node.name === '前端组')
1600
+ * console.log(frontend) // { id: '2', name: '前端组', children: [...] }
1601
+ *
1602
+ * const uiNode = Tree.findById(tree, '4')
1603
+ * console.log(uiNode) // { id: '4', name: 'UI 组', ... }
1604
+ *
1605
+ * // 3. 查找所有叶子节点
1606
+ * const leaves = Tree.findAll(tree, ({ node }) => {
1607
+ * return !node.children || node.children.length === 0
1608
+ * })
1609
+ * console.log(leaves) // [{ id: '4', ... }, { id: '5', ... }, { id: '3', ... }]
1610
+ *
1611
+ * // 4. 过滤节点(保留匹配节点及其祖先)
1612
+ * const filtered = Tree.filter(tree, ({ node }) => node.name.includes('组'))
1613
+ * // 返回包含所有 "组" 节点及其父级路径的树结构
1614
+ *
1615
+ * // 5. 转换节点结构
1616
+ * const menuTree = Tree.transform(tree, ({ node, depth }) => ({
1617
+ * key: node.id,
1618
+ * label: node.name,
1619
+ * level: depth,
1620
+ * indent: depth * 20
1621
+ * }))
1622
+ *
1623
+ * // 6. 遍历所有节点
1624
+ * Tree.forEach(tree, ({ node, depth, path }) => {
1625
+ * const indent = ' '.repeat(depth)
1626
+ * const breadcrumb = path.map(n => n.name).join(' > ')
1627
+ * console.log(`${indent}${node.name} (路径: ${breadcrumb})`)
1628
+ * })
1629
+ *
1630
+ * // 7. 修改树结构
1631
+ * Tree.insertBefore(tree, '3', { id: '6', name: '运维组' })
1632
+ * Tree.insertAfter(tree, '2', { id: '7', name: '移动组' })
1633
+ * const removed = Tree.remove(tree, '5')
1634
+ *
1635
+ * // 8. 获取统计信息
1636
+ * const stats = Tree.getStats(tree)
1637
+ * console.log(stats)
1638
+ * // { total: 5, leaves: 3, depth: 3, branches: 2 }
1639
+ *
1640
+ * // 9. 验证树结构
1641
+ * const validation = Tree.validate(tree)
1642
+ * if (!validation.isValid) {
1643
+ * console.error('树结构错误:', validation.errors)
635
1644
  * }
1645
+ *
1646
+ * // 10. 转换回扁平数组
1647
+ * const flatList = Tree.toList(tree)
1648
+ * console.log(flatList) // [{ id: '1', name: '技术部' }, ...]
636
1649
  * ```
637
1650
  */
638
- declare function convertSvgToPng(svg: string): Promise<Blob>;
1651
+ declare class Tree {
1652
+ static fromList: typeof fromList;
1653
+ static toList: typeof toList;
1654
+ static estimateSize: typeof estimateSize;
1655
+ static find: typeof find;
1656
+ static findAll: typeof findAll;
1657
+ static findById: typeof findById;
1658
+ static insertBefore: typeof insertBefore;
1659
+ static insertAfter: typeof insertAfter;
1660
+ static remove: typeof remove;
1661
+ static filter: typeof filter;
1662
+ static transform: typeof transform;
1663
+ static forEach: typeof forEach;
1664
+ static getStats: typeof getStats;
1665
+ static validate: typeof validate;
1666
+ }
639
1667
 
640
1668
  /**
641
- * 从响应头中提取文件名
1669
+ * 统一同步/异步返回类型
1670
+ * @typeParam T - 类型
1671
+ * @example
1672
+ * // type T = string | Promise<string>
1673
+ * // type R = ApiAwaitable<T>
1674
+ * // 结果:R 为 string | Promise<string>
1675
+ */
1676
+ type ApiAwaitable<T> = T | Promise<T>;
1677
+ /**
1678
+ * 提取Promise类型
1679
+ * @typeParam T - 类型
1680
+ * @example
1681
+ * // type T = Promise<string>
1682
+ * // type R = ApiUnwrapPromise<T>
1683
+ * // 结果:R 为 string
1684
+ */
1685
+ type ApiUnwrapPromise<T> = T extends Promise<infer U> ? U : T;
1686
+ /**
1687
+ * 提取函数返回类型
1688
+ * @typeParam TFn - 函数类型
1689
+ * @example
1690
+ * // type Fn = (x: number, y: string) => Promise<string>
1691
+ * // type R = ApiAwaitedReturn<Fn>
1692
+ * // 结果:R 为 string
1693
+ */
1694
+ type ApiAwaitedReturn<TFn> = TFn extends (...args: any[]) => ApiAwaitable<infer R> ? R : never;
1695
+
1696
+ /**
1697
+ * 提供字符串字面量提示的同时允许任意字符串
1698
+ * 在 IDE 中提供 T 类型的自动补全提示,但不限制只能使用这些值
642
1699
  *
643
- * @category File
644
- * @param headers 响应头对象
645
- * @param fallbackName 默认文件名
646
- * @returns 提取的文件名
647
1700
  * @example
648
1701
  * ```ts
649
- * // 从响应头中提取文件名
650
- * const headers = new Headers({
651
- * 'content-disposition': 'attachment; filename="report.pdf"'
652
- * })
653
- * const filename = extractFilename(headers, 'download')
654
- * console.log(filename) // 'report.pdf'
1702
+ * type Color = Suggest<'red' | 'blue' | 'green'>
655
1703
  *
656
- * // 处理编码的文件名
657
- * const encodedHeaders = new Headers({
658
- * 'content-disposition': 'attachment; filename*=UTF-8\'\'%E6%8A%A5%E5%91%8A.pdf'
659
- * })
660
- * const encodedFilename = extractFilename(encodedHeaders)
661
- * console.log(encodedFilename) // '报告.pdf'
1704
+ * // IDE 会提示 'red', 'blue', 'green',但也可以使用其他字符串
1705
+ * const color1: Color = 'red' // 有提示
1706
+ * const color2: Color = 'yellow' // 也可以,虽然没有提示
662
1707
  * ```
663
1708
  */
664
- declare function extractFilename(headers?: Headers, fallbackName?: string): string;
1709
+ type Suggest<T extends string> = T | (string & {});
665
1710
  /**
666
- * 触发浏览器下载文件
1711
+ * 响应式值类型 - 基于 Vue 的 `MaybeRefOrGetter` 扩展,额外支持上下文回调
1712
+ *
1713
+ * @template T - 值类型
1714
+ * @template CTX - 上下文类型(用于回调函数)
667
1715
  *
668
- * @category File
669
- * @param blob 文件数据
670
- * @param filename 文件名
671
1716
  * @example
672
1717
  * ```ts
673
- * // 下载文本文件
674
- * const textBlob = new Blob(['Hello, World!'], { type: 'text/plain' })
675
- * triggerDownload(textBlob, 'hello.txt')
676
- *
677
- * // 下载JSON数据
678
- * const data = { name: 'John', age: 30 }
679
- * const jsonBlob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
680
- * triggerDownload(jsonBlob, 'data.json')
681
- *
682
- * // 下载图片
683
- * const canvas = document.createElement('canvas')
684
- * canvas.toBlob((blob) => {
685
- * if (blob) {
686
- * triggerDownload(blob, 'image.png')
687
- * }
688
- * })
1718
+ * const value: ReactiveValue<boolean> = ref(false)
1719
+ * const getter: ReactiveValue<string> = () => name.value
1720
+ * const computed: ReactiveValue<number> = computed(() => count.value)
1721
+ * const withContext: ReactiveValue<boolean, Context> = (ctx) => ctx.value > 0
689
1722
  * ```
690
1723
  */
691
- declare function triggerDownload(blob: Blob, filename: string): void;
1724
+ type ReactiveValue<T, CTX = never> = [CTX] extends [never] ? MaybeRefOrGetter<T> : MaybeRefOrGetter<T> | ((ctx: CTX) => T);
1725
+ type StripNullable<T> = T extends null | undefined ? never : T;
1726
+
1727
+ interface ParsedUrl {
1728
+ /** 完整的原始 URL */
1729
+ href: string;
1730
+ /** 协议 (http:, https:, etc.) */
1731
+ protocol: string;
1732
+ /** 主机名 + 端口 */
1733
+ host: string;
1734
+ /** 主机名 */
1735
+ hostname: string;
1736
+ /** 端口号 */
1737
+ port: string;
1738
+ /** 路径部分 */
1739
+ pathname: string;
1740
+ /** 查询字符串 (包含 ?) */
1741
+ search: string;
1742
+ /** 哈希部分 (包含 #) */
1743
+ hash: string;
1744
+ /** 用户认证信息 (user:pass) */
1745
+ auth: string;
1746
+ /** 源 (protocol + host) */
1747
+ origin: string;
1748
+ }
1749
+ /**
1750
+ * 查询参数值类型
1751
+ */
1752
+ type QueryParamValue = string | number | boolean | null | undefined;
1753
+ /**
1754
+ * 查询参数对象类型
1755
+ */
1756
+ type QueryParams = Record<string, QueryParamValue | QueryParamValue[]>;
692
1757
 
693
1758
  /**
694
- * 格式化文件大小,将字节数转换为可读的文件大小字符串
1759
+ * vue-component-type-helpers
1760
+ * Copy from https://github.com/vuejs/language-tools/tree/master/packages/component-type-helpers
1761
+ */
1762
+ type IsComponent = StringOrVNode | Component | DefineComponent | ((...args: any[]) => any);
1763
+ type ComponentType<T> = T extends new (...args: any) => {} ? 1 : T extends (...args: any) => any ? 2 : 0;
1764
+ type ComponentProps<T> = T extends new (...args: any) => {
1765
+ $props: infer P;
1766
+ } ? NonNullable<P> : T extends (props: infer P, ...args: any) => any ? P : {};
1767
+ type ComponentSlots<T> = T extends new (...args: any) => {
1768
+ $slots: infer S;
1769
+ } ? NonNullable<S> : T extends (props: any, ctx: {
1770
+ slots: infer S;
1771
+ attrs: any;
1772
+ emit: any;
1773
+ }, ...args: any) => any ? NonNullable<S> : {};
1774
+ type ComponentAttrs<T> = T extends new (...args: any) => {
1775
+ $attrs: infer A;
1776
+ } ? NonNullable<A> : T extends (props: any, ctx: {
1777
+ slots: any;
1778
+ attrs: infer A;
1779
+ emit: any;
1780
+ }, ...args: any) => any ? NonNullable<A> : {};
1781
+ type ComponentEmit<T> = T extends new (...args: any) => {
1782
+ $emit: infer E;
1783
+ } ? NonNullable<E> : T extends (props: any, ctx: {
1784
+ slots: any;
1785
+ attrs: any;
1786
+ emit: infer E;
1787
+ }, ...args: any) => any ? NonNullable<E> : {};
1788
+ type ComponentExposed<T> = T extends new (...args: any) => infer E ? E : T extends (props: any, ctx: any, expose: (exposed: infer E) => any, ...args: any) => any ? NonNullable<E> : {};
1789
+
1790
+ /**
1791
+ * 将数组分割成指定大小的块
695
1792
  *
696
- * @category File
697
- * @param bytes 文件大小(字节)
698
- * @returns 格式化后的文件大小字符串
1793
+ * @category Array
1794
+ * @param arr 待分割的数组
1795
+ * @param size 每个块的大小
1796
+ * @returns 分割后的二维数组
699
1797
  * @example
700
1798
  * ```ts
701
- * console.log(formatFileSize(1024)) // '1 KB'
702
- * console.log(formatFileSize(1536)) // '1.5 KB'
703
- * console.log(formatFileSize(1048576)) // '1 MB'
704
- * console.log(formatFileSize(1073741824)) // '1 GB'
1799
+ * const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
1800
+ * const chunks = chunk(numbers, 3)
1801
+ * console.log(chunks) // [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
705
1802
  *
706
- * // 处理边界情况
707
- * console.log(formatFileSize(0)) // '0 Bytes'
708
- * console.log(formatFileSize(-100)) // '0 Bytes'
1803
+ * const names = ['Alice', 'Bob', 'Charlie', 'David', 'Eve']
1804
+ * const pairs = chunk(names, 2)
1805
+ * console.log(pairs) // [['Alice', 'Bob'], ['Charlie', 'David'], ['Eve']]
709
1806
  * ```
710
1807
  */
711
- declare function formatFileSize(bytes: number): string;
1808
+ declare function chunk<T>(arr: T[], size: number): T[][];
712
1809
 
713
1810
  /**
714
- * 替换SVG文件中的currentColor为指定颜色
1811
+ * 数组扁平化,将嵌套数组展平到指定深度
715
1812
  *
716
- * @category File
717
- * @param path SVG文件路径
718
- * @param color 替换的颜色值,不提供则返回原始SVG
719
- * @returns 处理后的SVG字符串
720
- * @throws 当文件获取失败或SVG无效时抛出错误
1813
+ * @category Array
1814
+ * @param arr 待扁平化的数组
1815
+ * @param depth 扁平化深度,默认为1
1816
+ * @returns 扁平化后的数组
721
1817
  * @example
722
1818
  * ```ts
723
- * // 获取并替换SVG中的currentColor
724
- * try {
725
- * const svgContent = await replaceCurrentColor('/icons/star.svg', '#ff0000')
726
- * const container = document.createElement('div')
727
- * container.innerHTML = svgContent
728
- * document.body.appendChild(container)
729
- * } catch (error) {
730
- * console.error('SVG处理失败:', error)
731
- * }
1819
+ * const nested = [1, [2, 3], [4, [5, 6]]]
1820
+ * const flat1 = flatten(nested)
1821
+ * console.log(flat1) // [1, 2, 3, 4, [5, 6]]
732
1822
  *
733
- * // 只获取SVG内容,不替换颜色
734
- * const originalSvg = await replaceCurrentColor('/icons/star.svg')
1823
+ * const flat2 = flatten(nested, 2)
1824
+ * console.log(flat2) // [1, 2, 3, 4, 5, 6]
735
1825
  * ```
736
1826
  */
737
- declare function replaceCurrentColor(path: string, color?: string): Promise<string>;
1827
+ declare function flatten<T>(arr: T[], depth?: number): any[];
738
1828
 
739
1829
  /**
740
- * 将对象的键名转换为kebab-case格式
1830
+ * 数组去重,返回去除重复元素后的新数组
741
1831
  *
742
- * @category Object
743
- * @param obj 待转换的对象
744
- * @param deep 是否深度转换嵌套对象,默认为false
745
- * @returns 转换后的对象
1832
+ * @category Array
1833
+ * @param arr 待去重的数组
1834
+ * @returns 去重后的新数组
746
1835
  * @example
747
1836
  * ```ts
748
- * const obj = {
749
- * firstName: 'John',
750
- * lastName: 'Doe',
751
- * userInfo: {
752
- * birthDate: '1990-01-01',
753
- * phoneNumber: '123-456-7890'
754
- * }
755
- * }
756
- *
757
- * const converted = convertToKebabCase(obj)
758
- * console.log(converted)
759
- * // {
760
- * // 'first-name': 'John',
761
- * // 'last-name': 'Doe',
762
- * // 'user-info': { birthDate: '1990-01-01', phoneNumber: '123-456-7890' }
763
- * // }
1837
+ * const numbers = [1, 2, 2, 3, 3, 4]
1838
+ * const uniqueNumbers = unique(numbers)
1839
+ * console.log(uniqueNumbers) // [1, 2, 3, 4]
764
1840
  *
765
- * const deepConverted = convertToKebabCase(obj, true)
766
- * console.log(deepConverted)
767
- * // {
768
- * // 'first-name': 'John',
769
- * // 'last-name': 'Doe',
770
- * // 'user-info': { 'birth-date': '1990-01-01', 'phone-number': '123-456-7890' }
771
- * // }
1841
+ * const strings = ['a', 'b', 'a', 'c']
1842
+ * const uniqueStrings = unique(strings)
1843
+ * console.log(uniqueStrings) // ['a', 'b', 'c']
772
1844
  * ```
773
1845
  */
774
- declare function convertToKebabCase<T extends AnyObject>(obj: T, deep?: boolean): T;
1846
+ declare function unique<T>(arr: T[]): T[];
775
1847
 
776
1848
  /**
777
- * 深拷贝任意 JavaScript 值。
778
- *
779
- * - 优先使用原生 `structuredClone`(若可用),覆盖 `Map`/`Set`/`TypedArray`/`ArrayBuffer` 等内建类型。
780
- * - 对不支持 `structuredClone` 的环境,使用回退实现:
781
- * - 支持循环引用(`WeakMap` 记忆化)。
782
- * - 保留原型与属性描述符(含 getter/setter),复制 symbol 键。
783
- * - 内建类型专项处理:`Date`/`RegExp`/`Map`/`Set`/`ArrayBuffer`/`TypedArray`/`URL`/`Error`。
784
- *
785
- * @category Object
786
- * @typeParam T 拷贝值的类型
787
- * @param obj 要被深拷贝的值
788
- * @param cache 内部使用的 `WeakMap`(循环引用记忆化),一般不需要传入
789
- * @returns 新的深拷贝值,与输入值结构等价、引用独立
1849
+ * 防抖函数,在指定时间内多次触发只执行最后一次
790
1850
  *
1851
+ * @category Async
1852
+ * @param func 需要防抖的函数
1853
+ * @param wait 防抖延迟时间(毫秒)
1854
+ * @returns 防抖处理后的函数
791
1855
  * @example
792
1856
  * ```ts
793
- * const source = { a: 1, d: new Date(), m: new Map([[1, { x: 2 }]]) }
794
- * const cloned = deepClone(source)
795
- * cloned !== source // true
796
- * cloned.d !== source.d // true
797
- * cloned.m !== source.m // true
798
- * cloned.m.get(1) !== source.m.get(1) // true
799
- * ```
1857
+ * const debouncedSearch = debounce((query: string) => {
1858
+ * console.log('搜索:', query)
1859
+ * }, 300)
800
1860
  *
801
- * @remarks
802
- * 若对象包含不可克隆资源(如带有原生句柄的自定义对象),请在外层进行自定义序列化逻辑或为该类型添加专用分支。
1861
+ * // 连续调用,只有最后一次会执行
1862
+ * debouncedSearch('a')
1863
+ * debouncedSearch('ab')
1864
+ * debouncedSearch('abc') // 只有这次会在300ms后执行
1865
+ * ```
803
1866
  */
804
- declare function deepClone<T>(obj: T, cache?: WeakMap<object, any>): T;
1867
+ declare function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void;
805
1868
 
806
1869
  /**
807
- * 从对象中排除指定的键,返回新对象
1870
+ * 延迟执行函数,返回一个在指定时间后resolve的Promise
808
1871
  *
809
- * @category Object
810
- * @param obj 源对象
811
- * @param keys 要排除的键数组
812
- * @returns 排除指定键后的新对象
1872
+ * @category Async
1873
+ * @param ms 延迟时间(毫秒)
1874
+ * @returns 延迟Promise
813
1875
  * @example
814
1876
  * ```ts
815
- * const user = {
816
- * id: 1,
817
- * name: 'John',
818
- * password: 'secret',
819
- * email: 'john@example.com'
820
- * }
821
- *
822
- * const publicUser = omit(user, ['password'])
823
- * console.log(publicUser) // { id: 1, name: 'John', email: 'john@example.com' }
1877
+ * // 延迟1秒后继续执行
1878
+ * await sleep(1000)
1879
+ * console.log('1秒后执行')
824
1880
  *
825
- * const basicInfo = omit(user, ['password', 'email'])
826
- * console.log(basicInfo) // { id: 1, name: 'John' }
1881
+ * // 在异步函数中使用
1882
+ * async function delayedOperation() {
1883
+ * console.log('开始')
1884
+ * await sleep(500)
1885
+ * console.log('500ms后执行')
1886
+ * }
827
1887
  * ```
828
1888
  */
829
- declare function omit<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): OmitByKey<T, K>;
1889
+ declare function sleep(ms: number): Promise<void>;
1890
+
830
1891
  /**
831
- * 从对象中排除值为undefined的键
1892
+ * 可取消的延迟函数,返回Promise和取消函数
832
1893
  *
833
- * @category Object
834
- * @param obj 源对象
835
- * @returns 排除undefined值后的新对象
1894
+ * @category Async
1895
+ * @param ms 延迟时间(毫秒)
1896
+ * @returns 包含Promise和取消函数的对象
836
1897
  * @example
837
1898
  * ```ts
838
- * const data = {
839
- * name: 'John',
840
- * age: undefined,
841
- * city: 'New York',
842
- * country: undefined
843
- * }
1899
+ * const { promise, cancel } = sleepWithCancel(5000)
844
1900
  *
845
- * const cleaned = omitUndefined(data)
846
- * console.log(cleaned) // { name: 'John', city: 'New York' }
1901
+ * // 在另一个地方取消延迟
1902
+ * setTimeout(() => {
1903
+ * cancel() // 取消延迟
1904
+ * }, 2000)
847
1905
  *
848
- * // 用于API请求前清理数据
849
- * const requestData = omitUndefined({
850
- * title: 'Post Title',
851
- * content: 'Post content',
852
- * tags: undefined,
853
- * published: true
854
- * })
1906
+ * try {
1907
+ * await promise
1908
+ * console.log('5秒后执行')
1909
+ * } catch (error) {
1910
+ * console.log('延迟被取消')
1911
+ * }
855
1912
  * ```
856
1913
  */
857
- declare function omitUndefined<T extends AnyObject>(obj: T): Partial<T>;
858
-
859
- type PathSegment = string | number;
860
- type PathSegments = PathSegment[];
861
- type PathInput = string | PathSegments;
862
- /**
863
- * 检查值是否为有效的容器类型(对象或数组)
864
- *
865
- * - isObject: 仅检查纯对象,排除数组
866
- * - isValidContainer: 检查所有可作为容器的类型(对象 + 数组)
867
- *
868
- * 支持 Vue 3 的 Proxy 对象和 Proxy 数组。
1914
+ declare function sleepWithCancel(ms: number): {
1915
+ promise: Promise<void>;
1916
+ cancel: () => void;
1917
+ };
1918
+
1919
+ /**
1920
+ * 节流函数,在指定时间内多次触发只执行第一次
869
1921
  *
870
- * @category Validator
871
- * @param value - 待检查的值
872
- * @returns 是否为有效容器(对象或数组)
1922
+ * @category Async
1923
+ * @param func 需要节流的函数
1924
+ * @param limit 节流时间间隔(毫秒)
1925
+ * @returns 节流处理后的函数
873
1926
  * @example
874
1927
  * ```ts
875
- * isValidContainer({}) // true
876
- * isValidContainer([]) // true
877
- * isValidContainer(new Proxy({}, {})) // true
878
- * isValidContainer(null) // false
879
- * isValidContainer('string') // false
880
- * isValidContainer(123) // false
1928
+ * const throttledScroll = throttle((event: Event) => {
1929
+ * console.log('滚动事件处理')
1930
+ * }, 100)
1931
+ *
1932
+ * // 监听滚动事件,每100ms最多执行一次
1933
+ * window.addEventListener('scroll', throttledScroll)
881
1934
  * ```
882
1935
  */
883
- declare function isValidContainer(value: any): boolean;
1936
+ declare function throttle<T extends (...args: any[]) => any>(func: T, limit: number): (...args: Parameters<T>) => void;
1937
+
884
1938
  /**
885
- * 将路径字符串解析为片段数组。
886
- *
887
- * - 支持点语法与方括号语法混用
888
- * - 引号键支持单/双引号与反斜杠转义
889
- * - 方括号内未引号的非负整数字面量解析为 number 段
890
- * - 点语法中的纯数字段保持字符串(不转为索引)
1939
+ * 解析 URL 字符串为结构化对象
891
1940
  *
892
- * @category Object
893
- * @param path 路径字符串或片段数组
894
- * @returns 解析后的片段数组
1941
+ * @category URL
1942
+ * @param url 要解析的 URL 字符串
1943
+ * @param base 可选的基础 URL,用于解析相对路径
1944
+ * @returns 解析后的 URL 对象,解析失败返回 null
895
1945
  * @example
896
1946
  * ```ts
897
- * toPath('a.b[0].c') // ['a', 'b', 0, 'c']
898
- * toPath("a['x.y']") // ['a', 'x.y']
1947
+ * parseUrl('https://example.com:8080/path?query=1#hash')
1948
+ * // {
1949
+ * // href: 'https://example.com:8080/path?query=1#hash',
1950
+ * // protocol: 'https:',
1951
+ * // host: 'example.com:8080',
1952
+ * // hostname: 'example.com',
1953
+ * // port: '8080',
1954
+ * // pathname: '/path',
1955
+ * // search: '?query=1',
1956
+ * // hash: '#hash',
1957
+ * // auth: '',
1958
+ * // origin: 'https://example.com:8080'
1959
+ * // }
1960
+ *
1961
+ * parseUrl('/path', 'https://example.com')
1962
+ * // 解析相对 URL
899
1963
  * ```
900
1964
  */
901
- declare function toPath(path: PathInput): PathSegments;
1965
+ declare function parseUrl(url: string, base?: string): ParsedUrl | null;
902
1966
  /**
903
- * 读取对象指定路径的值。
1967
+ * 检查字符串是否为有效的 URL
904
1968
  *
905
- * - 若取值结果为 undefined,则返回 defaultValue
906
- * - 若取值结果为 null,则直接返回 null(不触发默认值)
907
- * - 传入空路径时返回 object 本身
908
- *
909
- * @category Object
910
- * @param object 源对象
911
- * @param path 路径字符串或片段数组
912
- * @param defaultValue 结果为 undefined 时返回的默认值
913
- * @returns 读取到的值或默认值
1969
+ * @category URL
1970
+ * @param url 要检查的字符串
1971
+ * @returns 是否为有效 URL
914
1972
  * @example
915
1973
  * ```ts
916
- * const obj = { a: { b: { c: 1, d: undefined }, e: null }, arr: [{ x: 9 }] }
917
- * getPath(obj, 'a.b.c') // 1
918
- * getPath(obj, 'a.b.d', 42) // 42(d 为 undefined,使用默认值)
919
- * getPath(obj, 'a.e', 100) // null(null 不触发默认值)
920
- * getPath(obj, 'arr[0].x') // 9
921
- * getPath(obj, '') // 返回 obj 本身
1974
+ * isValidUrl('https://example.com') // true
1975
+ * isValidUrl('not a url') // false
1976
+ * isValidUrl('ftp://files.example.com') // true
922
1977
  * ```
923
1978
  */
924
- declare function getPath<T, D = undefined>(object: T, path: PathInput, defaultValue?: D): unknown | D;
1979
+ declare function isValidUrl(url: string): boolean;
925
1980
  /**
926
- * 在对象指定路径写入值。缺失路径会被自动创建:
927
- * - 下一段为 number(索引)时创建数组
928
- * - 下一段为 string(属性)时创建对象
1981
+ * 检查 URL 是否为绝对路径
929
1982
  *
930
- * 若中途遇到非容器类型(如字符串/数值/布尔),会被替换为正确的容器以继续写入。
931
- *
932
- * @category Object
933
- * @param object 目标对象(原地修改并返回同一引用)
934
- * @param path 路径字符串或片段数组
935
- * @param value 要写入的值
936
- * @returns 原对象(已修改)
1983
+ * @category URL
1984
+ * @param url 要检查的 URL
1985
+ * @returns 是否为绝对路径
937
1986
  * @example
938
1987
  * ```ts
939
- * const obj: any = {}
940
- * setPath(obj, 'a.b[0].c', 7)
941
- * // obj => { a: { b: [{ c: 7 }] } }
942
- *
943
- * setPath(obj, 'a.b[2].d', 8)
944
- * // 数组自动扩容到长度 3
945
- * // obj.a.b[2] => { d: 8 }
946
- *
947
- * setPath(obj, 'a.0.b', 1) // 点语法数字键保持为字符串键
948
- * // obj => { a: { 0: { b: 1 } } }
949
- * setPath(obj, 'a[0].b', 2) // 索引用方括号
950
- * // obj.a[0].b => 2
1988
+ * isAbsoluteUrl('https://example.com') // true
1989
+ * isAbsoluteUrl('/path/to/page') // false
1990
+ * isAbsoluteUrl('//example.com/path') // true (protocol-relative)
951
1991
  * ```
952
1992
  */
953
- declare function setPath<T extends Record<string, any>>(object: T, path: PathInput, value: unknown): T;
1993
+ declare function isAbsoluteUrl(url: string): boolean;
954
1994
  /**
955
- * 将片段数组序列化为路径字符串。
1995
+ * 检查 URL 是否为相对路径
956
1996
  *
957
- * 规则:
958
- * - 合法标识符段使用点拼接(a.b.c)
959
- * - 数字段转为索引([0])
960
- * - 其它需要转义的键使用方括号引号(['x.y']),并转义 \\ 与 '\''
961
- *
962
- * @category Object
963
- * @param segments 路径片段数组
964
- * @returns 路径字符串
1997
+ * @category URL
1998
+ * @param url 要检查的 URL
1999
+ * @returns 是否为相对路径
965
2000
  * @example
966
2001
  * ```ts
967
- * const p = joinPath(['a', 'x.y', 0, 'space key'])
968
- * // p: "a['x.y'][0]['space key']"
969
- * // 与解析往返:toPath(p) => ['a', 'x.y', 0, 'space key']
2002
+ * isRelativeUrl('/path/to/page') // true
2003
+ * isRelativeUrl('./page') // true
2004
+ * isRelativeUrl('../page') // true
2005
+ * isRelativeUrl('https://example.com') // false
970
2006
  * ```
971
2007
  */
972
- declare function joinPath(segments: (string | number)[]): string;
973
-
2008
+ declare function isRelativeUrl(url: string): boolean;
974
2009
  /**
975
- * 从对象中选择指定的键,返回新对象
2010
+ * 获取 URL 的域名部分
976
2011
  *
977
- * @category Object
978
- * @param obj 源对象
979
- * @param keys 要选择的键数组
980
- * @returns 只包含指定键的新对象
2012
+ * @category URL
2013
+ * @param url URL 字符串
2014
+ * @returns 域名,解析失败返回空字符串
981
2015
  * @example
982
2016
  * ```ts
983
- * const user = {
984
- * id: 1,
985
- * name: 'John',
986
- * email: 'john@example.com',
987
- * password: 'secret',
988
- * createdAt: '2023-01-01',
989
- * updatedAt: '2023-01-15'
990
- * }
991
- *
992
- * const publicInfo = pick(user, ['id', 'name', 'email'])
993
- * console.log(publicInfo) // { id: 1, name: 'John', email: 'john@example.com' }
994
- *
995
- * const basicInfo = pick(user, ['id', 'name'])
996
- * console.log(basicInfo) // { id: 1, name: 'John' }
2017
+ * getDomain('https://sub.example.com/path') // 'sub.example.com'
2018
+ * getDomain('https://example.com:8080') // 'example.com'
997
2019
  * ```
998
2020
  */
999
- declare function pick<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): PickByKey<T, K>;
1000
-
2021
+ declare function getDomain(url: string): string;
1001
2022
  /**
1002
- * 将对象按指定键分离为两个对象
2023
+ * 获取 URL 的根域名(顶级域名 + 二级域名)
1003
2024
  *
1004
- * @category Object
1005
- * @param obj 源对象
1006
- * @param keys 要分离的键数组
1007
- * @returns 包含picked和omitted两个对象的结果
2025
+ * @category URL
2026
+ * @param url URL 字符串
2027
+ * @returns 根域名,解析失败返回空字符串
1008
2028
  * @example
1009
2029
  * ```ts
1010
- * const user = {
1011
- * id: 1,
1012
- * name: 'John',
1013
- * email: 'john@example.com',
1014
- * password: 'secret',
1015
- * role: 'admin'
1016
- * }
1017
- *
1018
- * const { picked, omitted } = separate(user, ['id', 'name'])
1019
- * console.log(picked) // { id: 1, name: 'John' }
1020
- * console.log(omitted) // { email: 'john@example.com', password: 'secret', role: 'admin' }
1021
- *
1022
- * // 用于分离敏感信息
1023
- * const { picked: publicData, omitted: privateData } = separate(user, ['id', 'name', 'email'])
2030
+ * getRootDomain('https://sub.example.com') // 'example.com'
2031
+ * getRootDomain('https://a.b.example.co.uk') // 'example.co.uk'
1024
2032
  * ```
1025
2033
  */
1026
- declare function separate<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): {
1027
- picked: PickByKey<T, K>;
1028
- omitted: OmitByKey<T, K>;
1029
- };
2034
+ declare function getRootDomain(url: string): string;
1030
2035
  /**
1031
- * 将对象按多分组键集合进行分离(浅层),返回各分组与 others
1032
- *
1033
- * - 键冲突策略:先到先得。若同一键出现在多个分组中,则归入第一个匹配到的分组
1034
- * - 仅处理对象自有的浅层键,不解析深层路径
1035
- * - 分组中包含不存在于对象的键将被忽略
2036
+ * 获取 URL 的文件扩展名
1036
2037
  *
1037
- * @category Object
1038
- * @param obj 源对象
1039
- * @param groups 分组映射,如 { a: ['x', 'y'], b: ['z'] }
1040
- * @returns 一个对象,包含每个分组的子对象以及 others(其余未被分组捕获的键)
2038
+ * @category URL
2039
+ * @param url URL 字符串
2040
+ * @returns 文件扩展名(不含点),无扩展名返回空字符串
1041
2041
  * @example
1042
2042
  * ```ts
1043
- * const options = { id: 1, name: 'John', email: 'a@b.com', role: 'admin' }
1044
- * const { a, b, others } = separateMany(options, { a: ['id'], b: ['name'] as const })
1045
- * // a: { id: 1 }
1046
- * // b: { name: 'John' }
1047
- * // others: { email: 'a@b.com', role: 'admin' }
2043
+ * getUrlExtension('https://example.com/file.pdf') // 'pdf'
2044
+ * getUrlExtension('https://example.com/file.tar.gz') // 'gz'
2045
+ * getUrlExtension('https://example.com/path/') // ''
1048
2046
  * ```
1049
2047
  */
1050
- declare function separateMany<T extends AnyObject, M extends Record<string, readonly (keyof T)[]>>(obj: T, groups: M): {
1051
- [P in keyof M]: PickByKey<T, M[P][number]>;
1052
- } & {
1053
- others: OmitByKey<T, M[keyof M][number]>;
1054
- };
1055
-
2048
+ declare function getUrlExtension(url: string): string;
1056
2049
  /**
1057
- * 将字符串转换为Start Case格式(每个单词首字母大写,用空格分隔)。
2050
+ * 获取 URL 的文件名
1058
2051
  *
1059
- * @category String
1060
- * @param str 要转换的字符串
1061
- * @returns Start Case格式的字符串
2052
+ * @category URL
2053
+ * @param url URL 字符串
2054
+ * @param includeExtension 是否包含扩展名,默认 true
2055
+ * @returns 文件名
1062
2056
  * @example
1063
2057
  * ```ts
1064
- * startCase('firstName') // 'First Name'
1065
- * startCase('first_name') // 'First Name'
1066
- * startCase('first-name') // 'First Name'
1067
- * startCase('XMLHttpRequest') // 'XML Http Request'
2058
+ * getUrlFilename('https://example.com/path/file.pdf') // 'file.pdf'
2059
+ * getUrlFilename('https://example.com/path/file.pdf', false) // 'file'
2060
+ * getUrlFilename('https://example.com/path/') // ''
1068
2061
  * ```
1069
2062
  */
1070
- declare function startCase(str: string): string;
2063
+ declare function getUrlFilename(url: string, includeExtension?: boolean): string;
2064
+
1071
2065
  /**
1072
- * 将字符串转换为驼峰命名格式(第一个单词小写,后续单词首字母大写)。
2066
+ * 解析 URL 查询字符串为对象
1073
2067
  *
1074
- * @category String
1075
- * @param str 要转换的字符串
1076
- * @returns 驼峰命名格式的字符串
2068
+ * @category URL
2069
+ * @param search 查询字符串(可带或不带 ?)
2070
+ * @returns 解析后的查询参数对象
1077
2071
  * @example
1078
2072
  * ```ts
1079
- * camelCase('First Name') // 'firstName'
1080
- * camelCase('first_name') // 'firstName'
1081
- * camelCase('first-name') // 'firstName'
1082
- * camelCase('XMLHttpRequest') // 'xmlHttpRequest'
2073
+ * parseQuery('?name=John&age=30')
2074
+ * // { name: 'John', age: '30' }
2075
+ *
2076
+ * parseQuery('tags=a&tags=b&tags=c')
2077
+ * // { tags: ['a', 'b', 'c'] }
2078
+ *
2079
+ * parseQuery('encoded=%E4%B8%AD%E6%96%87')
2080
+ * // { encoded: '中文' }
1083
2081
  * ```
1084
2082
  */
1085
- declare function camelCase(str: string): string;
2083
+ declare function parseQuery(search: string): Record<string, string | string[]>;
1086
2084
  /**
1087
- * 将字符串转换为短横线命名格式(kebab-case)。
2085
+ * 将对象序列化为查询字符串
1088
2086
  *
1089
- * @category String
1090
- * @param str 要转换的字符串
1091
- * @returns 短横线命名格式的字符串
2087
+ * @category URL
2088
+ * @param params 查询参数对象
2089
+ * @param options 序列化选项
2090
+ * @param options.skipNull 跳过 null 和 undefined 值
2091
+ * @param options.skipEmpty 跳过空字符串
2092
+ * @param options.arrayFormat 数组格式: 'repeat' (默认), 'bracket', 'index', 'comma'
2093
+ * @returns 查询字符串(不含 ?)
1092
2094
  * @example
1093
2095
  * ```ts
1094
- * kebabCase('firstName') // 'first-name'
1095
- * kebabCase('First Name') // 'first-name'
1096
- * kebabCase('first_name') // 'first-name'
1097
- * kebabCase('XMLHttpRequest') // 'xml-http-request'
2096
+ * stringifyQuery({ name: 'John', age: 30 })
2097
+ * // 'name=John&age=30'
2098
+ *
2099
+ * stringifyQuery({ tags: ['a', 'b', 'c'] })
2100
+ * // 'tags=a&tags=b&tags=c'
2101
+ *
2102
+ * stringifyQuery({ name: '中文' })
2103
+ * // 'name=%E4%B8%AD%E6%96%87'
2104
+ *
2105
+ * stringifyQuery({ a: null, b: undefined, c: '' }, { skipNull: true, skipEmpty: true })
2106
+ * // ''
1098
2107
  * ```
1099
2108
  */
1100
- declare function kebabCase(str: string): string;
2109
+ declare function stringifyQuery(params: QueryParams, options?: {
2110
+ /** 跳过 null 和 undefined 值 */
2111
+ skipNull?: boolean;
2112
+ /** 跳过空字符串 */
2113
+ skipEmpty?: boolean;
2114
+ /** 数组格式: 'repeat' (默认), 'bracket', 'index', 'comma' */
2115
+ arrayFormat?: 'repeat' | 'bracket' | 'index' | 'comma';
2116
+ }): string;
1101
2117
  /**
1102
- * 将字符串转换为下划线命名格式(snake_case)。
2118
+ * 从 URL 获取指定查询参数的值
1103
2119
  *
1104
- * @category String
1105
- * @param str 要转换的字符串
1106
- * @returns 下划线命名格式的字符串
2120
+ * @category URL
2121
+ * @param url URL 字符串
2122
+ * @param key 参数名
2123
+ * @returns 参数值,不存在返回 null
1107
2124
  * @example
1108
2125
  * ```ts
1109
- * snakeCase('firstName') // 'first_name'
1110
- * snakeCase('First Name') // 'first_name'
1111
- * snakeCase('first-name') // 'first_name'
1112
- * snakeCase('XMLHttpRequest') // 'xml_http_request'
2126
+ * getQueryParam('https://example.com?name=John&age=30', 'name')
2127
+ * // 'John'
2128
+ *
2129
+ * getQueryParam('https://example.com?tags=a&tags=b', 'tags')
2130
+ * // 'a' (返回第一个值)
2131
+ *
2132
+ * getQueryParam('https://example.com', 'name')
2133
+ * // null
1113
2134
  * ```
1114
2135
  */
1115
- declare function snakeCase(str: string): string;
2136
+ declare function getQueryParam(url: string, key: string): string | null;
1116
2137
  /**
1117
- * 将字符串转换为帕斯卡命名格式(PascalCase,每个单词首字母大写)。
2138
+ * 从 URL 获取所有指定查询参数的值(用于多值参数)
1118
2139
  *
1119
- * @category String
1120
- * @param str 要转换的字符串
1121
- * @returns 帕斯卡命名格式的字符串
2140
+ * @category URL
2141
+ * @param url URL 字符串
2142
+ * @param key 参数名
2143
+ * @returns 参数值数组
1122
2144
  * @example
1123
2145
  * ```ts
1124
- * pascalCase('firstName') // 'FirstName'
1125
- * pascalCase('first_name') // 'FirstName'
1126
- * pascalCase('first-name') // 'FirstName'
1127
- * pascalCase('XMLHttpRequest') // 'XmlHttpRequest'
2146
+ * getQueryParams('https://example.com?tags=a&tags=b&tags=c', 'tags')
2147
+ * // ['a', 'b', 'c']
2148
+ *
2149
+ * getQueryParams('https://example.com?name=John', 'name')
2150
+ * // ['John']
2151
+ *
2152
+ * getQueryParams('https://example.com', 'name')
2153
+ * // []
1128
2154
  * ```
1129
2155
  */
1130
- declare function pascalCase(str: string): string;
2156
+ declare function getQueryParams(url: string, key: string): string[];
1131
2157
  /**
1132
- * 将字符串首字母大写,其余字母小写。
2158
+ * 设置 URL 的查询参数
1133
2159
  *
1134
- * @category String
1135
- * @param str 要转换的字符串
1136
- * @returns 首字母大写的字符串
2160
+ * @category URL
2161
+ * @param url URL 字符串
2162
+ * @param key 参数名
2163
+ * @param value 参数值
2164
+ * @returns 新的 URL 字符串
1137
2165
  * @example
1138
2166
  * ```ts
1139
- * capitalize('hello') // 'Hello'
1140
- * capitalize('HELLO') // 'Hello'
1141
- * capitalize('hello world') // 'Hello world'
2167
+ * setQueryParam('https://example.com', 'page', 1)
2168
+ * // 'https://example.com?page=1'
2169
+ *
2170
+ * setQueryParam('https://example.com?page=1', 'page', 2)
2171
+ * // 'https://example.com?page=2'
2172
+ *
2173
+ * setQueryParam('https://example.com?page=1', 'sort', 'name')
2174
+ * // 'https://example.com?page=1&sort=name'
1142
2175
  * ```
1143
2176
  */
1144
- declare function capitalize(str: string): string;
2177
+ declare function setQueryParam(url: string, key: string, value: QueryParamValue): string;
1145
2178
  /**
1146
- * 将字符串首字母大写,其余字母保持原样。
2179
+ * 批量设置 URL 的查询参数
1147
2180
  *
1148
- * @category String
1149
- * @param str 要转换的字符串
1150
- * @returns 首字母大写的字符串
2181
+ * @category URL
2182
+ * @param url URL 字符串
2183
+ * @param params 要设置的参数对象
2184
+ * @returns 新的 URL 字符串
1151
2185
  * @example
1152
2186
  * ```ts
1153
- * upperFirst('hello') // 'Hello'
1154
- * upperFirst('hELLO') // 'HELLO'
1155
- * upperFirst('hello world') // 'Hello world'
2187
+ * setQueryParams('https://example.com', { page: 1, limit: 10 })
2188
+ * // 'https://example.com?page=1&limit=10'
2189
+ *
2190
+ * setQueryParams('https://example.com?page=1', { page: 2, sort: 'name' })
2191
+ * // 'https://example.com?page=2&sort=name'
1156
2192
  * ```
1157
2193
  */
1158
- declare function upperFirst(str: string): string;
2194
+ declare function setQueryParams(url: string, params: QueryParams): string;
1159
2195
  /**
1160
- * 将字符串首字母小写,其余字母保持原样。
2196
+ * 追加查询参数(不覆盖已有同名参数)
1161
2197
  *
1162
- * @category String
1163
- * @param str 要转换的字符串
1164
- * @returns 首字母小写的字符串
2198
+ * @category URL
2199
+ * @param url URL 字符串
2200
+ * @param key 参数名
2201
+ * @param value 参数值
2202
+ * @returns 新的 URL 字符串
1165
2203
  * @example
1166
2204
  * ```ts
1167
- * lowerFirst('Hello') // 'hello'
1168
- * lowerFirst('HELLO') // 'hELLO'
1169
- * lowerFirst('Hello World') // 'hello World'
2205
+ * appendQueryParam('https://example.com?tag=a', 'tag', 'b')
2206
+ * // 'https://example.com?tag=a&tag=b'
1170
2207
  * ```
1171
2208
  */
1172
- declare function lowerFirst(str: string): string;
2209
+ declare function appendQueryParam(url: string, key: string, value: QueryParamValue): string;
1173
2210
  /**
1174
- * 将字符串转换为大写格式,单词之间用空格分隔。
2211
+ * 删除 URL 的指定查询参数
1175
2212
  *
1176
- * @category String
1177
- * @param str 要转换的字符串
1178
- * @returns 大写格式的字符串
2213
+ * @category URL
2214
+ * @param url URL 字符串
2215
+ * @param key 要删除的参数名
2216
+ * @returns 新的 URL 字符串
1179
2217
  * @example
1180
2218
  * ```ts
1181
- * upperCase('firstName') // 'FIRST NAME'
1182
- * upperCase('first_name') // 'FIRST NAME'
1183
- * upperCase('first-name') // 'FIRST NAME'
1184
- * upperCase('XMLHttpRequest') // 'XML HTTP REQUEST'
2219
+ * removeQueryParam('https://example.com?page=1&sort=name', 'page')
2220
+ * // 'https://example.com?sort=name'
2221
+ *
2222
+ * removeQueryParam('https://example.com?page=1', 'page')
2223
+ * // 'https://example.com'
1185
2224
  * ```
1186
2225
  */
1187
- declare function upperCase(str: string): string;
2226
+ declare function removeQueryParam(url: string, key: string): string;
1188
2227
  /**
1189
- * 将字符串转换为小写格式,单词之间用空格分隔。
2228
+ * 检查 URL 是否包含指定查询参数
1190
2229
  *
1191
- * @category String
1192
- * @param str 要转换的字符串
1193
- * @returns 小写格式的字符串
2230
+ * @category URL
2231
+ * @param url URL 字符串
2232
+ * @param key 参数名
2233
+ * @returns 是否包含该参数
1194
2234
  * @example
1195
2235
  * ```ts
1196
- * lowerCase('firstName') // 'first name'
1197
- * lowerCase('First_Name') // 'first name'
1198
- * lowerCase('FIRST-NAME') // 'first name'
1199
- * lowerCase('XMLHttpRequest') // 'xml http request'
2236
+ * hasQueryParam('https://example.com?page=1', 'page') // true
2237
+ * hasQueryParam('https://example.com?page=1', 'sort') // false
2238
+ * hasQueryParam('https://example.com?flag', 'flag') // true (无值参数)
1200
2239
  * ```
1201
2240
  */
1202
- declare function lowerCase(str: string): string;
1203
- /**
1204
- * 将驼峰命名转换为kebab-case
1205
- * @deprecated Use `kebabCase` instead
1206
- */
1207
- declare const camelToKebab: typeof kebabCase;
1208
- /**
1209
- * 将kebab-case转换为驼峰命名
1210
- * @deprecated Use `camelCase` instead
1211
- */
1212
- declare const kebabToCamel: typeof camelCase;
2241
+ declare function hasQueryParam(url: string, key: string): boolean;
1213
2242
 
1214
2243
  /**
1215
- * 将字符串分解为单词数组。支持camelCase、snake_case、kebab-case等各种命名风格。
2244
+ * 连接 URL 路径片段
1216
2245
  *
1217
- * @category String
1218
- * @param str 要分解的字符串
1219
- * @returns 单词数组
2246
+ * @category URL
2247
+ * @param parts URL 片段
2248
+ * @returns 连接后的 URL
1220
2249
  * @example
1221
2250
  * ```ts
1222
- * words('helloWorld') // ['hello', 'World']
1223
- * words('hello_world') // ['hello', 'world']
1224
- * words('hello-world') // ['hello', 'world']
1225
- * words('XMLHttpRequest') // ['XML', 'Http', 'Request']
2251
+ * joinUrl('https://example.com', 'api', 'users')
2252
+ * // 'https://example.com/api/users'
2253
+ *
2254
+ * joinUrl('https://example.com/', '/api/', '/users/')
2255
+ * // 'https://example.com/api/users/'
2256
+ *
2257
+ * joinUrl('/api', 'users', '123')
2258
+ * // '/api/users/123'
1226
2259
  * ```
1227
2260
  */
1228
- declare function words(str: string): string[];
1229
-
2261
+ declare function joinUrl(...parts: string[]): string;
1230
2262
  /**
1231
- * 树节点类型定义
2263
+ * 规范化 URL 路径(移除多余斜杠、处理 . 和 ..)
1232
2264
  *
1233
- * @template T 节点数据类型
1234
- */
1235
- type TreeNode<T = any> = T & {
1236
- children?: TreeNode<T>[];
1237
- [key: string]: any;
1238
- };
1239
- declare const TreeConfigSchema: z.ZodObject<{
1240
- id: z.ZodDefault<z.ZodString>;
1241
- pid: z.ZodDefault<z.ZodString>;
1242
- children: z.ZodDefault<z.ZodString>;
1243
- }, z.core.$strip>;
1244
- /**
1245
- * 树形配置输入类型
2265
+ * @category URL
2266
+ * @param url URL 字符串
2267
+ * @returns 规范化后的 URL
2268
+ * @example
2269
+ * ```ts
2270
+ * normalizeUrl('https://example.com//api///users/')
2271
+ * // 'https://example.com/api/users/'
2272
+ *
2273
+ * normalizeUrl('https://example.com/api/../users')
2274
+ * // 'https://example.com/users'
2275
+ *
2276
+ * normalizeUrl('/api/./users/../posts')
2277
+ * // '/api/posts'
2278
+ * ```
1246
2279
  */
1247
- type TreeConfigInput = z.input<typeof TreeConfigSchema>;
1248
- declare const _TreeStatsSchema: z.ZodObject<{
1249
- total: z.ZodNumber;
1250
- leaves: z.ZodNumber;
1251
- depth: z.ZodNumber;
1252
- branches: z.ZodNumber;
1253
- }, z.core.$strip>;
2280
+ declare function normalizeUrl(url: string): string;
1254
2281
  /**
1255
- * 树统计信息类型
2282
+ * 移除 URL 的尾部斜杠
2283
+ *
2284
+ * @category URL
2285
+ * @param url URL 字符串
2286
+ * @returns 移除尾部斜杠后的 URL
2287
+ * @example
2288
+ * ```ts
2289
+ * removeTrailingSlash('https://example.com/') // 'https://example.com'
2290
+ * removeTrailingSlash('https://example.com/path/') // 'https://example.com/path'
2291
+ * removeTrailingSlash('https://example.com') // 'https://example.com'
2292
+ * ```
1256
2293
  */
1257
- type TreeStats = z.infer<typeof _TreeStatsSchema>;
2294
+ declare function removeTrailingSlash(url: string): string;
1258
2295
  /**
1259
- * 树节点谓词函数类型
2296
+ * 确保 URL 以斜杠结尾
1260
2297
  *
1261
- * @template T 节点数据类型
1262
- * @param params 包含节点信息的对象参数
1263
- * @param params.node 当前节点
1264
- * @param params.depth 节点深度(从0开始)
1265
- * @param params.path 从根节点到当前节点的路径数组
1266
- * @param params.index 节点在同级节点中的索引
1267
- * @returns 是否满足条件
2298
+ * @category URL
2299
+ * @param url URL 字符串
2300
+ * @returns 带尾部斜杠的 URL
2301
+ * @example
2302
+ * ```ts
2303
+ * ensureTrailingSlash('https://example.com') // 'https://example.com/'
2304
+ * ensureTrailingSlash('https://example.com/path') // 'https://example.com/path/'
2305
+ * ensureTrailingSlash('https://example.com/') // 'https://example.com/'
2306
+ * ```
1268
2307
  */
1269
- type TreePredicate<T = any> = (params: {
1270
- node: TreeNode<T>;
1271
- depth: number;
1272
- path: readonly TreeNode<T>[];
1273
- index: number;
1274
- }) => boolean;
2308
+ declare function ensureTrailingSlash(url: string): string;
1275
2309
  /**
1276
- * 树节点转换函数类型
2310
+ * 移除 URL 的开头斜杠
1277
2311
  *
1278
- * @template T 源节点数据类型
1279
- * @template R 目标节点数据类型
1280
- * @param params 包含节点信息的对象参数
1281
- * @param params.node 当前节点
1282
- * @param params.depth 节点深度(从0开始)
1283
- * @param params.path 从根节点到当前节点的路径数组
1284
- * @param params.index 节点在同级节点中的索引
1285
- * @returns 转换后的节点数据
2312
+ * @category URL
2313
+ * @param url URL 或路径字符串
2314
+ * @returns 移除开头斜杠后的字符串
2315
+ * @example
2316
+ * ```ts
2317
+ * removeLeadingSlash('/path/to/page') // 'path/to/page'
2318
+ * removeLeadingSlash('///path') // 'path'
2319
+ * removeLeadingSlash('path') // 'path'
2320
+ * ```
1286
2321
  */
1287
- type TreeTransformer<T = any, R = any> = (params: {
1288
- node: TreeNode<T>;
1289
- depth: number;
1290
- path: readonly TreeNode<T>[];
1291
- index: number;
1292
- }) => R;
2322
+ declare function removeLeadingSlash(url: string): string;
1293
2323
  /**
1294
- * 树节点访问函数类型
2324
+ * 确保路径以斜杠开头
1295
2325
  *
1296
- * @template T 节点数据类型
1297
- * @param params 包含节点信息的对象参数
1298
- * @param params.node 当前节点
1299
- * @param params.depth 节点深度(从0开始)
1300
- * @param params.path 从根节点到当前节点的路径数组
1301
- * @param params.index 节点在同级节点中的索引
1302
- * @returns 返回false可以终止遍历或跳过子节点
2326
+ * @category URL
2327
+ * @param path 路径字符串
2328
+ * @returns 带开头斜杠的路径
2329
+ * @example
2330
+ * ```ts
2331
+ * ensureLeadingSlash('path/to/page') // '/path/to/page'
2332
+ * ensureLeadingSlash('/path') // '/path'
2333
+ * ```
1303
2334
  */
1304
- type TreeVisitor<T = any> = (params: {
1305
- node: TreeNode<T>;
1306
- depth: number;
1307
- path: readonly TreeNode<T>[];
1308
- index: number;
1309
- }) => void | boolean;
2335
+ declare function ensureLeadingSlash(path: string): string;
1310
2336
  /**
1311
- * 树形数据结构操作工具类
1312
- *
1313
- * 提供了一系列操作树形数据的静态方法,包括:
1314
- * - 查找:find, findAll, findById
1315
- * - 转换:fromList, toList, transform
1316
- * - 过滤:filter
1317
- * - 遍历:forEach
1318
- * - 统计:estimateSize, getStats
1319
- * - 修改:insertBefore, insertAfter, remove
1320
- * - 验证:validate
1321
- *
1322
- * 所有使用谓词函数或访问函数的方法都采用对象解构参数格式:
1323
- * `({ node, depth, path, index }) => boolean`
2337
+ * 构建完整 URL
1324
2338
  *
2339
+ * @category URL
2340
+ * @param base 基础 URL
2341
+ * @param path 路径部分
2342
+ * @param query 查询参数
2343
+ * @param hash 哈希部分
2344
+ * @returns 完整 URL
1325
2345
  * @example
1326
2346
  * ```ts
1327
- * const tree = [
1328
- * {
1329
- * id: '1',
1330
- * name: '根节点',
1331
- * children: [
1332
- * { id: '2', name: '子节点1', children: [] },
1333
- * { id: '3', name: '子节点2', children: [] }
1334
- * ]
1335
- * }
1336
- * ]
1337
- *
1338
- * // 查找节点
1339
- * const node = Tree.find(tree, ({ node }) => node.name === '子节点1')
2347
+ * buildUrl('https://example.com', '/api/users', { page: 1, limit: 10 })
2348
+ * // 'https://example.com/api/users?page=1&limit=10'
1340
2349
  *
1341
- * // 遍历所有节点
1342
- * Tree.forEach(tree, ({ node, depth }) => {
1343
- * console.log(`${' '.repeat(depth)}${node.name}`)
1344
- * })
2350
+ * buildUrl('https://example.com', '/page', null, 'section')
2351
+ * // 'https://example.com/page#section'
1345
2352
  *
1346
- * // 转换节点结构
1347
- * const transformed = Tree.transform(tree, ({ node, depth }) => ({
1348
- * key: node.id,
1349
- * title: node.name,
1350
- * level: depth
1351
- * }))
2353
+ * buildUrl('https://example.com', '/api', { ids: [1, 2, 3] })
2354
+ * // 'https://example.com/api?ids=1&ids=2&ids=3'
1352
2355
  * ```
1353
2356
  */
1354
- declare class Tree {
1355
- private static dfsGenerator;
1356
- private static bfsGenerator;
1357
- private static selectStrategy;
1358
- /**
1359
- * 从扁平数组创建树形结构
1360
- *
1361
- * @category Data Structures
1362
- * @param list 扁平数组数据
1363
- * @param config 树形配置选项
1364
- * @returns 树形结构数组
1365
- * @example
1366
- * ```ts
1367
- * const flatData = [
1368
- * { id: '1', name: '部门1', parentId: null },
1369
- * { id: '2', name: '部门1-1', parentId: '1' },
1370
- * { id: '3', name: '部门1-2', parentId: '1' },
1371
- * { id: '4', name: '部门1-1-1', parentId: '2' }
1372
- * ]
1373
- *
1374
- * const tree = Tree.fromList(flatData, {
1375
- * id: 'id',
1376
- * pid: 'parentId',
1377
- * children: 'children'
1378
- * })
1379
- *
1380
- * console.log(tree) // 转换为树形结构
1381
- * ```
1382
- */
1383
- static fromList<T = any>(list: T[], config?: TreeConfigInput): TreeNode<T>[];
1384
- /**
1385
- * 将树形结构转换为扁平数组
1386
- *
1387
- * @category Data Structures
1388
- * @param tree 树形结构(单个节点或节点数组)
1389
- * @param config 树形配置选项
1390
- * @returns 扁平数组
1391
- * @example
1392
- * ```ts
1393
- * const tree = [
1394
- * {
1395
- * id: '1',
1396
- * name: '根节点',
1397
- * children: [
1398
- * { id: '2', name: '子节点1', children: [] },
1399
- * { id: '3', name: '子节点2', children: [] }
1400
- * ]
1401
- * }
1402
- * ]
1403
- *
1404
- * const flatList = Tree.toList(tree)
1405
- * console.log(flatList) // [{ id: '1', name: '根节点' }, { id: '2', name: '子节点1' }, ...]
1406
- * ```
1407
- */
1408
- static toList<T = any>(tree: TreeNode<T> | TreeNode<T>[], config?: TreeConfigInput): T[];
1409
- /**
1410
- * 估算树形结构的节点数量
1411
- *
1412
- * @category Data Structures
1413
- * @param tree 树形结构(单个节点或节点数组)
1414
- * @param config 树形配置选项
1415
- * @returns 节点总数量
1416
- * @example
1417
- * ```ts
1418
- * const tree = [
1419
- * {
1420
- * id: '1',
1421
- * name: '根节点',
1422
- * children: [
1423
- * { id: '2', name: '子节点1', children: [] },
1424
- * { id: '3', name: '子节点2', children: [] }
1425
- * ]
1426
- * }
1427
- * ]
1428
- *
1429
- * const size = Tree.estimateSize(tree)
1430
- * console.log(size) // 3
1431
- * ```
1432
- */
1433
- static estimateSize<T = any>(tree: TreeNode<T> | TreeNode<T>[], config?: TreeConfigInput): number;
1434
- /**
1435
- * 查找树中第一个满足条件的节点
1436
- *
1437
- * @category Data Structures
1438
- * @param tree 树形结构(单个节点或节点数组)
1439
- * @param predicate 查找条件函数
1440
- * @param config 树形配置选项
1441
- * @returns 匹配的节点;未找到时返回undefined
1442
- * @example
1443
- * ```ts
1444
- * const tree = [
1445
- * {
1446
- * id: '1',
1447
- * name: '部门1',
1448
- * children: [
1449
- * { id: '2', name: '部门1-1', children: [] }
1450
- * ]
1451
- * }
1452
- * ]
1453
- *
1454
- * const result = Tree.find(tree, ({ node }) => node.name === '部门1-1')
1455
- * console.log(result?.id) // '2'
1456
- * ```
1457
- */
1458
- static find<T = any>(tree: TreeNode<T> | TreeNode<T>[], predicate: TreePredicate<T>, config?: TreeConfigInput): TreeNode<T> | undefined;
1459
- /**
1460
- * 查找树中所有满足条件的节点
1461
- *
1462
- * @category Data Structures
1463
- * @param tree 树形结构(单个节点或节点数组)
1464
- * @param predicate 查找条件函数
1465
- * @param config 树形配置选项
1466
- * @returns 所有匹配的节点数组
1467
- * @example
1468
- * ```ts
1469
- * const tree = [
1470
- * {
1471
- * id: '1',
1472
- * type: 'folder',
1473
- * name: '根目录',
1474
- * children: [
1475
- * { id: '2', type: 'file', name: '文件1', children: [] },
1476
- * { id: '3', type: 'file', name: '文件2', children: [] }
1477
- * ]
1478
- * }
1479
- * ]
1480
- *
1481
- * const files = Tree.findAll(tree, ({ node }) => node.type === 'file')
1482
- * console.log(files.length) // 2
1483
- * ```
1484
- */
1485
- static findAll<T = any>(tree: TreeNode<T> | TreeNode<T>[], predicate: TreePredicate<T>, config?: TreeConfigInput): TreeNode<T>[];
1486
- /**
1487
- * 根据ID查找树中的节点
1488
- *
1489
- * @category Data Structures
1490
- * @param tree 树形结构(单个节点或节点数组)
1491
- * @param id 要查找的节点ID
1492
- * @param config 树形配置选项
1493
- * @returns 匹配的节点;未找到时返回undefined
1494
- * @example
1495
- * ```ts
1496
- * const tree = [
1497
- * {
1498
- * id: '1',
1499
- * name: '根节点',
1500
- * children: [
1501
- * { id: '2', name: '子节点', children: [] }
1502
- * ]
1503
- * }
1504
- * ]
1505
- *
1506
- * const result = Tree.findById(tree, '2')
1507
- * console.log(result?.name) // '子节点'
1508
- * ```
1509
- */
1510
- static findById<T = any>(tree: TreeNode<T> | TreeNode<T>[], id: string, config?: TreeConfigInput): TreeNode<T> | undefined;
1511
- /**
1512
- * 获取树形结构的统计信息
1513
- *
1514
- * @category Data Structures
1515
- * @param tree 树形结构(单个节点或节点数组)
1516
- * @param config 树形配置选项
1517
- * @returns 树的统计信息,包含总节点数、叶子节点数、最大深度和分支节点数
1518
- * @example
1519
- * ```ts
1520
- * const tree = [
1521
- * {
1522
- * id: '1',
1523
- * name: '根节点',
1524
- * children: [
1525
- * { id: '2', name: '子节点1', children: [] },
1526
- * { id: '3', name: '子节点2', children: [
1527
- * { id: '4', name: '孙节点', children: [] }
1528
- * ] }
1529
- * ]
1530
- * }
1531
- * ]
1532
- *
1533
- * const stats = Tree.getStats(tree)
1534
- * console.log(stats) // { total: 4, leaves: 2, depth: 3, branches: 2 }
1535
- * ```
1536
- */
1537
- static getStats<T = any>(tree: TreeNode<T> | TreeNode<T>[], config?: TreeConfigInput): TreeStats;
1538
- /**
1539
- * 过滤树形结构,保留满足条件的节点及其祖先和后代
1540
- *
1541
- * @category Data Structures
1542
- * @param tree 树形结构(单个节点或节点数组)
1543
- * @param predicate 过滤条件函数,接收对象参数 {node, depth, path, index}
1544
- * @param config 树形配置选项
1545
- * @returns 过滤后的树形结构数组
1546
- * @example
1547
- * ```ts
1548
- * const tree = [
1549
- * {
1550
- * id: '1',
1551
- * type: 'folder',
1552
- * name: '根目录',
1553
- * children: [
1554
- * { id: '2', type: 'file', name: '文档.txt', children: [] },
1555
- * { id: '3', type: 'folder', name: '子目录', children: [
1556
- * { id: '4', type: 'file', name: '图片.jpg', children: [] }
1557
- * ] }
1558
- * ]
1559
- * }
1560
- * ]
1561
- *
1562
- * const filtered = Tree.filter(tree, ({ node }) => node.type === 'file')
1563
- * // 返回包含所有文件节点及其父级路径的树结构
1564
- * ```
1565
- */
1566
- static filter<T = any>(tree: TreeNode<T> | TreeNode<T>[], predicate: TreePredicate<T>, config?: TreeConfigInput): TreeNode<T>[];
1567
- /**
1568
- * 转换树形结构,将每个节点转换为新的结构
1569
- *
1570
- * @category Data Structures
1571
- * @param tree 树形结构(单个节点或节点数组)
1572
- * @param transformer 节点转换函数,接收对象参数 {node, depth, path, index}
1573
- * @param config 树形配置选项
1574
- * @returns 转换后的树形结构数组
1575
- * @example
1576
- * ```ts
1577
- * const tree = [
1578
- * {
1579
- * id: '1',
1580
- * name: '部门1',
1581
- * children: [
1582
- * { id: '2', name: '部门1-1', children: [] }
1583
- * ]
1584
- * }
1585
- * ]
1586
- *
1587
- * const transformed = Tree.transform(tree, ({ node, depth }) => ({
1588
- * key: node.id,
1589
- * title: node.name,
1590
- * level: depth
1591
- * }))
1592
- * // 转换为新的数据结构
1593
- * ```
1594
- */
1595
- static transform<T = any, R = any>(tree: TreeNode<T> | TreeNode<T>[], transformer: TreeTransformer<T, R>, config?: TreeConfigInput): TreeNode<R>[];
1596
- /**
1597
- * 遍历树形结构的每个节点
1598
- *
1599
- * @category Data Structures
1600
- * @param tree 树形结构(单个节点或节点数组)
1601
- * @param visitor 访问者函数,接收对象参数 {node, depth, path, index},返回false可以跳过子节点的遍历
1602
- * @param config 树形配置选项
1603
- * @example
1604
- * ```ts
1605
- * const tree = [
1606
- * {
1607
- * id: '1',
1608
- * name: '根节点',
1609
- * children: [
1610
- * { id: '2', name: '子节点', children: [] }
1611
- * ]
1612
- * }
1613
- * ]
1614
- *
1615
- * Tree.forEach(tree, ({ node, depth }) => {
1616
- * console.log(`${' '.repeat(depth * 2)}${node.name}`)
1617
- * // 输出缩进的树结构
1618
- * })
1619
- * ```
1620
- */
1621
- static forEach<T = any>(tree: TreeNode<T> | TreeNode<T>[], visitor: TreeVisitor<T>, config?: TreeConfigInput): void;
1622
- /**
1623
- * 在指定节点前插入新节点
1624
- *
1625
- * @category Data Structures
1626
- * @param tree 树形结构数组
1627
- * @param targetId 目标节点的ID
1628
- * @param newNode 要插入的新节点数据
1629
- * @param config 树形配置选项
1630
- * @returns 是否成功插入
1631
- * @example
1632
- * ```ts
1633
- * const tree = [
1634
- * {
1635
- * id: '1',
1636
- * name: '节点1',
1637
- * children: [
1638
- * { id: '2', name: '节点2', children: [] }
1639
- * ]
1640
- * }
1641
- * ]
1642
- *
1643
- * const success = Tree.insertBefore(tree, '2', { id: '1.5', name: '新节点' })
1644
- * console.log(success) // true
1645
- * ```
1646
- */
1647
- static insertBefore<T = any>(tree: TreeNode<T>[], targetId: string, newNode: T, config?: TreeConfigInput): boolean;
1648
- /**
1649
- * 在指定节点后插入新节点
1650
- *
1651
- * @category Data Structures
1652
- * @param tree 树形结构数组
1653
- * @param targetId 目标节点的ID
1654
- * @param newNode 要插入的新节点数据
1655
- * @param config 树形配置选项
1656
- * @returns 是否成功插入
1657
- * @example
1658
- * ```ts
1659
- * const tree = [
1660
- * {
1661
- * id: '1',
1662
- * name: '节点1',
1663
- * children: [
1664
- * { id: '2', name: '节点2', children: [] }
1665
- * ]
1666
- * }
1667
- * ]
1668
- *
1669
- * const success = Tree.insertAfter(tree, '2', { id: '3', name: '新节点' })
1670
- * console.log(success) // true
1671
- * ```
1672
- */
1673
- static insertAfter<T = any>(tree: TreeNode<T>[], targetId: string, newNode: T, config?: TreeConfigInput): boolean;
1674
- /**
1675
- * 从树中删除指定节点
1676
- *
1677
- * @category Data Structures
1678
- * @param tree 树形结构数组
1679
- * @param targetId 要删除的节点ID
1680
- * @param config 树形配置选项
1681
- * @returns 被删除的节点,未找到时返回undefined
1682
- * @example
1683
- * ```ts
1684
- * const tree = [
1685
- * {
1686
- * id: '1',
1687
- * name: '根节点',
1688
- * children: [
1689
- * { id: '2', name: '子节点', children: [] }
1690
- * ]
1691
- * }
1692
- * ]
1693
- *
1694
- * const removed = Tree.remove(tree, '2')
1695
- * console.log(removed?.name) // '子节点'
1696
- * ```
1697
- */
1698
- static remove<T = any>(tree: TreeNode<T>[], targetId: string, config?: TreeConfigInput): TreeNode<T> | undefined;
1699
- /**
1700
- * 验证树形结构的有效性
1701
- *
1702
- * @category Data Structures
1703
- * @param tree 树形结构(单个节点或节点数组)
1704
- * @param config 树形配置选项
1705
- * @returns 验证结果,包含是否有效和错误信息数组
1706
- * @example
1707
- * ```ts
1708
- * const tree = [
1709
- * {
1710
- * id: '1',
1711
- * name: '根节点',
1712
- * children: [
1713
- * { id: '2', name: '子节点', children: [] }
1714
- * ]
1715
- * }
1716
- * ]
1717
- *
1718
- * const result = Tree.validate(tree)
1719
- * console.log(result.isValid) // true
1720
- * console.log(result.errors) // []
1721
- * ```
1722
- */
1723
- static validate<T = any>(tree: TreeNode<T> | TreeNode<T>[], config?: TreeConfigInput): {
1724
- isValid: boolean;
1725
- errors: string[];
1726
- };
1727
- }
1728
-
2357
+ declare function buildUrl(base: string, path?: string, query?: QueryParams | null, hash?: string): string;
1729
2358
  /**
1730
- * 生成字符串的简单哈希值
2359
+ * 解码 URL 组件(安全版本,失败返回原字符串)
1731
2360
  *
1732
- * @category Utilities
1733
- * @param str 待哈希的字符串
1734
- * @returns 32位哈希值转换为36进制字符串
2361
+ * @category URL
2362
+ * @param str 要解码的字符串
2363
+ * @returns 解码后的字符串
1735
2364
  * @example
1736
2365
  * ```ts
1737
- * const hash1 = simpleHash('hello world')
1738
- * console.log(hash1) // 'nf5xd4'
2366
+ * safeDecodeURIComponent('%E4%B8%AD%E6%96%87') // '中文'
2367
+ * safeDecodeURIComponent('hello%20world') // 'hello world'
2368
+ * safeDecodeURIComponent('%invalid%') // '%invalid%'
2369
+ * ```
2370
+ */
2371
+ declare function safeDecodeURIComponent(str: string): string;
2372
+ /**
2373
+ * 编码 URL 组件(安全版本)
1739
2374
  *
1740
- * const hash2 = simpleHash('hello world')
1741
- * console.log(hash1 === hash2) // true,相同字符串产生相同哈希
2375
+ * @category URL
2376
+ * @param str 要编码的字符串
2377
+ * @returns 编码后的字符串
2378
+ * @example
2379
+ * ```ts
2380
+ * safeEncodeURIComponent('中文') // '%E4%B8%AD%E6%96%87'
2381
+ * safeEncodeURIComponent('hello world') // 'hello%20world'
2382
+ * ```
2383
+ */
2384
+ declare function safeEncodeURIComponent(str: string): string;
2385
+ /**
2386
+ * 检查两个 URL 是否同源
1742
2387
  *
1743
- * const hash3 = simpleHash('hello world!')
1744
- * console.log(hash1 === hash3) // false,不同字符串产生不同哈希
2388
+ * @category URL
2389
+ * @param url1 第一个 URL
2390
+ * @param url2 第二个 URL
2391
+ * @returns 是否同源
2392
+ * @example
2393
+ * ```ts
2394
+ * isSameOrigin('https://example.com/a', 'https://example.com/b') // true
2395
+ * isSameOrigin('https://example.com', 'https://sub.example.com') // false
2396
+ * isSameOrigin('https://example.com', 'http://example.com') // false
1745
2397
  * ```
1746
2398
  */
1747
- declare function simpleHash(str: string): string;
1748
-
2399
+ declare function isSameOrigin(url1: string, url2: string): boolean;
1749
2400
  /**
1750
- * 生成随机UUID字符串
2401
+ * 将相对 URL 转换为绝对 URL
1751
2402
  *
1752
- * @category Utilities
1753
- * @returns 符合UUID v4格式的随机字符串
2403
+ * @category URL
2404
+ * @param relativeUrl 相对 URL
2405
+ * @param baseUrl 基础 URL
2406
+ * @returns 绝对 URL,转换失败返回原字符串
1754
2407
  * @example
1755
2408
  * ```ts
1756
- * const id1 = getRandomUUID()
1757
- * console.log(id1) // 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
2409
+ * toAbsoluteUrl('/path', 'https://example.com')
2410
+ * // 'https://example.com/path'
1758
2411
  *
1759
- * const id2 = getRandomUUID()
1760
- * console.log(id2) // 'f47ac10b-58cc-4372-a567-0e02b2c3d480'
2412
+ * toAbsoluteUrl('../other', 'https://example.com/api/users')
2413
+ * // 'https://example.com/api/other'
1761
2414
  *
1762
- * // 用于生成唯一标识符
1763
- * const componentId = `component-${getRandomUUID()}`
2415
+ * toAbsoluteUrl('https://other.com', 'https://example.com')
2416
+ * // 'https://other.com' (已是绝对URL,不变)
1764
2417
  * ```
1765
2418
  */
1766
- declare function getRandomUUID(): string;
1767
-
2419
+ declare function toAbsoluteUrl(relativeUrl: string, baseUrl: string): string;
1768
2420
  /**
1769
- * 检查值是否为对象类型
2421
+ * 获取两个 URL 之间的相对路径
1770
2422
  *
1771
- * @category Validator
1772
- * @param value 待检查的值
1773
- * @returns 是否为对象类型
2423
+ * @category URL
2424
+ * @param from 起始 URL
2425
+ * @param to 目标 URL
2426
+ * @returns 相对路径
1774
2427
  * @example
1775
2428
  * ```ts
1776
- * console.log(isObject({})) // true
1777
- * console.log(isObject({ name: 'John' })) // true
1778
- * console.log(isObject([])) // false
1779
- * console.log(isObject(null)) // false
1780
- * console.log(isObject('string')) // false
2429
+ * getRelativePath('https://example.com/a/b', 'https://example.com/a/c')
2430
+ * // '../c'
2431
+ *
2432
+ * getRelativePath('https://example.com/a', 'https://example.com/a/b/c')
2433
+ * // 'b/c'
1781
2434
  * ```
1782
2435
  */
1783
- declare function isObject(value: any): value is AnyObject;
2436
+ declare function getRelativePath(from: string, to: string): string;
2437
+
1784
2438
  /**
1785
2439
  * 检查值是否为数组类型
1786
2440
  *
1787
- * @category Validator
2441
+ * @category Validators
1788
2442
  * @param value 待检查的值
1789
2443
  * @returns 是否为数组类型
1790
2444
  * @example
@@ -1796,40 +2450,31 @@ declare function isObject(value: any): value is AnyObject;
1796
2450
  * ```
1797
2451
  */
1798
2452
  declare function isArray(value: any): value is any[];
2453
+
1799
2454
  /**
1800
- * 检查值是否为字符串类型
1801
- *
1802
- * @category Validator
1803
- * @param value 待检查的值
1804
- * @returns 是否为字符串类型
1805
- * @example
1806
- * ```ts
1807
- * console.log(isString('hello')) // true
1808
- * console.log(isString('')) // true
1809
- * console.log(isString(123)) // false
1810
- * console.log(isString(null)) // false
1811
- * ```
1812
- */
1813
- declare function isString(value: any): value is string;
1814
- /**
1815
- * 检查值是否为有效数字类型
2455
+ * 检查值是否为空(null、undefined、空字符串、空数组、空对象)
1816
2456
  *
1817
- * @category Validator
2457
+ * @category Validators
1818
2458
  * @param value 待检查的值
1819
- * @returns 是否为有效数字类型
2459
+ * @returns 是否为空值
1820
2460
  * @example
1821
2461
  * ```ts
1822
- * console.log(isNumber(123)) // true
1823
- * console.log(isNumber(0)) // true
1824
- * console.log(isNumber(NaN)) // false
1825
- * console.log(isNumber('123')) // false
2462
+ * console.log(isEmpty(null)) // true
2463
+ * console.log(isEmpty(undefined)) // true
2464
+ * console.log(isEmpty('')) // true
2465
+ * console.log(isEmpty([])) // true
2466
+ * console.log(isEmpty({})) // true
2467
+ * console.log(isEmpty([1, 2])) // false
2468
+ * console.log(isEmpty({ name: 'John' })) // false
2469
+ * console.log(isEmpty('hello')) // false
1826
2470
  * ```
1827
2471
  */
1828
- declare function isNumber(value: any): value is number;
2472
+ declare function isEmpty(value: any): boolean;
2473
+
1829
2474
  /**
1830
2475
  * 检查值是否为函数类型
1831
2476
  *
1832
- * @category Validator
2477
+ * @category Validators
1833
2478
  * @param value 待检查的值
1834
2479
  * @returns 是否为函数类型
1835
2480
  * @example
@@ -1841,29 +2486,44 @@ declare function isNumber(value: any): value is number;
1841
2486
  * ```
1842
2487
  */
1843
2488
  declare function isFunction(value: any): value is (...args: any[]) => any;
2489
+
1844
2490
  /**
1845
- * 检查值是否为空(null、undefined、空字符串、空数组、空对象)
2491
+ * 检查值是否为有效数字类型
1846
2492
  *
1847
- * @category Validator
2493
+ * @category Validators
1848
2494
  * @param value 待检查的值
1849
- * @returns 是否为空值
2495
+ * @returns 是否为有效数字类型
1850
2496
  * @example
1851
2497
  * ```ts
1852
- * console.log(isEmpty(null)) // true
1853
- * console.log(isEmpty(undefined)) // true
1854
- * console.log(isEmpty('')) // true
1855
- * console.log(isEmpty([])) // true
1856
- * console.log(isEmpty({})) // true
1857
- * console.log(isEmpty([1, 2])) // false
1858
- * console.log(isEmpty({ name: 'John' })) // false
1859
- * console.log(isEmpty('hello')) // false
2498
+ * console.log(isNumber(123)) // true
2499
+ * console.log(isNumber(0)) // true
2500
+ * console.log(isNumber(NaN)) // false
2501
+ * console.log(isNumber('123')) // false
1860
2502
  * ```
1861
2503
  */
1862
- declare function isEmpty(value: any): boolean;
2504
+ declare function isNumber(value: any): value is number;
2505
+
2506
+ /**
2507
+ * 检查值是否为对象类型
2508
+ *
2509
+ * @category Validators
2510
+ * @param value 待检查的值
2511
+ * @returns 是否为对象类型
2512
+ * @example
2513
+ * ```ts
2514
+ * console.log(isObject({})) // true
2515
+ * console.log(isObject({ name: 'John' })) // true
2516
+ * console.log(isObject([])) // false
2517
+ * console.log(isObject(null)) // false
2518
+ * console.log(isObject('string')) // false
2519
+ * ```
2520
+ */
2521
+ declare function isObject(value: any): value is AnyObject;
2522
+
1863
2523
  /**
1864
2524
  * 判断值是否为纯对象(不包括数组、函数、日期等)
1865
2525
  *
1866
- * @category Object
2526
+ * @category Validators
1867
2527
  * @param value 要检查的值
1868
2528
  * @returns 是否为纯对象
1869
2529
  * @example
@@ -1876,5 +2536,44 @@ declare function isEmpty(value: any): boolean;
1876
2536
  */
1877
2537
  declare function isPlainObject(value: unknown): value is Record<string, any>;
1878
2538
 
1879
- export { StorageTypeSchema, Tree, camelCase, camelToKebab, capitalize, chunk, convertSvgToPng, convertToKebabCase, createStorageConfigSchema, debounce, deepClone, extractFilename, flatten, formatFileSize, getPath, getRandomUUID, isArray, isEmpty, isFunction, isNumber, isObject, isPlainObject, isString, isValidContainer, joinPath, kebabCase, kebabToCamel, lowerCase, lowerFirst, omit, omitUndefined, pascalCase, pick, replaceCurrentColor, separate, separateMany, setPath, simpleHash, sleep, sleepWithCancel, snakeCase, startCase, throttle, toPath, triggerDownload, unique, upperCase, upperFirst, useAppStorage, useCopyCode, words };
1880
- export type { AnyObject, ApiAwaitable, ApiAwaitedReturn, ApiUnwrapPromise, AppStorageReturn, ArrayFieldKeys, ComponentAttrs, ComponentEmit, ComponentExposed, ComponentProps, ComponentSlots, ComponentType, DeepPartial, FirstParam, FirstParameter, GetFieldValue, GetObjectField, IsComponent, IsPlainObject, Merge, MutableByKeys, NestedKeys, NonObjectFieldKeys, ObjectFieldKeys, OmitByKey, PartialByKeys, PickByKey, ReactiveValue, ReadonlyByKeys, RenameKeys, RequiredByKeys, StorageConfig, StorageConfigInput, StorageType, StringOrVNode, StripNullable, Suggest, UnionToIntersection, UnknownObject };
2539
+ /**
2540
+ * 检查值是否为字符串类型
2541
+ *
2542
+ * @category Validators
2543
+ * @param value 待检查的值
2544
+ * @returns 是否为字符串类型
2545
+ * @example
2546
+ * ```ts
2547
+ * console.log(isString('hello')) // true
2548
+ * console.log(isString('')) // true
2549
+ * console.log(isString(123)) // false
2550
+ * console.log(isString(null)) // false
2551
+ * ```
2552
+ */
2553
+ declare function isString(value: any): value is string;
2554
+
2555
+ /**
2556
+ * 检查值是否为有效的容器类型(对象或数组)
2557
+ *
2558
+ * - isObject: 仅检查纯对象,排除数组
2559
+ * - isValidContainer: 检查所有可作为容器的类型(对象 + 数组)
2560
+ *
2561
+ * 支持 Vue 3 的 Proxy 对象和 Proxy 数组。
2562
+ *
2563
+ * @category Validators
2564
+ * @param value - 待检查的值
2565
+ * @returns 是否为有效容器(对象或数组)
2566
+ * @example
2567
+ * ```ts
2568
+ * isValidContainer({}) // true
2569
+ * isValidContainer([]) // true
2570
+ * isValidContainer(new Proxy({}, {})) // true
2571
+ * isValidContainer(null) // false
2572
+ * isValidContainer('string') // false
2573
+ * isValidContainer(123) // false
2574
+ * ```
2575
+ */
2576
+ declare function isValidContainer(value: any): boolean;
2577
+
2578
+ export { Tree, appendQueryParam, buildUrl, camelCase, capitalize, chunk, convertSvgToPng, convertToKebabCase, createDeepMerge, debounce, deepClone, deepMerge, ensureLeadingSlash, ensureTrailingSlash, extractFilename, flatten, formatFileSize, getDomain, getPath, getQueryParam, getQueryParams, getRandomUUID, getRelativePath, getRootDomain, getUrlExtension, getUrlFilename, hasQueryParam, isAbsoluteUrl, isArray, isEmpty, isFunction, isNumber, isObject, isPlainObject, isRelativeUrl, isSameOrigin, isString, isValidContainer, isValidUrl, joinPath, joinUrl, kebabCase, lowerCase, lowerFirst, normalizeUrl, omit, omitUndefined, parseQuery, parseUrl, pascalCase, pick, removeLeadingSlash, removeQueryParam, removeTrailingSlash, replaceCurrentColor, safeDecodeURIComponent, safeEncodeURIComponent, separate, separateMany, setPath, setQueryParam, setQueryParams, simpleHash, sleep, sleepWithCancel, snakeCase, startCase, stringifyQuery, throttle, toAbsoluteUrl, toPath, triggerDownload, unique, upperCase, upperFirst, useAppStorage, useCopyCode, words };
2579
+ export type { AnyObject, ApiAwaitable, ApiAwaitedReturn, ApiUnwrapPromise, AppStorageReturn, ArrayFieldKeys, ArrayMergeStrategy, ComponentAttrs, ComponentEmit, ComponentExposed, ComponentProps, ComponentSlots, ComponentType, CustomMerger, DeepMerge, DeepMergeOptions, DeepPartial, FirstParam, FirstParameter, GetFieldValue, GetObjectField, IsComponent, IsPlainObject, Merge, MutableByKeys, NestedKeys, NonObjectFieldKeys, NullHandlingStrategy, ObjectFieldKeys, OmitByKey, ParsedUrl, PartialByKeys, PathInput, PathSegment, PathSegments, PickByKey, QueryParamValue, QueryParams, ReactiveValue, ReadonlyByKeys, RenameKeys, RequiredByKeys, StorageConfig, StorageConfigInput, StorageType, StringOrVNode, StripNullable, Suggest, TreeConfig, TreeConfigInput, TreeNode, TreePredicate, TreeStats, TreeTransformer, TreeVisitor, UnionToIntersection, UnknownObject };