@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
@@ -7,7 +7,7 @@
7
7
  * @throws `RangeError` 当值非有限或超出 0 至 255。
8
8
  */
9
9
  const assertRgbChannel = (value, channel) => {
10
- if (!Number.isFinite(value) || value < 0 || value > 255) throw new RangeError(`${channel} must be a finite number from 0 through 255.`);
10
+ if (!Number.isFinite(value) || value < 0 || value > 255) throw new RangeError(`\`${channel}\` 必须是 0 255 之间的有限数。`);
11
11
  };
12
12
  /**
13
13
  * 校验透明度通道。
@@ -16,7 +16,7 @@ const assertRgbChannel = (value, channel) => {
16
16
  * @throws `RangeError` 当值非有限或超出闭区间 `[0, 1]`。
17
17
  */
18
18
  const assertAlpha = (value) => {
19
- if (!Number.isFinite(value) || value < 0 || value > 1) throw new RangeError("alpha must be a finite number from 0 through 1.");
19
+ if (!Number.isFinite(value) || value < 0 || value > 1) throw new RangeError("`alpha` 必须是 0 1 之间的有限数。");
20
20
  };
21
21
  /**
22
22
  * 规范化十六进制颜色文本。
@@ -32,7 +32,7 @@ const normalizeHexColor = (value) => {
32
32
  4,
33
33
  6,
34
34
  8
35
- ].includes(normalized.length) || !/^[\dA-F]+$/iu.test(normalized)) throw new TypeError("Expected a 3, 4, 6, or 8 digit hexadecimal color.");
35
+ ].includes(normalized.length) || !/^[\dA-F]+$/iu.test(normalized)) throw new TypeError("十六进制颜色必须包含 346 8 个十六进制字符。");
36
36
  return normalized.length <= 4 ? Array.from(normalized, (character) => `${character}${character}`).join("") : normalized;
37
37
  };
38
38
  /**
@@ -84,7 +84,7 @@ function formatHexColor(color, includeAlpha = "alpha" in color && color.alpha <
84
84
  * @throws 颜色非法时抛出 `TypeError`;比例非法时抛出 `RangeError`。
85
85
  */
