@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/dist/index.mjs CHANGED
@@ -10,7 +10,7 @@ import { detectRuntime, hasWebCrypto, isBrowser, isMobileUserAgent, isNode, isTa
10
10
  import { Local, Session, base64StorageCodec, configureStorage, isStorageConfigured } from "./storage/index.mjs";
11
11
  import { camelCase, copy, decodeURIComponentRepeatedly, escapeHtml, generateUuidV4, isUuidV4, isValidJson, kebabCase, lowerFirst, normalizeWhitespace, parseQueryString, pascalCase, randomString, splitWords, truncateGraphemes, upperFirst } from "./string/index.mjs";
12
12
  import { configureInstallationIdentity, getOrCreateInstallationId, installationIdentity } from "./identity/index.mjs";
13
- import { createLogger, logger } from "./logger/index.mjs";
13
+ import { configureLogger, createLogger, logger } from "./logger/index.mjs";
14
14
  import { hasOwn, isPlainObject, mapValues, omit, pick, shallowEqual, toQueryString } from "./object/index.mjs";
15
15
  import { useEmits } from "./vue/emits.mjs";
16
16
  import { useExpose } from "./vue/expose.mjs";
@@ -20,4 +20,4 @@ import { definePropType, useProps } from "./vue/props.mjs";
20
20
  import { useRender } from "./vue/render.mjs";
21
21
  import { makeSlots } from "./vue/slots.mjs";
22
22
  import { withDefineType } from "./vue/with.mjs";
23
- export { AESDecrypt, AESDecryptAuthenticated, AESDecryptWithPassword, AESEncrypt, AESEncryptAuthenticated, AESEncryptWithPassword, DeriveECDHKeySHA256, DeriveECDHSecret, ECDSASign, ECDSAVerify, FixedTimeEquals, GenerateECDHKeyPair, GenerateECDSAKeyPair, GenerateRSAKeyPair, GenerateRandomBytes, HKDFSHA256, HMACSHA256Encrypt, HMACSHA384Encrypt, HMACSHA512Encrypt, HashPasswordPBKDF2SHA256, Local, MD5Encrypt, PBKDF2SHA256, RSADecryptOAEP, RSAEncryptOAEP, RSASignPSS, RSAVerifyPSS, SHA1Encrypt, SHA256Bytes, SHA256Encrypt, SHA384Bytes, SHA384Encrypt, SHA512Bytes, SHA512Encrypt, Session, VerifyPasswordPBKDF2SHA256, addCssUnit, addDays, addMonths, addYears, allEqualBy, average, base64StorageCodec, callOptionalFunction, camelCase, chunk, clamp, configureInstallationIdentity, configureStorage, contrastRatio, copy, createDateRangeShortcuts, createDateShortcuts, createLogger, createOneMonthRangeFromToday, debounce, decodeBase64, decodeBase64Bytes, decodeBase64Url, decodeBase64UrlBytes, decodeLatin1Base64, decodeSecureBase64, decodeURIComponentRepeatedly, definePropType, detectRuntime, difference, encodeBase64, encodeBase64Bytes, encodeBase64Url, encodeBase64UrlBytes, encodeLatin1Base64, encodeSecureBase64, endOfDay, escapeHtml, formatBytes, formatChineseRelativeTime, formatHexColor, formatRelativeTime, generateUuidV4, getLocalDayBounds, getLocalTimeGreeting, getOrCreateInstallationId, getStartOfToday, groupBy, hasDuplicatesBy, hasOwn, hasWebCrypto, inRange, installationIdentity, intersection, isBrowser, isDateAfterNow, isFuture, isMobileUserAgent, isNode, isPlainObject, isSameDay, isStorageConfigured, isTabletUserAgent, isUniApp, isUuidV4, isValidDate, isValidJson, isWebWorker, isWithinInterval, kebabCase, lerp, logger, lowerFirst, makeSlots, mapConcurrent, mapValues, mixHexColorWithBlack, mixHexColorWithWhite, mixHexColors, normalizeWhitespace, omit, parseHexColor, parseQueryString, partition, pascalCase, pick, pickHigherContrastColor, randomInt, randomString, relativeLuminance, removeNullishValues, retry, roundTo, serializeStyle, shallowEqual, sleep, splitWords, startOfDay, sum, throttle, toDate, toQueryString, truncateGraphemes, unique, uniqueBy, upperFirst, useEmits, useExpose, useProps, useRender, withDefineType, withInstall, withInstallDirective, withNoopInstall, withTimeout };
23
+ export { AESDecrypt, AESDecryptAuthenticated, AESDecryptWithPassword, AESEncrypt, AESEncryptAuthenticated, AESEncryptWithPassword, DeriveECDHKeySHA256, DeriveECDHSecret, ECDSASign, ECDSAVerify, FixedTimeEquals, GenerateECDHKeyPair, GenerateECDSAKeyPair, GenerateRSAKeyPair, GenerateRandomBytes, HKDFSHA256, HMACSHA256Encrypt, HMACSHA384Encrypt, HMACSHA512Encrypt, HashPasswordPBKDF2SHA256, Local, MD5Encrypt, PBKDF2SHA256, RSADecryptOAEP, RSAEncryptOAEP, RSASignPSS, RSAVerifyPSS, SHA1Encrypt, SHA256Bytes, SHA256Encrypt, SHA384Bytes, SHA384Encrypt, SHA512Bytes, SHA512Encrypt, Session, VerifyPasswordPBKDF2SHA256, addCssUnit, addDays, addMonths, addYears, allEqualBy, average, base64StorageCodec, callOptionalFunction, camelCase, chunk, clamp, configureInstallationIdentity, configureLogger, configureStorage, contrastRatio, copy, createDateRangeShortcuts, createDateShortcuts, createLogger, createOneMonthRangeFromToday, debounce, decodeBase64, decodeBase64Bytes, decodeBase64Url, decodeBase64UrlBytes, decodeLatin1Base64, decodeSecureBase64, decodeURIComponentRepeatedly, definePropType, detectRuntime, difference, encodeBase64, encodeBase64Bytes, encodeBase64Url, encodeBase64UrlBytes, encodeLatin1Base64, encodeSecureBase64, endOfDay, escapeHtml, formatBytes, formatChineseRelativeTime, formatHexColor, formatRelativeTime, generateUuidV4, getLocalDayBounds, getLocalTimeGreeting, getOrCreateInstallationId, getStartOfToday, groupBy, hasDuplicatesBy, hasOwn, hasWebCrypto, inRange, installationIdentity, intersection, isBrowser, isDateAfterNow, isFuture, isMobileUserAgent, isNode, isPlainObject, isSameDay, isStorageConfigured, isTabletUserAgent, isUniApp, isUuidV4, isValidDate, isValidJson, isWebWorker, isWithinInterval, kebabCase, lerp, logger, lowerFirst, makeSlots, mapConcurrent, mapValues, mixHexColorWithBlack, mixHexColorWithWhite, mixHexColors, normalizeWhitespace, omit, parseHexColor, parseQueryString, partition, pascalCase, pick, pickHigherContrastColor, randomInt, randomString, relativeLuminance, removeNullishValues, retry, roundTo, serializeStyle, shallowEqual, sleep, splitWords, startOfDay, sum, throttle, toDate, toQueryString, truncateGraphemes, unique, uniqueBy, upperFirst, useEmits, useExpose, useProps, useRender, withDefineType, withInstall, withInstallDirective, withNoopInstall, withTimeout };
@@ -0,0 +1,17 @@
1
+ //#region src/internal/text.d.ts
2
+ /** 解码或解密后的字符串扩展。 */
3
+ interface DecodedTextExtension {
4
+ /**
5
+ * 显式把原始文本解析为 JSON 值。
6
+ *
7
+ * @remarks 泛型只描述调用方期望的类型,不验证实际 JSON 结构;不可信数据仍需执行运行时校验。
8
+ * @returns `JSON.parse` 生成的对象、数组、标量或 `null`。
9
+ * @throws `TypeError` 当原始文本不是合法 JSON。
10
+ */
11
+ parseJson: <Value = any>() => Value;
12
+ }
13
+ /** 可直接作为原始字符串使用,并支持显式 JSON 解析的解码或解密结果。 */
14
+ type DecodedText = string & DecodedTextExtension;
15
+ //#endregion
16
+ export { DecodedText };
17
+ //# sourceMappingURL=text.d.mts.map
@@ -29,7 +29,45 @@ const getTextEncoder = () => {
29
29
  * @throws `Error` 当当前平台没有提供 `TextEncoder`。
30
30
  */
31
31
  const encodeUtf8 = (value) => getTextEncoder().encode(value);
32
+ const parseJsonMarker = Symbol.for("@fast-china/utils/parse-json");
33
+ /** 把当前字符串解析为 JSON 值。 */
34
+ const parseJson = function() {
35
+ try {
36
+ return JSON.parse(String(this));
37
+ } catch (cause) {
38
+ throw new TypeError("解码后的文本不是有效的 JSON。", { cause });
39
+ }
40
+ };
41
+ Object.defineProperty(parseJson, parseJsonMarker, { value: true });
42
+ /** 按需安装不可枚举的字符串 JSON 解析扩展。 */
43
+ const ensureParseJsonExtension = () => {
44
+ const descriptor = Object.getOwnPropertyDescriptor(String.prototype, "parseJson");
45
+ if (descriptor !== void 0) {
46
+ if (typeof descriptor.value === "function" && Reflect.get(descriptor.value, parseJsonMarker) === true) return;
47
+ throw new TypeError("String.prototype.parseJson 已被其他实现占用。");
48
+ }
49
+ try {
50
+ Object.defineProperty(String.prototype, "parseJson", {
51
+ configurable: true,
52
+ enumerable: false,
53
+ value: parseJson,
54
+ writable: true
55
+ });
56
+ } catch (cause) {
57
+ throw new TypeError("当前运行环境不允许安装 String.prototype.parseJson。", { cause });
58
+ }
59
+ };
60
+ /**
61
+ * 创建可链式解析 JSON 的原始字符串。
62
+ *
63
+ * @param text - 解码或解密后的原始文本。
64
+ * @returns 可直接作为字符串使用或显式调用 `.parseJson<Value>()` 的结果。
65
+ */
66
+ const createDecodedText = (text) => {
67
+ ensureParseJsonExtension();
68
+ return text;
69
+ };
32
70
  //#endregion
33
- export { encodeUtf8, getTextDecoder, getTextEncoder };
71
+ export { createDecodedText, encodeUtf8, getTextDecoder, getTextEncoder };
34
72
 
35
73
  //# sourceMappingURL=text.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"text.mjs","names":[],"sources":["../../src/internal/text.ts"],"sourcesContent":["/**\n * 延迟解析 UTF-8 TextDecoder,确保模块导入阶段不依赖 Encoding API。\n *\n * @returns 启用 Fatal 模式的新解码器,非法 UTF-8 会直接失败。\n * @throws `Error` 当当前平台没有提供 `TextDecoder`。\n */\nexport const getTextDecoder = (): TextDecoder => {\n\tconst TextDecoderConstructor = globalThis.TextDecoder;\n\tif (typeof TextDecoderConstructor !== \"function\") {\n\t\tthrow new Error(\"当前运行环境不支持 TextDecoder。\");\n\t}\n\treturn new TextDecoderConstructor(\"utf-8\", { fatal: true });\n};\n\n/**\n * 延迟解析 TextEncoder,让纯字节 API 在缺少 Encoding API 的平台仍可导入。\n *\n * @returns 新建的 UTF-8 编码器。\n * @throws `Error` 当当前平台没有提供 `TextEncoder`。\n */\nexport const getTextEncoder = (): TextEncoder => {\n\tconst TextEncoderConstructor = globalThis.TextEncoder;\n\tif (typeof TextEncoderConstructor !== \"function\") {\n\t\tthrow new Error(\"当前运行环境不支持 TextEncoder。\");\n\t}\n\treturn new TextEncoderConstructor();\n};\n\n/**\n * 在确认平台能力后把 JavaScript 字符串编码为 UTF-8。\n *\n * @param value - 待编码文本。\n * @returns 使用独立 ArrayBuffer 的 UTF-8 字节数组。\n * @throws `Error` 当当前平台没有提供 `TextEncoder`。\n */\nexport const encodeUtf8 = (value: string): Uint8Array<ArrayBuffer> => getTextEncoder().encode(value);\n"],"mappings":";;;;;;;AAMA,MAAa,uBAAoC;CAChD,MAAM,yBAAyB,WAAW;CAC1C,IAAI,OAAO,2BAA2B,YACrC,MAAM,IAAI,MAAM,wBAAwB;CAEzC,OAAO,IAAI,uBAAuB,SAAS,EAAE,OAAO,KAAK,CAAC;AAC3D;;;;;;;AAQA,MAAa,uBAAoC;CAChD,MAAM,yBAAyB,WAAW;CAC1C,IAAI,OAAO,2BAA2B,YACrC,MAAM,IAAI,MAAM,wBAAwB;CAEzC,OAAO,IAAI,uBAAuB;AACnC;;;;;;;;AASA,MAAa,cAAc,UAA2C,eAAe,CAAC,CAAC,OAAO,KAAK"}
1
+ {"version":3,"file":"text.mjs","names":[],"sources":["../../src/internal/text.ts"],"sourcesContent":["/**\n * 延迟解析 UTF-8 TextDecoder,确保模块导入阶段不依赖 Encoding API。\n *\n * @returns 启用 Fatal 模式的新解码器,非法 UTF-8 会直接失败。\n * @throws `Error` 当当前平台没有提供 `TextDecoder`。\n */\nexport const getTextDecoder = (): TextDecoder => {\n\tconst TextDecoderConstructor = globalThis.TextDecoder;\n\tif (typeof TextDecoderConstructor !== \"function\") {\n\t\tthrow new Error(\"当前运行环境不支持 TextDecoder。\");\n\t}\n\treturn new TextDecoderConstructor(\"utf-8\", { fatal: true });\n};\n\n/**\n * 延迟解析 TextEncoder,让纯字节 API 在缺少 Encoding API 的平台仍可导入。\n *\n * @returns 新建的 UTF-8 编码器。\n * @throws `Error` 当当前平台没有提供 `TextEncoder`。\n */\nexport const getTextEncoder = (): TextEncoder => {\n\tconst TextEncoderConstructor = globalThis.TextEncoder;\n\tif (typeof TextEncoderConstructor !== \"function\") {\n\t\tthrow new Error(\"当前运行环境不支持 TextEncoder。\");\n\t}\n\treturn new TextEncoderConstructor();\n};\n\n/**\n * 在确认平台能力后把 JavaScript 字符串编码为 UTF-8。\n *\n * @param value - 待编码文本。\n * @returns 使用独立 ArrayBuffer 的 UTF-8 字节数组。\n * @throws `Error` 当当前平台没有提供 `TextEncoder`。\n */\nexport const encodeUtf8 = (value: string): Uint8Array<ArrayBuffer> => getTextEncoder().encode(value);\n\n/** 解码或解密后的字符串扩展。 */\ninterface DecodedTextExtension {\n\t/**\n\t * 显式把原始文本解析为 JSON 值。\n\t *\n\t * @remarks 泛型只描述调用方期望的类型,不验证实际 JSON 结构;不可信数据仍需执行运行时校验。\n\t * @returns `JSON.parse` 生成的对象、数组、标量或 `null`。\n\t * @throws `TypeError` 当原始文本不是合法 JSON。\n\t */\n\t// eslint-disable-next-line @typescript-eslint/no-explicit-any -- 未传泛型时按公共 API 约定保留 JSON.parse 的 any 返回类型。\n\tparseJson: <Value = any>() => Value;\n}\n\n/** 可直接作为原始字符串使用,并支持显式 JSON 解析的解码或解密结果。 */\nexport type DecodedText = string & DecodedTextExtension;\n\nconst parseJsonMarker = Symbol.for(\"@fast-china/utils/parse-json\");\n\n/** 把当前字符串解析为 JSON 值。 */\nconst parseJson = function <Value = ReturnType<typeof JSON.parse>>(this: string): Value {\n\ttry {\n\t\treturn JSON.parse(String(this)) as Value;\n\t} catch (cause) {\n\t\tthrow new TypeError(\"解码后的文本不是有效的 JSON。\", { cause });\n\t}\n};\n\nObject.defineProperty(parseJson, parseJsonMarker, { value: true });\n\n/** 按需安装不可枚举的字符串 JSON 解析扩展。 */\nconst ensureParseJsonExtension = (): void => {\n\tconst descriptor = Object.getOwnPropertyDescriptor(String.prototype, \"parseJson\");\n\tif (descriptor !== undefined) {\n\t\tif (typeof descriptor.value === \"function\" && Reflect.get(descriptor.value, parseJsonMarker) === true) return;\n\t\tthrow new TypeError(\"String.prototype.parseJson 已被其他实现占用。\");\n\t}\n\n\ttry {\n\t\tObject.defineProperty(String.prototype, \"parseJson\", {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: false,\n\t\t\tvalue: parseJson,\n\t\t\twritable: true,\n\t\t});\n\t} catch (cause) {\n\t\tthrow new TypeError(\"当前运行环境不允许安装 String.prototype.parseJson。\", { cause });\n\t}\n};\n\n/**\n * 创建可链式解析 JSON 的原始字符串。\n *\n * @param text - 解码或解密后的原始文本。\n * @returns 可直接作为字符串使用或显式调用 `.parseJson<Value>()` 的结果。\n */\nexport const createDecodedText = (text: string): DecodedText => {\n\tensureParseJsonExtension();\n\treturn text as DecodedText;\n};\n"],"mappings":";;;;;;;AAMA,MAAa,uBAAoC;CAChD,MAAM,yBAAyB,WAAW;CAC1C,IAAI,OAAO,2BAA2B,YACrC,MAAM,IAAI,MAAM,wBAAwB;CAEzC,OAAO,IAAI,uBAAuB,SAAS,EAAE,OAAO,KAAK,CAAC;AAC3D;;;;;;;AAQA,MAAa,uBAAoC;CAChD,MAAM,yBAAyB,WAAW;CAC1C,IAAI,OAAO,2BAA2B,YACrC,MAAM,IAAI,MAAM,wBAAwB;CAEzC,OAAO,IAAI,uBAAuB;AACnC;;;;;;;;AASA,MAAa,cAAc,UAA2C,eAAe,CAAC,CAAC,OAAO,KAAK;AAkBnG,MAAM,kBAAkB,OAAO,IAAI,8BAA8B;;AAGjE,MAAM,YAAY,WAAsE;CACvF,IAAI;EACH,OAAO,KAAK,MAAM,OAAO,IAAI,CAAC;CAC/B,SAAS,OAAO;EACf,MAAM,IAAI,UAAU,qBAAqB,EAAE,MAAM,CAAC;CACnD;AACD;AAEA,OAAO,eAAe,WAAW,iBAAiB,EAAE,OAAO,KAAK,CAAC;;AAGjE,MAAM,iCAAuC;CAC5C,MAAM,aAAa,OAAO,yBAAyB,OAAO,WAAW,WAAW;CAChF,IAAI,eAAe,KAAA,GAAW;EAC7B,IAAI,OAAO,WAAW,UAAU,cAAc,QAAQ,IAAI,WAAW,OAAO,eAAe,MAAM,MAAM;EACvG,MAAM,IAAI,UAAU,sCAAsC;CAC3D;CAEA,IAAI;EACH,OAAO,eAAe,OAAO,WAAW,aAAa;GACpD,cAAc;GACd,YAAY;GACZ,OAAO;GACP,UAAU;EACX,CAAC;CACF,SAAS,OAAO;EACf,MAAM,IAAI,UAAU,2CAA2C,EAAE,MAAM,CAAC;CACzE;AACD;;;;;;;AAQA,MAAa,qBAAqB,SAA8B;CAC/D,yBAAyB;CACzB,OAAO;AACR"}
@@ -1,6 +1,6 @@
1
1
  //#region src/logger/index.d.ts
2
2
  /** 日志严重级别,按从低到高排列。 */
3
- type LogLevel = "debug" | "info" | "warn" | "error";
3
+ type LogLevel = "debug" | "log" | "warn" | "error";
4
4
  /** 日志输出目标需要实现的最小控制台接口。 */
5
5
  interface LoggerSink {
6
6
  /**
@@ -9,7 +9,7 @@ interface LoggerSink {
9
9
  */
10
10
  debug: (...data: unknown[]) => void;
11
11
  /**
12
- * 接收通过级别过滤后的普通信息参数;对应 Logger 的 `info` 级别。
12
+ * 接收通过级别过滤后的普通日志参数;对应 Logger 的 `log` 级别。
13
13
  * @param data - 已格式化的品牌、作用域、消息以及保持原始类型的附加值。
14
14
  */
15
15
  log: (...data: unknown[]) => void;
@@ -26,7 +26,7 @@ interface LoggerSink {
26
26
  }
27
27
  /** {@link createLogger} 的不可变配置。 */
28
28
  interface LoggerOptions {
29
- /** 最低输出级别,默认 `info`;低于该优先级的消息不会传给 Sink。 */
29
+ /** 最低输出级别,默认 `debug`;低于该优先级的消息不会传给 Sink。 */
30
30
  level?: LogLevel;
31
31
  /** 日志品牌前缀,默认 `Fast`;必须是无外围空白的非空字符串。 */
32
32
  prefix?: string;
@@ -35,40 +35,36 @@ interface LoggerOptions {
35
35
  /** uni-app App-Plus/HBuilderX 中把附加参数逐条转成单行文本输出,默认 `false`;其他平台忽略。 */
36
36
  uniAppPlusSplit?: boolean;
37
37
  }
38
- /** 配置隔离、无全局可变状态的轻量日志器。 */
38
+ /** 配置隔离的轻量日志器。 */
39
39
  interface Logger {
40
40
  /**
41
- * 输出指定作用域的调试信息。
41
+ * 输出指定作用域的调试信息或数据。
42
42
  * @param scope - 模块、组件或业务来源名称。
43
- * @param message - 主消息文本。
44
- * @param data - 保持原始类型的附加值。
43
+ * @param content - 可选的消息与附加值;非字符串值保持原始类型。
45
44
  * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。
46
45
  */
47
- debug: (scope: string, message: string, ...data: unknown[]) => void;
46
+ debug: (scope: string, ...content: unknown[]) => void;
48
47
  /**
49
- * 输出指定作用域的普通信息。
48
+ * 输出指定作用域的普通信息或数据。
50
49
  * @param scope - 模块、组件或业务来源名称。
51
- * @param message - 主消息文本。
52
- * @param data - 保持原始类型的附加值。
50
+ * @param content - 可选的消息与附加值;非字符串值保持原始类型。
53
51
  * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。
54
52
  */
55
- info: (scope: string, message: string, ...data: unknown[]) => void;
53
+ log: (scope: string, ...content: unknown[]) => void;
56
54
  /**
57
- * 输出指定作用域的警告信息。
55
+ * 输出指定作用域的警告信息或数据。
58
56
  * @param scope - 模块、组件或业务来源名称。
59
- * @param message - 主消息文本。
60
- * @param data - 保持原始类型的附加值。
57
+ * @param content - 可选的消息与附加值;非字符串值保持原始类型。
61
58
  * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。
62
59
  */
63
- warn: (scope: string, message: string, ...data: unknown[]) => void;
60
+ warn: (scope: string, ...content: unknown[]) => void;
64
61
  /**
65
- * 输出指定作用域的错误信息。
62
+ * 输出指定作用域的错误信息或数据。
66
63
  * @param scope - 模块、组件或业务来源名称。
67
- * @param message - 主消息文本。
68
- * @param data - 保持原始类型的附加值。
64
+ * @param content - 可选的消息与附加值;非字符串值保持原始类型。
69
65
  * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。
70
66
  */
71
- error: (scope: string, message: string, ...data: unknown[]) => void;
67
+ error: (scope: string, ...content: unknown[]) => void;
72
68
  }
73
69
  /**
74
70
  * 创建独立日志器。
@@ -80,8 +76,16 @@ interface Logger {
80
76
  * @throws `RangeError` 当级别未知,或前缀、作用域不是有效的非空字符串。
81
77
  */
82
78
  declare function createLogger(options?: LoggerOptions): Logger;
83
- /** 默认使用 `Fast` 前缀和 `info` 级别的便捷日志器。 */
79
+ /**
80
+ * 替换默认 {@link logger} 的完整配置。
81
+ *
82
+ * @remarks 已创建的独立 Logger 不受影响;默认 Logger 对象引用保持稳定,并立即转发到新配置。
83
+ * 省略选项会恢复 `createLogger()` 的全部默认值。
84
+ * @param options - 默认 Logger 使用的级别、前缀、输出目标和 uni-app App-Plus 拆分选项。
85
+ */
86
+ declare function configureLogger(options?: LoggerOptions): void;
87
+ /** 默认使用 `Fast` 前缀和 `debug` 级别、可通过 {@link configureLogger} 配置的便捷日志器。 */
84
88
  declare const logger: Logger;
85
89
  //#endregion
86
- export { LogLevel, Logger, LoggerOptions, LoggerSink, createLogger, logger };
90
+ export { LogLevel, Logger, LoggerOptions, LoggerSink, configureLogger, createLogger, logger };
87
91
  //# sourceMappingURL=index.d.mts.map
@@ -1,7 +1,7 @@
1
1
  //#region src/logger/index.ts
2
2
  const levelPriority = {
3
3
  debug: 10,
4
- info: 20,
4
+ log: 20,
5
5
  warn: 30,
6
6
  error: 40
7
7
  };
@@ -9,7 +9,7 @@ const levelPriority = {
9
9
  * 判断未知值是否为受支持日志级别。
10
10
  *
11
11
  * @param value - 待检查配置值。
12
- * @returns 值是 `debug`、`info`、`warn` 或 `error` 时返回 `true`。
12
+ * @returns 值是 `debug`、`log`、`warn` 或 `error` 时返回 `true`。
13
13
  */
14
14
  const isLogLevel = (value) => typeof value === "string" && Object.hasOwn(levelPriority, value);
15
15
  /**
@@ -70,7 +70,7 @@ const defaultConsoleSink = {
70
70
  * @throws `RangeError` 当级别未知,或前缀、作用域不是有效的非空字符串。
71
71
  */
72
72
  function createLogger(options = {}) {
73
- const requestedLevel = options.level ?? "info";
73
+ const requestedLevel = options.level ?? "debug";
74
74
  const requestedPrefix = options.prefix ?? "Fast";
75
75
  const sink = options.sink ?? defaultConsoleSink;
76
76
  if (!isLogLevel(requestedLevel)) throw new RangeError(`未知的日志级别:${String(requestedLevel)}。`);
@@ -83,41 +83,70 @@ function createLogger(options = {}) {
83
83
  *
84
84
  * @param messageLevel - 本条消息的严重级别。
85
85
  * @param scope - 模块、组件或业务来源名称。
86
- * @param message - 主消息文本。
87
- * @param data - 保持原始类型的附加值。
86
+ * @param content - 可选的消息与保持原始类型的附加值。
88
87
  * @throws `RangeError` 当作用域不是非空字符串或包含外围空白。
89
88
  */
90
- const write = (messageLevel, scope, message, data) => {
89
+ const write = (messageLevel, scope, content) => {
91
90
  if (typeof scope !== "string") throw new TypeError("日志作用域必须是字符串。");
92
91
  if (scope.length === 0 || scope.trim() !== scope) throw new RangeError("日志作用域必须是无外围空白的非空字符串。");
93
92
  if (levelPriority[messageLevel] < levelPriority[level]) return;
94
93
  const heading = `[${prefix}:${scope}]`;
95
- const sinkMethod = messageLevel === "info" ? "log" : messageLevel;
94
+ const sinkMethod = messageLevel;
96
95
  if (uniAppPlusSplit && isUniAppPlus()) {
97
- sink[sinkMethod](`${heading} ${message}`);
98
- for (const item of data) sink[sinkMethod](formatSplitValue(item));
96
+ const [first, ...remaining] = content;
97
+ if (typeof first === "string") {
98
+ sink[sinkMethod](`${heading} ${first}`);
99
+ for (const item of remaining) sink[sinkMethod](formatSplitValue(item));
100
+ } else {
101
+ sink[sinkMethod](heading);
102
+ for (const item of content) sink[sinkMethod](formatSplitValue(item));
103
+ }
99
104
  return;
100
105
  }
101
- sink[sinkMethod](heading, message, ...data);
106
+ sink[sinkMethod](heading, ...content);
102
107
  };
103
108
  return {
104
- debug: (scope, message, ...data) => {
105
- write("debug", scope, message, data);
109
+ debug: (scope, ...content) => {
110
+ write("debug", scope, content);
106
111
  },
107
- info: (scope, message, ...data) => {
108
- write("info", scope, message, data);
112
+ log: (scope, ...content) => {
113
+ write("log", scope, content);
109
114
  },
110
- warn: (scope, message, ...data) => {
111
- write("warn", scope, message, data);
115
+ warn: (scope, ...content) => {
116
+ write("warn", scope, content);
112
117
  },
113
- error: (scope, message, ...data) => {
114
- write("error", scope, message, data);
118
+ error: (scope, ...content) => {
119
+ write("error", scope, content);
115
120
  }
116
121
  };
117
122
  }
118
- /** 默认使用 `Fast` 前缀和 `info` 级别的便捷日志器。 */
119
- const logger = createLogger();
123
+ let activeDefaultLogger = createLogger();
124
+ /**
125
+ * 替换默认 {@link logger} 的完整配置。
126
+ *
127
+ * @remarks 已创建的独立 Logger 不受影响;默认 Logger 对象引用保持稳定,并立即转发到新配置。
128
+ * 省略选项会恢复 `createLogger()` 的全部默认值。
129
+ * @param options - 默认 Logger 使用的级别、前缀、输出目标和 uni-app App-Plus 拆分选项。
130
+ */
131
+ function configureLogger(options = {}) {
132
+ activeDefaultLogger = createLogger(options);
133
+ }
134
+ /** 默认使用 `Fast` 前缀和 `debug` 级别、可通过 {@link configureLogger} 配置的便捷日志器。 */
135
+ const logger = {
136
+ debug: (scope, ...content) => {
137
+ activeDefaultLogger.debug(scope, ...content);
138
+ },
139
+ log: (scope, ...content) => {
140
+ activeDefaultLogger.log(scope, ...content);
141
+ },
142
+ warn: (scope, ...content) => {
143
+ activeDefaultLogger.warn(scope, ...content);
144
+ },
145
+ error: (scope, ...content) => {
146
+ activeDefaultLogger.error(scope, ...content);
147
+ }
148
+ };
120
149
  //#endregion
121
- export { createLogger, logger };
150
+ export { configureLogger, createLogger, logger };
122
151
 
123
152
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/logger/index.ts"],"sourcesContent":["/** 日志严重级别,按从低到高排列。 */\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\n/** 日志输出目标需要实现的最小控制台接口。 */\nexport interface LoggerSink {\n\t/**\n\t * 接收通过级别过滤后的调试参数。\n\t * @param data - 已格式化的品牌、作用域、消息以及保持原始类型的附加值。\n\t */\n\tdebug: (...data: unknown[]) => void;\n\t/**\n\t * 接收通过级别过滤后的普通信息参数;对应 Logger 的 `info` 级别。\n\t * @param data - 已格式化的品牌、作用域、消息以及保持原始类型的附加值。\n\t */\n\tlog: (...data: unknown[]) => void;\n\t/**\n\t * 接收通过级别过滤后的警告参数。\n\t * @param data - 已格式化的品牌、作用域、消息以及保持原始类型的附加值。\n\t */\n\twarn: (...data: unknown[]) => void;\n\t/**\n\t * 接收通过级别过滤后的错误参数。\n\t * @param data - 已格式化的品牌、作用域、消息以及保持原始类型的附加值。\n\t */\n\terror: (...data: unknown[]) => void;\n}\n\n/** {@link createLogger} 的不可变配置。 */\nexport interface LoggerOptions {\n\t/** 最低输出级别,默认 `info`;低于该优先级的消息不会传给 Sink。 */\n\tlevel?: LogLevel;\n\t/** 日志品牌前缀,默认 `Fast`;必须是无外围空白的非空字符串。 */\n\tprefix?: string;\n\t/** 可注入输出目标,默认当前运行时的 `console`;Logger 不会修改该对象。 */\n\tsink?: LoggerSink;\n\t/** uni-app App-Plus/HBuilderX 中把附加参数逐条转成单行文本输出,默认 `false`;其他平台忽略。 */\n\tuniAppPlusSplit?: boolean;\n}\n\n/** 配置隔离、无全局可变状态的轻量日志器。 */\nexport interface Logger {\n\t/**\n\t * 输出指定作用域的调试信息。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param message - 主消息文本。\n\t * @param data - 保持原始类型的附加值。\n\t * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。\n\t */\n\tdebug: (scope: string, message: string, ...data: unknown[]) => void;\n\t/**\n\t * 输出指定作用域的普通信息。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param message - 主消息文本。\n\t * @param data - 保持原始类型的附加值。\n\t * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。\n\t */\n\tinfo: (scope: string, message: string, ...data: unknown[]) => void;\n\t/**\n\t * 输出指定作用域的警告信息。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param message - 主消息文本。\n\t * @param data - 保持原始类型的附加值。\n\t * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。\n\t */\n\twarn: (scope: string, message: string, ...data: unknown[]) => void;\n\t/**\n\t * 输出指定作用域的错误信息。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param message - 主消息文本。\n\t * @param data - 保持原始类型的附加值。\n\t * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。\n\t */\n\terror: (scope: string, message: string, ...data: unknown[]) => void;\n}\n\nconst levelPriority: Readonly<Record<LogLevel, number>> = {\n\tdebug: 10,\n\tinfo: 20,\n\twarn: 30,\n\terror: 40,\n};\n\n/**\n * 判断未知值是否为受支持日志级别。\n *\n * @param value - 待检查配置值。\n * @returns 值是 `debug`、`info`、`warn` 或 `error` 时返回 `true`。\n */\nconst isLogLevel = (value: unknown): value is LogLevel => typeof value === \"string\" && Object.hasOwn(levelPriority, value);\n\n/**\n * 检测 uni-app App-Plus 日志环境。\n *\n * @returns 全局 `uni` 与 `plus` 同时存在时返回 `true`。\n */\nconst isUniAppPlus = (): boolean => {\n\treturn Reflect.get(globalThis, \"uni\") !== undefined && Reflect.get(globalThis, \"plus\") !== undefined;\n};\n\n/**\n * 把日志附加值转换为适合 HBuilderX 单行输出的文本。\n *\n * @remarks 循环引用会替换为 `[Circular]`,BigInt 保留 `n` 后缀,Error 优先输出堆栈。\n * @param value - 任意日志附加值。\n * @returns 不会因 JSON 序列化失败而中断日志调用的文本。\n */\nconst formatSplitValue = (value: unknown): string => {\n\tif (typeof value === \"string\") return value;\n\tif (typeof value === \"bigint\") return `${value.toString()}n`;\n\tif (value instanceof Error) return value.stack ?? `${value.name}: ${value.message}`;\n\tconst visited = new WeakSet();\n\ttry {\n\t\tconst serialized: unknown = JSON.stringify(\n\t\t\tvalue,\n\t\t\t(_key, item: unknown): unknown => {\n\t\t\t\tif (typeof item === \"bigint\") return `${item.toString()}n`;\n\t\t\t\tif (typeof item !== \"object\" || item === null) return item;\n\t\t\t\tif (visited.has(item)) return \"[Circular]\";\n\t\t\t\tvisited.add(item);\n\t\t\t\treturn item;\n\t\t\t},\n\t\t\t2\n\t\t);\n\t\treturn typeof serialized === \"string\" ? serialized : String(value);\n\t} catch {\n\t\treturn String(value);\n\t}\n};\n\nconst defaultConsoleSink: LoggerSink = {\n\tdebug: (...data): void => {\n\t\tif (typeof console.debug === \"function\") console.debug(...data);\n\t\telse console.log(...data);\n\t},\n\tlog: (...data): void => {\n\t\tconsole.log(...data);\n\t},\n\twarn: (...data): void => {\n\t\tconsole.warn(...data);\n\t},\n\terror: (...data): void => {\n\t\tconsole.error(...data);\n\t},\n};\n\n/**\n * 创建独立日志器。\n *\n * @remarks 本库其他模块不会自动记录、吞掉或转换异常。日志内容可能进入持久化平台,\n * 调用方不得传入密码、令牌、密钥或完整个人数据。\n * @param options - 级别、前缀、输出目标和 uni-app App-Plus 拆分选项。\n * @returns 不会修改全局控制台或其他日志器配置的新实例。\n * @throws `RangeError` 当级别未知,或前缀、作用域不是有效的非空字符串。\n */\nexport function createLogger(options: LoggerOptions = {}): Logger {\n\tconst requestedLevel: unknown = options.level ?? \"info\";\n\tconst requestedPrefix: unknown = options.prefix ?? \"Fast\";\n\tconst sink = options.sink ?? defaultConsoleSink;\n\tif (!isLogLevel(requestedLevel)) throw new RangeError(`未知的日志级别:${String(requestedLevel)}。`);\n\tif (typeof requestedPrefix !== \"string\" || requestedPrefix.length === 0) {\n\t\tthrow new RangeError(\"日志前缀必须是非空字符串。\");\n\t}\n\tconst level = requestedLevel;\n\tconst prefix = requestedPrefix;\n\tconst uniAppPlusSplit = options.uniAppPlusSplit ?? false;\n\n\t/**\n\t * 应用级别过滤、标题格式和平台输出策略。\n\t *\n\t * @param messageLevel - 本条消息的严重级别。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param message - 主消息文本。\n\t * @param data - 保持原始类型的附加值。\n\t * @throws `RangeError` 当作用域不是非空字符串或包含外围空白。\n\t */\n\tconst write = (messageLevel: LogLevel, scope: string, message: string, data: readonly unknown[]): void => {\n\t\tif (typeof scope !== \"string\") throw new TypeError(\"日志作用域必须是字符串。\");\n\t\tif (scope.length === 0 || scope.trim() !== scope) {\n\t\t\tthrow new RangeError(\"日志作用域必须是无外围空白的非空字符串。\");\n\t\t}\n\t\tif (levelPriority[messageLevel] < levelPriority[level]) return;\n\t\tconst heading = `[${prefix}:${scope}]`;\n\t\tconst sinkMethod: keyof LoggerSink = messageLevel === \"info\" ? \"log\" : messageLevel;\n\t\tif (uniAppPlusSplit && isUniAppPlus()) {\n\t\t\tsink[sinkMethod](`${heading} ${message}`);\n\t\t\tfor (const item of data) sink[sinkMethod](formatSplitValue(item));\n\t\t\treturn;\n\t\t}\n\t\tsink[sinkMethod](heading, message, ...data);\n\t};\n\n\treturn {\n\t\tdebug: (scope, message, ...data): void => {\n\t\t\twrite(\"debug\", scope, message, data);\n\t\t},\n\t\tinfo: (scope, message, ...data): void => {\n\t\t\twrite(\"info\", scope, message, data);\n\t\t},\n\t\twarn: (scope, message, ...data): void => {\n\t\t\twrite(\"warn\", scope, message, data);\n\t\t},\n\t\terror: (scope, message, ...data): void => {\n\t\t\twrite(\"error\", scope, message, data);\n\t\t},\n\t};\n}\n\n/** 默认使用 `Fast` 前缀和 `info` 级别的便捷日志器。 */\nexport const logger: Logger = createLogger();\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/logger/index.ts"],"sourcesContent":["/** 日志严重级别,按从低到高排列。 */\nexport type LogLevel = \"debug\" | \"log\" | \"warn\" | \"error\";\n\n/** 日志输出目标需要实现的最小控制台接口。 */\nexport interface LoggerSink {\n\t/**\n\t * 接收通过级别过滤后的调试参数。\n\t * @param data - 已格式化的品牌、作用域、消息以及保持原始类型的附加值。\n\t */\n\tdebug: (...data: unknown[]) => void;\n\t/**\n\t * 接收通过级别过滤后的普通日志参数;对应 Logger 的 `log` 级别。\n\t * @param data - 已格式化的品牌、作用域、消息以及保持原始类型的附加值。\n\t */\n\tlog: (...data: unknown[]) => void;\n\t/**\n\t * 接收通过级别过滤后的警告参数。\n\t * @param data - 已格式化的品牌、作用域、消息以及保持原始类型的附加值。\n\t */\n\twarn: (...data: unknown[]) => void;\n\t/**\n\t * 接收通过级别过滤后的错误参数。\n\t * @param data - 已格式化的品牌、作用域、消息以及保持原始类型的附加值。\n\t */\n\terror: (...data: unknown[]) => void;\n}\n\n/** {@link createLogger} 的不可变配置。 */\nexport interface LoggerOptions {\n\t/** 最低输出级别,默认 `debug`;低于该优先级的消息不会传给 Sink。 */\n\tlevel?: LogLevel;\n\t/** 日志品牌前缀,默认 `Fast`;必须是无外围空白的非空字符串。 */\n\tprefix?: string;\n\t/** 可注入输出目标,默认当前运行时的 `console`;Logger 不会修改该对象。 */\n\tsink?: LoggerSink;\n\t/** uni-app App-Plus/HBuilderX 中把附加参数逐条转成单行文本输出,默认 `false`;其他平台忽略。 */\n\tuniAppPlusSplit?: boolean;\n}\n\n/** 配置隔离的轻量日志器。 */\nexport interface Logger {\n\t/**\n\t * 输出指定作用域的调试信息或数据。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param content - 可选的消息与附加值;非字符串值保持原始类型。\n\t * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。\n\t */\n\tdebug: (scope: string, ...content: unknown[]) => void;\n\t/**\n\t * 输出指定作用域的普通信息或数据。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param content - 可选的消息与附加值;非字符串值保持原始类型。\n\t * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。\n\t */\n\tlog: (scope: string, ...content: unknown[]) => void;\n\t/**\n\t * 输出指定作用域的警告信息或数据。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param content - 可选的消息与附加值;非字符串值保持原始类型。\n\t * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。\n\t */\n\twarn: (scope: string, ...content: unknown[]) => void;\n\t/**\n\t * 输出指定作用域的错误信息或数据。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param content - 可选的消息与附加值;非字符串值保持原始类型。\n\t * @throws `TypeError` 或 `RangeError` 当作用域不是无外围空白的非空字符串。\n\t */\n\terror: (scope: string, ...content: unknown[]) => void;\n}\n\nconst levelPriority: Readonly<Record<LogLevel, number>> = {\n\tdebug: 10,\n\tlog: 20,\n\twarn: 30,\n\terror: 40,\n};\n\n/**\n * 判断未知值是否为受支持日志级别。\n *\n * @param value - 待检查配置值。\n * @returns 值是 `debug`、`log`、`warn` 或 `error` 时返回 `true`。\n */\nconst isLogLevel = (value: unknown): value is LogLevel => typeof value === \"string\" && Object.hasOwn(levelPriority, value);\n\n/**\n * 检测 uni-app App-Plus 日志环境。\n *\n * @returns 全局 `uni` 与 `plus` 同时存在时返回 `true`。\n */\nconst isUniAppPlus = (): boolean => {\n\treturn Reflect.get(globalThis, \"uni\") !== undefined && Reflect.get(globalThis, \"plus\") !== undefined;\n};\n\n/**\n * 把日志附加值转换为适合 HBuilderX 单行输出的文本。\n *\n * @remarks 循环引用会替换为 `[Circular]`,BigInt 保留 `n` 后缀,Error 优先输出堆栈。\n * @param value - 任意日志附加值。\n * @returns 不会因 JSON 序列化失败而中断日志调用的文本。\n */\nconst formatSplitValue = (value: unknown): string => {\n\tif (typeof value === \"string\") return value;\n\tif (typeof value === \"bigint\") return `${value.toString()}n`;\n\tif (value instanceof Error) return value.stack ?? `${value.name}: ${value.message}`;\n\tconst visited = new WeakSet();\n\ttry {\n\t\tconst serialized: unknown = JSON.stringify(\n\t\t\tvalue,\n\t\t\t(_key, item: unknown): unknown => {\n\t\t\t\tif (typeof item === \"bigint\") return `${item.toString()}n`;\n\t\t\t\tif (typeof item !== \"object\" || item === null) return item;\n\t\t\t\tif (visited.has(item)) return \"[Circular]\";\n\t\t\t\tvisited.add(item);\n\t\t\t\treturn item;\n\t\t\t},\n\t\t\t2\n\t\t);\n\t\treturn typeof serialized === \"string\" ? serialized : String(value);\n\t} catch {\n\t\treturn String(value);\n\t}\n};\n\nconst defaultConsoleSink: LoggerSink = {\n\tdebug: (...data): void => {\n\t\tif (typeof console.debug === \"function\") console.debug(...data);\n\t\telse console.log(...data);\n\t},\n\tlog: (...data): void => {\n\t\tconsole.log(...data);\n\t},\n\twarn: (...data): void => {\n\t\tconsole.warn(...data);\n\t},\n\terror: (...data): void => {\n\t\tconsole.error(...data);\n\t},\n};\n\n/**\n * 创建独立日志器。\n *\n * @remarks 本库其他模块不会自动记录、吞掉或转换异常。日志内容可能进入持久化平台,\n * 调用方不得传入密码、令牌、密钥或完整个人数据。\n * @param options - 级别、前缀、输出目标和 uni-app App-Plus 拆分选项。\n * @returns 不会修改全局控制台或其他日志器配置的新实例。\n * @throws `RangeError` 当级别未知,或前缀、作用域不是有效的非空字符串。\n */\nexport function createLogger(options: LoggerOptions = {}): Logger {\n\tconst requestedLevel: unknown = options.level ?? \"debug\";\n\tconst requestedPrefix: unknown = options.prefix ?? \"Fast\";\n\tconst sink = options.sink ?? defaultConsoleSink;\n\tif (!isLogLevel(requestedLevel)) throw new RangeError(`未知的日志级别:${String(requestedLevel)}。`);\n\tif (typeof requestedPrefix !== \"string\" || requestedPrefix.length === 0) {\n\t\tthrow new RangeError(\"日志前缀必须是非空字符串。\");\n\t}\n\tconst level = requestedLevel;\n\tconst prefix = requestedPrefix;\n\tconst uniAppPlusSplit = options.uniAppPlusSplit ?? false;\n\n\t/**\n\t * 应用级别过滤、标题格式和平台输出策略。\n\t *\n\t * @param messageLevel - 本条消息的严重级别。\n\t * @param scope - 模块、组件或业务来源名称。\n\t * @param content - 可选的消息与保持原始类型的附加值。\n\t * @throws `RangeError` 当作用域不是非空字符串或包含外围空白。\n\t */\n\tconst write = (messageLevel: LogLevel, scope: string, content: readonly unknown[]): void => {\n\t\tif (typeof scope !== \"string\") throw new TypeError(\"日志作用域必须是字符串。\");\n\t\tif (scope.length === 0 || scope.trim() !== scope) {\n\t\t\tthrow new RangeError(\"日志作用域必须是无外围空白的非空字符串。\");\n\t\t}\n\t\tif (levelPriority[messageLevel] < levelPriority[level]) return;\n\t\tconst heading = `[${prefix}:${scope}]`;\n\t\tconst sinkMethod: keyof LoggerSink = messageLevel;\n\t\tif (uniAppPlusSplit && isUniAppPlus()) {\n\t\t\tconst [first, ...remaining] = content;\n\t\t\tif (typeof first === \"string\") {\n\t\t\t\tsink[sinkMethod](`${heading} ${first}`);\n\t\t\t\tfor (const item of remaining) sink[sinkMethod](formatSplitValue(item));\n\t\t\t} else {\n\t\t\t\tsink[sinkMethod](heading);\n\t\t\t\tfor (const item of content) sink[sinkMethod](formatSplitValue(item));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tsink[sinkMethod](heading, ...content);\n\t};\n\n\treturn {\n\t\tdebug: (scope, ...content): void => {\n\t\t\twrite(\"debug\", scope, content);\n\t\t},\n\t\tlog: (scope, ...content): void => {\n\t\t\twrite(\"log\", scope, content);\n\t\t},\n\t\twarn: (scope, ...content): void => {\n\t\t\twrite(\"warn\", scope, content);\n\t\t},\n\t\terror: (scope, ...content): void => {\n\t\t\twrite(\"error\", scope, content);\n\t\t},\n\t};\n}\n\nlet activeDefaultLogger: Logger = createLogger();\n\n/**\n * 替换默认 {@link logger} 的完整配置。\n *\n * @remarks 已创建的独立 Logger 不受影响;默认 Logger 对象引用保持稳定,并立即转发到新配置。\n * 省略选项会恢复 `createLogger()` 的全部默认值。\n * @param options - 默认 Logger 使用的级别、前缀、输出目标和 uni-app App-Plus 拆分选项。\n */\nexport function configureLogger(options: LoggerOptions = {}): void {\n\tactiveDefaultLogger = createLogger(options);\n}\n\n/** 默认使用 `Fast` 前缀和 `debug` 级别、可通过 {@link configureLogger} 配置的便捷日志器。 */\nexport const logger: Logger = {\n\tdebug: (scope, ...content): void => {\n\t\tactiveDefaultLogger.debug(scope, ...content);\n\t},\n\tlog: (scope, ...content): void => {\n\t\tactiveDefaultLogger.log(scope, ...content);\n\t},\n\twarn: (scope, ...content): void => {\n\t\tactiveDefaultLogger.warn(scope, ...content);\n\t},\n\terror: (scope, ...content): void => {\n\t\tactiveDefaultLogger.error(scope, ...content);\n\t},\n};\n"],"mappings":"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"}
@@ -27,8 +27,16 @@ interface StorageConfiguration {
27
27
  /** 所有物理键使用的非空命名空间前缀; */
28
28
  prefix?: string;
29
29
  }
30
+ /** 单次 Storage 读取配置。 */
31
+ interface StorageReadOptions {
32
+ /**
33
+ * 仅覆盖本次读取使用的 Codec;`true` 使用 Base64 混淆,`false` 使用 JSON,省略时使用全局配置。
34
+ * 必须与写入该条目时使用的单次设置一致。
35
+ */
36
+ crypto?: boolean;
37
+ }
30
38
  /** 单次 Storage 写入配置。 */
31
- interface StorageWriteOptions {
39
+ interface StorageWriteOptions extends StorageReadOptions {
32
40
  /** 从写入时刻开始的有效毫秒数;必须是大于 0 的有限数,省略时永久有效。 */
33
41
  ttlMs?: number;
34
42
  }
@@ -44,10 +52,11 @@ interface StorageArea {
44
52
  /**
45
53
  * 获取并解码业务值;已过期记录会在读取时删除。
46
54
  * @param key - 不含全局前缀的非空业务键。
47
- * @returns 解码后的值;键缺失或过期时返回 `undefined`。
55
+ * @param options - 可选的单次 Base64 混淆开关;必须与写入时一致。
56
+ * @returns 解码后的值;未传泛型时静态类型默认为 `string`,键缺失或过期时返回 `undefined`。
48
57
  * @throws 当键非法、包络损坏、Codec 解码失败或后端不可用时抛出错误。
49
58
  */
50
- get: <Value = unknown>(key: string) => Value | undefined;
59
+ get: <Value = string>(key: string, options?: StorageReadOptions) => Value | undefined;
51
60
  /**
52
61
  * 判断一个可成功读取且未过期的业务键是否存在。
53
62
  * @param key - 不含全局前缀的非空业务键。
@@ -79,7 +88,7 @@ interface StorageArea {
79
88
  * 编码并写入业务值,可附加惰性清理的 TTL。
80
89
  * @param key - 不含全局前缀的非空业务键。
81
90
  * @param value - 必须受当前 Codec 支持的业务值。
82
- * @param options - 可选的单次写入 TTL
91
+ * @param options - 可选的单次写入 TTL 与 Base64 混淆开关。
83
92
  * @throws 当键、TTL、业务值或后端写入无效时抛出错误。
84
93
  */
85
94
  set: <Value>(key: string, value: Value, options?: StorageWriteOptions) => void;
@@ -103,5 +112,5 @@ declare function configureStorage(options?: StorageConfiguration): void;
103
112
  /** 返回全局 Storage 是否已经由应用入口配置。 */
104
113
  declare function isStorageConfigured(): boolean;
105
114
  //#endregion
106
- export { Local, Session, StorageArea, StorageCodec, StorageConfiguration, StorageWriteOptions, base64StorageCodec, configureStorage, isStorageConfigured };
115
+ export { Local, Session, StorageArea, StorageCodec, StorageConfiguration, StorageReadOptions, StorageWriteOptions, base64StorageCodec, configureStorage, isStorageConfigured };
107
116
  //# sourceMappingURL=index.d.mts.map
@@ -25,7 +25,7 @@ const jsonCodec = {
25
25
  };
26
26
  /** Base64 混淆 Codec;只隐藏明文外观,不提供加密、完整性或认证。 */
27
27
  const base64StorageCodec = {
28
- decode: (value) => JSON.parse(decodeSecureBase64(value)),
28
+ decode: (value) => decodeSecureBase64(value).parseJson(),
29
29
  encode: (value) => {
30
30
  const encoded = JSON.stringify(value);
31
31
  if (typeof encoded !== "string") throw new TypeError("存储值无法序列化为 JSON。");
@@ -140,6 +140,18 @@ const createStorageArea = (backendFactory, prefix, codec, now) => {
140
140
  */
141
141
  const toStorageKey = (key) => `${prefix}${key}`;
142
142
  /**
143
+ * 解析本次读写实际使用的 Codec。
144
+ *
145
+ * @param crypto - 单次 Base64 混淆开关;省略时沿用 Area 全局 Codec。
146
+ * @returns 本次操作使用的全局、JSON 或 Base64 Codec。
147
+ * @throws `TypeError` 当 JavaScript 调用方传入非布尔值。
148
+ */
149
+ const resolveOperationCodec = (crypto) => {
150
+ if (crypto === void 0) return codec;
151
+ if (typeof crypto !== "boolean") throw new TypeError("Storage 单次 `crypto` 选项必须是布尔值。");
152
+ return crypto ? base64StorageCodec : jsonCodec;
153
+ };
154
+ /**
143
155
  * 枚举当前命名空间中的业务键。
144
156
  *
145
157
  * @param backend - 本次操作使用的后端。
@@ -178,11 +190,11 @@ const createStorageArea = (backendFactory, prefix, codec, now) => {
178
190
  const backend = backendFactory();
179
191
  for (const key of listBusinessKeys(backend)) backend.removeItem(toStorageKey(key));
180
192
  },
181
- get(key) {
193
+ get(key, options = {}) {
182
194
  const envelope = readStoredEnvelope(backendFactory(), key);
183
195
  if (envelope === void 0) return void 0;
184
196
  try {
185
- return codec.decode(envelope.data);
197
+ return resolveOperationCodec(options.crypto).decode(envelope.data);
186
198
  } catch (cause) {
187
199
  throw new TypeError(`无法解码 Storage 条目“${toStorageKey(key)}”。`, { cause });
188
200
  }
@@ -219,7 +231,7 @@ const createStorageArea = (backendFactory, prefix, codec, now) => {
219
231
  }
220
232
  let data;
221
233
  try {
222
- data = codec.encode(value);
234
+ data = resolveOperationCodec(options.crypto).encode(value);
223
235
  if (typeof data !== "string") throw new TypeError("Storage Codec 必须返回字符串。");
224
236
  } catch (cause) {
225
237
  throw new TypeError("无法编码存储值。", { cause });
@@ -268,7 +280,7 @@ const createStorageAreaProxy = (select, name) => {
268
280
  clear: () => {
269
281
  getArea().clear();
270
282
  },
271
- get: (key) => getArea().get(key),
283
+ get: (key, options) => getArea().get(key, options),
272
284
  has: (key) => getArea().has(key),
273
285
  keys: () => getArea().keys(),
274
286
  pruneExpired: () => getArea().pruneExpired(),