@fast-china/utils 2.1.10 → 2.1.11

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 (70) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +5 -2
  3. package/README.zh.md +5 -2
  4. package/dist/array/index.mjs +21 -21
  5. package/dist/array/index.mjs.map +1 -1
  6. package/dist/async/index.mjs +32 -32
  7. package/dist/async/index.mjs.map +1 -1
  8. package/dist/base64/index.mjs +44 -51
  9. package/dist/base64/index.mjs.map +1 -1
  10. package/dist/color/index.mjs +33 -33
  11. package/dist/color/index.mjs.map +1 -1
  12. package/dist/crypto/index.mjs +153 -155
  13. package/dist/crypto/index.mjs.map +1 -1
  14. package/dist/date/index.mjs +79 -46
  15. package/dist/date/index.mjs.map +1 -1
  16. package/dist/dom/style.mjs +7 -7
  17. package/dist/dom/style.mjs.map +1 -1
  18. package/dist/env/index.mjs +9 -14
  19. package/dist/env/index.mjs.map +1 -1
  20. package/dist/function/index.mjs +4 -4
  21. package/dist/function/index.mjs.map +1 -1
  22. package/dist/identity/index.mjs +7 -7
  23. package/dist/identity/index.mjs.map +1 -1
  24. package/dist/index.d.mts +371 -366
  25. package/dist/index.global.min.js +2 -2
  26. package/dist/index.global.min.js.map +1 -1
  27. package/dist/internal/text.mjs +70 -26
  28. package/dist/internal/text.mjs.map +1 -1
  29. package/dist/logger/index.mjs +12 -13
  30. package/dist/logger/index.mjs.map +1 -1
  31. package/dist/number/index.mjs +63 -42
  32. package/dist/number/index.mjs.map +1 -1
  33. package/dist/object/index.mjs +32 -31
  34. package/dist/object/index.mjs.map +1 -1
  35. package/dist/storage/index.mjs +58 -63
  36. package/dist/storage/index.mjs.map +1 -1
  37. package/dist/string/index.mjs +60 -64
  38. package/dist/string/index.mjs.map +1 -1
  39. package/dist/vue/breakpoints.mjs +14 -10
  40. package/dist/vue/breakpoints.mjs.map +1 -1
  41. package/dist/vue/element-size.mjs +4 -4
  42. package/dist/vue/element-size.mjs.map +1 -1
  43. package/dist/vue/emits.mjs +7 -7
  44. package/dist/vue/emits.mjs.map +1 -1
  45. package/dist/vue/event-listener.mjs +5 -5
  46. package/dist/vue/event-listener.mjs.map +1 -1
  47. package/dist/vue/expose.mjs +3 -3
  48. package/dist/vue/expose.mjs.map +1 -1
  49. package/dist/vue/func.mjs +2 -2
  50. package/dist/vue/func.mjs.map +1 -1
  51. package/dist/vue/install.mjs +24 -24
  52. package/dist/vue/install.mjs.map +1 -1
  53. package/dist/vue/now.mjs +4 -5
  54. package/dist/vue/now.mjs.map +1 -1
  55. package/dist/vue/props.mjs +5 -5
  56. package/dist/vue/props.mjs.map +1 -1
  57. package/dist/vue/render.mjs +2 -2
  58. package/dist/vue/render.mjs.map +1 -1
  59. package/dist/vue/resize-observer.mjs +4 -6
  60. package/dist/vue/resize-observer.mjs.map +1 -1
  61. package/dist/vue/slots.mjs.map +1 -1
  62. package/dist/vue/transition.mjs +5 -7
  63. package/dist/vue/transition.mjs.map +1 -1
  64. package/dist/vue/window-size.mjs +2 -4
  65. package/dist/vue/window-size.mjs.map +1 -1
  66. package/dist/vue/with.mjs +1 -1
  67. package/dist/vue/with.mjs.map +1 -1
  68. package/package.json +2 -1
  69. package/dist/internal/runtime.mjs +0 -32
  70. package/dist/internal/runtime.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { createDecodedText, encodeUtf8, getTextDecoder } from "../internal/text.mjs";
