@fast-china/utils 2.1.9 → 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.
- package/CHANGELOG.md +29 -0
- package/README.md +5 -2
- package/README.zh.md +5 -2
- package/dist/array/index.mjs +21 -21
- package/dist/array/index.mjs.map +1 -1
- package/dist/async/index.mjs +32 -32
- package/dist/async/index.mjs.map +1 -1
- package/dist/base64/index.mjs +44 -51
- package/dist/base64/index.mjs.map +1 -1
- package/dist/color/index.mjs +33 -33
- package/dist/color/index.mjs.map +1 -1
- package/dist/crypto/index.mjs +153 -155
- package/dist/crypto/index.mjs.map +1 -1
- package/dist/date/index.mjs +79 -46
- package/dist/date/index.mjs.map +1 -1
- package/dist/dom/style.mjs +7 -7
- package/dist/dom/style.mjs.map +1 -1
- package/dist/env/index.mjs +9 -14
- package/dist/env/index.mjs.map +1 -1
- package/dist/function/index.mjs +4 -4
- package/dist/function/index.mjs.map +1 -1
- package/dist/identity/index.mjs +7 -7
- package/dist/identity/index.mjs.map +1 -1
- package/dist/index.d.mts +393 -366
- package/dist/index.global.min.js +2 -2
- package/dist/index.global.min.js.map +1 -1
- package/dist/index.mjs +2 -1
- package/dist/internal/text.mjs +70 -26
- package/dist/internal/text.mjs.map +1 -1
- package/dist/logger/index.mjs +12 -13
- package/dist/logger/index.mjs.map +1 -1
- package/dist/number/index.mjs +63 -42
- package/dist/number/index.mjs.map +1 -1
- package/dist/object/index.mjs +32 -31
- package/dist/object/index.mjs.map +1 -1
- package/dist/storage/index.mjs +58 -63
- package/dist/storage/index.mjs.map +1 -1
- package/dist/string/index.mjs +60 -64
- package/dist/string/index.mjs.map +1 -1
- package/dist/vue/breakpoints.mjs +14 -10
- package/dist/vue/breakpoints.mjs.map +1 -1
- package/dist/vue/element-size.mjs +4 -4
- package/dist/vue/element-size.mjs.map +1 -1
- package/dist/vue/emits.mjs +7 -7
- package/dist/vue/emits.mjs.map +1 -1
- package/dist/vue/event-listener.mjs +5 -5
- package/dist/vue/event-listener.mjs.map +1 -1
- package/dist/vue/expose.mjs +3 -3
- package/dist/vue/expose.mjs.map +1 -1
- package/dist/vue/func.mjs +2 -2
- package/dist/vue/func.mjs.map +1 -1
- package/dist/vue/index.mjs +2 -1
- package/dist/vue/install.mjs +24 -24
- package/dist/vue/install.mjs.map +1 -1
- package/dist/vue/now.mjs +4 -5
- package/dist/vue/now.mjs.map +1 -1
- package/dist/vue/props.mjs +5 -5
- package/dist/vue/props.mjs.map +1 -1
- package/dist/vue/render.mjs +2 -2
- package/dist/vue/render.mjs.map +1 -1
- package/dist/vue/resize-observer.mjs +4 -6
- package/dist/vue/resize-observer.mjs.map +1 -1
- package/dist/vue/slots.mjs.map +1 -1
- package/dist/vue/transition.mjs +58 -0
- package/dist/vue/transition.mjs.map +1 -0
- package/dist/vue/window-size.mjs +2 -4
- package/dist/vue/window-size.mjs.map +1 -1
- package/dist/vue/with.mjs +1 -1
- package/dist/vue/with.mjs.map +1 -1
- package/package.json +2 -1
- package/dist/internal/runtime.mjs +0 -32
- package/dist/internal/runtime.mjs.map +0 -1
package/dist/base64/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createDecodedText,
|
|
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(
|
|
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
|
|
84
|
+
return decodeUtf8$1(bytes);
|
|
92
85
|
} catch (cause) {
|
|
93
|
-
throw new TypeError("
|
|
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
|
-
* @
|
|
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
|
|
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
|
-
* @
|
|
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
|
|
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("
|
|
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.
|
|
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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"}
|
package/dist/color/index.mjs
CHANGED
|
@@ -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}\`
|
|
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`
|
|
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("
|
|
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`
|
|
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
|
*/
|
package/dist/color/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/color/index.ts"],"sourcesContent":["/** 0 至 255 范围的 RGB
|
|
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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"}
|