@movk/core 0.0.4 → 0.0.5

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.mts CHANGED
@@ -1,37 +1,180 @@
1
1
  import { VNode, Ref } from 'vue';
2
2
  import { z } from 'zod/v4';
3
3
 
4
+ /**
5
+ * 统一同步/异步返回类型
6
+ * @typeParam T - 类型
7
+ * @example
8
+ * // type T = string | Promise<string>
9
+ * // type R = ApiAwaitable<T>
10
+ * // 结果:R 为 string | Promise<string>
11
+ */
12
+ type ApiAwaitable<T> = T | Promise<T>;
13
+ /**
14
+ * 提取Promise类型
15
+ * @typeParam T - 类型
16
+ * @example
17
+ * // type T = Promise<string>
18
+ * // type R = ApiUnwrapPromise<T>
19
+ * // 结果:R 为 string
20
+ */
21
+ type ApiUnwrapPromise<T> = T extends Promise<infer U> ? U : T;
22
+ /**
23
+ * 提取函数返回类型
24
+ * @typeParam TFn - 函数类型
25
+ * @example
26
+ * // type Fn = (x: number, y: string) => Promise<string>
27
+ * // type R = ApiAwaitedReturn<Fn>
28
+ * // 结果:R 为 string
29
+ */
30
+ type ApiAwaitedReturn<TFn> = TFn extends (...args: any[]) => ApiAwaitable<infer R> ? R : never;
31
+
32
+ type UnknownObject = Record<string, unknown>;
4
33
  type AnyObject = Record<string, any>;
34
+ /**
35
+ * 依据键名从对象类型 `T` 中剔除键 `K`。
36
+ * @typeParam T - 源对象类型
37
+ * @typeParam K - 要剔除的键(必须来自 `keyof T`)
38
+ * @example
39
+ * // type User = { id: string; name: string; age: number }
40
+ * // type R = OmitByKey<User, 'age'>
41
+ * // 结果:R 为 { id: string; name: string }
42
+ */
5
43
  type OmitByKey<T, K extends keyof T> = {
6
44
  [P in keyof T as P extends K ? never : P]: T[P];
7
45
  };
46
+ /**
47
+ * 依据键名从对象类型 `T` 中挑选键 `K`。
48
+ * @typeParam T - 源对象类型
49
+ * @typeParam K - 要保留的键(必须来自 `keyof T`)
50
+ * @example
51
+ * // type User = { id: string; name: string; age: number }
52
+ * // type R = PickByKey<User, 'id' | 'name'>
53
+ * // 结果:R 为 { id: string; name: string }
54
+ */
8
55
  type PickByKey<T, K extends keyof T> = {
9
56
  [P in keyof T as P extends K ? P : never]: T[P];
10
57
  };
58
+ /**
59
+ * 基于映射表 `Mapping` 对对象类型 `T` 的键进行重命名。
60
+ * 未在映射表中的键保持原名;映射值为 `PropertyKey`(string/number/symbol)。
61
+ * @typeParam T - 源对象类型
62
+ * @typeParam Mapping - 旧键到新键名的映射
63
+ * @example
64
+ * // type Src = { a: number; b: string }
65
+ * // type R = RenameKeys<Src, { a: 'id' }>
66
+ * // 结果:R 为 { id: number; b: string }
67
+ */
11
68
  type RenameKeys<T, Mapping extends {
12
69
  [K in keyof T]?: PropertyKey;
13
70
  }> = {
14
71
  [K in keyof T as K extends keyof Mapping ? Exclude<Mapping[K], undefined> : K]: T[K];
15
72
  };
73
+ /**
74
+ * 将对象类型 `T` 中的键 `K` 标记为必填(移除可选修饰)。
75
+ * @typeParam T - 源对象类型
76
+ * @typeParam K - 设为必填的键
77
+ * @example
78
+ * // type User = { id: string; name?: string }
79
+ * // type R = RequiredByKeys<User, 'name'>
80
+ * // 结果:R['name'] 为必填的 string
81
+ */
16
82
  type RequiredByKeys<T, K extends keyof T> = T & {
17
83
  [P in K]-?: T[P];
18
84
  };
85
+ /**
86
+ * 将对象类型 `T` 中的键 `K` 标记为可选。
87
+ * @typeParam T - 源对象类型
88
+ * @typeParam K - 设为可选的键
89
+ * @example
90
+ * // type User = { id: string; name: string }
91
+ * // type R = PartialByKeys<User, 'name'>
92
+ * // 结果:R['name'] 为可选(可能为 undefined)
93
+ */
19
94
  type PartialByKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
95
+ /**
96
+ * 将对象类型 `T` 中的键 `K` 标记为只读(浅只读)。
97
+ * @typeParam T - 源对象类型
98
+ * @typeParam K - 设为只读的键
99
+ * @example
100
+ * // type User = { id: string; name: string }
101
+ * // type R = ReadonlyByKeys<User, 'id'>
102
+ * // 结果:R['id'] 不可被重新赋值
103
+ */
20
104
  type ReadonlyByKeys<T, K extends keyof T> = T & {
21
105
  readonly [P in K]: T[P];
22
106
  };
107
+ /**
108
+ * 取消对象类型 `T` 中键 `K` 的只读限制,使其可写(浅层)。
109
+ * @typeParam T - 源对象类型
110
+ * @typeParam K - 取消只读的键
111
+ * @example
112
+ * // type User = { readonly id: string; name: string }
113
+ * // type R = MutableByKeys<User, 'id'>
114
+ * // 结果:R['id'] 变为可写
115
+ */
23
116
  type MutableByKeys<T, K extends keyof T> = {
24
117
  -readonly [P in K]: T[P];
25
118
  } & Omit<T, K>;
119
+ /**
120
+ * 将联合类型 `U` 转换为交叉类型,用于合并联合成员的属性。
121
+ * @typeParam U - 联合类型
122
+ * @example
123
+ * // type U = { a: number } | { b: string }
124
+ * // type R = UnionToIntersection<U>
125
+ * // 结果:R 为 { a: number } & { b: string }
126
+ */
26
127
  type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? {
27
128
  [K in keyof I]: I[K];
28
129
  } : never;
130
+ /**
131
+ * 若对象 `T` 在键 `K` 处的类型为元组,则提取其首个元素类型,否则为 `never`。
132
+ * @typeParam T - 具有元组属性的对象类型
133
+ * @typeParam K - 属性键
134
+ * @example
135
+ * // type Cfg = { params: [id: string, flag?: boolean] }
136
+ * // type R = FirstParam<Cfg, 'params'>
137
+ * // 结果:R 为 string
138
+ */
29
139
  type FirstParam<T, K extends keyof T> = T[K] extends [infer P, ...any[]] ? P : never;
140
+ /**
141
+ * 从函数类型中提取首个参数类型;若 `T` 非函数类型,则为 `undefined`。
142
+ * @typeParam T - 函数类型
143
+ * @example
144
+ * // type Fn = (x: number, y: string) => void
145
+ * // type R = FirstParameter<Fn>
146
+ * // 结果:R 为 number;若 T 非函数,则为 undefined
147
+ */
30
148
  type FirstParameter<T> = T extends (arg: infer P, ...args: any[]) => any ? P : undefined;
149
+ /**
150
+ * 递归将对象类型 `T` 的所有属性变为可选(深可选)。
151
+ * @typeParam T - 源对象类型
152
+ * @example
153
+ * // type Src = { a: { b: number } }
154
+ * // type R = DeepPartial<Src>
155
+ * // 结果:R 为 { a?: { b?: number | undefined } | undefined }
156
+ */
31
157
  type DeepPartial<T> = {
32
158
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P] | undefined;
33
159
  };
