@fast-china/utils 2.0.3 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +11 -1
  3. package/README.zh.md +12 -2
  4. package/dist/array/index.mjs +1 -1
  5. package/dist/array/index.mjs.map +1 -1
  6. package/dist/async/index.mjs +21 -19
  7. package/dist/async/index.mjs.map +1 -1
  8. package/dist/base64/index.d.mts +2 -2
  9. package/dist/base64/index.mjs +13 -12
  10. package/dist/base64/index.mjs.map +1 -1
  11. package/dist/color/index.mjs +4 -4
  12. package/dist/color/index.mjs.map +1 -1
  13. package/dist/crypto/index.d.mts +4 -3
  14. package/dist/crypto/index.mjs +38 -47
  15. package/dist/crypto/index.mjs.map +1 -1
  16. package/dist/date/index.mjs +3 -3
  17. package/dist/date/index.mjs.map +1 -1
  18. package/dist/dom/style.mjs +3 -3
  19. package/dist/dom/style.mjs.map +1 -1
  20. package/dist/env/index.d.mts +2 -2
  21. package/dist/env/index.mjs +12 -10
  22. package/dist/env/index.mjs.map +1 -1
  23. package/dist/identity/index.d.mts +6 -6
  24. package/dist/identity/index.mjs +8 -8
  25. package/dist/identity/index.mjs.map +1 -1
  26. package/dist/index.d.mts +3 -3
  27. package/dist/index.global.min.js +2 -2
  28. package/dist/index.global.min.js.map +1 -1
  29. package/dist/index.mjs +3 -3
  30. package/dist/internal/text.mjs +4 -5
  31. package/dist/internal/text.mjs.map +1 -1
  32. package/dist/logger/index.mjs +5 -6
  33. package/dist/logger/index.mjs.map +1 -1
  34. package/dist/number/index.d.mts +6 -5
  35. package/dist/number/index.mjs +22 -21
  36. package/dist/number/index.mjs.map +1 -1
  37. package/dist/object/index.mjs +1 -1
  38. package/dist/object/index.mjs.map +1 -1
  39. package/dist/storage/index.mjs +24 -25
  40. package/dist/storage/index.mjs.map +1 -1
  41. package/dist/string/index.d.mts +18 -6
  42. package/dist/string/index.mjs +84 -31
  43. package/dist/string/index.mjs.map +1 -1
  44. package/dist/vue/emits.mjs +2 -2
  45. package/dist/vue/emits.mjs.map +1 -1
  46. package/dist/vue/func.mjs +1 -1
  47. package/dist/vue/func.mjs.map +1 -1
  48. package/dist/vue/install.mjs +12 -12
  49. package/dist/vue/install.mjs.map +1 -1
  50. package/dist/vue/props.d.mts +1 -1
  51. package/dist/vue/props.mjs.map +1 -1
  52. package/dist/vue/render.mjs +1 -1
  53. package/dist/vue/render.mjs.map +1 -1
  54. package/docs/API.md +31 -15
  55. package/docs/API.zh-CN.md +22 -6
  56. package/docs/RUNTIME_CONTRACT.md +2 -2
  57. package/package.json +9 -9
package/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to Fast.Utils are documented in this file.
4
4
 
5
+ ## [2.1.1] - 2026-08-26
6
+
7
+ ### Changed
8
+
9
+ - Localized built-in validation, platform-capability, storage, cryptography, clipboard, and Vue integration error messages to Chinese while preserving their native error types and causes.
10
+ - Narrowed the `useProps` return type so keys passed through `ignoredProps` are excluded from the inferred computed result.
11
+ - Synchronized the self-contained ESLint Flat Config with the applicable Fast.ESLint.Config source rules and comments, refreshed compatible development dependencies, and documented the VS Code recommendations.
12
+
13
+ ## [2.1.0] - 2026-08-23
14
+
15
+ ### Added
16
+
17
+ - Restored the V1 `copy` text clipboard API for browsers and uni-app.
18
+
19
+ ### Changed
20
+
21
+ - Added `randomInt` and `randomString` as the random APIs.
22
+ - Standardized every random generation entry to prefer Web Crypto and fall back to `Math.random()` when unavailable.
23
+ - Accessed standard runtime capabilities directly through `globalThis` instead of maintaining asserted global-object views.
24
+
25
+ ### Breaking Changes
26
+
27
+ - Removed `secureRandomInt` and `secureRandomString`; use `randomInt` and `randomString` instead.
28
+
5
29
  ## [2.0.3] - 2026-08-11
6
30
 
7
31
  ### Changed
@@ -49,6 +73,8 @@ All notable changes to Fast.Utils are documented in this file.
49
73
 
50
74
  - Added authenticated ciphertext validation, bounded crypto parameters and payloads, unbiased Web Crypto randomness, prototype-safe query/object transforms, and namespace-scoped Storage cleanup.
51
75
 
76
+ [2.1.1]: https://github.com/China-xiaoFang/Fast.Utils/compare/v2.1.0...v2.1.1
77
+ [2.1.0]: https://github.com/China-xiaoFang/Fast.Utils/compare/v2.0.3...v2.1.0
52
78
  [2.0.3]: https://github.com/China-xiaoFang/Fast.Utils/releases/tag/v2.0.3
53
79
  [2.0.2]: https://github.com/China-xiaoFang/Fast.Utils/releases/tag/v2.0.2
54
80
  [2.0.1]: https://github.com/China-xiaoFang/Fast.Utils/releases/tag/v2.0.1
package/README.md CHANGED
@@ -69,6 +69,16 @@ encodeBase64Url("path/value");
69
69
 
70
70
  `encodeSecureBase64` and `decodeSecureBase64` exist only for the legacy dictionary format. Given the same default six-character prefix, valid legacy payloads remain byte-for-byte identical. A historical Base64-length 101–124 dictionary gap uses a one-character fallback that the legacy removal flow understands. This format is not encryption and must not protect passwords, tokens, or other secrets.
71
71
 
72
+ ## Copy text
73
+
74
+ ```ts
75
+ import { copy } from "@fast-china/utils";
76
+
77
+ await copy("Fast utilities");
78
+ ```
79
+
80
+ uni-app uses `setClipboardData`. Browsers prefer the Clipboard API and fall back to the legacy browser copy capability when it is unavailable. Unsupported platforms and denied clipboard access throw errors.
81
+
72
82
  ## Identity
73
83
 
74
84
  ```ts
@@ -79,7 +89,7 @@ configureInstallationIdentity({ cacheKey: "account:installation-id" });
79
89
  const installationId = getOrCreateInstallationId();
80
90
  ```
81
91
 
82
- Call `configureInstallationIdentity` in the application entry before first use. The default business key is `identity:installation-id`; repeated identical configuration is idempotent and conflicting configuration throws. Installation Identity uses the configured `Local` storage and Web Crypto UUID v4 generation. It never falls back to `Math.random()`.
92
+ Call `configureInstallationIdentity` in the application entry before first use. The default business key is `identity:installation-id`; repeated identical configuration is idempotent and conflicting configuration throws. Installation Identity UUID generation prefers Web Crypto and falls back to `Math.random()` when unavailable.
83
93
 
84
94
  ## Crypto
85
95
 
package/README.zh.md CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  - 提供完整类型、无副作用实现和统一具名导出入口,便于 Tree Shaking。
18
18
  - 明确支持浏览器、WebView、Vue 3 与 uni-app,导入阶段不访问平台全局对象。
19
- - 为 Storage、安装标识、编码、安全随机数与密码学能力划定清晰的安全边界。
19
+ - 为 Storage、安装标识、编码、随机数与密码学能力划定清晰的安全边界。
20
20
  - 使用 TypeScript 6 严格检查、ESLint、运行时测试、消费者类型测试、包契约与 Publint 共同验证。
21
21
 
22
22
  ## 安装
@@ -69,6 +69,16 @@ encodeBase64Url("path/参数");
69
69
 
70
70
  `encodeSecureBase64` / `decodeSecureBase64` 仅用于兼容旧字典格式。给定相同的默认 6 字符前缀时,有效旧载荷保持逐字符一致;历史字典无法自解码的 101–124 字符 Base64 区间使用旧删除流程可识别的单字符回退。它不是加密,不应用于密码、Token 或其他秘密。
71
71
 
72
+ ## 复制文本
73
+
74
+ ```ts
75
+ import { copy } from "@fast-china/utils";
76
+
77
+ await copy("Fast 工具库");
78
+ ```
79
+
80
+ uni-app 使用 `setClipboardData`;浏览器优先使用 Clipboard API,不可用时回退到旧版浏览器复制能力。平台不支持或拒绝访问剪贴板时会抛出错误。
81
+
72
82
  ## 安装实例标识
73
83
 
74
84
  ```ts
@@ -79,7 +89,7 @@ configureInstallationIdentity({ cacheKey: "account:installation-id" });
79
89
  const installationId = getOrCreateInstallationId();
80
90
  ```
81
91
 
82
- 在程序入口、首次使用安装标识前调用 `configureInstallationIdentity`。默认业务键是 `identity:installation-id`;相同配置可幂等重复调用,不同配置会抛错。安装标识使用已配置的 `Local` Web Crypto UUID v4,不回退 `Math.random()`。它不是硬件 ID、认证凭证或风控信号。
92
+ 在程序入口、首次使用安装标识前调用 `configureInstallationIdentity`。默认业务键是 `identity:installation-id`;相同配置可幂等重复调用,不同配置会抛错。安装标识 UUID 优先使用 Web Crypto 生成,能力缺失时回退到 `Math.random()`。它不是硬件 ID、认证凭证或风控信号。
83
93
 
84
94
  ## Crypto
85
95
 
@@ -9,7 +9,7 @@
9
9
  * @throws `RangeError` 当 `size` 不是正安全整数。
10
10
  */