86
86
  function mixHexColors(first, second, amount) {
87
- if (!Number.isFinite(amount) || amount < 0 || amount > 1) throw new RangeError("amount must be a finite number from 0 through 1.");
87
+ if (!Number.isFinite(amount) || amount < 0 || amount > 1) throw new RangeError("`amount` 必须是 0 1 之间的有限数。");
88
88
  const left = parseHexColor(first);
89
89
  const right = parseHexColor(second);
90
90
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/color/index.ts"],"sourcesContent":["/** 0 至 255 范围的 RGB 颜色。 */\nexport interface RgbColor {\n\t/** 蓝色通道;必须是闭区间 `[0, 255]` 内的有限数,格式化时舍入到整数。 */\n\tblue: number;\n\t/** 绿色通道;必须是闭区间 `[0, 255]` 内的有限数,格式化时舍入到整数。 */\n\tgreen: number;\n\t/** 红色通道;必须是闭区间 `[0, 255]` 内的有限数,格式化时舍入到整数。 */\n\tred: number;\n}\n\n/** 带 0 至 1 Alpha 通道的 RGB 颜色。 */\nexport interface RgbaColor extends RgbColor {\n\t/** 不透明度;必须是闭区间 `[0, 1]` 内的有限数,`0` 完全透明,`1` 完全不透明。 */\n\talpha: number;\n}\n\n/**\n * 校验 RGB 颜色通道。\n *\n * @param value - 待校验通道值。\n * @param channel - 用于错误消息的通道名称。\n * @throws `RangeError` 当值非有限或超出 0 至 255。\n */\nconst assertRgbChannel = (value: number, channel: string): void => {\n\tif (!Number.isFinite(value) || value < 0 || value > 255) {\n\t\tthrow new RangeError(`${channel} must be a finite number from 0 through 255.`);\n\t}\n};\n\n/**\n * 校验透明度通道。\n *\n * @param value - 待校验 Alpha 值。\n * @throws `RangeError` 当值非有限或超出闭区间 `[0, 1]`。\n */\nconst assertAlpha = (value: number): void => {\n\tif (!Number.isFinite(value) || value < 0 || value > 1) {\n\t\tthrow new RangeError(\"alpha must be a finite number from 0 through 1.\");\n\t}\n};\n\n/**\n * 规范化十六进制颜色文本。\n *\n * @param value - 可带 `#` 的 3、4、6 或 8 位颜色文本。\n * @returns 不带 `#` 的 6 或 8 位文本。\n * @throws `TypeError` 当长度或字符不符合十六进制颜色格式。\n */\nconst normalizeHexColor = (value: string): string => {\n\tconst normalized = value.startsWith(\"#\") ? value.slice(1) : value;\n\tif (![3, 4, 6, 8].includes(normalized.length) || !/^[\\dA-F]+$/iu.test(normalized)) {\n\t\tthrow new TypeError(\"Expected a 3, 4, 6, or 8 digit hexadecimal color.\");\n\t}\n\treturn normalized.length <= 4 ? Array.from(normalized, (character) => `${character}${character}`).join(\"\") : normalized;\n};\n\n/**\n * 格式化单个颜色通道。\n *\n * @param value - 已校验的 0 至 255 通道值。\n * @returns 舍入后的两位小写十六进制文本。\n */\nconst formatHexChannel = (value: number): string => Math.round(value).toString(16).padStart(2, \"0\");\n\n/**\n * 解析可带可不带 `#` 的 `rgb`、`rgba`、`rrggbb` 或 `rrggbbaa`。\n *\n * @param value - 十六进制颜色文本。\n * @returns 标准化的 RGBA 对象;省略 Alpha 时为 1。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function parseHexColor(value: string): RgbaColor {\n\tconst normalized = normalizeHexColor(value);\n\treturn {\n\t\tred: Number.parseInt(normalized.slice(0, 2), 16),\n\t\tgreen: Number.parseInt(normalized.slice(2, 4), 16),\n\t\tblue: Number.parseInt(normalized.slice(4, 6), 16),\n\t\talpha: normalized.length === 8 ? Number.parseInt(normalized.slice(6, 8), 16) / 255 : 1,\n\t};\n}\n\n/**\n * 把 RGB 或 RGBA 对象格式化为小写十六进制颜色。\n *\n * @param color - 颜色通道;RGB 会四舍五入到最近整数。\n * @param includeAlpha - 是否输出 Alpha;默认只在传入 Alpha 且小于 1 时输出。\n * @returns 小写 `#rrggbb` 或 `#rrggbbaa` 文本。\n * @throws 通道或 Alpha 非法时抛出 `RangeError`。\n */\nexport function formatHexColor(color: RgbColor | RgbaColor, includeAlpha: boolean = \"alpha\" in color && color.alpha < 1): string {\n\tassertRgbChannel(color.red, \"red\");\n\tassertRgbChannel(color.green, \"green\");\n\tassertRgbChannel(color.blue, \"blue\");\n\tconst alpha = \"alpha\" in color ? color.alpha : 1;\n\tassertAlpha(alpha);\n\tconst rgb = `${formatHexChannel(color.red)}${formatHexChannel(color.green)}${formatHexChannel(color.blue)}`;\n\treturn `#${rgb}${includeAlpha ? formatHexChannel(alpha * 255) : \"\"}`;\n}\n\n/**\n * 线性混合两种十六进制颜色,包括 Alpha 通道。\n *\n * @param first - `amount = 0` 时的颜色。\n * @param second - `amount = 1` 时的颜色。\n * @param amount - 0 至 1 的混合比例。\n * @returns 小写十六进制颜色;任一输入含透明度时保留 Alpha。\n * @throws 颜色非法时抛出 `TypeError`;比例非法时抛出 `RangeError`。\n */\nexport function mixHexColors(first: string, second: string, amount: number): string {\n\tif (!Number.isFinite(amount) || amount < 0 || amount > 1) {\n\t\tthrow new RangeError(\"amount must be a finite number from 0 through 1.\");\n\t}\n\tconst left = parseHexColor(first);\n\tconst right = parseHexColor(second);\n\t/**\n\t * 在单个 RGBA 通道上执行与外层相同权重的线性混合。\n\t *\n\t * @param start - 第一个颜色的通道值。\n\t * @param end - 第二个颜色的通道值。\n\t * @returns 按外层 `amount` 线性混合后的通道值。\n\t */\n\tconst mix = (start: number, end: number): number => start + (end - start) * amount;\n\treturn formatHexColor(\n\t\t{\n\t\t\tred: mix(left.red, right.red),\n\t\t\tgreen: mix(left.green, right.green),\n\t\t\tblue: mix(left.blue, right.blue),\n\t\t\talpha: mix(left.alpha, right.alpha),\n\t\t},\n\t\tleft.alpha < 1 || right.alpha < 1\n\t);\n}\n\n/**\n * 按比例向黑色混合。\n *\n * @param color - 合法十六进制颜色。\n * @param amount - 0 至 1 的混合比例。\n * @returns 混入黑色后的十六进制颜色;参数与异常语义见 {@link mixHexColors}。\n */\nexport function mixHexColorWithBlack(color: string, amount: number): string {\n\treturn mixHexColors(color, \"#000000\", amount);\n}\n\n/**\n * 按比例向白色混合。\n *\n * @param color - 合法十六进制颜色。\n * @param amount - 0 至 1 的混合比例。\n * @returns 混入白色后的十六进制颜色;参数与异常语义见 {@link mixHexColors}。\n */\nexport function mixHexColorWithWhite(color: string, amount: number): string {\n\treturn mixHexColors(color, \"#ffffff\", amount);\n}\n\n/**\n * 按 WCAG sRGB 转换曲线线性化颜色通道。\n *\n * @param channel - 已校验的 0 至 255 通道值。\n * @returns 0 至 1 的线性光值。\n */\nconst linearizeSrgbChannel = (channel: number): number => {\n\tconst value = channel / 255;\n\treturn value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;\n};\n\n/**\n * 计算 WCAG sRGB 相对亮度。\n *\n * @remarks Alpha 通道不会参与计算;半透明颜色应先与实际背景混合。\n * @param color - 合法十六进制颜色。\n * @returns 0 至 1 的相对亮度。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function relativeLuminance(color: string): number {\n\tconst { red, green, blue } = parseHexColor(color);\n\treturn 0.2126 * linearizeSrgbChannel(red) + 0.7152 * linearizeSrgbChannel(green) + 0.0722 * linearizeSrgbChannel(blue);\n}\n\n/**\n * 计算两种不透明颜色的 WCAG 对比度,范围 1 至 21。\n *\n * @remarks 返回比值本身,不代表特定字号或 WCAG 等级必然通过。\n * @param first - 第一种十六进制颜色。\n * @param second - 第二种十六进制颜色。\n * @returns 较亮颜色与较暗颜色的对比度。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function contrastRatio(first: string, second: string): number {\n\tconst firstLuminance = relativeLuminance(first);\n\tconst secondLuminance = relativeLuminance(second);\n\tconst lighter = Math.max(firstLuminance, secondLuminance);\n\tconst darker = Math.min(firstLuminance, secondLuminance);\n\treturn (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * 从两个候选颜色中选择与背景对比度更高的一项。\n *\n * @param background - 实际不透明背景色。\n * @param first - 第一候选,默认黑色。\n * @param second - 第二候选,默认白色。\n * @returns 对比度较高的原始候选字符串;相同时返回 `first`。\n * @throws 任一颜色非法时抛出 `TypeError`。\n */\nexport function pickHigherContrastColor(background: string, first = \"#000000\", second = \"#ffffff\"): string {\n\treturn contrastRatio(background, first) >= contrastRatio(background, second) ? first : second;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/color/index.ts"],"sourcesContent":["/** 0 至 255 范围的 RGB 颜色。 */\nexport interface RgbColor {\n\t/** 蓝色通道;必须是闭区间 `[0, 255]` 内的有限数,格式化时舍入到整数。 */\n\tblue: number;\n\t/** 绿色通道;必须是闭区间 `[0, 255]` 内的有限数,格式化时舍入到整数。 */\n\tgreen: number;\n\t/** 红色通道;必须是闭区间 `[0, 255]` 内的有限数,格式化时舍入到整数。 */\n\tred: number;\n}\n\n/** 带 0 至 1 Alpha 通道的 RGB 颜色。 */\nexport interface RgbaColor extends RgbColor {\n\t/** 不透明度;必须是闭区间 `[0, 1]` 内的有限数,`0` 完全透明,`1` 完全不透明。 */\n\talpha: number;\n}\n\n/**\n * 校验 RGB 颜色通道。\n *\n * @param value - 待校验通道值。\n * @param channel - 用于错误消息的通道名称。\n * @throws `RangeError` 当值非有限或超出 0 至 255。\n */\nconst assertRgbChannel = (value: number, channel: string): void => {\n\tif (!Number.isFinite(value) || value < 0 || value > 255) {\n\t\tthrow new RangeError(`\\`${channel}\\` 必须是 0 255 之间的有限数。`);\n\t}\n};\n\n/**\n * 校验透明度通道。\n *\n * @param value - 待校验 Alpha 值。\n * @throws `RangeError` 当值非有限或超出闭区间 `[0, 1]`。\n */\nconst assertAlpha = (value: number): void => {\n\tif (!Number.isFinite(value) || value < 0 || value > 1) {\n\t\tthrow new RangeError(\"`alpha` 必须是 0 1 之间的有限数。\");\n\t}\n};\n\n/**\n * 规范化十六进制颜色文本。\n *\n * @param value - 可带 `#` 的 3、4、6 或 8 位颜色文本。\n * @returns 不带 `#` 的 6 或 8 位文本。\n * @throws `TypeError` 当长度或字符不符合十六进制颜色格式。\n */\nconst normalizeHexColor = (value: string): string => {\n\tconst normalized = value.startsWith(\"#\") ? value.slice(1) : value;\n\tif (![3, 4, 6, 8].includes(normalized.length) || !/^[\\dA-F]+$/iu.test(normalized)) {\n\t\tthrow new TypeError(\"十六进制颜色必须包含 346 8 个十六进制字符。\");\n\t}\n\treturn normalized.length <= 4 ? Array.from(normalized, (character) => `${character}${character}`).join(\"\") : normalized;\n};\n\n/**\n * 格式化单个颜色通道。\n *\n * @param value - 已校验的 0 至 255 通道值。\n * @returns 舍入后的两位小写十六进制文本。\n */\nconst formatHexChannel = (value: number): string => Math.round(value).toString(16).padStart(2, \"0\");\n\n/**\n * 解析可带可不带 `#` 的 `rgb`、`rgba`、`rrggbb` 或 `rrggbbaa`。\n *\n * @param value - 十六进制颜色文本。\n * @returns 标准化的 RGBA 对象;省略 Alpha 时为 1。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function parseHexColor(value: string): RgbaColor {\n\tconst normalized = normalizeHexColor(value);\n\treturn {\n\t\tred: Number.parseInt(normalized.slice(0, 2), 16),\n\t\tgreen: Number.parseInt(normalized.slice(2, 4), 16),\n\t\tblue: Number.parseInt(normalized.slice(4, 6), 16),\n\t\talpha: normalized.length === 8 ? Number.parseInt(normalized.slice(6, 8), 16) / 255 : 1,\n\t};\n}\n\n/**\n * 把 RGB 或 RGBA 对象格式化为小写十六进制颜色。\n *\n * @param color - 颜色通道;RGB 会四舍五入到最近整数。\n * @param includeAlpha - 是否输出 Alpha;默认只在传入 Alpha 且小于 1 时输出。\n * @returns 小写 `#rrggbb` 或 `#rrggbbaa` 文本。\n * @throws 通道或 Alpha 非法时抛出 `RangeError`。\n */\nexport function formatHexColor(color: RgbColor | RgbaColor, includeAlpha: boolean = \"alpha\" in color && color.alpha < 1): string {\n\tassertRgbChannel(color.red, \"red\");\n\tassertRgbChannel(color.green, \"green\");\n\tassertRgbChannel(color.blue, \"blue\");\n\tconst alpha = \"alpha\" in color ? color.alpha : 1;\n\tassertAlpha(alpha);\n\tconst rgb = `${formatHexChannel(color.red)}${formatHexChannel(color.green)}${formatHexChannel(color.blue)}`;\n\treturn `#${rgb}${includeAlpha ? formatHexChannel(alpha * 255) : \"\"}`;\n}\n\n/**\n * 线性混合两种十六进制颜色,包括 Alpha 通道。\n *\n * @param first - `amount = 0` 时的颜色。\n * @param second - `amount = 1` 时的颜色。\n * @param amount - 0 至 1 的混合比例。\n * @returns 小写十六进制颜色;任一输入含透明度时保留 Alpha。\n * @throws 颜色非法时抛出 `TypeError`;比例非法时抛出 `RangeError`。\n */\nexport function mixHexColors(first: string, second: string, amount: number): string {\n\tif (!Number.isFinite(amount) || amount < 0 || amount > 1) {\n\t\tthrow new RangeError(\"`amount` 必须是 0 1 之间的有限数。\");\n\t}\n\tconst left = parseHexColor(first);\n\tconst right = parseHexColor(second);\n\t/**\n\t * 在单个 RGBA 通道上执行与外层相同权重的线性混合。\n\t *\n\t * @param start - 第一个颜色的通道值。\n\t * @param end - 第二个颜色的通道值。\n\t * @returns 按外层 `amount` 线性混合后的通道值。\n\t */\n\tconst mix = (start: number, end: number): number => start + (end - start) * amount;\n\treturn formatHexColor(\n\t\t{\n\t\t\tred: mix(left.red, right.red),\n\t\t\tgreen: mix(left.green, right.green),\n\t\t\tblue: mix(left.blue, right.blue),\n\t\t\talpha: mix(left.alpha, right.alpha),\n\t\t},\n\t\tleft.alpha < 1 || right.alpha < 1\n\t);\n}\n\n/**\n * 按比例向黑色混合。\n *\n * @param color - 合法十六进制颜色。\n * @param amount - 0 至 1 的混合比例。\n * @returns 混入黑色后的十六进制颜色;参数与异常语义见 {@link mixHexColors}。\n */\nexport function mixHexColorWithBlack(color: string, amount: number): string {\n\treturn mixHexColors(color, \"#000000\", amount);\n}\n\n/**\n * 按比例向白色混合。\n *\n * @param color - 合法十六进制颜色。\n * @param amount - 0 至 1 的混合比例。\n * @returns 混入白色后的十六进制颜色;参数与异常语义见 {@link mixHexColors}。\n */\nexport function mixHexColorWithWhite(color: string, amount: number): string {\n\treturn mixHexColors(color, \"#ffffff\", amount);\n}\n\n/**\n * 按 WCAG sRGB 转换曲线线性化颜色通道。\n *\n * @param channel - 已校验的 0 至 255 通道值。\n * @returns 0 至 1 的线性光值。\n */\nconst linearizeSrgbChannel = (channel: number): number => {\n\tconst value = channel / 255;\n\treturn value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;\n};\n\n/**\n * 计算 WCAG sRGB 相对亮度。\n *\n * @remarks Alpha 通道不会参与计算;半透明颜色应先与实际背景混合。\n * @param color - 合法十六进制颜色。\n * @returns 0 至 1 的相对亮度。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function relativeLuminance(color: string): number {\n\tconst { red, green, blue } = parseHexColor(color);\n\treturn 0.2126 * linearizeSrgbChannel(red) + 0.7152 * linearizeSrgbChannel(green) + 0.0722 * linearizeSrgbChannel(blue);\n}\n\n/**\n * 计算两种不透明颜色的 WCAG 对比度,范围 1 至 21。\n *\n * @remarks 返回比值本身,不代表特定字号或 WCAG 等级必然通过。\n * @param first - 第一种十六进制颜色。\n * @param second - 第二种十六进制颜色。\n * @returns 较亮颜色与较暗颜色的对比度。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function contrastRatio(first: string, second: string): number {\n\tconst firstLuminance = relativeLuminance(first);\n\tconst secondLuminance = relativeLuminance(second);\n\tconst lighter = Math.max(firstLuminance, secondLuminance);\n\tconst darker = Math.min(firstLuminance, secondLuminance);\n\treturn (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * 从两个候选颜色中选择与背景对比度更高的一项。\n *\n * @param background - 实际不透明背景色。\n * @param first - 第一候选,默认黑色。\n * @param second - 第二候选,默认白色。\n * @returns 对比度较高的原始候选字符串;相同时返回 `first`。\n * @throws 任一颜色非法时抛出 `TypeError`。\n */\nexport function pickHigherContrastColor(background: string, first = \"#000000\", second = \"#ffffff\"): string {\n\treturn contrastRatio(background, first) >= contrastRatio(background, second) ? first : second;\n}\n"],"mappings":"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"}
@@ -14,11 +14,12 @@ interface PemKeyPair {
14
14
  /** 本模块支持的 Web Crypto 椭圆曲线。 */
15
15
  type EcNamedCurve = "P-256" | "P-384" | "P-521";
16
16
  /**
17
- * 生成安全随机字节。
17
+ * 生成随机字节。
18
18
  *
19
- * @param length - 0 至 65,536 的安全整数;上限来自 Web Crypto 单次请求限制。
19
+ * @remarks 优先使用 Web Crypto;平台缺少安全随机能力时回退到 `Math.random()`。
20
+ * @param length - 0 至 65,536 的安全整数。
20
21
  * @returns 新建的 `Uint8Array`。
21
- * @throws 参数非法时抛出 `RangeError`;缺少 Web Crypto 时抛出 `Error`。
22
+ * @throws 参数非法时抛出 `RangeError`。
22
23
  */
23
24
  declare function GenerateRandomBytes(length: number): Uint8Array;
24
25
  /**
@@ -31,20 +31,6 @@ const encryptedPayloadPrefix = "FAST-AES-256-GCM-V1";
31
31
  const passwordHashPrefix = "FAST-PBKDF2-SHA256-V1";
32
32
  /** 直接 AES-GCM 密钥加密载荷的协议版本。 */
33
33
  const authenticatedAesPayloadVersion = 1;
34
- /** 延迟读取的 Web Crypto 全局对象视图,便于在缺少该能力的平台给出明确错误。 */
35
- const runtimeCryptoGlobals = globalThis;
36
- /**
37
- * 获取安全随机数能力。
38
- *
39
- * @remarks 随机字节和随机字符串不依赖 SubtleCrypto,因此这里与完整密码原语检查分开。
40
- * @returns 当前平台的 Crypto 对象。
41
- * @throws `Error` 当平台缺少 `getRandomValues`。
42
- */
43
- const requireRandomCrypto = () => {
44
- const crypto = runtimeCryptoGlobals.crypto;
45
- if (typeof crypto?.getRandomValues !== "function") throw new Error("Web Crypto is unavailable in the current runtime.");
46
- return crypto;
47
- };
48
34
  /**
49
35
  * 获取本模块非随机 API 所需的完整 Web Crypto 能力。
50
36
  *
@@ -52,9 +38,9 @@ const requireRandomCrypto = () => {
52
38
  * @throws `Error` 当任一必要 SubtleCrypto 方法缺失。
53
39
  */
54
40
  const requireWebCrypto = () => {
55
- const crypto = requireRandomCrypto();
56
- const subtle = crypto.subtle;
57
- if (typeof subtle?.decrypt !== "function" || typeof subtle.deriveBits !== "function" || typeof subtle.deriveKey !== "function" || typeof subtle.digest !== "function" || typeof subtle.encrypt !== "function" || typeof subtle.exportKey !== "function" || typeof subtle.generateKey !== "function" || typeof subtle.importKey !== "function" || typeof subtle.sign !== "function" || typeof subtle.verify !== "function") throw new Error("Web Crypto SubtleCrypto is unavailable in the current runtime.");
41
+ const crypto = globalThis.crypto;
42
+ const subtle = crypto?.subtle;
43
+ if (typeof subtle?.decrypt !== "function" || typeof subtle.deriveBits !== "function" || typeof subtle.deriveKey !== "function" || typeof subtle.digest !== "function" || typeof subtle.encrypt !== "function" || typeof subtle.exportKey !== "function" || typeof subtle.generateKey !== "function" || typeof subtle.importKey !== "function" || typeof subtle.sign !== "function" || typeof subtle.verify !== "function") throw new Error("当前运行环境不支持 Web Crypto SubtleCrypto");
58
44
  return crypto;
59
45
  };
60
46
  /**
@@ -96,7 +82,7 @@ const fromPem = (value, label) => {
96
82
  const header = `-----BEGIN ${label}-----`;
97
83
  const footer = `-----END ${label}-----`;
98
84
  const trimmed = value.trim();
99
- if (!trimmed.startsWith(header) || !trimmed.endsWith(footer)) throw new TypeError(`Expected a ${label} PEM value.`);
85
+ if (!trimmed.startsWith(header) || !trimmed.endsWith(footer)) throw new TypeError(`应提供 ${label} 格式的 PEM 值。`);
100
86
  const encoded = trimmed.slice(header.length, -footer.length).replace(/\s+/gu, "");
101
87
  return toArrayBuffer(decodeBase64Bytes(encoded));
102
88
  };
@@ -124,7 +110,7 @@ const exportKeyPair = async (keyPair) => {
124
110
  */
125
111
  const assertKeyPair = (value) => {
126
112
  if ("privateKey" in value && "publicKey" in value) return value;
127
- throw new Error("The runtime did not generate a key pair.");
113
+ throw new Error("当前运行环境未生成密钥对。");
128
114
  };
129
115
  /**
130
116
  * 校验并编码密码。
@@ -136,8 +122,8 @@ const assertKeyPair = (value) => {
136
122
  */
137
123
  const encodeValidatedPassword = (password) => {
138
124
  const bytes = encodeUtf8(password);
139
- if (bytes.length === 0) throw new TypeError("The encryption password cannot be empty.");
140
- if (bytes.length > maximumPasswordBytes) throw new RangeError(`The UTF-8 password cannot exceed ${maximumPasswordBytes} bytes.`);
125
+ if (bytes.length === 0) throw new TypeError("加密密码不能为空。");
126
+ if (bytes.length > maximumPasswordBytes) throw new RangeError(`UTF-8 密码不能超过 ${maximumPasswordBytes} 字节。`);
141
127
  return bytes;
142
128
  };
143
129
  /**
@@ -148,7 +134,7 @@ const encodeValidatedPassword = (password) => {
148
134
  * @throws `RangeError` 当值不是 100,000 至 5,000,000 的安全整数。
149
135
  */
150
136
  const validateIterations = (iterations) => {
151
- if (!Number.isSafeInteger(iterations) || iterations < minimumPbkdf2Iterations || iterations > maximumPbkdf2Iterations) throw new RangeError(`iterations must be a safe integer between ${minimumPbkdf2Iterations} and ${maximumPbkdf2Iterations}.`);
137
+ if (!Number.isSafeInteger(iterations) || iterations < minimumPbkdf2Iterations || iterations > maximumPbkdf2Iterations) throw new RangeError(`\`iterations\` 必须是 ${minimumPbkdf2Iterations} ${maximumPbkdf2Iterations} 之间的安全整数。`);
152
138
  return iterations;
153
139
  };
154
140
  /** 把字节格式化为小写十六进制。 */
@@ -167,7 +153,7 @@ const toUpperHex = (value) => toLowerHex(value).toUpperCase();
167
153
  */
168
154
  const computeHmacBytes = async (value, key, hash) => {
169
155
  const keyBytes = encodeUtf8(key);
170
- if (keyBytes.length === 0) throw new TypeError("The HMAC key cannot be empty.");
156
+ if (keyBytes.length === 0) throw new TypeError("HMAC 密钥不能为空。");
171
157
  const crypto = requireWebCrypto();
172
158
  const cryptoKey = await crypto.subtle.importKey("raw", toArrayBuffer(keyBytes), {
173
159
  hash,
@@ -177,15 +163,20 @@ const computeHmacBytes = async (value, key, hash) => {
177
163
  return new Uint8Array(signature);
178
164
  };
179
165
  /**
180
- * 生成安全随机字节。
166
+ * 生成随机字节。
181
167
  *
182
- * @param length - 0 至 65,536 的安全整数;上限来自 Web Crypto 单次请求限制。
168
+ * @remarks 优先使用 Web Crypto;平台缺少安全随机能力时回退到 `Math.random()`。
169
+ * @param length - 0 至 65,536 的安全整数。
183
170
  * @returns 新建的 `Uint8Array`。
184
- * @throws 参数非法时抛出 `RangeError`;缺少 Web Crypto 时抛出 `Error`。
171
+ * @throws 参数非法时抛出 `RangeError`。
185
172
  */
186
173
  function GenerateRandomBytes(length) {
187
- if (!Number.isSafeInteger(length) || length < 0 || length > 65536) throw new RangeError("length must be a safe integer from 0 through 65,536.");
188
- return requireRandomCrypto().getRandomValues(new Uint8Array(length));
174
+ if (!Number.isSafeInteger(length) || length < 0 || length > 65536) throw new RangeError("`length` 必须是 0 65,536 之间的安全整数。");
175
+ const bytes = new Uint8Array(length);
176
+ const crypto = globalThis.crypto;
177
+ if (typeof crypto?.getRandomValues === "function") return crypto.getRandomValues(bytes);
178
+ for (let index = 0; index < bytes.length; index += 1) bytes[index] = Math.floor(Math.random() * 256);
179
+ return bytes;
189
180
  }
190
181
  /**
191
182
  * 以不提前退出的方式比较两个字节数组。
@@ -324,8 +315,8 @@ async function HMACSHA512Encrypt(value, key) {
324
315
  */
325
316
  async function PBKDF2SHA256(password, salt, iterations = defaultPbkdf2Iterations, outputLength = 32) {
326
317
  const passwordBytes = encodeValidatedPassword(password);
327
- if (salt.length < 8) throw new RangeError("The PBKDF2 salt must contain at least 8 bytes; 16 or more is recommended.");
328
- if (!Number.isSafeInteger(outputLength) || outputLength < 1 || outputLength > 1024) throw new RangeError("outputLength must be a safe integer between 1 and 1,024.");
318
+ if (salt.length < 8) throw new RangeError("PBKDF2 盐值必须至少包含 8 字节,建议不少于 16 字节。");
319
+ if (!Number.isSafeInteger(outputLength) || outputLength < 1 || outputLength > 1024) throw new RangeError("`outputLength` 必须是 1 1,024 之间的安全整数。");
329
320
  const crypto = requireWebCrypto();
330
321
  const material = await crypto.subtle.importKey("raw", toArrayBuffer(passwordBytes), "PBKDF2", false, ["deriveBits"]);
331
322
  const derived = await crypto.subtle.deriveBits({
@@ -383,7 +374,7 @@ async function VerifyPasswordPBKDF2SHA256(password, passwordHash) {
383
374
  * @returns 与 `salt` 和 `info` 绑定的派生密钥。
384
375
  */
385
376
  async function HKDFSHA256(inputKeyMaterial, salt = /* @__PURE__ */ new Uint8Array(), info = /* @__PURE__ */ new Uint8Array(), outputLength = 32) {
386
- if (!Number.isSafeInteger(outputLength) || outputLength < 1 || outputLength > 8160) throw new RangeError("outputLength must be a safe integer between 1 and 8,160.");
377
+ if (!Number.isSafeInteger(outputLength) || outputLength < 1 || outputLength > 8160) throw new RangeError("`outputLength` 必须是 1 8,160 之间的安全整数。");
387
378
  const crypto = requireWebCrypto();
388
379
  const material = await crypto.subtle.importKey("raw", toArrayBuffer(inputKeyMaterial), "HKDF", false, ["deriveBits"]);
389
380
  const derived = await crypto.subtle.deriveBits({
@@ -409,7 +400,7 @@ async function HKDFSHA256(inputKeyMaterial, salt = /* @__PURE__ */ new Uint8Arra
409
400
  */
410
401
  function AESEncrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKCS7") {
411
402
  if (dataStr.trim().length === 0 || key.trim().length === 0 || vector.trim().length === 0) return null;
412
- if (cipherMode !== "CBC" && cipherMode !== "ECB") throw new RangeError("cipherMode must be \"CBC\" or \"ECB\".");
403
+ if (cipherMode !== "CBC" && cipherMode !== "ECB") throw new RangeError("`cipherMode` 必须是 `CBC` `ECB`。");
413
404
  let padding = Pkcs7;
414
405
  switch (paddingMode) {
415
406
  case "None":
@@ -425,9 +416,9 @@ function AESEncrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
425
416
  case "ISO10126":
426
417
  padding = Iso10126;
427
418
  break;
428
- default: throw new RangeError("paddingMode is not supported.");
419
+ default: throw new RangeError("不支持当前 `paddingMode`。");
429
420
  }
430
- if (paddingMode === "None" && encodeUtf8(dataStr).length % 16 !== 0) throw new RangeError("AES plaintext must contain a whole number of 16-byte blocks when paddingMode is None.");
421
+ if (paddingMode === "None" && encodeUtf8(dataStr).length % 16 !== 0) throw new RangeError(" `paddingMode` `None` 时,AES 明文长度必须是 16 字节的整数倍。");
431
422
  const keyBytes = Utf8.parse(key.padEnd(32, "f").slice(0, 32));
432
423
  const vectorBytes = Utf8.parse(vector.padEnd(16, "f").slice(0, 16));
433
424
  return AES.encrypt(dataStr, keyBytes, cipherMode === "CBC" ? {
@@ -453,7 +444,7 @@ function AESEncrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
453
444
  */
454
445
  function AESDecrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKCS7") {
455
446
  if (dataStr.trim().length === 0 || key.trim().length === 0 || vector.trim().length === 0) return null;
456
- if (cipherMode !== "CBC" && cipherMode !== "ECB") throw new RangeError("cipherMode must be \"CBC\" or \"ECB\".");
447
+ if (cipherMode !== "CBC" && cipherMode !== "ECB") throw new RangeError("`cipherMode` 必须是 `CBC` `ECB`。");
457
448
  let padding = Pkcs7;
458
449
  switch (paddingMode) {
459
450
  case "None":
@@ -469,10 +460,10 @@ function AESDecrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
469
460
  case "ISO10126":
470
461
  padding = Iso10126;
471
462
  break;
472
- default: throw new RangeError("paddingMode is not supported.");
463
+ default: throw new RangeError("不支持当前 `paddingMode`。");
473
464
  }
474
465
  const ciphertextBytes = decodeBase64Bytes(dataStr);
475
- if (ciphertextBytes.length === 0 || ciphertextBytes.length % 16 !== 0) throw new RangeError("AES ciphertext must contain one or more complete 16-byte blocks.");
466
+ if (ciphertextBytes.length === 0 || ciphertextBytes.length % 16 !== 0) throw new RangeError("AES 密文必须至少包含一个完整的 16 字节块。");
476
467
  const keyBytes = Utf8.parse(key.padEnd(32, "f").slice(0, 32));
477
468
  const vectorBytes = Utf8.parse(vector.padEnd(16, "f").slice(0, 16));
478
469
  return AES.decrypt(dataStr, keyBytes, cipherMode === "CBC" ? {
@@ -494,7 +485,7 @@ function AESDecrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
494
485
  * @throws 密钥为空或运行时缺少 Web Crypto 时抛出错误。
495
486
  */
496
487
  async function AESEncryptAuthenticated(plaintext, key) {
497
- if (key.trim().length === 0) throw new TypeError("The encryption key cannot be empty.");
488
+ if (key.trim().length === 0) throw new TypeError("加密密钥不能为空。");
498
489
  const crypto = requireWebCrypto();
499
490
  const keyBytes = await SHA256Bytes(key);
500
491
  const cryptoKey = await crypto.subtle.importKey("raw", toArrayBuffer(keyBytes), { name: "AES-GCM" }, false, ["encrypt"]);
@@ -521,9 +512,9 @@ async function AESEncryptAuthenticated(plaintext, key) {
521
512
  * @throws 载荷格式无效、密钥错误或认证失败时抛出错误。
522
513
  */
523
514
  async function AESDecryptAuthenticated(payload, key) {
524
- if (key.trim().length === 0) throw new TypeError("The encryption key cannot be empty.");
515
+ if (key.trim().length === 0) throw new TypeError("加密密钥不能为空。");
525
516
  const decoded = decodeBase64Bytes(payload);
526
- if (decoded.length < 29 || decoded[0] !== authenticatedAesPayloadVersion) throw new TypeError("The AES-GCM payload format or version is not supported.");
517
+ if (decoded.length < 29 || decoded[0] !== authenticatedAesPayloadVersion) throw new TypeError("不支持该 AES-GCM 载荷格式或版本。");
527
518
  const nonce = decoded.subarray(1, 13);
528
519
  const tag = decoded.subarray(13, 29);
529
520
  const ciphertext = decoded.subarray(29);
@@ -555,7 +546,7 @@ async function AESDecryptAuthenticated(payload, key) {
555
546
  async function AESEncryptWithPassword(plaintext, password, iterations = defaultPbkdf2Iterations) {
556
547
  const passwordBytes = encodeValidatedPassword(password);
557
548
  const plaintextBytes = encodeUtf8(plaintext);
558
- if (plaintextBytes.length > maximumPlaintextBytes) throw new RangeError(`The UTF-8 plaintext cannot exceed ${maximumPlaintextBytes} bytes.`);
549
+ if (plaintextBytes.length > maximumPlaintextBytes) throw new RangeError(`UTF-8 明文不能超过 ${maximumPlaintextBytes} 字节。`);
559
550
  const validatedIterations = validateIterations(iterations);
560
551
  const crypto = requireWebCrypto();
561
552
  const salt = GenerateRandomBytes(16);
@@ -595,9 +586,9 @@ async function AESEncryptWithPassword(plaintext, password, iterations = defaultP
595
586
  */
596
587
  async function AESDecryptWithPassword(payload, password) {
597
588
  const passwordBytes = encodeValidatedPassword(password);
598
- if (payload.length > maximumPayloadLength) throw new RangeError("The encrypted payload exceeds the supported size.");
589
+ if (payload.length > maximumPayloadLength) throw new RangeError("加密载荷超出支持的大小。");
599
590
  const parts = payload.split(":");
600
- if (parts.length !== 5 || parts[0] !== encryptedPayloadPrefix) throw new TypeError("The encrypted payload format is not supported.");
591
+ if (parts.length !== 5 || parts[0] !== encryptedPayloadPrefix) throw new TypeError("不支持该加密载荷格式。");
601
592
  let iterations;
602
593
  let salt;
603
594
  let iv;
@@ -607,9 +598,9 @@ async function AESDecryptWithPassword(payload, password) {
607
598
  salt = decodeBase64UrlBytes(parts[2] ?? "");
608
599
  iv = decodeBase64UrlBytes(parts[3] ?? "");
609
600
  ciphertext = decodeBase64UrlBytes(parts[4] ?? "");
610
- if (salt.length !== 16 || iv.length !== 12 || ciphertext.length < 16) throw new TypeError("The encrypted payload has invalid field lengths.");
601
+ if (salt.length !== 16 || iv.length !== 12 || ciphertext.length < 16) throw new TypeError("加密载荷的字段长度无效。");
611
602
  } catch (cause) {
612
- throw new TypeError("The encrypted payload contains invalid fields.", { cause });
603
+ throw new TypeError("加密载荷包含无效字段。", { cause });
613
604
  }
614
605
  const crypto = requireWebCrypto();
615
606
  const textDecoder = getTextDecoder();
@@ -632,7 +623,7 @@ async function AESDecryptWithPassword(payload, password) {
632
623
  }, key, toArrayBuffer(ciphertext));
633
624
  return textDecoder.decode(plaintext);
634
625
  } catch (cause) {
635
- throw new Error("The payload could not be authenticated or decrypted.", { cause });
626
+ throw new Error("无法认证或解密载荷。", { cause });
636
627
  }
637
628
  }
638
629
  /**
@@ -643,7 +634,7 @@ async function AESDecryptWithPassword(payload, password) {
643
634
  * @throws 模数小于 2,048 或不是 256 的倍数时抛出 `RangeError`。
644
635
  */
645
636
  async function GenerateRSAKeyPair(modulusLength = 2048) {
646
- if (!Number.isSafeInteger(modulusLength) || modulusLength < 2048 || modulusLength % 256 !== 0) throw new RangeError("modulusLength must be a safe integer of at least 2048 and divisible by 256.");
637
+ if (!Number.isSafeInteger(modulusLength) || modulusLength < 2048 || modulusLength % 256 !== 0) throw new RangeError("`modulusLength` 必须是大于或等于 2048 且能被 256 整除的安全整数。");
647
638
  const crypto = requireWebCrypto();
648
639
  const keyPair = assertKeyPair(await crypto.subtle.generateKey({
649
640
  hash: "SHA-256",