@fast-china/utils 2.1.9 → 2.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +5 -2
  3. package/README.zh.md +5 -2
  4. package/dist/array/index.mjs +21 -21
  5. package/dist/array/index.mjs.map +1 -1
  6. package/dist/async/index.mjs +32 -32
  7. package/dist/async/index.mjs.map +1 -1
  8. package/dist/base64/index.mjs +44 -51
  9. package/dist/base64/index.mjs.map +1 -1
  10. package/dist/color/index.mjs +33 -33
  11. package/dist/color/index.mjs.map +1 -1
  12. package/dist/crypto/index.mjs +153 -155
  13. package/dist/crypto/index.mjs.map +1 -1
  14. package/dist/date/index.mjs +79 -46
  15. package/dist/date/index.mjs.map +1 -1
  16. package/dist/dom/style.mjs +7 -7
  17. package/dist/dom/style.mjs.map +1 -1
  18. package/dist/env/index.mjs +9 -14
  19. package/dist/env/index.mjs.map +1 -1
  20. package/dist/function/index.mjs +4 -4
  21. package/dist/function/index.mjs.map +1 -1
  22. package/dist/identity/index.mjs +7 -7
  23. package/dist/identity/index.mjs.map +1 -1
  24. package/dist/index.d.mts +393 -366
  25. package/dist/index.global.min.js +2 -2
  26. package/dist/index.global.min.js.map +1 -1
  27. package/dist/index.mjs +2 -1
  28. package/dist/internal/text.mjs +70 -26
  29. package/dist/internal/text.mjs.map +1 -1
  30. package/dist/logger/index.mjs +12 -13
  31. package/dist/logger/index.mjs.map +1 -1
  32. package/dist/number/index.mjs +63 -42
  33. package/dist/number/index.mjs.map +1 -1
  34. package/dist/object/index.mjs +32 -31
  35. package/dist/object/index.mjs.map +1 -1
  36. package/dist/storage/index.mjs +58 -63
  37. package/dist/storage/index.mjs.map +1 -1
  38. package/dist/string/index.mjs +60 -64
  39. package/dist/string/index.mjs.map +1 -1
  40. package/dist/vue/breakpoints.mjs +14 -10
  41. package/dist/vue/breakpoints.mjs.map +1 -1
  42. package/dist/vue/element-size.mjs +4 -4
  43. package/dist/vue/element-size.mjs.map +1 -1
  44. package/dist/vue/emits.mjs +7 -7
  45. package/dist/vue/emits.mjs.map +1 -1
  46. package/dist/vue/event-listener.mjs +5 -5
  47. package/dist/vue/event-listener.mjs.map +1 -1
  48. package/dist/vue/expose.mjs +3 -3
  49. package/dist/vue/expose.mjs.map +1 -1
  50. package/dist/vue/func.mjs +2 -2
  51. package/dist/vue/func.mjs.map +1 -1
  52. package/dist/vue/index.mjs +2 -1
  53. package/dist/vue/install.mjs +24 -24
  54. package/dist/vue/install.mjs.map +1 -1
  55. package/dist/vue/now.mjs +4 -5
  56. package/dist/vue/now.mjs.map +1 -1
  57. package/dist/vue/props.mjs +5 -5
  58. package/dist/vue/props.mjs.map +1 -1
  59. package/dist/vue/render.mjs +2 -2
  60. package/dist/vue/render.mjs.map +1 -1
  61. package/dist/vue/resize-observer.mjs +4 -6
  62. package/dist/vue/resize-observer.mjs.map +1 -1
  63. package/dist/vue/slots.mjs.map +1 -1
  64. package/dist/vue/transition.mjs +58 -0
  65. package/dist/vue/transition.mjs.map +1 -0
  66. package/dist/vue/window-size.mjs +2 -4
  67. package/dist/vue/window-size.mjs.map +1 -1
  68. package/dist/vue/with.mjs +1 -1
  69. package/dist/vue/with.mjs.map +1 -1
  70. package/package.json +2 -1
  71. package/dist/internal/runtime.mjs +0 -32
  72. package/dist/internal/runtime.mjs.map +0 -1
@@ -1,4 +1,3 @@
1
- import { getRuntimeUni, runtimeGlobals } from "../internal/runtime.mjs";
2
1
  //#region src/string/index.ts
3
2
  const defaultRandomAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
4
3
  const defaultStringLocale = "en-US";
@@ -8,8 +7,7 @@ const uint32Range = 4294967296;
8
7
  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;
9
8
  /** 使用 Web Crypto 填充随机值,能力缺失时回退到 `Math.random()`。 */