160
+ /**
161
+ * 当 `MaybeObject` 为对象时,返回键 `Key` 对应的属性类型;否则为 `never`。
162
+ * @typeParam MaybeObject - 可能为对象的类型
163
+ * @typeParam Key - 目标键名(string)
164
+ * @example
165
+ * // type Obj = { id: number }
166
+ * // type R1 = GetObjectField<Obj, 'id'> // 结果:number
167
+ * // type R2 = GetObjectField<string, 'id'> // 结果:never
168
+ */
34
169
  type GetObjectField<MaybeObject, Key extends string> = MaybeObject extends Record<string, any> ? MaybeObject[Key] : never;
170
+ /**
171
+ * Vue 渲染相关文本/节点类型:可为字符串、`VNode` 或返回 `VNode` 的函数。
172
+ * @example
173
+ * // 在渲染 API 中允许三种形态:
174
+ * // - '标题'
175
+ * // - h('div', '标题') 产生的 VNode
176
+ * // - () => h('div', '标题') 的惰性渲染函数
177
+ */
35
178
  type StringOrVNode = string | VNode | (() => VNode);
36
179
 
37
180
  declare const StorageTypeSchema: z.ZodEnum<{
@@ -960,10 +1103,6 @@ declare function omitUndefined<T extends AnyObject>(obj: T): Partial<T>;
960
1103
  */
961
1104
  declare function pick<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): PickByKey<T, K>;
962
1105
 
963
- interface SeparateResult<T extends AnyObject, K extends keyof T> {
964
- picked: PickByKey<T, K>;
965
- omitted: OmitByKey<T, K>;
966
- }
967
1106
  /**
968
1107
  * 将对象按指定键分离为两个对象
969
1108
  *
@@ -989,7 +1128,35 @@ interface SeparateResult<T extends AnyObject, K extends keyof T> {
989
1128
  * const { picked: publicData, omitted: privateData } = separate(user, ['id', 'name', 'email'])
990
1129
  * ```
991
1130
  */
992
- declare function separate<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): SeparateResult<T, K>;
1131
+ declare function separate<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): {
1132
+ picked: PickByKey<T, K>;
1133
+ omitted: OmitByKey<T, K>;
1134
+ };
1135
+ /**
1136
+ * 将对象按多分组键集合进行分离(浅层),返回各分组与 others
1137
+ *
1138
+ * - 键冲突策略:先到先得。若同一键出现在多个分组中,则归入第一个匹配到的分组
1139
+ * - 仅处理对象自有的浅层键,不解析深层路径
1140
+ * - 分组中包含不存在于对象的键将被忽略
1141
+ *
1142
+ * @category Object
1143
+ * @param obj 源对象
1144
+ * @param groups 分组映射,如 { a: ['x', 'y'], b: ['z'] }
1145
+ * @returns 一个对象,包含每个分组的子对象以及 others(其余未被分组捕获的键)
1146
+ * @example
1147
+ * ```ts
1148
+ * const options = { id: 1, name: 'John', email: 'a@b.com', role: 'admin' }
1149
+ * const { a, b, others } = separateMany(options, { a: ['id'], b: ['name'] as const })
1150
+ * // a: { id: 1 }
1151
+ * // b: { name: 'John' }
1152
+ * // others: { email: 'a@b.com', role: 'admin' }
1153
+ * ```
1154
+ */
1155
+ declare function separateMany<T extends AnyObject, M extends Record<string, readonly (keyof T)[]>>(obj: T, groups: M): {
1156
+ [P in keyof M]: PickByKey<T, M[P][number]>;
1157
+ } & {
1158
+ others: OmitByKey<T, M[keyof M][number]>;
1159
+ };
993
1160
 
994
1161
  /**
995
1162
  * 将字符串首字母大写
@@ -1172,5 +1339,5 @@ declare function isFunction(value: any): value is (...args: any[]) => any;
1172
1339
  */
1173
1340
  declare function isEmpty(value: any): boolean;
1174
1341
 
1175
- export { StorageTypeSchema, Tree, camelToKebab, capitalize, chunk, convertSvgToPng, convertToKebabCase, createStorageConfigSchema, debounce, deepClone, extractFilename, flatten, formatFileSize, getRandomUUID, isArray, isEmpty, isFunction, isNumber, isObject, isString, kebabToCamel, omit, omitUndefined, pick, replaceCurrentColor, separate, simpleHash, sleep, sleepWithCancel, throttle, triggerDownload, unique, useAppStorage, useCopyCode };
1176
- export type { AnyObject, AppStorageReturn, DeepPartial, FirstParam, FirstParameter, GetObjectField, MutableByKeys, OmitByKey, PartialByKeys, PickByKey, ReadonlyByKeys, RenameKeys, RequiredByKeys, StorageConfig, StorageConfigInput, StorageType, StringOrVNode, UnionToIntersection };
1342
+ export { StorageTypeSchema, Tree, camelToKebab, capitalize, chunk, convertSvgToPng, convertToKebabCase, createStorageConfigSchema, debounce, deepClone, extractFilename, flatten, formatFileSize, getRandomUUID, isArray, isEmpty, isFunction, isNumber, isObject, isString, kebabToCamel, omit, omitUndefined, pick, replaceCurrentColor, separate, separateMany, simpleHash, sleep, sleepWithCancel, throttle, triggerDownload, unique, useAppStorage, useCopyCode };
1343
+ export type { AnyObject, ApiAwaitable, ApiAwaitedReturn, ApiUnwrapPromise, AppStorageReturn, DeepPartial, FirstParam, FirstParameter, GetObjectField, MutableByKeys, OmitByKey, PartialByKeys, PickByKey, ReadonlyByKeys, RenameKeys, RequiredByKeys, StorageConfig, StorageConfigInput, StorageType, StringOrVNode, UnionToIntersection, UnknownObject };
package/dist/index.d.ts CHANGED
@@ -1,37 +1,180 @@
1
1
  import { VNode, Ref } from 'vue';
2
2
  import { z } from 'zod/v4';
3
3
 
4
+ /**
5
+ * 统一同步/异步返回类型
6
+ * @typeParam T - 类型
7
+ * @example
8
+ * // type T = string | Promise<string>
9
+ * // type R = ApiAwaitable<T>
10
+ * // 结果:R 为 string | Promise<string>
11
+ */
12
+ type ApiAwaitable<T> = T | Promise<T>;
13
+ /**
14
+ * 提取Promise类型
15
+ * @typeParam T - 类型
16
+ * @example
17
+ * // type T = Promise<string>
18
+ * // type R = ApiUnwrapPromise<T>
19
+ * // 结果:R 为 string
20
+ */
21
+ type ApiUnwrapPromise<T> = T extends Promise<infer U> ? U : T;
22
+ /**
23
+ * 提取函数返回类型
24
+ * @typeParam TFn - 函数类型
25
+ * @example
26
+ * // type Fn = (x: number, y: string) => Promise<string>
27
+ * // type R = ApiAwaitedReturn<Fn>
28
+ * // 结果:R 为 string
29
+ */
30
+ type ApiAwaitedReturn<TFn> = TFn extends (...args: any[]) => ApiAwaitable<infer R> ? R : never;
31
+
32
+ type UnknownObject = Record<string, unknown>;
4
33
  type AnyObject = Record<string, any>;
34
+ /**
35
+ * 依据键名从对象类型 `T` 中剔除键 `K`。
36
+ * @typeParam T - 源对象类型
37
+ * @typeParam K - 要剔除的键(必须来自 `keyof T`)
38
+ * @example
39
+ * // type User = { id: string; name: string; age: number }
40
+ * // type R = OmitByKey<User, 'age'>
41
+ * // 结果:R 为 { id: string; name: string }
42
+ */
5
43
  type OmitByKey<T, K extends keyof T> = {
6
44
  [P in keyof T as P extends K ? never : P]: T[P];
7
45
  };
