@xxkatt/utils 0.0.3 → 0.0.4
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 +23 -0
- package/dist/index.js +159 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -32,6 +32,25 @@ export declare function randomId(length?: number, radix?: number): string;
|
|
|
32
32
|
* await download(new Blob(['hello'], { type: 'text/plain' }), 'hello.txt')
|
|
33
33
|
*/
|
|
34
34
|
export declare function download(source: string | Blob, filename: string): Promise<void>;
|
|
35
|
+
/** 将 File/Blob 异步读取为完整的 Base64 Data URL,需要 FileReader 支持。 */
|
|
36
|
+
export declare function fileToDataURL(file: Blob): Promise<string>;
|
|
37
|
+
/** 将完整 Data URL 同步还原为 Blob;支持 Base64 和百分号编码,保留 MIME 类型和字节。 */
|
|
38
|
+
export declare function dataURLToBlob(dataURL: string): Blob;
|
|
39
|
+
export type FormDataScalar = string | number | boolean | Blob;
|
|
40
|
+
export type FormDataValue = FormDataScalar | readonly FormDataScalar[] | null | undefined;
|
|
41
|
+
export type FormDataFields = Readonly<Record<string, FormDataValue>>;
|
|
42
|
+
/** 将字段追加到 FormData;数组按顺序生成重复键,null、undefined 和空数组会跳过。 */
|
|
43
|
+
export declare function appendFormData(formData: FormData, fields: FormDataFields): FormData;
|
|
44
|
+
/** 根据普通字段对象创建 FormData。 */
|
|
45
|
+
export declare function objectToFormData(fields: FormDataFields): FormData;
|
|
46
|
+
/**
|
|
47
|
+
* 复制文本;优先使用 Clipboard API,失败或不可用时回退到 execCommand。
|
|
48
|
+
*
|
|
49
|
+
* @returns 写入成功后完成;浏览器不支持或拒绝复制时抛错。
|
|
50
|
+
* @example
|
|
51
|
+
* await copyText('要复制的内容')
|
|
52
|
+
*/
|
|
53
|
+
export declare function copyText(text: string): Promise<void>;
|
|
35
54
|
export interface DebouncedFunction<This, Args extends unknown[]> {
|
|
36
55
|
(this: This, ...args: Args): void;
|
|
37
56
|
cancel(): void;
|
|
@@ -169,4 +188,8 @@ export declare function listToTree<T extends Record<IdKey, TreeId> & Partial<Rec
|
|
|
169
188
|
export declare function findTreeNode<T extends object, ChildrenKey extends string = "children">(tree: readonly T[], predicate: (node: T) => boolean, options?: TreeOptions<ChildrenKey>): T | undefined;
|
|
170
189
|
/** 查找首个匹配节点从根到自身的路径,保留节点引用;未找到时返回 undefined。 */
|
|
171
190
|
export declare function findTreePath<T extends object, ChildrenKey extends string = "children">(tree: readonly T[], predicate: (node: T) => boolean, options?: TreeOptions<ChildrenKey>): T[] | undefined;
|
|
191
|
+
/** 保留匹配节点及其祖先;逐个判断原节点,浅拷贝结果并重建子节点数组。 */
|
|
192
|
+
export declare function filterTree<T extends object, ChildrenKey extends string = "children">(tree: readonly T[], predicate: (node: T) => boolean, options?: TreeOptions<ChildrenKey>): TreeNode<T, ChildrenKey>[];
|
|
193
|
+
/** 按深度优先前序映射原节点;浅拷贝映射结果,保留层级并重建子节点数组。 */
|
|
194
|
+
export declare function mapTree<T extends object, R extends object, ChildrenKey extends string = "children">(tree: readonly T[], mapper: (node: T) => R, options?: TreeOptions<ChildrenKey>): TreeNode<R, ChildrenKey>[];
|
|
172
195
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -85,6 +85,114 @@ async function download(source, filename) {
|
|
|
85
85
|
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
+
// src/file/index.ts
|
|
89
|
+
/** 将 File/Blob 异步读取为完整的 Base64 Data URL,需要 FileReader 支持。 */
|
|
90
|
+
async function fileToDataURL(file) {
|
|
91
|
+
if (typeof FileReader === "undefined") throw new Error("fileToDataURL requires FileReader support");
|
|
92
|
+
return new Promise((resolve, reject) => {
|
|
93
|
+
const reader = new FileReader();
|
|
94
|
+
reader.onload = () => resolve(reader.result);
|
|
95
|
+
reader.onerror = () => reject(reader.error ?? /* @__PURE__ */ new Error("Failed to read file"));
|
|
96
|
+
reader.onabort = () => reject(/* @__PURE__ */ new Error("File reading was aborted"));
|
|
97
|
+
reader.readAsDataURL(file);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/** 将完整 Data URL 同步还原为 Blob;支持 Base64 和百分号编码,保留 MIME 类型和字节。 */
|
|
101
|
+
function dataURLToBlob(dataURL) {
|
|
102
|
+
const url = new URL(dataURL);
|
|
103
|
+
if (url.protocol !== "data:") throw new TypeError("Invalid Data URL");
|
|
104
|
+
const value = url.href.slice(5).split("#", 1)[0];
|
|
105
|
+
const commaIndex = value.indexOf(",");
|
|
106
|
+
if (commaIndex < 0) throw new TypeError("Invalid Data URL: missing comma");
|
|
107
|
+
let type = value.slice(0, commaIndex).trim();
|
|
108
|
+
const data = value.slice(commaIndex + 1);
|
|
109
|
+
let bytes;
|
|
110
|
+
if (/;base64$/i.test(type)) {
|
|
111
|
+
type = type.slice(0, -7).trimEnd();
|
|
112
|
+
const binary = atob(decodeURIComponent(data));
|
|
113
|
+
bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
114
|
+
} else {
|
|
115
|
+
bytes = new TextEncoder().encode(data);
|
|
116
|
+
let length = 0;
|
|
117
|
+
for (let index = 0; index < bytes.length; index++) if (bytes[index] === 37) {
|
|
118
|
+
const hex = String.fromCharCode(...bytes.subarray(index + 1, index + 3));
|
|
119
|
+
if (!/^[\da-f]{2}$/i.test(hex)) throw new TypeError("Invalid Data URL: invalid percent encoding");
|
|
120
|
+
bytes[length++] = Number.parseInt(hex, 16);
|
|
121
|
+
index += 2;
|
|
122
|
+
} else bytes[length++] = bytes[index];
|
|
123
|
+
bytes = bytes.subarray(0, length);
|
|
124
|
+
}
|
|
125
|
+
if (!type) type = "text/plain;charset=US-ASCII";
|
|
126
|
+
else if (type.startsWith(";")) type = `text/plain${type}`;
|
|
127
|
+
return new Blob([bytes], { type });
|
|
128
|
+
}
|
|
129
|
+
// src/form-data/index.ts
|
|
130
|
+
function appendValue(formData, name, value) {
|
|
131
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") formData.append(name, String(value));
|
|
132
|
+
else formData.append(name, value);
|
|
133
|
+
}
|
|
134
|
+
function isFormDataArray(value) {
|
|
135
|
+
return Array.isArray(value);
|
|
136
|
+
}
|
|
137
|
+
/** 将字段追加到 FormData;数组按顺序生成重复键,null、undefined 和空数组会跳过。 */
|
|
138
|
+
function appendFormData(formData, fields) {
|
|
139
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
140
|
+
if (value === null || value === void 0) continue;
|
|
141
|
+
if (isFormDataArray(value)) for (const item of value) appendValue(formData, name, item);
|
|
142
|
+
else appendValue(formData, name, value);
|
|
143
|
+
}
|
|
144
|
+
return formData;
|
|
145
|
+
}
|
|
146
|
+
/** 根据普通字段对象创建 FormData。 */
|
|
147
|
+
function objectToFormData(fields) {
|
|
148
|
+
return appendFormData(new FormData(), fields);
|
|
149
|
+
}
|
|
150
|
+
// src/clipboard/index.ts
|
|
151
|
+
/**
|
|
152
|
+
* 复制文本;优先使用 Clipboard API,失败或不可用时回退到 execCommand。
|
|
153
|
+
*
|
|
154
|
+
* @returns 写入成功后完成;浏览器不支持或拒绝复制时抛错。
|
|
155
|
+
* @example
|
|
156
|
+
* await copyText('要复制的内容')
|
|
157
|
+
*/
|
|
158
|
+
async function copyText(text) {
|
|
159
|
+
if (typeof document === "undefined") throw new Error("copyText requires a browser environment");
|
|
160
|
+
try {
|
|
161
|
+
if (typeof navigator !== "undefined" && typeof navigator.clipboard?.writeText === "function") {
|
|
162
|
+
await navigator.clipboard.writeText(text);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
} catch {}
|
|
166
|
+
const body = document.body;
|
|
167
|
+
if (!body || typeof document.execCommand !== "function") throw new Error("Text copying is not supported in this environment");
|
|
168
|
+
const activeElement = document.activeElement;
|
|
169
|
+
const activeInput = activeElement?.tagName === "INPUT" || activeElement?.tagName === "TEXTAREA" ? activeElement : null;
|
|
170
|
+
const inputStart = activeInput?.selectionStart;
|
|
171
|
+
const inputEnd = activeInput?.selectionEnd;
|
|
172
|
+
const inputDirection = activeInput?.selectionDirection;
|
|
173
|
+
const selection = document.getSelection?.();
|
|
174
|
+
const ranges = [];
|
|
175
|
+
if (selection) for (let index = 0; index < selection.rangeCount; index++) ranges.push(selection.getRangeAt(index).cloneRange());
|
|
176
|
+
const textarea = document.createElement("textarea");
|
|
177
|
+
textarea.value = text;
|
|
178
|
+
textarea.readOnly = true;
|
|
179
|
+
textarea.style.cssText = "position:fixed;top:0;left:0;opacity:0;font-size:16px;pointer-events:none;";
|
|
180
|
+
try {
|
|
181
|
+
body.appendChild(textarea);
|
|
182
|
+
textarea.focus({ preventScroll: true });
|
|
183
|
+
textarea.select();
|
|
184
|
+
textarea.setSelectionRange(0, text.length);
|
|
185
|
+
if (!document.execCommand("copy")) throw new Error("Failed to copy text");
|
|
186
|
+
} finally {
|
|
187
|
+
if (textarea.parentNode) textarea.parentNode.removeChild(textarea);
|
|
188
|
+
activeElement?.focus?.({ preventScroll: true });
|
|
189
|
+
if (selection) {
|
|
190
|
+
selection.removeAllRanges();
|
|
191
|
+
for (const range of ranges) selection.addRange(range);
|
|
192
|
+
}
|
|
193
|
+
if (inputStart != null && inputEnd != null) activeInput?.setSelectionRange(inputStart, inputEnd, inputDirection ?? void 0);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
88
196
|
// src/debounce/index.ts
|
|
89
197
|
/**
|
|
90
198
|
* 防抖:停止调用 wait 毫秒后,使用最后一次调用的 this 和参数执行回调。
|
|
@@ -480,6 +588,56 @@ function findTreeNode(tree, predicate, options = {}) {
|
|
|
480
588
|
function findTreePath(tree, predicate, options = {}) {
|
|
481
589
|
for (const { node, path } of walkTree(tree, options.childrenKey ?? "children")) if (predicate(node)) return [...path];
|
|
482
590
|
}
|
|
483
|
-
|
|
591
|
+
/** 保留匹配节点及其祖先;逐个判断原节点,浅拷贝结果并重建子节点数组。 */
|
|
592
|
+
function filterTree(tree, predicate, options = {}) {
|
|
593
|
+
const childrenKey = options.childrenKey ?? "children";
|
|
594
|
+
const filtered = [];
|
|
595
|
+
const stack = [{
|
|
596
|
+
iterator: tree[Symbol.iterator](),
|
|
597
|
+
children: filtered,
|
|
598
|
+
matches: false
|
|
599
|
+
}];
|
|
600
|
+
while (stack.length > 0) {
|
|
601
|
+
const frame = stack[stack.length - 1];
|
|
602
|
+
const result = frame.iterator.next();
|
|
603
|
+
if (result.done) {
|
|
604
|
+
stack.pop();
|
|
605
|
+
if (frame.item && (frame.matches || frame.children.length > 0)) stack[stack.length - 1].children.push(frame.item);
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
const node = result.value;
|
|
609
|
+
const matches = predicate(node);
|
|
610
|
+
const children = [];
|
|
611
|
+
const item = {
|
|
612
|
+
...node,
|
|
613
|
+
[childrenKey]: children
|
|
614
|
+
};
|
|
615
|
+
const sourceChildren = node[childrenKey];
|
|
616
|
+
stack.push({
|
|
617
|
+
iterator: (sourceChildren ?? [])[Symbol.iterator](),
|
|
618
|
+
children,
|
|
619
|
+
item,
|
|
620
|
+
matches
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
return filtered;
|
|
624
|
+
}
|
|
625
|
+
/** 按深度优先前序映射原节点;浅拷贝映射结果,保留层级并重建子节点数组。 */
|
|
626
|
+
function mapTree(tree, mapper, options = {}) {
|
|
627
|
+
const childrenKey = options.childrenKey ?? "children";
|
|
628
|
+
const mapped = [];
|
|
629
|
+
const childrenByDepth = [mapped];
|
|
630
|
+
for (const { node, path } of walkTree(tree, childrenKey)) {
|
|
631
|
+
const children = [];
|
|
632
|
+
const item = {
|
|
633
|
+
...mapper(node),
|
|
634
|
+
[childrenKey]: children
|
|
635
|
+
};
|
|
636
|
+
childrenByDepth[path.length - 1].push(item);
|
|
637
|
+
childrenByDepth[path.length] = children;
|
|
638
|
+
}
|
|
639
|
+
return mapped;
|
|
640
|
+
}
|
|
641
|
+
export { appendFormData, copyText, dataURLToBlob, debounce, download, fileToDataURL, filterTree, findTreeNode, findTreePath, getCookie, getLocalStorage, getQueryParam, getQueryParams, getUrlHash, is, isAllEmpty, isArray, isBigInt, isBoolean, isDate, isDefined, isEmail, isEmpty, isFunction, isJSON, isMap, isNil, isNull, isNumber, isObject, isPhone, isPlainObject, isPromise, isRegExp, isSet, isString, isSymbol, isUndefined, isUrl, listToTree, mapTree, objectToFormData, parseQuery, randomId, removeCookie, removeLocalStorage, removeQueryParams, setCookie, setLocalStorage, setQueryParams, setUrlHash, stringifyQuery, throttle, treeToList, uuid };
|
|
484
642
|
|
|
485
643
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/uuid/index.ts","../src/download/index.ts","../src/debounce/index.ts","../src/throttle/index.ts","../src/is/index.ts","../src/storage/index.ts","../src/cookie/index.ts","../src/url/index.ts","../src/tree/index.ts"],"sourcesContent":["const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'\n\n/**\n * 生成 UUID v4,默认返回 32 个小写十六进制字符。\n *\n * @param prefix 原样拼接在 UUID 前的前缀,分隔符需自行提供。\n * @param hyphens 是否保留标准 UUID 中的横杠,默认 false。\n * @example\n * uuid()\n * uuid('', true)\n * uuid('user_')\n * uuid('user_', true)\n */\nexport function uuid(prefix: string = '', hyphens: boolean = false): string {\n const crypto = globalThis.crypto\n let value: string\n\n if (typeof crypto?.randomUUID === 'function') {\n value = crypto.randomUUID()\n } else {\n if (typeof crypto?.getRandomValues !== 'function') {\n throw new Error('uuid requires crypto.randomUUID or crypto.getRandomValues')\n }\n\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n // 设置 UUID v4 的版本位和 RFC 变体位,保留其余随机位。\n bytes[6] = (bytes[6]! & 0x0f) | 0x40\n bytes[8] = (bytes[8]! & 0x3f) | 0x80\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')\n value = `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n }\n\n return prefix + (hyphens ? value : value.replace(/-/g, ''))\n}\n\n/**\n * 使用加密安全随机数生成 ID;自定义长度的 ID 不遵循 UUID 格式。\n * 字符表依次为 0–9、a–z、A–Z,使用前 radix 个字符。\n *\n * @param length 字符数量,必须为非负安全整数,默认 32;0 返回空字符串。\n * @param radix 基数,必须为 2–62 的整数,默认 16。\n * @example\n * randomId(8, 10)\n * randomId(21, 62)\n */\nexport function randomId(length: number = 32, radix: number = 16): string {\n if (!Number.isSafeInteger(length) || length < 0) {\n throw new RangeError('length must be a non-negative safe integer')\n }\n if (!Number.isInteger(radix) || radix < 2 || radix > ALPHABET.length) {\n throw new RangeError('radix must be an integer between 2 and 62')\n }\n if (length === 0) return ''\n\n const bytes = new Uint8Array(Math.min(length, 65_536))\n const limit = 256 - (256 % radix)\n let value = ''\n\n while (value.length < length) {\n globalThis.crypto.getRandomValues(bytes)\n for (const byte of bytes) {\n // 丢弃无法均分到字符表的尾部区间,避免取模导致分布偏差。\n if (byte >= limit) continue\n value += ALPHABET.charAt(byte % radix)\n if (value.length === length) break\n }\n }\n\n return value\n}\n","/**\n * 在浏览器中下载 URL、完整 Data URL 或 Blob/File。\n *\n * @param source 文件地址或文件内容;字符串始终作为地址处理。\n * @param filename 建议保存的完整文件名,包含扩展名。\n * @returns 触发下载后完成,不代表文件已保存到磁盘。\n * @example\n * await download('/files/report.pdf', 'report.pdf')\n * await download(new Blob(['hello'], { type: 'text/plain' }), 'hello.txt')\n */\nexport async function download(source: string | Blob, filename: string): Promise<void> {\n if (typeof document === 'undefined' || !document.body) {\n throw new Error('download requires a browser environment')\n }\n\n let blob: Blob\n if (typeof source === 'string') {\n const response = await fetch(source)\n if (!response.ok) {\n throw new Error(`Download failed: HTTP ${response.status}`)\n }\n blob = await response.blob()\n } else {\n blob = source\n }\n\n const anchor = document.createElement('a')\n const url = URL.createObjectURL(blob)\n try {\n anchor.href = url\n anchor.download = filename\n anchor.hidden = true\n document.body.append(anchor)\n anchor.click()\n } finally {\n anchor.remove()\n // 留出浏览器开始读取文件的时间,避免同步释放导致下载失败。\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n }\n}\n","export interface DebouncedFunction<This, Args extends unknown[]> {\n (this: This, ...args: Args): void\n cancel(): void\n}\n\n/**\n * 防抖:停止调用 wait 毫秒后,使用最后一次调用的 this 和参数执行回调。\n *\n * @param fn 要延迟执行的回调。\n * @param wait 等待时间,单位毫秒,遵循原生 setTimeout 的时间规则。\n * @returns 带有 cancel() 方法的防抖函数;cancel() 取消尚未执行的回调。\n * @example\n * const search = debounce((keyword: string) => console.log(keyword), 300)\n * search('hello')\n * search.cancel()\n */\nexport function debounce<This, Args extends unknown[]>(\n fn: (this: This, ...args: Args) => unknown,\n wait: number,\n): DebouncedFunction<This, Args> {\n let timer: ReturnType<typeof setTimeout> | undefined\n\n function debounced(this: This, ...args: Args): void {\n if (timer !== undefined) clearTimeout(timer)\n timer = setTimeout(() => {\n timer = undefined\n fn.apply(this, args)\n }, wait)\n }\n\n debounced.cancel = (): void => {\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n }\n\n return debounced\n}\n","export interface ThrottledFunction<This, Args extends unknown[]> {\n (this: This, ...args: Args): void\n cancel(): void\n}\n\n/**\n * 节流:首次调用立即执行,间隔内的调用在 wait 毫秒后以最后一次的 this 和参数执行。\n * 持续调用时,每隔 wait 毫秒执行一次;仅调用一次时不会重复执行。\n *\n * @param fn 要限制执行频率的回调。\n * @param wait 执行间隔,单位毫秒,遵循原生 setTimeout 的时间规则。\n * @returns 带有 cancel() 方法的节流函数;cancel() 取消待执行回调并重置间隔。\n * @example\n * const update = throttle((position: number) => console.log(position), 100)\n * update(10)\n * update.cancel()\n */\nexport function throttle<This, Args extends unknown[]>(\n fn: (this: This, ...args: Args) => unknown,\n wait: number,\n): ThrottledFunction<This, Args> {\n let timer: ReturnType<typeof setTimeout> | undefined\n let pending: { thisArg: This; args: Args } | undefined\n\n function onTimeout(): void {\n timer = undefined\n if (!pending) return\n\n const { thisArg, args } = pending\n pending = undefined\n // 在执行用户回调前建立下一轮间隔,支持回调内再次调用或取消。\n timer = setTimeout(onTimeout, wait)\n fn.apply(thisArg, args)\n }\n\n function throttled(this: This, ...args: Args): void {\n if (timer === undefined) {\n timer = setTimeout(onTimeout, wait)\n fn.apply(this, args)\n } else {\n pending = { thisArg: this, args }\n }\n }\n\n throttled.cancel = (): void => {\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n pending = undefined\n }\n\n return throttled\n}\n","/** 按 Object.prototype.toString 的标签判断类型,如 'Date'、'Map';区分大小写。 */\nexport function is(value: unknown, type: string): boolean {\n return Object.prototype.toString.call(value) === `[object ${type}]`\n}\n\n/** 仅判断原始字符串,不包含 String 包装对象。 */\nexport function isString(value: unknown): value is string {\n return typeof value === 'string'\n}\n\n/** 判断原始 number,包含 NaN 和 Infinity,不进行数值有效性检查。 */\nexport function isNumber(value: unknown): value is number {\n return typeof value === 'number'\n}\n\n/** 仅判断原始布尔值。 */\nexport function isBoolean(value: unknown): value is boolean {\n return typeof value === 'boolean'\n}\n\nexport function isBigInt(value: unknown): value is bigint {\n return typeof value === 'bigint'\n}\n\nexport function isSymbol(value: unknown): value is symbol {\n return typeof value === 'symbol'\n}\n\nexport function isUndefined(value: unknown): value is undefined {\n return value === undefined\n}\n\n/** 排除 undefined,保留 null。 */\nexport function isDefined<T>(value: T): value is Exclude<T, undefined> {\n return value !== undefined\n}\n\nexport function isNull(value: unknown): value is null {\n return value === null\n}\n\n/** 判断 null 或 undefined。 */\nexport function isNil(value: unknown): value is null | undefined {\n return value === null || value === undefined\n}\n\n/** 判断非 null 的对象,包含数组、内置对象和类实例,不包含函数。 */\nexport function isObject(value: unknown): value is object {\n return value !== null && typeof value === 'object'\n}\n\n/** 判断普通对象,支持对象字面量、跨 realm 对象和 Object.create(null)。 */\nexport function isPlainObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (!is(value, 'Object')) return false\n const prototype = Object.getPrototypeOf(value)\n if (prototype === null) return true\n const constructor = Object.hasOwn(prototype, 'constructor') && prototype.constructor\n return (\n typeof constructor === 'function' &&\n Function.prototype.toString.call(constructor) === Function.prototype.toString.call(Object)\n )\n}\n\n/** 判断 Date 类型,包含 Invalid Date。 */\nexport function isDate(value: unknown): value is Date {\n return is(value, 'Date')\n}\n\nexport function isRegExp(value: unknown): value is RegExp {\n return is(value, 'RegExp')\n}\n\n/** 包含普通函数、异步函数和生成器函数;不检查参数或返回值类型。 */\nexport function isFunction(value: unknown): value is (...args: never[]) => unknown {\n return typeof value === 'function'\n}\n\n/** 仅判断数组,不检查元素类型。 */\nexport function isArray(value: unknown): value is unknown[] {\n return Array.isArray(value)\n}\n\nexport function isMap(value: unknown): value is Map<unknown, unknown> {\n return is(value, 'Map')\n}\n\nexport function isSet(value: unknown): value is Set<unknown> {\n return is(value, 'Set')\n}\n\n/** 判断 Promise 或具有可调用 then 方法的对象/函数,收窄为 PromiseLike。 */\nexport function isPromise(value: unknown): value is PromiseLike<unknown> {\n return (isObject(value) || isFunction(value)) && 'then' in value && isFunction(value.then)\n}\n\n/** 判断可被 JSON.parse 解析的字符串,包含 JSON 原始值。 */\nexport function isJSON(value: unknown): boolean {\n if (!isString(value)) return false\n try {\n JSON.parse(value)\n return true\n } catch {\n return false\n }\n}\n\n/** 判断空字符串、数组、Map、Set 或无自有可枚举属性的普通对象。 */\nexport function isEmpty(value: unknown): boolean {\n if (isString(value) || isArray(value)) return value.length === 0\n if (isMap(value) || isSet(value)) return value.size === 0\n if (isPlainObject(value)) {\n return !Reflect.ownKeys(value).some((key) =>\n Object.prototype.propertyIsEnumerable.call(value, key),\n )\n }\n return false\n}\n\n/** 在 isEmpty 的基础上,将 null、undefined 也视为空。 */\nexport function isAllEmpty(value: unknown): boolean {\n return isNil(value) || isEmpty(value)\n}\n\n/** 判断绝对 HTTP(S) URL,拒绝空白字符、反斜杠和相对地址。 */\nexport function isUrl(value: unknown): boolean {\n if (!isString(value) || !/^https?:\\/\\//i.test(value) || /[\\s\\\\]/.test(value)) return false\n try {\n const url = new URL(value)\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.hostname !== ''\n } catch {\n return false\n }\n}\n\n/** 常用邮箱格式校验,不验证邮箱是否存在,不覆盖完整 RFC 邮箱语法。 */\nexport function isEmail(value: unknown): boolean {\n return (\n isString(value) &&\n /^[a-z\\d!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z\\d!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z\\d](?:[a-z\\d-]*[a-z\\d])?\\.)+[a-z]{2,}$/i.test(\n value,\n )\n )\n}\n\n/** 中国大陆 11 位手机号格式:以 1 开头,第二位为 3–9;不验证号段分配或号码存在性。 */\nexport function isPhone(value: unknown): boolean {\n return isString(value) && /^1[3-9]\\d{9}$/.test(value)\n}\n","function getStorage(): Storage {\n if (typeof window === 'undefined') {\n throw new Error('localStorage helpers require a browser environment')\n }\n return window.localStorage\n}\n\n/** 读取 JSON 值;不存在时返回 null。T 仅提供类型提示,不校验实际数据。 */\nexport function getLocalStorage<T = unknown>(key: string): T | null {\n const value = getStorage().getItem(key)\n return value === null ? null : (JSON.parse(value) as T)\n}\n\n/** 按 JSON.stringify 的规则写入值;无法序列化时抛错,不覆盖已有数据。 */\nexport function setLocalStorage(key: string, value: unknown): void {\n const storage = getStorage()\n const serialized = JSON.stringify(value)\n if (serialized === undefined) {\n throw new TypeError('The value cannot be serialized as JSON')\n }\n storage.setItem(key, serialized)\n}\n\n/** 删除指定键;键不存在时不做任何操作。 */\nexport function removeLocalStorage(key: string): void {\n getStorage().removeItem(key)\n}\n","export interface CookieOptions {\n /** 默认 /。删除 cookie 时需使用与写入时一致的路径。 */\n path?: string\n /** 省略时使用当前主机。 */\n domain?: string\n /** 绝对过期时间;与 maxAge 同时设置时,maxAge 优先。 */\n expires?: Date\n /** 有效期,单位为秒,必须是有限整数。 */\n maxAge?: number\n secure?: boolean\n sameSite?: 'strict' | 'lax' | 'none'\n}\n\nfunction getCookieDocument(): Document {\n if (typeof document === 'undefined') {\n throw new Error('cookie helpers require a browser environment')\n }\n return document\n}\n\n/** 读取并解码 cookie;不存在时返回 null,非法百分号编码保留原值。 */\nexport function getCookie(name: string): string | null {\n const prefix = `${encodeURIComponent(name)}=`\n for (const cookie of getCookieDocument().cookie.split(';')) {\n const entry = cookie.trim()\n if (!entry.startsWith(prefix)) continue\n const value = entry.slice(prefix.length)\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n }\n return null\n}\n\n/** 编码并写入字符串 cookie;省略有效期时创建会话 cookie。 */\nexport function setCookie(name: string, value: string, options: CookieOptions = {}): void {\n const cookieDocument = getCookieDocument()\n const { path = '/', domain, expires, maxAge, secure, sameSite } = options\n const attributes = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`]\n // 路径和域名直接用于属性值,禁止分隔符与控制字符注入其他属性。\n for (const attribute of [path, domain]) {\n // oxlint-disable-next-line no-control-regex -- 此处用于拒绝控制字符。\n if (attribute !== undefined && /[;\\u0000-\\u001f\\u007f]/.test(attribute)) {\n throw new TypeError('Cookie path and domain cannot contain semicolons or control characters')\n }\n }\n attributes.push(`Path=${path}`)\n if (domain !== undefined) attributes.push(`Domain=${domain}`)\n if (expires !== undefined) {\n if (!Number.isFinite(expires.getTime())) throw new TypeError('Invalid cookie expiration date')\n attributes.push(`Expires=${expires.toUTCString()}`)\n }\n if (maxAge !== undefined) {\n if (!Number.isInteger(maxAge)) throw new TypeError('Cookie maxAge must be a finite integer')\n attributes.push(`Max-Age=${maxAge}`)\n }\n if (secure) attributes.push('Secure')\n if (sameSite !== undefined) attributes.push(`SameSite=${sameSite}`)\n cookieDocument.cookie = attributes.join('; ')\n}\n\n/** 通过 Max-Age=0 删除 cookie;path 和 domain 需与写入时一致。 */\nexport function removeCookie(\n name: string,\n options: Pick<CookieOptions, 'path' | 'domain'> = {},\n): void {\n setCookie(name, '', { ...options, maxAge: 0 })\n}\n","export type QueryValue = string | number | boolean\n\nexport type QueryParams = Record<string, QueryValue | readonly QueryValue[] | null | undefined>\n\nexport type ParsedQuery = Record<string, string | string[]>\n\nfunction splitUrl(url: string): { path: string; query: string; hash: string } {\n const hashIndex = url.indexOf('#')\n const hash = hashIndex < 0 ? '' : url.slice(hashIndex)\n const beforeHash = hashIndex < 0 ? url : url.slice(0, hashIndex)\n const queryIndex = beforeHash.indexOf('?')\n return {\n path: queryIndex < 0 ? beforeHash : beforeHash.slice(0, queryIndex),\n query: queryIndex < 0 ? '' : beforeHash.slice(queryIndex),\n hash,\n }\n}\n\nfunction appendQueryValue(\n search: URLSearchParams,\n name: string,\n value: QueryValue | readonly QueryValue[],\n): void {\n for (const item of Array.isArray(value) ? value : [value]) {\n search.append(name, String(item))\n }\n}\n\n/** 解析查询串(可带前导 ?);重复键返回数组,所有值保持字符串。 */\nexport function parseQuery(query: string): ParsedQuery {\n const entries = new Map<string, string | string[]>()\n for (const [name, value] of new URLSearchParams(query)) {\n const previous = entries.get(name)\n if (previous === undefined) entries.set(name, value)\n else if (typeof previous === 'string') entries.set(name, [previous, value])\n else previous.push(value)\n }\n return Object.fromEntries(entries)\n}\n\n/** 序列化为不带 ? 的查询串;数组使用重复键,省略 null、undefined 和空数组。 */\nexport function stringifyQuery(params: QueryParams): string {\n const search = new URLSearchParams()\n for (const [name, value] of Object.entries(params)) {\n if (value !== null && value !== undefined) appendQueryValue(search, name, value)\n }\n return search.toString()\n}\n\n/** 读取 URL 中第一个同名查询参数;不存在时返回 null,不读取 hash 内的参数。 */\nexport function getQueryParam(url: string, name: string): string | null {\n return new URLSearchParams(splitUrl(url).query).get(name)\n}\n\n/** 读取 URL 的全部查询参数,重复键返回数组。 */\nexport function getQueryParams(url: string): ParsedQuery {\n return parseQuery(splitUrl(url).query)\n}\n\n/** 合并查询参数并返回新 URL;null 或空数组删除该键,undefined 保留原值。 */\nexport function setQueryParams(url: string, params: QueryParams): string {\n const { path, query, hash } = splitUrl(url)\n const search = new URLSearchParams(query)\n for (const [name, value] of Object.entries(params)) {\n if (value === undefined) continue\n search.delete(name)\n if (value !== null) appendQueryValue(search, name, value)\n }\n const serialized = search.toString()\n return path + (serialized ? `?${serialized}` : '') + hash\n}\n\n/** 删除一个或多个查询参数的全部同名项,返回新 URL。 */\nexport function removeQueryParams(url: string, names: string | readonly string[]): string {\n const params = Object.fromEntries(\n (typeof names === 'string' ? [names] : names).map((name) => [name, null]),\n )\n return setQueryParams(url, params)\n}\n\n/** 获取第一个 # 后的原始 hash,不含 #,不解码;不存在时返回空字符串。 */\nexport function getUrlHash(url: string): string {\n return splitUrl(url).hash.slice(1)\n}\n\n/** 设置原始 hash(可带一个前导 #),空字符串移除 hash;不进行编码。 */\nexport function setUrlHash(url: string, hash: string): string {\n const hashIndex = url.indexOf('#')\n const beforeHash = hashIndex < 0 ? url : url.slice(0, hashIndex)\n const value = hash.startsWith('#') ? hash.slice(1) : hash\n return beforeHash + (value ? `#${value}` : '')\n}\n","export type TreeId = string | number\n\n/** 保留业务字段,并将指定子节点字段替换为递归节点数组。 */\nexport type TreeNode<T, ChildrenKey extends string = 'children'> = Omit<T, ChildrenKey> & {\n [Key in ChildrenKey]: TreeNode<T, ChildrenKey>[]\n}\n\nexport interface TreeOptions<ChildrenKey extends string = 'children'> {\n childrenKey?: ChildrenKey\n}\n\nexport interface ListToTreeOptions<\n IdKey extends string = 'id',\n ParentIdKey extends string = 'parentId',\n ChildrenKey extends string = 'children',\n> extends TreeOptions<ChildrenKey> {\n idKey?: IdKey\n parentIdKey?: ParentIdKey\n}\n\nfunction* walkTree<T extends object>(\n tree: readonly T[],\n childrenKey: string,\n): Generator<{ node: T; path: readonly T[] }> {\n const stack = [tree[Symbol.iterator]()]\n const path: T[] = []\n\n while (stack.length > 0) {\n const iterator = stack[stack.length - 1]\n if (!iterator) break\n const result = iterator.next()\n if (result.done) {\n stack.pop()\n path.pop()\n continue\n }\n\n const node = result.value\n path.push(node)\n yield { node, path }\n const children = (node as Record<string, readonly T[] | null | undefined>)[childrenKey]\n if (children?.length) stack.push(children[Symbol.iterator]())\n else path.pop()\n }\n}\n\n/** 按深度优先前序展开树;浅拷贝节点并移除子节点字段,不补充 parentId。 */\nexport function treeToList<T extends object, ChildrenKey extends string = 'children'>(\n tree: readonly T[],\n options: TreeOptions<ChildrenKey> = {},\n): Omit<T, ChildrenKey>[] {\n const childrenKey = options.childrenKey ?? 'children'\n const list: Omit<T, ChildrenKey>[] = []\n for (const { node } of walkTree(tree, childrenKey)) {\n const item = { ...node }\n delete (item as Record<string, unknown>)[childrenKey]\n list.push(item)\n }\n return list\n}\n\n/** 列表转树;父 ID 为空或不存在时作为根节点,重复 ID 和循环关系抛错。 */\nexport function listToTree<\n T extends Record<IdKey, TreeId> & Partial<Record<ParentIdKey, TreeId | null | undefined>>,\n IdKey extends string = 'id',\n ParentIdKey extends string = 'parentId',\n ChildrenKey extends string = 'children',\n>(\n list: readonly T[],\n options: ListToTreeOptions<IdKey, ParentIdKey, ChildrenKey> = {},\n): TreeNode<T, ChildrenKey>[] {\n const idKey = options.idKey ?? ('id' as IdKey)\n const parentIdKey = options.parentIdKey ?? ('parentId' as ParentIdKey)\n const childrenKey = options.childrenKey ?? 'children'\n const nodes = new Map<\n TreeId,\n {\n node: TreeNode<T, ChildrenKey>\n children: TreeNode<T, ChildrenKey>[]\n parentId: TreeId | null | undefined\n }\n >()\n\n for (const item of list) {\n const id = item[idKey]\n if (nodes.has(id)) throw new Error(`Duplicate tree node ID: ${id}`)\n const children: TreeNode<T, ChildrenKey>[] = []\n const node = { ...item, [childrenKey]: children } as unknown as TreeNode<T, ChildrenKey>\n nodes.set(id, { node, children, parentId: item[parentIdKey] })\n }\n\n const roots: TreeNode<T, ChildrenKey>[] = []\n const visited = new Set<TreeId>()\n for (const [id, entry] of nodes) {\n // 每条父链只检查一次,避免深层列表退化为平方复杂度。\n const path = new Set<TreeId>()\n let current: TreeId | null | undefined = id\n while (current !== null && current !== undefined && !visited.has(current)) {\n if (path.has(current)) throw new Error(`Cycle detected in tree at ID: ${current}`)\n path.add(current)\n current = nodes.get(current)?.parentId\n }\n for (const ancestorId of path) visited.add(ancestorId)\n\n const parent = entry.parentId == null ? undefined : nodes.get(entry.parentId)\n if (parent) parent.children.push(entry.node)\n else roots.push(entry.node)\n }\n return roots\n}\n\n/** 按深度优先前序查找首个匹配节点,返回原引用;未找到时返回 undefined。 */\nexport function findTreeNode<T extends object, ChildrenKey extends string = 'children'>(\n tree: readonly T[],\n predicate: (node: T) => boolean,\n options: TreeOptions<ChildrenKey> = {},\n): T | undefined {\n for (const { node } of walkTree(tree, options.childrenKey ?? 'children')) {\n if (predicate(node)) return node\n }\n return undefined\n}\n\n/** 查找首个匹配节点从根到自身的路径,保留节点引用;未找到时返回 undefined。 */\nexport function findTreePath<T extends object, ChildrenKey extends string = 'children'>(\n tree: readonly T[],\n predicate: (node: T) => boolean,\n options: TreeOptions<ChildrenKey> = {},\n): T[] | undefined {\n for (const { node, path } of walkTree(tree, options.childrenKey ?? 'children')) {\n if (predicate(node)) return [...path]\n }\n return undefined\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/uuid/index.ts","../src/download/index.ts","../src/file/index.ts","../src/form-data/index.ts","../src/clipboard/index.ts","../src/debounce/index.ts","../src/throttle/index.ts","../src/is/index.ts","../src/storage/index.ts","../src/cookie/index.ts","../src/url/index.ts","../src/tree/index.ts"],"sourcesContent":["const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'\n\n/**\n * 生成 UUID v4,默认返回 32 个小写十六进制字符。\n *\n * @param prefix 原样拼接在 UUID 前的前缀,分隔符需自行提供。\n * @param hyphens 是否保留标准 UUID 中的横杠,默认 false。\n * @example\n * uuid()\n * uuid('', true)\n * uuid('user_')\n * uuid('user_', true)\n */\nexport function uuid(prefix: string = '', hyphens: boolean = false): string {\n const crypto = globalThis.crypto\n let value: string\n\n if (typeof crypto?.randomUUID === 'function') {\n value = crypto.randomUUID()\n } else {\n if (typeof crypto?.getRandomValues !== 'function') {\n throw new Error('uuid requires crypto.randomUUID or crypto.getRandomValues')\n }\n\n const bytes = new Uint8Array(16)\n crypto.getRandomValues(bytes)\n // 设置 UUID v4 的版本位和 RFC 变体位,保留其余随机位。\n bytes[6] = (bytes[6]! & 0x0f) | 0x40\n bytes[8] = (bytes[8]! & 0x3f) | 0x80\n const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('')\n value = `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`\n }\n\n return prefix + (hyphens ? value : value.replace(/-/g, ''))\n}\n\n/**\n * 使用加密安全随机数生成 ID;自定义长度的 ID 不遵循 UUID 格式。\n * 字符表依次为 0–9、a–z、A–Z,使用前 radix 个字符。\n *\n * @param length 字符数量,必须为非负安全整数,默认 32;0 返回空字符串。\n * @param radix 基数,必须为 2–62 的整数,默认 16。\n * @example\n * randomId(8, 10)\n * randomId(21, 62)\n */\nexport function randomId(length: number = 32, radix: number = 16): string {\n if (!Number.isSafeInteger(length) || length < 0) {\n throw new RangeError('length must be a non-negative safe integer')\n }\n if (!Number.isInteger(radix) || radix < 2 || radix > ALPHABET.length) {\n throw new RangeError('radix must be an integer between 2 and 62')\n }\n if (length === 0) return ''\n\n const bytes = new Uint8Array(Math.min(length, 65_536))\n const limit = 256 - (256 % radix)\n let value = ''\n\n while (value.length < length) {\n globalThis.crypto.getRandomValues(bytes)\n for (const byte of bytes) {\n // 丢弃无法均分到字符表的尾部区间,避免取模导致分布偏差。\n if (byte >= limit) continue\n value += ALPHABET.charAt(byte % radix)\n if (value.length === length) break\n }\n }\n\n return value\n}\n","/**\n * 在浏览器中下载 URL、完整 Data URL 或 Blob/File。\n *\n * @param source 文件地址或文件内容;字符串始终作为地址处理。\n * @param filename 建议保存的完整文件名,包含扩展名。\n * @returns 触发下载后完成,不代表文件已保存到磁盘。\n * @example\n * await download('/files/report.pdf', 'report.pdf')\n * await download(new Blob(['hello'], { type: 'text/plain' }), 'hello.txt')\n */\nexport async function download(source: string | Blob, filename: string): Promise<void> {\n if (typeof document === 'undefined' || !document.body) {\n throw new Error('download requires a browser environment')\n }\n\n let blob: Blob\n if (typeof source === 'string') {\n const response = await fetch(source)\n if (!response.ok) {\n throw new Error(`Download failed: HTTP ${response.status}`)\n }\n blob = await response.blob()\n } else {\n blob = source\n }\n\n const anchor = document.createElement('a')\n const url = URL.createObjectURL(blob)\n try {\n anchor.href = url\n anchor.download = filename\n anchor.hidden = true\n document.body.append(anchor)\n anchor.click()\n } finally {\n anchor.remove()\n // 留出浏览器开始读取文件的时间,避免同步释放导致下载失败。\n setTimeout(() => URL.revokeObjectURL(url), 1000)\n }\n}\n","/** 将 File/Blob 异步读取为完整的 Base64 Data URL,需要 FileReader 支持。 */\nexport async function fileToDataURL(file: Blob): Promise<string> {\n if (typeof FileReader === 'undefined') {\n throw new Error('fileToDataURL requires FileReader support')\n }\n\n return new Promise((resolve, reject) => {\n const reader = new FileReader()\n reader.onload = () => resolve(reader.result as string)\n reader.onerror = () => reject(reader.error ?? new Error('Failed to read file'))\n reader.onabort = () => reject(new Error('File reading was aborted'))\n reader.readAsDataURL(file)\n })\n}\n\n/** 将完整 Data URL 同步还原为 Blob;支持 Base64 和百分号编码,保留 MIME 类型和字节。 */\nexport function dataURLToBlob(dataURL: string): Blob {\n const url = new URL(dataURL)\n if (url.protocol !== 'data:') throw new TypeError('Invalid Data URL')\n\n // 保留查询串中的数据,排除 URL 的 fragment;内容中的 # 应写为 %23。\n const value = url.href.slice(5).split('#', 1)[0]!\n const commaIndex = value.indexOf(',')\n if (commaIndex < 0) throw new TypeError('Invalid Data URL: missing comma')\n let type = value.slice(0, commaIndex).trim()\n const data = value.slice(commaIndex + 1)\n let bytes: Uint8Array<ArrayBuffer>\n\n if (/;base64$/i.test(type)) {\n type = type.slice(0, -7).trimEnd()\n const binary = atob(decodeURIComponent(data))\n bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0))\n } else {\n bytes = new TextEncoder().encode(data)\n let length = 0\n for (let index = 0; index < bytes.length; index++) {\n if (bytes[index] === 37) {\n const hex = String.fromCharCode(...bytes.subarray(index + 1, index + 3))\n if (!/^[\\da-f]{2}$/i.test(hex))\n throw new TypeError('Invalid Data URL: invalid percent encoding')\n bytes[length++] = Number.parseInt(hex, 16)\n index += 2\n } else {\n bytes[length++] = bytes[index]!\n }\n }\n bytes = bytes.subarray(0, length)\n }\n\n if (!type) type = 'text/plain;charset=US-ASCII'\n else if (type.startsWith(';')) type = `text/plain${type}`\n return new Blob([bytes], { type })\n}\n","export type FormDataScalar = string | number | boolean | Blob\n\nexport type FormDataValue = FormDataScalar | readonly FormDataScalar[] | null | undefined\n\nexport type FormDataFields = Readonly<Record<string, FormDataValue>>\n\nfunction appendValue(formData: FormData, name: string, value: FormDataScalar): void {\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n formData.append(name, String(value))\n } else {\n formData.append(name, value)\n }\n}\n\nfunction isFormDataArray(value: FormDataValue): value is readonly FormDataScalar[] {\n return Array.isArray(value)\n}\n\n/** 将字段追加到 FormData;数组按顺序生成重复键,null、undefined 和空数组会跳过。 */\nexport function appendFormData(formData: FormData, fields: FormDataFields): FormData {\n for (const [name, value] of Object.entries(fields)) {\n if (value === null || value === undefined) continue\n\n if (isFormDataArray(value)) {\n for (const item of value) appendValue(formData, name, item)\n } else {\n appendValue(formData, name, value)\n }\n }\n\n return formData\n}\n\n/** 根据普通字段对象创建 FormData。 */\nexport function objectToFormData(fields: FormDataFields): FormData {\n return appendFormData(new FormData(), fields)\n}\n","/**\n * 复制文本;优先使用 Clipboard API,失败或不可用时回退到 execCommand。\n *\n * @returns 写入成功后完成;浏览器不支持或拒绝复制时抛错。\n * @example\n * await copyText('要复制的内容')\n */\nexport async function copyText(text: string): Promise<void> {\n if (typeof document === 'undefined') {\n throw new Error('copyText requires a browser environment')\n }\n\n try {\n if (typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function') {\n await navigator.clipboard.writeText(text)\n return\n }\n } catch {\n // 权限或安全策略可能拒绝 Clipboard API,继续尝试传统复制。\n }\n\n const body = document.body\n if (!body || typeof document.execCommand !== 'function') {\n throw new Error('Text copying is not supported in this environment')\n }\n\n const activeElement = document.activeElement as HTMLElement | null\n const activeInput =\n activeElement?.tagName === 'INPUT' || activeElement?.tagName === 'TEXTAREA'\n ? (activeElement as HTMLInputElement | HTMLTextAreaElement)\n : null\n const inputStart = activeInput?.selectionStart\n const inputEnd = activeInput?.selectionEnd\n const inputDirection = activeInput?.selectionDirection\n const selection = document.getSelection?.()\n const ranges: Range[] = []\n if (selection) {\n for (let index = 0; index < selection.rangeCount; index++) {\n ranges.push(selection.getRangeAt(index).cloneRange())\n }\n }\n\n const textarea = document.createElement('textarea')\n textarea.value = text\n textarea.readOnly = true\n // 保持元素可选中;固定定位防止滚动,16px 字体避免移动端聚焦时缩放。\n textarea.style.cssText =\n 'position:fixed;top:0;left:0;opacity:0;font-size:16px;pointer-events:none;'\n\n try {\n body.appendChild(textarea)\n textarea.focus({ preventScroll: true })\n textarea.select()\n textarea.setSelectionRange(0, text.length)\n if (!document.execCommand('copy')) {\n throw new Error('Failed to copy text')\n }\n } finally {\n if (textarea.parentNode) textarea.parentNode.removeChild(textarea)\n activeElement?.focus?.({ preventScroll: true })\n if (selection) {\n selection.removeAllRanges()\n for (const range of ranges) selection.addRange(range)\n }\n if (inputStart != null && inputEnd != null) {\n activeInput?.setSelectionRange(inputStart, inputEnd, inputDirection ?? undefined)\n }\n }\n}\n","export interface DebouncedFunction<This, Args extends unknown[]> {\n (this: This, ...args: Args): void\n cancel(): void\n}\n\n/**\n * 防抖:停止调用 wait 毫秒后,使用最后一次调用的 this 和参数执行回调。\n *\n * @param fn 要延迟执行的回调。\n * @param wait 等待时间,单位毫秒,遵循原生 setTimeout 的时间规则。\n * @returns 带有 cancel() 方法的防抖函数;cancel() 取消尚未执行的回调。\n * @example\n * const search = debounce((keyword: string) => console.log(keyword), 300)\n * search('hello')\n * search.cancel()\n */\nexport function debounce<This, Args extends unknown[]>(\n fn: (this: This, ...args: Args) => unknown,\n wait: number,\n): DebouncedFunction<This, Args> {\n let timer: ReturnType<typeof setTimeout> | undefined\n\n function debounced(this: This, ...args: Args): void {\n if (timer !== undefined) clearTimeout(timer)\n timer = setTimeout(() => {\n timer = undefined\n fn.apply(this, args)\n }, wait)\n }\n\n debounced.cancel = (): void => {\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n }\n\n return debounced\n}\n","export interface ThrottledFunction<This, Args extends unknown[]> {\n (this: This, ...args: Args): void\n cancel(): void\n}\n\n/**\n * 节流:首次调用立即执行,间隔内的调用在 wait 毫秒后以最后一次的 this 和参数执行。\n * 持续调用时,每隔 wait 毫秒执行一次;仅调用一次时不会重复执行。\n *\n * @param fn 要限制执行频率的回调。\n * @param wait 执行间隔,单位毫秒,遵循原生 setTimeout 的时间规则。\n * @returns 带有 cancel() 方法的节流函数;cancel() 取消待执行回调并重置间隔。\n * @example\n * const update = throttle((position: number) => console.log(position), 100)\n * update(10)\n * update.cancel()\n */\nexport function throttle<This, Args extends unknown[]>(\n fn: (this: This, ...args: Args) => unknown,\n wait: number,\n): ThrottledFunction<This, Args> {\n let timer: ReturnType<typeof setTimeout> | undefined\n let pending: { thisArg: This; args: Args } | undefined\n\n function onTimeout(): void {\n timer = undefined\n if (!pending) return\n\n const { thisArg, args } = pending\n pending = undefined\n // 在执行用户回调前建立下一轮间隔,支持回调内再次调用或取消。\n timer = setTimeout(onTimeout, wait)\n fn.apply(thisArg, args)\n }\n\n function throttled(this: This, ...args: Args): void {\n if (timer === undefined) {\n timer = setTimeout(onTimeout, wait)\n fn.apply(this, args)\n } else {\n pending = { thisArg: this, args }\n }\n }\n\n throttled.cancel = (): void => {\n if (timer !== undefined) clearTimeout(timer)\n timer = undefined\n pending = undefined\n }\n\n return throttled\n}\n","/** 按 Object.prototype.toString 的标签判断类型,如 'Date'、'Map';区分大小写。 */\nexport function is(value: unknown, type: string): boolean {\n return Object.prototype.toString.call(value) === `[object ${type}]`\n}\n\n/** 仅判断原始字符串,不包含 String 包装对象。 */\nexport function isString(value: unknown): value is string {\n return typeof value === 'string'\n}\n\n/** 判断原始 number,包含 NaN 和 Infinity,不进行数值有效性检查。 */\nexport function isNumber(value: unknown): value is number {\n return typeof value === 'number'\n}\n\n/** 仅判断原始布尔值。 */\nexport function isBoolean(value: unknown): value is boolean {\n return typeof value === 'boolean'\n}\n\nexport function isBigInt(value: unknown): value is bigint {\n return typeof value === 'bigint'\n}\n\nexport function isSymbol(value: unknown): value is symbol {\n return typeof value === 'symbol'\n}\n\nexport function isUndefined(value: unknown): value is undefined {\n return value === undefined\n}\n\n/** 排除 undefined,保留 null。 */\nexport function isDefined<T>(value: T): value is Exclude<T, undefined> {\n return value !== undefined\n}\n\nexport function isNull(value: unknown): value is null {\n return value === null\n}\n\n/** 判断 null 或 undefined。 */\nexport function isNil(value: unknown): value is null | undefined {\n return value === null || value === undefined\n}\n\n/** 判断非 null 的对象,包含数组、内置对象和类实例,不包含函数。 */\nexport function isObject(value: unknown): value is object {\n return value !== null && typeof value === 'object'\n}\n\n/** 判断普通对象,支持对象字面量、跨 realm 对象和 Object.create(null)。 */\nexport function isPlainObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (!is(value, 'Object')) return false\n const prototype = Object.getPrototypeOf(value)\n if (prototype === null) return true\n const constructor = Object.hasOwn(prototype, 'constructor') && prototype.constructor\n return (\n typeof constructor === 'function' &&\n Function.prototype.toString.call(constructor) === Function.prototype.toString.call(Object)\n )\n}\n\n/** 判断 Date 类型,包含 Invalid Date。 */\nexport function isDate(value: unknown): value is Date {\n return is(value, 'Date')\n}\n\nexport function isRegExp(value: unknown): value is RegExp {\n return is(value, 'RegExp')\n}\n\n/** 包含普通函数、异步函数和生成器函数;不检查参数或返回值类型。 */\nexport function isFunction(value: unknown): value is (...args: never[]) => unknown {\n return typeof value === 'function'\n}\n\n/** 仅判断数组,不检查元素类型。 */\nexport function isArray(value: unknown): value is unknown[] {\n return Array.isArray(value)\n}\n\nexport function isMap(value: unknown): value is Map<unknown, unknown> {\n return is(value, 'Map')\n}\n\nexport function isSet(value: unknown): value is Set<unknown> {\n return is(value, 'Set')\n}\n\n/** 判断 Promise 或具有可调用 then 方法的对象/函数,收窄为 PromiseLike。 */\nexport function isPromise(value: unknown): value is PromiseLike<unknown> {\n return (isObject(value) || isFunction(value)) && 'then' in value && isFunction(value.then)\n}\n\n/** 判断可被 JSON.parse 解析的字符串,包含 JSON 原始值。 */\nexport function isJSON(value: unknown): boolean {\n if (!isString(value)) return false\n try {\n JSON.parse(value)\n return true\n } catch {\n return false\n }\n}\n\n/** 判断空字符串、数组、Map、Set 或无自有可枚举属性的普通对象。 */\nexport function isEmpty(value: unknown): boolean {\n if (isString(value) || isArray(value)) return value.length === 0\n if (isMap(value) || isSet(value)) return value.size === 0\n if (isPlainObject(value)) {\n return !Reflect.ownKeys(value).some((key) =>\n Object.prototype.propertyIsEnumerable.call(value, key),\n )\n }\n return false\n}\n\n/** 在 isEmpty 的基础上,将 null、undefined 也视为空。 */\nexport function isAllEmpty(value: unknown): boolean {\n return isNil(value) || isEmpty(value)\n}\n\n/** 判断绝对 HTTP(S) URL,拒绝空白字符、反斜杠和相对地址。 */\nexport function isUrl(value: unknown): boolean {\n if (!isString(value) || !/^https?:\\/\\//i.test(value) || /[\\s\\\\]/.test(value)) return false\n try {\n const url = new URL(value)\n return (url.protocol === 'http:' || url.protocol === 'https:') && url.hostname !== ''\n } catch {\n return false\n }\n}\n\n/** 常用邮箱格式校验,不验证邮箱是否存在,不覆盖完整 RFC 邮箱语法。 */\nexport function isEmail(value: unknown): boolean {\n return (\n isString(value) &&\n /^[a-z\\d!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z\\d!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z\\d](?:[a-z\\d-]*[a-z\\d])?\\.)+[a-z]{2,}$/i.test(\n value,\n )\n )\n}\n\n/** 中国大陆 11 位手机号格式:以 1 开头,第二位为 3–9;不验证号段分配或号码存在性。 */\nexport function isPhone(value: unknown): boolean {\n return isString(value) && /^1[3-9]\\d{9}$/.test(value)\n}\n","function getStorage(): Storage {\n if (typeof window === 'undefined') {\n throw new Error('localStorage helpers require a browser environment')\n }\n return window.localStorage\n}\n\n/** 读取 JSON 值;不存在时返回 null。T 仅提供类型提示,不校验实际数据。 */\nexport function getLocalStorage<T = unknown>(key: string): T | null {\n const value = getStorage().getItem(key)\n return value === null ? null : (JSON.parse(value) as T)\n}\n\n/** 按 JSON.stringify 的规则写入值;无法序列化时抛错,不覆盖已有数据。 */\nexport function setLocalStorage(key: string, value: unknown): void {\n const storage = getStorage()\n const serialized = JSON.stringify(value)\n if (serialized === undefined) {\n throw new TypeError('The value cannot be serialized as JSON')\n }\n storage.setItem(key, serialized)\n}\n\n/** 删除指定键;键不存在时不做任何操作。 */\nexport function removeLocalStorage(key: string): void {\n getStorage().removeItem(key)\n}\n","export interface CookieOptions {\n /** 默认 /。删除 cookie 时需使用与写入时一致的路径。 */\n path?: string\n /** 省略时使用当前主机。 */\n domain?: string\n /** 绝对过期时间;与 maxAge 同时设置时,maxAge 优先。 */\n expires?: Date\n /** 有效期,单位为秒,必须是有限整数。 */\n maxAge?: number\n secure?: boolean\n sameSite?: 'strict' | 'lax' | 'none'\n}\n\nfunction getCookieDocument(): Document {\n if (typeof document === 'undefined') {\n throw new Error('cookie helpers require a browser environment')\n }\n return document\n}\n\n/** 读取并解码 cookie;不存在时返回 null,非法百分号编码保留原值。 */\nexport function getCookie(name: string): string | null {\n const prefix = `${encodeURIComponent(name)}=`\n for (const cookie of getCookieDocument().cookie.split(';')) {\n const entry = cookie.trim()\n if (!entry.startsWith(prefix)) continue\n const value = entry.slice(prefix.length)\n try {\n return decodeURIComponent(value)\n } catch {\n return value\n }\n }\n return null\n}\n\n/** 编码并写入字符串 cookie;省略有效期时创建会话 cookie。 */\nexport function setCookie(name: string, value: string, options: CookieOptions = {}): void {\n const cookieDocument = getCookieDocument()\n const { path = '/', domain, expires, maxAge, secure, sameSite } = options\n const attributes = [`${encodeURIComponent(name)}=${encodeURIComponent(value)}`]\n // 路径和域名直接用于属性值,禁止分隔符与控制字符注入其他属性。\n for (const attribute of [path, domain]) {\n // oxlint-disable-next-line no-control-regex -- 此处用于拒绝控制字符。\n if (attribute !== undefined && /[;\\u0000-\\u001f\\u007f]/.test(attribute)) {\n throw new TypeError('Cookie path and domain cannot contain semicolons or control characters')\n }\n }\n attributes.push(`Path=${path}`)\n if (domain !== undefined) attributes.push(`Domain=${domain}`)\n if (expires !== undefined) {\n if (!Number.isFinite(expires.getTime())) throw new TypeError('Invalid cookie expiration date')\n attributes.push(`Expires=${expires.toUTCString()}`)\n }\n if (maxAge !== undefined) {\n if (!Number.isInteger(maxAge)) throw new TypeError('Cookie maxAge must be a finite integer')\n attributes.push(`Max-Age=${maxAge}`)\n }\n if (secure) attributes.push('Secure')\n if (sameSite !== undefined) attributes.push(`SameSite=${sameSite}`)\n cookieDocument.cookie = attributes.join('; ')\n}\n\n/** 通过 Max-Age=0 删除 cookie;path 和 domain 需与写入时一致。 */\nexport function removeCookie(\n name: string,\n options: Pick<CookieOptions, 'path' | 'domain'> = {},\n): void {\n setCookie(name, '', { ...options, maxAge: 0 })\n}\n","export type QueryValue = string | number | boolean\n\nexport type QueryParams = Record<string, QueryValue | readonly QueryValue[] | null | undefined>\n\nexport type ParsedQuery = Record<string, string | string[]>\n\nfunction splitUrl(url: string): { path: string; query: string; hash: string } {\n const hashIndex = url.indexOf('#')\n const hash = hashIndex < 0 ? '' : url.slice(hashIndex)\n const beforeHash = hashIndex < 0 ? url : url.slice(0, hashIndex)\n const queryIndex = beforeHash.indexOf('?')\n return {\n path: queryIndex < 0 ? beforeHash : beforeHash.slice(0, queryIndex),\n query: queryIndex < 0 ? '' : beforeHash.slice(queryIndex),\n hash,\n }\n}\n\nfunction appendQueryValue(\n search: URLSearchParams,\n name: string,\n value: QueryValue | readonly QueryValue[],\n): void {\n for (const item of Array.isArray(value) ? value : [value]) {\n search.append(name, String(item))\n }\n}\n\n/** 解析查询串(可带前导 ?);重复键返回数组,所有值保持字符串。 */\nexport function parseQuery(query: string): ParsedQuery {\n const entries = new Map<string, string | string[]>()\n for (const [name, value] of new URLSearchParams(query)) {\n const previous = entries.get(name)\n if (previous === undefined) entries.set(name, value)\n else if (typeof previous === 'string') entries.set(name, [previous, value])\n else previous.push(value)\n }\n return Object.fromEntries(entries)\n}\n\n/** 序列化为不带 ? 的查询串;数组使用重复键,省略 null、undefined 和空数组。 */\nexport function stringifyQuery(params: QueryParams): string {\n const search = new URLSearchParams()\n for (const [name, value] of Object.entries(params)) {\n if (value !== null && value !== undefined) appendQueryValue(search, name, value)\n }\n return search.toString()\n}\n\n/** 读取 URL 中第一个同名查询参数;不存在时返回 null,不读取 hash 内的参数。 */\nexport function getQueryParam(url: string, name: string): string | null {\n return new URLSearchParams(splitUrl(url).query).get(name)\n}\n\n/** 读取 URL 的全部查询参数,重复键返回数组。 */\nexport function getQueryParams(url: string): ParsedQuery {\n return parseQuery(splitUrl(url).query)\n}\n\n/** 合并查询参数并返回新 URL;null 或空数组删除该键,undefined 保留原值。 */\nexport function setQueryParams(url: string, params: QueryParams): string {\n const { path, query, hash } = splitUrl(url)\n const search = new URLSearchParams(query)\n for (const [name, value] of Object.entries(params)) {\n if (value === undefined) continue\n search.delete(name)\n if (value !== null) appendQueryValue(search, name, value)\n }\n const serialized = search.toString()\n return path + (serialized ? `?${serialized}` : '') + hash\n}\n\n/** 删除一个或多个查询参数的全部同名项,返回新 URL。 */\nexport function removeQueryParams(url: string, names: string | readonly string[]): string {\n const params = Object.fromEntries(\n (typeof names === 'string' ? [names] : names).map((name) => [name, null]),\n )\n return setQueryParams(url, params)\n}\n\n/** 获取第一个 # 后的原始 hash,不含 #,不解码;不存在时返回空字符串。 */\nexport function getUrlHash(url: string): string {\n return splitUrl(url).hash.slice(1)\n}\n\n/** 设置原始 hash(可带一个前导 #),空字符串移除 hash;不进行编码。 */\nexport function setUrlHash(url: string, hash: string): string {\n const hashIndex = url.indexOf('#')\n const beforeHash = hashIndex < 0 ? url : url.slice(0, hashIndex)\n const value = hash.startsWith('#') ? hash.slice(1) : hash\n return beforeHash + (value ? `#${value}` : '')\n}\n","export type TreeId = string | number\n\n/** 保留业务字段,并将指定子节点字段替换为递归节点数组。 */\nexport type TreeNode<T, ChildrenKey extends string = 'children'> = Omit<T, ChildrenKey> & {\n [Key in ChildrenKey]: TreeNode<T, ChildrenKey>[]\n}\n\nexport interface TreeOptions<ChildrenKey extends string = 'children'> {\n childrenKey?: ChildrenKey\n}\n\nexport interface ListToTreeOptions<\n IdKey extends string = 'id',\n ParentIdKey extends string = 'parentId',\n ChildrenKey extends string = 'children',\n> extends TreeOptions<ChildrenKey> {\n idKey?: IdKey\n parentIdKey?: ParentIdKey\n}\n\nfunction* walkTree<T extends object>(\n tree: readonly T[],\n childrenKey: string,\n): Generator<{ node: T; path: readonly T[] }> {\n const stack = [tree[Symbol.iterator]()]\n const path: T[] = []\n\n while (stack.length > 0) {\n const iterator = stack[stack.length - 1]\n if (!iterator) break\n const result = iterator.next()\n if (result.done) {\n stack.pop()\n path.pop()\n continue\n }\n\n const node = result.value\n path.push(node)\n yield { node, path }\n const children = (node as Record<string, readonly T[] | null | undefined>)[childrenKey]\n if (children?.length) stack.push(children[Symbol.iterator]())\n else path.pop()\n }\n}\n\n/** 按深度优先前序展开树;浅拷贝节点并移除子节点字段,不补充 parentId。 */\nexport function treeToList<T extends object, ChildrenKey extends string = 'children'>(\n tree: readonly T[],\n options: TreeOptions<ChildrenKey> = {},\n): Omit<T, ChildrenKey>[] {\n const childrenKey = options.childrenKey ?? 'children'\n const list: Omit<T, ChildrenKey>[] = []\n for (const { node } of walkTree(tree, childrenKey)) {\n const item = { ...node }\n delete (item as Record<string, unknown>)[childrenKey]\n list.push(item)\n }\n return list\n}\n\n/** 列表转树;父 ID 为空或不存在时作为根节点,重复 ID 和循环关系抛错。 */\nexport function listToTree<\n T extends Record<IdKey, TreeId> & Partial<Record<ParentIdKey, TreeId | null | undefined>>,\n IdKey extends string = 'id',\n ParentIdKey extends string = 'parentId',\n ChildrenKey extends string = 'children',\n>(\n list: readonly T[],\n options: ListToTreeOptions<IdKey, ParentIdKey, ChildrenKey> = {},\n): TreeNode<T, ChildrenKey>[] {\n const idKey = options.idKey ?? ('id' as IdKey)\n const parentIdKey = options.parentIdKey ?? ('parentId' as ParentIdKey)\n const childrenKey = options.childrenKey ?? 'children'\n const nodes = new Map<\n TreeId,\n {\n node: TreeNode<T, ChildrenKey>\n children: TreeNode<T, ChildrenKey>[]\n parentId: TreeId | null | undefined\n }\n >()\n\n for (const item of list) {\n const id = item[idKey]\n if (nodes.has(id)) throw new Error(`Duplicate tree node ID: ${id}`)\n const children: TreeNode<T, ChildrenKey>[] = []\n const node = { ...item, [childrenKey]: children } as unknown as TreeNode<T, ChildrenKey>\n nodes.set(id, { node, children, parentId: item[parentIdKey] })\n }\n\n const roots: TreeNode<T, ChildrenKey>[] = []\n const visited = new Set<TreeId>()\n for (const [id, entry] of nodes) {\n // 每条父链只检查一次,避免深层列表退化为平方复杂度。\n const path = new Set<TreeId>()\n let current: TreeId | null | undefined = id\n while (current !== null && current !== undefined && !visited.has(current)) {\n if (path.has(current)) throw new Error(`Cycle detected in tree at ID: ${current}`)\n path.add(current)\n current = nodes.get(current)?.parentId\n }\n for (const ancestorId of path) visited.add(ancestorId)\n\n const parent = entry.parentId == null ? undefined : nodes.get(entry.parentId)\n if (parent) parent.children.push(entry.node)\n else roots.push(entry.node)\n }\n return roots\n}\n\n/** 按深度优先前序查找首个匹配节点,返回原引用;未找到时返回 undefined。 */\nexport function findTreeNode<T extends object, ChildrenKey extends string = 'children'>(\n tree: readonly T[],\n predicate: (node: T) => boolean,\n options: TreeOptions<ChildrenKey> = {},\n): T | undefined {\n for (const { node } of walkTree(tree, options.childrenKey ?? 'children')) {\n if (predicate(node)) return node\n }\n return undefined\n}\n\n/** 查找首个匹配节点从根到自身的路径,保留节点引用;未找到时返回 undefined。 */\nexport function findTreePath<T extends object, ChildrenKey extends string = 'children'>(\n tree: readonly T[],\n predicate: (node: T) => boolean,\n options: TreeOptions<ChildrenKey> = {},\n): T[] | undefined {\n for (const { node, path } of walkTree(tree, options.childrenKey ?? 'children')) {\n if (predicate(node)) return [...path]\n }\n return undefined\n}\n\n/** 保留匹配节点及其祖先;逐个判断原节点,浅拷贝结果并重建子节点数组。 */\nexport function filterTree<T extends object, ChildrenKey extends string = 'children'>(\n tree: readonly T[],\n predicate: (node: T) => boolean,\n options: TreeOptions<ChildrenKey> = {},\n): TreeNode<T, ChildrenKey>[] {\n const childrenKey = options.childrenKey ?? 'children'\n const filtered: TreeNode<T, ChildrenKey>[] = []\n const stack: {\n iterator: Iterator<T>\n children: TreeNode<T, ChildrenKey>[]\n item?: TreeNode<T, ChildrenKey>\n matches: boolean\n }[] = [{ iterator: tree[Symbol.iterator](), children: filtered, matches: false }]\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1]!\n const result = frame.iterator.next()\n if (result.done) {\n stack.pop()\n if (frame.item && (frame.matches || frame.children.length > 0)) {\n stack[stack.length - 1]!.children.push(frame.item)\n }\n continue\n }\n\n const node = result.value\n const matches = predicate(node)\n const children: TreeNode<T, ChildrenKey>[] = []\n const item = { ...node, [childrenKey]: children } as unknown as TreeNode<T, ChildrenKey>\n const sourceChildren = (node as Record<string, readonly T[] | null | undefined>)[childrenKey]\n stack.push({\n iterator: (sourceChildren ?? [])[Symbol.iterator](),\n children,\n item,\n matches,\n })\n }\n return filtered\n}\n\n/** 按深度优先前序映射原节点;浅拷贝映射结果,保留层级并重建子节点数组。 */\nexport function mapTree<\n T extends object,\n R extends object,\n ChildrenKey extends string = 'children',\n>(\n tree: readonly T[],\n mapper: (node: T) => R,\n options: TreeOptions<ChildrenKey> = {},\n): TreeNode<R, ChildrenKey>[] {\n const childrenKey = options.childrenKey ?? 'children'\n const mapped: TreeNode<R, ChildrenKey>[] = []\n const childrenByDepth = [mapped]\n\n for (const { node, path } of walkTree(tree, childrenKey)) {\n const children: TreeNode<R, ChildrenKey>[] = []\n const item = { ...mapper(node), [childrenKey]: children } as unknown as TreeNode<R, ChildrenKey>\n childrenByDepth[path.length - 1]!.push(item)\n childrenByDepth[path.length] = children\n }\n return mapped\n}\n"],"mappings":"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"}
|