1
+ import { createDecodedText, decodeUtf8 as decodeUtf8$1, encodeUtf8 } from "../internal/text.mjs";
2
2
  import { randomInt } from "../number/index.mjs";
3
3
  //#region src/base64/index.ts
4
4
  const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
@@ -6,17 +6,17 @@ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
6
6
  * 创建统一的 Base64 输入错误。
7
7
  *
8
8
  * @param variant - 当前解析的标准 Base64 或 Base64URL 变体。
9
- * @returns 带有具体变体名称的 `TypeError`。
9
+ * @returns 带有具体变体名称的 `TypeError`
10
10
  */
11
11
  const invalidBase64 = (variant) => {
12
- return /* @__PURE__ */ new TypeError(`该值不是有效的 ${variant === "url" ? "Base64URL" : "Base64"}。`);
12
+ return /* @__PURE__ */ new TypeError(`The value is not valid ${variant === "url" ? "Base64URL" : "Base64"}.`);
13
13
  };
14
14
  /**
15
15
  * 校验并规范化 Base64 文本。
16
16
  *
17
17
  * @remarks 标准 Base64 允许 ASCII 空白,Base64URL 不允许;两者最终都会转换为标准字符表和完整填充。
18
- * @param value - 原始编码文本。
19
- * @param variant - 需要应用的字符表与空白规则。
18
+ * @param value - 原始编码文本
19
+ * @param variant - 需要应用的字符表与空白规则
20
20
  * @returns 使用标准字符表且长度为 4 倍数的文本。
21
21
  * @throws `TypeError` 当字符、填充位置或编码长度非法。
22
22
  */