46
+ /**
47
+ * 依据键名从对象类型 `T` 中挑选键 `K`。
48
+ * @typeParam T - 源对象类型
49
+ * @typeParam K - 要保留的键(必须来自 `keyof T`)
50
+ * @example
51
+ * // type User = { id: string; name: string; age: number }
52
+ * // type R = PickByKey<User, 'id' | 'name'>
53
+ * // 结果:R 为 { id: string; name: string }
54
+ */
8
55
  type PickByKey<T, K extends keyof T> = {
9
56
  [P in keyof T as P extends K ? P : never]: T[P];
10
57
  };
58
+ /**
59
+ * 基于映射表 `Mapping` 对对象类型 `T` 的键进行重命名。
60
+ * 未在映射表中的键保持原名;映射值为 `PropertyKey`(string/number/symbol)。
61
+ * @typeParam T - 源对象类型
62
+ * @typeParam Mapping - 旧键到新键名的映射
63
+ * @example
64
+ * // type Src = { a: number; b: string }
65
+ * // type R = RenameKeys<Src, { a: 'id' }>
66
+ * // 结果:R 为 { id: number; b: string }
67
+ */
11
68
  type RenameKeys<T, Mapping extends {
12
69
  [K in keyof T]?: PropertyKey;
13
70
  }> = {
14
71
  [K in keyof T as K extends keyof Mapping ? Exclude<Mapping[K], undefined> : K]: T[K];
15
72
  };
73
+ /**
74
+ * 将对象类型 `T` 中的键 `K` 标记为必填(移除可选修饰)。
75
+ * @typeParam T - 源对象类型
76
+ * @typeParam K - 设为必填的键
77
+ * @example
78
+ * // type User = { id: string; name?: string }
79
+ * // type R = RequiredByKeys<User, 'name'>
80
+ * // 结果:R['name'] 为必填的 string
81
+ */
16
82
  type RequiredByKeys<T, K extends keyof T> = T & {
17
83
  [P in K]-?: T[P];
18
84
  };
85
+ /**
86
+ * 将对象类型 `T` 中的键 `K` 标记为可选。
87
+ * @typeParam T - 源对象类型
88
+ * @typeParam K - 设为可选的键
89
+ * @example
90
+ * // type User = { id: string; name: string }
91
+ * // type R = PartialByKeys<User, 'name'>
92
+ * // 结果:R['name'] 为可选(可能为 undefined)
93
+ */
19
94
  type PartialByKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
95
+ /**
96
+ * 将对象类型 `T` 中的键 `K` 标记为只读(浅只读)。
97
+ * @typeParam T - 源对象类型
98
+ * @typeParam K - 设为只读的键
99
+ * @example
100
+ * // type User = { id: string; name: string }
101
+ * // type R = ReadonlyByKeys<User, 'id'>
102
+ * // 结果:R['id'] 不可被重新赋值
103
+ */
20
104
  type ReadonlyByKeys<T, K extends keyof T> = T & {
21
105
  readonly [P in K]: T[P];
22
106
  };
107
+ /**
108
+ * 取消对象类型 `T` 中键 `K` 的只读限制,使其可写(浅层)。
109
+ * @typeParam T - 源对象类型
110
+ * @typeParam K - 取消只读的键
111
+ * @example
112
+ * // type User = { readonly id: string; name: string }
113
+ * // type R = MutableByKeys<User, 'id'>
114
+ * // 结果:R['id'] 变为可写
115
+ */
23
116
  type MutableByKeys<T, K extends keyof T> = {
24
117
  -readonly [P in K]: T[P];
25
118
  } & Omit<T, K>;
119
+ /**
120
+ * 将联合类型 `U` 转换为交叉类型,用于合并联合成员的属性。
121
+ * @typeParam U - 联合类型
122
+ * @example
123
+ * // type U = { a: number } | { b: string }
124
+ * // type R = UnionToIntersection<U>
125
+ * // 结果:R 为 { a: number } & { b: string }
126
+ */
26
127
  type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? {
27
128
  [K in keyof I]: I[K];
28
129
  } : never;
130
+ /**
131
+ * 若对象 `T` 在键 `K` 处的类型为元组,则提取其首个元素类型,否则为 `never`。
132
+ * @typeParam T - 具有元组属性的对象类型
133
+ * @typeParam K - 属性键
134
+ * @example
135
+ * // type Cfg = { params: [id: string, flag?: boolean] }
136
+ * // type R = FirstParam<Cfg, 'params'>
137
+ * // 结果:R 为 string
138
+ */
29
139
  type FirstParam<T, K extends keyof T> = T[K] extends [infer P, ...any[]] ? P : never;
140
+ /**
141
+ * 从函数类型中提取首个参数类型;若 `T` 非函数类型,则为 `undefined`。
142
+ * @typeParam T - 函数类型
143
+ * @example
144
+ * // type Fn = (x: number, y: string) => void
145
+ * // type R = FirstParameter<Fn>
146
+ * // 结果:R 为 number;若 T 非函数,则为 undefined
147
+ */
30
148
  type FirstParameter<T> = T extends (arg: infer P, ...args: any[]) => any ? P : undefined;
149
+ /**
150
+ * 递归将对象类型 `T` 的所有属性变为可选(深可选)。
151
+ * @typeParam T - 源对象类型
152
+ * @example
153
+ * // type Src = { a: { b: number } }
154
+ * // type R = DeepPartial<Src>
155
+ * // 结果:R 为 { a?: { b?: number | undefined } | undefined }
156
+ */
31
157
  type DeepPartial<T> = {
32
158
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P] | undefined;
33
159
  };
160
+ /**
161
+ * 当 `MaybeObject` 为对象时,返回键 `Key` 对应的属性类型;否则为 `never`。
162
+ * @typeParam MaybeObject - 可能为对象的类型
163
+ * @typeParam Key - 目标键名(string)
164
+ * @example
165
+ * // type Obj = { id: number }
166
+ * // type R1 = GetObjectField<Obj, 'id'> // 结果:number
167
+ * // type R2 = GetObjectField<string, 'id'> // 结果:never
168
+ */
34
169
  type GetObjectField<MaybeObject, Key extends string> = MaybeObject extends Record<string, any> ? MaybeObject[Key] : never;
170
+ /**
171
+ * Vue 渲染相关文本/节点类型:可为字符串、`VNode` 或返回 `VNode` 的函数。
172
+ * @example
173
+ * // 在渲染 API 中允许三种形态:
174
+ * // - '标题'
175
+ * // - h('div', '标题') 产生的 VNode
176
+ * // - () => h('div', '标题') 的惰性渲染函数
177
+ */
35
178
  type StringOrVNode = string | VNode | (() => VNode);
36
179
 
37
180
  declare const StorageTypeSchema: z.ZodEnum<{
@@ -960,10 +1103,6 @@ declare function omitUndefined<T extends AnyObject>(obj: T): Partial<T>;
960
1103
  */
961
1104
  declare function pick<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): PickByKey<T, K>;
962
1105
 
963
- interface SeparateResult<T extends AnyObject, K extends keyof T> {
964
- picked: PickByKey<T, K>;
965
- omitted: OmitByKey<T, K>;
966
- }
967
1106
  /**
968
1107
  * 将对象按指定键分离为两个对象
969
1108
  *
@@ -989,7 +1128,35 @@ interface SeparateResult<T extends AnyObject, K extends keyof T> {
989
1128
  * const { picked: publicData, omitted: privateData } = separate(user, ['id', 'name', 'email'])
990
1129
  * ```
991
1130
  */