10
9
  const fillRandomValues = (values) => {
11
- const crypto = runtimeGlobals.crypto;
12
- if (typeof crypto?.getRandomValues === "function") {
10
+ if (typeof crypto !== "undefined" && typeof crypto?.getRandomValues === "function") {
13
11
  crypto.getRandomValues(values);
14
12
  return;
15
13
  }
@@ -20,7 +18,7 @@ const fillRandomValues = (values) => {
20
18
  * 从随机字节创建 UUID v4。
21
19
  *
22
20
  * @param bytes - 长度至少为 16 的随机字节;Version 与 Variant 位会被原地修改。
23
- * @returns 小写、带连字符的 RFC 4122 UUID v4。
21
+ * @returns 小写、带连字符的 RFC 4122 UUID v4
24
22
  */
25
23
  const createUuidV4FromBytes = (bytes) => {
26
24
  bytes[6] = (bytes[6] ?? 0) & 15 | 64;
@@ -31,27 +29,26 @@ const createUuidV4FromBytes = (bytes) => {
31
29
  /**
32
30
  * 按用户可见字素切分文本。
33
31
  *
34
- * @param value - 待切分字符串。
32
+ * @param value - 待切分字符串
35
33
  * @param locale - Segmenter 使用的显式语言;省略时使用固定默认值。
36
- * @returns 保留组合 Emoji、变音符号和连接序列的字素数组。
34
+ * @returns 保留组合 Emoji、变音符号和连接序列的字素数组
37
35
  * @throws `Error` 当平台缺少 `Intl.Segmenter`。
38
36
  */
39
37
  const splitGraphemes = (value, locale) => {
40
- const Segmenter = runtimeGlobals.Intl?.Segmenter;
41
- if (typeof Segmenter !== "function") throw new Error("当前运行环境不支持 Intl.Segmenter。");
42
- const segmenter = new Segmenter(locale ?? defaultStringLocale, { granularity: "grapheme" });
38
+ if (typeof Intl === "undefined" || typeof Intl.Segmenter !== "function") throw new Error("The current runtime does not support Intl.Segmenter.");
39
+ const segmenter = new Intl.Segmenter(locale ?? defaultStringLocale, { granularity: "grapheme" });
43
40
  return Array.from(segmenter.segment(value), ({ segment }) => segment);
44
41
  };
45
42
  /**
46
43
  * 重复执行 URI 组件解码,直到值稳定或达到深度上限。
47
44
  *
48
- * @param value - 不包含 URI 路径语义的编码组件。
49
- * @param maxDepth - 最大解码次数,默认 `10`。
50
- * @returns 解码稳定或达到上限后的组件文本。
45
+ * @param value - 不包含 URI 路径语义的编码组件
46
+ * @param maxDepth - 最大解码次数,默认 `10`
47
+ * @returns 解码稳定或达到上限后的组件文本
51
48
  * @throws `URIError` 当任一层包含非法百分号序列;深度非法时抛出 `RangeError`。
52
49
  */
53
50
  function decodeURIComponentRepeatedly(value, maxDepth = 10) {
54
- if (!Number.isSafeInteger(maxDepth) || maxDepth < 0) throw new RangeError("`maxDepth` 必须是非负安全整数。");
51
+ if (!Number.isSafeInteger(maxDepth) || maxDepth < 0) throw new RangeError("`maxDepth` must be a nonnegative safe integer.");
55
52
  let decoded = value;
56
53
  for (let index = 0; index < maxDepth; index += 1) {
57
54
  const next = decodeURIComponent(decoded);
@@ -64,8 +61,9 @@ function decodeURIComponentRepeatedly(value, maxDepth = 10) {
64
61
  * 解析带 `://` 的绝对 URL、`?query` 或纯查询字符串。
65
62
  *
66
63
  * @remarks 纯查询字符串值中的未编码 `?` 会作为值内容保留;片段标识及其后内容被忽略。
67
- * @param input - 完整 URL、带前导问号或不带前导问号的查询文本。
64
+ * @param input - 完整 URL、带前导问号或不带前导问号的查询文本
68
65
  * @returns 重复键对应字符串数组,空值保留为空字符串。
66
+ * @throws `URIError` 当键或值包含非法百分号编码或无效的 UTF-8 字节序列。
69
67
  */
70
68
  function parseQueryString(input) {
71
69
  const fragmentStart = input.indexOf("#");
@@ -75,8 +73,12 @@ function parseQueryString(input) {
75
73
  if (isAbsoluteUrl && queryStart < 0) return {};
76
74
  const query = isAbsoluteUrl ? withoutFragment.slice(queryStart + 1) : withoutFragment.replace(/^\?/u, "");
77
75
  const result = {};
78
- for (const [key, value] of new URLSearchParams(query)) {
79
- const existing = Object.hasOwn(result, key) ? result[key] : void 0;
76
+ for (const entry of query.replace(/^\?/u, "").replace(/\+/gu, " ").split("&")) {
77
+ if (entry === "") continue;
78
+ const separator = entry.indexOf("=");
79
+ const key = decodeURIComponent(separator < 0 ? entry : entry.slice(0, separator));
80
+ const value = decodeURIComponent(separator < 0 ? "" : entry.slice(separator + 1));
81
+ const existing = Object.prototype.hasOwnProperty.call(result, key) ? result[key] : void 0;
80
82
  if (existing === void 0) Object.defineProperty(result, key, {
81
83
  configurable: true,
82
84
  enumerable: true,
@@ -112,8 +114,8 @@ function isValidJson(value) {
112
114
  * 按大小写边界、连字符、下划线与空白切分单词。
113
115
  *
114
116
  * @example `XMLHttp_request` 返回 `["XML", "Http", "request"]`。
115
- * @param value - 待拆分文本。
116
- * @returns 删除空项、保持输入顺序的单词数组。
117
+ * @param value - 待拆分文本
118
+ * @returns 删除空项、保持输入顺序的单词数组
117
119
  */
118
120
  function splitWords(value) {
119
121
  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);
@@ -122,36 +124,36 @@ function splitWords(value) {
122
124
  * 将首个 Unicode 码点转为大写。
123
125
  *
124
126
  * @param value - 输入文本;空字符串保持为空。
125
- * @param locale - 显式语言,默认固定为 `en-US`。
126
- * @returns 首个 Unicode 码点转换后的文本。
127
+ * @param locale - 显式语言;省略时使用标准 Unicode 大小写规则。
128
+ * @returns 首个 Unicode 码点转换后的文本
127
129
  */
128
130
  function upperFirst(value, locale) {
129
131
  const characters = Array.from(value);
130
132
  const first = characters.shift();
131
- return first === void 0 ? "" : first.toLocaleUpperCase(locale ?? defaultStringLocale) + characters.join("");
133
+ return first === void 0 ? "" : (locale === void 0 ? first.toUpperCase() : first.toLocaleUpperCase(locale)) + characters.join("");
132
134
  }
133
135
  /**
134
136
  * 将首个 Unicode 码点转为小写。
135
137
  *
136
138
  * @param value - 输入文本;空字符串保持为空。
137
- * @param locale - 显式语言,默认固定为 `en-US`。
138
- * @returns 首个 Unicode 码点转换后的文本。
139
+ * @param locale - 显式语言;省略时使用标准 Unicode 大小写规则。
140
+ * @returns 首个 Unicode 码点转换后的文本
139
141
  */
140
142
  function lowerFirst(value, locale) {
141
143
  const characters = Array.from(value);
142
144
  const first = characters.shift();
143
- return first === void 0 ? "" : first.toLocaleLowerCase(locale ?? defaultStringLocale) + characters.join("");
145
+ return first === void 0 ? "" : (locale === void 0 ? first.toLowerCase() : first.toLocaleLowerCase(locale)) + characters.join("");
144
146
  }
145
147
  /**
146
148
  * 将文本转换为 camelCase。
147
149
  *
148
- * @param value - 由大小写、连字符、下划线或空白分隔的文本。
149
- * @param locale - 大小写转换使用的语言,默认固定为 `en-US`。
150
- * @returns camelCase 文本。
150
+ * @param value - 由大小写、连字符、下划线或空白分隔的文本
151
+ * @param locale - 大小写转换使用的语言;省略时使用标准 Unicode 大小写规则。
152
+ * @returns camelCase 文本
151
153
  */
152
154
  function camelCase(value, locale) {
153
155
  return splitWords(value).map((part, index) => {
154
- const normalized = part.toLocaleLowerCase(locale ?? defaultStringLocale);
156
+ const normalized = locale === void 0 ? part.toLowerCase() : part.toLocaleLowerCase(locale);
155
157
  return index === 0 ? normalized : upperFirst(normalized, locale);
156
158
  }).join("");
157
159
  }
@@ -159,8 +161,8 @@ function camelCase(value, locale) {
159
161
  * 将文本转换为 PascalCase。
160
162
  *
161
163
  * @param value - 参数语义与 {@link camelCase} 一致。
162
- * @param locale - 大小写转换使用的显式语言。
163
- * @returns PascalCase 文本。
164
+ * @param locale - 大小写转换使用的显式语言
165
+ * @returns PascalCase 文本
164
166
  */
165
167
  function pascalCase(value, locale) {
166
168
  return upperFirst(camelCase(value, locale), locale);
@@ -169,25 +171,25 @@ function pascalCase(value, locale) {
169
171
  * 将文本转换为 kebab-case。
170
172
  *
171
173
  * @param value - 参数语义与 {@link camelCase} 一致。
172
- * @param locale - 大小写转换使用的显式语言。
173
- * @returns kebab-case 文本。
174
+ * @param locale - 大小写转换使用的显式语言
175
+ * @returns kebab-case 文本
174
176
  */
175
177
  function kebabCase(value, locale) {
176
- return splitWords(value).map((part) => part.toLocaleLowerCase(locale ?? defaultStringLocale)).join("-");
178
+ return splitWords(value).map((part) => locale === void 0 ? part.toLowerCase() : part.toLocaleLowerCase(locale)).join("-");
177
179
  }
178
180
  /**
179
181
  * 按 Unicode 字素簇截断文本,避免拆开 emoji、组合音标或代理对。
180
182
  *
181
- * @param value - 输入文本。
182
- * @param maxLength - 保留的最大字素簇数量。
183
+ * @param value - 输入文本
184
+ * @param maxLength - 保留的最大字素簇数量
183
185
  * @param suffix - 被截断时追加的文本,默认单字符省略号 `…`;不计入上限。
184
- * @param locale - 字素分割语言,默认固定为 `en-US`。
186
+ * @param locale - 字素分割语言,默认固定为 `en-US`
185
187
  * @returns 未超限时返回原字符串,否则返回截断内容与后缀。
186
188
  * @throws `RangeError` 当 `maxLength` 不是非负安全整数或 Locale 无效;缺少
187
189
  * `Intl.Segmenter` 时抛出 `Error`。
188
190
  */
189
191
  function truncateGraphemes(value, maxLength, suffix = "…", locale) {
190
- if (!Number.isSafeInteger(maxLength) || maxLength < 0) throw new RangeError("`maxLength` 必须是非负安全整数。");
192
+ if (!Number.isSafeInteger(maxLength) || maxLength < 0) throw new RangeError("`maxLength` must be a nonnegative safe integer.");
191
193
  const segments = splitGraphemes(value, locale);
192
194
  return segments.length > maxLength ? segments.slice(0, maxLength).join("") + suffix : value;
193
195
  }
@@ -196,35 +198,30 @@ function truncateGraphemes(value, maxLength, suffix = "…", locale) {
196
198
  *
197
199
  * @remarks uni-app 使用 `setClipboardData`;浏览器优先使用 Clipboard API,并在该 API 不可用时
198
200
  * 回退到 `document.execCommand("copy")`。平台拒绝访问剪贴板时不会静默忽略错误。
199
- * @param value - 要复制的文本。
200
- * @returns 复制完成后兑现的 Promise。
201
+ * @param value - 要复制的文本
202
+ * @returns 复制完成后兑现的 Promise
201
203
  * @throws `Error` 当运行时没有可用的剪贴板能力或复制失败。
202
204
  */
203
205
  async function copy(value) {
204
- const uni = getRuntimeUni();
205
- if (uni !== void 0) {
206
- if (typeof uni !== "object" && typeof uni !== "function" || uni === null) throw new TypeError("全局 uni 对象未提供 `setClipboardData`。");
207
- const clipboard = uni;
208
- const setClipboardData = clipboard.setClipboardData?.bind(clipboard);
209
- if (setClipboardData === void 0) throw new TypeError("全局 uni 对象未提供 `setClipboardData`。");
206
+ if (typeof uni !== "undefined") {
207
+ if (uni === null || typeof uni.setClipboardData !== "function") throw new TypeError("The global uni object does not provide `setClipboardData`.");
210
208
  await new Promise((resolve, reject) => {
211
- setClipboardData({
209
+ uni.setClipboardData({
212
210
  data: value,
213
211
  fail: (error) => {
214
- reject(error instanceof Error ? error : new Error("文本复制到剪贴板失败。", { cause: error }));
212
+ reject(error instanceof Error ? error : new Error("Failed to copy text to the clipboard.", { cause: error }));
215
213
  },
216
214
  success: resolve
217
215
  });
218
216
  });
219
217
  return;
220
218
  }
221
- const browserClipboard = runtimeGlobals.navigator?.clipboard;
222
- if (runtimeGlobals.isSecureContext === true && typeof browserClipboard?.writeText === "function") {
223
- await browserClipboard.writeText(value);
219
+ if (typeof isSecureContext !== "undefined" && isSecureContext && typeof navigator !== "undefined" && typeof navigator.clipboard?.writeText === "function") {
220
+ await navigator.clipboard.writeText(value);
224
221
  return;
225
222
  }
226
- const document = runtimeGlobals.document;
227
- if (typeof document?.createElement !== "function" || document.body == null || typeof document.execCommand !== "function") throw new Error("当前运行环境不支持访问剪贴板。");
223
+ if (typeof document === "undefined" || document === null) throw new Error("The current runtime does not support clipboard access.");
224
+ if (typeof document.createElement !== "function" || document.body == null || typeof document.execCommand !== "function") throw new Error("The current runtime does not support clipboard access.");
228
225
  const textarea = document.createElement("textarea");
229
226
  textarea.value = value;
230
227
  textarea.style.left = "-999999px";
@@ -240,7 +237,7 @@ async function copy(value) {
240
237
  } finally {
241
238
  textarea.remove();
242
239
  }
243
- if (!copied) throw new Error("文本复制到剪贴板失败。");
240
+ if (!copied) throw new Error("Failed to copy text to the clipboard.");
244
241
  }
245
242
  /**
246
243
  * 生成随机字符串。
@@ -248,15 +245,15 @@ async function copy(value) {
248
245
  * @remarks 优先使用 Web Crypto;平台缺少安全随机能力时回退到 `Math.random()`。
249
246
  * @param length - 字符数量,必须是 0 至 1,000,000 的安全整数。
250
247
  * @param alphabet - 不得为空、包含重复字符或超过 2^32 个 Unicode 码点。
251
- * @returns 由 `alphabet` 中 Unicode 码点组成的随机文本。
248
+ * @returns 由 `alphabet` 中 Unicode 码点组成的随机文本
252
249
  * @throws `RangeError` 当长度或字母表非法。
253
250
  */
254
251
  function randomString(length, alphabet = defaultRandomAlphabet) {
255
- if (!Number.isSafeInteger(length) || length < 0 || length > maximumRandomStringLength) throw new RangeError(`\`length\` 必须是 0 到 ${maximumRandomStringLength} 之间的安全整数。`);
252
+ if (!Number.isSafeInteger(length) || length < 0 || length > maximumRandomStringLength) throw new RangeError(`\`length\` must be a safe integer between 0 and ${maximumRandomStringLength}.`);
256
253
  const characters = Array.from(alphabet);
257
- if (characters.length === 0) throw new RangeError("`alphabet` 不能为空。");
258
- if (new Set(characters).size !== characters.length) throw new RangeError("`alphabet` 不能包含重复字符。");
259
- if (characters.length > 4294967296) throw new RangeError("`alphabet` 不能包含超过 2^32 个字符。");
254
+ if (characters.length === 0) throw new RangeError("`alphabet` must not be empty.");
255
+ if (new Set(characters).size !== characters.length) throw new RangeError("`alphabet` must not contain duplicate characters.");
256
+ if (characters.length > 4294967296) throw new RangeError("`alphabet` must not contain more than 2^32 characters.");
260
257
  if (length === 0) return "";
261
258
  const acceptanceLimit = Math.floor(uint32Range / characters.length) * characters.length;
262
259
  const result = [];
@@ -279,11 +276,10 @@ function randomString(length, alphabet = defaultRandomAlphabet) {
279
276
  *
280
277
  * @remarks 优先使用 Web Crypto;平台缺少安全随机能力时回退到 `Math.random()`。
281
278
  * 该 UUID 适合普通唯一标识,不应作为安全令牌或秘密。
282
- * @returns 小写、带连字符的 UUID v4。
279
+ * @returns 小写、带连字符的 UUID v4
283
280
  */
284
281
  function generateUuidV4() {
285
- const crypto = runtimeGlobals.crypto;
286
- if (typeof crypto?.randomUUID === "function") return crypto.randomUUID();
282
+ if (typeof crypto !== "undefined" && typeof crypto?.randomUUID === "function") return crypto.randomUUID();
287
283
  const bytes = /* @__PURE__ */ new Uint8Array(16);
288
284
  fillRandomValues(bytes);
289
285
  return createUuidV4FromBytes(bytes);
@@ -301,8 +297,8 @@ function isUuidV4(value) {
301
297
  * 转义 HTML 文本上下文中的五个特殊字符。
302
298
  *
303
299
  * @remarks 这不是 HTML 清洗器,不能让不可信文本安全进入 URL、CSS、脚本或属性名上下文。
304
- * @param value - 将作为 HTML 文本节点内容的字符串。
305
- * @returns 转义 `&`、`<`、`>`、双引号与单引号后的文本。
300
+ * @param value - 将作为 HTML 文本节点内容的字符串
301
+ * @returns 转义 `&`、`<`、`>`、双引号与单引号后的文本
306
302
  */
307
303
  function escapeHtml(value) {
308
304
  return value.replace(/[&<>"']/gu, (character) => {
@@ -318,7 +314,7 @@ function escapeHtml(value) {
318
314
  /**
319
315
  * 把连续 Unicode 空白折叠为单个空格并删除两端空白。
320
316
  *
321
- * @param value - 输入文本。
317
+ * @param value - 输入文本
322
318
  * @returns 规范化后的文本;全空白输入返回空字符串。
323
319
  */
324
320
  function normalizeWhitespace(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/string/index.ts"],"sourcesContent":["import { getRuntimeUni, runtimeGlobals } from \"../internal/runtime\";\n\nconst defaultRandomAlphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\nconst defaultStringLocale = \"en-US\";\nconst maximumRandomStringLength = 1_000_000;\nconst maximumRandomValuesPerBatch = 16_384;\nconst uint32Range = 0x1_0000_0000;\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/** 查询字符串解析结果;重复键保留为数组,不存在的键读取为 `undefined`。 */\nexport type ParsedQueryParameters = Record<string, string | string[] | undefined>;\n\n/** 大小写与字素分割可接受的显式语言;省略时固定使用 `en-US` 以保持输出稳定。 */\nexport type StringLocale = string | readonly string[] | undefined;\n\n/** uni-app 文本复制所需的最小运行时能力。 */\ninterface UniClipboard {\n\tsetClipboardData: (options: { data: string; fail: (error: unknown) => void; success: () => void }) => void;\n}\n\n/** 使用 Web Crypto 填充随机值,能力缺失时回退到 `Math.random()`。 */\nconst fillRandomValues = (values: Uint8Array<ArrayBuffer> | Uint32Array<ArrayBuffer>): void => {\n\tconst crypto = runtimeGlobals.crypto;\n\tif (typeof crypto?.getRandomValues === \"function\") {\n\t\tcrypto.getRandomValues(values);\n\t\treturn;\n\t}\n\tconst range = values.BYTES_PER_ELEMENT === Uint8Array.BYTES_PER_ELEMENT ? 0x100 : uint32Range;\n\tfor (let index = 0; index < values.length; index += 1) values[index] = Math.floor(Math.random() * range);\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。\");\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` 必须是非负安全整数。\");\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)) {\n\t\t\texisting.push(value);\n\t\t} else {\n\t\t\tObject.defineProperty(result, key, { configurable: true, enumerable: true, value: [existing, value], writable: true });\n\t\t}\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` 必须是非负安全整数。\");\n\tconst segments = splitGraphemes(value, locale);\n\treturn segments.length > maxLength ? segments.slice(0, maxLength).join(\"\") + suffix : value;\n}\n\n/**\n * 把文本复制到系统剪贴板。\n *\n * @remarks uni-app 使用 `setClipboardData`;浏览器优先使用 Clipboard API,并在该 API 不可用时\n * 回退到 `document.execCommand(\"copy\")`。平台拒绝访问剪贴板时不会静默忽略错误。\n * @param value - 要复制的文本。\n * @returns 复制完成后兑现的 Promise。\n * @throws `Error` 当运行时没有可用的剪贴板能力或复制失败。\n */\nexport async function copy(value: string): Promise<void> {\n\tconst uni = getRuntimeUni();\n\tif (uni !== undefined) {\n\t\tif ((typeof uni !== \"object\" && typeof uni !== \"function\") || uni === null) {\n\t\t\tthrow new TypeError(\"全局 uni 对象未提供 `setClipboardData`。\");\n\t\t}\n\t\tconst clipboard = uni as Partial<UniClipboard>;\n\t\tconst setClipboardData = clipboard.setClipboardData?.bind(clipboard);\n\t\tif (setClipboardData === undefined) throw new TypeError(\"全局 uni 对象未提供 `setClipboardData`。\");\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tsetClipboardData({\n\t\t\t\tdata: value,\n\t\t\t\tfail: (error: unknown) => {\n\t\t\t\t\treject(error instanceof Error ? error : new Error(\"文本复制到剪贴板失败。\", { cause: error }));\n\t\t\t\t},\n\t\t\t\tsuccess: resolve,\n\t\t\t});\n\t\t});\n\t\treturn;\n\t}\n\n\tconst browserClipboard = runtimeGlobals.navigator?.clipboard;\n\tif (runtimeGlobals.isSecureContext === true && typeof browserClipboard?.writeText === \"function\") {\n\t\tawait browserClipboard.writeText(value);\n\t\treturn;\n\t}\n\n\tconst document = runtimeGlobals.document;\n\t// eslint-disable-next-line @typescript-eslint/no-deprecated -- 兼容不支持 Clipboard API 的旧 WebView。\n\tif (typeof document?.createElement !== \"function\" || document.body == null || typeof document.execCommand !== \"function\") {\n\t\tthrow new Error(\"当前运行环境不支持访问剪贴板。\");\n\t}\n\tconst textarea = document.createElement(\"textarea\");\n\ttextarea.value = value;\n\ttextarea.style.left = \"-999999px\";\n\ttextarea.style.opacity = \"0\";\n\ttextarea.style.position = \"fixed\";\n\ttextarea.style.top = \"-999999px\";\n\tdocument.body.appendChild(textarea);\n\tlet copied: boolean;\n\ttry {\n\t\ttextarea.focus();\n\t\ttextarea.select();\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated -- 兼容不支持 Clipboard API 的旧 WebView。\n\t\tcopied = document.execCommand(\"copy\");\n\t} finally {\n\t\ttextarea.remove();\n\t}\n\tif (!copied) throw new Error(\"文本复制到剪贴板失败。\");\n}\n\n/**\n * 生成随机字符串。\n *\n * @remarks 优先使用 Web Crypto;平台缺少安全随机能力时回退到 `Math.random()`。\n * @param length - 字符数量,必须是 0 至 1,000,000 的安全整数。\n * @param alphabet - 不得为空、包含重复字符或超过 2^32 个 Unicode 码点。\n * @returns 由 `alphabet` 中 Unicode 码点组成的随机文本。\n * @throws `RangeError` 当长度或字母表非法。\n */\nexport function randomString(length: number, alphabet: string = defaultRandomAlphabet): string {\n\tif (!Number.isSafeInteger(length) || length < 0 || length > maximumRandomStringLength) {\n\t\tthrow new RangeError(`\\`length\\` 必须是 0 到 ${maximumRandomStringLength} 之间的安全整数。`);\n\t}\n\tconst characters = Array.from(alphabet);\n\tif (characters.length === 0) throw new RangeError(\"`alphabet` 不能为空。\");\n\tif (new Set(characters).size !== characters.length) throw new RangeError(\"`alphabet` 不能包含重复字符。\");\n\tif (characters.length > 0x1_0000_0000) throw new RangeError(\"`alphabet` 不能包含超过 2^32 个字符。\");\n\tif (length === 0) return \"\";\n\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 = new Uint32Array(Math.min(remaining, maximumRandomValuesPerBatch));\n\t\tfillRandomValues(samples);\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 * 生成 RFC 4122 version 4 UUID。\n *\n * @remarks 优先使用 Web Crypto;平台缺少安全随机能力时回退到 `Math.random()`。\n * 该 UUID 适合普通唯一标识,不应作为安全令牌或秘密。\n * @returns 小写、带连字符的 UUID v4。\n */\nexport function generateUuidV4(): string {\n\tconst crypto = runtimeGlobals.crypto;\n\tif (typeof crypto?.randomUUID === \"function\") return crypto.randomUUID();\n\tconst bytes = new Uint8Array(16);\n\tfillRandomValues(bytes);\n\treturn createUuidV4FromBytes(bytes);\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 \"&amp;\";\n\t\t\tcase \"<\":\n\t\t\t\treturn \"&lt;\";\n\t\t\tcase \">\":\n\t\t\t\treturn \"&gt;\";\n\t\t\tcase '\"':\n\t\t\t\treturn \"&quot;\";\n\t\t\tdefault:\n\t\t\t\treturn \"&#39;\";\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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"}
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 uint32Range = 0x1_0000_0000;\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/** 查询字符串解析结果;重复键保留为数组,不存在的键读取为 `undefined`。 */\nexport type ParsedQueryParameters = Record<string, string | string[] | undefined>;\n\n/** 大小写与字素分割可接受的显式语言;省略时大小写转换使用标准 Unicode 规则,字素分割固定使用 `en-US`。 */\nexport type StringLocale = string | readonly string[] | undefined;\n\n/** 使用 Web Crypto 填充随机值,能力缺失时回退到 `Math.random()`。 */\nconst fillRandomValues = (values: Uint8Array<ArrayBuffer> | Uint32Array<ArrayBuffer>): void => {\n\tif (typeof crypto !== \"undefined\" && typeof crypto?.getRandomValues === \"function\") {\n\t\tcrypto.getRandomValues(values);\n\t\treturn;\n\t}\n\tconst range = values.BYTES_PER_ELEMENT === Uint8Array.BYTES_PER_ELEMENT ? 0x100 : uint32Range;\n\tfor (let index = 0; index < values.length; index += 1) values[index] = Math.floor(Math.random() * range);\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\tif (typeof Intl === \"undefined\" || typeof Intl.Segmenter !== \"function\") {\n\t\tthrow new Error(\"The current runtime does not support Intl.Segmenter.\");\n\t}\n\tconst segmenter = new Intl.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 nonnegative 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 * @throws `URIError` 当键或值包含非法百分号编码或无效的 UTF-8 字节序列。\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 entry of query.replace(/^\\?/u, \"\").replace(/\\+/gu, \" \").split(\"&\")) {\n\t\tif (entry === \"\") continue;\n\t\tconst separator = entry.indexOf(\"=\");\n\t\tconst key = decodeURIComponent(separator < 0 ? entry : entry.slice(0, separator));\n\t\tconst value = decodeURIComponent(separator < 0 ? \"\" : entry.slice(separator + 1));\n\t\tconst existing = Object.prototype.hasOwnProperty.call(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)) {\n\t\t\texisting.push(value);\n\t\t} else {\n\t\t\tObject.defineProperty(result, key, { configurable: true, enumerable: true, value: [existing, value], writable: true });\n\t\t}\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 - 显式语言;省略时使用标准 Unicode 大小写规则。\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 ? \"\" : (locale === undefined ? first.toUpperCase() : first.toLocaleUpperCase(locale)) + characters.join(\"\");\n}\n\n/**\n * 将首个 Unicode 码点转为小写。\n *\n * @param value - 输入文本;空字符串保持为空。\n * @param locale - 显式语言;省略时使用标准 Unicode 大小写规则。\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 ? \"\" : (locale === undefined ? first.toLowerCase() : first.toLocaleLowerCase(locale)) + characters.join(\"\");\n}\n\n/**\n * 将文本转换为 camelCase。\n *\n * @param value - 由大小写、连字符、下划线或空白分隔的文本\n * @param locale - 大小写转换使用的语言;省略时使用标准 Unicode 大小写规则。\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 = locale === undefined ? part.toLowerCase() : part.toLocaleLowerCase(locale);\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) => (locale === undefined ? part.toLowerCase() : part.toLocaleLowerCase(locale)))\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 nonnegative safe integer.\");\n\tconst segments = splitGraphemes(value, locale);\n\treturn segments.length > maxLength ? segments.slice(0, maxLength).join(\"\") + suffix : value;\n}\n\n/**\n * 把文本复制到系统剪贴板。\n *\n * @remarks uni-app 使用 `setClipboardData`;浏览器优先使用 Clipboard API,并在该 API 不可用时\n * 回退到 `document.execCommand(\"copy\")`。平台拒绝访问剪贴板时不会静默忽略错误。\n * @param value - 要复制的文本\n * @returns 复制完成后兑现的 Promise\n * @throws `Error` 当运行时没有可用的剪贴板能力或复制失败。\n */\nexport async function copy(value: string): Promise<void> {\n\tif (typeof uni !== \"undefined\") {\n\t\tif (uni === null || typeof uni.setClipboardData !== \"function\") {\n\t\t\tthrow new TypeError(\"The global uni object does not provide `setClipboardData`.\");\n\t\t}\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tuni.setClipboardData({\n\t\t\t\tdata: value,\n\t\t\t\tfail: (error: unknown) => {\n\t\t\t\t\treject(error instanceof Error ? error : new Error(\"Failed to copy text to the clipboard.\", { cause: error }));\n\t\t\t\t},\n\t\t\t\tsuccess: resolve,\n\t\t\t});\n\t\t});\n\t\treturn;\n\t}\n\n\tif (\n\t\ttypeof isSecureContext !== \"undefined\" &&\n\t\tisSecureContext &&\n\t\ttypeof navigator !== \"undefined\" &&\n\t\ttypeof navigator.clipboard?.writeText === \"function\"\n\t) {\n\t\tawait navigator.clipboard.writeText(value);\n\t\treturn;\n\t}\n\n\tif (typeof document === \"undefined\" || document === null) {\n\t\tthrow new Error(\"The current runtime does not support clipboard access.\");\n\t}\n\t// eslint-disable-next-line @typescript-eslint/no-deprecated -- 兼容不支持 Clipboard API 的旧 WebView。\n\tif (typeof document.createElement !== \"function\" || document.body == null || typeof document.execCommand !== \"function\") {\n\t\tthrow new Error(\"The current runtime does not support clipboard access.\");\n\t}\n\tconst textarea = document.createElement(\"textarea\");\n\ttextarea.value = value;\n\ttextarea.style.left = \"-999999px\";\n\ttextarea.style.opacity = \"0\";\n\ttextarea.style.position = \"fixed\";\n\ttextarea.style.top = \"-999999px\";\n\tdocument.body.appendChild(textarea);\n\tlet copied: boolean;\n\ttry {\n\t\ttextarea.focus();\n\t\ttextarea.select();\n\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated -- 兼容不支持 Clipboard API 的旧 WebView。\n\t\tcopied = document.execCommand(\"copy\");\n\t} finally {\n\t\ttextarea.remove();\n\t}\n\tif (!copied) throw new Error(\"Failed to copy text to the clipboard.\");\n}\n\n/**\n * 生成随机字符串。\n *\n * @remarks 优先使用 Web Crypto;平台缺少安全随机能力时回退到 `Math.random()`。\n * @param length - 字符数量,必须是 0 至 1,000,000 的安全整数。\n * @param alphabet - 不得为空、包含重复字符或超过 2^32 个 Unicode 码点。\n * @returns 由 `alphabet` 中 Unicode 码点组成的随机文本\n * @throws `RangeError` 当长度或字母表非法。\n */\nexport function randomString(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 between 0 and ${maximumRandomStringLength}.`);\n\t}\n\tconst characters = Array.from(alphabet);\n\tif (characters.length === 0) throw new RangeError(\"`alphabet` must not be empty.\");\n\tif (new Set(characters).size !== characters.length) throw new RangeError(\"`alphabet` must not contain duplicate characters.\");\n\tif (characters.length > 0x1_0000_0000) throw new RangeError(\"`alphabet` must not contain more than 2^32 characters.\");\n\tif (length === 0) return \"\";\n\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 = new Uint32Array(Math.min(remaining, maximumRandomValuesPerBatch));\n\t\tfillRandomValues(samples);\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 * 生成 RFC 4122 version 4 UUID。\n *\n * @remarks 优先使用 Web Crypto;平台缺少安全随机能力时回退到 `Math.random()`。\n * 该 UUID 适合普通唯一标识,不应作为安全令牌或秘密。\n * @returns 小写、带连字符的 UUID v4\n */\nexport function generateUuidV4(): string {\n\tif (typeof crypto !== \"undefined\" && typeof crypto?.randomUUID === \"function\") return crypto.randomUUID();\n\tconst bytes = new Uint8Array(16);\n\tfillRandomValues(bytes);\n\treturn createUuidV4FromBytes(bytes);\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 \"&amp;\";\n\t\t\tcase \"<\":\n\t\t\t\treturn \"&lt;\";\n\t\t\tcase \">\":\n\t\t\t\treturn \"&gt;\";\n\t\t\tcase '\"':\n\t\t\t\treturn \"&quot;\";\n\t\t\tdefault:\n\t\t\t\treturn \"&#39;\";\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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"}
@@ -1,12 +1,11 @@
1
- import { runtimeGlobals } from "../internal/runtime.mjs";
2
1
  import { useEventListener } from "./event-listener.mjs";
3
- import { computed, getCurrentScope, readonly, shallowRef } from "vue";
2
+ import { computed, getCurrentScope, onScopeDispose, readonly, shallowRef } from "vue";
4
3
  //#region src/vue/breakpoints.ts
5
4
  /**
6
5
  * 使用原生 Media Query 创建响应式最小宽度断点。
7
6
  *
8
- * @param breakpoints - 断点名称与非负像素宽度的映射。
9
- * @returns 每个断点的只读状态和当前最大命中断点。
7
+ * @param breakpoints - 断点名称与非负像素宽度的映射
8
+ * @returns 每个断点的只读状态和当前最大命中断点
10
9
  * @throws `Error` 当浏览器环境中不存在可用于自动清理的 Vue 响应式作用域。
11
10
  * @throws `TypeError` 当断点使用保留名称 `active`。
12
11
  * @throws `RangeError` 当断点宽度不是非负有限数值。
@@ -15,21 +14,26 @@ function useBreakpoints(breakpoints) {
15
14
  const entries = Object.entries(breakpoints);
16
15
  entries.sort((left, right) => left[1] - right[1]);
17
16
  for (const [name, minimumWidth] of entries) {
18
- if (name === "active") throw new TypeError("断点名称不能使用保留名称“active”。");
19
- if (!Number.isFinite(minimumWidth) || minimumWidth < 0) throw new RangeError(`断点“${name}”必须是非负有限数值。`);
17
+ if (name === "active") throw new TypeError("The breakpoint name must not be the reserved name `active`.");
18
+ if (!Number.isFinite(minimumWidth) || minimumWidth < 0) throw new RangeError(`Breakpoint "${name}" must be a nonnegative finite number.`);
20
19
  }
21
20
  const states = Object.create(null);
22
- const window = runtimeGlobals.window;
23
- if (window !== void 0 && getCurrentScope() === void 0) throw new Error("`useBreakpoints` 必须在 Vue 响应式作用域内调用。");
21
+ if (typeof window !== "undefined" && getCurrentScope() === void 0) throw new Error("`useBreakpoints` must be called within a Vue reactive scope.");
24
22
  for (const [name, minimumWidth] of entries) {
25
23
  const state = shallowRef(false);
26
- if (window !== void 0 && typeof window.matchMedia === "function") {
24
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
27
25
  const mediaQuery = window.matchMedia(`(min-width: ${minimumWidth}px)`);
28
26
  const update = () => {
29
27
  state.value = mediaQuery.matches;
30
28
  };
31
29
  update();
32
- useEventListener(mediaQuery, "change", update);
30
+ if (typeof mediaQuery.addEventListener === "function") useEventListener(mediaQuery, "change", update);
31
+ else {
32
+ mediaQuery.addListener(update);
33
+ onScopeDispose(() => {
34
+ mediaQuery.removeListener(update);
35
+ });
36
+ }
33
37
  }
34
38
  states[name] = readonly(state);
35
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"breakpoints.mjs","names":[],"sources":["../../src/vue/breakpoints.ts"],"sourcesContent":["import { computed, getCurrentScope, readonly, shallowRef } from \"vue\";\nimport { runtimeGlobals } from \"../internal/runtime\";\nimport { useEventListener } from \"./event-listener\";\nimport type { ComputedRef, ShallowRef } from \"vue\";\n\n/** 断点名称与最小视口宽度的映射。 */\nexport type Breakpoints<Key extends string = string> = Readonly<Record<Key, number>>;\n\n/** `useBreakpoints` 返回的断点状态。 */\nexport type UseBreakpointsReturn<Key extends string> = Readonly<Record<Key, Readonly<ShallowRef<boolean>>>> & {\n\t/** 返回当前命中的最大断点名称。 */\n\tactive: () => ComputedRef<Key | \"\">;\n};\n\n/**\n * 使用原生 Media Query 创建响应式最小宽度断点。\n *\n * @param breakpoints - 断点名称与非负像素宽度的映射。\n * @returns 每个断点的只读状态和当前最大命中断点。\n * @throws `Error` 当浏览器环境中不存在可用于自动清理的 Vue 响应式作用域。\n * @throws `TypeError` 当断点使用保留名称 `active`。\n * @throws `RangeError` 当断点宽度不是非负有限数值。\n */\nexport function useBreakpoints<Key extends string>(breakpoints: Breakpoints<Key>): UseBreakpointsReturn<Key> {\n\tconst entries = Object.entries(breakpoints) as [Key, number][];\n\tentries.sort((left, right) => left[1] - right[1]);\n\tfor (const [name, minimumWidth] of entries) {\n\t\tif (name === \"active\") throw new TypeError(\"断点名称不能使用保留名称“active”。\");\n\t\tif (!Number.isFinite(minimumWidth) || minimumWidth < 0) throw new RangeError(`断点“${name}”必须是非负有限数值。`);\n\t}\n\tconst states = Object.create(null) as Record<Key, Readonly<ShallowRef<boolean>>>;\n\tconst window = runtimeGlobals.window;\n\tif (window !== undefined && getCurrentScope() === undefined) {\n\t\tthrow new Error(\"`useBreakpoints` 必须在 Vue 响应式作用域内调用。\");\n\t}\n\tfor (const [name, minimumWidth] of entries) {\n\t\tconst state = shallowRef(false);\n\t\tif (window !== undefined && typeof window.matchMedia === \"function\") {\n\t\t\tconst mediaQuery = window.matchMedia(`(min-width: ${minimumWidth}px)`);\n\t\t\tconst update = () => {\n\t\t\t\tstate.value = mediaQuery.matches;\n\t\t\t};\n\t\t\tupdate();\n\t\t\tuseEventListener(mediaQuery, \"change\", update);\n\t\t}\n\t\tstates[name] = readonly(state);\n\t}\n\tconst active = computed<Key | \"\">(() => {\n\t\tlet current: Key | \"\" = \"\";\n\t\tfor (const [name] of entries) {\n\t\t\tif (states[name].value) current = name;\n\t\t}\n\t\treturn current;\n\t});\n\treturn Object.assign(states, { active: () => active });\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,SAAgB,eAAmC,aAA0D;CAC5G,MAAM,UAAU,OAAO,QAAQ,WAAW;CAC1C,QAAQ,MAAM,MAAM,UAAU,KAAK,KAAK,MAAM,EAAE;CAChD,KAAK,MAAM,CAAC,MAAM,iBAAiB,SAAS;EAC3C,IAAI,SAAS,UAAU,MAAM,IAAI,UAAU,uBAAuB;EAClE,IAAI,CAAC,OAAO,SAAS,YAAY,KAAK,eAAe,GAAG,MAAM,IAAI,WAAW,MAAM,KAAK,YAAY;CACrG;CACA,MAAM,SAAS,OAAO,OAAO,IAAI;CACjC,MAAM,SAAS,eAAe;CAC9B,IAAI,WAAW,KAAA,KAAa,gBAAgB,MAAM,KAAA,GACjD,MAAM,IAAI,MAAM,qCAAqC;CAEtD,KAAK,MAAM,CAAC,MAAM,iBAAiB,SAAS;EAC3C,MAAM,QAAQ,WAAW,KAAK;EAC9B,IAAI,WAAW,KAAA,KAAa,OAAO,OAAO,eAAe,YAAY;GACpE,MAAM,aAAa,OAAO,WAAW,eAAe,aAAa,IAAI;GACrE,MAAM,eAAe;IACpB,MAAM,QAAQ,WAAW;GAC1B;GACA,OAAO;GACP,iBAAiB,YAAY,UAAU,MAAM;EAC9C;EACA,OAAO,QAAQ,SAAS,KAAK;CAC9B;CACA,MAAM,SAAS,eAAyB;EACvC,IAAI,UAAoB;EACxB,KAAK,MAAM,CAAC,SAAS,SACpB,IAAI,OAAO,KAAK,CAAC,OAAO,UAAU;EAEnC,OAAO;CACR,CAAC;CACD,OAAO,OAAO,OAAO,QAAQ,EAAE,cAAc,OAAO,CAAC;AACtD"}
1
+ {"version":3,"file":"breakpoints.mjs","names":[],"sources":["../../src/vue/breakpoints.ts"],"sourcesContent":["import { computed, getCurrentScope, onScopeDispose, readonly, shallowRef } from \"vue\";\nimport { useEventListener } from \"./event-listener\";\nimport type { ComputedRef, ShallowRef } from \"vue\";\n\n/** 断点名称与最小视口宽度的映射 */\nexport type Breakpoints<Key extends string = string> = Readonly<Record<Key, number>>;\n\n/** `useBreakpoints` 返回的断点状态。 */\nexport type UseBreakpointsReturn<Key extends string> = Readonly<Record<Key, Readonly<ShallowRef<boolean>>>> & {\n\t/** 返回当前命中的最大断点名称。 */\n\tactive: () => ComputedRef<Key | \"\">;\n};\n\n/**\n * 使用原生 Media Query 创建响应式最小宽度断点。\n *\n * @param breakpoints - 断点名称与非负像素宽度的映射\n * @returns 每个断点的只读状态和当前最大命中断点\n * @throws `Error` 当浏览器环境中不存在可用于自动清理的 Vue 响应式作用域。\n * @throws `TypeError` 当断点使用保留名称 `active`。\n * @throws `RangeError` 当断点宽度不是非负有限数值。\n */\nexport function useBreakpoints<Key extends string>(breakpoints: Breakpoints<Key>): UseBreakpointsReturn<Key> {\n\tconst entries = Object.entries(breakpoints) as [Key, number][];\n\tentries.sort((left, right) => left[1] - right[1]);\n\tfor (const [name, minimumWidth] of entries) {\n\t\tif (name === \"active\") throw new TypeError(\"The breakpoint name must not be the reserved name `active`.\");\n\t\tif (!Number.isFinite(minimumWidth) || minimumWidth < 0) throw new RangeError(`Breakpoint \"${name}\" must be a nonnegative finite number.`);\n\t}\n\tconst states = Object.create(null) as Record<Key, Readonly<ShallowRef<boolean>>>;\n\tif (typeof window !== \"undefined\" && getCurrentScope() === undefined) {\n\t\tthrow new Error(\"`useBreakpoints` must be called within a Vue reactive scope.\");\n\t}\n\tfor (const [name, minimumWidth] of entries) {\n\t\tconst state = shallowRef(false);\n\t\tif (typeof window !== \"undefined\" && typeof window.matchMedia === \"function\") {\n\t\t\tconst mediaQuery = window.matchMedia(`(min-width: ${minimumWidth}px)`);\n\t\t\tconst update = () => {\n\t\t\t\tstate.value = mediaQuery.matches;\n\t\t\t};\n\t\t\tupdate();\n\t\t\tif (typeof mediaQuery.addEventListener === \"function\") {\n\t\t\t\tuseEventListener(mediaQuery, \"change\", update);\n\t\t\t} else {\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated -- 兼容旧 WebView 的 MediaQueryList\n\t\t\t\tmediaQuery.addListener(update);\n\t\t\t\tonScopeDispose(() => {\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-deprecated -- 与旧式监听配对清理\n\t\t\t\t\tmediaQuery.removeListener(update);\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tstates[name] = readonly(state);\n\t}\n\tconst active = computed<Key | \"\">(() => {\n\t\tlet current: Key | \"\" = \"\";\n\t\tfor (const [name] of entries) {\n\t\t\tif (states[name].value) current = name;\n\t\t}\n\t\treturn current;\n\t});\n\treturn Object.assign(states, { active: () => active });\n}\n"],"mappings":";;;;;;;;;;;;AAsBA,SAAgB,eAAmC,aAA0D;CAC5G,MAAM,UAAU,OAAO,QAAQ,WAAW;CAC1C,QAAQ,MAAM,MAAM,UAAU,KAAK,KAAK,MAAM,EAAE;CAChD,KAAK,MAAM,CAAC,MAAM,iBAAiB,SAAS;EAC3C,IAAI,SAAS,UAAU,MAAM,IAAI,UAAU,6DAA6D;EACxG,IAAI,CAAC,OAAO,SAAS,YAAY,KAAK,eAAe,GAAG,MAAM,IAAI,WAAW,eAAe,KAAK,uCAAuC;CACzI;CACA,MAAM,SAAS,OAAO,OAAO,IAAI;CACjC,IAAI,OAAO,WAAW,eAAe,gBAAgB,MAAM,KAAA,GAC1D,MAAM,IAAI,MAAM,8DAA8D;CAE/E,KAAK,MAAM,CAAC,MAAM,iBAAiB,SAAS;EAC3C,MAAM,QAAQ,WAAW,KAAK;EAC9B,IAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;GAC7E,MAAM,aAAa,OAAO,WAAW,eAAe,aAAa,IAAI;GACrE,MAAM,eAAe;IACpB,MAAM,QAAQ,WAAW;GAC1B;GACA,OAAO;GACP,IAAI,OAAO,WAAW,qBAAqB,YAC1C,iBAAiB,YAAY,UAAU,MAAM;QACvC;IAEN,WAAW,YAAY,MAAM;IAC7B,qBAAqB;KAEpB,WAAW,eAAe,MAAM;IACjC,CAAC;GACF;EACD;EACA,OAAO,QAAQ,SAAS,KAAK;CAC9B;CACA,MAAM,SAAS,eAAyB;EACvC,IAAI,UAAoB;EACxB,KAAK,MAAM,CAAC,SAAS,SACpB,IAAI,OAAO,KAAK,CAAC,OAAO,UAAU;EAEnC,OAAO;CACR,CAAC;CACD,OAAO,OAAO,OAAO,QAAQ,EAAE,cAAc,OAAO,CAAC;AACtD"}
@@ -4,10 +4,10 @@ import { readonly, shallowRef } from "vue";
4
4
  /**
5
5
  * 响应式读取元素 Content Rect 尺寸。
6
6
  *
7
- * @param target - 原生元素、Ref 或 Getter。
8
- * @param initialSize - 收到首次观察结果前的尺寸,默认均为 `0`。
9
- * @param options - 原生元素观察选项。
10
- * @returns 只读宽度、高度和手动停止函数。
7
+ * @param target - 原生元素、Ref 或 Getter
8
+ * @param initialSize - 收到首次观察结果前的尺寸,默认均为 `0`
9
+ * @param options - 原生元素观察选项
10
+ * @returns 只读宽度、高度和手动停止函数
11
11
  */
12
12
  function useElementSize(target, initialSize = {
13
13
  height: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"element-size.mjs","names":[],"sources":["../../src/vue/element-size.ts"],"sourcesContent":["import { readonly, shallowRef } from \"vue\";\nimport { useResizeObserver } from \"./resize-observer\";\nimport type { ShallowRef } from \"vue\";\nimport type { ResizeObserverTarget } from \"./resize-observer\";\n\n/** 元素的二维尺寸。 */\nexport interface ElementSize {\n\treadonly width: number;\n\treadonly height: number;\n}\n\n/** `useElementSize` 返回的响应式尺寸和停止函数。 */\nexport interface UseElementSizeReturn {\n\treadonly width: Readonly<ShallowRef<number>>;\n\treadonly height: Readonly<ShallowRef<number>>;\n\treadonly stop: () => void;\n}\n\n/**\n * 响应式读取元素 Content Rect 尺寸。\n *\n * @param target - 原生元素、Ref 或 Getter。\n * @param initialSize - 收到首次观察结果前的尺寸,默认均为 `0`。\n * @param options - 原生元素观察选项。\n * @returns 只读宽度、高度和手动停止函数。\n */\nexport function useElementSize(\n\ttarget: ResizeObserverTarget,\n\tinitialSize: ElementSize = { height: 0, width: 0 },\n\toptions?: ResizeObserverOptions\n): UseElementSizeReturn {\n\tconst width = shallowRef(initialSize.width);\n\tconst height = shallowRef(initialSize.height);\n\tconst stop = useResizeObserver(\n\t\ttarget,\n\t\t(entries) => {\n\t\t\tconst entry = entries[0];\n\t\t\tif (entry === undefined) return;\n\t\t\twidth.value = entry.contentRect.width;\n\t\t\theight.value = entry.contentRect.height;\n\t\t},\n\t\toptions\n\t);\n\treturn { height: readonly(height), stop, width: readonly(width) };\n}\n"],"mappings":";;;;;;;;;;;AA0BA,SAAgB,eACf,QACA,cAA2B;CAAE,QAAQ;CAAG,OAAO;AAAE,GACjD,SACuB;CACvB,MAAM,QAAQ,WAAW,YAAY,KAAK;CAC1C,MAAM,SAAS,WAAW,YAAY,MAAM;CAC5C,MAAM,OAAO,kBACZ,SACC,YAAY;EACZ,MAAM,QAAQ,QAAQ;EACtB,IAAI,UAAU,KAAA,GAAW;EACzB,MAAM,QAAQ,MAAM,YAAY;EAChC,OAAO,QAAQ,MAAM,YAAY;CAClC,GACA,OACD;CACA,OAAO;EAAE,QAAQ,SAAS,MAAM;EAAG;EAAM,OAAO,SAAS,KAAK;CAAE;AACjE"}
1
+ {"version":3,"file":"element-size.mjs","names":[],"sources":["../../src/vue/element-size.ts"],"sourcesContent":["import { readonly, shallowRef } from \"vue\";\nimport { useResizeObserver } from \"./resize-observer\";\nimport type { ShallowRef } from \"vue\";\nimport type { ResizeObserverTarget } from \"./resize-observer\";\n\n/** 元素的二维尺寸 */\nexport interface ElementSize {\n\treadonly width: number;\n\treadonly height: number;\n}\n\n/** `useElementSize` 返回的响应式尺寸和停止函数。 */\nexport interface UseElementSizeReturn {\n\treadonly width: Readonly<ShallowRef<number>>;\n\treadonly height: Readonly<ShallowRef<number>>;\n\treadonly stop: () => void;\n}\n\n/**\n * 响应式读取元素 Content Rect 尺寸。\n *\n * @param target - 原生元素、Ref 或 Getter\n * @param initialSize - 收到首次观察结果前的尺寸,默认均为 `0`\n * @param options - 原生元素观察选项\n * @returns 只读宽度、高度和手动停止函数\n */\nexport function useElementSize(\n\ttarget: ResizeObserverTarget,\n\tinitialSize: ElementSize = { height: 0, width: 0 },\n\toptions?: ResizeObserverOptions\n): UseElementSizeReturn {\n\tconst width = shallowRef(initialSize.width);\n\tconst height = shallowRef(initialSize.height);\n\tconst stop = useResizeObserver(\n\t\ttarget,\n\t\t(entries) => {\n\t\t\tconst entry = entries[0];\n\t\t\tif (entry === undefined) return;\n\t\t\twidth.value = entry.contentRect.width;\n\t\t\theight.value = entry.contentRect.height;\n\t\t},\n\t\toptions\n\t);\n\treturn { height: readonly(height), stop, width: readonly(width) };\n}\n"],"mappings":";;;;;;;;;;;AA0BA,SAAgB,eACf,QACA,cAA2B;CAAE,QAAQ;CAAG,OAAO;AAAE,GACjD,SACuB;CACvB,MAAM,QAAQ,WAAW,YAAY,KAAK;CAC1C,MAAM,SAAS,WAAW,YAAY,MAAM;CAC5C,MAAM,OAAO,kBACZ,SACC,YAAY;EACZ,MAAM,QAAQ,QAAQ;EACtB,IAAI,UAAU,KAAA,GAAW;EACzB,MAAM,QAAQ,MAAM,YAAY;EAChC,OAAO,QAAQ,MAAM,YAAY;CAClC,GACA,OACD;CACA,OAAO;EAAE,QAAQ,SAAS,MAAM;EAAG;EAAM,OAAO,SAAS,KAAK;CAAE;AACjE"}
@@ -4,7 +4,7 @@ import { computed } from "vue";
4
4
  * 把事件名转换为 Vue Handler Prop 名称。
5
5
  *
6
6
  * @param eventName - Emits 对象中的原始事件名,可使用 kebab-case。
7
- * @returns `onPascalCase` 形式的属性名。
7
+ * @returns `onPascalCase` 形式的属性名
8
8
  * @throws `TypeError` 当事件名包含空片段或无法生成有效 Handler 名称。
9
9
  */
10
10
  const toHandlerName = (eventName) => {
@@ -13,10 +13,10 @@ const toHandlerName = (eventName) => {
13
13
  /**
14
14
  * 构建响应式 Vue 事件处理器。
15
15
  *
16
- * @param emits - Vue emits 配置对象。
17
- * @param emit - `setup` 上下文提供的 emit 函数。
18
- * @param ignoredEvents - 不需要向子组件透传的事件名。
19
- * @returns 随配置重新计算的事件处理器对象。
16
+ * @param emits - Vue emits 配置对象
17
+ * @param emit - `setup` 上下文提供的 emit 函数
18
+ * @param ignoredEvents - 不需要向子组件透传的事件名
19
+ * @returns 随配置重新计算的事件处理器对象
20
20
  */
21
21
  function useEmits(emits, emit, ignoredEvents = []) {
22
22
  const ignored = new Set(ignoredEvents);
@@ -26,9 +26,9 @@ function useEmits(emits, emit, ignoredEvents = []) {
26
26
  const handlerNames = /* @__PURE__ */ new Set();
27
27
  for (const eventName of Object.keys(emits)) {
28
28
  if (ignored.has(eventName)) continue;
29
- if (eventName.length === 0 || /\s/u.test(eventName)) throw new TypeError(`无效的 Vue 事件名称:“${eventName}”。`);
29
+ if (eventName.length === 0 || /\s/u.test(eventName)) throw new TypeError(`Invalid Vue event name: "${eventName}".`);
30
30
  const handlerName = toHandlerName(eventName);
31
- if (handlerNames.has(handlerName)) throw new TypeError(`多个 Vue 事件映射到同一处理器属性:“${handlerName}”。`);
31
+ if (handlerNames.has(handlerName)) throw new TypeError(`Multiple Vue events map to the same handler property: "${handlerName}".`);
32
32
  handlerNames.add(handlerName);
33
33
  Object.defineProperty(handlers, handlerName, {
34
34
  enumerable: true,
@@ -1 +1 @@
1
- {"version":3,"file":"emits.mjs","names":[],"sources":["../../src/vue/emits.ts"],"sourcesContent":["import { computed } from \"vue\";\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 (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(`无效的 Vue 事件名称:“${eventName}”。`);\n\t\t\tconst handlerName = toHandlerName(eventName);\n\t\t\tif (handlerNames.has(handlerName)) {\n\t\t\t\tthrow new TypeError(`多个 Vue 事件映射到同一处理器属性:“${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[]) => {\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":";;;;;;;;;AA0BA,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,iBAAiB,UAAU,GAAG;GACvG,MAAM,cAAc,cAAc,SAAS;GAC3C,IAAI,aAAa,IAAI,WAAW,GAC/B,MAAM,IAAI,UAAU,wBAAwB,YAAY,GAAG;GAE5D,aAAa,IAAI,WAAW;GAC5B,OAAO,eAAe,UAAU,aAAa;IAC5C,YAAY;IACZ,QAAQ,GAAG,eAA0B;KACpC,UAAU,WAAW,GAAG,UAAU;IACnC;IACA,UAAU;GACX,CAAC;EACF;EACA,OAAO;CACR,CAAC;AACF"}
1
+ {"version":3,"file":"emits.mjs","names":[],"sources":["../../src/vue/emits.ts"],"sourcesContent":["import { computed } from \"vue\";\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 (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(`Multiple 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[]) => {\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":";;;;;;;;;AA0BA,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,0DAA0D,YAAY,GAAG;GAE9F,aAAa,IAAI,WAAW;GAC5B,OAAO,eAAe,UAAU,aAAa;IAC5C,YAAY;IACZ,QAAQ,GAAG,eAA0B;KACpC,UAAU,WAAW,GAAG,UAAU;IACnC;IACA,UAAU;GACX,CAAC;EACF;EACA,OAAO;CACR,CAAC;AACF"}
@@ -3,11 +3,11 @@ import { onScopeDispose, toValue, watch } from "vue";
3
3
  /**
4
4
  * 注册原生事件监听器,并在目标变化或 Vue 作用域销毁时自动移除。
5
5
  *
6
- * @param target - 原生事件目标、Ref 或 Getter。
7
- * @param event - 原生事件名称。
8
- * @param listener - 事件回调。
9
- * @param options - 原生事件监听选项。
10
- * @returns 可提前移除监听器的停止函数。
6
+ * @param target - 原生事件目标、Ref 或 Getter
7
+ * @param event - 原生事件名称
8
+ * @param listener - 事件回调
9
+ * @param options - 原生事件监听选项
10
+ * @returns 可提前移除监听器的停止函数
11
11
  */
12
12
  function useEventListener(target, event, listener, options) {
13
13
  const eventListener = listener;
@@ -1 +1 @@
1
- {"version":3,"file":"event-listener.mjs","names":[],"sources":["../../src/vue/event-listener.ts"],"sourcesContent":["import { onScopeDispose, toValue, watch } from \"vue\";\nimport type { MaybeRefOrGetter } from \"vue\";\n\n/** `useEventListener` 接受的原生事件目标或响应式事件目标。 */\nexport type EventTargetSource = MaybeRefOrGetter<EventTarget | null | undefined>;\n\n/**\n * 注册原生事件监听器,并在目标变化或 Vue 作用域销毁时自动移除。\n *\n * @param target - 原生事件目标、Ref 或 Getter。\n * @param event - 原生事件名称。\n * @param listener - 事件回调。\n * @param options - 原生事件监听选项。\n * @returns 可提前移除监听器的停止函数。\n */\nexport function useEventListener<EventType extends Event = Event>(\n\ttarget: EventTargetSource,\n\tevent: string,\n\tlistener: (event: EventType) => void,\n\toptions?: boolean | AddEventListenerOptions\n): () => void {\n\tconst eventListener = listener as EventListener;\n\tconst stop = watch(\n\t\t() => toValue(target),\n\t\t(currentTarget, _previousTarget, onCleanup) => {\n\t\t\tif (currentTarget === null || currentTarget === undefined) return;\n\t\t\tcurrentTarget.addEventListener(event, eventListener, options);\n\t\t\tonCleanup(() => {\n\t\t\t\tcurrentTarget.removeEventListener(event, eventListener, options);\n\t\t\t});\n\t\t},\n\t\t{ flush: \"sync\", immediate: true }\n\t);\n\tonScopeDispose(stop, true);\n\treturn stop;\n}\n"],"mappings":";;;;;;;;;;;AAeA,SAAgB,iBACf,QACA,OACA,UACA,SACa;CACb,MAAM,gBAAgB;CACtB,MAAM,OAAO,YACN,QAAQ,MAAM,IACnB,eAAe,iBAAiB,cAAc;EAC9C,IAAI,kBAAkB,QAAQ,kBAAkB,KAAA,GAAW;EAC3D,cAAc,iBAAiB,OAAO,eAAe,OAAO;EAC5D,gBAAgB;GACf,cAAc,oBAAoB,OAAO,eAAe,OAAO;EAChE,CAAC;CACF,GACA;EAAE,OAAO;EAAQ,WAAW;CAAK,CAClC;CACA,eAAe,MAAM,IAAI;CACzB,OAAO;AACR"}
1
+ {"version":3,"file":"event-listener.mjs","names":[],"sources":["../../src/vue/event-listener.ts"],"sourcesContent":["import { onScopeDispose, toValue, watch } from \"vue\";\nimport type { MaybeRefOrGetter } from \"vue\";\n\n/** `useEventListener` 接受的原生事件目标或响应式事件目标 */\nexport type EventTargetSource = MaybeRefOrGetter<EventTarget | null | undefined>;\n\n/**\n * 注册原生事件监听器,并在目标变化或 Vue 作用域销毁时自动移除。\n *\n * @param target - 原生事件目标、Ref 或 Getter\n * @param event - 原生事件名称\n * @param listener - 事件回调\n * @param options - 原生事件监听选项\n * @returns 可提前移除监听器的停止函数\n */\nexport function useEventListener<EventType extends Event = Event>(\n\ttarget: EventTargetSource,\n\tevent: string,\n\tlistener: (event: EventType) => void,\n\toptions?: boolean | AddEventListenerOptions\n): () => void {\n\tconst eventListener = listener as EventListener;\n\tconst stop = watch(\n\t\t() => toValue(target),\n\t\t(currentTarget, _previousTarget, onCleanup) => {\n\t\t\tif (currentTarget === null || currentTarget === undefined) return;\n\t\t\tcurrentTarget.addEventListener(event, eventListener, options);\n\t\t\tonCleanup(() => {\n\t\t\t\tcurrentTarget.removeEventListener(event, eventListener, options);\n\t\t\t});\n\t\t},\n\t\t{ flush: \"sync\", immediate: true }\n\t);\n\tonScopeDispose(stop, true);\n\treturn stop;\n}\n"],"mappings":";;;;;;;;;;;AAeA,SAAgB,iBACf,QACA,OACA,UACA,SACa;CACb,MAAM,gBAAgB;CACtB,MAAM,OAAO,YACN,QAAQ,MAAM,IACnB,eAAe,iBAAiB,cAAc;EAC9C,IAAI,kBAAkB,QAAQ,kBAAkB,KAAA,GAAW;EAC3D,cAAc,iBAAiB,OAAO,eAAe,OAAO;EAC5D,gBAAgB;GACf,cAAc,oBAAoB,OAAO,eAAe,OAAO;EAChE,CAAC;CACF,GACA;EAAE,OAAO;EAAQ,WAAW;CAAK,CAClC;CACA,eAAe,MAAM,IAAI;CACzB,OAAO;AACR"}
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * 同时暴露组件实例能力并返回同一个对象,便于 `setup` 返回状态供 Vue Devtools 查看。
4
4
  *
5
- * @param expose - `setup` 上下文提供的 expose 函数。
6
- * @param exposed - 需要暴露的状态和方法。
7
- * @returns 原始 exposed 对象。
5
+ * @param expose - `setup` 上下文提供的 expose 函数
6
+ * @param exposed - 需要暴露的状态和方法
7
+ * @returns 原始 exposed 对象
8
8
  */
9
9
  function useExpose(expose, exposed) {
10
10
  expose(exposed);
@@ -1 +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"}
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"}