@@ -30,8 +30,8 @@ const normalizeBase64 = (value, variant) => {
30
30
  /**
31
31
  * 使用固定字符表编码任意字节。
32
32
  *
33
- * @param bytes - 不会被修改的源字节。
34
- * @returns 带标准 `=` 填充的 Base64 文本。
33
+ * @param bytes - 不会被修改的源字节
34
+ * @returns 带标准 `=` 填充的 Base64 文本
35
35
  */
36
36
  const encodeBytes = (bytes) => {
37
37
  let result = "";
@@ -51,9 +51,9 @@ const encodeBytes = (bytes) => {
51
51
  * 把 Base64 或 Base64URL 文本解码为字节。
52
52
  *
53
53
  * @remarks 最后一组未使用位必须为零,以拒绝同一字节序列的非规范编码。
54
- * @param value - 待解码文本。
55
- * @param variant - 输入使用的编码变体。
56
- * @returns 新建的字节数组。
54
+ * @param value - 待解码文本
55
+ * @param variant - 输入使用的编码变体
56
+ * @returns 新建的字节数组
57
57
  * @throws `TypeError` 当输入格式或尾部位非法。
58
58
  */
59
59
  const decodeBytes = (value, variant) => {
@@ -78,26 +78,19 @@ const decodeBytes = (value, variant) => {
78
78
  }
79
79
  return result;
80
80
  };
81
- /**
82
- * 以 Fatal 模式解码 UTF-8。
83
- *
84
- * @param bytes - 待解码字节。
85
- * @returns 解码后的 JavaScript 字符串。
86
- * @throws `TypeError` 当字节不是有效 UTF-8;原始平台异常保存在 `cause` 中。
87
- */
81
+ /** 统一 UTF-8 解码错误,并保留底层原因。 */
88
82
  const decodeUtf8 = (bytes) => {
89
- const textDecoder = getTextDecoder();
90
83
  try {
91
- return textDecoder.decode(bytes);
84
+ return decodeUtf8$1(bytes);
92
85
  } catch (cause) {
93
- throw new TypeError("解码后的字节不是有效的 UTF-8。", { cause });
86
+ throw new TypeError("The decoded bytes are not valid UTF-8.", { cause });
94
87
  }
95
88
  };
96
89
  /**
97
90
  * 将任意字节编码为标准 Base64。
98
91
  *
99
- * @param bytes - 不会被修改的字节序列。
100
- * @returns 带标准 `=` 填充的 Base64 文本。
92
+ * @param bytes - 不会被修改的字节序列
93
+ * @returns 带标准 `=` 填充的 Base64 文本
101
94
  */
102
95
  function encodeBase64Bytes(bytes) {
103
96
  return encodeBytes(bytes);
@@ -106,8 +99,8 @@ function encodeBase64Bytes(bytes) {
106
99
  * 解码标准 Base64。
107
100
  *
108
101
  * @remarks 允许省略填充和包含 ASCII 空白,但拒绝非规范尾部位。
109
- * @param value - Base64 文本。
110
- * @returns 新建的字节数组。
102
+ * @param value - Base64 文本
103
+ * @returns 新建的字节数组
111
104
  * @throws 输入非法时抛出 `TypeError`。
112
105
  */
113
106
  function decodeBase64Bytes(value) {
@@ -116,9 +109,9 @@ function decodeBase64Bytes(value) {
116
109
  /**
117
110
  * 将 UTF-8 文本编码为标准 Base64。
118
111
  *
119
- * @param value - 任意 Unicode 字符串。
120
- * @returns 带标准填充的 Base64 文本。
121
- * @throws 缺少 Encoding API 时抛出 `Error`。
112
+ * @param value - 任意 Unicode 字符串
113
+ * @returns 带标准填充的 Base64 文本
114
+ * @remarks 使用内部 UTF-8 编码,不依赖平台 Encoding API。
122
115
  */
123
116
  function encodeBase64(value) {
124
117
  return encodeBytes(encodeUtf8(value));
@@ -126,9 +119,9 @@ function encodeBase64(value) {
126
119
  /**
127
120
  * 将标准 Base64 解码为 UTF-8 文本。
128
121
  *
129
- * @param value - Base64 文本。
122
+ * @param value - Base64 文本
130
123
  * @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。
131
- * @throws Base64 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。
124
+ * @throws Base64 或 UTF-8 非法时抛出 `TypeError`。
132
125
  */
133
126
  function decodeBase64(value) {
134
127
  return createDecodedText(decodeUtf8(decodeBase64Bytes(value)));
@@ -136,8 +129,8 @@ function decodeBase64(value) {
136
129
  /**
137
130
  * 将任意字节编码为无填充 Base64URL。
138
131
  *
139
- * @param bytes - 不会被修改的字节序列。
140
- * @returns 仅使用 URL 安全字母表的文本。
132
+ * @param bytes - 不会被修改的字节序列
133
+ * @returns 仅使用 URL 安全字母表的文本
141
134
  */
142
135
  function encodeBase64UrlBytes(bytes) {
143
136
  return encodeBytes(bytes).replace(/\+/gu, "-").replace(/\//gu, "_").replace(/=+$/u, "");
@@ -146,8 +139,8 @@ function encodeBase64UrlBytes(bytes) {
146
139
  * 解码 Base64URL 字节。
147
140
  *
148
141
  * @remarks 接受带填充和无填充形式,不接受空白或标准 Base64 的 `+`、`/`。
149
- * @param value - Base64URL 文本。
150
- * @returns 新建的字节数组。
142
+ * @param value - Base64URL 文本
143
+ * @returns 新建的字节数组
151
144
  * @throws 输入非法时抛出 `TypeError`。
152
145
  */
153
146
  function decodeBase64UrlBytes(value) {
@@ -156,9 +149,9 @@ function decodeBase64UrlBytes(value) {
156
149
  /**
157
150
  * 将 UTF-8 文本编码为无填充 Base64URL。
158
151
  *
159
- * @param value - 任意 Unicode 字符串。
160
- * @returns 仅使用 URL 安全字母表的文本。
161
- * @throws 缺少 Encoding API 时抛出 `Error`。
152
+ * @param value - 任意 Unicode 字符串
153
+ * @returns 仅使用 URL 安全字母表的文本
154
+ * @remarks 使用内部 UTF-8 编码,不依赖平台 Encoding API。
162
155
  */
163
156
  function encodeBase64Url(value) {
164
157
  return encodeBase64UrlBytes(encodeUtf8(value));
@@ -166,9 +159,9 @@ function encodeBase64Url(value) {
166
159
  /**
167
160
  * 将 Base64URL 解码为 UTF-8 文本。
168
161
  *
169
- * @param value - 带填充或无填充的 Base64URL 文本。
162
+ * @param value - 带填充或无填充的 Base64URL 文本
170
163
  * @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。
171
- * @throws Base64URL 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。
164
+ * @throws Base64URL 或 UTF-8 非法时抛出 `TypeError`。
172
165
  */
173
166
  function decodeBase64Url(value) {
174
167
  return createDecodedText(decodeUtf8(decodeBase64UrlBytes(value)));
@@ -177,14 +170,14 @@ function decodeBase64Url(value) {
177
170
  * 把 Latin-1 文本编码为标准 Base64。
178
171
  *
179
172
  * @param value - 每个 UTF-16 码元都必须位于 0–255 的文本。
180
- * @returns 带标准填充的 Base64 文本。
173
+ * @returns 带标准填充的 Base64 文本
181
174
  * @throws `TypeError` 当文本包含 Latin-1 范围外的码元。
182
175
  */
183
176
  function encodeLatin1Base64(value) {
184
177
  const bytes = new Uint8Array(value.length);
185
178
  for (let index = 0; index < value.length; index += 1) {
186
179
  const code = value.charCodeAt(index);
187
- if (code > 255) throw new TypeError("待编码字符串包含超出 Latin-1 范围的字符。");
180
+ if (code > 255) throw new TypeError("The input string contains characters outside the Latin-1 range.");
188
181
  bytes[index] = code;
189
182
  }
190
183
  return encodeBase64Bytes(bytes);
@@ -338,11 +331,11 @@ const base64PasswordDictionary = Object.freeze([
338
331
  /**
339
332
  * 校验 SecureBase64 兼容格式的前缀长度。
340
333
  *
341
- * @param length - 待校验的前缀字符数。
334
+ * @param length - 待校验的前缀字符数
342
335
  * @throws `RangeError` 当长度不是非负安全整数。
343
336
  */
344
337
  const assertPrefixLength = (length) => {
345
- if (!Number.isSafeInteger(length) || length < 0) throw new RangeError("`prefixStrLength` 必须是非负安全整数。");
338
+ if (!Number.isSafeInteger(length) || length < 0) throw new RangeError("`prefixStrLength` must be a nonnegative safe integer.");
346
339
  };
347
340
  /**
348
341
  * 生成 SecureBase64 兼容格式使用的随机前缀。
@@ -350,7 +343,7 @@ const assertPrefixLength = (length) => {
350
343
  * @remarks 优先使用 Web Crypto,能力缺失时回退到 `Math.random()`。前缀只用于降低相同明文
351
344
  * 产生相同载荷的概率,本身不构成加密,也不得承担安全用途。
352
345
  * @param length - 需要生成的字符数,调用前必须完成校验。
353
- * @returns 由历史字母表组成的文本。
346
+ * @returns 由历史字母表组成的文本
354
347
  */
355
348
  const createRandomPrefix = (length) => {
356
349
  let result = "";
@@ -361,14 +354,14 @@ const createRandomPrefix = (length) => {
361
354
  * 按固定字典向 Base64 文本插入冗余字符。
362
355
  *
363
356
  * @remarks 字典按高索引到低索引排列,确保插入不会改变尚未处理的位置。
364
- * @param base64Value - 尚未插入兼容字典字符的 Base64 文本。
365
- * @returns 与旧持久化格式兼容的 SecureBase64 载荷。
357
+ * @param base64Value - 尚未插入兼容字典字符的 Base64 文本
358
+ * @returns 与旧持久化格式兼容的 SecureBase64 载荷
366
359
  */
367
360
  const insertDictionaryCharacters = (base64Value) => {
368
361
  let result = base64Value;
369
362
  for (const item of base64PasswordDictionary) {
370
363
  if (item.index >= base64Value.length) continue;
371
- const character = base64Value[item.randomIndex] ?? base64Value.at(-1) ?? "";
364
+ const character = base64Value[item.randomIndex] ?? base64Value[base64Value.length - 1] ?? "";
372
365
  result = result.slice(0, item.index) + character + result.slice(item.index);
373
366
  }
374
367
  return result;
@@ -376,8 +369,8 @@ const insertDictionaryCharacters = (base64Value) => {
376
369
  /**
377
370
  * 移除历史字典插入的冗余字符。
378
371
  *
379
- * @param base64Value - 已移除随机前缀的 SecureBase64 载荷。
380
- * @returns 可交给标准 Base64 解码器的文本。
372
+ * @param base64Value - 已移除随机前缀的 SecureBase64 载荷
373
+ * @returns 可交给标准 Base64 解码器的文本
381
374
  */
382
375
  const removeDictionaryCharacters = (base64Value) => {
383
376
  let result = base64Value;
@@ -393,8 +386,8 @@ const removeDictionaryCharacters = (base64Value) => {
393
386
  * @remarks 给定相同的 6 字符前缀时,默认输出与旧有效载荷逐字符兼容。旧字典在 101–124 字符载荷中引用越界;这里复制末字符作为单字符回退,
394
387
  * 使旧删除字典流程仍能解码。传入 `0` 会同时关闭随机前缀与字典插入。
395
388
  * 该格式只是可逆编码,不提供加密、完整性或身份认证。
396
- * @param value - 任意可由 `encodeURIComponent` 处理的 Unicode 文本。
397
- * @param prefixLength - 随机字母前缀长度;默认 `6`。
389
+ * @param value - 任意可由 `encodeURIComponent` 处理的 Unicode 文本
390
+ * @param prefixLength - 随机字母前缀长度;默认 `6`
398
391
  * @returns 带随机前缀和兼容字典字符的 Base64 文本;空输入返回空字符串。
399
392
  * @throws `RangeError` 当前缀长度不是非负安全整数;输入包含孤立代理项时保留平台错误。
400
393
  */
@@ -417,7 +410,7 @@ function encodeSecureBase64(value, prefixLength = defaultRandomPrefixLength) {
417
410
  function decodeSecureBase64(value, prefixLength = defaultRandomPrefixLength) {
418
411
  if (value.length === 0) return createDecodedText("");
419
412
  assertPrefixLength(prefixLength);
420
- if (prefixLength > value.length) throw new TypeError("Base64 值的长度小于配置的前缀长度。");
413
+ if (prefixLength > value.length) throw new TypeError("The Base64 value is shorter than the configured prefix.");
421
414
  let encoded = value.slice(prefixLength);
422
415
  if (prefixLength !== 0) encoded = removeDictionaryCharacters(encoded);
423
416
  return createDecodedText(decodeURIComponent(decodeLatin1Base64(encoded)));
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/base64/index.ts"],"sourcesContent":["import { type DecodedText, createDecodedText, encodeUtf8, getTextDecoder } from \"../internal/text\";\nimport { randomInt } from \"../number/index\";\n\nexport type { DecodedText } from \"../internal/text\";\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 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。\n * @throws Base64 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。\n */\nexport function decodeBase64(value: string): DecodedText {\n\treturn createDecodedText(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 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。\n * @throws Base64URL 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。\n */\nexport function decodeBase64Url(value: string): DecodedText {\n\treturn createDecodedText(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 可直接使用或显式调用 `.parseJson<Value>()` 的 Latin-1 原始字符串。\n * @throws `TypeError` 当 Base64 格式或尾部位非法。\n */\nexport function decodeLatin1Base64(value: string): DecodedText {\n\tconst bytes = decodeBase64Bytes(value);\n\tlet result = \"\";\n\tfor (const byte of bytes) result += String.fromCharCode(byte);\n\treturn createDecodedText(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 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串;空输入返回空字符串。\n * @throws `RangeError` 当前缀长度不是非负安全整数;载荷、Base64 或 URI 编码非法时抛出 `TypeError` 或 `URIError`。\n */\nexport function decodeSecureBase64(value: string, prefixLength: number = defaultRandomPrefixLength): DecodedText {\n\tif (value.length === 0) return createDecodedText(\"\");\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 createDecodedText(decodeURIComponent(decodeLatin1Base64(encoded)));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["decodeUtf8Strict"],"sources":["../../src/base64/index.ts"],"sourcesContent":["import { type DecodedText, createDecodedText, decodeUtf8 as decodeUtf8Strict, encodeUtf8 } from \"../internal/text\";\nimport { randomInt } from \"../number/index\";\n\nexport type { DecodedText } from \"../internal/text\";\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/** 统一 UTF-8 解码错误,并保留底层原因。 */\nconst decodeUtf8 = (bytes: Uint8Array): string => {\n\ttry {\n\t\treturn decodeUtf8Strict(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 * @remarks 使用内部 UTF-8 编码,不依赖平台 Encoding API。\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 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。\n * @throws Base64 或 UTF-8 非法时抛出 `TypeError`。\n */\nexport function decodeBase64(value: string): DecodedText {\n\treturn createDecodedText(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 * @remarks 使用内部 UTF-8 编码,不依赖平台 Encoding API。\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 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。\n * @throws Base64URL 或 UTF-8 非法时抛出 `TypeError`。\n */\nexport function decodeBase64Url(value: string): DecodedText {\n\treturn createDecodedText(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 input string contains characters outside 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 可直接使用或显式调用 `.parseJson<Value>()` 的 Latin-1 原始字符串。\n * @throws `TypeError` 当 Base64 格式或尾部位非法。\n */\nexport function decodeLatin1Base64(value: string): DecodedText {\n\tconst bytes = decodeBase64Bytes(value);\n\tlet result = \"\";\n\tfor (const byte of bytes) result += String.fromCharCode(byte);\n\treturn createDecodedText(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 nonnegative safe integer.\");\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[base64Value.length - 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 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串;空输入返回空字符串。\n * @throws `RangeError` 当前缀长度不是非负安全整数;载荷、Base64 或 URI 编码非法时抛出 `TypeError` 或 `URIError`。\n */\nexport function decodeSecureBase64(value: string, prefixLength: number = defaultRandomPrefixLength): DecodedText {\n\tif (value.length === 0) return createDecodedText(\"\");\n\tassertPrefixLength(prefixLength);\n\tif (prefixLength > value.length) throw new TypeError(\"The Base64 value is shorter than the configured prefix.\");\n\tlet encoded = value.slice(prefixLength);\n\tif (prefixLength !== 0) encoded = removeDictionaryCharacters(encoded);\n\treturn createDecodedText(decodeURIComponent(decodeLatin1Base64(encoded)));\n}\n"],"mappings":"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"}
@@ -2,27 +2,27 @@
2
2
  /**
3
3
  * 校验 RGB 颜色通道。
4
4
  *
5
- * @param value - 待校验通道值。
6
- * @param channel - 用于错误消息的通道名称。
5
+ * @param value - 待校验通道值
6
+ * @param channel - 用于错误消息的通道名称
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}\` 必须是 0 到 255 之间的有限数。`);
10
+ if (!Number.isFinite(value) || value < 0 || value > 255) throw new RangeError(`\`${channel}\` must be a finite number between 0 and 255.`);
11
11
  };
12
12
  /**
13
13
  * 校验透明度通道。
14
14
  *
15
- * @param value - 待校验 Alpha 值。
15
+ * @param value - 待校验 Alpha 值
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` 必须是 0 到 1 之间的有限数。");
19
+ if (!Number.isFinite(value) || value < 0 || value > 1) throw new RangeError("`alpha` must be a finite number between 0 and 1.");
20
20
  };
21
21
  /**
22
22
  * 规范化十六进制颜色文本。
23
23
  *
24
- * @param value - 可带 `#` 的 3、4、6 或 8 位颜色文本。
25
- * @returns 不带 `#` 的 6 或 8 位文本。
24
+ * @param value - 可带 `#` 的 3、4、6 或 8 位颜色文本
25
+ * @returns 不带 `#` 的 6 或 8 位文本
26
26
  * @throws `TypeError` 当长度或字符不符合十六进制颜色格式。
27
27
  */
28
28
  const normalizeHexColor = (value) => {
@@ -32,20 +32,20 @@ const normalizeHexColor = (value) => {
32
32
  4,
33
33
  6,
34
34
  8
35
- ].includes(normalized.length) || !/^[\dA-F]+$/iu.test(normalized)) throw new TypeError("十六进制颜色必须包含 3、4、6 或 8 个十六进制字符。");
35
+ ].includes(normalized.length) || !/^[\dA-F]+$/iu.test(normalized)) throw new TypeError("A hex color must contain 3, 4, 6, or 8 hexadecimal digits.");
36
36
  return normalized.length <= 4 ? Array.from(normalized, (character) => `${character}${character}`).join("") : normalized;
37
37
  };
38
38
  /**
39
39
  * 格式化单个颜色通道。
40
40
  *
41
- * @param value - 已校验的 0 至 255 通道值。
42
- * @returns 舍入后的两位小写十六进制文本。
41
+ * @param value - 已校验的 0 至 255 通道值
42
+ * @returns 舍入后的两位小写十六进制文本
43
43
  */
44
44
  const formatHexChannel = (value) => Math.round(value).toString(16).padStart(2, "0");
45
45
  /**
46
46
  * 解析可带可不带 `#` 的 `rgb`、`rgba`、`rrggbb` 或 `rrggbbaa`。
47
47
  *
48
- * @param value - 十六进制颜色文本。
48
+ * @param value - 十六进制颜色文本
49
49
  * @returns 标准化的 RGBA 对象;省略 Alpha 时为 1。
50
50
  * @throws 输入非法时抛出 `TypeError`。
51
51
  */
@@ -63,7 +63,7 @@ function parseHexColor(value) {
63
63
  *
64
64
  * @param color - 颜色通道;RGB 会四舍五入到最近整数。
65
65
  * @param includeAlpha - 是否输出 Alpha;默认只在传入 Alpha 且小于 1 时输出。
66
- * @returns 小写 `#rrggbb` 或 `#rrggbbaa` 文本。
66
+ * @returns 小写 `#rrggbb` 或 `#rrggbbaa` 文本
67
67
  * @throws 通道或 Alpha 非法时抛出 `RangeError`。
68
68
  */
69
69
  function formatHexColor(color, includeAlpha = "alpha" in color && color.alpha < 1) {
@@ -77,22 +77,22 @@ function formatHexColor(color, includeAlpha = "alpha" in color && color.alpha <
77
77
  /**
78
78
  * 线性混合两种十六进制颜色,包括 Alpha 通道。
79
79
  *
80
- * @param first - `amount = 0` 时的颜色。
81
- * @param second - `amount = 1` 时的颜色。
82
- * @param amount - 0 至 1 的混合比例。
80
+ * @param first - `amount = 0` 时的颜色
81
+ * @param second - `amount = 1` 时的颜色
82
+ * @param amount - 0 至 1 的混合比例
83
83
  * @returns 小写十六进制颜色;任一输入含透明度时保留 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` 必须是 0 到 1 之间的有限数。");
87
+ if (!Number.isFinite(amount) || amount < 0 || amount > 1) throw new RangeError("`amount` must be a finite number between 0 and 1.");
88
88
  const left = parseHexColor(first);
89
89
  const right = parseHexColor(second);
90
90
  /**
91
91
  * 在单个 RGBA 通道上执行与外层相同权重的线性混合。
92
92
  *
93
- * @param start - 第一个颜色的通道值。
94
- * @param end - 第二个颜色的通道值。
95
- * @returns 按外层 `amount` 线性混合后的通道值。
93
+ * @param start - 第一个颜色的通道值
94
+ * @param end - 第二个颜色的通道值
95
+ * @returns 按外层 `amount` 线性混合后的通道值
96
96
  */
97
97
  const mix = (start, end) => start + (end - start) * amount;
98
98
  return formatHexColor({
@@ -105,8 +105,8 @@ function mixHexColors(first, second, amount) {
105
105
  /**
106
106
  * 按比例向黑色混合。
107
107
  *
108
- * @param color - 合法十六进制颜色。
109
- * @param amount - 0 至 1 的混合比例。
108
+ * @param color - 合法十六进制颜色
109
+ * @param amount - 0 至 1 的混合比例
110
110
  * @returns 混入黑色后的十六进制颜色;参数与异常语义见 {@link mixHexColors}。
111
111
  */
112
112
  function mixHexColorWithBlack(color, amount) {
@@ -115,8 +115,8 @@ function mixHexColorWithBlack(color, amount) {
115
115
  /**
116
116
  * 按比例向白色混合。
117
117
  *
118
- * @param color - 合法十六进制颜色。
119
- * @param amount - 0 至 1 的混合比例。
118
+ * @param color - 合法十六进制颜色
119
+ * @param amount - 0 至 1 的混合比例
120
120
  * @returns 混入白色后的十六进制颜色;参数与异常语义见 {@link mixHexColors}。
121
121
  */
122
122
  function mixHexColorWithWhite(color, amount) {
@@ -125,8 +125,8 @@ function mixHexColorWithWhite(color, amount) {
125
125
  /**
126
126
  * 按 WCAG sRGB 转换曲线线性化颜色通道。
127
127
  *
128
- * @param channel - 已校验的 0 至 255 通道值。
129
- * @returns 0 至 1 的线性光值。
128
+ * @param channel - 已校验的 0 至 255 通道值
129
+ * @returns 0 至 1 的线性光值
130
130
  */
131
131
  const linearizeSrgbChannel = (channel) => {
132
132
  const value = channel / 255;
@@ -136,8 +136,8 @@ const linearizeSrgbChannel = (channel) => {
136
136
  * 计算 WCAG sRGB 相对亮度。
137
137
  *
138
138
  * @remarks Alpha 通道不会参与计算;半透明颜色应先与实际背景混合。
139
- * @param color - 合法十六进制颜色。
140
- * @returns 0 至 1 的相对亮度。
139
+ * @param color - 合法十六进制颜色
140
+ * @returns 0 至 1 的相对亮度
141
141
  * @throws 输入非法时抛出 `TypeError`。
142
142
  */
143
143
  function relativeLuminance(color) {
@@ -148,9 +148,9 @@ function relativeLuminance(color) {
148
148
  * 计算两种不透明颜色的 WCAG 对比度,范围 1 至 21。
149
149
  *
150
150
  * @remarks 返回比值本身,不代表特定字号或 WCAG 等级必然通过。
151
- * @param first - 第一种十六进制颜色。
152
- * @param second - 第二种十六进制颜色。
153
- * @returns 较亮颜色与较暗颜色的对比度。
151
+ * @param first - 第一种十六进制颜色
152
+ * @param second - 第二种十六进制颜色
153
+ * @returns 较亮颜色与较暗颜色的对比度
154
154
  * @throws 输入非法时抛出 `TypeError`。
155
155
  */
156
156
  function contrastRatio(first, second) {
@@ -163,9 +163,9 @@ function contrastRatio(first, second) {
163
163
  /**
164
164
  * 从两个候选颜色中选择与背景对比度更高的一项。
165
165
  *
166
- * @param background - 实际不透明背景色。
167
- * @param first - 第一候选,默认黑色。
168
- * @param second - 第二候选,默认白色。
166
+ * @param background - 实际不透明背景色
167
+ * @param first - 第一候选,默认黑色
168
+ * @param second - 第二候选,默认白色
169
169
  * @returns 对比度较高的原始候选字符串;相同时返回 `first`。
170
170
  * @throws 任一颜色非法时抛出 `TypeError`。
171
171
  */
@@ -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}\\` 必须是 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(\"十六进制颜色必须包含 3、4、6 或 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) => 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}\\` must be a finite number between 0 and 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 between 0 and 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(\"A hex color must contain 3, 4, 6, or 8 hexadecimal digits.\");\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 between 0 and 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) => 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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"}