992
- declare function separate<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): SeparateResult<T, K>;
1131
+ declare function separate<T extends AnyObject, K extends keyof T>(obj: T, keys: K[]): {
1132
+ picked: PickByKey<T, K>;
1133
+ omitted: OmitByKey<T, K>;
1134
+ };
1135
+ /**
1136
+ * 将对象按多分组键集合进行分离(浅层),返回各分组与 others
1137
+ *
1138
+ * - 键冲突策略:先到先得。若同一键出现在多个分组中,则归入第一个匹配到的分组
1139
+ * - 仅处理对象自有的浅层键,不解析深层路径
1140
+ * - 分组中包含不存在于对象的键将被忽略
1141
+ *
1142
+ * @category Object
1143
+ * @param obj 源对象
1144
+ * @param groups 分组映射,如 { a: ['x', 'y'], b: ['z'] }
1145
+ * @returns 一个对象,包含每个分组的子对象以及 others(其余未被分组捕获的键)
1146
+ * @example
1147
+ * ```ts
1148
+ * const options = { id: 1, name: 'John', email: 'a@b.com', role: 'admin' }
1149
+ * const { a, b, others } = separateMany(options, { a: ['id'], b: ['name'] as const })
1150
+ * // a: { id: 1 }
1151
+ * // b: { name: 'John' }
1152
+ * // others: { email: 'a@b.com', role: 'admin' }
1153
+ * ```
1154
+ */
1155
+ declare function separateMany<T extends AnyObject, M extends Record<string, readonly (keyof T)[]>>(obj: T, groups: M): {
1156
+ [P in keyof M]: PickByKey<T, M[P][number]>;
1157
+ } & {
1158
+ others: OmitByKey<T, M[keyof M][number]>;
1159
+ };
993
1160
 
994
1161
  /**
995
1162
  * 将字符串首字母大写
@@ -1172,5 +1339,5 @@ declare function isFunction(value: any): value is (...args: any[]) => any;
1172
1339
  */
1173
1340
  declare function isEmpty(value: any): boolean;
1174
1341
 
