@fast-china/utils 1.0.37 → 2.0.0
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 +21 -0
- package/CONTRIBUTING.md +87 -0
- package/README.md +111 -50
- package/README.zh.md +111 -50
- package/SECURITY.md +46 -0
- package/dist/array/index.d.mts +87 -0
- package/dist/array/index.d.mts.map +1 -0
- package/dist/array/index.mjs +161 -0
- package/dist/array/index.mjs.map +1 -0
- package/dist/async/index.d.mts +146 -0
- package/dist/async/index.d.mts.map +1 -0
- package/dist/async/index.mjs +336 -0
- package/dist/async/index.mjs.map +1 -0
- package/dist/base64/index.d.mts +105 -0
- package/dist/base64/index.d.mts.map +1 -0
- package/dist/base64/index.mjs +427 -0
- package/dist/base64/index.mjs.map +1 -0
- package/dist/color/index.d.mts +90 -0
- package/dist/color/index.d.mts.map +1 -0
- package/dist/color/index.mjs +178 -0
- package/dist/color/index.mjs.map +1 -0
- package/dist/crypto/index.d.mts +157 -0
- package/dist/crypto/index.d.mts.map +1 -0
- package/dist/crypto/index.mjs +541 -0
- package/dist/crypto/index.mjs.map +1 -0
- package/dist/date/index.d.mts +191 -0
- package/dist/date/index.d.mts.map +1 -0
- package/dist/date/index.mjs +383 -0
- package/dist/date/index.mjs.map +1 -0
- package/dist/dom/style.d.mts +30 -0
- package/dist/dom/style.d.mts.map +1 -0
- package/dist/dom/style.mjs +74 -0
- package/dist/dom/style.mjs.map +1 -0
- package/dist/env/index.d.mts +63 -0
- package/dist/env/index.d.mts.map +1 -0
- package/dist/env/index.mjs +97 -0
- package/dist/env/index.mjs.map +1 -0
- package/dist/identity/index.d.mts +78 -0
- package/dist/identity/index.d.mts.map +1 -0
- package/dist/identity/index.mjs +87 -0
- package/dist/identity/index.mjs.map +1 -0
- package/dist/index.d.mts +24 -0
- package/dist/index.mjs +23 -0
- package/dist/internal/text.mjs +36 -0
- package/dist/internal/text.mjs.map +1 -0
- package/dist/logger/index.d.mts +87 -0
- package/dist/logger/index.d.mts.map +1 -0
- package/dist/logger/index.mjs +124 -0
- package/dist/logger/index.mjs.map +1 -0
- package/dist/number/index.d.mts +89 -0
- package/dist/number/index.d.mts.map +1 -0
- package/dist/number/index.mjs +215 -0
- package/dist/number/index.mjs.map +1 -0
- package/dist/object/index.d.mts +76 -0
- package/dist/object/index.d.mts.map +1 -0
- package/dist/object/index.mjs +134 -0
- package/dist/object/index.mjs.map +1 -0
- package/dist/storage/index.d.mts +104 -0
- package/dist/storage/index.d.mts.map +1 -0
- package/dist/storage/index.mjs +324 -0
- package/dist/storage/index.mjs.map +1 -0
- package/dist/string/index.d.mts +130 -0
- package/dist/string/index.d.mts.map +1 -0
- package/dist/string/index.mjs +275 -0
- package/dist/string/index.mjs.map +1 -0
- package/dist/vue/emits.d.mts +24 -0
- package/dist/vue/emits.d.mts.map +1 -0
- package/dist/vue/emits.mjs +47 -0
- package/dist/vue/emits.mjs.map +1 -0
- package/dist/vue/expose.d.mts +12 -0
- package/dist/vue/expose.d.mts.map +1 -0
- package/dist/vue/expose.mjs +16 -0
- package/dist/vue/expose.mjs.map +1 -0
- package/dist/vue/func.d.mts +14 -0
- package/dist/vue/func.d.mts.map +1 -0
- package/dist/vue/func.mjs +16 -0
- package/dist/vue/func.mjs.map +1 -0
- package/dist/vue/index.d.mts +9 -0
- package/dist/vue/install.d.mts +69 -0
- package/dist/vue/install.d.mts.map +1 -0
- package/dist/vue/install.mjs +123 -0
- package/dist/vue/install.mjs.map +1 -0
- package/dist/vue/props.d.mts +23 -0
- package/dist/vue/props.d.mts.map +1 -0
- package/dist/vue/props.mjs +41 -0
- package/dist/vue/props.mjs.map +1 -0
- package/dist/vue/render.d.mts +13 -0
- package/dist/vue/render.d.mts.map +1 -0
- package/dist/vue/render.mjs +23 -0
- package/dist/vue/render.mjs.map +1 -0
- package/dist/vue/slots.d.mts +19 -0
- package/dist/vue/slots.d.mts.map +1 -0
- package/dist/vue/slots.mjs +13 -0
- package/dist/vue/slots.mjs.map +1 -0
- package/dist/vue/with.d.mts +12 -0
- package/dist/vue/with.d.mts.map +1 -0
- package/dist/vue/with.mjs +15 -0
- package/dist/vue/with.mjs.map +1 -0
- package/docs/API.md +96 -0
- package/docs/API.zh-CN.md +96 -0
- package/docs/DEVELOPMENT_RELEASE.zh-CN.md +65 -0
- package/docs/RUNTIME_CONTRACT.md +37 -0
- package/package.json +65 -73
- package/src/array/index.ts +173 -0
- package/src/async/index.ts +475 -0
- package/src/base64/index.ts +374 -0
- package/src/color/index.ts +208 -0
- package/src/crypto/index.ts +670 -0
- package/src/date/index.ts +451 -0
- package/src/dom/index.ts +6 -0
- package/src/dom/style.ts +92 -0
- package/src/env/index.ts +169 -0
- package/src/identity/index.ts +144 -0
- package/src/index.ts +20 -0
- package/src/internal/text.ts +46 -0
- package/src/logger/index.ts +219 -0
- package/src/number/index.ts +235 -0
- package/src/object/index.ts +160 -0
- package/src/storage/index.ts +524 -0
- package/src/string/index.ts +328 -0
- package/src/vue/emits.ts +71 -0
- package/src/vue/expose.ts +11 -0
- package/src/vue/func.ts +17 -0
- package/src/vue/index.ts +13 -0
- package/src/vue/install.ts +185 -0
- package/src/vue/props.ts +39 -0
- package/src/vue/render.ts +41 -0
- package/src/vue/slots.ts +23 -0
- package/src/vue/with.ts +10 -0
- package/Fast.png +0 -0
- package/dist/index.global.js +0 -14500
- package/dist/index.global.js.map +0 -1
- package/dist/index.global.min.js +0 -2
- package/dist/index.global.min.js.map +0 -1
- package/es/array/index.d.ts +0 -19
- package/es/array/index.mjs +0 -2
- package/es/array/index.mjs.map +0 -1
- package/es/base64/index.d.ts +0 -21
- package/es/base64/index.mjs +0 -2
- package/es/base64/index.mjs.map +0 -1
- package/es/click/index.d.ts +0 -33
- package/es/click/index.mjs +0 -2
- package/es/click/index.mjs.map +0 -1
- package/es/color/index.d.ts +0 -33
- package/es/color/index.mjs +0 -2
- package/es/color/index.mjs.map +0 -1
- package/es/console/index.d.ts +0 -31
- package/es/console/index.mjs +0 -2
- package/es/console/index.mjs.map +0 -1
- package/es/crypto/index.d.ts +0 -46
- package/es/crypto/index.mjs +0 -2
- package/es/crypto/index.mjs.map +0 -1
- package/es/date/index.d.ts +0 -44
- package/es/date/index.mjs +0 -2
- package/es/date/index.mjs.map +0 -1
- package/es/dom/index.d.ts +0 -1
- package/es/dom/index.mjs +0 -2
- package/es/dom/index.mjs.map +0 -1
- package/es/dom/style.d.ts +0 -12
- package/es/dom/style.mjs +0 -2
- package/es/dom/style.mjs.map +0 -1
- package/es/env/index.d.ts +0 -25
- package/es/env/index.mjs +0 -2
- package/es/env/index.mjs.map +0 -1
- package/es/error/index.d.ts +0 -3
- package/es/error/index.mjs +0 -2
- package/es/error/index.mjs.map +0 -1
- package/es/identity/index.d.ts +0 -14
- package/es/identity/index.mjs +0 -2
- package/es/identity/index.mjs.map +0 -1
- package/es/index.d.ts +0 -15
- package/es/index.es.d.ts +0 -2
- package/es/index.mjs +0 -2
- package/es/index.mjs.map +0 -1
- package/es/object/index.d.ts +0 -13
- package/es/object/index.mjs +0 -2
- package/es/object/index.mjs.map +0 -1
- package/es/storage/index.d.ts +0 -96
- package/es/storage/index.mjs +0 -2
- package/es/storage/index.mjs.map +0 -1
- package/es/string/index.d.ts +0 -114
- package/es/string/index.mjs +0 -2
- package/es/string/index.mjs.map +0 -1
- package/es/vue/emits.d.ts +0 -8
- package/es/vue/emits.mjs +0 -2
- package/es/vue/emits.mjs.map +0 -1
- package/es/vue/expose.d.ts +0 -4
- package/es/vue/expose.mjs +0 -2
- package/es/vue/expose.mjs.map +0 -1
- package/es/vue/func.d.ts +0 -6
- package/es/vue/func.mjs +0 -2
- package/es/vue/func.mjs.map +0 -1
- package/es/vue/index.d.ts +0 -8
- package/es/vue/index.mjs +0 -2
- package/es/vue/index.mjs.map +0 -1
- package/es/vue/install.d.ts +0 -5
- package/es/vue/install.mjs +0 -2
- package/es/vue/install.mjs.map +0 -1
- package/es/vue/props.d.ts +0 -9
- package/es/vue/props.mjs +0 -2
- package/es/vue/props.mjs.map +0 -1
- package/es/vue/slots.d.ts +0 -11
- package/es/vue/slots.mjs +0 -2
- package/es/vue/slots.mjs.map +0 -1
- package/es/vue/useRender.d.ts +0 -6
- package/es/vue/useRender.mjs +0 -2
- package/es/vue/useRender.mjs.map +0 -1
- package/es/vue/with.d.ts +0 -5
- package/es/vue/with.mjs +0 -2
- package/es/vue/with.mjs.map +0 -1
- package/lib/array/index.d.ts +0 -19
- package/lib/array/index.js +0 -2
- package/lib/array/index.js.map +0 -1
- package/lib/base64/index.d.ts +0 -21
- package/lib/base64/index.js +0 -2
- package/lib/base64/index.js.map +0 -1
- package/lib/click/index.d.ts +0 -33
- package/lib/click/index.js +0 -2
- package/lib/click/index.js.map +0 -1
- package/lib/color/index.d.ts +0 -33
- package/lib/color/index.js +0 -2
- package/lib/color/index.js.map +0 -1
- package/lib/console/index.d.ts +0 -31
- package/lib/console/index.js +0 -2
- package/lib/console/index.js.map +0 -1
- package/lib/crypto/index.d.ts +0 -46
- package/lib/crypto/index.js +0 -2
- package/lib/crypto/index.js.map +0 -1
- package/lib/date/index.d.ts +0 -44
- package/lib/date/index.js +0 -2
- package/lib/date/index.js.map +0 -1
- package/lib/dom/index.d.ts +0 -1
- package/lib/dom/index.js +0 -2
- package/lib/dom/index.js.map +0 -1
- package/lib/dom/style.d.ts +0 -12
- package/lib/dom/style.js +0 -2
- package/lib/dom/style.js.map +0 -1
- package/lib/env/index.d.ts +0 -25
- package/lib/env/index.js +0 -2
- package/lib/env/index.js.map +0 -1
- package/lib/error/index.d.ts +0 -3
- package/lib/error/index.js +0 -2
- package/lib/error/index.js.map +0 -1
- package/lib/identity/index.d.ts +0 -14
- package/lib/identity/index.js +0 -2
- package/lib/identity/index.js.map +0 -1
- package/lib/index.d.ts +0 -15
- package/lib/index.es.d.ts +0 -2
- package/lib/index.js +0 -2
- package/lib/index.js.map +0 -1
- package/lib/object/index.d.ts +0 -13
- package/lib/object/index.js +0 -2
- package/lib/object/index.js.map +0 -1
- package/lib/storage/index.d.ts +0 -96
- package/lib/storage/index.js +0 -2
- package/lib/storage/index.js.map +0 -1
- package/lib/string/index.d.ts +0 -114
- package/lib/string/index.js +0 -2
- package/lib/string/index.js.map +0 -1
- package/lib/vue/emits.d.ts +0 -8
- package/lib/vue/emits.js +0 -2
- package/lib/vue/emits.js.map +0 -1
- package/lib/vue/expose.d.ts +0 -4
- package/lib/vue/expose.js +0 -2
- package/lib/vue/expose.js.map +0 -1
- package/lib/vue/func.d.ts +0 -6
- package/lib/vue/func.js +0 -2
- package/lib/vue/func.js.map +0 -1
- package/lib/vue/index.d.ts +0 -8
- package/lib/vue/index.js +0 -2
- package/lib/vue/index.js.map +0 -1
- package/lib/vue/install.d.ts +0 -5
- package/lib/vue/install.js +0 -2
- package/lib/vue/install.js.map +0 -1
- package/lib/vue/props.d.ts +0 -9
- package/lib/vue/props.js +0 -2
- package/lib/vue/props.js.map +0 -1
- package/lib/vue/slots.d.ts +0 -11
- package/lib/vue/slots.js +0 -2
- package/lib/vue/slots.js.map +0 -1
- package/lib/vue/useRender.d.ts +0 -6
- package/lib/vue/useRender.js +0 -2
- package/lib/vue/useRender.js.map +0 -1
- package/lib/vue/with.d.ts +0 -5
- package/lib/vue/with.js +0 -2
- package/lib/vue/with.js.map +0 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//#region src/string/index.d.ts
|
|
2
|
+
/** 查询字符串解析结果;重复键保留为数组,不存在的键读取为 `undefined`。 */
|
|
3
|
+
type ParsedQueryParameters = Record<string, string | string[] | undefined>;
|
|
4
|
+
/** 大小写与字素分割可接受的显式语言;省略时固定使用 `en-US` 以保持输出稳定。 */
|
|
5
|
+
type StringLocale = string | readonly string[] | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* 重复执行 URI 组件解码,直到值稳定或达到深度上限。
|
|
8
|
+
*
|
|
9
|
+
* @param value - 不包含 URI 路径语义的编码组件。
|
|
10
|
+
* @param maxDepth - 最大解码次数,默认 `10`。
|
|
11
|
+
* @returns 解码稳定或达到上限后的组件文本。
|
|
12
|
+
* @throws `URIError` 当任一层包含非法百分号序列;深度非法时抛出 `RangeError`。
|
|
13
|
+
*/
|
|
14
|
+
declare function decodeURIComponentRepeatedly(value: string, maxDepth?: number): string;
|
|
15
|
+
/**
|
|
16
|
+
* 解析带 `://` 的绝对 URL、`?query` 或纯查询字符串。
|
|
17
|
+
*
|
|
18
|
+
* @remarks 纯查询字符串值中的未编码 `?` 会作为值内容保留;片段标识及其后内容被忽略。
|
|
19
|
+
* @param input - 完整 URL、带前导问号或不带前导问号的查询文本。
|
|
20
|
+
* @returns 重复键对应字符串数组,空值保留为空字符串。
|
|
21
|
+
*/
|
|
22
|
+
declare function parseQueryString(input: string): ParsedQueryParameters;
|
|
23
|
+
/**
|
|
24
|
+
* 判断文本是否为任意合法 JSON 值,包括标量与 `null`。
|
|
25
|
+
*
|
|
26
|
+
* @param value - 待解析文本;纯空白不视为 JSON。
|
|
27
|
+
* @returns `JSON.parse` 能完整解析时返回 `true`。
|
|
28
|
+
*/
|
|
29
|
+
declare function isValidJson(value: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* 按大小写边界、连字符、下划线与空白切分单词。
|
|
32
|
+
*
|
|
33
|
+
* @example `XMLHttp_request` 返回 `["XML", "Http", "request"]`。
|
|
34
|
+
* @param value - 待拆分文本。
|
|
35
|
+
* @returns 删除空项、保持输入顺序的单词数组。
|
|
36
|
+
*/
|
|
37
|
+
declare function splitWords(value: string): string[];
|
|
38
|
+
/**
|
|
39
|
+
* 将首个 Unicode 码点转为大写。
|
|
40
|
+
*
|
|
41
|
+
* @param value - 输入文本;空字符串保持为空。
|
|
42
|
+
* @param locale - 显式语言,默认固定为 `en-US`。
|
|
43
|
+
* @returns 首个 Unicode 码点转换后的文本。
|
|
44
|
+
*/
|
|
45
|
+
declare function upperFirst(value: string, locale?: StringLocale): string;
|
|
46
|
+
/**
|
|
47
|
+
* 将首个 Unicode 码点转为小写。
|
|
48
|
+
*
|
|
49
|
+
* @param value - 输入文本;空字符串保持为空。
|
|
50
|
+
* @param locale - 显式语言,默认固定为 `en-US`。
|
|
51
|
+
* @returns 首个 Unicode 码点转换后的文本。
|
|
52
|
+
*/
|
|
53
|
+
declare function lowerFirst(value: string, locale?: StringLocale): string;
|
|
54
|
+
/**
|
|
55
|
+
* 将文本转换为 camelCase。
|
|
56
|
+
*
|
|
57
|
+
* @param value - 由大小写、连字符、下划线或空白分隔的文本。
|
|
58
|
+
* @param locale - 大小写转换使用的语言,默认固定为 `en-US`。
|
|
59
|
+
* @returns camelCase 文本。
|
|
60
|
+
*/
|
|
61
|
+
declare function camelCase(value: string, locale?: StringLocale): string;
|
|
62
|
+
/**
|
|
63
|
+
* 将文本转换为 PascalCase。
|
|
64
|
+
*
|
|
65
|
+
* @param value - 参数语义与 {@link camelCase} 一致。
|
|
66
|
+
* @param locale - 大小写转换使用的显式语言。
|
|
67
|
+
* @returns PascalCase 文本。
|
|
68
|
+
*/
|
|
69
|
+
declare function pascalCase(value: string, locale?: StringLocale): string;
|
|
70
|
+
/**
|
|
71
|
+
* 将文本转换为 kebab-case。
|
|
72
|
+
*
|
|
73
|
+
* @param value - 参数语义与 {@link camelCase} 一致。
|
|
74
|
+
* @param locale - 大小写转换使用的显式语言。
|
|
75
|
+
* @returns kebab-case 文本。
|
|
76
|
+
*/
|
|
77
|
+
declare function kebabCase(value: string, locale?: StringLocale): string;
|
|
78
|
+
/**
|
|
79
|
+
* 按 Unicode 字素簇截断文本,避免拆开 emoji、组合音标或代理对。
|
|
80
|
+
*
|
|
81
|
+
* @param value - 输入文本。
|
|
82
|
+
* @param maxLength - 保留的最大字素簇数量。
|
|
83
|
+
* @param suffix - 被截断时追加的文本,默认单字符省略号 `…`;不计入上限。
|
|
84
|
+
* @param locale - 字素分割语言,默认固定为 `en-US`。
|
|
85
|
+
* @returns 未超限时返回原字符串,否则返回截断内容与后缀。
|
|
86
|
+
* @throws `RangeError` 当 `maxLength` 不是非负安全整数或 Locale 无效;缺少
|
|
87
|
+
* `Intl.Segmenter` 时抛出 `Error`。
|
|
88
|
+
*/
|
|
89
|
+
declare function truncateGraphemes(value: string, maxLength: number, suffix?: string, locale?: StringLocale): string;
|
|
90
|
+
/**
|
|
91
|
+
* 使用无偏 Web Crypto 随机数生成字符串。
|
|
92
|
+
*
|
|
93
|
+
* @param length - 字符数量,必须是 0 至 1,000,000 的安全整数。
|
|
94
|
+
* @param alphabet - 不得为空、包含重复字符或超过 2^32 个 Unicode 码点。
|
|
95
|
+
* @returns 由 `alphabet` 中 Unicode 码点组成的随机文本。
|
|
96
|
+
* @throws `RangeError` 当长度或字母表非法;缺少 Web Crypto 时抛出 `Error`。
|
|
97
|
+
*/
|
|
98
|
+
declare function secureRandomString(length: number, alphabet?: string): string;
|
|
99
|
+
/**
|
|
100
|
+
* 使用 Web Crypto 生成 RFC 4122 version 4 UUID。
|
|
101
|
+
*
|
|
102
|
+
* @returns 小写、带连字符的 UUID v4。
|
|
103
|
+
* @throws 缺少 Web Crypto 时抛出 `Error`。
|
|
104
|
+
*/
|
|
105
|
+
declare function generateUuidV4(): string;
|
|
106
|
+
/**
|
|
107
|
+
* 判断字符串是否为 RFC 4122 version 4 UUID。
|
|
108
|
+
*
|
|
109
|
+
* @param value - 待验证文本;十六进制字母大小写均可。
|
|
110
|
+
* @returns 版本位与 Variant 位均正确时返回 `true`。
|
|
111
|
+
*/
|
|
112
|
+
declare function isUuidV4(value: string): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* 转义 HTML 文本上下文中的五个特殊字符。
|
|
115
|
+
*
|
|
116
|
+
* @remarks 这不是 HTML 清洗器,不能让不可信文本安全进入 URL、CSS、脚本或属性名上下文。
|
|
117
|
+
* @param value - 将作为 HTML 文本节点内容的字符串。
|
|
118
|
+
* @returns 转义 `&`、`<`、`>`、双引号与单引号后的文本。
|
|
119
|
+
*/
|
|
120
|
+
declare function escapeHtml(value: string): string;
|
|
121
|
+
/**
|
|
122
|
+
* 把连续 Unicode 空白折叠为单个空格并删除两端空白。
|
|
123
|
+
*
|
|
124
|
+
* @param value - 输入文本。
|
|
125
|
+
* @returns 规范化后的文本;全空白输入返回空字符串。
|
|
126
|
+
*/
|
|
127
|
+
declare function normalizeWhitespace(value: string): string;
|
|
128
|
+
//#endregion
|
|
129
|
+
export { ParsedQueryParameters, StringLocale, camelCase, decodeURIComponentRepeatedly, escapeHtml, generateUuidV4, isUuidV4, isValidJson, kebabCase, lowerFirst, normalizeWhitespace, parseQueryString, pascalCase, secureRandomString, splitWords, truncateGraphemes, upperFirst };
|
|
130
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/string/index.ts"],"mappings":";;KA0BY,wBAAwB;;KAGxB;;;;;;;;;iBAsDI,6BAA6B,eAAe;;;;;;;;iBAkB5C,iBAAiB,gBAAgB;;;;;;;iBAyBjC,YAAY;;;;;;;;iBAiBZ,WAAW;;;;;;;;iBAgBX,WAAW,eAAe,SAAS;;;;;;;;iBAanC,WAAW,eAAe,SAAS;;;;;;;;iBAanC,UAAU,eAAe,SAAS;;;;;;;;iBAgBlC,WAAW,eAAe,SAAS;;;;;;;;iBAWnC,UAAU,eAAe,SAAS;;;;;;;;;;;;iBAiBlC,kBAAkB,eAAe,mBAAmB,iBAAc,SAAS;;;;;;;;;iBAc3E,mBAAmB,gBAAgB;;;;;;;iBAoCnC;;;;;;;iBAYA,SAAS;;;;;;;;iBAWT,WAAW;;;;;;;iBAuBX,oBAAoB"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
//#region src/string/index.ts
|
|
2
|
+
const defaultRandomAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
3
|
+
const defaultStringLocale = "en-US";
|
|
4
|
+
const maximumRandomStringLength = 1e6;
|
|
5
|
+
const maximumRandomValuesPerBatch = 16384;
|
|
6
|
+
const uuidV4Pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
7
|
+
const runtimeGlobals = globalThis;
|
|
8
|
+
/**
|
|
9
|
+
* 获取字符串随机 API 所需的 Web Crypto 能力。
|
|
10
|
+
*
|
|
11
|
+
* @returns 具有 `getRandomValues` 的当前 Crypto 对象。
|
|
12
|
+
* @throws `Error` 当平台没有安全随机能力;绝不回退到 `Math.random()`。
|
|
13
|
+
*/
|
|
14
|
+
const requireWebCrypto = () => {
|
|
15
|
+
const crypto = runtimeGlobals.crypto;
|
|
16
|
+
if (typeof crypto?.getRandomValues !== "function") throw new Error("Web Crypto random generation is unavailable in the current runtime.");
|
|
17
|
+
return crypto;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* 从随机字节创建 UUID v4。
|
|
21
|
+
*
|
|
22
|
+
* @param bytes - 长度至少为 16 的随机字节;Version 与 Variant 位会被原地修改。
|
|
23
|
+
* @returns 小写、带连字符的 RFC 4122 UUID v4。
|
|
24
|
+
*/
|
|
25
|
+
const createUuidV4FromBytes = (bytes) => {
|
|
26
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
27
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
28
|
+
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
29
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* 按用户可见字素切分文本。
|
|
33
|
+
*
|
|
34
|
+
* @param value - 待切分字符串。
|
|
35
|
+
* @param locale - Segmenter 使用的显式语言;省略时使用固定默认值。
|
|
36
|
+
* @returns 保留组合 Emoji、变音符号和连接序列的字素数组。
|
|
37
|
+
* @throws `Error` 当平台缺少 `Intl.Segmenter`。
|
|
38
|
+
*/
|
|
39
|
+
const splitGraphemes = (value, locale) => {
|
|
40
|
+
const Segmenter = runtimeGlobals.Intl?.Segmenter;
|
|
41
|
+
if (typeof Segmenter !== "function") throw new Error("Intl.Segmenter is unavailable in the current runtime.");
|
|
42
|
+
const segmenter = new Segmenter(locale ?? defaultStringLocale, { granularity: "grapheme" });
|
|
43
|
+
return Array.from(segmenter.segment(value), ({ segment }) => segment);
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* 重复执行 URI 组件解码,直到值稳定或达到深度上限。
|
|
47
|
+
*
|
|
48
|
+
* @param value - 不包含 URI 路径语义的编码组件。
|
|
49
|
+
* @param maxDepth - 最大解码次数,默认 `10`。
|
|
50
|
+
* @returns 解码稳定或达到上限后的组件文本。
|
|
51
|
+
* @throws `URIError` 当任一层包含非法百分号序列;深度非法时抛出 `RangeError`。
|
|
52
|
+
*/
|
|
53
|
+
function decodeURIComponentRepeatedly(value, maxDepth = 10) {
|
|
54
|
+
if (!Number.isSafeInteger(maxDepth) || maxDepth < 0) throw new RangeError("maxDepth must be a non-negative safe integer.");
|
|
55
|
+
let decoded = value;
|
|
56
|
+
for (let index = 0; index < maxDepth; index += 1) {
|
|
57
|
+
const next = decodeURIComponent(decoded);
|
|
58
|
+
if (next === decoded) break;
|
|
59
|
+
decoded = next;
|
|
60
|
+
}
|
|
61
|
+
return decoded;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 解析带 `://` 的绝对 URL、`?query` 或纯查询字符串。
|
|
65
|
+
*
|
|
66
|
+
* @remarks 纯查询字符串值中的未编码 `?` 会作为值内容保留;片段标识及其后内容被忽略。
|
|
67
|
+
* @param input - 完整 URL、带前导问号或不带前导问号的查询文本。
|
|
68
|
+
* @returns 重复键对应字符串数组,空值保留为空字符串。
|
|
69
|
+
*/
|
|
70
|
+
function parseQueryString(input) {
|
|
71
|
+
const fragmentStart = input.indexOf("#");
|
|
72
|
+
const withoutFragment = fragmentStart < 0 ? input : input.slice(0, fragmentStart);
|
|
73
|
+
const isAbsoluteUrl = /^[a-z][a-z\d+.-]*:\/\//iu.test(withoutFragment);
|
|
74
|
+
const queryStart = withoutFragment.indexOf("?");
|
|
75
|
+
if (isAbsoluteUrl && queryStart < 0) return {};
|
|
76
|
+
const query = isAbsoluteUrl ? withoutFragment.slice(queryStart + 1) : withoutFragment.replace(/^\?/u, "");
|
|
77
|
+
const result = {};
|
|
78
|
+
for (const [key, value] of new URLSearchParams(query)) {
|
|
79
|
+
const existing = Object.hasOwn(result, key) ? result[key] : void 0;
|
|
80
|
+
if (existing === void 0) Object.defineProperty(result, key, {
|
|
81
|
+
configurable: true,
|
|
82
|
+
enumerable: true,
|
|
83
|
+
value,
|
|
84
|
+
writable: true
|
|
85
|
+
});
|
|
86
|
+
else if (Array.isArray(existing)) existing.push(value);
|
|
87
|
+
else Object.defineProperty(result, key, {
|
|
88
|
+
configurable: true,
|
|
89
|
+
enumerable: true,
|
|
90
|
+
value: [existing, value],
|
|
91
|
+
writable: true
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 判断文本是否为任意合法 JSON 值,包括标量与 `null`。
|
|
98
|
+
*
|
|
99
|
+
* @param value - 待解析文本;纯空白不视为 JSON。
|
|
100
|
+
* @returns `JSON.parse` 能完整解析时返回 `true`。
|
|
101
|
+
*/
|
|
102
|
+
function isValidJson(value) {
|
|
103
|
+
if (value.trim().length === 0) return false;
|
|
104
|
+
try {
|
|
105
|
+
JSON.parse(value);
|
|
106
|
+
return true;
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 按大小写边界、连字符、下划线与空白切分单词。
|
|
113
|
+
*
|
|
114
|
+
* @example `XMLHttp_request` 返回 `["XML", "Http", "request"]`。
|
|
115
|
+
* @param value - 待拆分文本。
|
|
116
|
+
* @returns 删除空项、保持输入顺序的单词数组。
|
|
117
|
+
*/
|
|
118
|
+
function splitWords(value) {
|
|
119
|
+
return value.trim().replace(/(\p{Ll}|\p{N})(\p{Lu})/gu, "$1 $2").replace(/(\p{Lu})(\p{Lu}\p{Ll})/gu, "$1 $2").split(/[\s_-]+/u).filter((part) => part.length > 0);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* 将首个 Unicode 码点转为大写。
|
|
123
|
+
*
|
|
124
|
+
* @param value - 输入文本;空字符串保持为空。
|
|
125
|
+
* @param locale - 显式语言,默认固定为 `en-US`。
|
|
126
|
+
* @returns 首个 Unicode 码点转换后的文本。
|
|
127
|
+
*/
|
|
128
|
+
function upperFirst(value, locale) {
|
|
129
|
+
const characters = Array.from(value);
|
|
130
|
+
const first = characters.shift();
|
|
131
|
+
return first === void 0 ? "" : first.toLocaleUpperCase(locale ?? defaultStringLocale) + characters.join("");
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 将首个 Unicode 码点转为小写。
|
|
135
|
+
*
|
|
136
|
+
* @param value - 输入文本;空字符串保持为空。
|
|
137
|
+
* @param locale - 显式语言,默认固定为 `en-US`。
|
|
138
|
+
* @returns 首个 Unicode 码点转换后的文本。
|
|
139
|
+
*/
|
|
140
|
+
function lowerFirst(value, locale) {
|
|
141
|
+
const characters = Array.from(value);
|
|
142
|
+
const first = characters.shift();
|
|
143
|
+
return first === void 0 ? "" : first.toLocaleLowerCase(locale ?? defaultStringLocale) + characters.join("");
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* 将文本转换为 camelCase。
|
|
147
|
+
*
|
|
148
|
+
* @param value - 由大小写、连字符、下划线或空白分隔的文本。
|
|
149
|
+
* @param locale - 大小写转换使用的语言,默认固定为 `en-US`。
|
|
150
|
+
* @returns camelCase 文本。
|
|
151
|
+
*/
|
|
152
|
+
function camelCase(value, locale) {
|
|
153
|
+
return splitWords(value).map((part, index) => {
|
|
154
|
+
const normalized = part.toLocaleLowerCase(locale ?? defaultStringLocale);
|
|
155
|
+
return index === 0 ? normalized : upperFirst(normalized, locale);
|
|
156
|
+
}).join("");
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* 将文本转换为 PascalCase。
|
|
160
|
+
*
|
|
161
|
+
* @param value - 参数语义与 {@link camelCase} 一致。
|
|
162
|
+
* @param locale - 大小写转换使用的显式语言。
|
|
163
|
+
* @returns PascalCase 文本。
|
|
164
|
+
*/
|
|
165
|
+
function pascalCase(value, locale) {
|
|
166
|
+
return upperFirst(camelCase(value, locale), locale);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* 将文本转换为 kebab-case。
|
|
170
|
+
*
|
|
171
|
+
* @param value - 参数语义与 {@link camelCase} 一致。
|
|
172
|
+
* @param locale - 大小写转换使用的显式语言。
|
|
173
|
+
* @returns kebab-case 文本。
|
|
174
|
+
*/
|
|
175
|
+
function kebabCase(value, locale) {
|
|
176
|
+
return splitWords(value).map((part) => part.toLocaleLowerCase(locale ?? defaultStringLocale)).join("-");
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* 按 Unicode 字素簇截断文本,避免拆开 emoji、组合音标或代理对。
|
|
180
|
+
*
|
|
181
|
+
* @param value - 输入文本。
|
|
182
|
+
* @param maxLength - 保留的最大字素簇数量。
|
|
183
|
+
* @param suffix - 被截断时追加的文本,默认单字符省略号 `…`;不计入上限。
|
|
184
|
+
* @param locale - 字素分割语言,默认固定为 `en-US`。
|
|
185
|
+
* @returns 未超限时返回原字符串,否则返回截断内容与后缀。
|
|
186
|
+
* @throws `RangeError` 当 `maxLength` 不是非负安全整数或 Locale 无效;缺少
|
|
187
|
+
* `Intl.Segmenter` 时抛出 `Error`。
|
|
188
|
+
*/
|
|
189
|
+
function truncateGraphemes(value, maxLength, suffix = "…", locale) {
|
|
190
|
+
if (!Number.isSafeInteger(maxLength) || maxLength < 0) throw new RangeError("maxLength must be a non-negative safe integer.");
|
|
191
|
+
const segments = splitGraphemes(value, locale);
|
|
192
|
+
return segments.length > maxLength ? segments.slice(0, maxLength).join("") + suffix : value;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 使用无偏 Web Crypto 随机数生成字符串。
|
|
196
|
+
*
|
|
197
|
+
* @param length - 字符数量,必须是 0 至 1,000,000 的安全整数。
|
|
198
|
+
* @param alphabet - 不得为空、包含重复字符或超过 2^32 个 Unicode 码点。
|
|
199
|
+
* @returns 由 `alphabet` 中 Unicode 码点组成的随机文本。
|
|
200
|
+
* @throws `RangeError` 当长度或字母表非法;缺少 Web Crypto 时抛出 `Error`。
|
|
201
|
+
*/
|
|
202
|
+
function secureRandomString(length, alphabet = defaultRandomAlphabet) {
|
|
203
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > maximumRandomStringLength) throw new RangeError(`length must be a safe integer from 0 through ${maximumRandomStringLength}.`);
|
|
204
|
+
const characters = Array.from(alphabet);
|
|
205
|
+
if (characters.length === 0) throw new RangeError("alphabet cannot be empty.");
|
|
206
|
+
if (new Set(characters).size !== characters.length) throw new RangeError("alphabet cannot contain duplicate characters.");
|
|
207
|
+
if (characters.length > 4294967296) throw new RangeError("alphabet cannot contain more than 2^32 characters.");
|
|
208
|
+
if (length === 0) return "";
|
|
209
|
+
const crypto = requireWebCrypto();
|
|
210
|
+
const acceptanceLimit = Math.floor(4294967296 / characters.length) * characters.length;
|
|
211
|
+
const result = [];
|
|
212
|
+
while (result.length < length) {
|
|
213
|
+
const remaining = length - result.length;
|
|
214
|
+
const samples = crypto.getRandomValues(new Uint32Array(Math.min(remaining, maximumRandomValuesPerBatch)));
|
|
215
|
+
for (const sample of samples) {
|
|
216
|
+
if (sample >= acceptanceLimit) continue;
|
|
217
|
+
const character = characters[sample % characters.length];
|
|
218
|
+
if (character === void 0) continue;
|
|
219
|
+
result.push(character);
|
|
220
|
+
if (result.length === length) break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return result.join("");
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* 使用 Web Crypto 生成 RFC 4122 version 4 UUID。
|
|
227
|
+
*
|
|
228
|
+
* @returns 小写、带连字符的 UUID v4。
|
|
229
|
+
* @throws 缺少 Web Crypto 时抛出 `Error`。
|
|
230
|
+
*/
|
|
231
|
+
function generateUuidV4() {
|
|
232
|
+
const crypto = requireWebCrypto();
|
|
233
|
+
if (typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
234
|
+
return createUuidV4FromBytes(crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16)));
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* 判断字符串是否为 RFC 4122 version 4 UUID。
|
|
238
|
+
*
|
|
239
|
+
* @param value - 待验证文本;十六进制字母大小写均可。
|
|
240
|
+
* @returns 版本位与 Variant 位均正确时返回 `true`。
|
|
241
|
+
*/
|
|
242
|
+
function isUuidV4(value) {
|
|
243
|
+
return uuidV4Pattern.test(value);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* 转义 HTML 文本上下文中的五个特殊字符。
|
|
247
|
+
*
|
|
248
|
+
* @remarks 这不是 HTML 清洗器,不能让不可信文本安全进入 URL、CSS、脚本或属性名上下文。
|
|
249
|
+
* @param value - 将作为 HTML 文本节点内容的字符串。
|
|
250
|
+
* @returns 转义 `&`、`<`、`>`、双引号与单引号后的文本。
|
|
251
|
+
*/
|
|
252
|
+
function escapeHtml(value) {
|
|
253
|
+
return value.replace(/[&<>"']/gu, (character) => {
|
|
254
|
+
switch (character) {
|
|
255
|
+
case "&": return "&";
|
|
256
|
+
case "<": return "<";
|
|
257
|
+
case ">": return ">";
|
|
258
|
+
case "\"": return """;
|
|
259
|
+
default: return "'";
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* 把连续 Unicode 空白折叠为单个空格并删除两端空白。
|
|
265
|
+
*
|
|
266
|
+
* @param value - 输入文本。
|
|
267
|
+
* @returns 规范化后的文本;全空白输入返回空字符串。
|
|
268
|
+
*/
|
|
269
|
+
function normalizeWhitespace(value) {
|
|
270
|
+
return value.trim().replace(/\s+/gu, " ");
|
|
271
|
+
}
|
|
272
|
+
//#endregion
|
|
273
|
+
export { camelCase, decodeURIComponentRepeatedly, escapeHtml, generateUuidV4, isUuidV4, isValidJson, kebabCase, lowerFirst, normalizeWhitespace, parseQueryString, pascalCase, secureRandomString, splitWords, truncateGraphemes, upperFirst };
|
|
274
|
+
|
|
275
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/string/index.ts"],"sourcesContent":["const defaultRandomAlphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\nconst defaultStringLocale = \"en-US\";\nconst maximumRandomStringLength = 1_000_000;\nconst maximumRandomValuesPerBatch = 16_384;\nconst uuidV4Pattern = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;\n\n/** 字符串随机 API 需要的 Web Crypto 最小能力。 */\ntype RuntimeStringCrypto = Partial<Pick<Crypto, \"getRandomValues\" | \"randomUUID\">>;\n\n/** 字素分割需要的可选 Intl 能力。 */\ninterface RuntimeStringIntl {\n\t/** 可选 Segmenter 构造器;缺失时字素 API 使用内部兼容路径。 */\n\tSegmenter?: typeof Intl.Segmenter;\n}\n\n/** 字符串工具延迟访问的平台全局对象最小视图。 */\ninterface RuntimeStringGlobals {\n\t/** 安全随机字符串与 UUID 所需的可选 Web Crypto 能力。 */\n\tcrypto?: RuntimeStringCrypto;\n\t/** 字素分割所需的可选 Intl 能力。 */\n\tIntl?: RuntimeStringIntl;\n}\n\nconst runtimeGlobals = globalThis as unknown as RuntimeStringGlobals;\n\n/** 查询字符串解析结果;重复键保留为数组,不存在的键读取为 `undefined`。 */\nexport type ParsedQueryParameters = Record<string, string | string[] | undefined>;\n\n/** 大小写与字素分割可接受的显式语言;省略时固定使用 `en-US` 以保持输出稳定。 */\nexport type StringLocale = string | readonly string[] | undefined;\n\n/**\n * 获取字符串随机 API 所需的 Web Crypto 能力。\n *\n * @returns 具有 `getRandomValues` 的当前 Crypto 对象。\n * @throws `Error` 当平台没有安全随机能力;绝不回退到 `Math.random()`。\n */\nconst requireWebCrypto = (): Crypto => {\n\tconst crypto = runtimeGlobals.crypto;\n\tif (typeof crypto?.getRandomValues !== \"function\") {\n\t\tthrow new Error(\"Web Crypto random generation is unavailable in the current runtime.\");\n\t}\n\treturn crypto as Crypto;\n};\n\n/**\n * 从随机字节创建 UUID v4。\n *\n * @param bytes - 长度至少为 16 的随机字节;Version 与 Variant 位会被原地修改。\n * @returns 小写、带连字符的 RFC 4122 UUID v4。\n */\nconst createUuidV4FromBytes = (bytes: Uint8Array): string => {\n\tbytes[6] = ((bytes[6] ?? 0) & 15) | 64;\n\tbytes[8] = ((bytes[8] ?? 0) & 63) | 128;\n\tconst hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n\treturn `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n};\n\n/**\n * 按用户可见字素切分文本。\n *\n * @param value - 待切分字符串。\n * @param locale - Segmenter 使用的显式语言;省略时使用固定默认值。\n * @returns 保留组合 Emoji、变音符号和连接序列的字素数组。\n * @throws `Error` 当平台缺少 `Intl.Segmenter`。\n */\nconst splitGraphemes = (value: string, locale: StringLocale): string[] => {\n\tconst Segmenter = runtimeGlobals.Intl?.Segmenter;\n\tif (typeof Segmenter !== \"function\") {\n\t\tthrow new Error(\"Intl.Segmenter is unavailable in the current runtime.\");\n\t}\n\tconst segmenter = new Segmenter(locale ?? defaultStringLocale, { granularity: \"grapheme\" });\n\treturn Array.from(segmenter.segment(value), ({ segment }) => segment);\n};\n\n/**\n * 重复执行 URI 组件解码,直到值稳定或达到深度上限。\n *\n * @param value - 不包含 URI 路径语义的编码组件。\n * @param maxDepth - 最大解码次数,默认 `10`。\n * @returns 解码稳定或达到上限后的组件文本。\n * @throws `URIError` 当任一层包含非法百分号序列;深度非法时抛出 `RangeError`。\n */\nexport function decodeURIComponentRepeatedly(value: string, maxDepth = 10): string {\n\tif (!Number.isSafeInteger(maxDepth) || maxDepth < 0) throw new RangeError(\"maxDepth must be a non-negative safe integer.\");\n\tlet decoded = value;\n\tfor (let index = 0; index < maxDepth; index += 1) {\n\t\tconst next = decodeURIComponent(decoded);\n\t\tif (next === decoded) break;\n\t\tdecoded = next;\n\t}\n\treturn decoded;\n}\n\n/**\n * 解析带 `://` 的绝对 URL、`?query` 或纯查询字符串。\n *\n * @remarks 纯查询字符串值中的未编码 `?` 会作为值内容保留;片段标识及其后内容被忽略。\n * @param input - 完整 URL、带前导问号或不带前导问号的查询文本。\n * @returns 重复键对应字符串数组,空值保留为空字符串。\n */\nexport function parseQueryString(input: string): ParsedQueryParameters {\n\tconst fragmentStart = input.indexOf(\"#\");\n\tconst withoutFragment = fragmentStart < 0 ? input : input.slice(0, fragmentStart);\n\tconst isAbsoluteUrl = /^[a-z][a-z\\d+.-]*:\\/\\//iu.test(withoutFragment);\n\tconst queryStart = withoutFragment.indexOf(\"?\");\n\tif (isAbsoluteUrl && queryStart < 0) return {};\n\tconst query = isAbsoluteUrl ? withoutFragment.slice(queryStart + 1) : withoutFragment.replace(/^\\?/u, \"\");\n\tconst result: ParsedQueryParameters = {};\n\tfor (const [key, value] of new URLSearchParams(query)) {\n\t\tconst existing = Object.hasOwn(result, key) ? result[key] : undefined;\n\t\tif (existing === undefined) {\n\t\t\t// defineProperty 让 `__proto__` 成为普通自有键,不触发 Object.prototype Setter。\n\t\t\tObject.defineProperty(result, key, { configurable: true, enumerable: true, value, writable: true });\n\t\t} else if (Array.isArray(existing)) existing.push(value);\n\t\telse Object.defineProperty(result, key, { configurable: true, enumerable: true, value: [existing, value], writable: true });\n\t}\n\treturn result;\n}\n\n/**\n * 判断文本是否为任意合法 JSON 值,包括标量与 `null`。\n *\n * @param value - 待解析文本;纯空白不视为 JSON。\n * @returns `JSON.parse` 能完整解析时返回 `true`。\n */\nexport function isValidJson(value: string): boolean {\n\tif (value.trim().length === 0) return false;\n\ttry {\n\t\tJSON.parse(value);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * 按大小写边界、连字符、下划线与空白切分单词。\n *\n * @example `XMLHttp_request` 返回 `[\"XML\", \"Http\", \"request\"]`。\n * @param value - 待拆分文本。\n * @returns 删除空项、保持输入顺序的单词数组。\n */\nexport function splitWords(value: string): string[] {\n\treturn value\n\t\t.trim()\n\t\t.replace(/(\\p{Ll}|\\p{N})(\\p{Lu})/gu, \"$1 $2\")\n\t\t.replace(/(\\p{Lu})(\\p{Lu}\\p{Ll})/gu, \"$1 $2\")\n\t\t.split(/[\\s_-]+/u)\n\t\t.filter((part) => part.length > 0);\n}\n\n/**\n * 将首个 Unicode 码点转为大写。\n *\n * @param value - 输入文本;空字符串保持为空。\n * @param locale - 显式语言,默认固定为 `en-US`。\n * @returns 首个 Unicode 码点转换后的文本。\n */\nexport function upperFirst(value: string, locale?: StringLocale): string {\n\tconst characters = Array.from(value);\n\tconst first = characters.shift();\n\treturn first === undefined ? \"\" : first.toLocaleUpperCase(locale ?? defaultStringLocale) + characters.join(\"\");\n}\n\n/**\n * 将首个 Unicode 码点转为小写。\n *\n * @param value - 输入文本;空字符串保持为空。\n * @param locale - 显式语言,默认固定为 `en-US`。\n * @returns 首个 Unicode 码点转换后的文本。\n */\nexport function lowerFirst(value: string, locale?: StringLocale): string {\n\tconst characters = Array.from(value);\n\tconst first = characters.shift();\n\treturn first === undefined ? \"\" : first.toLocaleLowerCase(locale ?? defaultStringLocale) + characters.join(\"\");\n}\n\n/**\n * 将文本转换为 camelCase。\n *\n * @param value - 由大小写、连字符、下划线或空白分隔的文本。\n * @param locale - 大小写转换使用的语言,默认固定为 `en-US`。\n * @returns camelCase 文本。\n */\nexport function camelCase(value: string, locale?: StringLocale): string {\n\treturn splitWords(value)\n\t\t.map((part, index) => {\n\t\t\tconst normalized = part.toLocaleLowerCase(locale ?? defaultStringLocale);\n\t\t\treturn index === 0 ? normalized : upperFirst(normalized, locale);\n\t\t})\n\t\t.join(\"\");\n}\n\n/**\n * 将文本转换为 PascalCase。\n *\n * @param value - 参数语义与 {@link camelCase} 一致。\n * @param locale - 大小写转换使用的显式语言。\n * @returns PascalCase 文本。\n */\nexport function pascalCase(value: string, locale?: StringLocale): string {\n\treturn upperFirst(camelCase(value, locale), locale);\n}\n\n/**\n * 将文本转换为 kebab-case。\n *\n * @param value - 参数语义与 {@link camelCase} 一致。\n * @param locale - 大小写转换使用的显式语言。\n * @returns kebab-case 文本。\n */\nexport function kebabCase(value: string, locale?: StringLocale): string {\n\treturn splitWords(value)\n\t\t.map((part) => part.toLocaleLowerCase(locale ?? defaultStringLocale))\n\t\t.join(\"-\");\n}\n\n/**\n * 按 Unicode 字素簇截断文本,避免拆开 emoji、组合音标或代理对。\n *\n * @param value - 输入文本。\n * @param maxLength - 保留的最大字素簇数量。\n * @param suffix - 被截断时追加的文本,默认单字符省略号 `…`;不计入上限。\n * @param locale - 字素分割语言,默认固定为 `en-US`。\n * @returns 未超限时返回原字符串,否则返回截断内容与后缀。\n * @throws `RangeError` 当 `maxLength` 不是非负安全整数或 Locale 无效;缺少\n * `Intl.Segmenter` 时抛出 `Error`。\n */\nexport function truncateGraphemes(value: string, maxLength: number, suffix = \"…\", locale?: StringLocale): string {\n\tif (!Number.isSafeInteger(maxLength) || maxLength < 0) throw new RangeError(\"maxLength must be a non-negative safe integer.\");\n\tconst segments = splitGraphemes(value, locale);\n\treturn segments.length > maxLength ? segments.slice(0, maxLength).join(\"\") + suffix : value;\n}\n\n/**\n * 使用无偏 Web Crypto 随机数生成字符串。\n *\n * @param length - 字符数量,必须是 0 至 1,000,000 的安全整数。\n * @param alphabet - 不得为空、包含重复字符或超过 2^32 个 Unicode 码点。\n * @returns 由 `alphabet` 中 Unicode 码点组成的随机文本。\n * @throws `RangeError` 当长度或字母表非法;缺少 Web Crypto 时抛出 `Error`。\n */\nexport function secureRandomString(length: number, alphabet: string = defaultRandomAlphabet): string {\n\tif (!Number.isSafeInteger(length) || length < 0 || length > maximumRandomStringLength) {\n\t\tthrow new RangeError(`length must be a safe integer from 0 through ${maximumRandomStringLength}.`);\n\t}\n\tconst characters = Array.from(alphabet);\n\tif (characters.length === 0) throw new RangeError(\"alphabet cannot be empty.\");\n\tif (new Set(characters).size !== characters.length) throw new RangeError(\"alphabet cannot contain duplicate characters.\");\n\tif (characters.length > 0x1_0000_0000) throw new RangeError(\"alphabet cannot contain more than 2^32 characters.\");\n\tif (length === 0) return \"\";\n\n\tconst crypto = requireWebCrypto();\n\tconst uint32Range = 0x1_0000_0000;\n\t// 丢弃不能平均映射到字母表的尾部区间,避免 `%` 造成前部字符概率偏高。\n\tconst acceptanceLimit = Math.floor(uint32Range / characters.length) * characters.length;\n\tconst result: string[] = [];\n\twhile (result.length < length) {\n\t\tconst remaining = length - result.length;\n\t\t// 分批请求可控制临时内存,并避开 Web Crypto 单次随机数组大小限制。\n\t\tconst samples = crypto.getRandomValues(new Uint32Array(Math.min(remaining, maximumRandomValuesPerBatch)));\n\t\tfor (const sample of samples) {\n\t\t\tif (sample >= acceptanceLimit) continue;\n\t\t\tconst character = characters[sample % characters.length];\n\t\t\tif (character === undefined) continue;\n\t\t\tresult.push(character);\n\t\t\tif (result.length === length) break;\n\t\t}\n\t}\n\treturn result.join(\"\");\n}\n\n/**\n * 使用 Web Crypto 生成 RFC 4122 version 4 UUID。\n *\n * @returns 小写、带连字符的 UUID v4。\n * @throws 缺少 Web Crypto 时抛出 `Error`。\n */\nexport function generateUuidV4(): string {\n\tconst crypto = requireWebCrypto();\n\tif (typeof crypto.randomUUID === \"function\") return crypto.randomUUID();\n\treturn createUuidV4FromBytes(crypto.getRandomValues(new Uint8Array(16)));\n}\n\n/**\n * 判断字符串是否为 RFC 4122 version 4 UUID。\n *\n * @param value - 待验证文本;十六进制字母大小写均可。\n * @returns 版本位与 Variant 位均正确时返回 `true`。\n */\nexport function isUuidV4(value: string): boolean {\n\treturn uuidV4Pattern.test(value);\n}\n\n/**\n * 转义 HTML 文本上下文中的五个特殊字符。\n *\n * @remarks 这不是 HTML 清洗器,不能让不可信文本安全进入 URL、CSS、脚本或属性名上下文。\n * @param value - 将作为 HTML 文本节点内容的字符串。\n * @returns 转义 `&`、`<`、`>`、双引号与单引号后的文本。\n */\nexport function escapeHtml(value: string): string {\n\treturn value.replace(/[&<>\"']/gu, (character) => {\n\t\tswitch (character) {\n\t\t\tcase \"&\":\n\t\t\t\treturn \"&\";\n\t\t\tcase \"<\":\n\t\t\t\treturn \"<\";\n\t\t\tcase \">\":\n\t\t\t\treturn \">\";\n\t\t\tcase '\"':\n\t\t\t\treturn \""\";\n\t\t\tdefault:\n\t\t\t\treturn \"'\";\n\t\t}\n\t});\n}\n\n/**\n * 把连续 Unicode 空白折叠为单个空格并删除两端空白。\n *\n * @param value - 输入文本。\n * @returns 规范化后的文本;全空白输入返回空字符串。\n */\nexport function normalizeWhitespace(value: string): string {\n\treturn value.trim().replace(/\\s+/gu, \" \");\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ComputedRef } from "vue";
|
|
2
|
+
//#region src/vue/emits.d.ts
|
|
3
|
+
/** Vue Emits 对象中允许的校验器形状。 */
|
|
4
|
+
type EmitValidator = ((...arguments_: never[]) => unknown) | null;
|
|
5
|
+
/** 事件名到可选参数校验器的内部映射。 */
|
|
6
|
+
type EmitsOptions = Record<string, EmitValidator>;
|
|
7
|
+
/** 从校验器中提取事件参数;无校验器时保留未知参数。 */
|
|
8
|
+
type EventArguments<Validator> = Validator extends ((...arguments_: infer Arguments) => unknown) ? Arguments : unknown[];
|
|
9
|
+
/** 在类型层递归把 kebab-case 事件名转换为 PascalCase。 */
|
|
10
|
+
type PascalEventName<Value extends string> = Value extends `${infer Head}-${infer Tail}` ? `${Capitalize<Head>}${PascalEventName<Tail>}` : Capitalize<Value>;
|
|
11
|
+
/** 把事件配置映射为 Vue `onXxx` 属性。 */
|
|
12
|
+
type EmitHandlers<Emits extends EmitsOptions> = { [Name in keyof Emits as Name extends string ? `on${PascalEventName<Name>}` : never]: (...arguments_: EventArguments<Emits[Name]>) => void; };
|
|
13
|
+
/**
|
|
14
|
+
* 构建响应式 Vue 事件处理器。
|
|
15
|
+
*
|
|
16
|
+
* @param emits - Vue emits 配置对象。
|
|
17
|
+
* @param emit - `setup` 上下文提供的 emit 函数。
|
|
18
|
+
* @param ignoredEvents - 不需要向子组件透传的事件名。
|
|
19
|
+
* @returns 随配置重新计算的事件处理器对象。
|
|
20
|
+
*/
|
|
21
|
+
declare function useEmits<Emits extends EmitsOptions>(emits: Emits, emit: (...arguments_: never[]) => unknown, ignoredEvents?: readonly (keyof Emits)[]): ComputedRef<Partial<EmitHandlers<Emits>>>;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { EmitHandlers, useEmits };
|
|
24
|
+
//# sourceMappingURL=emits.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emits.d.mts","names":[],"sources":["../../src/vue/emits.ts"],"mappings":";;;KAKK,qBAAqB;;KAErB,eAAe,eAAe;;KAE9B,eAAe,aAAa,uBAAsB,kBAAkB,yBAAwB;;KAE5F,gBAAgB,wBAAwB,uBAAuB,cAAc,YAC5E,WAAW,QAAQ,gBAAgB,UACtC,WAAW;;KAGF,aAAa,cAAc,mBACrC,cAAc,SAAS,2BAA2B,gBAAgB,sBAAsB,YAAY,eAAe,MAAM;;;;;;;;;iBAyB3G,SAAS,cAAc,cACtC,OAAO,OACP,UAAU,iCACV,gCAA+B,WAC7B,YAAY,QAAQ,aAAa"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
//#region src/vue/emits.ts
|
|
3
|
+
/**
|
|
4
|
+
* 把事件名转换为 Vue Handler Prop 名称。
|
|
5
|
+
*
|
|
6
|
+
* @param eventName - Emits 对象中的原始事件名,可使用 kebab-case。
|
|
7
|
+
* @returns `onPascalCase` 形式的属性名。
|
|
8
|
+
* @throws `TypeError` 当事件名包含空片段或无法生成有效 Handler 名称。
|
|
9
|
+
*/
|
|
10
|
+
const toHandlerName = (eventName) => {
|
|
11
|
+
return `on${eventName.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("")}`;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* 构建响应式 Vue 事件处理器。
|
|
15
|
+
*
|
|
16
|
+
* @param emits - Vue emits 配置对象。
|
|
17
|
+
* @param emit - `setup` 上下文提供的 emit 函数。
|
|
18
|
+
* @param ignoredEvents - 不需要向子组件透传的事件名。
|
|
19
|
+
* @returns 随配置重新计算的事件处理器对象。
|
|
20
|
+
*/
|
|
21
|
+
function useEmits(emits, emit, ignoredEvents = []) {
|
|
22
|
+
const ignored = new Set(ignoredEvents);
|
|
23
|
+
const emitEvent = emit;
|
|
24
|
+
return computed(() => {
|
|
25
|
+
const handlers = {};
|
|
26
|
+
const handlerNames = /* @__PURE__ */ new Set();
|
|
27
|
+
for (const eventName of Object.keys(emits)) {
|
|
28
|
+
if (ignored.has(eventName)) continue;
|
|
29
|
+
if (eventName.length === 0 || /\s/u.test(eventName)) throw new TypeError(`Invalid Vue event name: "${eventName}".`);
|
|
30
|
+
const handlerName = toHandlerName(eventName);
|
|
31
|
+
if (handlerNames.has(handlerName)) throw new TypeError(`Vue events map to the same handler property: "${handlerName}".`);
|
|
32
|
+
handlerNames.add(handlerName);
|
|
33
|
+
Object.defineProperty(handlers, handlerName, {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
value: (...arguments_) => {
|
|
36
|
+
emitEvent(eventName, ...arguments_);
|
|
37
|
+
},
|
|
38
|
+
writable: true
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return handlers;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { useEmits };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=emits.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emits.mjs","names":[],"sources":["../../src/vue/emits.ts"],"sourcesContent":["import { computed } from \"vue\";\n\nimport type { ComputedRef } from \"vue\";\n\n/** Vue Emits 对象中允许的校验器形状。 */\ntype EmitValidator = ((...arguments_: never[]) => unknown) | null;\n/** 事件名到可选参数校验器的内部映射。 */\ntype EmitsOptions = Record<string, EmitValidator>;\n/** 从校验器中提取事件参数;无校验器时保留未知参数。 */\ntype EventArguments<Validator> = Validator extends (...arguments_: infer Arguments) => unknown ? Arguments : unknown[];\n/** 在类型层递归把 kebab-case 事件名转换为 PascalCase。 */\ntype PascalEventName<Value extends string> = Value extends `${infer Head}-${infer Tail}`\n\t? `${Capitalize<Head>}${PascalEventName<Tail>}`\n\t: Capitalize<Value>;\n\n/** 把事件配置映射为 Vue `onXxx` 属性。 */\nexport type EmitHandlers<Emits extends EmitsOptions> = {\n\t[Name in keyof Emits as Name extends string ? `on${PascalEventName<Name>}` : never]: (...arguments_: EventArguments<Emits[Name]>) => void;\n};\n\n/**\n * 把事件名转换为 Vue Handler Prop 名称。\n *\n * @param eventName - Emits 对象中的原始事件名,可使用 kebab-case。\n * @returns `onPascalCase` 形式的属性名。\n * @throws `TypeError` 当事件名包含空片段或无法生成有效 Handler 名称。\n */\nconst toHandlerName = (eventName: string): string => {\n\treturn `on${eventName\n\t\t.split(\"-\")\n\t\t.map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n\t\t.join(\"\")}`;\n};\n\n/**\n * 构建响应式 Vue 事件处理器。\n *\n * @param emits - Vue emits 配置对象。\n * @param emit - `setup` 上下文提供的 emit 函数。\n * @param ignoredEvents - 不需要向子组件透传的事件名。\n * @returns 随配置重新计算的事件处理器对象。\n */\nexport function useEmits<Emits extends EmitsOptions>(\n\temits: Emits,\n\temit: (...arguments_: never[]) => unknown,\n\tignoredEvents: readonly (keyof Emits)[] = []\n): ComputedRef<Partial<EmitHandlers<Emits>>> {\n\tconst ignored = new Set<PropertyKey>(ignoredEvents);\n\tconst emitEvent = emit as unknown as (eventName: string, ...arguments_: unknown[]) => void;\n\treturn computed<Partial<EmitHandlers<Emits>>>(() => {\n\t\tconst handlers = {} as Partial<EmitHandlers<Emits>>;\n\t\tconst handlerNames = new Set<string>();\n\t\tfor (const eventName of Object.keys(emits)) {\n\t\t\tif (ignored.has(eventName)) continue;\n\t\t\tif (eventName.length === 0 || /\\s/u.test(eventName)) throw new TypeError(`Invalid Vue event name: \"${eventName}\".`);\n\t\t\tconst handlerName = toHandlerName(eventName);\n\t\t\tif (handlerNames.has(handlerName)) {\n\t\t\t\tthrow new TypeError(`Vue events map to the same handler property: \"${handlerName}\".`);\n\t\t\t}\n\t\t\thandlerNames.add(handlerName);\n\t\t\tObject.defineProperty(handlers, handlerName, {\n\t\t\t\tenumerable: true,\n\t\t\t\tvalue: (...arguments_: unknown[]): void => {\n\t\t\t\t\temitEvent(eventName, ...arguments_);\n\t\t\t\t},\n\t\t\t\twritable: true,\n\t\t\t});\n\t\t}\n\t\treturn handlers;\n\t});\n}\n"],"mappings":";;;;;;;;;AA2BA,MAAM,iBAAiB,cAA8B;CACpD,OAAO,KAAK,UACV,MAAM,GAAG,CAAC,CACV,KAAK,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAC3D,KAAK,EAAE;AACV;;;;;;;;;AAUA,SAAgB,SACf,OACA,MACA,gBAA0C,CAAC,GACC;CAC5C,MAAM,UAAU,IAAI,IAAiB,aAAa;CAClD,MAAM,YAAY;CAClB,OAAO,eAA6C;EACnD,MAAM,WAAW,CAAC;EAClB,MAAM,+BAAe,IAAI,IAAY;EACrC,KAAK,MAAM,aAAa,OAAO,KAAK,KAAK,GAAG;GAC3C,IAAI,QAAQ,IAAI,SAAS,GAAG;GAC5B,IAAI,UAAU,WAAW,KAAK,MAAM,KAAK,SAAS,GAAG,MAAM,IAAI,UAAU,4BAA4B,UAAU,GAAG;GAClH,MAAM,cAAc,cAAc,SAAS;GAC3C,IAAI,aAAa,IAAI,WAAW,GAC/B,MAAM,IAAI,UAAU,iDAAiD,YAAY,GAAG;GAErF,aAAa,IAAI,WAAW;GAC5B,OAAO,eAAe,UAAU,aAAa;IAC5C,YAAY;IACZ,QAAQ,GAAG,eAAgC;KAC1C,UAAU,WAAW,GAAG,UAAU;IACnC;IACA,UAAU;GACX,CAAC;EACF;EACA,OAAO;CACR,CAAC;AACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/vue/expose.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* 同时暴露组件实例能力并返回同一个对象,便于 `setup` 返回状态供 Vue Devtools 查看。
|
|
4
|
+
*
|
|
5
|
+
* @param expose - `setup` 上下文提供的 expose 函数。
|
|
6
|
+
* @param exposed - 需要暴露的状态和方法。
|
|
7
|
+
* @returns 原始 exposed 对象。
|
|
8
|
+
*/
|
|
9
|
+
declare function useExpose<Exposed extends object>(expose: (exposed?: Exposed) => void, exposed: Exposed): Exposed;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { useExpose };
|
|
12
|
+
//# sourceMappingURL=expose.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expose.d.mts","names":[],"sources":["../../src/vue/expose.ts"],"mappings":";;;;;;;;iBAOgB,UAAU,wBAAwB,SAAS,UAAU,kBAAkB,SAAS,UAAU"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/vue/expose.ts
|
|
2
|
+
/**
|
|
3
|
+
* 同时暴露组件实例能力并返回同一个对象,便于 `setup` 返回状态供 Vue Devtools 查看。
|
|
4
|
+
*
|
|
5
|
+
* @param expose - `setup` 上下文提供的 expose 函数。
|
|
6
|
+
* @param exposed - 需要暴露的状态和方法。
|
|
7
|
+
* @returns 原始 exposed 对象。
|
|
8
|
+
*/
|
|
9
|
+
function useExpose(expose, exposed) {
|
|
10
|
+
expose(exposed);
|
|
11
|
+
return exposed;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { useExpose };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=expose.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expose.mjs","names":[],"sources":["../../src/vue/expose.ts"],"sourcesContent":["/**\n * 同时暴露组件实例能力并返回同一个对象,便于 `setup` 返回状态供 Vue Devtools 查看。\n *\n * @param expose - `setup` 上下文提供的 expose 函数。\n * @param exposed - 需要暴露的状态和方法。\n * @returns 原始 exposed 对象。\n */\nexport function useExpose<Exposed extends object>(expose: (exposed?: Exposed) => void, exposed: Exposed): Exposed {\n\texpose(exposed);\n\treturn exposed;\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,UAAkC,QAAqC,SAA2B;CACjH,OAAO,OAAO;CACd,OAAO;AACR"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/vue/func.d.ts
|
|
2
|
+
/** 可同步或异步返回结果的函数。 */
|
|
3
|
+
type AwaitableFunction<Arguments extends readonly unknown[], Result> = (...arguments_: Arguments) => Result | PromiseLike<Result>;
|
|
4
|
+
/**
|
|
5
|
+
* 统一执行同步或异步函数,异常保持原样向调用方传播。
|
|
6
|
+
*
|
|
7
|
+
* @param function_ - 可选的待执行函数。
|
|
8
|
+
* @param arguments_ - 原样传入函数的参数。
|
|
9
|
+
* @returns 函数结果;未传函数时返回 `undefined`。
|
|
10
|
+
*/
|
|
11
|
+
declare function callOptionalFunction<Arguments extends readonly unknown[], Result>(function_: AwaitableFunction<Arguments, Result> | null | undefined, ...arguments_: Arguments): Promise<Awaited<Result> | undefined>;
|
|
12
|
+
//#endregion
|
|
13
|
+
export { AwaitableFunction, callOptionalFunction };
|
|
14
|
+
//# sourceMappingURL=func.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"func.d.mts","names":[],"sources":["../../src/vue/func.ts"],"mappings":";;KACY,kBAAkB,sCAAsC,cAAc,YAAY,cAAc,SAAS,YAAY;;;;;;;;iBAS3G,qBAAqB,sCAAsC,QAChF,WAAW,kBAAkB,WAAW,+BACrC,YAAY,YACb,QAAQ,QAAQ"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/vue/func.ts
|
|
2
|
+
/**
|
|
3
|
+
* 统一执行同步或异步函数,异常保持原样向调用方传播。
|
|
4
|
+
*
|
|
5
|
+
* @param function_ - 可选的待执行函数。
|
|
6
|
+
* @param arguments_ - 原样传入函数的参数。
|
|
7
|
+
* @returns 函数结果;未传函数时返回 `undefined`。
|
|
8
|
+
*/
|
|
9
|
+
async function callOptionalFunction(function_, ...arguments_) {
|
|
10
|
+
if (function_ === null || function_ === void 0) return void 0;
|
|
11
|
+
return await function_(...arguments_);
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { callOptionalFunction };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=func.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"func.mjs","names":[],"sources":["../../src/vue/func.ts"],"sourcesContent":["/** 可同步或异步返回结果的函数。 */\nexport type AwaitableFunction<Arguments extends readonly unknown[], Result> = (...arguments_: Arguments) => Result | PromiseLike<Result>;\n\n/**\n * 统一执行同步或异步函数,异常保持原样向调用方传播。\n *\n * @param function_ - 可选的待执行函数。\n * @param arguments_ - 原样传入函数的参数。\n * @returns 函数结果;未传函数时返回 `undefined`。\n */\nexport async function callOptionalFunction<Arguments extends readonly unknown[], Result>(\n\tfunction_: AwaitableFunction<Arguments, Result> | null | undefined,\n\t...arguments_: Arguments\n): Promise<Awaited<Result> | undefined> {\n\tif (function_ === null || function_ === undefined) return undefined;\n\treturn await function_(...arguments_);\n}\n"],"mappings":";;;;;;;;AAUA,eAAsB,qBACrB,WACA,GAAG,YACoC;CACvC,IAAI,cAAc,QAAQ,cAAc,KAAA,GAAW,OAAO,KAAA;CAC1D,OAAO,MAAM,UAAU,GAAG,UAAU;AACrC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EmitHandlers, useEmits } from "./emits.mjs";
|
|
2
|
+
import { useExpose } from "./expose.mjs";
|
|
3
|
+
import { AwaitableFunction, callOptionalFunction } from "./func.mjs";
|
|
4
|
+
import { Installable, TSXWithInstall, VueInstallValue, VueRegistrationTarget, withInstall, withInstallDirective, withNoopInstall } from "./install.mjs";
|
|
5
|
+
import { definePropType, useProps } from "./props.mjs";
|
|
6
|
+
import { useRender } from "./render.mjs";
|
|
7
|
+
import { TypedSlots, TypedSlotsDeclaration, makeSlots } from "./slots.mjs";
|
|
8
|
+
import { withDefineType } from "./with.mjs";
|
|
9
|
+
export { AwaitableFunction, EmitHandlers, Installable, TSXWithInstall, TypedSlots, TypedSlotsDeclaration, VueInstallValue, VueRegistrationTarget, callOptionalFunction, definePropType, makeSlots, useEmits, useExpose, useProps, useRender, withDefineType, withInstall, withInstallDirective, withNoopInstall };
|