@fast-china/utils 2.1.1 → 2.1.2
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 +15 -0
- package/README.md +32 -3
- package/README.zh.md +32 -3
- package/dist/base64/index.d.mts +10 -9
- package/dist/base64/index.mjs +10 -10
- package/dist/base64/index.mjs.map +1 -1
- package/dist/crypto/index.d.mts +9 -8
- package/dist/crypto/index.mjs +10 -10
- package/dist/crypto/index.mjs.map +1 -1
- package/dist/index.d.mts +4 -3
- package/dist/index.global.min.js +2 -2
- package/dist/index.global.min.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/internal/text.d.mts +17 -0
- package/dist/internal/text.mjs +39 -1
- package/dist/internal/text.mjs.map +1 -1
- package/dist/logger/index.d.mts +26 -22
- package/dist/logger/index.mjs +50 -21
- package/dist/logger/index.mjs.map +1 -1
- package/dist/storage/index.d.mts +14 -5
- package/dist/storage/index.mjs +17 -5
- package/dist/storage/index.mjs.map +1 -1
- package/docs/API.md +32 -6
- package/docs/API.zh-CN.md +31 -6
- package/docs/RUNTIME_CONTRACT.md +9 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to Fast.Utils are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [2.1.2] - 2026-08-30
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added per-operation Storage `crypto` overrides for `Local` and `Session` reads and writes without mutating global configuration.
|
|
10
|
+
- Added chainable `.parseJson<T = any>()` access to primitive string results from Base64 and Crypto text decoding APIs.
|
|
11
|
+
- Added `configureLogger` for the default Logger and allowed Logger severity methods to emit data without a message string.
|
|
12
|
+
|
|
13
|
+
### Breaking Changes
|
|
14
|
+
|
|
15
|
+
- Replaced Logger's `info` method and level with `log`; `logger.log()` now maps directly to `sink.log()` and `console.log()`, and the default minimum level is now `debug`.
|
|
16
|
+
- Changed the default type parameter of `StorageArea.get` from `unknown` to `string`; runtime codec results remain unchanged and may still be objects or other JSON values.
|
|
17
|
+
- Refined `decodeBase64`, `decodeBase64Url`, `decodeLatin1Base64`, `decodeSecureBase64`, `AESDecrypt`, `AESDecryptAuthenticated`, `AESDecryptWithPassword`, and `RSADecryptOAEP` results to the primitive-string `DecodedText` type. The first text decode installs a non-enumerable `String.prototype.parseJson`; an existing foreign implementation causes an explicit conflict error instead of being overwritten.
|
|
18
|
+
|
|
5
19
|
## [2.1.1] - 2026-08-26
|
|
6
20
|
|
|
7
21
|
### Changed
|
|
@@ -73,6 +87,7 @@ All notable changes to Fast.Utils are documented in this file.
|
|
|
73
87
|
|
|
74
88
|
- Added authenticated ciphertext validation, bounded crypto parameters and payloads, unbiased Web Crypto randomness, prototype-safe query/object transforms, and namespace-scoped Storage cleanup.
|
|
75
89
|
|
|
90
|
+
[2.1.2]: https://github.com/China-xiaoFang/Fast.Utils/compare/v2.1.1...v2.1.2
|
|
76
91
|
[2.1.1]: https://github.com/China-xiaoFang/Fast.Utils/compare/v2.1.0...v2.1.1
|
|
77
92
|
[2.1.0]: https://github.com/China-xiaoFang/Fast.Utils/compare/v2.0.3...v2.1.0
|
|
78
93
|
[2.0.3]: https://github.com/China-xiaoFang/Fast.Utils/releases/tag/v2.0.3
|
package/README.md
CHANGED
|
@@ -39,9 +39,15 @@ import { Local, Session } from "@fast-china/utils";
|
|
|
39
39
|
Local.set("user", { id: 1 }, { ttlMs: 30 * 60 * 1000 });
|
|
40
40
|
const user = Local.get<{ id: number }>("user");
|
|
41
41
|
|
|
42
|
+
Local.set("private-user", { id: 2 }, { crypto: true });
|
|
43
|
+
const privateUser = Local.get<{ id: number }>("private-user", { crypto: true });
|
|
44
|
+
|
|
42
45
|
Session.set("redirect", "/home");
|
|
46
|
+
const redirect = Session.get("redirect"); // string | undefined
|
|
43
47
|
```
|
|
44
48
|
|
|
49
|
+
`get<Value = string>()` returns `string | undefined` when its generic is omitted, so string values require no type argument. Storage codecs still deserialize JSON at runtime; pass an explicit generic for an accurate type when the stored value is an object, array, or another non-string value.
|
|
50
|
+
|
|
45
51
|
Call `configureStorage` before the first Storage operation only when overriding defaults. The legacy-compatible `crypto` option applies reversible Base64 obfuscation; it is not encryption and must not protect secrets:
|
|
46
52
|
|
|
47
53
|
```ts
|
|
@@ -53,7 +59,7 @@ configureStorage({
|
|
|
53
59
|
});
|
|
54
60
|
```
|
|
55
61
|
|
|
56
|
-
The active configuration is immutable. Repeating the same configuration is idempotent; a conflicting configuration throws. A custom `codec` may be supplied instead of `crypto`. In uni-app, `Local` automatically uses the global synchronous Storage API; `Session` throws because uni-app has no sessionStorage equivalent. `clear()` removes only keys inside the active prefix.
|
|
62
|
+
The active global configuration is immutable. Repeating the same configuration is idempotent; a conflicting configuration throws. The `crypto` option on `set/get` overrides only that operation and must match when writing and reading the same entry; it does not mutate global configuration. A custom `codec` may be supplied instead of global `crypto`. In uni-app, `Local` automatically uses the global synchronous Storage API; `Session` throws because uni-app has no sessionStorage equivalent. `clear()` removes only keys inside the active prefix.
|
|
57
63
|
|
|
58
64
|
## Base64
|
|
59
65
|
|
|
@@ -62,8 +68,10 @@ Prefer the UTF-8 or Base64URL APIs in new code:
|
|
|
62
68
|
```ts
|
|
63
69
|
import { decodeBase64, encodeBase64, encodeBase64Url } from "@fast-china/utils";
|
|
64
70
|
|
|
65
|
-
const encoded = encodeBase64("Fast utilities");
|
|
66
|
-
decodeBase64(encoded);
|
|
71
|
+
const encoded = encodeBase64('{"name":"Fast utilities"}');
|
|
72
|
+
const decoded = decodeBase64(encoded);
|
|
73
|
+
decoded;
|
|
74
|
+
decoded.parseJson<{ name: string }>();
|
|
67
75
|
encodeBase64Url("path/value");
|
|
68
76
|
```
|
|
69
77
|
|
|
@@ -91,6 +99,21 @@ const installationId = getOrCreateInstallationId();
|
|
|
91
99
|
|
|
92
100
|
Call `configureInstallationIdentity` in the application entry before first use. The default business key is `identity:installation-id`; repeated identical configuration is idempotent and conflicting configuration throws. Installation Identity UUID generation prefers Web Crypto and falls back to `Math.random()` when unavailable.
|
|
93
101
|
|
|
102
|
+
## Logger
|
|
103
|
+
|
|
104
|
+
The default `logger` works without construction and has a minimum level of `debug`. Enable split mode at application startup when uni-app App-Plus needs HBuilderX-compatible object output:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { configureLogger, logger } from "@fast-china/utils";
|
|
108
|
+
|
|
109
|
+
configureLogger({ uniAppPlusSplit: true });
|
|
110
|
+
logger.log("Launch", { code: 200, data: { id: 1 } });
|
|
111
|
+
logger.error("Request", "request failed", error);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Log messages are optional, so objects, arrays, errors, and other values may be passed directly. Normal runtimes preserve the original values; App-Plus
|
|
115
|
+
split mode converts additional values into readable text one at a time. Use `createLogger` for an isolated configuration unaffected by the default Logger.
|
|
116
|
+
|
|
94
117
|
## Crypto
|
|
95
118
|
|
|
96
119
|
The TypeScript Crypto API mirrors the public methods and algorithm casing of .NET `CryptoUtil`. AES-GCM payloads, password-based AES payloads, PBKDF2 password hashes, and PEM keys interoperate across both implementations.
|
|
@@ -100,8 +123,14 @@ import { AESDecryptWithPassword, AESEncryptWithPassword } from "@fast-china/util
|
|
|
100
123
|
|
|
101
124
|
const payload = await AESEncryptWithPassword("protected content", "correct horse battery staple");
|
|
102
125
|
const plaintext = await AESDecryptWithPassword(payload, "correct horse battery staple");
|
|
126
|
+
plaintext;
|
|
127
|
+
|
|
128
|
+
const jsonPayload = await AESEncryptWithPassword('{"id":1}', "correct horse battery staple");
|
|
129
|
+
const result = (await AESDecryptWithPassword(jsonPayload, "correct horse battery staple")).parseJson<{ id: number }>();
|
|
103
130
|
```
|
|
104
131
|
|
|
132
|
+
Base64 and Crypto text decoding/decryption functions return the primitive-string `DecodedText` type, which can be used directly as a `string`; JSON is parsed only by an explicit `.parseJson<T = any>()` call. The first text decode lazily installs a non-enumerable `String.prototype.parseJson`; a foreign method with the same name causes an explicit conflict error. The generic type does not validate the runtime structure of untrusted JSON.
|
|
133
|
+
|
|
105
134
|
Store passwords with `HashPasswordPBKDF2SHA256` and `VerifyPasswordPBKDF2SHA256`. MD5, SHA-1, AES-CBC, and AES-ECB do not provide password-storage or authenticated-encryption guarantees. See the [API reference](./docs/API.md#crypto) for the complete method list and security boundaries.
|
|
106
135
|
|
|
107
136
|
## Vue 3
|
package/README.zh.md
CHANGED
|
@@ -39,9 +39,15 @@ import { Local, Session } from "@fast-china/utils";
|
|
|
39
39
|
Local.set("user", { id: 1 }, { ttlMs: 30 * 60 * 1000 });
|
|
40
40
|
const user = Local.get<{ id: number }>("user");
|
|
41
41
|
|
|
42
|
+
Local.set("private-user", { id: 2 }, { crypto: true });
|
|
43
|
+
const privateUser = Local.get<{ id: number }>("private-user", { crypto: true });
|
|
44
|
+
|
|
42
45
|
Session.set("redirect", "/home");
|
|
46
|
+
const redirect = Session.get("redirect"); // string | undefined
|
|
43
47
|
```
|
|
44
48
|
|
|
49
|
+
`get<Value = string>()` 未传泛型时默认返回 `string | undefined`,因此字符串场景可以直接调用。Storage Codec 仍会在运行时执行 JSON 反序列化;若存储的是对象、数组或其他非字符串值,建议显式传入对应泛型以获得准确类型。
|
|
50
|
+
|
|
45
51
|
只有需要覆盖默认值时,才需在首次 Storage 操作前调用 `configureStorage`。兼容旧版的 `crypto` 选项只执行可逆 Base64 混淆,不是加密,不能保护敏感数据:
|
|
46
52
|
|
|
47
53
|
```ts
|
|
@@ -53,7 +59,7 @@ configureStorage({
|
|
|
53
59
|
});
|
|
54
60
|
```
|
|
55
61
|
|
|
56
|
-
|
|
62
|
+
激活后的全局配置不可变;相同配置重复调用保持幂等,不同配置会抛错。`set/get` 的 `crypto` 选项只覆盖单次操作,读写同一条目时必须保持一致,不会改变全局配置。自定义 `codec` 与全局 `crypto` 不能同时使用。uni-app 中 `Local` 会自动使用全局同步 Storage API;由于没有等价的 sessionStorage,`Session` 会明确抛错。`clear()` 只清理当前前缀。
|
|
57
63
|
|
|
58
64
|
## Base64
|
|
59
65
|
|
|
@@ -62,8 +68,10 @@ configureStorage({
|
|
|
62
68
|
```ts
|
|
63
69
|
import { decodeBase64, encodeBase64, encodeBase64Url } from "@fast-china/utils";
|
|
64
70
|
|
|
65
|
-
const encoded = encodeBase64("Fast 工具库");
|
|
66
|
-
decodeBase64(encoded);
|
|
71
|
+
const encoded = encodeBase64('{"name":"Fast 工具库"}');
|
|
72
|
+
const decoded = decodeBase64(encoded);
|
|
73
|
+
decoded;
|
|
74
|
+
decoded.parseJson<{ name: string }>();
|
|
67
75
|
encodeBase64Url("path/参数");
|
|
68
76
|
```
|
|
69
77
|
|
|
@@ -91,6 +99,21 @@ const installationId = getOrCreateInstallationId();
|
|
|
91
99
|
|
|
92
100
|
在程序入口、首次使用安装标识前调用 `configureInstallationIdentity`。默认业务键是 `identity:installation-id`;相同配置可幂等重复调用,不同配置会抛错。安装标识 UUID 优先使用 Web Crypto 生成,能力缺失时回退到 `Math.random()`。它不是硬件 ID、认证凭证或风控信号。
|
|
93
101
|
|
|
102
|
+
## Logger
|
|
103
|
+
|
|
104
|
+
默认 `logger` 可以直接使用,最低输出级别为 `debug`。uni-app App-Plus 需要兼容 HBuilderX 对象输出时,在应用入口配置拆分模式:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { configureLogger, logger } from "@fast-china/utils";
|
|
108
|
+
|
|
109
|
+
configureLogger({ uniAppPlusSplit: true });
|
|
110
|
+
logger.log("Launch", { code: 200, data: { id: 1 } });
|
|
111
|
+
logger.error("Request", "请求失败", error);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
日志消息可以省略,对象、数组和错误等值可以直接传入。普通环境保留原始值;App-Plus 拆分模式会将附加值逐条转换为可读文本。
|
|
115
|
+
需要独立配置且不受默认 Logger 影响时使用 `createLogger`。
|
|
116
|
+
|
|
94
117
|
## Crypto
|
|
95
118
|
|
|
96
119
|
TypeScript Crypto API 与 .NET `CryptoUtil` 的公开方法及算法名称大小写保持一致。AES-GCM、密码 AES 载荷、PBKDF2 密码哈希和 PEM 密钥支持两端互操作。
|
|
@@ -100,8 +123,14 @@ import { AESDecryptWithPassword, AESEncryptWithPassword } from "@fast-china/util
|
|
|
100
123
|
|
|
101
124
|
const payload = await AESEncryptWithPassword("受保护内容", "correct horse battery staple");
|
|
102
125
|
const plaintext = await AESDecryptWithPassword(payload, "correct horse battery staple");
|
|
126
|
+
plaintext;
|
|
127
|
+
|
|
128
|
+
const jsonPayload = await AESEncryptWithPassword('{"id":1}', "correct horse battery staple");
|
|
129
|
+
const result = (await AESDecryptWithPassword(jsonPayload, "correct horse battery staple")).parseJson<{ id: number }>();
|
|
103
130
|
```
|
|
104
131
|
|
|
132
|
+
Base64 与 Crypto 的文本解码/解密入口返回原始字符串类型 `DecodedText`,可以直接作为 `string` 使用;只有显式调用 `.parseJson<T = any>()` 才解析 JSON。首次文本解码会按需安装不可枚举的 `String.prototype.parseJson`,若同名方法已被其他实现占用则明确抛错。泛型不会验证不可信 JSON 的实际结构。
|
|
133
|
+
|
|
105
134
|
密码存储使用 `HashPasswordPBKDF2SHA256` 和 `VerifyPasswordPBKDF2SHA256`。MD5、SHA-1、AES-CBC 与 AES-ECB 不提供密码存储或认证加密保证。完整方法列表和安全边界见 [API 文档](./docs/API.zh-CN.md#crypto)。
|
|
106
135
|
|
|
107
136
|
## Vue 3
|
package/dist/base64/index.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DecodedText } from "../internal/text.mjs";
|
|
1
2
|
//#region src/base64/index.d.ts
|
|
2
3
|
/**
|
|
3
4
|
* 将任意字节编码为标准 Base64。
|
|
@@ -27,10 +28,10 @@ declare function encodeBase64(value: string): string;
|
|
|
27
28
|
* 将标准 Base64 解码为 UTF-8 文本。
|
|
28
29
|
*
|
|
29
30
|
* @param value - Base64 文本。
|
|
30
|
-
* @returns
|
|
31
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。
|
|
31
32
|
* @throws Base64 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。
|
|
32
33
|
*/
|
|
33
|
-
declare function decodeBase64(value: string):
|
|
34
|
+
declare function decodeBase64(value: string): DecodedText;
|
|
34
35
|
/**
|
|
35
36
|
* 将任意字节编码为无填充 Base64URL。
|
|
36
37
|
*
|
|
@@ -59,10 +60,10 @@ declare function encodeBase64Url(value: string): string;
|
|
|
59
60
|
* 将 Base64URL 解码为 UTF-8 文本。
|
|
60
61
|
*
|
|
61
62
|
* @param value - 带填充或无填充的 Base64URL 文本。
|
|
62
|
-
* @returns
|
|
63
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。
|
|
63
64
|
* @throws Base64URL 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。
|
|
64
65
|
*/
|
|
65
|
-
declare function decodeBase64Url(value: string):
|
|
66
|
+
declare function decodeBase64Url(value: string): DecodedText;
|
|
66
67
|
/**
|
|
67
68
|
* 把 Latin-1 文本编码为标准 Base64。
|
|
68
69
|
*
|
|
@@ -75,10 +76,10 @@ declare function encodeLatin1Base64(value: string): string;
|
|
|
75
76
|
* 把标准 Base64 解码为 Latin-1 文本。
|
|
76
77
|
*
|
|
77
78
|
* @param value - 标准 Base64 文本;允许 ASCII 空白和省略尾部填充。
|
|
78
|
-
* @returns
|
|
79
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的 Latin-1 原始字符串。
|
|
79
80
|
* @throws `TypeError` 当 Base64 格式或尾部位非法。
|
|
80
81
|
*/
|
|
81
|
-
declare function decodeLatin1Base64(value: string):
|
|
82
|
+
declare function decodeLatin1Base64(value: string): DecodedText;
|
|
82
83
|
/**
|
|
83
84
|
* 使用固定字典和随机前缀编码文本。
|
|
84
85
|
*
|
|
@@ -96,10 +97,10 @@ declare function encodeSecureBase64(value: string, prefixLength?: number): strin
|
|
|
96
97
|
*
|
|
97
98
|
* @param value - SecureBase64 文本;必须使用与编码时相同的前缀长度。
|
|
98
99
|
* @param prefixLength - 需要移除的前缀长度;默认 `6`,传入 `0` 时不移除字典字符。
|
|
99
|
-
* @returns
|
|
100
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串;空输入返回空字符串。
|
|
100
101
|
* @throws `RangeError` 当前缀长度不是非负安全整数;载荷、Base64 或 URI 编码非法时抛出 `TypeError` 或 `URIError`。
|
|
101
102
|
*/
|
|
102
|
-
declare function decodeSecureBase64(value: string, prefixLength?: number):
|
|
103
|
+
declare function decodeSecureBase64(value: string, prefixLength?: number): DecodedText;
|
|
103
104
|
//#endregion
|
|
104
|
-
export { decodeBase64, decodeBase64Bytes, decodeBase64Url, decodeBase64UrlBytes, decodeLatin1Base64, decodeSecureBase64, encodeBase64, encodeBase64Bytes, encodeBase64Url, encodeBase64UrlBytes, encodeLatin1Base64, encodeSecureBase64 };
|
|
105
|
+
export { type DecodedText, decodeBase64, decodeBase64Bytes, decodeBase64Url, decodeBase64UrlBytes, decodeLatin1Base64, decodeSecureBase64, encodeBase64, encodeBase64Bytes, encodeBase64Url, encodeBase64UrlBytes, encodeLatin1Base64, encodeSecureBase64 };
|
|
105
106
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/base64/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { encodeUtf8, getTextDecoder } from "../internal/text.mjs";
|
|
1
|
+
import { createDecodedText, encodeUtf8, getTextDecoder } from "../internal/text.mjs";
|
|
2
2
|
import { randomInt } from "../number/index.mjs";
|
|
3
3
|
//#region src/base64/index.ts
|
|
4
4
|
const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
@@ -127,11 +127,11 @@ function encodeBase64(value) {
|
|
|
127
127
|
* 将标准 Base64 解码为 UTF-8 文本。
|
|
128
128
|
*
|
|
129
129
|
* @param value - Base64 文本。
|
|
130
|
-
* @returns
|
|
130
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。
|
|
131
131
|
* @throws Base64 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。
|
|
132
132
|
*/
|
|
133
133
|
function decodeBase64(value) {
|
|
134
|
-
return decodeUtf8(decodeBase64Bytes(value));
|
|
134
|
+
return createDecodedText(decodeUtf8(decodeBase64Bytes(value)));
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
137
|
* 将任意字节编码为无填充 Base64URL。
|
|
@@ -167,11 +167,11 @@ function encodeBase64Url(value) {
|
|
|
167
167
|
* 将 Base64URL 解码为 UTF-8 文本。
|
|
168
168
|
*
|
|
169
169
|
* @param value - 带填充或无填充的 Base64URL 文本。
|
|
170
|
-
* @returns
|
|
170
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串。
|
|
171
171
|
* @throws Base64URL 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。
|
|
172
172
|
*/
|
|
173
173
|
function decodeBase64Url(value) {
|
|
174
|
-
return decodeUtf8(decodeBase64UrlBytes(value));
|
|
174
|
+
return createDecodedText(decodeUtf8(decodeBase64UrlBytes(value)));
|
|
175
175
|
}
|
|
176
176
|
/**
|
|
177
177
|
* 把 Latin-1 文本编码为标准 Base64。
|
|
@@ -193,14 +193,14 @@ function encodeLatin1Base64(value) {
|
|
|
193
193
|
* 把标准 Base64 解码为 Latin-1 文本。
|
|
194
194
|
*
|
|
195
195
|
* @param value - 标准 Base64 文本;允许 ASCII 空白和省略尾部填充。
|
|
196
|
-
* @returns
|
|
196
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的 Latin-1 原始字符串。
|
|
197
197
|
* @throws `TypeError` 当 Base64 格式或尾部位非法。
|
|
198
198
|
*/
|
|
199
199
|
function decodeLatin1Base64(value) {
|
|
200
200
|
const bytes = decodeBase64Bytes(value);
|
|
201
201
|
let result = "";
|
|
202
202
|
for (const byte of bytes) result += String.fromCharCode(byte);
|
|
203
|
-
return result;
|
|
203
|
+
return createDecodedText(result);
|
|
204
204
|
}
|
|
205
205
|
const randomPrefixAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
206
206
|
const defaultRandomPrefixLength = 6;
|
|
@@ -411,16 +411,16 @@ function encodeSecureBase64(value, prefixLength = defaultRandomPrefixLength) {
|
|
|
411
411
|
*
|
|
412
412
|
* @param value - SecureBase64 文本;必须使用与编码时相同的前缀长度。
|
|
413
413
|
* @param prefixLength - 需要移除的前缀长度;默认 `6`,传入 `0` 时不移除字典字符。
|
|
414
|
-
* @returns
|
|
414
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 Unicode 字符串;空输入返回空字符串。
|
|
415
415
|
* @throws `RangeError` 当前缀长度不是非负安全整数;载荷、Base64 或 URI 编码非法时抛出 `TypeError` 或 `URIError`。
|
|
416
416
|
*/
|
|
417
417
|
function decodeSecureBase64(value, prefixLength = defaultRandomPrefixLength) {
|
|
418
|
-
if (value.length === 0) return "";
|
|
418
|
+
if (value.length === 0) return createDecodedText("");
|
|
419
419
|
assertPrefixLength(prefixLength);
|
|
420
420
|
if (prefixLength > value.length) throw new TypeError("Base64 值的长度小于配置的前缀长度。");
|
|
421
421
|
let encoded = value.slice(prefixLength);
|
|
422
422
|
if (prefixLength !== 0) encoded = removeDictionaryCharacters(encoded);
|
|
423
|
-
return decodeURIComponent(decodeLatin1Base64(encoded));
|
|
423
|
+
return createDecodedText(decodeURIComponent(decodeLatin1Base64(encoded)));
|
|
424
424
|
}
|
|
425
425
|
//#endregion
|
|
426
426
|
export { decodeBase64, decodeBase64Bytes, decodeBase64Url, decodeBase64UrlBytes, decodeLatin1Base64, decodeSecureBase64, encodeBase64, encodeBase64Bytes, encodeBase64Url, encodeBase64UrlBytes, encodeLatin1Base64, encodeSecureBase64 };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/base64/index.ts"],"sourcesContent":["import { encodeUtf8, getTextDecoder } from \"../internal/text\";\nimport { randomInt } from \"../number/index\";\n\nconst alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n/** 内部共享解码器支持的两种字符表规则。 */\ntype Base64Variant = \"standard\" | \"url\";\n\n/**\n * 创建统一的 Base64 输入错误。\n *\n * @param variant - 当前解析的标准 Base64 或 Base64URL 变体。\n * @returns 带有具体变体名称的 `TypeError`。\n */\nconst invalidBase64 = (variant: Base64Variant): TypeError => {\n\treturn new TypeError(`该值不是有效的 ${variant === \"url\" ? \"Base64URL\" : \"Base64\"}。`);\n};\n\n/**\n * 校验并规范化 Base64 文本。\n *\n * @remarks 标准 Base64 允许 ASCII 空白,Base64URL 不允许;两者最终都会转换为标准字符表和完整填充。\n * @param value - 原始编码文本。\n * @param variant - 需要应用的字符表与空白规则。\n * @returns 使用标准字符表且长度为 4 倍数的文本。\n * @throws `TypeError` 当字符、填充位置或编码长度非法。\n */\nconst normalizeBase64 = (value: string, variant: Base64Variant): string => {\n\tconst withoutWhitespace = variant === \"standard\" ? value.replace(/[\\t\\n\\f\\r ]+/gu, \"\") : value;\n\tconst pattern = variant === \"url\" ? /^[\\w-]*={0,2}$/u : /^[A-Za-z\\d+/]*={0,2}$/u;\n\tif (!pattern.test(withoutWhitespace) || withoutWhitespace.length % 4 === 1) throw invalidBase64(variant);\n\tif (withoutWhitespace.includes(\"=\") && withoutWhitespace.length % 4 !== 0) throw invalidBase64(variant);\n\n\tconst standard = (variant === \"url\" ? withoutWhitespace.replace(/-/gu, \"+\").replace(/_/gu, \"/\") : withoutWhitespace).replace(/=+$/u, \"\");\n\treturn standard.padEnd(Math.ceil(standard.length / 4) * 4, \"=\");\n};\n\n/**\n * 使用固定字符表编码任意字节。\n *\n * @param bytes - 不会被修改的源字节。\n * @returns 带标准 `=` 填充的 Base64 文本。\n */\nconst encodeBytes = (bytes: Uint8Array): string => {\n\tlet result = \"\";\n\tfor (let index = 0; index < bytes.length; index += 3) {\n\t\tconst first = bytes[index] ?? 0;\n\t\tconst second = bytes[index + 1];\n\t\tconst third = bytes[index + 2];\n\t\tconst bitmap = (first << 16) | ((second ?? 0) << 8) | (third ?? 0);\n\t\tresult += alphabet[(bitmap >> 18) & 63] ?? \"\";\n\t\tresult += alphabet[(bitmap >> 12) & 63] ?? \"\";\n\t\tresult += second === undefined ? \"=\" : (alphabet[(bitmap >> 6) & 63] ?? \"\");\n\t\tresult += third === undefined ? \"=\" : (alphabet[bitmap & 63] ?? \"\");\n\t}\n\treturn result;\n};\n\n/**\n * 把 Base64 或 Base64URL 文本解码为字节。\n *\n * @remarks 最后一组未使用位必须为零,以拒绝同一字节序列的非规范编码。\n * @param value - 待解码文本。\n * @param variant - 输入使用的编码变体。\n * @returns 新建的字节数组。\n * @throws `TypeError` 当输入格式或尾部位非法。\n */\nconst decodeBytes = (value: string, variant: Base64Variant): Uint8Array => {\n\tconst normalized = normalizeBase64(value, variant);\n\tif (normalized.length === 0) return new Uint8Array();\n\n\tconst padding = normalized.endsWith(\"==\") ? 2 : normalized.endsWith(\"=\") ? 1 : 0;\n\tconst result = new Uint8Array((normalized.length / 4) * 3 - padding);\n\tlet outputIndex = 0;\n\n\tfor (let index = 0; index < normalized.length; index += 4) {\n\t\tconst first = alphabet.indexOf(normalized[index] ?? \"\");\n\t\tconst second = alphabet.indexOf(normalized[index + 1] ?? \"\");\n\t\tconst thirdCharacter = normalized[index + 2] ?? \"=\";\n\t\tconst fourthCharacter = normalized[index + 3] ?? \"=\";\n\t\tconst third = thirdCharacter === \"=\" ? 0 : alphabet.indexOf(thirdCharacter);\n\t\tconst fourth = fourthCharacter === \"=\" ? 0 : alphabet.indexOf(fourthCharacter);\n\t\tif (first < 0 || second < 0 || third < 0 || fourth < 0) throw invalidBase64(variant);\n\n\t\tconst isLast = index + 4 === normalized.length;\n\t\t// 填充字符对应的未使用低位必须为零,否则不同文本可以解码为同一字节序列。\n\t\tif (isLast && ((padding === 2 && (second & 15) !== 0) || (padding === 1 && (third & 3) !== 0))) {\n\t\t\tthrow invalidBase64(variant);\n\t\t}\n\n\t\tconst bitmap = (first << 18) | (second << 12) | (third << 6) | fourth;\n\t\tif (outputIndex < result.length) result[outputIndex++] = (bitmap >> 16) & 255;\n\t\tif (outputIndex < result.length) result[outputIndex++] = (bitmap >> 8) & 255;\n\t\tif (outputIndex < result.length) result[outputIndex++] = bitmap & 255;\n\t}\n\treturn result;\n};\n\n/**\n * 以 Fatal 模式解码 UTF-8。\n *\n * @param bytes - 待解码字节。\n * @returns 解码后的 JavaScript 字符串。\n * @throws `TypeError` 当字节不是有效 UTF-8;原始平台异常保存在 `cause` 中。\n */\nconst decodeUtf8 = (bytes: Uint8Array): string => {\n\tconst textDecoder = getTextDecoder();\n\ttry {\n\t\treturn textDecoder.decode(bytes);\n\t} catch (cause) {\n\t\tthrow new TypeError(\"解码后的字节不是有效的 UTF-8。\", { cause });\n\t}\n};\n\n/**\n * 将任意字节编码为标准 Base64。\n *\n * @param bytes - 不会被修改的字节序列。\n * @returns 带标准 `=` 填充的 Base64 文本。\n */\nexport function encodeBase64Bytes(bytes: Uint8Array): string {\n\treturn encodeBytes(bytes);\n}\n\n/**\n * 解码标准 Base64。\n *\n * @remarks 允许省略填充和包含 ASCII 空白,但拒绝非规范尾部位。\n * @param value - Base64 文本。\n * @returns 新建的字节数组。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function decodeBase64Bytes(value: string): Uint8Array {\n\treturn decodeBytes(value, \"standard\");\n}\n\n/**\n * 将 UTF-8 文本编码为标准 Base64。\n *\n * @param value - 任意 Unicode 字符串。\n * @returns 带标准填充的 Base64 文本。\n * @throws 缺少 Encoding API 时抛出 `Error`。\n */\nexport function encodeBase64(value: string): string {\n\treturn encodeBytes(encodeUtf8(value));\n}\n\n/**\n * 将标准 Base64 解码为 UTF-8 文本。\n *\n * @param value - Base64 文本。\n * @returns 解码后的 Unicode 字符串。\n * @throws Base64 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。\n */\nexport function decodeBase64(value: string): string {\n\treturn decodeUtf8(decodeBase64Bytes(value));\n}\n\n/**\n * 将任意字节编码为无填充 Base64URL。\n *\n * @param bytes - 不会被修改的字节序列。\n * @returns 仅使用 URL 安全字母表的文本。\n */\nexport function encodeBase64UrlBytes(bytes: Uint8Array): string {\n\treturn encodeBytes(bytes).replace(/\\+/gu, \"-\").replace(/\\//gu, \"_\").replace(/=+$/u, \"\");\n}\n\n/**\n * 解码 Base64URL 字节。\n *\n * @remarks 接受带填充和无填充形式,不接受空白或标准 Base64 的 `+`、`/`。\n * @param value - Base64URL 文本。\n * @returns 新建的字节数组。\n * @throws 输入非法时抛出 `TypeError`。\n */\nexport function decodeBase64UrlBytes(value: string): Uint8Array {\n\treturn decodeBytes(value, \"url\");\n}\n\n/**\n * 将 UTF-8 文本编码为无填充 Base64URL。\n *\n * @param value - 任意 Unicode 字符串。\n * @returns 仅使用 URL 安全字母表的文本。\n * @throws 缺少 Encoding API 时抛出 `Error`。\n */\nexport function encodeBase64Url(value: string): string {\n\treturn encodeBase64UrlBytes(encodeUtf8(value));\n}\n\n/**\n * 将 Base64URL 解码为 UTF-8 文本。\n *\n * @param value - 带填充或无填充的 Base64URL 文本。\n * @returns 解码后的 Unicode 字符串。\n * @throws Base64URL 或 UTF-8 非法时抛出 `TypeError`;缺少 Encoding API 时抛出 `Error`。\n */\nexport function decodeBase64Url(value: string): string {\n\treturn decodeUtf8(decodeBase64UrlBytes(value));\n}\n\n/**\n * 把 Latin-1 文本编码为标准 Base64。\n *\n * @param value - 每个 UTF-16 码元都必须位于 0–255 的文本。\n * @returns 带标准填充的 Base64 文本。\n * @throws `TypeError` 当文本包含 Latin-1 范围外的码元。\n */\nexport function encodeLatin1Base64(value: string): string {\n\tconst bytes = new Uint8Array(value.length);\n\tfor (let index = 0; index < value.length; index += 1) {\n\t\tconst code = value.charCodeAt(index);\n\t\tif (code > 255) throw new TypeError(\"待编码字符串包含超出 Latin-1 范围的字符。\");\n\t\tbytes[index] = code;\n\t}\n\treturn encodeBase64Bytes(bytes);\n}\n\n/**\n * 把标准 Base64 解码为 Latin-1 文本。\n *\n * @param value - 标准 Base64 文本;允许 ASCII 空白和省略尾部填充。\n * @returns 每个字节直接映射为同值 UTF-16 码元的文本。\n * @throws `TypeError` 当 Base64 格式或尾部位非法。\n */\nexport function decodeLatin1Base64(value: string): string {\n\tconst bytes = decodeBase64Bytes(value);\n\tlet result = \"\";\n\tfor (const byte of bytes) result += String.fromCharCode(byte);\n\treturn result;\n}\n\nconst randomPrefixAlphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\nconst defaultRandomPrefixLength = 6;\n\n/** SecureBase64 兼容格式中一组不可变的字符插入位置。 */\ninterface Base64PasswordDictionaryEntry {\n\t/** 原始载荷中的固定字符索引;超出短载荷长度时忽略当前条目。 */\n\tindex: number;\n\t/** 从原始载荷复制字符的固定索引;数值属于持久化协议,不能重新计算。 */\n\trandomIndex: number;\n}\n\n/** 固定 SecureBase64 字典;索引、顺序与映射属于持久化格式,禁止修改。 */\nconst base64PasswordDictionary: readonly Readonly<Base64PasswordDictionaryEntry>[] = Object.freeze([\n\t{ index: 977, randomIndex: 188 },\n\t{ index: 926, randomIndex: 201 },\n\t{ index: 851, randomIndex: 225 },\n\t{ index: 700, randomIndex: 255 },\n\t{ index: 600, randomIndex: 268 },\n\t{ index: 500, randomIndex: 277 },\n\t{ index: 400, randomIndex: 288 },\n\t{ index: 330, randomIndex: 327 },\n\t{ index: 300, randomIndex: 180 },\n\t{ index: 200, randomIndex: 178 },\n\t{ index: 100, randomIndex: 124 },\n\t{ index: 98, randomIndex: 95 },\n\t{ index: 92, randomIndex: 90 },\n\t{ index: 91, randomIndex: 87 },\n\t{ index: 88, randomIndex: 84 },\n\t{ index: 82, randomIndex: 79 },\n\t{ index: 78, randomIndex: 71 },\n\t{ index: 72, randomIndex: 69 },\n\t{ index: 68, randomIndex: 66 },\n\t{ index: 59, randomIndex: 55 },\n\t{ index: 48, randomIndex: 43 },\n\t{ index: 42, randomIndex: 37 },\n\t{ index: 36, randomIndex: 30 },\n\t{ index: 33, randomIndex: 27 },\n\t{ index: 24, randomIndex: 20 },\n\t{ index: 23, randomIndex: 18 },\n\t{ index: 21, randomIndex: 16 },\n\t{ index: 17, randomIndex: 14 },\n\t{ index: 13, randomIndex: 9 },\n\t{ index: 7, randomIndex: 4 },\n\t{ index: 5, randomIndex: 3 },\n\t{ index: 2, randomIndex: 1 },\n]);\n\n/**\n * 校验 SecureBase64 兼容格式的前缀长度。\n *\n * @param length - 待校验的前缀字符数。\n * @throws `RangeError` 当长度不是非负安全整数。\n */\nconst assertPrefixLength = (length: number): void => {\n\tif (!Number.isSafeInteger(length) || length < 0) throw new RangeError(\"`prefixStrLength` 必须是非负安全整数。\");\n};\n\n/**\n * 生成 SecureBase64 兼容格式使用的随机前缀。\n *\n * @remarks 优先使用 Web Crypto,能力缺失时回退到 `Math.random()`。前缀只用于降低相同明文\n * 产生相同载荷的概率,本身不构成加密,也不得承担安全用途。\n * @param length - 需要生成的字符数,调用前必须完成校验。\n * @returns 由历史字母表组成的文本。\n */\nconst createRandomPrefix = (length: number): string => {\n\tlet result = \"\";\n\tfor (let index = 0; index < length; index += 1) {\n\t\tresult += randomPrefixAlphabet[randomInt(0, randomPrefixAlphabet.length)] ?? \"\";\n\t}\n\treturn result;\n};\n\n/**\n * 按固定字典向 Base64 文本插入冗余字符。\n *\n * @remarks 字典按高索引到低索引排列,确保插入不会改变尚未处理的位置。\n * @param base64Value - 尚未插入兼容字典字符的 Base64 文本。\n * @returns 与旧持久化格式兼容的 SecureBase64 载荷。\n */\nconst insertDictionaryCharacters = (base64Value: string): string => {\n\tlet result = base64Value;\n\tfor (const item of base64PasswordDictionary) {\n\t\tif (item.index >= base64Value.length) continue;\n\t\t// 旧字典的 index=100 项会在 101–124 字符载荷中越界;退回末字符可保持单字符插入协议,旧解码器仍能移除。\n\t\tconst character = base64Value[item.randomIndex] ?? base64Value.at(-1) ?? \"\";\n\t\tresult = result.slice(0, item.index) + character + result.slice(item.index);\n\t}\n\treturn result;\n};\n\n/**\n * 移除历史字典插入的冗余字符。\n *\n * @param base64Value - 已移除随机前缀的 SecureBase64 载荷。\n * @returns 可交给标准 Base64 解码器的文本。\n */\nconst removeDictionaryCharacters = (base64Value: string): string => {\n\tlet result = base64Value;\n\tfor (let index = base64PasswordDictionary.length - 1; index >= 0; index -= 1) {\n\t\tconst item = base64PasswordDictionary[index];\n\t\tif (item !== undefined && item.index < base64Value.length) result = result.slice(0, item.index) + result.slice(item.index + 1);\n\t}\n\treturn result;\n};\n\n/**\n * 使用固定字典和随机前缀编码文本。\n *\n * @remarks 给定相同的 6 字符前缀时,默认输出与旧有效载荷逐字符兼容。旧字典在 101–124 字符载荷中引用越界;这里复制末字符作为单字符回退,\n * 使旧删除字典流程仍能解码。传入 `0` 会同时关闭随机前缀与字典插入。\n * 该格式只是可逆编码,不提供加密、完整性或身份认证。\n * @param value - 任意可由 `encodeURIComponent` 处理的 Unicode 文本。\n * @param prefixLength - 随机字母前缀长度;默认 `6`。\n * @returns 带随机前缀和兼容字典字符的 Base64 文本;空输入返回空字符串。\n * @throws `RangeError` 当前缀长度不是非负安全整数;输入包含孤立代理项时保留平台错误。\n */\nexport function encodeSecureBase64(value: string, prefixLength: number = defaultRandomPrefixLength): string {\n\tif (value.length === 0) return \"\";\n\tassertPrefixLength(prefixLength);\n\tconst prefix = createRandomPrefix(prefixLength);\n\tlet encoded = encodeLatin1Base64(encodeURIComponent(value));\n\tif (prefixLength !== 0) encoded = insertDictionaryCharacters(encoded);\n\treturn prefix + encoded;\n}\n\n/**\n * 解码 {@link encodeSecureBase64} 生成的 SecureBase64 兼容格式。\n *\n * @param value - SecureBase64 文本;必须使用与编码时相同的前缀长度。\n * @param prefixLength - 需要移除的前缀长度;默认 `6`,传入 `0` 时不移除字典字符。\n * @returns 解码后的 Unicode 文本;空输入返回空字符串。\n * @throws `RangeError` 当前缀长度不是非负安全整数;载荷、Base64 或 URI 编码非法时抛出 `TypeError` 或 `URIError`。\n */\nexport function decodeSecureBase64(value: string, prefixLength: number = defaultRandomPrefixLength): string {\n\tif (value.length === 0) return \"\";\n\tassertPrefixLength(prefixLength);\n\tif (prefixLength > value.length) throw new TypeError(\"Base64 值的长度小于配置的前缀长度。\");\n\tlet encoded = value.slice(prefixLength);\n\tif (prefixLength !== 0) encoded = removeDictionaryCharacters(encoded);\n\treturn decodeURIComponent(decodeLatin1Base64(encoded));\n}\n"],"mappings":"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"}
|
|
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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"}
|
package/dist/crypto/index.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DecodedText } from "../internal/text.mjs";
|
|
1
2
|
import "crypto-js/pad-ansix923.js";
|
|
2
3
|
//#region src/crypto/index.d.ts
|
|
3
4
|
/** AES 分组密码模式;与 .NET `CipherMode.CBC` 和 `CipherMode.ECB` 对应。 */
|
|
@@ -177,10 +178,10 @@ declare function AESEncrypt(dataStr: string, key: string, vector: string, cipher
|
|
|
177
178
|
* @param vector - 加密时使用的初始化向量文本。
|
|
178
179
|
* @param cipherMode - AES 分组模式,默认 `CBC`。
|
|
179
180
|
* @param paddingMode - AES 填充模式,默认 `PKCS7`。
|
|
180
|
-
* @returns
|
|
181
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串;输入、密钥或 IV 为空白时返回 `null`。
|
|
181
182
|
* @throws 模式、填充、Base64、密钥或密文无效时抛出错误。
|
|
182
183
|
*/
|
|
183
|
-
declare function AESDecrypt(dataStr: string, key: string, vector: string, cipherMode?: AesCipherMode, paddingMode?: AesPaddingMode):
|
|
184
|
+
declare function AESDecrypt(dataStr: string, key: string, vector: string, cipherMode?: AesCipherMode, paddingMode?: AesPaddingMode): DecodedText | null;
|
|
184
185
|
/**
|
|
185
186
|
* 使用 SHA-256 归一化文本密钥,再以 AES-256-GCM 认证加密 UTF-8 文本。
|
|
186
187
|
*
|
|
@@ -196,10 +197,10 @@ declare function AESEncryptAuthenticated(plaintext: string, key: string): Promis
|
|
|
196
197
|
*
|
|
197
198
|
* @param payload - Base64 编码的 v1 AES-GCM 二进制载荷。
|
|
198
199
|
* @param key - 加密时使用的非空 UTF-8 文本密钥。
|
|
199
|
-
* @returns
|
|
200
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串。
|
|
200
201
|
* @throws 载荷格式无效、密钥错误或认证失败时抛出错误。
|
|
201
202
|
*/
|
|
202
|
-
declare function AESDecryptAuthenticated(payload: string, key: string): Promise<
|
|
203
|
+
declare function AESDecryptAuthenticated(payload: string, key: string): Promise<DecodedText>;
|
|
203
204
|
/**
|
|
204
205
|
* 使用 PBKDF2-HMAC-SHA-256 派生密钥,再以 AES-256-GCM 认证加密 UTF-8 文本。
|
|
205
206
|
*
|
|
@@ -218,11 +219,11 @@ declare function AESEncryptWithPassword(plaintext: string, password: string, ite
|
|
|
218
219
|
*
|
|
219
220
|
* @param payload - 未修改的 v1 载荷,最大约 16 MiB 文本。
|
|
220
221
|
* @param password - 加密时使用的口令。
|
|
221
|
-
* @returns
|
|
222
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串。
|
|
222
223
|
* @throws 格式或字段非法时抛出 `TypeError`,载荷过大时抛出 `RangeError`,认证或密码
|
|
223
224
|
* 失败及缺少平台能力时抛出 `Error`。
|
|
224
225
|
*/
|
|
225
|
-
declare function AESDecryptWithPassword(payload: string, password: string): Promise<
|
|
226
|
+
declare function AESDecryptWithPassword(payload: string, password: string): Promise<DecodedText>;
|
|
226
227
|
/**
|
|
227
228
|
* 生成可供 RSA-OAEP/SHA-256 与 RSA-PSS/SHA-256 共用的 PEM 密钥对。
|
|
228
229
|
*
|
|
@@ -245,10 +246,10 @@ declare function RSAEncryptOAEP(plaintext: string, publicKeyPem: string): Promis
|
|
|
245
246
|
*
|
|
246
247
|
* @param ciphertext - Base64 编码的 RSA 密文。
|
|
247
248
|
* @param privateKeyPem - 未加密的 PKCS#8 PEM 私钥。
|
|
248
|
-
* @returns
|
|
249
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串。
|
|
249
250
|
* @throws 私钥、Base64 或密文无效时抛出错误。
|
|
250
251
|
*/
|
|
251
|
-
declare function RSADecryptOAEP(ciphertext: string, privateKeyPem: string): Promise<
|
|
252
|
+
declare function RSADecryptOAEP(ciphertext: string, privateKeyPem: string): Promise<DecodedText>;
|
|
252
253
|
/**
|
|
253
254
|
* 使用 RSA-PSS/SHA-256 私钥签名文本或字节。
|
|
254
255
|
*
|
package/dist/crypto/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { encodeUtf8, getTextDecoder } from "../internal/text.mjs";
|
|
1
|
+
import { createDecodedText, encodeUtf8, getTextDecoder } from "../internal/text.mjs";
|
|
2
2
|
import { decodeBase64Bytes, decodeBase64UrlBytes, encodeBase64Bytes, encodeBase64UrlBytes } from "../base64/index.mjs";
|
|
3
3
|
import AES from "crypto-js/aes.js";
|
|
4
4
|
import CryptoCore from "crypto-js/core.js";
|
|
@@ -439,7 +439,7 @@ function AESEncrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
|
|
|
439
439
|
* @param vector - 加密时使用的初始化向量文本。
|
|
440
440
|
* @param cipherMode - AES 分组模式,默认 `CBC`。
|
|
441
441
|
* @param paddingMode - AES 填充模式,默认 `PKCS7`。
|
|
442
|
-
* @returns
|
|
442
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串;输入、密钥或 IV 为空白时返回 `null`。
|
|
443
443
|
* @throws 模式、填充、Base64、密钥或密文无效时抛出错误。
|
|
444
444
|
*/
|
|
445
445
|
function AESDecrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKCS7") {
|
|
@@ -466,14 +466,14 @@ function AESDecrypt(dataStr, key, vector, cipherMode = "CBC", paddingMode = "PKC
|
|
|
466
466
|
if (ciphertextBytes.length === 0 || ciphertextBytes.length % 16 !== 0) throw new RangeError("AES 密文必须至少包含一个完整的 16 字节块。");
|
|
467
467
|
const keyBytes = Utf8.parse(key.padEnd(32, "f").slice(0, 32));
|
|
468
468
|
const vectorBytes = Utf8.parse(vector.padEnd(16, "f").slice(0, 16));
|
|
469
|
-
return AES.decrypt(dataStr, keyBytes, cipherMode === "CBC" ? {
|
|
469
|
+
return createDecodedText(AES.decrypt(dataStr, keyBytes, cipherMode === "CBC" ? {
|
|
470
470
|
iv: vectorBytes,
|
|
471
471
|
padding
|
|
472
472
|
} : {
|
|
473
473
|
iv: vectorBytes,
|
|
474
474
|
mode: ECB,
|
|
475
475
|
padding
|
|
476
|
-
}).toString(Utf8);
|
|
476
|
+
}).toString(Utf8));
|
|
477
477
|
}
|
|
478
478
|
/**
|
|
479
479
|
* 使用 SHA-256 归一化文本密钥,再以 AES-256-GCM 认证加密 UTF-8 文本。
|
|
@@ -508,7 +508,7 @@ async function AESEncryptAuthenticated(plaintext, key) {
|
|
|
508
508
|
*
|
|
509
509
|
* @param payload - Base64 编码的 v1 AES-GCM 二进制载荷。
|
|
510
510
|
* @param key - 加密时使用的非空 UTF-8 文本密钥。
|
|
511
|
-
* @returns
|
|
511
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串。
|
|
512
512
|
* @throws 载荷格式无效、密钥错误或认证失败时抛出错误。
|
|
513
513
|
*/
|
|
514
514
|
async function AESDecryptAuthenticated(payload, key) {
|
|
@@ -529,7 +529,7 @@ async function AESDecryptAuthenticated(payload, key) {
|
|
|
529
529
|
name: "AES-GCM",
|
|
530
530
|
tagLength: 128
|
|
531
531
|
}, cryptoKey, toArrayBuffer(ciphertextAndTag));
|
|
532
|
-
return getTextDecoder().decode(plaintext);
|
|
532
|
+
return createDecodedText(getTextDecoder().decode(plaintext));
|
|
533
533
|
}
|
|
534
534
|
/**
|
|
535
535
|
* 使用 PBKDF2-HMAC-SHA-256 派生密钥,再以 AES-256-GCM 认证加密 UTF-8 文本。
|
|
@@ -580,7 +580,7 @@ async function AESEncryptWithPassword(plaintext, password, iterations = defaultP
|
|
|
580
580
|
*
|
|
581
581
|
* @param payload - 未修改的 v1 载荷,最大约 16 MiB 文本。
|
|
582
582
|
* @param password - 加密时使用的口令。
|
|
583
|
-
* @returns
|
|
583
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串。
|
|
584
584
|
* @throws 格式或字段非法时抛出 `TypeError`,载荷过大时抛出 `RangeError`,认证或密码
|
|
585
585
|
* 失败及缺少平台能力时抛出 `Error`。
|
|
586
586
|
*/
|
|
@@ -621,7 +621,7 @@ async function AESDecryptWithPassword(payload, password) {
|
|
|
621
621
|
name: "AES-GCM",
|
|
622
622
|
tagLength: 128
|
|
623
623
|
}, key, toArrayBuffer(ciphertext));
|
|
624
|
-
return textDecoder.decode(plaintext);
|
|
624
|
+
return createDecodedText(textDecoder.decode(plaintext));
|
|
625
625
|
} catch (cause) {
|
|
626
626
|
throw new Error("无法认证或解密载荷。", { cause });
|
|
627
627
|
}
|
|
@@ -666,7 +666,7 @@ async function RSAEncryptOAEP(plaintext, publicKeyPem) {
|
|
|
666
666
|
*
|
|
667
667
|
* @param ciphertext - Base64 编码的 RSA 密文。
|
|
668
668
|
* @param privateKeyPem - 未加密的 PKCS#8 PEM 私钥。
|
|
669
|
-
* @returns
|
|
669
|
+
* @returns 可直接使用或显式调用 `.parseJson<Value>()` 的原始 UTF-8 字符串。
|
|
670
670
|
* @throws 私钥、Base64 或密文无效时抛出错误。
|
|
671
671
|
*/
|
|
672
672
|
async function RSADecryptOAEP(ciphertext, privateKeyPem) {
|
|
@@ -676,7 +676,7 @@ async function RSADecryptOAEP(ciphertext, privateKeyPem) {
|
|
|
676
676
|
name: "RSA-OAEP"
|
|
677
677
|
}, false, ["decrypt"]);
|
|
678
678
|
const plaintext = await crypto.subtle.decrypt({ name: "RSA-OAEP" }, key, toArrayBuffer(decodeBase64Bytes(ciphertext)));
|
|
679
|
-
return getTextDecoder().decode(plaintext);
|
|
679
|
+
return createDecodedText(getTextDecoder().decode(plaintext));
|
|
680
680
|
}
|
|
681
681
|
/**
|
|
682
682
|
* 使用 RSA-PSS/SHA-256 私钥签名文本或字节。
|