1175
- export { StorageTypeSchema, Tree, camelToKebab, capitalize, chunk, convertSvgToPng, convertToKebabCase, createStorageConfigSchema, debounce, deepClone, extractFilename, flatten, formatFileSize, getRandomUUID, isArray, isEmpty, isFunction, isNumber, isObject, isString, kebabToCamel, omit, omitUndefined, pick, replaceCurrentColor, separate, simpleHash, sleep, sleepWithCancel, throttle, triggerDownload, unique, useAppStorage, useCopyCode };
1176
- export type { AnyObject, AppStorageReturn, DeepPartial, FirstParam, FirstParameter, GetObjectField, MutableByKeys, OmitByKey, PartialByKeys, PickByKey, ReadonlyByKeys, RenameKeys, RequiredByKeys, StorageConfig, StorageConfigInput, StorageType, StringOrVNode, UnionToIntersection };
1342
+ export { StorageTypeSchema, Tree, camelToKebab, capitalize, chunk, convertSvgToPng, convertToKebabCase, createStorageConfigSchema, debounce, deepClone, extractFilename, flatten, formatFileSize, getRandomUUID, isArray, isEmpty, isFunction, isNumber, isObject, isString, kebabToCamel, omit, omitUndefined, pick, replaceCurrentColor, separate, separateMany, simpleHash, sleep, sleepWithCancel, throttle, triggerDownload, unique, useAppStorage, useCopyCode };
1343
+ export type { AnyObject, ApiAwaitable, ApiAwaitedReturn, ApiUnwrapPromise, AppStorageReturn, DeepPartial, FirstParam, FirstParameter, GetObjectField, MutableByKeys, OmitByKey, PartialByKeys, PickByKey, ReadonlyByKeys, RenameKeys, RequiredByKeys, StorageConfig, StorageConfigInput, StorageType, StringOrVNode, UnionToIntersection, UnknownObject };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{useStorage as $}from"@vueuse/core";import{z as y}from"zod/v4";const x=y.enum(["localStorage","sessionStorage"]);function A(e){return y.object({key:y.string().min(1,{message:"Key cannot be empty"}),schema:y.custom(t=>t instanceof y.ZodType,{message:"Schema must be a valid Zod schema"}),defaultValue:y.custom(t=>e.safeParse(t).success,{message:"Default value must match the provided schema"}),prefix:y.string().default("movk"),storage:x.default("localStorage")})}function k(e){const t=A(e.schema).parse(e),{key:n,defaultValue:r,schema:o,storage:s,prefix:a}=t,i=`${a}:${n}`,c=(()=>{if(!(typeof window>"u"))return s==="localStorage"?localStorage:sessionStorage})();function l(p){if(p===null)return r;try{const g=JSON.parse(p),w=o.safeParse(g);return w.success?w.data:(console.warn(`[AppStorage] Validation failed for key "${i}". Using default value.`,w.error.issues),r)}catch(g){return console.warn(`[AppStorage] Failed to parse value for key "${i}". Using default value.`,g),r}}const u=$(i,r,c,{mergeDefaults:!0,serializer:{read:l,write:p=>JSON.stringify(p)}});function d(){if(!c)return r;const p=c.getItem(i);return l(p)}function h(p){const g=o.safeParse(p);if(!g.success){console.warn(`[AppStorage] Invalid value for key "${i}". Aborting setItem.`,g.error.issues);return}u.value=g.data}function f(){c&&(u.value=null)}return{state:u,getItem:d,setItem:h,removeItem:f}}async function O(e){if(typeof e!="string")throw new TypeError("Text must be a string");if(typeof window>"u")return console.warn("useCopyCode: Not available in server environment"),!1;if(navigator.clipboard&&window.isSecureContext)try{return await navigator.clipboard.writeText(e),!0}catch(t){console.warn("Clipboard API failed, falling back to legacy method:",t)}try{return I(e)}catch(t){return console.error("Failed to copy text:",t),!1}}function I(e){if(typeof document>"u")return console.warn("copyTextLegacy: Document not available"),!1;const t=document.createElement("textarea"),n=document.activeElement,r=document.getSelection(),o=r&&r.rangeCount>0?r.getRangeAt(0):null;try{return t.value=e,t.setAttribute("readonly",""),t.setAttribute("contenteditable","true"),Object.assign(t.style,{contain:"strict",position:"absolute",left:"-9999px",top:"-9999px",fontSize:"12pt",border:"0",padding:"0",margin:"0",outline:"none",boxShadow:"none",background:"transparent"}),document.body.appendChild(t),t.focus(),t.select(),t.setSelectionRange&&t.setSelectionRange(0,e.length),document.execCommand("copy")}catch(s){return console.error("Legacy copy method failed:",s),!1}finally{t.parentNode&&document.body.removeChild(t),o&&r&&(r.removeAllRanges(),r.addRange(o)),n instanceof HTMLElement&&n.focus()}}const m=y.object({id:y.string().default("id"),pid:y.string().default("pid"),children:y.string().default("children")});y.object({total:y.number().int().nonnegative(),leaves:y.number().int().nonnegative(),depth:y.number().int().nonnegative(),branches:y.number().int().nonnegative()});class b{static*dfsGenerator(t,n={},r=[]){const{children:o}=m.parse(n);let s=0;for(const a of t){const i=[...r,a];yield{node:a,path:i,depth:r.length,index:s++};const c=a[o];c&&c.length>0&&(yield*b.dfsGenerator(c,n,i))}}static*bfsGenerator(t,n={}){const{children:r}=m.parse(n),o=t.map((a,i)=>({node:a,path:[a],depth:0,index:i}));let s=t.length;for(;o.length>0;){const a=o.shift();yield a;const i=a.node[r];i&&i.length>0&&i.forEach(c=>{const l=[...a.path,c];o.push({node:c,path:l,depth:a.depth+1,index:s++})})}}static selectStrategy(t){switch(t){case"find":case"findAll":case"filter":case"transform":case"forEach":case"stats":case"validate":return"dfs";default:return"dfs"}}static fromList(t,n={}){const r=m.parse(n),{id:o,pid:s,children:a}=r;if(!Array.isArray(t)||t.length===0)return[];const i=new Map,c=[];return t.forEach(l=>{const u={...l,[a]:[]};i.set(l[o],u)}),t.forEach(l=>{const u=i.get(l[o]),d=l[s];d&&i.has(d)?i.get(d)[a].push(u):c.push(u)}),c}static toList(t,n={}){const r=m.parse(n),{children:o}=r,s=[],a=Array.isArray(t)?t:[t],i=c=>{const{[o]:l,...u}=c;s.push(u),l&&l.length>0&&l.forEach(i)};return a.forEach(i),s}static estimateSize(t,n={}){const r=m.parse(n),{children:o}=r,s=Array.isArray(t)?t:[t];let a=0;const i=c=>{a++;const l=c[o];l&&l.length>0&&l.forEach(i)};return s.forEach(i),a}static find(t,n,r={}){const o=Array.isArray(t)?t:[t],s=b.selectStrategy("find")==="dfs"?b.dfsGenerator(o,r):b.bfsGenerator(o,r);for(const a of s)if(n(a.node,a.depth,a.path))return a}static findAll(t,n,r={}){const o=Array.isArray(t)?t:[t],s=b.selectStrategy("findAll"),a=[],i=s==="dfs"?b.dfsGenerator(o,r):b.bfsGenerator(o,r);for(const c of i)n(c.node,c.depth,c.path)&&a.push(c);return a}static findById(t,n,r={}){const o=m.parse(r),{id:s}=o;return this.find(t,a=>a[s]===n,r)}static getStats(t,n={}){const r=m.parse(n),{children:o}=r,s=Array.isArray(t)?t:[t];let a=0,i=0,c=0,l=0;const u=(d,h)=>{a++,c=Math.max(c,h);const f=d[o];f&&f.length>0?(l++,f.forEach(p=>u(p,h+1))):i++};return s.forEach(d=>u(d,1)),{total:a,leaves:i,depth:c,branches:l}}static filter(t,n,r={}){const o=m.parse(r),{children:s}=o,a=Array.isArray(t)?t:[t],i=[],c=(l,u,d)=>{const h=l[s],f=[];if(h&&h.length>0){const p=[...d,l];h.forEach(g=>{const w=c(g,u+1,p);w&&f.push(w)})}return n(l,u,d)||f.length>0?{...l,[s]:f}:null};return a.forEach(l=>{const u=c(l,0,[]);u&&i.push(u)}),i}static transform(t,n,r={}){const o=m.parse(r),{children:s}=o,a=Array.isArray(t)?t:[t],i=[],c=(l,u,d)=>{const h=l[s],f=[];if(h&&h.length>0){const p=[...d,l];h.forEach(g=>{f.push(c(g,u+1,p))})}return{...n(l,u,d),[s]:f}};return a.forEach(l=>{i.push(c(l,0,[]))}),i}static forEach(t,n,r={}){const o=m.parse(r),{children:s}=o,a=Array.isArray(t)?t:[t],i=(c,l,u)=>{if(n(c,l,u)!==!1){const d=c[s];if(d&&d.length>0){const h=[...u,c];d.forEach(f=>{i(f,l+1,h)})}}};a.forEach(c=>i(c,0,[]))}static insertBefore(t,n,r,o={}){const s=m.parse(o),{id:a,children:i}=s,c={...r,[i]:[]},l=(u,d)=>{for(let h=0;h<u.length;h++){const f=u[h];if(f[a]===n)return u.splice(h,0,c),!0;const p=f[i];if(p&&p.length>0){const g=[...d,f];if(l(p,g))return!0}}return!1};return l(t,[])}static insertAfter(t,n,r,o={}){const s=m.parse(o),{id:a,children:i}=s,c={...r,[i]:[]},l=(u,d)=>{for(let h=0;h<u.length;h++){const f=u[h];if(f[a]===n)return u.splice(h+1,0,c),!0;const p=f[i];if(p&&p.length>0){const g=[...d,f];if(l(p,g))return!0}}return!1};return l(t,[])}static remove(t,n,r={}){const o=m.parse(r),{id:s,children:a}=o,i=c=>{for(let l=0;l<c.length;l++){const u=c[l];if(u[s]===n)return c.splice(l,1)[0];const d=u[a];if(d&&d.length>0){const h=i(d);if(h)return h}}};return i(t)}static validate(t,n={}){const r=m.parse(n),{id:o,children:s}=r,a=Array.isArray(t)?t:[t],i=[],c=new Set,l=(u,d,h)=>{const f=u[o];if(!f||typeof f!="string"){i.push(`Node at depth ${d} has invalid or missing ID`);return}if(c.has(f)){i.push(`Duplicate ID found: ${f}`);return}if(c.add(f),h.some(g=>g[o]===f)){i.push(`Circular reference detected for ID: ${f}`);return}const p=u[s];if(p!==void 0&&!Array.isArray(p)){i.push(`Node ${f} has invalid children property (not an array)`);return}if(p&&p.length>0){const g=[...h,u];p.forEach(w=>{l(w,d+1,g)})}};return a.forEach(u=>l(u,0,[])),{isValid:i.length===0,errors:i}}}function G(e){return[...new Set(e)]}function N(e,t){const n=[];for(let r=0;r<e.length;r+=t)n.push(e.slice(r,r+t));return n}function T(e,t=1){return t===1?e.flat():e.flat(t)}function D(e,t){let n;return(...r)=>{clearTimeout(n),n=setTimeout(()=>e(...r),t)}}function F(e){return new Promise(t=>setTimeout(t,e))}function P(e){let t,n;return{promise:new Promise((r,o)=>{n=o,t=setTimeout(r,e)}),cancel:()=>{clearTimeout(t),n(new Error("Sleep was cancelled"))}}}function M(e,t){let n=!1;return function(...r){n||(e.apply(this,r),n=!0,setTimeout(()=>{n=!1},t))}}async function U(e){if(!e||typeof e!="string")throw new Error("Invalid SVG string provided");if(typeof window>"u"||typeof document>"u")throw new TypeError("convertSvgToPng is only available in browser environment");return new Promise((t,n)=>{const r=new Image,o=document.createElement("canvas"),s=o.getContext("2d");if(!s){n(new Error("Canvas context not available"));return}r.onload=()=>{try{o.width=r.width,o.height=r.height,s.drawImage(r,0,0),o.toBlob(a=>{a?t(a):n(new Error("Failed to convert canvas to Blob"))},"image/png")}catch(a){n(new Error(`Error during canvas conversion: ${a}`))}},r.onerror=()=>{n(new Error("Failed to load SVG image"))};try{r.src=`data:image/svg+xml;base64,${btoa(e)}`}catch(a){n(new Error(`Failed to encode SVG: ${a}`))}})}function L(e,t="file"){if(!e)return t;const n=e.get("content-disposition");if(n){const r=n.match(/filename\*?=['"]?([^'";]+)['"]?/i);if(r){let o=r[1];if(n.includes("filename*=")){const s=o.split("''");s.length===2&&(o=decodeURIComponent(s[1]))}return o}}return t}function V(e,t){if(typeof window>"u"||typeof document>"u"){console.warn("triggerDownload: Not available in server environment");return}const n=URL.createObjectURL(e),r=document.createElement("a");r.href=n,r.download=t,r.style.display="none",document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(n)}function z(e){if(!Number.isFinite(e)||e<0||e===0)return"0 Bytes";const t=1024,n=["Bytes","KB","MB","GB","TB","PB"],r=Math.floor(Math.log(e)/Math.log(t));return r>=n.length?`${Number.parseFloat((e/t**(n.length-1)).toFixed(2))} ${n[n.length-1]}`:`${Number.parseFloat((e/t**r).toFixed(2))} ${n[r]}`}async function B(e,t){if(!e||typeof e!="string")throw new Error("Invalid SVG path provided");try{const n=await fetch(e);if(!n.ok)throw new Error(`Failed to fetch SVG file: ${n.status} ${n.statusText}`);const r=await n.text();if(!t)return r;if(typeof window>"u"||typeof DOMParser>"u")return console.warn("replaceCurrentColor: DOM manipulation not available in server environment, returning original SVG"),r;const o=new DOMParser().parseFromString(r,"image/svg+xml");if(o.querySelector("parsererror"))throw new Error("Invalid SVG content");const s=o.querySelector("svg");if(!s)throw new Error("No SVG element found in the document");s.hasAttribute("fill")||s.setAttribute("fill","currentColor");const a=i=>{i.getAttribute("fill")==="currentColor"&&i.setAttribute("fill",t),i.getAttribute("stroke")==="currentColor"&&i.setAttribute("stroke",t),Array.from(i.children).forEach(c=>{a(c)})};return a(s),new XMLSerializer().serializeToString(o)}catch(n){throw n instanceof Error?n:new Error(`Unexpected error occurred: ${n}`)}}function S(e,t=!1){if(!e||typeof e!="object"||Array.isArray(e))return e;const n=o=>o.replace(/([a-z\d])([A-Z])/g,"$1-$2").toLowerCase(),r={};for(const o in e)if(Object.prototype.hasOwnProperty.call(e,o)){const s=n(o),a=e[o];t&&a&&typeof a=="object"&&!Array.isArray(a)?r[s]=S(a,!0):r[s]=a}return r}function v(e){if(e===null||typeof e!="object")return e;if(e instanceof Date)return new Date(e.getTime());if(Array.isArray(e))return e.map(t=>v(t));if(typeof e=="object"){const t={};for(const n in e)t[n]=v(e[n]);return t}return e}function R(e,t){if(!e||typeof e!="object")return{};const n=new Set(t),r={};for(const o in e)Object.prototype.hasOwnProperty.call(e,o)&&!n.has(o)&&(r[o]=e[o]);return r}function K(e){return!e||typeof e!="object"||Array.isArray(e)?e:Object.fromEntries(Object.entries(e).filter(([,t])=>t!==void 0))}function Z(e,t){if(!e||typeof e!="object")return{};const n={};for(const r of t)Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}function q(e,t){if(!e||typeof e!="object")return{picked:{},omitted:{}};if(t.length===0)return{picked:{},omitted:{...e}};const n=new Set(t),r={},o={};for(const s in e)if(Object.hasOwn(e,s)){const a=s;n.has(a)?r[s]=e[a]:o[s]=e[a]}return{picked:r,omitted:o}}function H(e){return e.charAt(0).toUpperCase()+e.slice(1)}function J(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function W(e){return e.replace(/-([a-z])/g,(t,n)=>n.toUpperCase())}function X(e){let t=0;for(let n=0;n<e.length;n++){const r=e.charCodeAt(n);t=(t<<5)-t+r,t=t&t}return Math.abs(t).toString(36)}function Q(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{const t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}function E(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function C(e){return Array.isArray(e)}function j(e){return typeof e=="string"}function Y(e){return typeof e=="number"&&!Number.isNaN(e)}function _(e){return typeof e=="function"}function tt(e){return e==null?!0:C(e)||j(e)?e.length===0:E(e)?Object.keys(e).length===0:!1}export{x as StorageTypeSchema,b as Tree,J as camelToKebab,H as capitalize,N as chunk,U as convertSvgToPng,S as convertToKebabCase,A as createStorageConfigSchema,D as debounce,v as deepClone,L as extractFilename,T as flatten,z as formatFileSize,Q as getRandomUUID,C as isArray,tt as isEmpty,_ as isFunction,Y as isNumber,E as isObject,j as isString,W as kebabToCamel,R as omit,K as omitUndefined,Z as pick,B as replaceCurrentColor,q as separate,X as simpleHash,F as sleep,P as sleepWithCancel,M as throttle,V as triggerDownload,G as unique,k as useAppStorage,O as useCopyCode};
1
+ import{useStorage as O}from"@vueuse/core";import{z as y}from"zod/v4";const x=y.enum(["localStorage","sessionStorage"]);function A(t){return y.object({key:y.string().min(1,{message:"Key cannot be empty"}),schema:y.custom(e=>e instanceof y.ZodType,{message:"Schema must be a valid Zod schema"}),defaultValue:y.custom(e=>t.safeParse(e).success,{message:"Default value must match the provided schema"}),prefix:y.string().default("movk"),storage:x.default("localStorage")})}function k(t){const e=A(t.schema).parse(t),{key:n,defaultValue:r,schema:o,storage:i,prefix:a}=e,s=`${a}:${n}`,c=(()=>{if(!(typeof window>"u"))return i==="localStorage"?localStorage:sessionStorage})();function l(p){if(p===null)return r;try{const g=JSON.parse(p),w=o.safeParse(g);return w.success?w.data:(console.warn(`[AppStorage] Validation failed for key "${s}". Using default value.`,w.error.issues),r)}catch(g){return console.warn(`[AppStorage] Failed to parse value for key "${s}". Using default value.`,g),r}}const u=O(s,r,c,{mergeDefaults:!0,serializer:{read:l,write:p=>JSON.stringify(p)}});function d(){if(!c)return r;const p=c.getItem(s);return l(p)}function h(p){const g=o.safeParse(p);if(!g.success){console.warn(`[AppStorage] Invalid value for key "${s}". Aborting setItem.`,g.error.issues);return}u.value=g.data}function f(){c&&(u.value=null)}return{state:u,getItem:d,setItem:h,removeItem:f}}async function $(t){if(typeof t!="string")throw new TypeError("Text must be a string");if(typeof window>"u")return console.warn("useCopyCode: Not available in server environment"),!1;if(navigator.clipboard&&window.isSecureContext)try{return await navigator.clipboard.writeText(t),!0}catch(e){console.warn("Clipboard API failed, falling back to legacy method:",e)}try{return I(t)}catch(e){return console.error("Failed to copy text:",e),!1}}function I(t){if(typeof document>"u")return console.warn("copyTextLegacy: Document not available"),!1;const e=document.createElement("textarea"),n=document.activeElement,r=document.getSelection(),o=r&&r.rangeCount>0?r.getRangeAt(0):null;try{return e.value=t,e.setAttribute("readonly",""),e.setAttribute("contenteditable","true"),Object.assign(e.style,{contain:"strict",position:"absolute",left:"-9999px",top:"-9999px",fontSize:"12pt",border:"0",padding:"0",margin:"0",outline:"none",boxShadow:"none",background:"transparent"}),document.body.appendChild(e),e.focus(),e.select(),e.setSelectionRange&&e.setSelectionRange(0,t.length),document.execCommand("copy")}catch(i){return console.error("Legacy copy method failed:",i),!1}finally{e.parentNode&&document.body.removeChild(e),o&&r&&(r.removeAllRanges(),r.addRange(o)),n instanceof HTMLElement&&n.focus()}}const m=y.object({id:y.string().default("id"),pid:y.string().default("pid"),children:y.string().default("children")});y.object({total:y.number().int().nonnegative(),leaves:y.number().int().nonnegative(),depth:y.number().int().nonnegative(),branches:y.number().int().nonnegative()});class b{static*dfsGenerator(e,n={},r=[]){const{children:o}=m.parse(n);let i=0;for(const a of e){const s=[...r,a];yield{node:a,path:s,depth:r.length,index:i++};const c=a[o];c&&c.length>0&&(yield*b.dfsGenerator(c,n,s))}}static*bfsGenerator(e,n={}){const{children:r}=m.parse(n),o=e.map((a,s)=>({node:a,path:[a],depth:0,index:s}));let i=e.length;for(;o.length>0;){const a=o.shift();yield a;const s=a.node[r];s&&s.length>0&&s.forEach(c=>{const l=[...a.path,c];o.push({node:c,path:l,depth:a.depth+1,index:i++})})}}static selectStrategy(e){switch(e){case"find":case"findAll":case"filter":case"transform":case"forEach":case"stats":case"validate":return"dfs";default:return"dfs"}}static fromList(e,n={}){const r=m.parse(n),{id:o,pid:i,children:a}=r;if(!Array.isArray(e)||e.length===0)return[];const s=new Map,c=[];return e.forEach(l=>{const u={...l,[a]:[]};s.set(l[o],u)}),e.forEach(l=>{const u=s.get(l[o]),d=l[i];d&&s.has(d)?s.get(d)[a].push(u):c.push(u)}),c}static toList(e,n={}){const r=m.parse(n),{children:o}=r,i=[],a=Array.isArray(e)?e:[e],s=c=>{const{[o]:l,...u}=c;i.push(u),l&&l.length>0&&l.forEach(s)};return a.forEach(s),i}static estimateSize(e,n={}){const r=m.parse(n),{children:o}=r,i=Array.isArray(e)?e:[e];let a=0;const s=c=>{a++;const l=c[o];l&&l.length>0&&l.forEach(s)};return i.forEach(s),a}static find(e,n,r={}){const o=Array.isArray(e)?e:[e],i=b.selectStrategy("find")==="dfs"?b.dfsGenerator(o,r):b.bfsGenerator(o,r);for(const a of i)if(n(a.node,a.depth,a.path))return a}static findAll(e,n,r={}){const o=Array.isArray(e)?e:[e],i=b.selectStrategy("findAll"),a=[],s=i==="dfs"?b.dfsGenerator(o,r):b.bfsGenerator(o,r);for(const c of s)n(c.node,c.depth,c.path)&&a.push(c);return a}static findById(e,n,r={}){const o=m.parse(r),{id:i}=o;return this.find(e,a=>a[i]===n,r)}static getStats(e,n={}){const r=m.parse(n),{children:o}=r,i=Array.isArray(e)?e:[e];let a=0,s=0,c=0,l=0;const u=(d,h)=>{a++,c=Math.max(c,h);const f=d[o];f&&f.length>0?(l++,f.forEach(p=>u(p,h+1))):s++};return i.forEach(d=>u(d,1)),{total:a,leaves:s,depth:c,branches:l}}static filter(e,n,r={}){const o=m.parse(r),{children:i}=o,a=Array.isArray(e)?e:[e],s=[],c=(l,u,d)=>{const h=l[i],f=[];if(h&&h.length>0){const p=[...d,l];h.forEach(g=>{const w=c(g,u+1,p);w&&f.push(w)})}return n(l,u,d)||f.length>0?{...l,[i]:f}:null};return a.forEach(l=>{const u=c(l,0,[]);u&&s.push(u)}),s}static transform(e,n,r={}){const o=m.parse(r),{children:i}=o,a=Array.isArray(e)?e:[e],s=[],c=(l,u,d)=>{const h=l[i],f=[];if(h&&h.length>0){const p=[...d,l];h.forEach(g=>{f.push(c(g,u+1,p))})}return{...n(l,u,d),[i]:f}};return a.forEach(l=>{s.push(c(l,0,[]))}),s}static forEach(e,n,r={}){const o=m.parse(r),{children:i}=o,a=Array.isArray(e)?e:[e],s=(c,l,u)=>{if(n(c,l,u)!==!1){const d=c[i];if(d&&d.length>0){const h=[...u,c];d.forEach(f=>{s(f,l+1,h)})}}};a.forEach(c=>s(c,0,[]))}static insertBefore(e,n,r,o={}){const i=m.parse(o),{id:a,children:s}=i,c={...r,[s]:[]},l=(u,d)=>{for(let h=0;h<u.length;h++){const f=u[h];if(f[a]===n)return u.splice(h,0,c),!0;const p=f[s];if(p&&p.length>0){const g=[...d,f];if(l(p,g))return!0}}return!1};return l(e,[])}static insertAfter(e,n,r,o={}){const i=m.parse(o),{id:a,children:s}=i,c={...r,[s]:[]},l=(u,d)=>{for(let h=0;h<u.length;h++){const f=u[h];if(f[a]===n)return u.splice(h+1,0,c),!0;const p=f[s];if(p&&p.length>0){const g=[...d,f];if(l(p,g))return!0}}return!1};return l(e,[])}static remove(e,n,r={}){const o=m.parse(r),{id:i,children:a}=o,s=c=>{for(let l=0;l<c.length;l++){const u=c[l];if(u[i]===n)return c.splice(l,1)[0];const d=u[a];if(d&&d.length>0){const h=s(d);if(h)return h}}};return s(e)}static validate(e,n={}){const r=m.parse(n),{id:o,children:i}=r,a=Array.isArray(e)?e:[e],s=[],c=new Set,l=(u,d,h)=>{const f=u[o];if(!f||typeof f!="string"){s.push(`Node at depth ${d} has invalid or missing ID`);return}if(c.has(f)){s.push(`Duplicate ID found: ${f}`);return}if(c.add(f),h.some(g=>g[o]===f)){s.push(`Circular reference detected for ID: ${f}`);return}const p=u[i];if(p!==void 0&&!Array.isArray(p)){s.push(`Node ${f} has invalid children property (not an array)`);return}if(p&&p.length>0){const g=[...h,u];p.forEach(w=>{l(w,d+1,g)})}};return a.forEach(u=>l(u,0,[])),{isValid:s.length===0,errors:s}}}function G(t){return[...new Set(t)]}function N(t,e){const n=[];for(let r=0;r<t.length;r+=e)n.push(t.slice(r,r+e));return n}function T(t,e=1){return e===1?t.flat():t.flat(e)}function D(t,e){let n;return(...r)=>{clearTimeout(n),n=setTimeout(()=>t(...r),e)}}function F(t){return new Promise(e=>setTimeout(e,t))}function M(t){let e,n;return{promise:new Promise((r,o)=>{n=o,e=setTimeout(r,t)}),cancel:()=>{clearTimeout(e),n(new Error("Sleep was cancelled"))}}}function P(t,e){let n=!1;return function(...r){n||(t.apply(this,r),n=!0,setTimeout(()=>{n=!1},e))}}async function U(t){if(!t||typeof t!="string")throw new Error("Invalid SVG string provided");if(typeof window>"u"||typeof document>"u")throw new TypeError("convertSvgToPng is only available in browser environment");return new Promise((e,n)=>{const r=new Image,o=document.createElement("canvas"),i=o.getContext("2d");if(!i){n(new Error("Canvas context not available"));return}r.onload=()=>{try{o.width=r.width,o.height=r.height,i.drawImage(r,0,0),o.toBlob(a=>{a?e(a):n(new Error("Failed to convert canvas to Blob"))},"image/png")}catch(a){n(new Error(`Error during canvas conversion: ${a}`))}},r.onerror=()=>{n(new Error("Failed to load SVG image"))};try{r.src=`data:image/svg+xml;base64,${btoa(t)}`}catch(a){n(new Error(`Failed to encode SVG: ${a}`))}})}function L(t,e="file"){if(!t)return e;const n=t.get("content-disposition");if(n){const r=n.match(/filename\*?=['"]?([^'";]+)['"]?/i);if(r){let o=r[1];if(n.includes("filename*=")){const i=o.split("''");i.length===2&&(o=decodeURIComponent(i[1]))}return o}}return e}function V(t,e){if(typeof window>"u"||typeof document>"u"){console.warn("triggerDownload: Not available in server environment");return}const n=URL.createObjectURL(t),r=document.createElement("a");r.href=n,r.download=e,r.style.display="none",document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(n)}function z(t){if(!Number.isFinite(t)||t<0||t===0)return"0 Bytes";const e=1024,n=["Bytes","KB","MB","GB","TB","PB"],r=Math.floor(Math.log(t)/Math.log(e));return r>=n.length?`${Number.parseFloat((t/e**(n.length-1)).toFixed(2))} ${n[n.length-1]}`:`${Number.parseFloat((t/e**r).toFixed(2))} ${n[r]}`}async function B(t,e){if(!t||typeof t!="string")throw new Error("Invalid SVG path provided");try{const n=await fetch(t);if(!n.ok)throw new Error(`Failed to fetch SVG file: ${n.status} ${n.statusText}`);const r=await n.text();if(!e)return r;if(typeof window>"u"||typeof DOMParser>"u")return console.warn("replaceCurrentColor: DOM manipulation not available in server environment, returning original SVG"),r;const o=new DOMParser().parseFromString(r,"image/svg+xml");if(o.querySelector("parsererror"))throw new Error("Invalid SVG content");const i=o.querySelector("svg");if(!i)throw new Error("No SVG element found in the document");i.hasAttribute("fill")||i.setAttribute("fill","currentColor");const a=s=>{s.getAttribute("fill")==="currentColor"&&s.setAttribute("fill",e),s.getAttribute("stroke")==="currentColor"&&s.setAttribute("stroke",e),Array.from(s.children).forEach(c=>{a(c)})};return a(i),new XMLSerializer().serializeToString(o)}catch(n){throw n instanceof Error?n:new Error(`Unexpected error occurred: ${n}`)}}function S(t,e=!1){if(!t||typeof t!="object"||Array.isArray(t))return t;const n=o=>o.replace(/([a-z\d])([A-Z])/g,"$1-$2").toLowerCase(),r={};for(const o in t)if(Object.prototype.hasOwnProperty.call(t,o)){const i=n(o),a=t[o];e&&a&&typeof a=="object"&&!Array.isArray(a)?r[i]=S(a,!0):r[i]=a}return r}function v(t){if(t===null||typeof t!="object")return t;if(t instanceof Date)return new Date(t.getTime());if(Array.isArray(t))return t.map(e=>v(e));if(typeof t=="object"){const e={};for(const n in t)e[n]=v(t[n]);return e}return t}function R(t,e){if(!t||typeof t!="object")return{};const n=new Set(e),r={};for(const o in t)Object.prototype.hasOwnProperty.call(t,o)&&!n.has(o)&&(r[o]=t[o]);return r}function K(t){return!t||typeof t!="object"||Array.isArray(t)?t:Object.fromEntries(Object.entries(t).filter(([,e])=>e!==void 0))}function Z(t,e){if(!t||typeof t!="object")return{};const n={};for(const r of e)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r]);return n}function q(t,e){if(!t||typeof t!="object")return{picked:{},omitted:{}};if(e.length===0)return{picked:{},omitted:{...t}};const n=new Set(e),r={},o={};for(const i in t)if(Object.hasOwn(t,i)){const a=i;n.has(a)?r[i]=t[a]:o[i]=t[a]}return{picked:r,omitted:o}}function H(t,e){if(!t||typeof t!="object")return{...Object.fromEntries(Object.keys(e).map(i=>[i,{}])),others:{}};const n=Object.keys(e),r=new Map;for(const i of n){const a=new Set(e[i]);r.set(i,a)}const o=Object.create(null);for(const i of n)o[i]={};o.others={};for(const i in t)if(Object.hasOwn(t,i)){const a=i;let s=!1;for(const c of n)if(r.get(c).has(a)){o[c][i]=t[a],s=!0;break}s||(o.others[i]=t[a])}return o}function J(t){return t.charAt(0).toUpperCase()+t.slice(1)}function W(t){return t.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function X(t){return t.replace(/-([a-z])/g,(e,n)=>n.toUpperCase())}function Q(t){let e=0;for(let n=0;n<t.length;n++){const r=t.charCodeAt(n);e=(e<<5)-e+r,e=e&e}return Math.abs(e).toString(36)}function Y(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const e=Math.random()*16|0;return(t==="x"?e:e&3|8).toString(16)})}function E(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}function C(t){return Array.isArray(t)}function j(t){return typeof t=="string"}function _(t){return typeof t=="number"&&!Number.isNaN(t)}function ee(t){return typeof t=="function"}function te(t){return t==null?!0:C(t)||j(t)?t.length===0:E(t)?Object.keys(t).length===0:!1}export{x as StorageTypeSchema,b as Tree,W as camelToKebab,J as capitalize,N as chunk,U as convertSvgToPng,S as convertToKebabCase,A as createStorageConfigSchema,D as debounce,v as deepClone,L as extractFilename,T as flatten,z as formatFileSize,Y as getRandomUUID,C as isArray,te as isEmpty,ee as isFunction,_ as isNumber,E as isObject,j as isString,X as kebabToCamel,R as omit,K as omitUndefined,Z as pick,B as replaceCurrentColor,q as separate,H as separateMany,Q as simpleHash,F as sleep,M as sleepWithCancel,P as throttle,V as triggerDownload,G as unique,k as useAppStorage,$ as useCopyCode};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@movk/core",
3
3
  "type": "module",