11
11
  function chunk(items, size) {
12
- if (!Number.isSafeInteger(size) || size <= 0) throw new RangeError("size must be a positive safe integer.");
12
+ if (!Number.isSafeInteger(size) || size <= 0) throw new RangeError("`size` 必须是正安全整数。");
13
13
  const result = [];
14
14
  for (let index = 0; index < items.length; index += size) result.push(items.slice(index, index + size));
15
15
  return result;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/array/index.ts"],"sourcesContent":["/** 从数组项中提取可比较键的函数。 */\nexport type KeySelector<Item, Key> = (item: Item, index: number) => Key;\n\n/**\n * 将只读数组按固定大小分组。\n *\n * @typeParam Item - 数组项类型。\n * @param items - 不会被修改的输入数组。\n * @param size - 每组最多包含的项目数,必须是正安全整数。\n * @returns 新建的二维数组;最后一组可能小于 `size`。\n * @throws `RangeError` 当 `size` 不是正安全整数。\n */\nexport function chunk<Item>(items: readonly Item[], size: number): Item[][] {\n\tif (!Number.isSafeInteger(size) || size <= 0) {\n\t\tthrow new RangeError(\"size must be a positive safe integer.\");\n\t}\n\n\tconst result: Item[][] = [];\n\tfor (let index = 0; index < items.length; index += size) {\n\t\tresult.push(items.slice(index, index + size));\n\t}\n\treturn result;\n}\n\n/**\n * 删除数组中的 `null` 与 `undefined`,保留 `false`、`0` 和空字符串。\n *\n * @param items - 可包含空值的只读数组。\n * @returns 保持原顺序的新数组。\n */\nexport function removeNullishValues<Item>(items: readonly (Item | null | undefined)[]): Item[] {\n\treturn items.filter((item): item is Item => item !== null && item !== undefined);\n}\n\n/**\n * 使用 JavaScript `Set` 的 SameValueZero 语义去重。\n *\n * @param items - 不会被修改的输入数组。\n * @returns 保留每个值首次出现顺序的新数组;稀疏数组空位被忽略。\n */\nexport function unique<Item>(items: readonly Item[]): Item[] {\n\tconst result: Item[] = [];\n\tconst seen = new Set<Item>();\n\tfor (let index = 0; index < items.length; index += 1) {\n\t\tif (!(index in items)) continue;\n\t\tconst item = items[index] as Item;\n\t\tif (seen.has(item)) continue;\n\t\tseen.add(item);\n\t\tresult.push(item);\n\t}\n\treturn result;\n}\n\n/**\n * 按选择器返回的键去重。\n *\n * @param items - 不会被修改的输入数组。\n * @param selectKey - 接收项目与索引并返回去重键的函数。\n * @returns 保留每个键首次出现项目的新数组;稀疏数组空位被忽略。\n */\nexport function uniqueBy<Item, Key>(items: readonly Item[], selectKey: KeySelector<Item, Key>): Item[] {\n\tconst seen = new Set<Key>();\n\tconst result: Item[] = [];\n\tfor (let index = 0; index < items.length; index += 1) {\n\t\tconst item = items[index];\n\t\tif (item === undefined && !(index in items)) continue;\n\t\tconst key = selectKey(item as Item, index);\n\t\tif (seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tresult.push(item as Item);\n\t}\n\treturn result;\n}\n\n/**\n * 按选择器结果分组。\n *\n * @param items - 不会被修改的输入数组。\n * @param selectKey - 返回任意 `Map` 键的函数。\n * @returns 按键首次出现顺序排列的 `Map`;每个分组保持输入顺序,稀疏空位被忽略。\n */\nexport function groupBy<Item, Key>(items: readonly Item[], selectKey: KeySelector<Item, Key>): Map<Key, Item[]> {\n\tconst groups = new Map<Key, Item[]>();\n\titems.forEach((item, index) => {\n\t\tconst key = selectKey(item, index);\n\t\tconst group = groups.get(key);\n\t\tif (group === undefined) groups.set(key, [item]);\n\t\telse group.push(item);\n\t});\n\treturn groups;\n}\n\n/**\n * 按谓词把数组拆分为匹配项和非匹配项。\n *\n * @param items - 不会被修改的输入数组。\n * @param predicate - 接收项目与索引的判断函数。\n * @returns 二元组:第一项匹配谓词,第二项不匹配;两组都保持原顺序并忽略稀疏空位。\n */\nexport function partition<Item>(items: readonly Item[], predicate: (item: Item, index: number) => boolean): [matched: Item[], unmatched: Item[]] {\n\tconst matched: Item[] = [];\n\tconst unmatched: Item[] = [];\n\titems.forEach((item, index) => (predicate(item, index) ? matched : unmatched).push(item));\n\treturn [matched, unmatched];\n}\n\n/**\n * 返回只出现在左侧数组中的不同值。\n *\n * @param left - 主输入数组。\n * @param right - 需要排除的值。\n * @returns 保留左侧首次出现顺序的去重结果;两侧稀疏空位都被忽略。\n */\nexport function difference<Item>(left: readonly Item[], right: readonly Item[]): Item[] {\n\tconst excluded = new Set(unique(right));\n\treturn unique(left).filter((item) => !excluded.has(item));\n}\n\n/**\n * 返回两个数组共有的不同值。\n *\n * @param left - 决定结果顺序的数组。\n * @param right - 用于成员判断的数组。\n * @returns 保留左侧首次出现顺序的去重结果;两侧稀疏空位都被忽略。\n */\nexport function intersection<Item>(left: readonly Item[], right: readonly Item[]): Item[] {\n\tconst included = new Set(unique(right));\n\treturn unique(left).filter((item) => included.has(item));\n}\n\n/**\n * 判断选择器产生的键是否重复。\n *\n * @param items - 不会被修改的输入数组。\n * @param selectKey - 返回比较键的函数;键使用 SameValueZero 语义比较。\n * @returns 存在至少一个重复键时返回 `true`。\n */\nexport function hasDuplicatesBy<Item, Key>(items: readonly Item[], selectKey: KeySelector<Item, Key>): boolean {\n\tconst seen = new Set<Key>();\n\tfor (let index = 0; index < items.length; index += 1) {\n\t\tconst item = items[index];\n\t\tif (item === undefined && !(index in items)) continue;\n\t\tconst key = selectKey(item as Item, index);\n\t\tif (seen.has(key)) return true;\n\t\tseen.add(key);\n\t}\n\treturn false;\n}\n\n/**\n * 判断所有项目是否具有相同的选择器结果。\n *\n * @remarks 空数组、只有稀疏空位的数组和单项数组按数学惯例返回 `true`;空位不会调用选择器。\n * @param items - 不会被修改的输入数组。\n * @param selectKey - 返回比较键的函数。\n * @returns 所有键都满足 SameValueZero 相等时返回 `true`。\n */\nexport function allEqualBy<Item, Key>(items: readonly Item[], selectKey: KeySelector<Item, Key>): boolean {\n\tlet first: Key | undefined;\n\tlet hasFirst = false;\n\tfor (let index = 0; index < items.length; index += 1) {\n\t\tconst item = items[index];\n\t\tif (item === undefined && !(index in items)) continue;\n\t\tconst current = selectKey(item as Item, index);\n\t\tif (!hasFirst) {\n\t\t\tfirst = current;\n\t\t\thasFirst = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!(first === current || (Number.isNaN(first) && Number.isNaN(current)))) return false;\n\t}\n\treturn true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/array/index.ts"],"sourcesContent":["/** 从数组项中提取可比较键的函数。 */\nexport type KeySelector<Item, Key> = (item: Item, index: number) => Key;\n\n/**\n * 将只读数组按固定大小分组。\n *\n * @typeParam Item - 数组项类型。\n * @param items - 不会被修改的输入数组。\n * @param size - 每组最多包含的项目数,必须是正安全整数。\n * @returns 新建的二维数组;最后一组可能小于 `size`。\n * @throws `RangeError` 当 `size` 不是正安全整数。\n */\nexport function chunk<Item>(items: readonly Item[], size: number): Item[][] {\n\tif (!Number.isSafeInteger(size) || size <= 0) {\n\t\tthrow new RangeError(\"`size` 必须是正安全整数。\");\n\t}\n\n\tconst result: Item[][] = [];\n\tfor (let index = 0; index < items.length; index += size) {\n\t\tresult.push(items.slice(index, index + size));\n\t}\n\treturn result;\n}\n\n/**\n * 删除数组中的 `null` 与 `undefined`,保留 `false`、`0` 和空字符串。\n *\n * @param items - 可包含空值的只读数组。\n * @returns 保持原顺序的新数组。\n */\nexport function removeNullishValues<Item>(items: readonly (Item | null | undefined)[]): Item[] {\n\treturn items.filter((item): item is Item => item !== null && item !== undefined);\n}\n\n/**\n * 使用 JavaScript `Set` 的 SameValueZero 语义去重。\n *\n * @param items - 不会被修改的输入数组。\n * @returns 保留每个值首次出现顺序的新数组;稀疏数组空位被忽略。\n */\nexport function unique<Item>(items: readonly Item[]): Item[] {\n\tconst result: Item[] = [];\n\tconst seen = new Set<Item>();\n\tfor (let index = 0; index < items.length; index += 1) {\n\t\tif (!(index in items)) continue;\n\t\tconst item = items[index] as Item;\n\t\tif (seen.has(item)) continue;\n\t\tseen.add(item);\n\t\tresult.push(item);\n\t}\n\treturn result;\n}\n\n/**\n * 按选择器返回的键去重。\n *\n * @param items - 不会被修改的输入数组。\n * @param selectKey - 接收项目与索引并返回去重键的函数。\n * @returns 保留每个键首次出现项目的新数组;稀疏数组空位被忽略。\n */\nexport function uniqueBy<Item, Key>(items: readonly Item[], selectKey: KeySelector<Item, Key>): Item[] {\n\tconst seen = new Set<Key>();\n\tconst result: Item[] = [];\n\tfor (let index = 0; index < items.length; index += 1) {\n\t\tconst item = items[index];\n\t\tif (item === undefined && !(index in items)) continue;\n\t\tconst key = selectKey(item as Item, index);\n\t\tif (seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tresult.push(item as Item);\n\t}\n\treturn result;\n}\n\n/**\n * 按选择器结果分组。\n *\n * @param items - 不会被修改的输入数组。\n * @param selectKey - 返回任意 `Map` 键的函数。\n * @returns 按键首次出现顺序排列的 `Map`;每个分组保持输入顺序,稀疏空位被忽略。\n */\nexport function groupBy<Item, Key>(items: readonly Item[], selectKey: KeySelector<Item, Key>): Map<Key, Item[]> {\n\tconst groups = new Map<Key, Item[]>();\n\titems.forEach((item, index) => {\n\t\tconst key = selectKey(item, index);\n\t\tconst group = groups.get(key);\n\t\tif (group === undefined) groups.set(key, [item]);\n\t\telse group.push(item);\n\t});\n\treturn groups;\n}\n\n/**\n * 按谓词把数组拆分为匹配项和非匹配项。\n *\n * @param items - 不会被修改的输入数组。\n * @param predicate - 接收项目与索引的判断函数。\n * @returns 二元组:第一项匹配谓词,第二项不匹配;两组都保持原顺序并忽略稀疏空位。\n */\nexport function partition<Item>(items: readonly Item[], predicate: (item: Item, index: number) => boolean): [matched: Item[], unmatched: Item[]] {\n\tconst matched: Item[] = [];\n\tconst unmatched: Item[] = [];\n\titems.forEach((item, index) => (predicate(item, index) ? matched : unmatched).push(item));\n\treturn [matched, unmatched];\n}\n\n/**\n * 返回只出现在左侧数组中的不同值。\n *\n * @param left - 主输入数组。\n * @param right - 需要排除的值。\n * @returns 保留左侧首次出现顺序的去重结果;两侧稀疏空位都被忽略。\n */\nexport function difference<Item>(left: readonly Item[], right: readonly Item[]): Item[] {\n\tconst excluded = new Set(unique(right));\n\treturn unique(left).filter((item) => !excluded.has(item));\n}\n\n/**\n * 返回两个数组共有的不同值。\n *\n * @param left - 决定结果顺序的数组。\n * @param right - 用于成员判断的数组。\n * @returns 保留左侧首次出现顺序的去重结果;两侧稀疏空位都被忽略。\n */\nexport function intersection<Item>(left: readonly Item[], right: readonly Item[]): Item[] {\n\tconst included = new Set(unique(right));\n\treturn unique(left).filter((item) => included.has(item));\n}\n\n/**\n * 判断选择器产生的键是否重复。\n *\n * @param items - 不会被修改的输入数组。\n * @param selectKey - 返回比较键的函数;键使用 SameValueZero 语义比较。\n * @returns 存在至少一个重复键时返回 `true`。\n */\nexport function hasDuplicatesBy<Item, Key>(items: readonly Item[], selectKey: KeySelector<Item, Key>): boolean {\n\tconst seen = new Set<Key>();\n\tfor (let index = 0; index < items.length; index += 1) {\n\t\tconst item = items[index];\n\t\tif (item === undefined && !(index in items)) continue;\n\t\tconst key = selectKey(item as Item, index);\n\t\tif (seen.has(key)) return true;\n\t\tseen.add(key);\n\t}\n\treturn false;\n}\n\n/**\n * 判断所有项目是否具有相同的选择器结果。\n *\n * @remarks 空数组、只有稀疏空位的数组和单项数组按数学惯例返回 `true`;空位不会调用选择器。\n * @param items - 不会被修改的输入数组。\n * @param selectKey - 返回比较键的函数。\n * @returns 所有键都满足 SameValueZero 相等时返回 `true`。\n */\nexport function allEqualBy<Item, Key>(items: readonly Item[], selectKey: KeySelector<Item, Key>): boolean {\n\tlet first: Key | undefined;\n\tlet hasFirst = false;\n\tfor (let index = 0; index < items.length; index += 1) {\n\t\tconst item = items[index];\n\t\tif (item === undefined && !(index in items)) continue;\n\t\tconst current = selectKey(item as Item, index);\n\t\tif (!hasFirst) {\n\t\t\tfirst = current;\n\t\t\thasFirst = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!(first === current || (Number.isNaN(first) && Number.isNaN(current)))) return false;\n\t}\n\treturn true;\n}\n"],"mappings":"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"}
@@ -7,7 +7,7 @@ const maximumTimerDelay = 2147483647;
7
7
  * @returns 名称为 `AbortError` 的新错误实例。
8
8
  */
9
9
  const createAbortError = (signal) => {
10
- const error = new Error("The operation was aborted.", { cause: signal.reason });
10
+ const error = new Error("操作已取消。", { cause: signal.reason });
11
11
  error.name = "AbortError";
12
12
  return error;
13
13
  };
@@ -29,7 +29,7 @@ const throwIfAborted = (signal) => {
29
29
  * @throws `RangeError` 当值非有限、为负数或超过 32 位计时器上限。
30
30
  */
31
31
  const assertDelay = (milliseconds, name = "milliseconds") => {
32
- if (!Number.isFinite(milliseconds) || milliseconds < 0 || milliseconds > maximumTimerDelay) throw new RangeError(`${name} must be a finite number from 0 through ${maximumTimerDelay}.`);
32
+ if (!Number.isFinite(milliseconds) || milliseconds < 0 || milliseconds > maximumTimerDelay) throw new RangeError(`\`${name}\` 必须是 0 ${maximumTimerDelay} 之间的有限数。`);
33
33
  return milliseconds;
34
34
  };
35
35
  /**
@@ -45,13 +45,14 @@ function sleep(milliseconds, options = {}) {
45
45
  const signal = options.signal;
46
46
  throwIfAborted(signal);
47
47
  return new Promise((resolve, reject) => {
48
+ let timer;
48
49
  /** 取消计时器并使用标准取消错误拒绝等待。 */
49
- const onAbort = () => {
50
+ function onAbort() {
50
51
  if (signal === void 0) return;
51
52
  clearTimeout(timer);
52
53
  reject(createAbortError(signal));
53
- };
54
- const timer = setTimeout(() => {
54
+ }
55
+ timer = setTimeout(() => {
55
56
  signal?.removeEventListener("abort", onAbort);
56
57
  resolve();
57
58
  }, delay);
@@ -75,32 +76,33 @@ function withTimeout(promise, timeoutMs, options = {}) {
75
76
  throwIfAborted(signal);
76
77
  return new Promise((resolve, reject) => {
77
78
  let settled = false;
79
+ let timer;
78
80
  /** 清理竞争结束后不再需要的计时器和监听器。 */
79
- const cleanup = () => {
81
+ function cleanup() {
80
82
  clearTimeout(timer);
81
83
  signal?.removeEventListener("abort", onAbort);
82
- };
84
+ }
83
85
  /**
84
86
  * 只允许 Promise、超时和取消三个竞争来源中的首个结果生效。
85
87
  *
86
88
  * @param action - 首个完成来源的结算动作。
87
89
  */
88
- const settle = (action) => {
90
+ function settle(action) {
89
91
  if (settled) return;
90
92
  settled = true;
91
93
  cleanup();
92
94
  action();
93
- };
95
+ }
94
96
  /** 使用调用方取消原因结束当前等待。 */
95
- const onAbort = () => {
97
+ function onAbort() {
96
98
  if (signal === void 0) return;
97
99
  settle(() => {
98
100
  reject(createAbortError(signal));
99
101
  });
100
- };
101
- const timer = setTimeout(() => {
102
+ }
103
+ timer = setTimeout(() => {
102
104
  settle(() => {
103
- reject(new Error(options.message ?? `The operation exceeded ${delay} ms.`));
105
+ reject(new Error(options.message ?? `操作超过 ${delay} 毫秒仍未完成。`));
104
106
  });
105
107
  }, delay);
106
108
  signal?.addEventListener("abort", onAbort, { once: true });
@@ -129,8 +131,8 @@ async function retry(operation, options = {}) {
129
131
  const initialDelay = assertDelay(options.delayMs ?? 200, "delayMs");
130
132
  const maximumDelay = assertDelay(options.maxDelayMs ?? 3e4, "maxDelayMs");
131
133
  const factor = options.factor ?? 2;
132
- if (!Number.isSafeInteger(attempts) || attempts <= 0) throw new RangeError("attempts must be a positive safe integer.");
133
- if (!Number.isFinite(factor) || factor < 1) throw new RangeError("factor must be a finite number greater than or equal to 1.");
134
+ if (!Number.isSafeInteger(attempts) || attempts <= 0) throw new RangeError("`attempts` 必须是正安全整数。");
135
+ if (!Number.isFinite(factor) || factor < 1) throw new RangeError("`factor` 必须是大于或等于 1 的有限数。");
134
136
  for (let attempt = 1; attempt <= attempts; attempt += 1) {
135
137
  throwIfAborted(options.signal);
136
138
  const context = options.signal === void 0 ? { attempt } : {
@@ -144,7 +146,7 @@ async function retry(operation, options = {}) {
144
146
  await sleep(initialDelay === 0 ? 0 : Math.min(initialDelay * factor ** (attempt - 1), maximumDelay), options.signal === void 0 ? {} : { signal: options.signal });
145
147
  }
146
148
  }
147
- throw new Error("Retry finished without a result.");
149
+ throw new Error("重试结束但未获得结果。");
148
150
  }
149
151
  /**
150
152
  * 以固定并发度映射数组,并保持结果顺序。
@@ -159,7 +161,7 @@ async function retry(operation, options = {}) {
159
161
  * @throws `RangeError` 当 `concurrency` 不是正安全整数;取消时抛出名称为 `AbortError` 的 `Error`。
160
162
  */
161
163
  async function mapConcurrent(items, concurrency, mapper, options = {}) {
162
- if (!Number.isSafeInteger(concurrency) || concurrency <= 0) throw new RangeError("concurrency must be a positive safe integer.");
164
+ if (!Number.isSafeInteger(concurrency) || concurrency <= 0) throw new RangeError("`concurrency` 必须是正安全整数。");
163
165
  throwIfAborted(options.signal);
164
166
  const results = new Array(items.length);
165
167
  let nextIndex = 0;
@@ -213,7 +215,7 @@ function debounce(callback, delayMs = 300) {
213
215
  */
214
216
  const execute = async () => {
215
217
  const arguments_ = latestArguments;
216
- if (arguments_ === void 0) throw new Error("No debounced invocation is pending.");
218
+ if (arguments_ === void 0) throw new Error("当前没有待处理的防抖调用。");
217
219
  latestArguments = void 0;
218
220
  timer = void 0;
219
221
  const currentWaiters = waiters;
@@ -252,7 +254,7 @@ function debounce(callback, delayMs = 300) {
252
254
  if (timer !== void 0) clearTimeout(timer);
253
255
  timer = void 0;
254
256
  latestArguments = void 0;
255
- const error = reason ?? /* @__PURE__ */ new Error("The debounced invocation was cancelled.");
257
+ const error = reason ?? /* @__PURE__ */ new Error("防抖调用已取消。");
256
258
  waiters.forEach((waiter) => {
257
259
  waiter.reject(error);
258
260
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/async/index.ts"],"sourcesContent":["/** 统一同步返回值与 PromiseLike 返回值的内部回调签名。 */\ntype AsyncCallback<Arguments extends unknown[], Result> = (...arguments_: Arguments) => Result | PromiseLike<Result>;\n\n/** 记录同一防抖批次中每个调用方独立的 Promise 结算函数。 */\ninterface PromiseWaiter<Result> {\n\t/**\n\t * 使用批次失败原因拒绝当前调用方。\n\t * @param reason - `cancel` 提供的原因或共享回调抛出的原始错误。\n\t */\n\treject: (reason?: unknown) => void;\n\t/**\n\t * 使用共享回调结果完成当前调用方,并采用传入 PromiseLike 的最终状态。\n\t * @param value - 当前防抖批次唯一一次回调执行产生的共享结果。\n\t */\n\tresolve: (value: Result | PromiseLike<Result>) => void;\n}\n\n// 浏览器和 Node.js 的计时器普遍以有符号 32 位整数保存延迟;更大的值可能被\n// 静默截断为约 1 ms,因此公共 API 在进入平台计时器前统一拒绝它。\nconst maximumTimerDelay = 2_147_483_647;\n\n/** 可接收取消信号的通用选项。 */\nexport interface AbortOptions {\n\t/** 已取消时立即失败;运行期间取消时停止等待并拒绝 Promise。 */\n\tsignal?: AbortSignal;\n}\n\n/** {@link withTimeout} 的行为选项。 */\nexport interface TimeoutOptions extends AbortOptions {\n\t/** 超时时使用的开发者消息。 */\n\tmessage?: string;\n}\n\n/** 每次重试操作接收的上下文。 */\nexport interface RetryContext {\n\t/** 从 1 开始的当前尝试次数。 */\n\tattempt: number;\n\t/** 调用方提供的取消信号。 */\n\tsignal?: AbortSignal;\n}\n\n/** {@link retry} 的策略选项。 */\nexport interface RetryOptions extends AbortOptions {\n\t/** 最大尝试次数,包含首次调用;默认 `3`。 */\n\tattempts?: number;\n\t/** 首次重试前的等待毫秒数,最大 2,147,483,647;默认 `200`。 */\n\tdelayMs?: number;\n\t/** 每次失败后的退避倍数,必须不小于 1;默认 `2`。 */\n\tfactor?: number;\n\t/** 单次等待上限,最大 2,147,483,647;默认 `30_000` 毫秒。 */\n\tmaxDelayMs?: number;\n\t/**\n\t * 决定当前失败后是否继续下一次尝试;默认重试所有尚未到达上限的错误。\n\t * @param error - 当前操作抛出或拒绝的原始值。\n\t * @param context - 当前尝试次数和调用方取消信号。\n\t * @returns `false` 时立即原样抛出当前错误;支持同步值或 PromiseLike。\n\t */\n\tshouldRetry?: (error: unknown, context: RetryContext) => boolean | PromiseLike<boolean>;\n}\n\n/** {@link mapConcurrent} 的执行选项。 */\nexport interface ConcurrentMapOptions {\n\t/** 已取消时停止调度新任务;已经开始的映射器需要自行响应同一信号。 */\n\tsignal?: AbortSignal;\n}\n\n/** Promise 感知的防抖函数。 */\nexport interface DebouncedFunction<Arguments extends unknown[], Result> {\n\t/**\n\t * 调度一次调用;同一等待窗口内的调用共享最后一组参数对应的结果。\n\t * @param arguments_ - 传给原始回调的参数;后续调用会覆盖尚未执行批次保存的参数。\n\t * @returns 当前批次的独立 Promise,最终与共享回调结果保持相同状态。\n\t */\n\t(...arguments_: Arguments): Promise<Result>;\n\t/**\n\t * 取消尚未执行的批次,并拒绝该批次的所有 Promise。\n\t * @param reason - 可选拒绝原因;省略时使用内部取消错误。\n\t */\n\tcancel: (reason?: unknown) => void;\n\t/**\n\t * 立即执行待处理批次,不创建第二次回调执行。\n\t * @returns 待处理批次的共享执行 Promise;没有批次时返回 `undefined`。\n\t */\n\tflush: () => Promise<Result> | undefined;\n\t/** @returns 当前存在尚未开始的批次时返回 `true`;正在执行但没有等待批次时返回 `false`。 */\n\tpending: () => boolean;\n}\n\n/** Promise 感知的前缘节流函数。 */\nexport interface ThrottledFunction<Arguments extends unknown[], Result> {\n\t/**\n\t * 在空闲时立即调用原始回调;执行期和冷却期内的调用共享首次调用的 Promise。\n\t * @param arguments_ - 仅窗口内首次调用的参数会传给原始回调。\n\t * @returns 当前执行窗口共享的 Promise。\n\t */\n\t(...arguments_: Arguments): Promise<Result>;\n\t/** 提前结束冷却期;已经开始的操作不会被取消,结束前仍禁止并发重入。 */\n\tcancel: () => void;\n\t/** @returns 原始回调正在执行或计时器仍处于冷却期时返回 `true`。 */\n\tpending: () => boolean;\n}\n\n/**\n * 创建符合 Web Platform 约定的取消错误。\n *\n * @param signal - 已进入取消状态的信号;其 `reason` 会保存在错误的 `cause` 中。\n * @returns 名称为 `AbortError` 的新错误实例。\n */\nconst createAbortError = (signal: AbortSignal): Error => {\n\tconst error = new Error(\"The operation was aborted.\", { cause: signal.reason });\n\terror.name = \"AbortError\";\n\treturn error;\n};\n\n/**\n * 在启动异步工作前同步拒绝已经取消的信号。\n *\n * @param signal - 可选取消信号;省略或尚未取消时不执行操作。\n * @throws `Error` 当信号已经取消,错误名称为 `AbortError`。\n */\nconst throwIfAborted = (signal: AbortSignal | undefined): void => {\n\tif (signal?.aborted) throw createAbortError(signal);\n};\n\n/**\n * 校验宿主计时器可以稳定表示的延迟。\n *\n * @param milliseconds - 待校验的毫秒数。\n * @param name - 用于错误消息的参数名称。\n * @returns 原始延迟值,便于调用方在校验后直接使用。\n * @throws `RangeError` 当值非有限、为负数或超过 32 位计时器上限。\n */\nconst assertDelay = (milliseconds: number, name = \"milliseconds\"): number => {\n\tif (!Number.isFinite(milliseconds) || milliseconds < 0 || milliseconds > maximumTimerDelay) {\n\t\tthrow new RangeError(`${name} must be a finite number from 0 through ${maximumTimerDelay}.`);\n\t}\n\treturn milliseconds;\n};\n\n/**\n * 等待指定时间,并支持 `AbortSignal`。\n *\n * @param milliseconds - 0 至 2,147,483,647 的有限毫秒数。\n * @param options - 可选取消信号。\n * @returns 到期后完成的 Promise。\n * @throws 取消时抛出名称为 `AbortError` 的 `Error`;参数非法时抛出 `RangeError`。\n */\nexport function sleep(milliseconds: number, options: AbortOptions = {}): Promise<void> {\n\tconst delay = assertDelay(milliseconds);\n\tconst signal = options.signal;\n\tthrowIfAborted(signal);\n\n\treturn new Promise<void>((resolve, reject) => {\n\t\t/** 取消计时器并使用标准取消错误拒绝等待。 */\n\t\tconst onAbort = (): void => {\n\t\t\tif (signal === undefined) return;\n\t\t\tclearTimeout(timer);\n\t\t\treject(createAbortError(signal));\n\t\t};\n\t\tconst timer = setTimeout(() => {\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tresolve();\n\t\t}, delay);\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n}\n\n/**\n * 为 Promise 增加等待上限。\n *\n * @remarks 超时或取消只停止等待,不能自动取消底层操作;需要真正取消时应同时把\n * 同一个 `AbortSignal` 传给底层 API。\n * @param promise - 需要等待的 Promise 或 PromiseLike。\n * @param timeoutMs - 0 至 2,147,483,647 的有限等待时间。\n * @param options - 取消信号与自定义消息。\n * @returns 底层 Promise 的结果。\n * @throws 超时抛出 `Error`,取消时抛出名称为 `AbortError` 的 `Error`;等待时间非法时抛出 `RangeError`。\n */\nexport function withTimeout<Result>(promise: PromiseLike<Result>, timeoutMs: number, options: TimeoutOptions = {}): Promise<Result> {\n\tconst delay = assertDelay(timeoutMs, \"timeoutMs\");\n\tconst signal = options.signal;\n\tthrowIfAborted(signal);\n\n\treturn new Promise<Result>((resolve, reject) => {\n\t\tlet settled = false;\n\t\t/** 清理竞争结束后不再需要的计时器和监听器。 */\n\t\tconst cleanup = (): void => {\n\t\t\tclearTimeout(timer);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t};\n\t\t/**\n\t\t * 只允许 Promise、超时和取消三个竞争来源中的首个结果生效。\n\t\t *\n\t\t * @param action - 首个完成来源的结算动作。\n\t\t */\n\t\tconst settle = (action: () => void): void => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\taction();\n\t\t};\n\t\t/** 使用调用方取消原因结束当前等待。 */\n\t\tconst onAbort = (): void => {\n\t\t\tif (signal === undefined) return;\n\t\t\tsettle(() => {\n\t\t\t\treject(createAbortError(signal));\n\t\t\t});\n\t\t};\n\t\tconst timer = setTimeout(() => {\n\t\t\tsettle(() => {\n\t\t\t\treject(new Error(options.message ?? `The operation exceeded ${delay} ms.`));\n\t\t\t});\n\t\t}, delay);\n\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tPromise.resolve(promise).then(\n\t\t\t(value) => {\n\t\t\t\tsettle(() => {\n\t\t\t\t\tresolve(value);\n\t\t\t\t});\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tsettle(() => {\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t\t}\n\t\t);\n\t});\n}\n\n/**\n * 使用有上限的指数退避重试操作。\n *\n * @typeParam Result - 操作结果类型。\n * @param operation - 每次尝试都会调用的函数;`attempt` 从 1 开始。\n * @param options - 尝试次数、退避和取消策略。\n * @returns 首次成功结果。\n * @throws 最后一次操作错误、`shouldRetry` 错误或名称为 `AbortError` 的取消错误;策略参数非法时抛出 `RangeError`。\n */\nexport async function retry<Result>(\n\toperation: (context: RetryContext) => Result | PromiseLike<Result>,\n\toptions: RetryOptions = {}\n): Promise<Awaited<Result>> {\n\tconst attempts = options.attempts ?? 3;\n\tconst initialDelay = assertDelay(options.delayMs ?? 200, \"delayMs\");\n\tconst maximumDelay = assertDelay(options.maxDelayMs ?? 30_000, \"maxDelayMs\");\n\tconst factor = options.factor ?? 2;\n\tif (!Number.isSafeInteger(attempts) || attempts <= 0) throw new RangeError(\"attempts must be a positive safe integer.\");\n\tif (!Number.isFinite(factor) || factor < 1) throw new RangeError(\"factor must be a finite number greater than or equal to 1.\");\n\n\tfor (let attempt = 1; attempt <= attempts; attempt += 1) {\n\t\tthrowIfAborted(options.signal);\n\t\tconst context: RetryContext = options.signal === undefined ? { attempt } : { attempt, signal: options.signal };\n\t\ttry {\n\t\t\treturn await operation(context);\n\t\t} catch (error) {\n\t\t\tif (attempt === attempts || (options.shouldRetry !== undefined && !(await options.shouldRetry(error, context)))) throw error;\n\t\t\t// `0 * Infinity` is `NaN`; a zero initial delay must remain zero even when\n\t\t\t// a very large factor overflows during a later attempt.\n\t\t\tconst delay = initialDelay === 0 ? 0 : Math.min(initialDelay * factor ** (attempt - 1), maximumDelay);\n\t\t\tawait sleep(delay, options.signal === undefined ? {} : { signal: options.signal });\n\t\t}\n\t}\n\n\tthrow new Error(\"Retry finished without a result.\");\n}\n\n/**\n * 以固定并发度映射数组,并保持结果顺序。\n *\n * @remarks 任一映射失败后不会再调度新项目,但已经开始的映射无法自动取消;映射器\n * 应使用传入的 `signal` 取消底层工作。\n * @param items - 不会被修改的输入数组。\n * @param concurrency - 同时运行的最大任务数,必须为正安全整数。\n * @param mapper - 接收项目、索引和取消信号的映射函数。\n * @param options - 可选取消信号。\n * @returns 与输入长度和顺序一致的结果数组;稀疏空位保持为空位且不会调用映射器。\n * @throws `RangeError` 当 `concurrency` 不是正安全整数;取消时抛出名称为 `AbortError` 的 `Error`。\n */\nexport async function mapConcurrent<Item, Result>(\n\titems: readonly Item[],\n\tconcurrency: number,\n\tmapper: (item: Item, index: number, signal: AbortSignal | undefined) => Result | PromiseLike<Result>,\n\toptions: ConcurrentMapOptions = {}\n): Promise<Awaited<Result>[]> {\n\tif (!Number.isSafeInteger(concurrency) || concurrency <= 0) {\n\t\tthrow new RangeError(\"concurrency must be a positive safe integer.\");\n\t}\n\tthrowIfAborted(options.signal);\n\n\tconst results = new Array<Awaited<Result>>(items.length);\n\tlet nextIndex = 0;\n\tlet failed = false;\n\t/**\n\t * 从共享游标持续领取映射任务。\n\t *\n\t * @remarks JavaScript 单线程执行保证“读取索引并递增”不会被另一个 Worker 插入,因此每个索引只会领取一次。\n\t * @returns 当前 Worker 没有剩余任务时完成。\n\t * @throws 原样传播取消错误或 Mapper 错误,并阻止其他 Worker 领取新任务。\n\t */\n\tconst worker = async (): Promise<void> => {\n\t\twhile (!failed) {\n\t\t\tthrowIfAborted(options.signal);\n\t\t\tconst index = nextIndex;\n\t\t\tif (index >= items.length) return;\n\t\t\tnextIndex += 1;\n\t\t\tif (!(index in items)) continue;\n\t\t\ttry {\n\t\t\t\tresults[index] = await mapper(items[index] as Item, index, options.signal);\n\t\t\t} catch (error) {\n\t\t\t\tfailed = true;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t};\n\n\tconst workerCount = Math.min(concurrency, items.length);\n\tawait Promise.all(Array.from({ length: workerCount }, () => worker()));\n\treturn results;\n}\n\n/**\n * 创建 Promise 感知的防抖函数。\n *\n * @remarks 同一窗口内的所有调用都会等待最后一组参数对应的执行结果;回调错误会原样\n * 拒绝该批次的全部调用,不会留下永久 pending 的 Promise。\n * @param callback - 同步或异步回调。\n * @param delayMs - 0 至 2,147,483,647 的有限等待时间,默认 300 毫秒。\n * @returns 具有取消、立即执行和状态方法的防抖函数。\n * @throws `RangeError` 当延迟不在平台计时器支持范围内。\n */\nexport function debounce<Arguments extends unknown[], Result>(\n\tcallback: AsyncCallback<Arguments, Result>,\n\tdelayMs = 300\n): DebouncedFunction<Arguments, Awaited<Result>> {\n\tconst delay = assertDelay(delayMs, \"delayMs\");\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet latestArguments: Arguments | undefined;\n\tlet waiters: PromiseWaiter<Awaited<Result>>[] = [];\n\n\t/**\n\t * 执行并结算当前防抖批次。\n\t *\n\t * @returns 最后一组参数对应的回调结果。\n\t * @throws 没有待处理批次时抛出 `Error`;回调错误会原样传播给批次中的全部调用方。\n\t */\n\tconst execute = async (): Promise<Awaited<Result>> => {\n\t\tconst arguments_ = latestArguments;\n\t\tif (arguments_ === undefined) {\n\t\t\tthrow new Error(\"No debounced invocation is pending.\");\n\t\t}\n\t\tlatestArguments = undefined;\n\t\ttimer = undefined;\n\t\tconst currentWaiters = waiters;\n\t\twaiters = [];\n\t\ttry {\n\t\t\tconst result = await callback(...arguments_);\n\t\t\tcurrentWaiters.forEach((waiter) => {\n\t\t\t\twaiter.resolve(result);\n\t\t\t});\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tcurrentWaiters.forEach((waiter) => {\n\t\t\t\twaiter.reject(error);\n\t\t\t});\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\t/**\n\t * 更新批次参数并返回当前调用方专属的等待 Promise。\n\t *\n\t * @param arguments_ - 本次调用参数;同批次中只有最后一组参数会执行。\n\t * @returns 与当前批次共享结果、但可独立结算的 Promise。\n\t */\n\tconst debounced = (...arguments_: Arguments): Promise<Awaited<Result>> => {\n\t\tlatestArguments = arguments_;\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t\ttimer = setTimeout(() => {\n\t\t\tvoid execute().catch(() => undefined);\n\t\t}, delay);\n\t\treturn new Promise<Awaited<Result>>((resolve, reject) => waiters.push({ reject, resolve }));\n\t};\n\n\tdebounced.cancel = (reason?: unknown): void => {\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t\ttimer = undefined;\n\t\tlatestArguments = undefined;\n\t\tconst error = reason ?? new Error(\"The debounced invocation was cancelled.\");\n\t\twaiters.forEach((waiter) => {\n\t\t\twaiter.reject(error);\n\t\t});\n\t\twaiters = [];\n\t};\n\tdebounced.flush = (): Promise<Awaited<Result>> | undefined => {\n\t\tif (timer === undefined) return undefined;\n\t\tclearTimeout(timer);\n\t\treturn execute();\n\t};\n\tdebounced.pending = (): boolean => timer !== undefined;\n\treturn debounced;\n}\n\n/**\n * 创建 Promise 感知的前缘节流函数。\n *\n * @remarks 窗口内的调用共享首次调用结果。若回调执行时间超过窗口,后续调用仍会等待\n * 当前回调,避免异步操作重入;该函数不安排尾缘调用。\n * @param callback - 同步或异步回调。\n * @param delayMs - 0 至 2,147,483,647 的有限冷却时间,默认 300 毫秒。\n * @returns 具有取消和状态方法的前缘节流函数。\n * @throws `RangeError` 当延迟不在平台计时器支持范围内。\n */\nexport function throttle<Arguments extends unknown[], Result>(\n\tcallback: AsyncCallback<Arguments, Result>,\n\tdelayMs = 300\n): ThrottledFunction<Arguments, Awaited<Result>> {\n\tconst delay = assertDelay(delayMs, \"delayMs\");\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet current: Promise<Awaited<Result>> | undefined;\n\tlet cooling = false;\n\tlet settled = false;\n\n\t/**\n\t * 尝试释放当前节流窗口。\n\t *\n\t * @remarks 只有回调和冷却计时器都结束后才清空共享 Promise,避免长回调发生重入。\n\t */\n\tconst release = (): void => {\n\t\tif (!cooling && settled) current = undefined;\n\t};\n\t/**\n\t * 执行前缘调用或复用当前窗口的共享 Promise。\n\t *\n\t * @param arguments_ - 仅新窗口首个调用会使用的参数。\n\t * @returns 当前窗口首次调用的 Promise。\n\t */\n\tconst throttled = (...arguments_: Arguments): Promise<Awaited<Result>> => {\n\t\tif (current !== undefined) return current;\n\t\tcooling = true;\n\t\tsettled = false;\n\t\tlet invocation: Promise<Awaited<Result>>;\n\t\ttry {\n\t\t\tinvocation = Promise.resolve(callback(...arguments_));\n\t\t} catch (error) {\n\t\t\tinvocation = Promise.reject(error);\n\t\t}\n\t\tcurrent = invocation;\n\t\tinvocation.then(\n\t\t\t() => {\n\t\t\t\tsettled = true;\n\t\t\t\trelease();\n\t\t\t},\n\t\t\t() => {\n\t\t\t\tsettled = true;\n\t\t\t\trelease();\n\t\t\t}\n\t\t);\n\t\ttimer = setTimeout(() => {\n\t\t\ttimer = undefined;\n\t\t\tcooling = false;\n\t\t\trelease();\n\t\t}, delay);\n\t\treturn invocation;\n\t};\n\n\tthrottled.cancel = (): void => {\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t\ttimer = undefined;\n\t\tcooling = false;\n\t\trelease();\n\t};\n\tthrottled.pending = (): boolean => current !== undefined;\n\treturn throttled;\n}\n"],"mappings":";AAmBA,MAAM,oBAAoB;;;;;;;AAyF1B,MAAM,oBAAoB,WAA+B;CACxD,MAAM,QAAQ,IAAI,MAAM,8BAA8B,EAAE,OAAO,OAAO,OAAO,CAAC;CAC9E,MAAM,OAAO;CACb,OAAO;AACR;;;;;;;AAQA,MAAM,kBAAkB,WAA0C;CACjE,IAAI,QAAQ,SAAS,MAAM,iBAAiB,MAAM;AACnD;;;;;;;;;AAUA,MAAM,eAAe,cAAsB,OAAO,mBAA2B;CAC5E,IAAI,CAAC,OAAO,SAAS,YAAY,KAAK,eAAe,KAAK,eAAe,mBACxE,MAAM,IAAI,WAAW,GAAG,KAAK,0CAA0C,kBAAkB,EAAE;CAE5F,OAAO;AACR;;;;;;;;;AAUA,SAAgB,MAAM,cAAsB,UAAwB,CAAC,GAAkB;CACtF,MAAM,QAAQ,YAAY,YAAY;CACtC,MAAM,SAAS,QAAQ;CACvB,eAAe,MAAM;CAErB,OAAO,IAAI,SAAe,SAAS,WAAW;;EAE7C,MAAM,gBAAsB;GAC3B,IAAI,WAAW,KAAA,GAAW;GAC1B,aAAa,KAAK;GAClB,OAAO,iBAAiB,MAAM,CAAC;EAChC;EACA,MAAM,QAAQ,iBAAiB;GAC9B,QAAQ,oBAAoB,SAAS,OAAO;GAC5C,QAAQ;EACT,GAAG,KAAK;EACR,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC1D,CAAC;AACF;;;;;;;;;;;;AAaA,SAAgB,YAAoB,SAA8B,WAAmB,UAA0B,CAAC,GAAoB;CACnI,MAAM,QAAQ,YAAY,WAAW,WAAW;CAChD,MAAM,SAAS,QAAQ;CACvB,eAAe,MAAM;CAErB,OAAO,IAAI,SAAiB,SAAS,WAAW;EAC/C,IAAI,UAAU;;EAEd,MAAM,gBAAsB;GAC3B,aAAa,KAAK;GAClB,QAAQ,oBAAoB,SAAS,OAAO;EAC7C;;;;;;EAMA,MAAM,UAAU,WAA6B;GAC5C,IAAI,SAAS;GACb,UAAU;GACV,QAAQ;GACR,OAAO;EACR;;EAEA,MAAM,gBAAsB;GAC3B,IAAI,WAAW,KAAA,GAAW;GAC1B,aAAa;IACZ,OAAO,iBAAiB,MAAM,CAAC;GAChC,CAAC;EACF;EACA,MAAM,QAAQ,iBAAiB;GAC9B,aAAa;IACZ,OAAO,IAAI,MAAM,QAAQ,WAAW,0BAA0B,MAAM,KAAK,CAAC;GAC3E,CAAC;EACF,GAAG,KAAK;EAER,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACzD,QAAQ,QAAQ,OAAO,CAAC,CAAC,MACvB,UAAU;GACV,aAAa;IACZ,QAAQ,KAAK;GACd,CAAC;EACF,IACC,UAAmB;GACnB,aAAa;IACZ,OAAO,KAAK;GACb,CAAC;EACF,CACD;CACD,CAAC;AACF;;;;;;;;;;AAWA,eAAsB,MACrB,WACA,UAAwB,CAAC,GACE;CAC3B,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,eAAe,YAAY,QAAQ,WAAW,KAAK,SAAS;CAClE,MAAM,eAAe,YAAY,QAAQ,cAAc,KAAQ,YAAY;CAC3E,MAAM,SAAS,QAAQ,UAAU;CACjC,IAAI,CAAC,OAAO,cAAc,QAAQ,KAAK,YAAY,GAAG,MAAM,IAAI,WAAW,2CAA2C;CACtH,IAAI,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG,MAAM,IAAI,WAAW,4DAA4D;CAE7H,KAAK,IAAI,UAAU,GAAG,WAAW,UAAU,WAAW,GAAG;EACxD,eAAe,QAAQ,MAAM;EAC7B,MAAM,UAAwB,QAAQ,WAAW,KAAA,IAAY,EAAE,QAAQ,IAAI;GAAE;GAAS,QAAQ,QAAQ;EAAO;EAC7G,IAAI;GACH,OAAO,MAAM,UAAU,OAAO;EAC/B,SAAS,OAAO;GACf,IAAI,YAAY,YAAa,QAAQ,gBAAgB,KAAA,KAAa,CAAE,MAAM,QAAQ,YAAY,OAAO,OAAO,GAAK,MAAM;GAIvH,MAAM,MADQ,iBAAiB,IAAI,IAAI,KAAK,IAAI,eAAe,WAAW,UAAU,IAAI,YAAY,GACjF,QAAQ,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,QAAQ,OAAO,CAAC;EAClF;CACD;CAEA,MAAM,IAAI,MAAM,kCAAkC;AACnD;;;;;;;;;;;;;AAcA,eAAsB,cACrB,OACA,aACA,QACA,UAAgC,CAAC,GACJ;CAC7B,IAAI,CAAC,OAAO,cAAc,WAAW,KAAK,eAAe,GACxD,MAAM,IAAI,WAAW,8CAA8C;CAEpE,eAAe,QAAQ,MAAM;CAE7B,MAAM,UAAU,IAAI,MAAuB,MAAM,MAAM;CACvD,IAAI,YAAY;CAChB,IAAI,SAAS;;;;;;;;CAQb,MAAM,SAAS,YAA2B;EACzC,OAAO,CAAC,QAAQ;GACf,eAAe,QAAQ,MAAM;GAC7B,MAAM,QAAQ;GACd,IAAI,SAAS,MAAM,QAAQ;GAC3B,aAAa;GACb,IAAI,EAAE,SAAS,QAAQ;GACvB,IAAI;IACH,QAAQ,SAAS,MAAM,OAAO,MAAM,QAAgB,OAAO,QAAQ,MAAM;GAC1E,SAAS,OAAO;IACf,SAAS;IACT,MAAM;GACP;EACD;CACD;CAEA,MAAM,cAAc,KAAK,IAAI,aAAa,MAAM,MAAM;CACtD,MAAM,QAAQ,IAAI,MAAM,KAAK,EAAE,QAAQ,YAAY,SAAS,OAAO,CAAC,CAAC;CACrE,OAAO;AACR;;;;;;;;;;;AAYA,SAAgB,SACf,UACA,UAAU,KACsC;CAChD,MAAM,QAAQ,YAAY,SAAS,SAAS;CAC5C,IAAI;CACJ,IAAI;CACJ,IAAI,UAA4C,CAAC;;;;;;;CAQjD,MAAM,UAAU,YAAsC;EACrD,MAAM,aAAa;EACnB,IAAI,eAAe,KAAA,GAClB,MAAM,IAAI,MAAM,qCAAqC;EAEtD,kBAAkB,KAAA;EAClB,QAAQ,KAAA;EACR,MAAM,iBAAiB;EACvB,UAAU,CAAC;EACX,IAAI;GACH,MAAM,SAAS,MAAM,SAAS,GAAG,UAAU;GAC3C,eAAe,SAAS,WAAW;IAClC,OAAO,QAAQ,MAAM;GACtB,CAAC;GACD,OAAO;EACR,SAAS,OAAO;GACf,eAAe,SAAS,WAAW;IAClC,OAAO,OAAO,KAAK;GACpB,CAAC;GACD,MAAM;EACP;CACD;;;;;;;CAQA,MAAM,aAAa,GAAG,eAAoD;EACzE,kBAAkB;EAClB,IAAI,UAAU,KAAA,GAAW,aAAa,KAAK;EAC3C,QAAQ,iBAAiB;GACxB,QAAa,CAAC,CAAC,YAAY,KAAA,CAAS;EACrC,GAAG,KAAK;EACR,OAAO,IAAI,SAA0B,SAAS,WAAW,QAAQ,KAAK;GAAE;GAAQ;EAAQ,CAAC,CAAC;CAC3F;CAEA,UAAU,UAAU,WAA2B;EAC9C,IAAI,UAAU,KAAA,GAAW,aAAa,KAAK;EAC3C,QAAQ,KAAA;EACR,kBAAkB,KAAA;EAClB,MAAM,QAAQ,0BAAU,IAAI,MAAM,yCAAyC;EAC3E,QAAQ,SAAS,WAAW;GAC3B,OAAO,OAAO,KAAK;EACpB,CAAC;EACD,UAAU,CAAC;CACZ;CACA,UAAU,cAAoD;EAC7D,IAAI,UAAU,KAAA,GAAW,OAAO,KAAA;EAChC,aAAa,KAAK;EAClB,OAAO,QAAQ;CAChB;CACA,UAAU,gBAAyB,UAAU,KAAA;CAC7C,OAAO;AACR;;;;;;;;;;;AAYA,SAAgB,SACf,UACA,UAAU,KACsC;CAChD,MAAM,QAAQ,YAAY,SAAS,SAAS;CAC5C,IAAI;CACJ,IAAI;CACJ,IAAI,UAAU;CACd,IAAI,UAAU;;;;;;CAOd,MAAM,gBAAsB;EAC3B,IAAI,CAAC,WAAW,SAAS,UAAU,KAAA;CACpC;;;;;;;CAOA,MAAM,aAAa,GAAG,eAAoD;EACzE,IAAI,YAAY,KAAA,GAAW,OAAO;EAClC,UAAU;EACV,UAAU;EACV,IAAI;EACJ,IAAI;GACH,aAAa,QAAQ,QAAQ,SAAS,GAAG,UAAU,CAAC;EACrD,SAAS,OAAO;GACf,aAAa,QAAQ,OAAO,KAAK;EAClC;EACA,UAAU;EACV,WAAW,WACJ;GACL,UAAU;GACV,QAAQ;EACT,SACM;GACL,UAAU;GACV,QAAQ;EACT,CACD;EACA,QAAQ,iBAAiB;GACxB,QAAQ,KAAA;GACR,UAAU;GACV,QAAQ;EACT,GAAG,KAAK;EACR,OAAO;CACR;CAEA,UAAU,eAAqB;EAC9B,IAAI,UAAU,KAAA,GAAW,aAAa,KAAK;EAC3C,QAAQ,KAAA;EACR,UAAU;EACV,QAAQ;CACT;CACA,UAAU,gBAAyB,YAAY,KAAA;CAC/C,OAAO;AACR"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/async/index.ts"],"sourcesContent":["/** 统一同步返回值与 PromiseLike 返回值的内部回调签名。 */\ntype AsyncCallback<Arguments extends unknown[], Result> = (...arguments_: Arguments) => Result | PromiseLike<Result>;\n\n/** 记录同一防抖批次中每个调用方独立的 Promise 结算函数。 */\ninterface PromiseWaiter<Result> {\n\t/**\n\t * 使用批次失败原因拒绝当前调用方。\n\t * @param reason - `cancel` 提供的原因或共享回调抛出的原始错误。\n\t */\n\treject: (reason?: unknown) => void;\n\t/**\n\t * 使用共享回调结果完成当前调用方,并采用传入 PromiseLike 的最终状态。\n\t * @param value - 当前防抖批次唯一一次回调执行产生的共享结果。\n\t */\n\tresolve: (value: Result | PromiseLike<Result>) => void;\n}\n\n// 浏览器和 Node.js 的计时器普遍以有符号 32 位整数保存延迟;更大的值可能被\n// 静默截断为约 1 ms,因此公共 API 在进入平台计时器前统一拒绝它。\nconst maximumTimerDelay = 2_147_483_647;\n\n/** 可接收取消信号的通用选项。 */\nexport interface AbortOptions {\n\t/** 已取消时立即失败;运行期间取消时停止等待并拒绝 Promise。 */\n\tsignal?: AbortSignal;\n}\n\n/** {@link withTimeout} 的行为选项。 */\nexport interface TimeoutOptions extends AbortOptions {\n\t/** 超时时使用的开发者消息。 */\n\tmessage?: string;\n}\n\n/** 每次重试操作接收的上下文。 */\nexport interface RetryContext {\n\t/** 从 1 开始的当前尝试次数。 */\n\tattempt: number;\n\t/** 调用方提供的取消信号。 */\n\tsignal?: AbortSignal;\n}\n\n/** {@link retry} 的策略选项。 */\nexport interface RetryOptions extends AbortOptions {\n\t/** 最大尝试次数,包含首次调用;默认 `3`。 */\n\tattempts?: number;\n\t/** 首次重试前的等待毫秒数,最大 2,147,483,647;默认 `200`。 */\n\tdelayMs?: number;\n\t/** 每次失败后的退避倍数,必须不小于 1;默认 `2`。 */\n\tfactor?: number;\n\t/** 单次等待上限,最大 2,147,483,647;默认 `30_000` 毫秒。 */\n\tmaxDelayMs?: number;\n\t/**\n\t * 决定当前失败后是否继续下一次尝试;默认重试所有尚未到达上限的错误。\n\t * @param error - 当前操作抛出或拒绝的原始值。\n\t * @param context - 当前尝试次数和调用方取消信号。\n\t * @returns `false` 时立即原样抛出当前错误;支持同步值或 PromiseLike。\n\t */\n\tshouldRetry?: (error: unknown, context: RetryContext) => boolean | PromiseLike<boolean>;\n}\n\n/** {@link mapConcurrent} 的执行选项。 */\nexport interface ConcurrentMapOptions {\n\t/** 已取消时停止调度新任务;已经开始的映射器需要自行响应同一信号。 */\n\tsignal?: AbortSignal;\n}\n\n/** Promise 感知的防抖函数。 */\nexport interface DebouncedFunction<Arguments extends unknown[], Result> {\n\t/**\n\t * 调度一次调用;同一等待窗口内的调用共享最后一组参数对应的结果。\n\t * @param arguments_ - 传给原始回调的参数;后续调用会覆盖尚未执行批次保存的参数。\n\t * @returns 当前批次的独立 Promise,最终与共享回调结果保持相同状态。\n\t */\n\t(...arguments_: Arguments): Promise<Result>;\n\t/**\n\t * 取消尚未执行的批次,并拒绝该批次的所有 Promise。\n\t * @param reason - 可选拒绝原因;省略时使用内部取消错误。\n\t */\n\tcancel: (reason?: unknown) => void;\n\t/**\n\t * 立即执行待处理批次,不创建第二次回调执行。\n\t * @returns 待处理批次的共享执行 Promise;没有批次时返回 `undefined`。\n\t */\n\tflush: () => Promise<Result> | undefined;\n\t/** @returns 当前存在尚未开始的批次时返回 `true`;正在执行但没有等待批次时返回 `false`。 */\n\tpending: () => boolean;\n}\n\n/** Promise 感知的前缘节流函数。 */\nexport interface ThrottledFunction<Arguments extends unknown[], Result> {\n\t/**\n\t * 在空闲时立即调用原始回调;执行期和冷却期内的调用共享首次调用的 Promise。\n\t * @param arguments_ - 仅窗口内首次调用的参数会传给原始回调。\n\t * @returns 当前执行窗口共享的 Promise。\n\t */\n\t(...arguments_: Arguments): Promise<Result>;\n\t/** 提前结束冷却期;已经开始的操作不会被取消,结束前仍禁止并发重入。 */\n\tcancel: () => void;\n\t/** @returns 原始回调正在执行或计时器仍处于冷却期时返回 `true`。 */\n\tpending: () => boolean;\n}\n\n/**\n * 创建符合 Web Platform 约定的取消错误。\n *\n * @param signal - 已进入取消状态的信号;其 `reason` 会保存在错误的 `cause` 中。\n * @returns 名称为 `AbortError` 的新错误实例。\n */\nconst createAbortError = (signal: AbortSignal): Error => {\n\tconst error = new Error(\"操作已取消。\", { cause: signal.reason });\n\terror.name = \"AbortError\";\n\treturn error;\n};\n\n/**\n * 在启动异步工作前同步拒绝已经取消的信号。\n *\n * @param signal - 可选取消信号;省略或尚未取消时不执行操作。\n * @throws `Error` 当信号已经取消,错误名称为 `AbortError`。\n */\nconst throwIfAborted = (signal: AbortSignal | undefined): void => {\n\tif (signal?.aborted) throw createAbortError(signal);\n};\n\n/**\n * 校验宿主计时器可以稳定表示的延迟。\n *\n * @param milliseconds - 待校验的毫秒数。\n * @param name - 用于错误消息的参数名称。\n * @returns 原始延迟值,便于调用方在校验后直接使用。\n * @throws `RangeError` 当值非有限、为负数或超过 32 位计时器上限。\n */\nconst assertDelay = (milliseconds: number, name = \"milliseconds\"): number => {\n\tif (!Number.isFinite(milliseconds) || milliseconds < 0 || milliseconds > maximumTimerDelay) {\n\t\tthrow new RangeError(`\\`${name}\\` 必须是 0 到 ${maximumTimerDelay} 之间的有限数。`);\n\t}\n\treturn milliseconds;\n};\n\n/**\n * 等待指定时间,并支持 `AbortSignal`。\n *\n * @param milliseconds - 0 至 2,147,483,647 的有限毫秒数。\n * @param options - 可选取消信号。\n * @returns 到期后完成的 Promise。\n * @throws 取消时抛出名称为 `AbortError` 的 `Error`;参数非法时抛出 `RangeError`。\n */\nexport function sleep(milliseconds: number, options: AbortOptions = {}): Promise<void> {\n\tconst delay = assertDelay(milliseconds);\n\tconst signal = options.signal;\n\tthrowIfAborted(signal);\n\n\treturn new Promise<void>((resolve, reject) => {\n\t\tlet timer: ReturnType<typeof setTimeout>;\n\t\t/** 取消计时器并使用标准取消错误拒绝等待。 */\n\t\tfunction onAbort(): void {\n\t\t\tif (signal === undefined) return;\n\t\t\tclearTimeout(timer);\n\t\t\treject(createAbortError(signal));\n\t\t}\n\t\ttimer = setTimeout(() => {\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tresolve();\n\t\t}, delay);\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t});\n}\n\n/**\n * 为 Promise 增加等待上限。\n *\n * @remarks 超时或取消只停止等待,不能自动取消底层操作;需要真正取消时应同时把\n * 同一个 `AbortSignal` 传给底层 API。\n * @param promise - 需要等待的 Promise 或 PromiseLike。\n * @param timeoutMs - 0 至 2,147,483,647 的有限等待时间。\n * @param options - 取消信号与自定义消息。\n * @returns 底层 Promise 的结果。\n * @throws 超时抛出 `Error`,取消时抛出名称为 `AbortError` 的 `Error`;等待时间非法时抛出 `RangeError`。\n */\nexport function withTimeout<Result>(promise: PromiseLike<Result>, timeoutMs: number, options: TimeoutOptions = {}): Promise<Result> {\n\tconst delay = assertDelay(timeoutMs, \"timeoutMs\");\n\tconst signal = options.signal;\n\tthrowIfAborted(signal);\n\n\treturn new Promise<Result>((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet timer: ReturnType<typeof setTimeout>;\n\t\t/** 清理竞争结束后不再需要的计时器和监听器。 */\n\t\tfunction cleanup(): void {\n\t\t\tclearTimeout(timer);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t}\n\t\t/**\n\t\t * 只允许 Promise、超时和取消三个竞争来源中的首个结果生效。\n\t\t *\n\t\t * @param action - 首个完成来源的结算动作。\n\t\t */\n\t\tfunction settle(action: () => void): void {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tcleanup();\n\t\t\taction();\n\t\t}\n\t\t/** 使用调用方取消原因结束当前等待。 */\n\t\tfunction onAbort(): void {\n\t\t\tif (signal === undefined) return;\n\t\t\tsettle(() => {\n\t\t\t\treject(createAbortError(signal));\n\t\t\t});\n\t\t}\n\t\ttimer = setTimeout(() => {\n\t\t\tsettle(() => {\n\t\t\t\treject(new Error(options.message ?? `操作超过 ${delay} 毫秒仍未完成。`));\n\t\t\t});\n\t\t}, delay);\n\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\tPromise.resolve(promise).then(\n\t\t\t(value) => {\n\t\t\t\tsettle(() => {\n\t\t\t\t\tresolve(value);\n\t\t\t\t});\n\t\t\t},\n\t\t\t(error: unknown) => {\n\t\t\t\tsettle(() => {\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t\t}\n\t\t);\n\t});\n}\n\n/**\n * 使用有上限的指数退避重试操作。\n *\n * @typeParam Result - 操作结果类型。\n * @param operation - 每次尝试都会调用的函数;`attempt` 从 1 开始。\n * @param options - 尝试次数、退避和取消策略。\n * @returns 首次成功结果。\n * @throws 最后一次操作错误、`shouldRetry` 错误或名称为 `AbortError` 的取消错误;策略参数非法时抛出 `RangeError`。\n */\nexport async function retry<Result>(\n\toperation: (context: RetryContext) => Result | PromiseLike<Result>,\n\toptions: RetryOptions = {}\n): Promise<Awaited<Result>> {\n\tconst attempts = options.attempts ?? 3;\n\tconst initialDelay = assertDelay(options.delayMs ?? 200, \"delayMs\");\n\tconst maximumDelay = assertDelay(options.maxDelayMs ?? 30_000, \"maxDelayMs\");\n\tconst factor = options.factor ?? 2;\n\tif (!Number.isSafeInteger(attempts) || attempts <= 0) throw new RangeError(\"`attempts` 必须是正安全整数。\");\n\tif (!Number.isFinite(factor) || factor < 1) throw new RangeError(\"`factor` 必须是大于或等于 1 的有限数。\");\n\n\tfor (let attempt = 1; attempt <= attempts; attempt += 1) {\n\t\tthrowIfAborted(options.signal);\n\t\tconst context: RetryContext = options.signal === undefined ? { attempt } : { attempt, signal: options.signal };\n\t\ttry {\n\t\t\treturn await operation(context);\n\t\t} catch (error) {\n\t\t\tif (attempt === attempts || (options.shouldRetry !== undefined && !(await options.shouldRetry(error, context)))) throw error;\n\t\t\t// `0 * Infinity` is `NaN`; a zero initial delay must remain zero even when\n\t\t\t// a very large factor overflows during a later attempt.\n\t\t\tconst delay = initialDelay === 0 ? 0 : Math.min(initialDelay * factor ** (attempt - 1), maximumDelay);\n\t\t\tawait sleep(delay, options.signal === undefined ? {} : { signal: options.signal });\n\t\t}\n\t}\n\n\tthrow new Error(\"重试结束但未获得结果。\");\n}\n\n/**\n * 以固定并发度映射数组,并保持结果顺序。\n *\n * @remarks 任一映射失败后不会再调度新项目,但已经开始的映射无法自动取消;映射器\n * 应使用传入的 `signal` 取消底层工作。\n * @param items - 不会被修改的输入数组。\n * @param concurrency - 同时运行的最大任务数,必须为正安全整数。\n * @param mapper - 接收项目、索引和取消信号的映射函数。\n * @param options - 可选取消信号。\n * @returns 与输入长度和顺序一致的结果数组;稀疏空位保持为空位且不会调用映射器。\n * @throws `RangeError` 当 `concurrency` 不是正安全整数;取消时抛出名称为 `AbortError` 的 `Error`。\n */\nexport async function mapConcurrent<Item, Result>(\n\titems: readonly Item[],\n\tconcurrency: number,\n\tmapper: (item: Item, index: number, signal: AbortSignal | undefined) => Result | PromiseLike<Result>,\n\toptions: ConcurrentMapOptions = {}\n): Promise<Awaited<Result>[]> {\n\tif (!Number.isSafeInteger(concurrency) || concurrency <= 0) {\n\t\tthrow new RangeError(\"`concurrency` 必须是正安全整数。\");\n\t}\n\tthrowIfAborted(options.signal);\n\n\tconst results = new Array<Awaited<Result>>(items.length);\n\tlet nextIndex = 0;\n\tlet failed = false;\n\t/**\n\t * 从共享游标持续领取映射任务。\n\t *\n\t * @remarks JavaScript 单线程执行保证“读取索引并递增”不会被另一个 Worker 插入,因此每个索引只会领取一次。\n\t * @returns 当前 Worker 没有剩余任务时完成。\n\t * @throws 原样传播取消错误或 Mapper 错误,并阻止其他 Worker 领取新任务。\n\t */\n\tconst worker = async (): Promise<void> => {\n\t\twhile (!failed) {\n\t\t\tthrowIfAborted(options.signal);\n\t\t\tconst index = nextIndex;\n\t\t\tif (index >= items.length) return;\n\t\t\tnextIndex += 1;\n\t\t\tif (!(index in items)) continue;\n\t\t\ttry {\n\t\t\t\tresults[index] = await mapper(items[index] as Item, index, options.signal);\n\t\t\t} catch (error) {\n\t\t\t\tfailed = true;\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t}\n\t};\n\n\tconst workerCount = Math.min(concurrency, items.length);\n\tawait Promise.all(Array.from({ length: workerCount }, () => worker()));\n\treturn results;\n}\n\n/**\n * 创建 Promise 感知的防抖函数。\n *\n * @remarks 同一窗口内的所有调用都会等待最后一组参数对应的执行结果;回调错误会原样\n * 拒绝该批次的全部调用,不会留下永久 pending 的 Promise。\n * @param callback - 同步或异步回调。\n * @param delayMs - 0 至 2,147,483,647 的有限等待时间,默认 300 毫秒。\n * @returns 具有取消、立即执行和状态方法的防抖函数。\n * @throws `RangeError` 当延迟不在平台计时器支持范围内。\n */\nexport function debounce<Arguments extends unknown[], Result>(\n\tcallback: AsyncCallback<Arguments, Result>,\n\tdelayMs = 300\n): DebouncedFunction<Arguments, Awaited<Result>> {\n\tconst delay = assertDelay(delayMs, \"delayMs\");\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet latestArguments: Arguments | undefined;\n\tlet waiters: PromiseWaiter<Awaited<Result>>[] = [];\n\n\t/**\n\t * 执行并结算当前防抖批次。\n\t *\n\t * @returns 最后一组参数对应的回调结果。\n\t * @throws 没有待处理批次时抛出 `Error`;回调错误会原样传播给批次中的全部调用方。\n\t */\n\tconst execute = async (): Promise<Awaited<Result>> => {\n\t\tconst arguments_ = latestArguments;\n\t\tif (arguments_ === undefined) {\n\t\t\tthrow new Error(\"当前没有待处理的防抖调用。\");\n\t\t}\n\t\tlatestArguments = undefined;\n\t\ttimer = undefined;\n\t\tconst currentWaiters = waiters;\n\t\twaiters = [];\n\t\ttry {\n\t\t\tconst result = await callback(...arguments_);\n\t\t\tcurrentWaiters.forEach((waiter) => {\n\t\t\t\twaiter.resolve(result);\n\t\t\t});\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tcurrentWaiters.forEach((waiter) => {\n\t\t\t\twaiter.reject(error);\n\t\t\t});\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\t/**\n\t * 更新批次参数并返回当前调用方专属的等待 Promise。\n\t *\n\t * @param arguments_ - 本次调用参数;同批次中只有最后一组参数会执行。\n\t * @returns 与当前批次共享结果、但可独立结算的 Promise。\n\t */\n\tconst debounced = (...arguments_: Arguments): Promise<Awaited<Result>> => {\n\t\tlatestArguments = arguments_;\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t\ttimer = setTimeout(() => {\n\t\t\tvoid execute().catch(() => undefined);\n\t\t}, delay);\n\t\treturn new Promise<Awaited<Result>>((resolve, reject) => waiters.push({ reject, resolve }));\n\t};\n\n\tdebounced.cancel = (reason?: unknown): void => {\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t\ttimer = undefined;\n\t\tlatestArguments = undefined;\n\t\tconst error = reason ?? new Error(\"防抖调用已取消。\");\n\t\twaiters.forEach((waiter) => {\n\t\t\twaiter.reject(error);\n\t\t});\n\t\twaiters = [];\n\t};\n\tdebounced.flush = (): Promise<Awaited<Result>> | undefined => {\n\t\tif (timer === undefined) return undefined;\n\t\tclearTimeout(timer);\n\t\treturn execute();\n\t};\n\tdebounced.pending = (): boolean => timer !== undefined;\n\treturn debounced;\n}\n\n/**\n * 创建 Promise 感知的前缘节流函数。\n *\n * @remarks 窗口内的调用共享首次调用结果。若回调执行时间超过窗口,后续调用仍会等待\n * 当前回调,避免异步操作重入;该函数不安排尾缘调用。\n * @param callback - 同步或异步回调。\n * @param delayMs - 0 至 2,147,483,647 的有限冷却时间,默认 300 毫秒。\n * @returns 具有取消和状态方法的前缘节流函数。\n * @throws `RangeError` 当延迟不在平台计时器支持范围内。\n */\nexport function throttle<Arguments extends unknown[], Result>(\n\tcallback: AsyncCallback<Arguments, Result>,\n\tdelayMs = 300\n): ThrottledFunction<Arguments, Awaited<Result>> {\n\tconst delay = assertDelay(delayMs, \"delayMs\");\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet current: Promise<Awaited<Result>> | undefined;\n\tlet cooling = false;\n\tlet settled = false;\n\n\t/**\n\t * 尝试释放当前节流窗口。\n\t *\n\t * @remarks 只有回调和冷却计时器都结束后才清空共享 Promise,避免长回调发生重入。\n\t */\n\tconst release = (): void => {\n\t\tif (!cooling && settled) current = undefined;\n\t};\n\t/**\n\t * 执行前缘调用或复用当前窗口的共享 Promise。\n\t *\n\t * @param arguments_ - 仅新窗口首个调用会使用的参数。\n\t * @returns 当前窗口首次调用的 Promise。\n\t */\n\tconst throttled = (...arguments_: Arguments): Promise<Awaited<Result>> => {\n\t\tif (current !== undefined) return current;\n\t\tcooling = true;\n\t\tsettled = false;\n\t\tlet invocation: Promise<Awaited<Result>>;\n\t\ttry {\n\t\t\tinvocation = Promise.resolve(callback(...arguments_));\n\t\t} catch (error) {\n\t\t\tinvocation = Promise.reject(error);\n\t\t}\n\t\tcurrent = invocation;\n\t\tinvocation.then(\n\t\t\t() => {\n\t\t\t\tsettled = true;\n\t\t\t\trelease();\n\t\t\t},\n\t\t\t() => {\n\t\t\t\tsettled = true;\n\t\t\t\trelease();\n\t\t\t}\n\t\t);\n\t\ttimer = setTimeout(() => {\n\t\t\ttimer = undefined;\n\t\t\tcooling = false;\n\t\t\trelease();\n\t\t}, delay);\n\t\treturn invocation;\n\t};\n\n\tthrottled.cancel = (): void => {\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t\ttimer = undefined;\n\t\tcooling = false;\n\t\trelease();\n\t};\n\tthrottled.pending = (): boolean => current !== undefined;\n\treturn throttled;\n}\n"],"mappings":"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"}
@@ -80,7 +80,7 @@ declare function encodeLatin1Base64(value: string): string;
80
80
  */
81
81
  declare function decodeLatin1Base64(value: string): string;
82
82
  /**
83
- * 使用固定字典和安全随机前缀编码文本。
83
+ * 使用固定字典和随机前缀编码文本。
84
84
  *
85
85
  * @remarks 给定相同的 6 字符前缀时,默认输出与旧有效载荷逐字符兼容。旧字典在 101–124 字符载荷中引用越界;这里复制末字符作为单字符回退,
86
86
  * 使旧删除字典流程仍能解码。传入 `0` 会同时关闭随机前缀与字典插入。
@@ -88,7 +88,7 @@ declare function decodeLatin1Base64(value: string): string;
88
88
  * @param value - 任意可由 `encodeURIComponent` 处理的 Unicode 文本。
89
89
  * @param prefixLength - 随机字母前缀长度;默认 `6`。
90
90
  * @returns 带随机前缀和兼容字典字符的 Base64 文本;空输入返回空字符串。
91
- * @throws `RangeError` 当前缀长度不是非负安全整数;缺少 Web Crypto 或输入包含孤立代理项时保留平台错误。
91
+ * @throws `RangeError` 当前缀长度不是非负安全整数;输入包含孤立代理项时保留平台错误。
92
92
  */
93
93
  declare function encodeSecureBase64(value: string, prefixLength?: number): string;
94
94
  /**
@@ -1,5 +1,5 @@
1
1
  import { encodeUtf8, getTextDecoder } from "../internal/text.mjs";
2
- import { secureRandomInt } from "../number/index.mjs";
2
+ import { randomInt } from "../number/index.mjs";
3
3
  //#region src/base64/index.ts
4
4
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
5
5
  /**
@@ -9,7 +9,7 @@ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
9
9
  * @returns 带有具体变体名称的 `TypeError`。
10
10
  */
11
11
  const invalidBase64 = (variant) => {
12
- return /* @__PURE__ */ new TypeError(`The value is not valid ${variant === "url" ? "Base64URL" : "Base64"}.`);
12
+ return /* @__PURE__ */ new TypeError(`该值不是有效的 ${variant === "url" ? "Base64URL" : "Base64"}。`);
13
13
  };
14
14
  /**
15
15
  * 校验并规范化 Base64 文本。
@@ -90,7 +90,7 @@ const decodeUtf8 = (bytes) => {
90
90
  try {
91
91
  return textDecoder.decode(bytes);
92
92
  } catch (cause) {
93
- throw new TypeError("The decoded bytes are not valid UTF-8.", { cause });
93
+ throw new TypeError("解码后的字节不是有效的 UTF-8", { cause });
94
94
  }
95
95
  };
96
96
  /**
@@ -184,7 +184,7 @@ function encodeLatin1Base64(value) {
184
184
  const bytes = new Uint8Array(value.length);
185
185
  for (let index = 0; index < value.length; index += 1) {
186
186
  const code = value.charCodeAt(index);
187
- if (code > 255) throw new TypeError("The string to be encoded contains characters outside of the Latin-1 range.");
187
+ if (code > 255) throw new TypeError("待编码字符串包含超出 Latin-1 范围的字符。");
188
188
  bytes[index] = code;
189
189
  }
190
190
  return encodeBase64Bytes(bytes);
@@ -342,18 +342,19 @@ const base64PasswordDictionary = Object.freeze([
342
342
  * @throws `RangeError` 当长度不是非负安全整数。
343
343
  */
344
344
  const assertPrefixLength = (length) => {
345
- if (!Number.isSafeInteger(length) || length < 0) throw new RangeError("prefixStrLength must be a non-negative safe integer.");
345
+ if (!Number.isSafeInteger(length) || length < 0) throw new RangeError("`prefixStrLength` 必须是非负安全整数。");
346
346
  };
347
347
  /**
348
- * 生成 SecureBase64 兼容格式使用的安全随机前缀。
348
+ * 生成 SecureBase64 兼容格式使用的随机前缀。
349
349
  *
350
- * @remarks 前缀字符使用 Web Crypto 均匀生成,用于降低相同明文产生相同载荷的概率;它本身不构成加密。
350
+ * @remarks 优先使用 Web Crypto,能力缺失时回退到 `Math.random()`。前缀只用于降低相同明文
351
+ * 产生相同载荷的概率,本身不构成加密,也不得承担安全用途。
351
352
  * @param length - 需要生成的字符数,调用前必须完成校验。
352
353
  * @returns 由历史字母表组成的文本。
353
354
  */
354
355
  const createRandomPrefix = (length) => {
355
356
  let result = "";
356
- for (let index = 0; index < length; index += 1) result += randomPrefixAlphabet[secureRandomInt(0, 52)] ?? "";
357
+ for (let index = 0; index < length; index += 1) result += randomPrefixAlphabet[randomInt(0, 52)] ?? "";
357
358
  return result;
358
359
  };
359
360
  /**
@@ -375,7 +376,7 @@ const insertDictionaryCharacters = (base64Value) => {
375
376
  /**
376
377
  * 移除历史字典插入的冗余字符。
377
378
  *
378
- * @param base64Value - 已移除安全随机前缀的 SecureBase64 载荷。
379
+ * @param base64Value - 已移除随机前缀的 SecureBase64 载荷。
379
380
  * @returns 可交给标准 Base64 解码器的文本。
380
381
  */
381
382
  const removeDictionaryCharacters = (base64Value) => {
@@ -387,7 +388,7 @@ const removeDictionaryCharacters = (base64Value) => {
387
388
  return result;
388
389
  };
389
390
  /**
390
- * 使用固定字典和安全随机前缀编码文本。
391
+ * 使用固定字典和随机前缀编码文本。
391
392
  *
392
393
  * @remarks 给定相同的 6 字符前缀时,默认输出与旧有效载荷逐字符兼容。旧字典在 101–124 字符载荷中引用越界;这里复制末字符作为单字符回退,
393
394
  * 使旧删除字典流程仍能解码。传入 `0` 会同时关闭随机前缀与字典插入。
@@ -395,7 +396,7 @@ const removeDictionaryCharacters = (base64Value) => {
395
396
  * @param value - 任意可由 `encodeURIComponent` 处理的 Unicode 文本。
396
397
  * @param prefixLength - 随机字母前缀长度;默认 `6`。
397
398
  * @returns 带随机前缀和兼容字典字符的 Base64 文本;空输入返回空字符串。
398
- * @throws `RangeError` 当前缀长度不是非负安全整数;缺少 Web Crypto 或输入包含孤立代理项时保留平台错误。
399
+ * @throws `RangeError` 当前缀长度不是非负安全整数;输入包含孤立代理项时保留平台错误。
399
400
  */
400
401
  function encodeSecureBase64(value, prefixLength = defaultRandomPrefixLength) {
401
402
  if (value.length === 0) return "";
@@ -416,7 +417,7 @@ function encodeSecureBase64(value, prefixLength = defaultRandomPrefixLength) {
416
417
  function decodeSecureBase64(value, prefixLength = defaultRandomPrefixLength) {
417
418
  if (value.length === 0) return "";
418
419
  assertPrefixLength(prefixLength);
419
- if (prefixLength > value.length) throw new TypeError("The Base64 value is shorter than its configured prefix.");
420
+ if (prefixLength > value.length) throw new TypeError("Base64 值的长度小于配置的前缀长度。");
420
421
  let encoded = value.slice(prefixLength);
421
422
  if (prefixLength !== 0) encoded = removeDictionaryCharacters(encoded);
422
423
  return decodeURIComponent(decodeLatin1Base64(encoded));
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/base64/index.ts"],"sourcesContent":["import { encodeUtf8, getTextDecoder } from \"../internal/text\";\nimport { secureRandomInt } from \"../number/index\";\n\nconst alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n/** 内部共享解码器支持的两种字符表规则。 */\ntype Base64Variant = \"standard\" | \"url\";\n\n/**\n * 创建统一的 Base64 输入错误。\n *\n * @param variant - 当前解析的标准 Base64 或 Base64URL 变体。\n * @returns 带有具体变体名称的 `TypeError`。\n */\nconst invalidBase64 = (variant: Base64Variant): TypeError => {\n\treturn new TypeError(`The value is not valid ${variant === \"url\" ? \"Base64URL\" : \"Base64\"}.`);\n};\n\n/**\n * 校验并规范化 Base64 文本。\n *\n * @remarks 标准 Base64 允许 ASCII 空白,Base64URL 不允许;两者最终都会转换为标准字符表和完整填充。\n * @param value - 原始编码文本。\n * @param variant - 需要应用的字符表与空白规则。\n * @returns 使用标准字符表且长度为 4 倍数的文本。\n * @throws `TypeError` 当字符、填充位置或编码长度非法。\n */\nconst normalizeBase64 = (value: string, variant: Base64Variant): string => {\n\tconst withoutWhitespace = variant === \"standard\" ? value.replace(/[\\t\\n\\f\\r ]+/gu, \"\") : value;\n\tconst pattern = variant === \"url\" ? /^[\\w-]*={0,2}$/u : /^[A-Za-z\\d+/]*={0,2}$/u;\n\tif (!pattern.test(withoutWhitespace) || withoutWhitespace.length % 4 === 1) throw invalidBase64(variant);\n\tif (withoutWhitespace.includes(\"=\") && withoutWhitespace.length % 4 !== 0) throw invalidBase64(variant);\n\n\tconst standard = (variant === \"url\" ? withoutWhitespace.replace(/-/gu, \"+\").replace(/_/gu, \"/\") : withoutWhitespace).replace(/=+$/u, \"\");\n\treturn standard.padEnd(Math.ceil(standard.length / 4) * 4, \"=\");\n};\n\n/**\n * 使用固定字符表编码任意字节。\n *\n * @param bytes - 不会被修改的源字节。\n * @returns 带标准 `=` 填充的 Base64 文本。\n */\nconst encodeBytes = (bytes: Uint8Array): string => {\n\tlet result = \"\";\n\tfor (let index = 0; index < bytes.length; index += 3) {\n\t\tconst first = bytes[index] ?? 0;\n\t\tconst second = bytes[index + 1];\n\t\tconst third = bytes[index + 2];\n\t\tconst bitmap = (first << 16) | ((second ?? 0) << 8) | (third ?? 0);\n\t\tresult += alphabet[(bitmap >> 18) & 63] ?? \"\";\n\t\tresult += alphabet[(bitmap >> 12) & 63] ?? \"\";\n\t\tresult += second === undefined ? \"=\" : (alphabet[(bitmap >> 6) & 63] ?? \"\");\n\t\tresult += third === undefined ? \"=\" : (alphabet[bitmap & 63] ?? \"\");\n\t}\n\treturn result;\n};\n\n/**\n * 把 Base64 或 Base64URL 文本解码为字节。\n *\n * @remarks 最后一组未使用位必须为零,以拒绝同一字节序列的非规范编码。\n * @param value - 待解码文本。\n * @param variant - 输入使用的编码变体。\n * @returns 新建的字节数组。\n * @throws `TypeError` 当输入格式或尾部位非法。\n */\nconst decodeBytes = (value: string, variant: Base64Variant): Uint8Array => {\n\tconst normalized = normalizeBase64(value, variant);\n\tif (normalized.length === 0) return new Uint8Array();\n\n\tconst padding = normalized.endsWith(\"==\") ? 2 : normalized.endsWith(\"=\") ? 1 : 0;\n\tconst result = new Uint8Array((normalized.length / 4) * 3 - padding);\n\tlet outputIndex = 0;\n\n\tfor (let index = 0; index < normalized.length; index += 4) {\n\t\tconst first = alphabet.indexOf(normalized[index] ?? \"\");\n\t\tconst second = alphabet.indexOf(normalized[index + 1] ?? \"\");\n\t\tconst thirdCharacter = normalized[index + 2] ?? \"=\";\n\t\tconst fourthCharacter = normalized[index + 3] ?? \"=\";\n\t\tconst third = thirdCharacter === \"=\" ? 0 : alphabet.indexOf(thirdCharacter);\n\t\tconst fourth = fourthCharacter === \"=\" ? 0 : alphabet.indexOf(fourthCharacter);\n\t\tif (first < 0 || second < 0 || third < 0 || fourth < 0) throw invalidBase64(variant);\n\n\t\tconst isLast = index + 4 === normalized.length;\n\t\t// 填充字符对应的未使用低位必须为零,否则不同文本可以解码为同一字节序列。\n\t\tif (isLast && ((padding === 2 && (second & 15) !== 0) || (padding === 1 && (third & 3) !== 0))) {\n\t\t\tthrow invalidBase64(variant);\n\t\t}\n\n\t\tconst bitmap = (first << 18) | (second << 12) | (third << 6) | fourth;\n\t\tif (outputIndex < result.length) result[outputIndex++] = (bitmap >> 16) & 255;\n\t\tif (outputIndex < result.length) result[outputIndex++] = (bitmap >> 8) & 255;\n\t\tif (outputIndex < result.length) result[outputIndex++] = bitmap & 255;\n\t}\n\treturn result;\n};\n\n/**\n * 以 Fatal 模式解码 UTF-8。\n *\n * @param bytes - 待解码字节。\n * @returns 解码后的 JavaScript 字符串。\n * @throws `TypeError` 当字节不是有效 UTF-8;原始平台异常保存在 `cause` 中。\n */\nconst decodeUtf8 = (bytes: Uint8Array): string => {\n\tconst textDecoder = getTextDecoder();\n\ttry {\n\t\treturn textDecoder.decode(bytes);\n\t} catch (cause) {\n\t\tthrow new TypeError(\"The decoded bytes are not valid UTF-8.\", { cause });\n\t}\n};\n\n/**\n * 将任意字节编码为标准 Base64。\n *\n * @param bytes - 不会被修改的字节序列。\n * @returns 带标准 `=` 填充的 Base64 文本。\n */\nexport function encodeBase64Bytes(bytes: Uint8Array): string {\n\treturn encodeBytes(bytes);\n}\n\n/**\n * 解码标准 Base64。\n *\n * @remarks 允许省略填充和包含 ASCII 空白,但拒绝非规范尾部位。\n * @param value - Base64 文本。\n * @returns 新建的字节数组。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function decodeBase64Bytes(value: string): Uint8Array {\n\treturn decodeBytes(value, \"standard\");\n}\n\n/**\n * 将 UTF-8 文本编码为标准 Base64。\n *\n * @param value - 任意 Unicode 字符串。\n * @returns 带标准填充的 Base64 文本。\n * @throws 缺少 Encoding API 时抛出 `Error`。\n */\nexport function encodeBase64(value: string): string {\n\treturn encodeBytes(encodeUtf8(value));\n}\n\n/**\n * 将标准 Base64 解码为 UTF-8 文本。\n *\n * @param value - Base64 文本。\n * @returns 解码后的 Unicode 字符串。\n * @throws Base64 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。\n */\nexport function decodeBase64(value: string): string {\n\treturn decodeUtf8(decodeBase64Bytes(value));\n}\n\n/**\n * 将任意字节编码为无填充 Base64URL。\n *\n * @param bytes - 不会被修改的字节序列。\n * @returns 仅使用 URL 安全字母表的文本。\n */\nexport function encodeBase64UrlBytes(bytes: Uint8Array): string {\n\treturn encodeBytes(bytes).replace(/\\+/gu, \"-\").replace(/\\//gu, \"_\").replace(/=+$/u, \"\");\n}\n\n/**\n * 解码 Base64URL 字节。\n *\n * @remarks 接受带填充和无填充形式,不接受空白或标准 Base64 的 `+`、`/`。\n * @param value - Base64URL 文本。\n * @returns 新建的字节数组。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function decodeBase64UrlBytes(value: string): Uint8Array {\n\treturn decodeBytes(value, \"url\");\n}\n\n/**\n * 将 UTF-8 文本编码为无填充 Base64URL。\n *\n * @param value - 任意 Unicode 字符串。\n * @returns 仅使用 URL 安全字母表的文本。\n * @throws 缺少 Encoding API 时抛出 `Error`。\n */\nexport function encodeBase64Url(value: string): string {\n\treturn encodeBase64UrlBytes(encodeUtf8(value));\n}\n\n/**\n * 将 Base64URL 解码为 UTF-8 文本。\n *\n * @param value - 带填充或无填充的 Base64URL 文本。\n * @returns 解码后的 Unicode 字符串。\n * @throws Base64URL 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。\n */\nexport function decodeBase64Url(value: string): string {\n\treturn decodeUtf8(decodeBase64UrlBytes(value));\n}\n\n/**\n * 把 Latin-1 文本编码为标准 Base64。\n *\n * @param value - 每个 UTF-16 码元都必须位于 0–255 的文本。\n * @returns 带标准填充的 Base64 文本。\n * @throws `TypeError` 当文本包含 Latin-1 范围外的码元。\n */\nexport function encodeLatin1Base64(value: string): string {\n\tconst bytes = new Uint8Array(value.length);\n\tfor (let index = 0; index < value.length; index += 1) {\n\t\tconst code = value.charCodeAt(index);\n\t\tif (code > 255) throw new TypeError(\"The string to be encoded contains characters outside of the Latin-1 range.\");\n\t\tbytes[index] = code;\n\t}\n\treturn encodeBase64Bytes(bytes);\n}\n\n/**\n * 把标准 Base64 解码为 Latin-1 文本。\n *\n * @param value - 标准 Base64 文本;允许 ASCII 空白和省略尾部填充。\n * @returns 每个字节直接映射为同值 UTF-16 码元的文本。\n * @throws `TypeError` 当 Base64 格式或尾部位非法。\n */\nexport function decodeLatin1Base64(value: string): string {\n\tconst bytes = decodeBase64Bytes(value);\n\tlet result = \"\";\n\tfor (const byte of bytes) result += String.fromCharCode(byte);\n\treturn result;\n}\n\nconst randomPrefixAlphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\nconst defaultRandomPrefixLength = 6;\n\n/** SecureBase64 兼容格式中一组不可变的字符插入位置。 */\ninterface Base64PasswordDictionaryEntry {\n\t/** 原始载荷中的固定字符索引;超出短载荷长度时忽略当前条目。 */\n\tindex: number;\n\t/** 从原始载荷复制字符的固定索引;数值属于持久化协议,不能重新计算。 */\n\trandomIndex: number;\n}\n\n/** 固定 SecureBase64 字典;索引、顺序与映射属于持久化格式,禁止修改。 */\nconst base64PasswordDictionary: readonly Readonly<Base64PasswordDictionaryEntry>[] = Object.freeze([\n\t{ index: 977, randomIndex: 188 },\n\t{ index: 926, randomIndex: 201 },\n\t{ index: 851, randomIndex: 225 },\n\t{ index: 700, randomIndex: 255 },\n\t{ index: 600, randomIndex: 268 },\n\t{ index: 500, randomIndex: 277 },\n\t{ index: 400, randomIndex: 288 },\n\t{ index: 330, randomIndex: 327 },\n\t{ index: 300, randomIndex: 180 },\n\t{ index: 200, randomIndex: 178 },\n\t{ index: 100, randomIndex: 124 },\n\t{ index: 98, randomIndex: 95 },\n\t{ index: 92, randomIndex: 90 },\n\t{ index: 91, randomIndex: 87 },\n\t{ index: 88, randomIndex: 84 },\n\t{ index: 82, randomIndex: 79 },\n\t{ index: 78, randomIndex: 71 },\n\t{ index: 72, randomIndex: 69 },\n\t{ index: 68, randomIndex: 66 },\n\t{ index: 59, randomIndex: 55 },\n\t{ index: 48, randomIndex: 43 },\n\t{ index: 42, randomIndex: 37 },\n\t{ index: 36, randomIndex: 30 },\n\t{ index: 33, randomIndex: 27 },\n\t{ index: 24, randomIndex: 20 },\n\t{ index: 23, randomIndex: 18 },\n\t{ index: 21, randomIndex: 16 },\n\t{ index: 17, randomIndex: 14 },\n\t{ index: 13, randomIndex: 9 },\n\t{ index: 7, randomIndex: 4 },\n\t{ index: 5, randomIndex: 3 },\n\t{ index: 2, randomIndex: 1 },\n]);\n\n/**\n * 校验 SecureBase64 兼容格式的前缀长度。\n *\n * @param length - 待校验的前缀字符数。\n * @throws `RangeError` 当长度不是非负安全整数。\n */\nconst assertPrefixLength = (length: number): void => {\n\tif (!Number.isSafeInteger(length) || length < 0) throw new RangeError(\"prefixStrLength must be a non-negative safe integer.\");\n};\n\n/**\n * 生成 SecureBase64 兼容格式使用的安全随机前缀。\n *\n * @remarks 前缀字符使用 Web Crypto 均匀生成,用于降低相同明文产生相同载荷的概率;它本身不构成加密。\n * @param length - 需要生成的字符数,调用前必须完成校验。\n * @returns 由历史字母表组成的文本。\n */\nconst createRandomPrefix = (length: number): string => {\n\tlet result = \"\";\n\tfor (let index = 0; index < length; index += 1) {\n\t\tresult += randomPrefixAlphabet[secureRandomInt(0, randomPrefixAlphabet.length)] ?? \"\";\n\t}\n\treturn result;\n};\n\n/**\n * 按固定字典向 Base64 文本插入冗余字符。\n *\n * @remarks 字典按高索引到低索引排列,确保插入不会改变尚未处理的位置。\n * @param base64Value - 尚未插入兼容字典字符的 Base64 文本。\n * @returns 与旧持久化格式兼容的 SecureBase64 载荷。\n */\nconst insertDictionaryCharacters = (base64Value: string): string => {\n\tlet result = base64Value;\n\tfor (const item of base64PasswordDictionary) {\n\t\tif (item.index >= base64Value.length) continue;\n\t\t// 旧字典的 index=100 项会在 101–124 字符载荷中越界;退回末字符可保持单字符插入协议,旧解码器仍能移除。\n\t\tconst character = base64Value[item.randomIndex] ?? base64Value.at(-1) ?? \"\";\n\t\tresult = result.slice(0, item.index) + character + result.slice(item.index);\n\t}\n\treturn result;\n};\n\n/**\n * 移除历史字典插入的冗余字符。\n *\n * @param base64Value - 已移除安全随机前缀的 SecureBase64 载荷。\n * @returns 可交给标准 Base64 解码器的文本。\n */\nconst removeDictionaryCharacters = (base64Value: string): string => {\n\tlet result = base64Value;\n\tfor (let index = base64PasswordDictionary.length - 1; index >= 0; index -= 1) {\n\t\tconst item = base64PasswordDictionary[index];\n\t\tif (item !== undefined && item.index < base64Value.length) result = result.slice(0, item.index) + result.slice(item.index + 1);\n\t}\n\treturn result;\n};\n\n/**\n * 使用固定字典和安全随机前缀编码文本。\n *\n * @remarks 给定相同的 6 字符前缀时,默认输出与旧有效载荷逐字符兼容。旧字典在 101–124 字符载荷中引用越界;这里复制末字符作为单字符回退,\n * 使旧删除字典流程仍能解码。传入 `0` 会同时关闭随机前缀与字典插入。\n * 该格式只是可逆编码,不提供加密、完整性或身份认证。\n * @param value - 任意可由 `encodeURIComponent` 处理的 Unicode 文本。\n * @param prefixLength - 随机字母前缀长度;默认 `6`。\n * @returns 带随机前缀和兼容字典字符的 Base64 文本;空输入返回空字符串。\n * @throws `RangeError` 当前缀长度不是非负安全整数;缺少 Web Crypto 或输入包含孤立代理项时保留平台错误。\n */\nexport function encodeSecureBase64(value: string, prefixLength: number = defaultRandomPrefixLength): string {\n\tif (value.length === 0) return \"\";\n\tassertPrefixLength(prefixLength);\n\tconst prefix = createRandomPrefix(prefixLength);\n\tlet encoded = encodeLatin1Base64(encodeURIComponent(value));\n\tif (prefixLength !== 0) encoded = insertDictionaryCharacters(encoded);\n\treturn prefix + encoded;\n}\n\n/**\n * 解码 {@link encodeSecureBase64} 生成的 SecureBase64 兼容格式。\n *\n * @param value - SecureBase64 文本;必须使用与编码时相同的前缀长度。\n * @param prefixLength - 需要移除的前缀长度;默认 `6`,传入 `0` 时不移除字典字符。\n * @returns 解码后的 Unicode 文本;空输入返回空字符串。\n * @throws `RangeError` 当前缀长度不是非负安全整数;载荷、Base64 或 URI 编码非法时抛出 `TypeError` 或 `URIError`。\n */\nexport function decodeSecureBase64(value: string, prefixLength: number = defaultRandomPrefixLength): string {\n\tif (value.length === 0) return \"\";\n\tassertPrefixLength(prefixLength);\n\tif (prefixLength > value.length) throw new TypeError(\"The Base64 value is shorter than its configured prefix.\");\n\tlet encoded = value.slice(prefixLength);\n\tif (prefixLength !== 0) encoded = removeDictionaryCharacters(encoded);\n\treturn decodeURIComponent(decodeLatin1Base64(encoded));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/base64/index.ts"],"sourcesContent":["import { encodeUtf8, getTextDecoder } from \"../internal/text\";\nimport { randomInt } from \"../number/index\";\n\nconst alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n/** 内部共享解码器支持的两种字符表规则。 */\ntype Base64Variant = \"standard\" | \"url\";\n\n/**\n * 创建统一的 Base64 输入错误。\n *\n * @param variant - 当前解析的标准 Base64 或 Base64URL 变体。\n * @returns 带有具体变体名称的 `TypeError`。\n */\nconst invalidBase64 = (variant: Base64Variant): TypeError => {\n\treturn new TypeError(`该值不是有效的 ${variant === \"url\" ? \"Base64URL\" : \"Base64\"}。`);\n};\n\n/**\n * 校验并规范化 Base64 文本。\n *\n * @remarks 标准 Base64 允许 ASCII 空白,Base64URL 不允许;两者最终都会转换为标准字符表和完整填充。\n * @param value - 原始编码文本。\n * @param variant - 需要应用的字符表与空白规则。\n * @returns 使用标准字符表且长度为 4 倍数的文本。\n * @throws `TypeError` 当字符、填充位置或编码长度非法。\n */\nconst normalizeBase64 = (value: string, variant: Base64Variant): string => {\n\tconst withoutWhitespace = variant === \"standard\" ? value.replace(/[\\t\\n\\f\\r ]+/gu, \"\") : value;\n\tconst pattern = variant === \"url\" ? /^[\\w-]*={0,2}$/u : /^[A-Za-z\\d+/]*={0,2}$/u;\n\tif (!pattern.test(withoutWhitespace) || withoutWhitespace.length % 4 === 1) throw invalidBase64(variant);\n\tif (withoutWhitespace.includes(\"=\") && withoutWhitespace.length % 4 !== 0) throw invalidBase64(variant);\n\n\tconst standard = (variant === \"url\" ? withoutWhitespace.replace(/-/gu, \"+\").replace(/_/gu, \"/\") : withoutWhitespace).replace(/=+$/u, \"\");\n\treturn standard.padEnd(Math.ceil(standard.length / 4) * 4, \"=\");\n};\n\n/**\n * 使用固定字符表编码任意字节。\n *\n * @param bytes - 不会被修改的源字节。\n * @returns 带标准 `=` 填充的 Base64 文本。\n */\nconst encodeBytes = (bytes: Uint8Array): string => {\n\tlet result = \"\";\n\tfor (let index = 0; index < bytes.length; index += 3) {\n\t\tconst first = bytes[index] ?? 0;\n\t\tconst second = bytes[index + 1];\n\t\tconst third = bytes[index + 2];\n\t\tconst bitmap = (first << 16) | ((second ?? 0) << 8) | (third ?? 0);\n\t\tresult += alphabet[(bitmap >> 18) & 63] ?? \"\";\n\t\tresult += alphabet[(bitmap >> 12) & 63] ?? \"\";\n\t\tresult += second === undefined ? \"=\" : (alphabet[(bitmap >> 6) & 63] ?? \"\");\n\t\tresult += third === undefined ? \"=\" : (alphabet[bitmap & 63] ?? \"\");\n\t}\n\treturn result;\n};\n\n/**\n * 把 Base64 或 Base64URL 文本解码为字节。\n *\n * @remarks 最后一组未使用位必须为零,以拒绝同一字节序列的非规范编码。\n * @param value - 待解码文本。\n * @param variant - 输入使用的编码变体。\n * @returns 新建的字节数组。\n * @throws `TypeError` 当输入格式或尾部位非法。\n */\nconst decodeBytes = (value: string, variant: Base64Variant): Uint8Array => {\n\tconst normalized = normalizeBase64(value, variant);\n\tif (normalized.length === 0) return new Uint8Array();\n\n\tconst padding = normalized.endsWith(\"==\") ? 2 : normalized.endsWith(\"=\") ? 1 : 0;\n\tconst result = new Uint8Array((normalized.length / 4) * 3 - padding);\n\tlet outputIndex = 0;\n\n\tfor (let index = 0; index < normalized.length; index += 4) {\n\t\tconst first = alphabet.indexOf(normalized[index] ?? \"\");\n\t\tconst second = alphabet.indexOf(normalized[index + 1] ?? \"\");\n\t\tconst thirdCharacter = normalized[index + 2] ?? \"=\";\n\t\tconst fourthCharacter = normalized[index + 3] ?? \"=\";\n\t\tconst third = thirdCharacter === \"=\" ? 0 : alphabet.indexOf(thirdCharacter);\n\t\tconst fourth = fourthCharacter === \"=\" ? 0 : alphabet.indexOf(fourthCharacter);\n\t\tif (first < 0 || second < 0 || third < 0 || fourth < 0) throw invalidBase64(variant);\n\n\t\tconst isLast = index + 4 === normalized.length;\n\t\t// 填充字符对应的未使用低位必须为零,否则不同文本可以解码为同一字节序列。\n\t\tif (isLast && ((padding === 2 && (second & 15) !== 0) || (padding === 1 && (third & 3) !== 0))) {\n\t\t\tthrow invalidBase64(variant);\n\t\t}\n\n\t\tconst bitmap = (first << 18) | (second << 12) | (third << 6) | fourth;\n\t\tif (outputIndex < result.length) result[outputIndex++] = (bitmap >> 16) & 255;\n\t\tif (outputIndex < result.length) result[outputIndex++] = (bitmap >> 8) & 255;\n\t\tif (outputIndex < result.length) result[outputIndex++] = bitmap & 255;\n\t}\n\treturn result;\n};\n\n/**\n * 以 Fatal 模式解码 UTF-8。\n *\n * @param bytes - 待解码字节。\n * @returns 解码后的 JavaScript 字符串。\n * @throws `TypeError` 当字节不是有效 UTF-8;原始平台异常保存在 `cause` 中。\n */\nconst decodeUtf8 = (bytes: Uint8Array): string => {\n\tconst textDecoder = getTextDecoder();\n\ttry {\n\t\treturn textDecoder.decode(bytes);\n\t} catch (cause) {\n\t\tthrow new TypeError(\"解码后的字节不是有效的 UTF-8。\", { cause });\n\t}\n};\n\n/**\n * 将任意字节编码为标准 Base64。\n *\n * @param bytes - 不会被修改的字节序列。\n * @returns 带标准 `=` 填充的 Base64 文本。\n */\nexport function encodeBase64Bytes(bytes: Uint8Array): string {\n\treturn encodeBytes(bytes);\n}\n\n/**\n * 解码标准 Base64。\n *\n * @remarks 允许省略填充和包含 ASCII 空白,但拒绝非规范尾部位。\n * @param value - Base64 文本。\n * @returns 新建的字节数组。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function decodeBase64Bytes(value: string): Uint8Array {\n\treturn decodeBytes(value, \"standard\");\n}\n\n/**\n * 将 UTF-8 文本编码为标准 Base64。\n *\n * @param value - 任意 Unicode 字符串。\n * @returns 带标准填充的 Base64 文本。\n * @throws 缺少 Encoding API 时抛出 `Error`。\n */\nexport function encodeBase64(value: string): string {\n\treturn encodeBytes(encodeUtf8(value));\n}\n\n/**\n * 将标准 Base64 解码为 UTF-8 文本。\n *\n * @param value - Base64 文本。\n * @returns 解码后的 Unicode 字符串。\n * @throws Base64 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。\n */\nexport function decodeBase64(value: string): string {\n\treturn decodeUtf8(decodeBase64Bytes(value));\n}\n\n/**\n * 将任意字节编码为无填充 Base64URL。\n *\n * @param bytes - 不会被修改的字节序列。\n * @returns 仅使用 URL 安全字母表的文本。\n */\nexport function encodeBase64UrlBytes(bytes: Uint8Array): string {\n\treturn encodeBytes(bytes).replace(/\\+/gu, \"-\").replace(/\\//gu, \"_\").replace(/=+$/u, \"\");\n}\n\n/**\n * 解码 Base64URL 字节。\n *\n * @remarks 接受带填充和无填充形式,不接受空白或标准 Base64 的 `+`、`/`。\n * @param value - Base64URL 文本。\n * @returns 新建的字节数组。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function decodeBase64UrlBytes(value: string): Uint8Array {\n\treturn decodeBytes(value, \"url\");\n}\n\n/**\n * 将 UTF-8 文本编码为无填充 Base64URL。\n *\n * @param value - 任意 Unicode 字符串。\n * @returns 仅使用 URL 安全字母表的文本。\n * @throws 缺少 Encoding API 时抛出 `Error`。\n */\nexport function encodeBase64Url(value: string): string {\n\treturn encodeBase64UrlBytes(encodeUtf8(value));\n}\n\n/**\n * 将 Base64URL 解码为 UTF-8 文本。\n *\n * @param value - 带填充或无填充的 Base64URL 文本。\n * @returns 解码后的 Unicode 字符串。\n * @throws Base64URL 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。\n */\nexport function decodeBase64Url(value: string): string {\n\treturn decodeUtf8(decodeBase64UrlBytes(value));\n}\n\n/**\n * 把 Latin-1 文本编码为标准 Base64。\n *\n * @param value - 每个 UTF-16 码元都必须位于 0–255 的文本。\n * @returns 带标准填充的 Base64 文本。\n * @throws `TypeError` 当文本包含 Latin-1 范围外的码元。\n */\nexport function encodeLatin1Base64(value: string): string {\n\tconst bytes = new Uint8Array(value.length);\n\tfor (let index = 0; index < value.length; index += 1) {\n\t\tconst code = value.charCodeAt(index);\n\t\tif (code > 255) throw new TypeError(\"待编码字符串包含超出 Latin-1 范围的字符。\");\n\t\tbytes[index] = code;\n\t}\n\treturn encodeBase64Bytes(bytes);\n}\n\n/**\n * 把标准 Base64 解码为 Latin-1 文本。\n *\n * @param value - 标准 Base64 文本;允许 ASCII 空白和省略尾部填充。\n * @returns 每个字节直接映射为同值 UTF-16 码元的文本。\n * @throws `TypeError` 当 Base64 格式或尾部位非法。\n */\nexport function decodeLatin1Base64(value: string): string {\n\tconst bytes = decodeBase64Bytes(value);\n\tlet result = \"\";\n\tfor (const byte of bytes) result += String.fromCharCode(byte);\n\treturn result;\n}\n\nconst randomPrefixAlphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\nconst defaultRandomPrefixLength = 6;\n\n/** SecureBase64 兼容格式中一组不可变的字符插入位置。 */\ninterface Base64PasswordDictionaryEntry {\n\t/** 原始载荷中的固定字符索引;超出短载荷长度时忽略当前条目。 */\n\tindex: number;\n\t/** 从原始载荷复制字符的固定索引;数值属于持久化协议,不能重新计算。 */\n\trandomIndex: number;\n}\n\n/** 固定 SecureBase64 字典;索引、顺序与映射属于持久化格式,禁止修改。 */\nconst base64PasswordDictionary: readonly Readonly<Base64PasswordDictionaryEntry>[] = Object.freeze([\n\t{ index: 977, randomIndex: 188 },\n\t{ index: 926, randomIndex: 201 },\n\t{ index: 851, randomIndex: 225 },\n\t{ index: 700, randomIndex: 255 },\n\t{ index: 600, randomIndex: 268 },\n\t{ index: 500, randomIndex: 277 },\n\t{ index: 400, randomIndex: 288 },\n\t{ index: 330, randomIndex: 327 },\n\t{ index: 300, randomIndex: 180 },\n\t{ index: 200, randomIndex: 178 },\n\t{ index: 100, randomIndex: 124 },\n\t{ index: 98, randomIndex: 95 },\n\t{ index: 92, randomIndex: 90 },\n\t{ index: 91, randomIndex: 87 },\n\t{ index: 88, randomIndex: 84 },\n\t{ index: 82, randomIndex: 79 },\n\t{ index: 78, randomIndex: 71 },\n\t{ index: 72, randomIndex: 69 },\n\t{ index: 68, randomIndex: 66 },\n\t{ index: 59, randomIndex: 55 },\n\t{ index: 48, randomIndex: 43 },\n\t{ index: 42, randomIndex: 37 },\n\t{ index: 36, randomIndex: 30 },\n\t{ index: 33, randomIndex: 27 },\n\t{ index: 24, randomIndex: 20 },\n\t{ index: 23, randomIndex: 18 },\n\t{ index: 21, randomIndex: 16 },\n\t{ index: 17, randomIndex: 14 },\n\t{ index: 13, randomIndex: 9 },\n\t{ index: 7, randomIndex: 4 },\n\t{ index: 5, randomIndex: 3 },\n\t{ index: 2, randomIndex: 1 },\n]);\n\n/**\n * 校验 SecureBase64 兼容格式的前缀长度。\n *\n * @param length - 待校验的前缀字符数。\n * @throws `RangeError` 当长度不是非负安全整数。\n */\nconst assertPrefixLength = (length: number): void => {\n\tif (!Number.isSafeInteger(length) || length < 0) throw new RangeError(\"`prefixStrLength` 必须是非负安全整数。\");\n};\n\n/**\n * 生成 SecureBase64 兼容格式使用的随机前缀。\n *\n * @remarks 优先使用 Web Crypto,能力缺失时回退到 `Math.random()`。前缀只用于降低相同明文\n * 产生相同载荷的概率,本身不构成加密,也不得承担安全用途。\n * @param length - 需要生成的字符数,调用前必须完成校验。\n * @returns 由历史字母表组成的文本。\n */\nconst createRandomPrefix = (length: number): string => {\n\tlet result = \"\";\n\tfor (let index = 0; index < length; index += 1) {\n\t\tresult += randomPrefixAlphabet[randomInt(0, randomPrefixAlphabet.length)] ?? \"\";\n\t}\n\treturn result;\n};\n\n/**\n * 按固定字典向 Base64 文本插入冗余字符。\n *\n * @remarks 字典按高索引到低索引排列,确保插入不会改变尚未处理的位置。\n * @param base64Value - 尚未插入兼容字典字符的 Base64 文本。\n * @returns 与旧持久化格式兼容的 SecureBase64 载荷。\n */\nconst insertDictionaryCharacters = (base64Value: string): string => {\n\tlet result = base64Value;\n\tfor (const item of base64PasswordDictionary) {\n\t\tif (item.index >= base64Value.length) continue;\n\t\t// 旧字典的 index=100 项会在 101–124 字符载荷中越界;退回末字符可保持单字符插入协议,旧解码器仍能移除。\n\t\tconst character = base64Value[item.randomIndex] ?? base64Value.at(-1) ?? \"\";\n\t\tresult = result.slice(0, item.index) + character + result.slice(item.index);\n\t}\n\treturn result;\n};\n\n/**\n * 移除历史字典插入的冗余字符。\n *\n * @param base64Value - 已移除随机前缀的 SecureBase64 载荷。\n * @returns 可交给标准 Base64 解码器的文本。\n */\nconst removeDictionaryCharacters = (base64Value: string): string => {\n\tlet result = base64Value;\n\tfor (let index = base64PasswordDictionary.length - 1; index >= 0; index -= 1) {\n\t\tconst item = base64PasswordDictionary[index];\n\t\tif (item !== undefined && item.index < base64Value.length) result = result.slice(0, item.index) + result.slice(item.index + 1);\n\t}\n\treturn result;\n};\n\n/**\n * 使用固定字典和随机前缀编码文本。\n *\n * @remarks 给定相同的 6 字符前缀时,默认输出与旧有效载荷逐字符兼容。旧字典在 101–124 字符载荷中引用越界;这里复制末字符作为单字符回退,\n * 使旧删除字典流程仍能解码。传入 `0` 会同时关闭随机前缀与字典插入。\n * 该格式只是可逆编码,不提供加密、完整性或身份认证。\n * @param value - 任意可由 `encodeURIComponent` 处理的 Unicode 文本。\n * @param prefixLength - 随机字母前缀长度;默认 `6`。\n * @returns 带随机前缀和兼容字典字符的 Base64 文本;空输入返回空字符串。\n * @throws `RangeError` 当前缀长度不是非负安全整数;输入包含孤立代理项时保留平台错误。\n */\nexport function encodeSecureBase64(value: string, prefixLength: number = defaultRandomPrefixLength): string {\n\tif (value.length === 0) return \"\";\n\tassertPrefixLength(prefixLength);\n\tconst prefix = createRandomPrefix(prefixLength);\n\tlet encoded = encodeLatin1Base64(encodeURIComponent(value));\n\tif (prefixLength !== 0) encoded = insertDictionaryCharacters(encoded);\n\treturn prefix + encoded;\n}\n\n/**\n * 解码 {@link encodeSecureBase64} 生成的 SecureBase64 兼容格式。\n *\n * @param value - SecureBase64 文本;必须使用与编码时相同的前缀长度。\n * @param prefixLength - 需要移除的前缀长度;默认 `6`,传入 `0` 时不移除字典字符。\n * @returns 解码后的 Unicode 文本;空输入返回空字符串。\n * @throws `RangeError` 当前缀长度不是非负安全整数;载荷、Base64 或 URI 编码非法时抛出 `TypeError` 或 `URIError`。\n */\nexport function decodeSecureBase64(value: string, prefixLength: number = defaultRandomPrefixLength): string {\n\tif (value.length === 0) return \"\";\n\tassertPrefixLength(prefixLength);\n\tif (prefixLength > value.length) throw new TypeError(\"Base64 值的长度小于配置的前缀长度。\");\n\tlet encoded = value.slice(prefixLength);\n\tif (prefixLength !== 0) encoded = removeDictionaryCharacters(encoded);\n\treturn decodeURIComponent(decodeLatin1Base64(encoded));\n}\n"],"mappings":";;;AAGA,MAAM,WAAW;;;;;;;AAWjB,MAAM,iBAAiB,YAAsC;CAC5D,uBAAO,IAAI,UAAU,WAAW,YAAY,QAAQ,cAAc,SAAS,EAAE;AAC9E;;;;;;;;;;AAWA,MAAM,mBAAmB,OAAe,YAAmC;CAC1E,MAAM,oBAAoB,YAAY,aAAa,MAAM,QAAQ,kBAAkB,EAAE,IAAI;CAEzF,IAAI,EADY,YAAY,QAAQ,oBAAoB,yBAAA,CAC3C,KAAK,iBAAiB,KAAK,kBAAkB,SAAS,MAAM,GAAG,MAAM,cAAc,OAAO;CACvG,IAAI,kBAAkB,SAAS,GAAG,KAAK,kBAAkB,SAAS,MAAM,GAAG,MAAM,cAAc,OAAO;CAEtG,MAAM,YAAY,YAAY,QAAQ,kBAAkB,QAAQ,OAAO,GAAG,CAAC,CAAC,QAAQ,OAAO,GAAG,IAAI,kBAAA,CAAmB,QAAQ,QAAQ,EAAE;CACvI,OAAO,SAAS,OAAO,KAAK,KAAK,SAAS,SAAS,CAAC,IAAI,GAAG,GAAG;AAC/D;;;;;;;AAQA,MAAM,eAAe,UAA8B;CAClD,IAAI,SAAS;CACb,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACrD,MAAM,QAAQ,MAAM,UAAU;EAC9B,MAAM,SAAS,MAAM,QAAQ;EAC7B,MAAM,QAAQ,MAAM,QAAQ;EAC5B,MAAM,SAAU,SAAS,MAAQ,UAAU,MAAM,KAAM,SAAS;EAChE,UAAU,SAAU,UAAU,KAAM,OAAO;EAC3C,UAAU,SAAU,UAAU,KAAM,OAAO;EAC3C,UAAU,WAAW,KAAA,IAAY,MAAO,SAAU,UAAU,IAAK,OAAO;EACxE,UAAU,UAAU,KAAA,IAAY,MAAO,SAAS,SAAS,OAAO;CACjE;CACA,OAAO;AACR;;;;;;;;;;AAWA,MAAM,eAAe,OAAe,YAAuC;CAC1E,MAAM,aAAa,gBAAgB,OAAO,OAAO;CACjD,IAAI,WAAW,WAAW,GAAG,uBAAO,IAAI,WAAW;CAEnD,MAAM,UAAU,WAAW,SAAS,IAAI,IAAI,IAAI,WAAW,SAAS,GAAG,IAAI,IAAI;CAC/E,MAAM,SAAS,IAAI,WAAY,WAAW,SAAS,IAAK,IAAI,OAAO;CACnE,IAAI,cAAc;CAElB,KAAK,IAAI,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS,GAAG;EAC1D,MAAM,QAAQ,SAAS,QAAQ,WAAW,UAAU,EAAE;EACtD,MAAM,SAAS,SAAS,QAAQ,WAAW,QAAQ,MAAM,EAAE;EAC3D,MAAM,iBAAiB,WAAW,QAAQ,MAAM;EAChD,MAAM,kBAAkB,WAAW,QAAQ,MAAM;EACjD,MAAM,QAAQ,mBAAmB,MAAM,IAAI,SAAS,QAAQ,cAAc;EAC1E,MAAM,SAAS,oBAAoB,MAAM,IAAI,SAAS,QAAQ,eAAe;EAC7E,IAAI,QAAQ,KAAK,SAAS,KAAK,QAAQ,KAAK,SAAS,GAAG,MAAM,cAAc,OAAO;EAInF,IAFe,QAAQ,MAAM,WAAW,WAExB,YAAY,MAAM,SAAS,QAAQ,KAAO,YAAY,MAAM,QAAQ,OAAO,IAC1F,MAAM,cAAc,OAAO;EAG5B,MAAM,SAAU,SAAS,KAAO,UAAU,KAAO,SAAS,IAAK;EAC/D,IAAI,cAAc,OAAO,QAAQ,OAAO,iBAAkB,UAAU,KAAM;EAC1E,IAAI,cAAc,OAAO,QAAQ,OAAO,iBAAkB,UAAU,IAAK;EACzE,IAAI,cAAc,OAAO,QAAQ,OAAO,iBAAiB,SAAS;CACnE;CACA,OAAO;AACR;;;;;;;;AASA,MAAM,cAAc,UAA8B;CACjD,MAAM,cAAc,eAAe;CACnC,IAAI;EACH,OAAO,YAAY,OAAO,KAAK;CAChC,SAAS,OAAO;EACf,MAAM,IAAI,UAAU,sBAAsB,EAAE,MAAM,CAAC;CACpD;AACD;;;;;;;AAQA,SAAgB,kBAAkB,OAA2B;CAC5D,OAAO,YAAY,KAAK;AACzB;;;;;;;;;AAUA,SAAgB,kBAAkB,OAA2B;CAC5D,OAAO,YAAY,OAAO,UAAU;AACrC;;;;;;;;AASA,SAAgB,aAAa,OAAuB;CACnD,OAAO,YAAY,WAAW,KAAK,CAAC;AACrC;;;;;;;;AASA,SAAgB,aAAa,OAAuB;CACnD,OAAO,WAAW,kBAAkB,KAAK,CAAC;AAC3C;;;;;;;AAQA,SAAgB,qBAAqB,OAA2B;CAC/D,OAAO,YAAY,KAAK,CAAC,CAAC,QAAQ,QAAQ,GAAG,CAAC,CAAC,QAAQ,QAAQ,GAAG,CAAC,CAAC,QAAQ,QAAQ,EAAE;AACvF;;;;;;;;;AAUA,SAAgB,qBAAqB,OAA2B;CAC/D,OAAO,YAAY,OAAO,KAAK;AAChC;;;;;;;;AASA,SAAgB,gBAAgB,OAAuB;CACtD,OAAO,qBAAqB,WAAW,KAAK,CAAC;AAC9C;;;;;;;;AASA,SAAgB,gBAAgB,OAAuB;CACtD,OAAO,WAAW,qBAAqB,KAAK,CAAC;AAC9C;;;;;;;;AASA,SAAgB,mBAAmB,OAAuB;CACzD,MAAM,QAAQ,IAAI,WAAW,MAAM,MAAM;CACzC,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACrD,MAAM,OAAO,MAAM,WAAW,KAAK;EACnC,IAAI,OAAO,KAAK,MAAM,IAAI,UAAU,2BAA2B;EAC/D,MAAM,SAAS;CAChB;CACA,OAAO,kBAAkB,KAAK;AAC/B;;;;;;;;AASA,SAAgB,mBAAmB,OAAuB;CACzD,MAAM,QAAQ,kBAAkB,KAAK;CACrC,IAAI,SAAS;CACb,KAAK,MAAM,QAAQ,OAAO,UAAU,OAAO,aAAa,IAAI;CAC5D,OAAO;AACR;AAEA,MAAM,uBAAuB;AAC7B,MAAM,4BAA4B;;AAWlC,MAAM,2BAA+E,OAAO,OAAO;CAClG;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAK,aAAa;CAAI;CAC/B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAG;CAC7B;EAAE,OAAO;EAAI,aAAa;CAAE;CAC5B;EAAE,OAAO;EAAG,aAAa;CAAE;CAC3B;EAAE,OAAO;EAAG,aAAa;CAAE;CAC3B;EAAE,OAAO;EAAG,aAAa;CAAE;AAC5B,CAAC;;;;;;;AAQD,MAAM,sBAAsB,WAAyB;CACpD,IAAI,CAAC,OAAO,cAAc,MAAM,KAAK,SAAS,GAAG,MAAM,IAAI,WAAW,8BAA8B;AACrG;;;;;;;;;AAUA,MAAM,sBAAsB,WAA2B;CACtD,IAAI,SAAS;CACb,KAAK,IAAI,QAAQ,GAAG,QAAQ,QAAQ,SAAS,GAC5C,UAAU,qBAAqB,UAAU,GAAG,EAA2B,MAAM;CAE9E,OAAO;AACR;;;;;;;;AASA,MAAM,8BAA8B,gBAAgC;CACnE,IAAI,SAAS;CACb,KAAK,MAAM,QAAQ,0BAA0B;EAC5C,IAAI,KAAK,SAAS,YAAY,QAAQ;EAEtC,MAAM,YAAY,YAAY,KAAK,gBAAgB,YAAY,GAAG,EAAE,KAAK;EACzE,SAAS,OAAO,MAAM,GAAG,KAAK,KAAK,IAAI,YAAY,OAAO,MAAM,KAAK,KAAK;CAC3E;CACA,OAAO;AACR;;;;;;;AAQA,MAAM,8BAA8B,gBAAgC;CACnE,IAAI,SAAS;CACb,KAAK,IAAI,QAAQ,yBAAyB,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;EAC7E,MAAM,OAAO,yBAAyB;EACtC,IAAI,SAAS,KAAA,KAAa,KAAK,QAAQ,YAAY,QAAQ,SAAS,OAAO,MAAM,GAAG,KAAK,KAAK,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC;CAC9H;CACA,OAAO;AACR;;;;;;;;;;;;AAaA,SAAgB,mBAAmB,OAAe,eAAuB,2BAAmC;CAC3G,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,mBAAmB,YAAY;CAC/B,MAAM,SAAS,mBAAmB,YAAY;CAC9C,IAAI,UAAU,mBAAmB,mBAAmB,KAAK,CAAC;CAC1D,IAAI,iBAAiB,GAAG,UAAU,2BAA2B,OAAO;CACpE,OAAO,SAAS;AACjB;;;;;;;;;AAUA,SAAgB,mBAAmB,OAAe,eAAuB,2BAAmC;CAC3G,IAAI,MAAM,WAAW,GAAG,OAAO;CAC/B,mBAAmB,YAAY;CAC/B,IAAI,eAAe,MAAM,QAAQ,MAAM,IAAI,UAAU,uBAAuB;CAC5E,IAAI,UAAU,MAAM,MAAM,YAAY;CACtC,IAAI,iBAAiB,GAAG,UAAU,2BAA2B,OAAO;CACpE,OAAO,mBAAmB,mBAAmB,OAAO,CAAC;AACtD"}