4
- "version": "0.0.4",
4
+ "version": "0.0.5",
5
5
  "description": "Modern Vue.js utilities and composables collection with TypeScript support",
6
6
  "author": "yixuan",
7
7
  "license": "MIT",
@@ -37,24 +37,23 @@
37
37
  "dist"
38
38
  ],
39
39
  "peerDependencies": {
40
- "vue": "^3.5.18"
40
+ "vue": "^3.5.21"
41
41
  },
42
42
  "dependencies": {
43
- "@vueuse/core": "^13.6.0",
44
- "zod": "^4.0.14"
43
+ "@vueuse/core": "^13.9.0",
44
+ "zod": "^4.1.5"
45
45
  },
46
46
  "devDependencies": {
47
- "@antfu/eslint-config": "^5.0.0",
47
+ "@antfu/eslint-config": "^5.2.1",
48
48
  "@release-it/conventional-changelog": "^10.0.1",
49
- "@types/node": "^24.1.0",
50
- "bumpp": "^10.2.1",
51
- "eslint": "^9.32.0",
49
+ "@types/node": "^24.3.0",
50
+ "eslint": "^9.34.0",
52
51
  "release-it": "^19.0.4",
53
- "taze": "^19.1.0",
54
- "tsx": "^4.20.3",
55
- "typescript": "^5.8.3",
56
- "unbuild": "^3.6.0",
57
- "vite": "^7.0.6",
52
+ "taze": "^19.4.0",
53
+ "tsx": "^4.20.5",
54
+ "typescript": "^5.9.2",
55
+ "unbuild": "^3.6.1",
56
+ "vite": "^7.1.4",
58
57
  "vitest": "^3.2.4"
59
58
  },
60
59
  "scripts": {