a2bei4-utils 1.0.9 → 1.0.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 (44) hide show
  1. package/dist/a2bei4.utils.cjs.js +193 -2
  2. package/dist/a2bei4.utils.cjs.js.map +1 -1
  3. package/dist/a2bei4.utils.cjs.min.js +1 -1
  4. package/dist/a2bei4.utils.cjs.min.js.map +1 -1
  5. package/dist/a2bei4.utils.esm.js +191 -2
  6. package/dist/a2bei4.utils.esm.js.map +1 -1
  7. package/dist/a2bei4.utils.esm.min.js +1 -1
  8. package/dist/a2bei4.utils.esm.min.js.map +1 -1
  9. package/dist/a2bei4.utils.umd.js +193 -2
  10. package/dist/a2bei4.utils.umd.js.map +1 -1
  11. package/dist/a2bei4.utils.umd.min.js +1 -1
  12. package/dist/a2bei4.utils.umd.min.js.map +1 -1
  13. package/dist/arr.cjs.map +1 -1
  14. package/dist/arr.js.map +1 -1
  15. package/dist/audio.cjs.map +1 -1
  16. package/dist/audio.js.map +1 -1
  17. package/dist/browser.cjs.map +1 -1
  18. package/dist/browser.js.map +1 -1
  19. package/dist/common.cjs.map +1 -1
  20. package/dist/common.js.map +1 -1
  21. package/dist/date.cjs.map +1 -1
  22. package/dist/date.js.map +1 -1
  23. package/dist/download.cjs.map +1 -1
  24. package/dist/download.js.map +1 -1
  25. package/dist/evt.cjs.map +1 -1
  26. package/dist/evt.js.map +1 -1
  27. package/dist/id.cjs.map +1 -1
  28. package/dist/id.js.map +1 -1
  29. package/dist/menu.cjs.map +1 -1
  30. package/dist/menu.js.map +1 -1
  31. package/dist/timer.cjs +14 -0
  32. package/dist/timer.cjs.map +1 -1
  33. package/dist/timer.js +14 -0
  34. package/dist/timer.js.map +1 -1
  35. package/dist/tree.cjs +177 -0
  36. package/dist/tree.cjs.map +1 -1
  37. package/dist/tree.js +176 -1
  38. package/dist/tree.js.map +1 -1
  39. package/dist/webSocket.cjs.map +1 -1
  40. package/dist/webSocket.js.map +1 -1
  41. package/package.json +5 -5
  42. package/types/index.d.ts +186 -2
  43. package/types/timer.d.ts +4 -0
  44. package/types/tree.d.ts +182 -1
package/dist/arr.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"arr.cjs","sources":["../src/source/arr.js"],"sourcesContent":["/**\r\n * 使用 Fisher-Yates 算法对数组 **原地** 随机乱序。\r\n * @template T\r\n * @param {T[]} arr - 要乱序的数组\r\n * @returns {T[]} 返回传入的同一数组实例(已乱序)\r\n */\r\nexport function shuffle(arr) {\r\n // 方式一:\r\n // arr.sort(() => Math.random() - 0.5);\r\n // 方式二:\r\n for (let i = arr.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1));\r\n [arr[i], arr[j]] = [arr[j], arr[i]];\r\n }\r\n return arr;\r\n}\r\n\r\n/**\r\n * 将数组中的元素从 `fromIndex` 移动到 `toIndex`,**原地** 修改并返回该数组。\r\n * @template T\r\n * @param {T[]} arr - 要操作的数组\r\n * @param {number} fromIndex - 原始下标\r\n * @param {number} toIndex - 目标下标\r\n * @returns {T[]} 返回传入的同一数组实例\r\n */\r\nexport function moveItem(arr, fromIndex, toIndex) {\r\n arr.splice(toIndex, 0, arr.splice(fromIndex, 1)[0]);\r\n}\r\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,OAAO,CAAC,GAAG,EAAE;AAC7B;AACA;AACA;AACA,IAAI,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;AAC7C,QAAQ,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACtD,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5C,IAAI,CAAC;AACL,IAAI,OAAO,GAAG,CAAC;AACf,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE;AAClD,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD;;;;;"}
1
+ {"version":3,"file":"arr.cjs","sources":["../src/source/arr.js"],"sourcesContent":["/**\r\n * 使用 Fisher-Yates 算法对数组 **原地** 随机乱序。\r\n * @template T\r\n * @param {T[]} arr - 要乱序的数组\r\n * @returns {T[]} 返回传入的同一数组实例(已乱序)\r\n */\r\nexport function shuffle(arr) {\r\n // 方式一:\r\n // arr.sort(() => Math.random() - 0.5);\r\n // 方式二:\r\n for (let i = arr.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1));\r\n [arr[i], arr[j]] = [arr[j], arr[i]];\r\n }\r\n return arr;\r\n}\r\n\r\n/**\r\n * 将数组中的元素从 `fromIndex` 移动到 `toIndex`,**原地** 修改并返回该数组。\r\n * @template T\r\n * @param {T[]} arr - 要操作的数组\r\n * @param {number} fromIndex - 原始下标\r\n * @param {number} toIndex - 目标下标\r\n * @returns {T[]} 返回传入的同一数组实例\r\n */\r\nexport function moveItem(arr, fromIndex, toIndex) {\r\n arr.splice(toIndex, 0, arr.splice(fromIndex, 1)[0]);\r\n}\r\n"],"names":[],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,OAAO,CAAC,GAAG,EAAE;AAC7B;AACA;AACA;AACA,IAAI,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;AAC7C,QAAQ,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACtD,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5C,IAAI,CAAC;AACL,IAAI,OAAO,GAAG,CAAC;AACf,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE;AAClD,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD;;;"}
package/dist/arr.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"arr.js","sources":["../src/source/arr.js"],"sourcesContent":["/**\r\n * 使用 Fisher-Yates 算法对数组 **原地** 随机乱序。\r\n * @template T\r\n * @param {T[]} arr - 要乱序的数组\r\n * @returns {T[]} 返回传入的同一数组实例(已乱序)\r\n */\r\nexport function shuffle(arr) {\r\n // 方式一:\r\n // arr.sort(() => Math.random() - 0.5);\r\n // 方式二:\r\n for (let i = arr.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1));\r\n [arr[i], arr[j]] = [arr[j], arr[i]];\r\n }\r\n return arr;\r\n}\r\n\r\n/**\r\n * 将数组中的元素从 `fromIndex` 移动到 `toIndex`,**原地** 修改并返回该数组。\r\n * @template T\r\n * @param {T[]} arr - 要操作的数组\r\n * @param {number} fromIndex - 原始下标\r\n * @param {number} toIndex - 目标下标\r\n * @returns {T[]} 返回传入的同一数组实例\r\n */\r\nexport function moveItem(arr, fromIndex, toIndex) {\r\n arr.splice(toIndex, 0, arr.splice(fromIndex, 1)[0]);\r\n}\r\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,OAAO,CAAC,GAAG,EAAE;AAC7B;AACA;AACA;AACA,IAAI,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;AAC7C,QAAQ,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACtD,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5C,IAAI,CAAC;AACL,IAAI,OAAO,GAAG,CAAC;AACf,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE;AAClD,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD;;;;"}
1
+ {"version":3,"file":"arr.js","sources":["../src/source/arr.js"],"sourcesContent":["/**\r\n * 使用 Fisher-Yates 算法对数组 **原地** 随机乱序。\r\n * @template T\r\n * @param {T[]} arr - 要乱序的数组\r\n * @returns {T[]} 返回传入的同一数组实例(已乱序)\r\n */\r\nexport function shuffle(arr) {\r\n // 方式一:\r\n // arr.sort(() => Math.random() - 0.5);\r\n // 方式二:\r\n for (let i = arr.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1));\r\n [arr[i], arr[j]] = [arr[j], arr[i]];\r\n }\r\n return arr;\r\n}\r\n\r\n/**\r\n * 将数组中的元素从 `fromIndex` 移动到 `toIndex`,**原地** 修改并返回该数组。\r\n * @template T\r\n * @param {T[]} arr - 要操作的数组\r\n * @param {number} fromIndex - 原始下标\r\n * @param {number} toIndex - 目标下标\r\n * @returns {T[]} 返回传入的同一数组实例\r\n */\r\nexport function moveItem(arr, fromIndex, toIndex) {\r\n arr.splice(toIndex, 0, arr.splice(fromIndex, 1)[0]);\r\n}\r\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,OAAO,CAAC,GAAG,EAAE;AAC7B;AACA;AACA;AACA,IAAI,KAAK,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE;AAC7C,QAAQ,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACtD,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5C,IAAI,CAAC;AACL,IAAI,OAAO,GAAG,CAAC;AACf,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE;AAClD,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"audio.cjs","sources":["../src/source/audio.js"],"sourcesContent":["const AudioStreamResamplerProcessorCode = `\r\nclass AudioStreamResamplerProcessor extends AudioWorkletProcessor {\r\n constructor(options) {\r\n super();\r\n const config = options.processorOptions || {};\r\n this.targetSampleRate = config.targetSampleRate || 16000;\r\n this.sourceSampleRate = sampleRate;\r\n this.downsampleRatio = this.sourceSampleRate / this.targetSampleRate;\r\n\r\n // 16000Hz 用 60ms chunk (960 samples),其他用 1024\r\n this.chunkSize = this.targetSampleRate === 16000 ? 960 : 1024;\r\n\r\n const sourceBufferSize = config.sourceBufferSize || 16384; // ~340ms @48kHz\r\n const pcmBufferSize = config.pcmBufferSize || (this.chunkSize * 10); // 更大缓冲,减少溢出概率\r\n\r\n this.sourceBuffer = new Float32Array(sourceBufferSize);\r\n this.sourceBufferLength = 0;\r\n\r\n this.pcmBuffer = new Int16Array(pcmBufferSize);\r\n this.pcmBufferIndex = 0;\r\n }\r\n\r\n process(inputs, outputs, parameters) {\r\n const input = inputs[0];\r\n if (!input || input.length === 0 || input[0].length === 0) return true;\r\n const inputChannel = input[0];\r\n\r\n // 1. 写入源缓冲区(溢出时覆盖最旧数据)\r\n const newLength = this.sourceBufferLength + inputChannel.length;\r\n if (newLength > this.sourceBuffer.length) {\r\n const overflow = newLength - this.sourceBuffer.length;\r\n this.sourceBuffer.copyWithin(0, overflow);\r\n this.sourceBufferLength = this.sourceBuffer.length - inputChannel.length;\r\n }\r\n this.sourceBuffer.set(inputChannel, this.sourceBufferLength);\r\n this.sourceBufferLength += inputChannel.length;\r\n\r\n // 2. 计算可降采样样本数\r\n const availableOutputSamples = Math.floor(this.sourceBufferLength / this.downsampleRatio);\r\n if (availableOutputSamples === 0) return true;\r\n\r\n // 3. 线性插值降采样\r\n const downsampled = new Float32Array(availableOutputSamples);\r\n for (let i = 0; i < availableOutputSamples; i++) {\r\n const srcIndex = i * this.downsampleRatio;\r\n const srcIndexInt = Math.floor(srcIndex);\r\n const fraction = srcIndex - srcIndexInt;\r\n const val0 = this.sourceBuffer[srcIndexInt];\r\n const val1 = srcIndexInt + 1 < this.sourceBufferLength ? this.sourceBuffer[srcIndexInt + 1] : val0;\r\n downsampled[i] = val0 + (val1 - val0) * fraction;\r\n }\r\n\r\n // 4. Float32 → Int16 PCM\r\n const pcmData = this.floatTo16BitPCM(downsampled);\r\n\r\n // 5. 写入 PCM 缓冲区(空间不足时直接覆盖最旧,缓冲区足够大基本不会触发)\r\n if (this.pcmBufferIndex + pcmData.length > this.pcmBuffer.length) {\r\n // 简单策略:从头覆盖(丢弃最旧数据)\r\n this.pcmBufferIndex = 0;\r\n }\r\n this.pcmBuffer.set(pcmData, this.pcmBufferIndex);\r\n this.pcmBufferIndex += pcmData.length;\r\n\r\n // 6. 发送所有完整的 chunk(关键:复制到新数组再转移)\r\n while (this.pcmBufferIndex >= this.chunkSize) {\r\n // 方法1:推荐,使用 slice(隐式复制到新 ArrayBuffer)\r\n const chunk = this.pcmBuffer.slice(0, this.chunkSize);\r\n\r\n // 方法2:等价写法\r\n // const chunk = new Int16Array(this.pcmBuffer.subarray(0, this.chunkSize));\r\n\r\n this.port.postMessage(chunk, [chunk.buffer]); // 转移新缓冲区,安全!\r\n\r\n // 移动剩余数据到开头\r\n this.pcmBuffer.copyWithin(0, this.chunkSize, this.pcmBufferIndex);\r\n this.pcmBufferIndex -= this.chunkSize;\r\n }\r\n\r\n // 7. 清理已消费的源数据\r\n const consumedSrc = Math.floor(availableOutputSamples * this.downsampleRatio + 0.5); // 四舍五入更准\r\n if (consumedSrc > 0) {\r\n this.sourceBuffer.copyWithin(0, consumedSrc, this.sourceBufferLength);\r\n this.sourceBufferLength -= consumedSrc;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n floatTo16BitPCM(input) {\r\n const output = new Int16Array(input.length);\r\n for (let i = 0; i < input.length; i++) {\r\n const s = Math.max(-1, Math.min(1, input[i]));\r\n output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;\r\n }\r\n return output;\r\n }\r\n}\r\n\r\nregisterProcessor('audio-stream-resampler-processor', AudioStreamResamplerProcessor);\r\n`;\r\n\r\n/**\r\n * 浏览器端实时音频流重采样器。\r\n * 基于 AudioWorklet 将麦克风/媒体流转换为 16 kHz、16-bit、单声道 PCM,\r\n * 并通过回调逐块输出,可选保存完整 PCM 用于后续合并。\r\n */\r\nexport class AudioStreamResampler {\r\n /**\r\n * @param {object} config\r\n * @param {function(Int16Array)} config.onData - 收到一个 chunk PCM 数据的回调\r\n * @param {function(string, string)} [config.onStateChange] - 状态变化回调\r\n * @param {object} [config.processorOptions] - 传递给 AudioWorklet 的选项\r\n * @param {boolean} [config.saveFullPcm=false] - 是否在内部保存所有 PCM 用于 stop 时合并(长时间录音建议关闭)\r\n */\r\n constructor(config) {\r\n this.onData = config.onData || (() => {});\r\n this.onStateChange = config.onStateChange || (() => {});\r\n this.processorOptions = config.processorOptions || {};\r\n this.saveFullPcm = config.saveFullPcm ?? false;\r\n\r\n this.audioContext = null;\r\n this.workletNode = null;\r\n this.source = null;\r\n this.workletUrl = null;\r\n this.fullPcmData = this.saveFullPcm ? [] : null;\r\n\r\n this.isInitialized = false;\r\n this.isProcessing = false;\r\n }\r\n\r\n /**\r\n * 初始化 AudioContext 并加载 AudioWorklet。\r\n * 完成后状态变为 `\"ready\"`。\r\n */\r\n async init() {\r\n this.onStateChange(\"initializing\", \"正在初始化音频环境...\");\r\n try {\r\n this.audioContext = new (window.AudioContext || window.webkitAudioContext)();\r\n\r\n const blob = new Blob([AudioStreamResamplerProcessorCode], { type: \"application/javascript\" });\r\n this.workletUrl = URL.createObjectURL(blob);\r\n await this.audioContext.audioWorklet.addModule(this.workletUrl);\r\n\r\n this.workletNode = new AudioWorkletNode(this.audioContext, \"audio-stream-resampler-processor\", {\r\n processorOptions: this.processorOptions\r\n });\r\n\r\n this.workletNode.port.onmessage = (event) => {\r\n const chunk = event.data; // Int16Array\r\n this.onData(chunk);\r\n if (this.saveFullPcm) {\r\n this.fullPcmData.push(chunk);\r\n }\r\n };\r\n\r\n this.isInitialized = true;\r\n this.onStateChange(\"ready\", \"音频环境已就绪\");\r\n } catch (err) {\r\n console.error(\"AudioStreamResampler init error:\", err);\r\n this.onStateChange(\"error\", `初始化失败: ${err.message}`);\r\n }\r\n }\r\n\r\n /**\r\n * 绑定媒体流,开始实时处理。\r\n * @param {MediaStream} stream - 通过 getUserMedia 或其他方式获得的流\r\n */\r\n setMediaStream(stream) {\r\n if (!this.isInitialized) {\r\n console.error(\"请先调用 init()\");\r\n return;\r\n }\r\n\r\n if (this.source) {\r\n this.source.disconnect();\r\n }\r\n\r\n this.source = this.audioContext.createMediaStreamSource(stream);\r\n this.source.connect(this.workletNode);\r\n // workletNode 默认连接到 destination,可选断开以静音\r\n // this.workletNode.connect(this.audioContext.destination);\r\n\r\n this.isProcessing = true;\r\n this.onStateChange(\"processing\", \"正在处理音频流...\");\r\n }\r\n\r\n /**\r\n * 停止处理并释放资源。\r\n * @param {(fullPcm: Int16Array) => void} [callback] - 若构造时 `saveFullPcm=true`,会把合并后的完整 PCM 通过此回调传出\r\n */\r\n stop(callback) {\r\n if (!this.isProcessing) return;\r\n\r\n this.onStateChange(\"stopping\", \"正在停止...\");\r\n\r\n if (this.source) {\r\n this.source.disconnect();\r\n this.source = null;\r\n }\r\n\r\n this._cleanup();\r\n\r\n this.onStateChange(\"stopped\", \"已停止\");\r\n\r\n if (this.saveFullPcm && callback && typeof callback === \"function\") {\r\n const totalLength = this.fullPcmData.reduce((sum, chunk) => sum + chunk.length, 0);\r\n const combined = new Int16Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.fullPcmData) {\r\n combined.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n callback(combined);\r\n }\r\n }\r\n\r\n _cleanup() {\r\n if (this.workletNode) {\r\n this.workletNode.disconnect();\r\n this.workletNode.port.close();\r\n this.workletNode = null;\r\n }\r\n if (this.audioContext && this.audioContext.state !== \"closed\") {\r\n this.audioContext.close();\r\n this.audioContext = null;\r\n }\r\n if (this.workletUrl) {\r\n URL.revokeObjectURL(this.workletUrl);\r\n this.workletUrl = null;\r\n }\r\n\r\n this.isProcessing = false;\r\n this.isInitialized = false;\r\n if (this.fullPcmData) {\r\n this.fullPcmData.length = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 将 16-bit 单声道 PCM 数据封装成标准 WAV Blob。\r\n *\r\n * @param {Int16Array} pcmData - PCM 采样数据\r\n * @param {number} [sampleRate=16000] - 采样率,默认 16 kHz\r\n * @returns {Blob} audio/wav Blob\r\n */\r\nexport function pcmToWavBlob(pcmData, sampleRate = 16000) {\r\n const length = pcmData.length;\r\n const buffer = new ArrayBuffer(44 + length * 2);\r\n const view = new DataView(buffer);\r\n const writeString = (offset, string) => {\r\n for (let i = 0; i < string.length; i++) {\r\n view.setUint8(offset + i, string.charCodeAt(i));\r\n }\r\n };\r\n writeString(0, \"RIFF\");\r\n view.setUint32(4, 36 + length * 2, true);\r\n writeString(8, \"WAVE\");\r\n writeString(12, \"fmt \");\r\n view.setUint32(16, 16, true);\r\n view.setUint16(20, 1, true);\r\n view.setUint16(22, 1, true);\r\n view.setUint32(24, sampleRate, true);\r\n view.setUint32(28, sampleRate * 2, true);\r\n view.setUint16(32, 2, true);\r\n view.setUint16(34, 16, true);\r\n writeString(36, \"data\");\r\n view.setUint32(40, length * 2, true);\r\n let offset = 44;\r\n for (let i = 0; i < length; i++) {\r\n view.setInt16(offset, pcmData[i], true);\r\n offset += 2;\r\n }\r\n return new Blob([buffer], { type: \"audio/wav\" });\r\n}\r\n"],"names":[],"mappings":";;AAAA,MAAM,iCAAiC,GAAG,CAAC;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,CAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,oBAAoB,CAAC;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,MAAM,EAAE;AACxB,QAAQ,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AAClD,QAAQ,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AAChE,QAAQ,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC;AAC9D,QAAQ,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC;AACvD;AACA,QAAQ,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;AACjC,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;AAChC,QAAQ,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;AAC3B,QAAQ,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;AAC/B,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC;AACxD;AACA,QAAQ,IAAI,CAAC,aAA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"}
1
+ {"version":3,"file":"audio.cjs","sources":["../src/source/audio.js"],"sourcesContent":["const AudioStreamResamplerProcessorCode = `\r\nclass AudioStreamResamplerProcessor extends AudioWorkletProcessor {\r\n constructor(options) {\r\n super();\r\n const config = options.processorOptions || {};\r\n this.targetSampleRate = config.targetSampleRate || 16000;\r\n this.sourceSampleRate = sampleRate;\r\n this.downsampleRatio = this.sourceSampleRate / this.targetSampleRate;\r\n\r\n // 16000Hz 用 60ms chunk (960 samples),其他用 1024\r\n this.chunkSize = this.targetSampleRate === 16000 ? 960 : 1024;\r\n\r\n const sourceBufferSize = config.sourceBufferSize || 16384; // ~340ms @48kHz\r\n const pcmBufferSize = config.pcmBufferSize || (this.chunkSize * 10); // 更大缓冲,减少溢出概率\r\n\r\n this.sourceBuffer = new Float32Array(sourceBufferSize);\r\n this.sourceBufferLength = 0;\r\n\r\n this.pcmBuffer = new Int16Array(pcmBufferSize);\r\n this.pcmBufferIndex = 0;\r\n }\r\n\r\n process(inputs, outputs, parameters) {\r\n const input = inputs[0];\r\n if (!input || input.length === 0 || input[0].length === 0) return true;\r\n const inputChannel = input[0];\r\n\r\n // 1. 写入源缓冲区(溢出时覆盖最旧数据)\r\n const newLength = this.sourceBufferLength + inputChannel.length;\r\n if (newLength > this.sourceBuffer.length) {\r\n const overflow = newLength - this.sourceBuffer.length;\r\n this.sourceBuffer.copyWithin(0, overflow);\r\n this.sourceBufferLength = this.sourceBuffer.length - inputChannel.length;\r\n }\r\n this.sourceBuffer.set(inputChannel, this.sourceBufferLength);\r\n this.sourceBufferLength += inputChannel.length;\r\n\r\n // 2. 计算可降采样样本数\r\n const availableOutputSamples = Math.floor(this.sourceBufferLength / this.downsampleRatio);\r\n if (availableOutputSamples === 0) return true;\r\n\r\n // 3. 线性插值降采样\r\n const downsampled = new Float32Array(availableOutputSamples);\r\n for (let i = 0; i < availableOutputSamples; i++) {\r\n const srcIndex = i * this.downsampleRatio;\r\n const srcIndexInt = Math.floor(srcIndex);\r\n const fraction = srcIndex - srcIndexInt;\r\n const val0 = this.sourceBuffer[srcIndexInt];\r\n const val1 = srcIndexInt + 1 < this.sourceBufferLength ? this.sourceBuffer[srcIndexInt + 1] : val0;\r\n downsampled[i] = val0 + (val1 - val0) * fraction;\r\n }\r\n\r\n // 4. Float32 → Int16 PCM\r\n const pcmData = this.floatTo16BitPCM(downsampled);\r\n\r\n // 5. 写入 PCM 缓冲区(空间不足时直接覆盖最旧,缓冲区足够大基本不会触发)\r\n if (this.pcmBufferIndex + pcmData.length > this.pcmBuffer.length) {\r\n // 简单策略:从头覆盖(丢弃最旧数据)\r\n this.pcmBufferIndex = 0;\r\n }\r\n this.pcmBuffer.set(pcmData, this.pcmBufferIndex);\r\n this.pcmBufferIndex += pcmData.length;\r\n\r\n // 6. 发送所有完整的 chunk(关键:复制到新数组再转移)\r\n while (this.pcmBufferIndex >= this.chunkSize) {\r\n // 方法1:推荐,使用 slice(隐式复制到新 ArrayBuffer)\r\n const chunk = this.pcmBuffer.slice(0, this.chunkSize);\r\n\r\n // 方法2:等价写法\r\n // const chunk = new Int16Array(this.pcmBuffer.subarray(0, this.chunkSize));\r\n\r\n this.port.postMessage(chunk, [chunk.buffer]); // 转移新缓冲区,安全!\r\n\r\n // 移动剩余数据到开头\r\n this.pcmBuffer.copyWithin(0, this.chunkSize, this.pcmBufferIndex);\r\n this.pcmBufferIndex -= this.chunkSize;\r\n }\r\n\r\n // 7. 清理已消费的源数据\r\n const consumedSrc = Math.floor(availableOutputSamples * this.downsampleRatio + 0.5); // 四舍五入更准\r\n if (consumedSrc > 0) {\r\n this.sourceBuffer.copyWithin(0, consumedSrc, this.sourceBufferLength);\r\n this.sourceBufferLength -= consumedSrc;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n floatTo16BitPCM(input) {\r\n const output = new Int16Array(input.length);\r\n for (let i = 0; i < input.length; i++) {\r\n const s = Math.max(-1, Math.min(1, input[i]));\r\n output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;\r\n }\r\n return output;\r\n }\r\n}\r\n\r\nregisterProcessor('audio-stream-resampler-processor', AudioStreamResamplerProcessor);\r\n`;\r\n\r\n/**\r\n * 浏览器端实时音频流重采样器。\r\n * 基于 AudioWorklet 将麦克风/媒体流转换为 16 kHz、16-bit、单声道 PCM,\r\n * 并通过回调逐块输出,可选保存完整 PCM 用于后续合并。\r\n */\r\nexport class AudioStreamResampler {\r\n /**\r\n * @param {object} config\r\n * @param {function(Int16Array)} config.onData - 收到一个 chunk PCM 数据的回调\r\n * @param {function(string, string)} [config.onStateChange] - 状态变化回调\r\n * @param {object} [config.processorOptions] - 传递给 AudioWorklet 的选项\r\n * @param {boolean} [config.saveFullPcm=false] - 是否在内部保存所有 PCM 用于 stop 时合并(长时间录音建议关闭)\r\n */\r\n constructor(config) {\r\n this.onData = config.onData || (() => {});\r\n this.onStateChange = config.onStateChange || (() => {});\r\n this.processorOptions = config.processorOptions || {};\r\n this.saveFullPcm = config.saveFullPcm ?? false;\r\n\r\n this.audioContext = null;\r\n this.workletNode = null;\r\n this.source = null;\r\n this.workletUrl = null;\r\n this.fullPcmData = this.saveFullPcm ? [] : null;\r\n\r\n this.isInitialized = false;\r\n this.isProcessing = false;\r\n }\r\n\r\n /**\r\n * 初始化 AudioContext 并加载 AudioWorklet。\r\n * 完成后状态变为 `\"ready\"`。\r\n */\r\n async init() {\r\n this.onStateChange(\"initializing\", \"正在初始化音频环境...\");\r\n try {\r\n this.audioContext = new (window.AudioContext || window.webkitAudioContext)();\r\n\r\n const blob = new Blob([AudioStreamResamplerProcessorCode], { type: \"application/javascript\" });\r\n this.workletUrl = URL.createObjectURL(blob);\r\n await this.audioContext.audioWorklet.addModule(this.workletUrl);\r\n\r\n this.workletNode = new AudioWorkletNode(this.audioContext, \"audio-stream-resampler-processor\", {\r\n processorOptions: this.processorOptions\r\n });\r\n\r\n this.workletNode.port.onmessage = (event) => {\r\n const chunk = event.data; // Int16Array\r\n this.onData(chunk);\r\n if (this.saveFullPcm) {\r\n this.fullPcmData.push(chunk);\r\n }\r\n };\r\n\r\n this.isInitialized = true;\r\n this.onStateChange(\"ready\", \"音频环境已就绪\");\r\n } catch (err) {\r\n console.error(\"AudioStreamResampler init error:\", err);\r\n this.onStateChange(\"error\", `初始化失败: ${err.message}`);\r\n }\r\n }\r\n\r\n /**\r\n * 绑定媒体流,开始实时处理。\r\n * @param {MediaStream} stream - 通过 getUserMedia 或其他方式获得的流\r\n */\r\n setMediaStream(stream) {\r\n if (!this.isInitialized) {\r\n console.error(\"请先调用 init()\");\r\n return;\r\n }\r\n\r\n if (this.source) {\r\n this.source.disconnect();\r\n }\r\n\r\n this.source = this.audioContext.createMediaStreamSource(stream);\r\n this.source.connect(this.workletNode);\r\n // workletNode 默认连接到 destination,可选断开以静音\r\n // this.workletNode.connect(this.audioContext.destination);\r\n\r\n this.isProcessing = true;\r\n this.onStateChange(\"processing\", \"正在处理音频流...\");\r\n }\r\n\r\n /**\r\n * 停止处理并释放资源。\r\n * @param {(fullPcm: Int16Array) => void} [callback] - 若构造时 `saveFullPcm=true`,会把合并后的完整 PCM 通过此回调传出\r\n */\r\n stop(callback) {\r\n if (!this.isProcessing) return;\r\n\r\n this.onStateChange(\"stopping\", \"正在停止...\");\r\n\r\n if (this.source) {\r\n this.source.disconnect();\r\n this.source = null;\r\n }\r\n\r\n this._cleanup();\r\n\r\n this.onStateChange(\"stopped\", \"已停止\");\r\n\r\n if (this.saveFullPcm && callback && typeof callback === \"function\") {\r\n const totalLength = this.fullPcmData.reduce((sum, chunk) => sum + chunk.length, 0);\r\n const combined = new Int16Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.fullPcmData) {\r\n combined.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n callback(combined);\r\n }\r\n }\r\n\r\n _cleanup() {\r\n if (this.workletNode) {\r\n this.workletNode.disconnect();\r\n this.workletNode.port.close();\r\n this.workletNode = null;\r\n }\r\n if (this.audioContext && this.audioContext.state !== \"closed\") {\r\n this.audioContext.close();\r\n this.audioContext = null;\r\n }\r\n if (this.workletUrl) {\r\n URL.revokeObjectURL(this.workletUrl);\r\n this.workletUrl = null;\r\n }\r\n\r\n this.isProcessing = false;\r\n this.isInitialized = false;\r\n if (this.fullPcmData) {\r\n this.fullPcmData.length = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 将 16-bit 单声道 PCM 数据封装成标准 WAV Blob。\r\n *\r\n * @param {Int16Array} pcmData - PCM 采样数据\r\n * @param {number} [sampleRate=16000] - 采样率,默认 16 kHz\r\n * @returns {Blob} audio/wav Blob\r\n */\r\nexport function pcmToWavBlob(pcmData, sampleRate = 16000) {\r\n const length = pcmData.length;\r\n const buffer = new ArrayBuffer(44 + length * 2);\r\n const view = new DataView(buffer);\r\n const writeString = (offset, string) => {\r\n for (let i = 0; i < string.length; i++) {\r\n view.setUint8(offset + i, string.charCodeAt(i));\r\n }\r\n };\r\n writeString(0, \"RIFF\");\r\n view.setUint32(4, 36 + length * 2, true);\r\n writeString(8, \"WAVE\");\r\n writeString(12, \"fmt \");\r\n view.setUint32(16, 16, true);\r\n view.setUint16(20, 1, true);\r\n view.setUint16(22, 1, true);\r\n view.setUint32(24, sampleRate, true);\r\n view.setUint32(28, sampleRate * 2, true);\r\n view.setUint16(32, 2, true);\r\n view.setUint16(34, 16, true);\r\n writeString(36, \"data\");\r\n view.setUint32(40, length * 2, true);\r\n let offset = 44;\r\n for (let i = 0; i < length; i++) {\r\n view.setInt16(offset, pcmData[i], true);\r\n offset += 2;\r\n }\r\n return new Blob([buffer], { type: \"audio/wav\" });\r\n}\r\n"],"names":[],"mappings":";;AAAA,MAAM,iCAAiC,GAAG,CAAC;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,CAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,oBAAoB,CAAC;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,MAAM,EAAE;AACxB,QAAQ,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AAClD,QAAQ,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AAChE,QAAQ,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC;AAC9D,QAAQ,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC;AACvD;AACA,QAAQ,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;AACjC,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;AAChC,QAAQ,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;AAC3B,QAAQ,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;AAC/B,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC;AACxD;AACA,QAAQ,IAAI,CAAC,aAA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"}
package/dist/audio.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"audio.js","sources":["../src/source/audio.js"],"sourcesContent":["const AudioStreamResamplerProcessorCode = `\r\nclass AudioStreamResamplerProcessor extends AudioWorkletProcessor {\r\n constructor(options) {\r\n super();\r\n const config = options.processorOptions || {};\r\n this.targetSampleRate = config.targetSampleRate || 16000;\r\n this.sourceSampleRate = sampleRate;\r\n this.downsampleRatio = this.sourceSampleRate / this.targetSampleRate;\r\n\r\n // 16000Hz 用 60ms chunk (960 samples),其他用 1024\r\n this.chunkSize = this.targetSampleRate === 16000 ? 960 : 1024;\r\n\r\n const sourceBufferSize = config.sourceBufferSize || 16384; // ~340ms @48kHz\r\n const pcmBufferSize = config.pcmBufferSize || (this.chunkSize * 10); // 更大缓冲,减少溢出概率\r\n\r\n this.sourceBuffer = new Float32Array(sourceBufferSize);\r\n this.sourceBufferLength = 0;\r\n\r\n this.pcmBuffer = new Int16Array(pcmBufferSize);\r\n this.pcmBufferIndex = 0;\r\n }\r\n\r\n process(inputs, outputs, parameters) {\r\n const input = inputs[0];\r\n if (!input || input.length === 0 || input[0].length === 0) return true;\r\n const inputChannel = input[0];\r\n\r\n // 1. 写入源缓冲区(溢出时覆盖最旧数据)\r\n const newLength = this.sourceBufferLength + inputChannel.length;\r\n if (newLength > this.sourceBuffer.length) {\r\n const overflow = newLength - this.sourceBuffer.length;\r\n this.sourceBuffer.copyWithin(0, overflow);\r\n this.sourceBufferLength = this.sourceBuffer.length - inputChannel.length;\r\n }\r\n this.sourceBuffer.set(inputChannel, this.sourceBufferLength);\r\n this.sourceBufferLength += inputChannel.length;\r\n\r\n // 2. 计算可降采样样本数\r\n const availableOutputSamples = Math.floor(this.sourceBufferLength / this.downsampleRatio);\r\n if (availableOutputSamples === 0) return true;\r\n\r\n // 3. 线性插值降采样\r\n const downsampled = new Float32Array(availableOutputSamples);\r\n for (let i = 0; i < availableOutputSamples; i++) {\r\n const srcIndex = i * this.downsampleRatio;\r\n const srcIndexInt = Math.floor(srcIndex);\r\n const fraction = srcIndex - srcIndexInt;\r\n const val0 = this.sourceBuffer[srcIndexInt];\r\n const val1 = srcIndexInt + 1 < this.sourceBufferLength ? this.sourceBuffer[srcIndexInt + 1] : val0;\r\n downsampled[i] = val0 + (val1 - val0) * fraction;\r\n }\r\n\r\n // 4. Float32 → Int16 PCM\r\n const pcmData = this.floatTo16BitPCM(downsampled);\r\n\r\n // 5. 写入 PCM 缓冲区(空间不足时直接覆盖最旧,缓冲区足够大基本不会触发)\r\n if (this.pcmBufferIndex + pcmData.length > this.pcmBuffer.length) {\r\n // 简单策略:从头覆盖(丢弃最旧数据)\r\n this.pcmBufferIndex = 0;\r\n }\r\n this.pcmBuffer.set(pcmData, this.pcmBufferIndex);\r\n this.pcmBufferIndex += pcmData.length;\r\n\r\n // 6. 发送所有完整的 chunk(关键:复制到新数组再转移)\r\n while (this.pcmBufferIndex >= this.chunkSize) {\r\n // 方法1:推荐,使用 slice(隐式复制到新 ArrayBuffer)\r\n const chunk = this.pcmBuffer.slice(0, this.chunkSize);\r\n\r\n // 方法2:等价写法\r\n // const chunk = new Int16Array(this.pcmBuffer.subarray(0, this.chunkSize));\r\n\r\n this.port.postMessage(chunk, [chunk.buffer]); // 转移新缓冲区,安全!\r\n\r\n // 移动剩余数据到开头\r\n this.pcmBuffer.copyWithin(0, this.chunkSize, this.pcmBufferIndex);\r\n this.pcmBufferIndex -= this.chunkSize;\r\n }\r\n\r\n // 7. 清理已消费的源数据\r\n const consumedSrc = Math.floor(availableOutputSamples * this.downsampleRatio + 0.5); // 四舍五入更准\r\n if (consumedSrc > 0) {\r\n this.sourceBuffer.copyWithin(0, consumedSrc, this.sourceBufferLength);\r\n this.sourceBufferLength -= consumedSrc;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n floatTo16BitPCM(input) {\r\n const output = new Int16Array(input.length);\r\n for (let i = 0; i < input.length; i++) {\r\n const s = Math.max(-1, Math.min(1, input[i]));\r\n output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;\r\n }\r\n return output;\r\n }\r\n}\r\n\r\nregisterProcessor('audio-stream-resampler-processor', AudioStreamResamplerProcessor);\r\n`;\r\n\r\n/**\r\n * 浏览器端实时音频流重采样器。\r\n * 基于 AudioWorklet 将麦克风/媒体流转换为 16 kHz、16-bit、单声道 PCM,\r\n * 并通过回调逐块输出,可选保存完整 PCM 用于后续合并。\r\n */\r\nexport class AudioStreamResampler {\r\n /**\r\n * @param {object} config\r\n * @param {function(Int16Array)} config.onData - 收到一个 chunk PCM 数据的回调\r\n * @param {function(string, string)} [config.onStateChange] - 状态变化回调\r\n * @param {object} [config.processorOptions] - 传递给 AudioWorklet 的选项\r\n * @param {boolean} [config.saveFullPcm=false] - 是否在内部保存所有 PCM 用于 stop 时合并(长时间录音建议关闭)\r\n */\r\n constructor(config) {\r\n this.onData = config.onData || (() => {});\r\n this.onStateChange = config.onStateChange || (() => {});\r\n this.processorOptions = config.processorOptions || {};\r\n this.saveFullPcm = config.saveFullPcm ?? false;\r\n\r\n this.audioContext = null;\r\n this.workletNode = null;\r\n this.source = null;\r\n this.workletUrl = null;\r\n this.fullPcmData = this.saveFullPcm ? [] : null;\r\n\r\n this.isInitialized = false;\r\n this.isProcessing = false;\r\n }\r\n\r\n /**\r\n * 初始化 AudioContext 并加载 AudioWorklet。\r\n * 完成后状态变为 `\"ready\"`。\r\n */\r\n async init() {\r\n this.onStateChange(\"initializing\", \"正在初始化音频环境...\");\r\n try {\r\n this.audioContext = new (window.AudioContext || window.webkitAudioContext)();\r\n\r\n const blob = new Blob([AudioStreamResamplerProcessorCode], { type: \"application/javascript\" });\r\n this.workletUrl = URL.createObjectURL(blob);\r\n await this.audioContext.audioWorklet.addModule(this.workletUrl);\r\n\r\n this.workletNode = new AudioWorkletNode(this.audioContext, \"audio-stream-resampler-processor\", {\r\n processorOptions: this.processorOptions\r\n });\r\n\r\n this.workletNode.port.onmessage = (event) => {\r\n const chunk = event.data; // Int16Array\r\n this.onData(chunk);\r\n if (this.saveFullPcm) {\r\n this.fullPcmData.push(chunk);\r\n }\r\n };\r\n\r\n this.isInitialized = true;\r\n this.onStateChange(\"ready\", \"音频环境已就绪\");\r\n } catch (err) {\r\n console.error(\"AudioStreamResampler init error:\", err);\r\n this.onStateChange(\"error\", `初始化失败: ${err.message}`);\r\n }\r\n }\r\n\r\n /**\r\n * 绑定媒体流,开始实时处理。\r\n * @param {MediaStream} stream - 通过 getUserMedia 或其他方式获得的流\r\n */\r\n setMediaStream(stream) {\r\n if (!this.isInitialized) {\r\n console.error(\"请先调用 init()\");\r\n return;\r\n }\r\n\r\n if (this.source) {\r\n this.source.disconnect();\r\n }\r\n\r\n this.source = this.audioContext.createMediaStreamSource(stream);\r\n this.source.connect(this.workletNode);\r\n // workletNode 默认连接到 destination,可选断开以静音\r\n // this.workletNode.connect(this.audioContext.destination);\r\n\r\n this.isProcessing = true;\r\n this.onStateChange(\"processing\", \"正在处理音频流...\");\r\n }\r\n\r\n /**\r\n * 停止处理并释放资源。\r\n * @param {(fullPcm: Int16Array) => void} [callback] - 若构造时 `saveFullPcm=true`,会把合并后的完整 PCM 通过此回调传出\r\n */\r\n stop(callback) {\r\n if (!this.isProcessing) return;\r\n\r\n this.onStateChange(\"stopping\", \"正在停止...\");\r\n\r\n if (this.source) {\r\n this.source.disconnect();\r\n this.source = null;\r\n }\r\n\r\n this._cleanup();\r\n\r\n this.onStateChange(\"stopped\", \"已停止\");\r\n\r\n if (this.saveFullPcm && callback && typeof callback === \"function\") {\r\n const totalLength = this.fullPcmData.reduce((sum, chunk) => sum + chunk.length, 0);\r\n const combined = new Int16Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.fullPcmData) {\r\n combined.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n callback(combined);\r\n }\r\n }\r\n\r\n _cleanup() {\r\n if (this.workletNode) {\r\n this.workletNode.disconnect();\r\n this.workletNode.port.close();\r\n this.workletNode = null;\r\n }\r\n if (this.audioContext && this.audioContext.state !== \"closed\") {\r\n this.audioContext.close();\r\n this.audioContext = null;\r\n }\r\n if (this.workletUrl) {\r\n URL.revokeObjectURL(this.workletUrl);\r\n this.workletUrl = null;\r\n }\r\n\r\n this.isProcessing = false;\r\n this.isInitialized = false;\r\n if (this.fullPcmData) {\r\n this.fullPcmData.length = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 将 16-bit 单声道 PCM 数据封装成标准 WAV Blob。\r\n *\r\n * @param {Int16Array} pcmData - PCM 采样数据\r\n * @param {number} [sampleRate=16000] - 采样率,默认 16 kHz\r\n * @returns {Blob} audio/wav Blob\r\n */\r\nexport function pcmToWavBlob(pcmData, sampleRate = 16000) {\r\n const length = pcmData.length;\r\n const buffer = new ArrayBuffer(44 + length * 2);\r\n const view = new DataView(buffer);\r\n const writeString = (offset, string) => {\r\n for (let i = 0; i < string.length; i++) {\r\n view.setUint8(offset + i, string.charCodeAt(i));\r\n }\r\n };\r\n writeString(0, \"RIFF\");\r\n view.setUint32(4, 36 + length * 2, true);\r\n writeString(8, \"WAVE\");\r\n writeString(12, \"fmt \");\r\n view.setUint32(16, 16, true);\r\n view.setUint16(20, 1, true);\r\n view.setUint16(22, 1, true);\r\n view.setUint32(24, sampleRate, true);\r\n view.setUint32(28, sampleRate * 2, true);\r\n view.setUint16(32, 2, true);\r\n view.setUint16(34, 16, true);\r\n writeString(36, \"data\");\r\n view.setUint32(40, length * 2, true);\r\n let offset = 44;\r\n for (let i = 0; i < length; i++) {\r\n view.setInt16(offset, pcmData[i], true);\r\n offset += 2;\r\n }\r\n return new Blob([buffer], { type: \"audio/wav\" });\r\n}\r\n"],"names":[],"mappings":"AAAA,MAAM,iCAAiC,GAAG,CAAC;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,CAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,oBAAoB,CAAC;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,MAAM,EAAE;AACxB,QAAQ,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AAClD,QAAQ,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AAChE,QAAQ,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC;AAC9D,QAAQ,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC;AACvD;AACA,QAAQ,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;AACjC,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;AAChC,QAAQ,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;AAC3B,QAAQ,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;AAC/B,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC;AACxD;AACA,QAAQ,IAAI,CAAC,aAAa,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"}
1
+ {"version":3,"file":"audio.js","sources":["../src/source/audio.js"],"sourcesContent":["const AudioStreamResamplerProcessorCode = `\r\nclass AudioStreamResamplerProcessor extends AudioWorkletProcessor {\r\n constructor(options) {\r\n super();\r\n const config = options.processorOptions || {};\r\n this.targetSampleRate = config.targetSampleRate || 16000;\r\n this.sourceSampleRate = sampleRate;\r\n this.downsampleRatio = this.sourceSampleRate / this.targetSampleRate;\r\n\r\n // 16000Hz 用 60ms chunk (960 samples),其他用 1024\r\n this.chunkSize = this.targetSampleRate === 16000 ? 960 : 1024;\r\n\r\n const sourceBufferSize = config.sourceBufferSize || 16384; // ~340ms @48kHz\r\n const pcmBufferSize = config.pcmBufferSize || (this.chunkSize * 10); // 更大缓冲,减少溢出概率\r\n\r\n this.sourceBuffer = new Float32Array(sourceBufferSize);\r\n this.sourceBufferLength = 0;\r\n\r\n this.pcmBuffer = new Int16Array(pcmBufferSize);\r\n this.pcmBufferIndex = 0;\r\n }\r\n\r\n process(inputs, outputs, parameters) {\r\n const input = inputs[0];\r\n if (!input || input.length === 0 || input[0].length === 0) return true;\r\n const inputChannel = input[0];\r\n\r\n // 1. 写入源缓冲区(溢出时覆盖最旧数据)\r\n const newLength = this.sourceBufferLength + inputChannel.length;\r\n if (newLength > this.sourceBuffer.length) {\r\n const overflow = newLength - this.sourceBuffer.length;\r\n this.sourceBuffer.copyWithin(0, overflow);\r\n this.sourceBufferLength = this.sourceBuffer.length - inputChannel.length;\r\n }\r\n this.sourceBuffer.set(inputChannel, this.sourceBufferLength);\r\n this.sourceBufferLength += inputChannel.length;\r\n\r\n // 2. 计算可降采样样本数\r\n const availableOutputSamples = Math.floor(this.sourceBufferLength / this.downsampleRatio);\r\n if (availableOutputSamples === 0) return true;\r\n\r\n // 3. 线性插值降采样\r\n const downsampled = new Float32Array(availableOutputSamples);\r\n for (let i = 0; i < availableOutputSamples; i++) {\r\n const srcIndex = i * this.downsampleRatio;\r\n const srcIndexInt = Math.floor(srcIndex);\r\n const fraction = srcIndex - srcIndexInt;\r\n const val0 = this.sourceBuffer[srcIndexInt];\r\n const val1 = srcIndexInt + 1 < this.sourceBufferLength ? this.sourceBuffer[srcIndexInt + 1] : val0;\r\n downsampled[i] = val0 + (val1 - val0) * fraction;\r\n }\r\n\r\n // 4. Float32 → Int16 PCM\r\n const pcmData = this.floatTo16BitPCM(downsampled);\r\n\r\n // 5. 写入 PCM 缓冲区(空间不足时直接覆盖最旧,缓冲区足够大基本不会触发)\r\n if (this.pcmBufferIndex + pcmData.length > this.pcmBuffer.length) {\r\n // 简单策略:从头覆盖(丢弃最旧数据)\r\n this.pcmBufferIndex = 0;\r\n }\r\n this.pcmBuffer.set(pcmData, this.pcmBufferIndex);\r\n this.pcmBufferIndex += pcmData.length;\r\n\r\n // 6. 发送所有完整的 chunk(关键:复制到新数组再转移)\r\n while (this.pcmBufferIndex >= this.chunkSize) {\r\n // 方法1:推荐,使用 slice(隐式复制到新 ArrayBuffer)\r\n const chunk = this.pcmBuffer.slice(0, this.chunkSize);\r\n\r\n // 方法2:等价写法\r\n // const chunk = new Int16Array(this.pcmBuffer.subarray(0, this.chunkSize));\r\n\r\n this.port.postMessage(chunk, [chunk.buffer]); // 转移新缓冲区,安全!\r\n\r\n // 移动剩余数据到开头\r\n this.pcmBuffer.copyWithin(0, this.chunkSize, this.pcmBufferIndex);\r\n this.pcmBufferIndex -= this.chunkSize;\r\n }\r\n\r\n // 7. 清理已消费的源数据\r\n const consumedSrc = Math.floor(availableOutputSamples * this.downsampleRatio + 0.5); // 四舍五入更准\r\n if (consumedSrc > 0) {\r\n this.sourceBuffer.copyWithin(0, consumedSrc, this.sourceBufferLength);\r\n this.sourceBufferLength -= consumedSrc;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n floatTo16BitPCM(input) {\r\n const output = new Int16Array(input.length);\r\n for (let i = 0; i < input.length; i++) {\r\n const s = Math.max(-1, Math.min(1, input[i]));\r\n output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;\r\n }\r\n return output;\r\n }\r\n}\r\n\r\nregisterProcessor('audio-stream-resampler-processor', AudioStreamResamplerProcessor);\r\n`;\r\n\r\n/**\r\n * 浏览器端实时音频流重采样器。\r\n * 基于 AudioWorklet 将麦克风/媒体流转换为 16 kHz、16-bit、单声道 PCM,\r\n * 并通过回调逐块输出,可选保存完整 PCM 用于后续合并。\r\n */\r\nexport class AudioStreamResampler {\r\n /**\r\n * @param {object} config\r\n * @param {function(Int16Array)} config.onData - 收到一个 chunk PCM 数据的回调\r\n * @param {function(string, string)} [config.onStateChange] - 状态变化回调\r\n * @param {object} [config.processorOptions] - 传递给 AudioWorklet 的选项\r\n * @param {boolean} [config.saveFullPcm=false] - 是否在内部保存所有 PCM 用于 stop 时合并(长时间录音建议关闭)\r\n */\r\n constructor(config) {\r\n this.onData = config.onData || (() => {});\r\n this.onStateChange = config.onStateChange || (() => {});\r\n this.processorOptions = config.processorOptions || {};\r\n this.saveFullPcm = config.saveFullPcm ?? false;\r\n\r\n this.audioContext = null;\r\n this.workletNode = null;\r\n this.source = null;\r\n this.workletUrl = null;\r\n this.fullPcmData = this.saveFullPcm ? [] : null;\r\n\r\n this.isInitialized = false;\r\n this.isProcessing = false;\r\n }\r\n\r\n /**\r\n * 初始化 AudioContext 并加载 AudioWorklet。\r\n * 完成后状态变为 `\"ready\"`。\r\n */\r\n async init() {\r\n this.onStateChange(\"initializing\", \"正在初始化音频环境...\");\r\n try {\r\n this.audioContext = new (window.AudioContext || window.webkitAudioContext)();\r\n\r\n const blob = new Blob([AudioStreamResamplerProcessorCode], { type: \"application/javascript\" });\r\n this.workletUrl = URL.createObjectURL(blob);\r\n await this.audioContext.audioWorklet.addModule(this.workletUrl);\r\n\r\n this.workletNode = new AudioWorkletNode(this.audioContext, \"audio-stream-resampler-processor\", {\r\n processorOptions: this.processorOptions\r\n });\r\n\r\n this.workletNode.port.onmessage = (event) => {\r\n const chunk = event.data; // Int16Array\r\n this.onData(chunk);\r\n if (this.saveFullPcm) {\r\n this.fullPcmData.push(chunk);\r\n }\r\n };\r\n\r\n this.isInitialized = true;\r\n this.onStateChange(\"ready\", \"音频环境已就绪\");\r\n } catch (err) {\r\n console.error(\"AudioStreamResampler init error:\", err);\r\n this.onStateChange(\"error\", `初始化失败: ${err.message}`);\r\n }\r\n }\r\n\r\n /**\r\n * 绑定媒体流,开始实时处理。\r\n * @param {MediaStream} stream - 通过 getUserMedia 或其他方式获得的流\r\n */\r\n setMediaStream(stream) {\r\n if (!this.isInitialized) {\r\n console.error(\"请先调用 init()\");\r\n return;\r\n }\r\n\r\n if (this.source) {\r\n this.source.disconnect();\r\n }\r\n\r\n this.source = this.audioContext.createMediaStreamSource(stream);\r\n this.source.connect(this.workletNode);\r\n // workletNode 默认连接到 destination,可选断开以静音\r\n // this.workletNode.connect(this.audioContext.destination);\r\n\r\n this.isProcessing = true;\r\n this.onStateChange(\"processing\", \"正在处理音频流...\");\r\n }\r\n\r\n /**\r\n * 停止处理并释放资源。\r\n * @param {(fullPcm: Int16Array) => void} [callback] - 若构造时 `saveFullPcm=true`,会把合并后的完整 PCM 通过此回调传出\r\n */\r\n stop(callback) {\r\n if (!this.isProcessing) return;\r\n\r\n this.onStateChange(\"stopping\", \"正在停止...\");\r\n\r\n if (this.source) {\r\n this.source.disconnect();\r\n this.source = null;\r\n }\r\n\r\n this._cleanup();\r\n\r\n this.onStateChange(\"stopped\", \"已停止\");\r\n\r\n if (this.saveFullPcm && callback && typeof callback === \"function\") {\r\n const totalLength = this.fullPcmData.reduce((sum, chunk) => sum + chunk.length, 0);\r\n const combined = new Int16Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of this.fullPcmData) {\r\n combined.set(chunk, offset);\r\n offset += chunk.length;\r\n }\r\n callback(combined);\r\n }\r\n }\r\n\r\n _cleanup() {\r\n if (this.workletNode) {\r\n this.workletNode.disconnect();\r\n this.workletNode.port.close();\r\n this.workletNode = null;\r\n }\r\n if (this.audioContext && this.audioContext.state !== \"closed\") {\r\n this.audioContext.close();\r\n this.audioContext = null;\r\n }\r\n if (this.workletUrl) {\r\n URL.revokeObjectURL(this.workletUrl);\r\n this.workletUrl = null;\r\n }\r\n\r\n this.isProcessing = false;\r\n this.isInitialized = false;\r\n if (this.fullPcmData) {\r\n this.fullPcmData.length = 0;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 将 16-bit 单声道 PCM 数据封装成标准 WAV Blob。\r\n *\r\n * @param {Int16Array} pcmData - PCM 采样数据\r\n * @param {number} [sampleRate=16000] - 采样率,默认 16 kHz\r\n * @returns {Blob} audio/wav Blob\r\n */\r\nexport function pcmToWavBlob(pcmData, sampleRate = 16000) {\r\n const length = pcmData.length;\r\n const buffer = new ArrayBuffer(44 + length * 2);\r\n const view = new DataView(buffer);\r\n const writeString = (offset, string) => {\r\n for (let i = 0; i < string.length; i++) {\r\n view.setUint8(offset + i, string.charCodeAt(i));\r\n }\r\n };\r\n writeString(0, \"RIFF\");\r\n view.setUint32(4, 36 + length * 2, true);\r\n writeString(8, \"WAVE\");\r\n writeString(12, \"fmt \");\r\n view.setUint32(16, 16, true);\r\n view.setUint16(20, 1, true);\r\n view.setUint16(22, 1, true);\r\n view.setUint32(24, sampleRate, true);\r\n view.setUint32(28, sampleRate * 2, true);\r\n view.setUint16(32, 2, true);\r\n view.setUint16(34, 16, true);\r\n writeString(36, \"data\");\r\n view.setUint32(40, length * 2, true);\r\n let offset = 44;\r\n for (let i = 0; i < length; i++) {\r\n view.setInt16(offset, pcmData[i], true);\r\n offset += 2;\r\n }\r\n return new Blob([buffer], { type: \"audio/wav\" });\r\n}\r\n"],"names":[],"mappings":"AAAA,MAAM,iCAAiC,GAAG,CAAC;AAC3C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC,CAAC;AACF;AACA;AACA;AACA;AACA;AACA;AACO,MAAM,oBAAoB,CAAC;AAClC;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,WAAW,CAAC,MAAM,EAAE;AACxB,QAAQ,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AAClD,QAAQ,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;AAChE,QAAQ,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC;AAC9D,QAAQ,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,KAAK,CAAC;AACvD;AACA,QAAQ,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;AACjC,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;AAChC,QAAQ,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;AAC3B,QAAQ,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;AAC/B,QAAQ,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,GAAG,EAAE,GAAG,IAAI,CAAC;AACxD;AACA,QAAQ,IAAI,CAAC,aAAa,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"}
@@ -1 +1 @@
1
- {"version":3,"file":"browser.cjs","sources":["../src/source/browser.js"],"sourcesContent":["/**\r\n * 视口尺寸对象。\r\n * @typedef {Object} ViewportDimensions\r\n * @property {number} w 视口宽度,单位像素。\r\n * @property {number} h 视口高度,单位像素。\r\n */\r\n\r\n/**\r\n * 获取当前视口(viewport)的宽高。\r\n *\r\n * 兼容策略:\r\n * 1. 优先使用 `window.innerWidth/innerHeight`(现代浏览器)。\r\n * 2. 降级到 `document.documentElement.clientWidth/clientHeight`(IE9+ 及怪异模式)。\r\n * 3. 最后降级到 `document.body.clientWidth/clientHeight`(IE6-8 怪异模式)。\r\n *\r\n * @returns {ViewportDimensions} 包含 `w`(宽度)和 `h`(高度)的对象,单位为像素。\r\n *\r\n * @example\r\n * const { w, h } = getViewportSize();\r\n * console.log(`视口尺寸:${w} × ${h}`);\r\n */\r\nexport function getViewportSize() {\r\n const d = document,\r\n root = d.documentElement,\r\n body = d.body;\r\n\r\n return {\r\n w: window.innerWidth || root.clientWidth || body.clientWidth,\r\n h: window.innerHeight || root.clientHeight || body.clientHeight\r\n };\r\n}\r\n\r\n/**\r\n * 将当前页面 URL 的 query 部分解析成键值对对象。\r\n *\r\n * @returns {Record<string, string>} 所有查询参数组成的平凡对象\r\n * (同名 key 仅保留最后一项)\r\n */\r\nexport function getAllSearchParams() {\r\n const urlSearchParams = new URLSearchParams(location.search);\r\n return Object.fromEntries(urlSearchParams.entries());\r\n}\r\n\r\n/**\r\n * 根据 key 获取当前页面 URL 中的单个查询参数。\r\n *\r\n * @param {string} key - 要提取的参数名\r\n * @returns {string | undefined} 对应参数值;不存在时返回 `undefined`\r\n */\r\nexport function getSearchParam(key) {\r\n const params = getAllSearchParams();\r\n return params[key];\r\n}\r\n\r\n/**\r\n * 全屏操作辅助工具对象\r\n * @namespace fullscreenHelper\r\n */\r\nexport const fullscreenHelper = {\r\n /**\r\n * 请求进入全屏模式\r\n * @param {Element} element - 要全屏显示的元素\r\n * @returns {Promise<void> | undefined} 全屏请求 Promise(如支持)\r\n */\r\n requestFullscreen: (element) => {\r\n if (!element) {\r\n console.warn(\"未提供有效的 DOM 元素\");\r\n return;\r\n }\r\n if (element.requestFullscreen) {\r\n return element.requestFullscreen();\r\n } else if (element.mozRequestFullScreen) {\r\n return element.mozRequestFullScreen();\r\n } else if (element.webkitRequestFullscreen) {\r\n return element.webkitRequestFullscreen();\r\n } else if (element.msRequestFullscreen) {\r\n return element.msRequestFullscreen();\r\n } else {\r\n console.warn(\"当前浏览器不支持全屏 API\");\r\n }\r\n },\r\n\r\n /**\r\n * 退出全屏模式\r\n * @returns {Promise<void> | undefined} 退出全屏请求 Promise(如支持)\r\n */\r\n exitFullscreen: () => {\r\n if (document.exitFullscreen) {\r\n return document.exitFullscreen();\r\n } else if (document.mozCancelFullScreen) {\r\n return document.mozCancelFullScreen();\r\n } else if (document.webkitExitFullscreen) {\r\n return document.webkitExitFullscreen();\r\n } else if (document.msExitFullscreen) {\r\n return document.msExitFullscreen();\r\n }\r\n },\r\n\r\n /**\r\n * 获取当前全屏元素\r\n * @returns {Element | null} 当前处于全屏模式的元素,无则返回 null\r\n */\r\n getFullscreenElement: () => {\r\n return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement || null;\r\n },\r\n\r\n /**\r\n * 检测当前是否处于全屏模式\r\n * @returns {boolean} 是否全屏中\r\n */\r\n isFullscreen: () => {\r\n return !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement);\r\n },\r\n\r\n /**\r\n * 检测浏览器是否支持全屏 API\r\n * @returns {boolean} 是否支持全屏\r\n */\r\n isFullscreenEnabled: () => {\r\n return !!(document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled);\r\n },\r\n\r\n /**\r\n * 切换指定元素的全屏状态\r\n * @param {Element} element - 要切换全屏的元素\r\n * @returns {Promise<void> | undefined} 全屏操作 Promise\r\n */\r\n toggleFullscreen: (element) => {\r\n if (fullscreenHelper.isFullscreen()) {\r\n return fullscreenHelper.exitFullscreen();\r\n } else {\r\n return fullscreenHelper.requestFullscreen(element);\r\n }\r\n },\r\n\r\n /**\r\n * 监听全屏变化事件\r\n * @param {Function} callback - 全屏状态变化时的回调函数,参数为 isFullscreen: boolean\r\n * @returns {Function} 取消监听的函数\r\n */\r\n onFullscreenChange: (callback) => {\r\n const handler = () => {\r\n callback(fullscreenHelper.isFullscreen());\r\n };\r\n document.addEventListener(\"fullscreenchange\", handler);\r\n document.addEventListener(\"webkitfullscreenchange\", handler);\r\n document.addEventListener(\"mozfullscreenchange\", handler);\r\n document.addEventListener(\"msfullscreenchange\", handler);\r\n\r\n return () => {\r\n document.removeEventListener(\"fullscreenchange\", handler);\r\n document.removeEventListener(\"webkitfullscreenchange\", handler);\r\n document.removeEventListener(\"mozfullscreenchange\", handler);\r\n document.removeEventListener(\"msfullscreenchange\", handler);\r\n };\r\n }\r\n};\r\n\r\n/**\r\n * 复制文本到剪贴板\r\n * @param {String} text\r\n * @returns {Promise<void>}\r\n */\r\nexport async function copyTextToClipboard(text) {\r\n if (window.navigator.clipboard) {\r\n await window.navigator.clipboard.writeText(text);\r\n return;\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const ta = document.createElement(\"textarea\");\r\n ta.value = text;\r\n ta.style.position = \"fixed\"; // 防止滚动\r\n ta.style.opacity = \"0\";\r\n document.body.appendChild(ta);\r\n ta.focus();\r\n ta.select();\r\n\r\n try {\r\n document.execCommand(\"copy\") ? resolve() : reject(new Error(\"execCommand failed\"));\r\n } catch (err) {\r\n reject(err);\r\n } finally {\r\n document.body.removeChild(ta);\r\n }\r\n });\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser.cjs","sources":["../src/source/browser.js"],"sourcesContent":["/**\r\n * 视口尺寸对象。\r\n * @typedef {Object} ViewportDimensions\r\n * @property {number} w 视口宽度,单位像素。\r\n * @property {number} h 视口高度,单位像素。\r\n */\r\n\r\n/**\r\n * 获取当前视口(viewport)的宽高。\r\n *\r\n * 兼容策略:\r\n * 1. 优先使用 `window.innerWidth/innerHeight`(现代浏览器)。\r\n * 2. 降级到 `document.documentElement.clientWidth/clientHeight`(IE9+ 及怪异模式)。\r\n * 3. 最后降级到 `document.body.clientWidth/clientHeight`(IE6-8 怪异模式)。\r\n *\r\n * @returns {ViewportDimensions} 包含 `w`(宽度)和 `h`(高度)的对象,单位为像素。\r\n *\r\n * @example\r\n * const { w, h } = getViewportSize();\r\n * console.log(`视口尺寸:${w} × ${h}`);\r\n */\r\nexport function getViewportSize() {\r\n const d = document,\r\n root = d.documentElement,\r\n body = d.body;\r\n\r\n return {\r\n w: window.innerWidth || root.clientWidth || body.clientWidth,\r\n h: window.innerHeight || root.clientHeight || body.clientHeight\r\n };\r\n}\r\n\r\n/**\r\n * 将当前页面 URL 的 query 部分解析成键值对对象。\r\n *\r\n * @returns {Record<string, string>} 所有查询参数组成的平凡对象\r\n * (同名 key 仅保留最后一项)\r\n */\r\nexport function getAllSearchParams() {\r\n const urlSearchParams = new URLSearchParams(location.search);\r\n return Object.fromEntries(urlSearchParams.entries());\r\n}\r\n\r\n/**\r\n * 根据 key 获取当前页面 URL 中的单个查询参数。\r\n *\r\n * @param {string} key - 要提取的参数名\r\n * @returns {string | undefined} 对应参数值;不存在时返回 `undefined`\r\n */\r\nexport function getSearchParam(key) {\r\n const params = getAllSearchParams();\r\n return params[key];\r\n}\r\n\r\n/**\r\n * 全屏操作辅助工具对象\r\n * @namespace fullscreenHelper\r\n */\r\nexport const fullscreenHelper = {\r\n /**\r\n * 请求进入全屏模式\r\n * @param {Element} element - 要全屏显示的元素\r\n * @returns {Promise<void> | undefined} 全屏请求 Promise(如支持)\r\n */\r\n requestFullscreen: (element) => {\r\n if (!element) {\r\n console.warn(\"未提供有效的 DOM 元素\");\r\n return;\r\n }\r\n if (element.requestFullscreen) {\r\n return element.requestFullscreen();\r\n } else if (element.mozRequestFullScreen) {\r\n return element.mozRequestFullScreen();\r\n } else if (element.webkitRequestFullscreen) {\r\n return element.webkitRequestFullscreen();\r\n } else if (element.msRequestFullscreen) {\r\n return element.msRequestFullscreen();\r\n } else {\r\n console.warn(\"当前浏览器不支持全屏 API\");\r\n }\r\n },\r\n\r\n /**\r\n * 退出全屏模式\r\n * @returns {Promise<void> | undefined} 退出全屏请求 Promise(如支持)\r\n */\r\n exitFullscreen: () => {\r\n if (document.exitFullscreen) {\r\n return document.exitFullscreen();\r\n } else if (document.mozCancelFullScreen) {\r\n return document.mozCancelFullScreen();\r\n } else if (document.webkitExitFullscreen) {\r\n return document.webkitExitFullscreen();\r\n } else if (document.msExitFullscreen) {\r\n return document.msExitFullscreen();\r\n }\r\n },\r\n\r\n /**\r\n * 获取当前全屏元素\r\n * @returns {Element | null} 当前处于全屏模式的元素,无则返回 null\r\n */\r\n getFullscreenElement: () => {\r\n return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement || null;\r\n },\r\n\r\n /**\r\n * 检测当前是否处于全屏模式\r\n * @returns {boolean} 是否全屏中\r\n */\r\n isFullscreen: () => {\r\n return !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement);\r\n },\r\n\r\n /**\r\n * 检测浏览器是否支持全屏 API\r\n * @returns {boolean} 是否支持全屏\r\n */\r\n isFullscreenEnabled: () => {\r\n return !!(document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled);\r\n },\r\n\r\n /**\r\n * 切换指定元素的全屏状态\r\n * @param {Element} element - 要切换全屏的元素\r\n * @returns {Promise<void> | undefined} 全屏操作 Promise\r\n */\r\n toggleFullscreen: (element) => {\r\n if (fullscreenHelper.isFullscreen()) {\r\n return fullscreenHelper.exitFullscreen();\r\n } else {\r\n return fullscreenHelper.requestFullscreen(element);\r\n }\r\n },\r\n\r\n /**\r\n * 监听全屏变化事件\r\n * @param {Function} callback - 全屏状态变化时的回调函数,参数为 isFullscreen: boolean\r\n * @returns {Function} 取消监听的函数\r\n */\r\n onFullscreenChange: (callback) => {\r\n const handler = () => {\r\n callback(fullscreenHelper.isFullscreen());\r\n };\r\n document.addEventListener(\"fullscreenchange\", handler);\r\n document.addEventListener(\"webkitfullscreenchange\", handler);\r\n document.addEventListener(\"mozfullscreenchange\", handler);\r\n document.addEventListener(\"msfullscreenchange\", handler);\r\n\r\n return () => {\r\n document.removeEventListener(\"fullscreenchange\", handler);\r\n document.removeEventListener(\"webkitfullscreenchange\", handler);\r\n document.removeEventListener(\"mozfullscreenchange\", handler);\r\n document.removeEventListener(\"msfullscreenchange\", handler);\r\n };\r\n }\r\n};\r\n\r\n/**\r\n * 复制文本到剪贴板\r\n * @param {String} text\r\n * @returns {Promise<void>}\r\n */\r\nexport async function copyTextToClipboard(text) {\r\n if (window.navigator.clipboard) {\r\n await window.navigator.clipboard.writeText(text);\r\n return;\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const ta = document.createElement(\"textarea\");\r\n ta.value = text;\r\n ta.style.position = \"fixed\"; // 防止滚动\r\n ta.style.opacity = \"0\";\r\n document.body.appendChild(ta);\r\n ta.focus();\r\n ta.select();\r\n\r\n try {\r\n document.execCommand(\"copy\") ? resolve() : reject(new Error(\"execCommand failed\"));\r\n } catch (err) {\r\n reject(err);\r\n } finally {\r\n document.body.removeChild(ta);\r\n }\r\n });\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","sources":["../src/source/browser.js"],"sourcesContent":["/**\r\n * 视口尺寸对象。\r\n * @typedef {Object} ViewportDimensions\r\n * @property {number} w 视口宽度,单位像素。\r\n * @property {number} h 视口高度,单位像素。\r\n */\r\n\r\n/**\r\n * 获取当前视口(viewport)的宽高。\r\n *\r\n * 兼容策略:\r\n * 1. 优先使用 `window.innerWidth/innerHeight`(现代浏览器)。\r\n * 2. 降级到 `document.documentElement.clientWidth/clientHeight`(IE9+ 及怪异模式)。\r\n * 3. 最后降级到 `document.body.clientWidth/clientHeight`(IE6-8 怪异模式)。\r\n *\r\n * @returns {ViewportDimensions} 包含 `w`(宽度)和 `h`(高度)的对象,单位为像素。\r\n *\r\n * @example\r\n * const { w, h } = getViewportSize();\r\n * console.log(`视口尺寸:${w} × ${h}`);\r\n */\r\nexport function getViewportSize() {\r\n const d = document,\r\n root = d.documentElement,\r\n body = d.body;\r\n\r\n return {\r\n w: window.innerWidth || root.clientWidth || body.clientWidth,\r\n h: window.innerHeight || root.clientHeight || body.clientHeight\r\n };\r\n}\r\n\r\n/**\r\n * 将当前页面 URL 的 query 部分解析成键值对对象。\r\n *\r\n * @returns {Record<string, string>} 所有查询参数组成的平凡对象\r\n * (同名 key 仅保留最后一项)\r\n */\r\nexport function getAllSearchParams() {\r\n const urlSearchParams = new URLSearchParams(location.search);\r\n return Object.fromEntries(urlSearchParams.entries());\r\n}\r\n\r\n/**\r\n * 根据 key 获取当前页面 URL 中的单个查询参数。\r\n *\r\n * @param {string} key - 要提取的参数名\r\n * @returns {string | undefined} 对应参数值;不存在时返回 `undefined`\r\n */\r\nexport function getSearchParam(key) {\r\n const params = getAllSearchParams();\r\n return params[key];\r\n}\r\n\r\n/**\r\n * 全屏操作辅助工具对象\r\n * @namespace fullscreenHelper\r\n */\r\nexport const fullscreenHelper = {\r\n /**\r\n * 请求进入全屏模式\r\n * @param {Element} element - 要全屏显示的元素\r\n * @returns {Promise<void> | undefined} 全屏请求 Promise(如支持)\r\n */\r\n requestFullscreen: (element) => {\r\n if (!element) {\r\n console.warn(\"未提供有效的 DOM 元素\");\r\n return;\r\n }\r\n if (element.requestFullscreen) {\r\n return element.requestFullscreen();\r\n } else if (element.mozRequestFullScreen) {\r\n return element.mozRequestFullScreen();\r\n } else if (element.webkitRequestFullscreen) {\r\n return element.webkitRequestFullscreen();\r\n } else if (element.msRequestFullscreen) {\r\n return element.msRequestFullscreen();\r\n } else {\r\n console.warn(\"当前浏览器不支持全屏 API\");\r\n }\r\n },\r\n\r\n /**\r\n * 退出全屏模式\r\n * @returns {Promise<void> | undefined} 退出全屏请求 Promise(如支持)\r\n */\r\n exitFullscreen: () => {\r\n if (document.exitFullscreen) {\r\n return document.exitFullscreen();\r\n } else if (document.mozCancelFullScreen) {\r\n return document.mozCancelFullScreen();\r\n } else if (document.webkitExitFullscreen) {\r\n return document.webkitExitFullscreen();\r\n } else if (document.msExitFullscreen) {\r\n return document.msExitFullscreen();\r\n }\r\n },\r\n\r\n /**\r\n * 获取当前全屏元素\r\n * @returns {Element | null} 当前处于全屏模式的元素,无则返回 null\r\n */\r\n getFullscreenElement: () => {\r\n return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement || null;\r\n },\r\n\r\n /**\r\n * 检测当前是否处于全屏模式\r\n * @returns {boolean} 是否全屏中\r\n */\r\n isFullscreen: () => {\r\n return !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement);\r\n },\r\n\r\n /**\r\n * 检测浏览器是否支持全屏 API\r\n * @returns {boolean} 是否支持全屏\r\n */\r\n isFullscreenEnabled: () => {\r\n return !!(document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled);\r\n },\r\n\r\n /**\r\n * 切换指定元素的全屏状态\r\n * @param {Element} element - 要切换全屏的元素\r\n * @returns {Promise<void> | undefined} 全屏操作 Promise\r\n */\r\n toggleFullscreen: (element) => {\r\n if (fullscreenHelper.isFullscreen()) {\r\n return fullscreenHelper.exitFullscreen();\r\n } else {\r\n return fullscreenHelper.requestFullscreen(element);\r\n }\r\n },\r\n\r\n /**\r\n * 监听全屏变化事件\r\n * @param {Function} callback - 全屏状态变化时的回调函数,参数为 isFullscreen: boolean\r\n * @returns {Function} 取消监听的函数\r\n */\r\n onFullscreenChange: (callback) => {\r\n const handler = () => {\r\n callback(fullscreenHelper.isFullscreen());\r\n };\r\n document.addEventListener(\"fullscreenchange\", handler);\r\n document.addEventListener(\"webkitfullscreenchange\", handler);\r\n document.addEventListener(\"mozfullscreenchange\", handler);\r\n document.addEventListener(\"msfullscreenchange\", handler);\r\n\r\n return () => {\r\n document.removeEventListener(\"fullscreenchange\", handler);\r\n document.removeEventListener(\"webkitfullscreenchange\", handler);\r\n document.removeEventListener(\"mozfullscreenchange\", handler);\r\n document.removeEventListener(\"msfullscreenchange\", handler);\r\n };\r\n }\r\n};\r\n\r\n/**\r\n * 复制文本到剪贴板\r\n * @param {String} text\r\n * @returns {Promise<void>}\r\n */\r\nexport async function copyTextToClipboard(text) {\r\n if (window.navigator.clipboard) {\r\n await window.navigator.clipboard.writeText(text);\r\n return;\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const ta = document.createElement(\"textarea\");\r\n ta.value = text;\r\n ta.style.position = \"fixed\"; // 防止滚动\r\n ta.style.opacity = \"0\";\r\n document.body.appendChild(ta);\r\n ta.focus();\r\n ta.select();\r\n\r\n try {\r\n document.execCommand(\"copy\") ? resolve() : reject(new Error(\"execCommand failed\"));\r\n } catch (err) {\r\n reject(err);\r\n } finally {\r\n document.body.removeChild(ta);\r\n }\r\n });\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"browser.js","sources":["../src/source/browser.js"],"sourcesContent":["/**\r\n * 视口尺寸对象。\r\n * @typedef {Object} ViewportDimensions\r\n * @property {number} w 视口宽度,单位像素。\r\n * @property {number} h 视口高度,单位像素。\r\n */\r\n\r\n/**\r\n * 获取当前视口(viewport)的宽高。\r\n *\r\n * 兼容策略:\r\n * 1. 优先使用 `window.innerWidth/innerHeight`(现代浏览器)。\r\n * 2. 降级到 `document.documentElement.clientWidth/clientHeight`(IE9+ 及怪异模式)。\r\n * 3. 最后降级到 `document.body.clientWidth/clientHeight`(IE6-8 怪异模式)。\r\n *\r\n * @returns {ViewportDimensions} 包含 `w`(宽度)和 `h`(高度)的对象,单位为像素。\r\n *\r\n * @example\r\n * const { w, h } = getViewportSize();\r\n * console.log(`视口尺寸:${w} × ${h}`);\r\n */\r\nexport function getViewportSize() {\r\n const d = document,\r\n root = d.documentElement,\r\n body = d.body;\r\n\r\n return {\r\n w: window.innerWidth || root.clientWidth || body.clientWidth,\r\n h: window.innerHeight || root.clientHeight || body.clientHeight\r\n };\r\n}\r\n\r\n/**\r\n * 将当前页面 URL 的 query 部分解析成键值对对象。\r\n *\r\n * @returns {Record<string, string>} 所有查询参数组成的平凡对象\r\n * (同名 key 仅保留最后一项)\r\n */\r\nexport function getAllSearchParams() {\r\n const urlSearchParams = new URLSearchParams(location.search);\r\n return Object.fromEntries(urlSearchParams.entries());\r\n}\r\n\r\n/**\r\n * 根据 key 获取当前页面 URL 中的单个查询参数。\r\n *\r\n * @param {string} key - 要提取的参数名\r\n * @returns {string | undefined} 对应参数值;不存在时返回 `undefined`\r\n */\r\nexport function getSearchParam(key) {\r\n const params = getAllSearchParams();\r\n return params[key];\r\n}\r\n\r\n/**\r\n * 全屏操作辅助工具对象\r\n * @namespace fullscreenHelper\r\n */\r\nexport const fullscreenHelper = {\r\n /**\r\n * 请求进入全屏模式\r\n * @param {Element} element - 要全屏显示的元素\r\n * @returns {Promise<void> | undefined} 全屏请求 Promise(如支持)\r\n */\r\n requestFullscreen: (element) => {\r\n if (!element) {\r\n console.warn(\"未提供有效的 DOM 元素\");\r\n return;\r\n }\r\n if (element.requestFullscreen) {\r\n return element.requestFullscreen();\r\n } else if (element.mozRequestFullScreen) {\r\n return element.mozRequestFullScreen();\r\n } else if (element.webkitRequestFullscreen) {\r\n return element.webkitRequestFullscreen();\r\n } else if (element.msRequestFullscreen) {\r\n return element.msRequestFullscreen();\r\n } else {\r\n console.warn(\"当前浏览器不支持全屏 API\");\r\n }\r\n },\r\n\r\n /**\r\n * 退出全屏模式\r\n * @returns {Promise<void> | undefined} 退出全屏请求 Promise(如支持)\r\n */\r\n exitFullscreen: () => {\r\n if (document.exitFullscreen) {\r\n return document.exitFullscreen();\r\n } else if (document.mozCancelFullScreen) {\r\n return document.mozCancelFullScreen();\r\n } else if (document.webkitExitFullscreen) {\r\n return document.webkitExitFullscreen();\r\n } else if (document.msExitFullscreen) {\r\n return document.msExitFullscreen();\r\n }\r\n },\r\n\r\n /**\r\n * 获取当前全屏元素\r\n * @returns {Element | null} 当前处于全屏模式的元素,无则返回 null\r\n */\r\n getFullscreenElement: () => {\r\n return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement || null;\r\n },\r\n\r\n /**\r\n * 检测当前是否处于全屏模式\r\n * @returns {boolean} 是否全屏中\r\n */\r\n isFullscreen: () => {\r\n return !!(document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement);\r\n },\r\n\r\n /**\r\n * 检测浏览器是否支持全屏 API\r\n * @returns {boolean} 是否支持全屏\r\n */\r\n isFullscreenEnabled: () => {\r\n return !!(document.fullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled);\r\n },\r\n\r\n /**\r\n * 切换指定元素的全屏状态\r\n * @param {Element} element - 要切换全屏的元素\r\n * @returns {Promise<void> | undefined} 全屏操作 Promise\r\n */\r\n toggleFullscreen: (element) => {\r\n if (fullscreenHelper.isFullscreen()) {\r\n return fullscreenHelper.exitFullscreen();\r\n } else {\r\n return fullscreenHelper.requestFullscreen(element);\r\n }\r\n },\r\n\r\n /**\r\n * 监听全屏变化事件\r\n * @param {Function} callback - 全屏状态变化时的回调函数,参数为 isFullscreen: boolean\r\n * @returns {Function} 取消监听的函数\r\n */\r\n onFullscreenChange: (callback) => {\r\n const handler = () => {\r\n callback(fullscreenHelper.isFullscreen());\r\n };\r\n document.addEventListener(\"fullscreenchange\", handler);\r\n document.addEventListener(\"webkitfullscreenchange\", handler);\r\n document.addEventListener(\"mozfullscreenchange\", handler);\r\n document.addEventListener(\"msfullscreenchange\", handler);\r\n\r\n return () => {\r\n document.removeEventListener(\"fullscreenchange\", handler);\r\n document.removeEventListener(\"webkitfullscreenchange\", handler);\r\n document.removeEventListener(\"mozfullscreenchange\", handler);\r\n document.removeEventListener(\"msfullscreenchange\", handler);\r\n };\r\n }\r\n};\r\n\r\n/**\r\n * 复制文本到剪贴板\r\n * @param {String} text\r\n * @returns {Promise<void>}\r\n */\r\nexport async function copyTextToClipboard(text) {\r\n if (window.navigator.clipboard) {\r\n await window.navigator.clipboard.writeText(text);\r\n return;\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n const ta = document.createElement(\"textarea\");\r\n ta.value = text;\r\n ta.style.position = \"fixed\"; // 防止滚动\r\n ta.style.opacity = \"0\";\r\n document.body.appendChild(ta);\r\n ta.focus();\r\n ta.select();\r\n\r\n try {\r\n document.execCommand(\"copy\") ? resolve() : reject(new Error(\"execCommand failed\"));\r\n } catch (err) {\r\n reject(err);\r\n } finally {\r\n document.body.removeChild(ta);\r\n }\r\n });\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"common.cjs","sources":["../src/source/common.js"],"sourcesContent":["//#region 数据类型判断\r\n\r\n/**\r\n * 返回任意值的运行时类型字符串(小写形式)。\r\n * \r\n * @param {*} obj 待检测的值\r\n * @returns {keyof globalThis|\"blob\"|\"file\"|\"formdata\"|string} 小写类型名\r\n */\r\nexport function getDataType(obj) {\r\n return Object.prototype.toString\r\n .call(obj)\r\n .replace(/^\\[object\\s(\\w+)\\]$/, \"$1\")\r\n .toLowerCase();\r\n}\r\n\r\n/**\r\n * 判断值是否为原生 Blob(含 File)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Blob}\r\n */\r\nexport function isBlob(obj) {\r\n return getDataType(obj) === \"blob\";\r\n}\r\n\r\n/**\r\n * 判断值是否为**纯粹**的 Object(即 `{}` 或 `new Object()`,不含数组、null、自定义类等)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Record<PropertyKey, any>}\r\n */\r\nexport function isPlainObject(obj) {\r\n return getDataType(obj) === \"object\";\r\n}\r\n\r\n/**\r\n * 判断值是否为 Promise(含 Promise 子类)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Promise<any>}\r\n */\r\nexport function isPromise(obj) {\r\n return getDataType(obj) === \"promise\";\r\n}\r\n\r\n/**\r\n * 判断值是否为合法 Date 对象(含 Invalid Date 返回 false)。\r\n *\r\n * @param {*} t - 待检测值\r\n * @returns {t is Date}\r\n */\r\nexport function isDate(t) {\r\n return getDataType(t) === \"date\";\r\n}\r\n\r\n/**\r\n * 判断值是否为函数(含异步函数、生成器函数、类)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Function}\r\n */\r\nexport function isFunction(obj) {\r\n return typeof obj === \"function\";\r\n}\r\n\r\n/**\r\n * 判断值是否为**非空**字符串。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is string}\r\n */\r\nexport function isNonEmptyString(obj) {\r\n return getDataType(obj) === \"string\" && obj.length > 0;\r\n}\r\n\r\n//#endregion\r\n\r\n//#region 随机数据\r\n\r\n/**\r\n * 在闭区间 [min, max] 内生成一个均匀分布的随机整数。\r\n * 若 min > max 则自动交换。\r\n *\r\n * @param {number} min - 整数下界(包含)\r\n * @param {number} max - 整数上界(包含)\r\n * @returns {number}\r\n * @throws {TypeError} 当 min 或 max 不是整数时抛出\r\n */\r\nexport function randomIntInRange(min, max) {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new TypeError(\"Arguments must be integers\");\r\n }\r\n if (min > max) [min, max] = [max, min];\r\n // 注意加 1,否则 max 永远取不到;Math.floor 保证均匀\r\n return Math.floor(Math.random() * (max - min + 1)) + min;\r\n}\r\n\r\n/**\r\n * 随机生成一个汉字(可控制范围)。\r\n *\r\n * @param {boolean} [base=true] - 是否启用基本区(0x4E00-0x9FA5)\r\n * @param {boolean} [extA=false] - 是否启用扩展 A 区(0x3400-0x4DBF)\r\n * @param {boolean} [extBH=false] - 是否启用扩展 B~H 区(0x20000-0x2EBEF,代理对)\r\n * @returns {string} 单个汉字字符\r\n * @throws {RangeError} 未启用任何区段时抛出\r\n */\r\nexport function randomHan(base = true, extA = false, extBH = false) {\r\n // 1. 收集已启用的“区段”\r\n const ranges = [];\r\n if (base) ranges.push({ min: 0x4e00, max: 0x9fa5, surrogate: false });\r\n if (extA) ranges.push({ min: 0x3400, max: 0x4dbf, surrogate: false });\r\n if (extBH) ranges.push({ min: 0x20000, max: 0x2ebef, surrogate: true });\r\n\r\n if (ranges.length === 0) {\r\n throw new RangeError(\"At least one range must be enabled\");\r\n }\r\n\r\n // 2. 按总码位数抽号\r\n const total = ranges.reduce((sum, r) => sum + (r.max - r.min + 1), 0);\r\n let n = randomIntInRange(0, total - 1);\r\n\r\n // 3. 定位落在哪个区段\r\n for (const { min, max, surrogate } of ranges) {\r\n const size = max - min + 1;\r\n if (n < size) {\r\n const code = min + n;\r\n if (!surrogate) return String.fromCharCode(code);\r\n // 代理对\r\n const offset = code - 0x10000;\r\n const hi = (offset >> 10) + 0xd800;\r\n const lo = (offset & 0x3ff) + 0xdc00;\r\n return String.fromCharCode(hi, lo);\r\n }\r\n n -= size;\r\n }\r\n}\r\n\r\n/**\r\n * 随机生成一个英文字母。\r\n *\r\n * @param {'lower'|'upper'} [type] - 指定大小写;留空则随机\r\n * @returns {string} 单个字母\r\n */\r\nexport function randomEnLetter(type) {\r\n const lower = \"abcdefghijklmnopqrstuvwxyz\";\r\n const upper = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\r\n const randomNum = randomIntInRange(0, 25);\r\n\r\n switch (type) {\r\n case \"lower\":\r\n return lower[randomNum];\r\n case \"upper\":\r\n return upper[randomNum];\r\n default:\r\n return (Math.random() < 0.5 ? lower : upper)[randomNum];\r\n }\r\n}\r\n\r\n/**\r\n * 生成指定长度的随机“中英混合”字符串。\r\n *\r\n * @param {number} [len=1] - 目标长度(≥1,自动取整)\r\n * @param {number} [zhProb=0.5] - 每个位置选择汉字的概率,默认 0.5\r\n * @returns {string}\r\n */\r\nexport function randomHanOrEn(len, zhProb = 0.5) {\r\n len = Math.max(1, Math.floor(len));\r\n const buf = [];\r\n for (let i = 0; i < len; i++) {\r\n buf.push(Math.random() < zhProb ? randomHan() : randomEnLetter());\r\n }\r\n return buf.join(\"\");\r\n}\r\n\r\n//#endregion\r\n\r\n//#region 防抖节流\r\n\r\n/**\r\n * 创建 debounced(防抖)函数。\r\n * - 默认 trailing 触发;当 `leading=true` 时,首次调用或超过等待间隔会立即执行。\r\n * - 支持手动取消。\r\n *\r\n * @template {(...args: any[]) => any} T\r\n * @param {T} fn - 要防抖的原始函数\r\n * @param {number} wait - 防抖等待时间(毫秒)\r\n * @param {boolean} [leading=false] - 是否启用立即执行(leading edge)\r\n * @returns {T & { cancel(): void }} 返回经过防抖包装的函数,并附带 `cancel` 方法\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\nexport function debounce(fn, wait, leading = false) {\r\n if (typeof fn !== \"function\") throw new TypeError(\"fn must be function\");\r\n wait = Math.max(0, Number(wait) || 0);\r\n let timeoutId;\r\n let lastCall = 0; // 0 表示从未调用过\r\n\r\n function debounced(...args) {\r\n const isFirst = lastCall === 0;\r\n const isOverWait = Date.now() - lastCall >= wait;\r\n\r\n clearTimeout(timeoutId);\r\n\r\n // 首次调用 || 已达到等待间隔\r\n if (leading && (isFirst || isOverWait)) {\r\n lastCall = Date.now();\r\n return fn.apply(this, args);\r\n }\r\n\r\n timeoutId = setTimeout(() => {\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n }, wait);\r\n }\r\n\r\n debounced.cancel = () => {\r\n clearTimeout(timeoutId);\r\n lastCall = 0; // 恢复初始状态\r\n };\r\n\r\n return debounced;\r\n}\r\n\r\n/**\r\n * 创建 throttled(节流)函数。\r\n * 支持 leading/trailing 边缘触发,可手动取消。\r\n *\r\n * @template {(...args: any[]) => any} T\r\n * @param {T} fn - 要节流的原始函数\r\n * @param {number} wait - 节流间隔(毫秒)\r\n * @param {object} [options] - 配置项\r\n * @param {boolean} [options.leading=true] - 是否在 leading 边缘执行\r\n * @param {boolean} [options.trailing=true] - 是否在 trailing 边缘执行\r\n * @returns {T & { cancel(): void }} 返回经过节流包装的函数,并附带 `cancel` 方法\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\nexport function throttle(fn, wait, { leading = true, trailing = true } = {}) {\r\n if (typeof fn !== \"function\") throw new TypeError(\"fn must be function\");\r\n wait = Math.max(0, Number(wait) || 0);\r\n\r\n let timeoutId = null,\r\n lastCall = 0;\r\n\r\n function throttled(...args) {\r\n const remaining = wait - (Date.now() - lastCall);\r\n\r\n if (leading && (lastCall === 0 || remaining <= 0)) {\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n } else if (trailing && !timeoutId) {\r\n timeoutId = setTimeout(\r\n () => {\r\n timeoutId = null;\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n },\r\n remaining > 0 ? remaining : wait\r\n );\r\n }\r\n }\r\n\r\n throttled.cancel = () => {\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastCall = 0;\r\n };\r\n\r\n return throttled;\r\n}\r\n\r\n//#endregion\r\n\r\n/**\r\n * 利用 JSON 序列化/反序列化实现**深拷贝**。\r\n * 注意:会丢失 `undefined`、函数、循环引用、特殊包装对象等。\r\n *\r\n * @template T\r\n * @param {T} obj - 待拷贝的 JSON 兼容值\r\n * @returns {T} 深拷贝后的值\r\n */\r\nexport function deepCloneByJSON(obj) {\r\n return JSON.parse(JSON.stringify(obj));\r\n}\r\n\r\n/**\r\n * **安全**地将源对象中**已存在**的属性赋值到目标对象。\r\n * 不会新增键,也不会复制原型链上的属性。\r\n *\r\n * @template {Record<PropertyKey, any>} T\r\n * @param {T} target - 目标对象(将被就地修改)\r\n * @param {...Partial<T>} sources - 一个或多个源对象\r\n * @returns {T} 修改后的目标对象(即第一个参数本身)\r\n *\r\n * @example\r\n * const defaults = { a: 1, b: 2 };\r\n * assignExisting(defaults, { a: 9, c: 99 }); // defaults 变为 { a: 9, b: 2 }\r\n */\r\nexport function assignExisting(target, ...sources) {\r\n sources.forEach((source) => {\r\n Object.keys(source).forEach((key) => {\r\n if (target.hasOwnProperty(key)) {\r\n target[key] = source[key];\r\n }\r\n });\r\n });\r\n return target;\r\n}\r\n\r\n/**\r\n * 提取任意函数(含箭头函数、普通函数、async、class 构造器)的形参名称列表。\r\n * 通过源码正则解析,不支持解构参数、默认参数、剩余参数等复杂语法;\r\n * 若出现上述场景将返回空数组或部分名称。\r\n *\r\n * @param {Function} fn - 目标函数\r\n * @returns {string[]} 按声明顺序排列的参数名数组;解析失败时返回空数组\r\n *\r\n * @example\r\n * getFunctionArgNames(function (a, b) {}) // [\"a\", \"b\"]\r\n * getFunctionArgNames((foo, bar) => {}) // [\"foo\", \"bar\"]\r\n * getFunctionArgNames(async function x({a} = {}) {}) // [] (解构无法识别)\r\n */\r\nexport function getFunctionArgNames(fn) {\r\n const FN_ARG_SPLIT = /,/,\r\n FN_ARG = /^\\s*(_?)(\\S+?)\\1\\s*$/,\r\n FN_ARGS = /^[^(]*\\(\\s*([^)]*)\\)/m,\r\n ARROW_ARG = /^([^(]+?)=>/,\r\n STRIP_COMMENTS = /((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/gm;\r\n\r\n const fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, \"\");\r\n const argDecl = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);\r\n const retArgNames = [];\r\n [].forEach.call(argDecl[1].split(FN_ARG_SPLIT), function (arg) {\r\n arg.replace(FN_ARG, function (all, underscore, name) {\r\n retArgNames.push(name);\r\n });\r\n });\r\n return retArgNames;\r\n}\r\n\r\n/**\r\n * 将 Blob(或 File)读取为文本,并可选择自动执行 `JSON.parse`。\r\n * 当 `isParse=true` 且内容非法 JSON 时,会回退为返回原始文本。\r\n *\r\n * @param {Blob} blob - 待读取的 Blob/File 对象\r\n * @param {boolean} [isParse=true] - 是否尝试将结果按 JSON 解析\r\n * @returns {Promise<string | any>} 解析后的 JSON 对象或原始文本\r\n *\r\n * @example\r\n * const json = await readBlobAsText(blob); // 自动 JSON.parse\r\n * const text = await readBlobAsText(blob, false); // 仅返回文本\r\n */\r\nexport function readBlobAsText(blob, isParse = true) {\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = (evt) => {\r\n const result = evt.target.result;\r\n if (isParse) {\r\n try {\r\n resolve(JSON.parse(result));\r\n } catch (error) {\r\n console.error(error);\r\n resolve(result);\r\n }\r\n } else {\r\n resolve(result);\r\n }\r\n };\r\n reader.onerror = reject;\r\n reader.readAsText(blob);\r\n });\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"common.cjs","sources":["../src/source/common.js"],"sourcesContent":["//#region 数据类型判断\r\n\r\n/**\r\n * 返回任意值的运行时类型字符串(小写形式)。\r\n * \r\n * @param {*} obj 待检测的值\r\n * @returns {keyof globalThis|\"blob\"|\"file\"|\"formdata\"|string} 小写类型名\r\n */\r\nexport function getDataType(obj) {\r\n return Object.prototype.toString\r\n .call(obj)\r\n .replace(/^\\[object\\s(\\w+)\\]$/, \"$1\")\r\n .toLowerCase();\r\n}\r\n\r\n/**\r\n * 判断值是否为原生 Blob(含 File)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Blob}\r\n */\r\nexport function isBlob(obj) {\r\n return getDataType(obj) === \"blob\";\r\n}\r\n\r\n/**\r\n * 判断值是否为**纯粹**的 Object(即 `{}` 或 `new Object()`,不含数组、null、自定义类等)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Record<PropertyKey, any>}\r\n */\r\nexport function isPlainObject(obj) {\r\n return getDataType(obj) === \"object\";\r\n}\r\n\r\n/**\r\n * 判断值是否为 Promise(含 Promise 子类)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Promise<any>}\r\n */\r\nexport function isPromise(obj) {\r\n return getDataType(obj) === \"promise\";\r\n}\r\n\r\n/**\r\n * 判断值是否为合法 Date 对象(含 Invalid Date 返回 false)。\r\n *\r\n * @param {*} t - 待检测值\r\n * @returns {t is Date}\r\n */\r\nexport function isDate(t) {\r\n return getDataType(t) === \"date\";\r\n}\r\n\r\n/**\r\n * 判断值是否为函数(含异步函数、生成器函数、类)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Function}\r\n */\r\nexport function isFunction(obj) {\r\n return typeof obj === \"function\";\r\n}\r\n\r\n/**\r\n * 判断值是否为**非空**字符串。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is string}\r\n */\r\nexport function isNonEmptyString(obj) {\r\n return getDataType(obj) === \"string\" && obj.length > 0;\r\n}\r\n\r\n//#endregion\r\n\r\n//#region 随机数据\r\n\r\n/**\r\n * 在闭区间 [min, max] 内生成一个均匀分布的随机整数。\r\n * 若 min > max 则自动交换。\r\n *\r\n * @param {number} min - 整数下界(包含)\r\n * @param {number} max - 整数上界(包含)\r\n * @returns {number}\r\n * @throws {TypeError} 当 min 或 max 不是整数时抛出\r\n */\r\nexport function randomIntInRange(min, max) {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new TypeError(\"Arguments must be integers\");\r\n }\r\n if (min > max) [min, max] = [max, min];\r\n // 注意加 1,否则 max 永远取不到;Math.floor 保证均匀\r\n return Math.floor(Math.random() * (max - min + 1)) + min;\r\n}\r\n\r\n/**\r\n * 随机生成一个汉字(可控制范围)。\r\n *\r\n * @param {boolean} [base=true] - 是否启用基本区(0x4E00-0x9FA5)\r\n * @param {boolean} [extA=false] - 是否启用扩展 A 区(0x3400-0x4DBF)\r\n * @param {boolean} [extBH=false] - 是否启用扩展 B~H 区(0x20000-0x2EBEF,代理对)\r\n * @returns {string} 单个汉字字符\r\n * @throws {RangeError} 未启用任何区段时抛出\r\n */\r\nexport function randomHan(base = true, extA = false, extBH = false) {\r\n // 1. 收集已启用的“区段”\r\n const ranges = [];\r\n if (base) ranges.push({ min: 0x4e00, max: 0x9fa5, surrogate: false });\r\n if (extA) ranges.push({ min: 0x3400, max: 0x4dbf, surrogate: false });\r\n if (extBH) ranges.push({ min: 0x20000, max: 0x2ebef, surrogate: true });\r\n\r\n if (ranges.length === 0) {\r\n throw new RangeError(\"At least one range must be enabled\");\r\n }\r\n\r\n // 2. 按总码位数抽号\r\n const total = ranges.reduce((sum, r) => sum + (r.max - r.min + 1), 0);\r\n let n = randomIntInRange(0, total - 1);\r\n\r\n // 3. 定位落在哪个区段\r\n for (const { min, max, surrogate } of ranges) {\r\n const size = max - min + 1;\r\n if (n < size) {\r\n const code = min + n;\r\n if (!surrogate) return String.fromCharCode(code);\r\n // 代理对\r\n const offset = code - 0x10000;\r\n const hi = (offset >> 10) + 0xd800;\r\n const lo = (offset & 0x3ff) + 0xdc00;\r\n return String.fromCharCode(hi, lo);\r\n }\r\n n -= size;\r\n }\r\n}\r\n\r\n/**\r\n * 随机生成一个英文字母。\r\n *\r\n * @param {'lower'|'upper'} [type] - 指定大小写;留空则随机\r\n * @returns {string} 单个字母\r\n */\r\nexport function randomEnLetter(type) {\r\n const lower = \"abcdefghijklmnopqrstuvwxyz\";\r\n const upper = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\r\n const randomNum = randomIntInRange(0, 25);\r\n\r\n switch (type) {\r\n case \"lower\":\r\n return lower[randomNum];\r\n case \"upper\":\r\n return upper[randomNum];\r\n default:\r\n return (Math.random() < 0.5 ? lower : upper)[randomNum];\r\n }\r\n}\r\n\r\n/**\r\n * 生成指定长度的随机“中英混合”字符串。\r\n *\r\n * @param {number} [len=1] - 目标长度(≥1,自动取整)\r\n * @param {number} [zhProb=0.5] - 每个位置选择汉字的概率,默认 0.5\r\n * @returns {string}\r\n */\r\nexport function randomHanOrEn(len, zhProb = 0.5) {\r\n len = Math.max(1, Math.floor(len));\r\n const buf = [];\r\n for (let i = 0; i < len; i++) {\r\n buf.push(Math.random() < zhProb ? randomHan() : randomEnLetter());\r\n }\r\n return buf.join(\"\");\r\n}\r\n\r\n//#endregion\r\n\r\n//#region 防抖节流\r\n\r\n/**\r\n * 创建 debounced(防抖)函数。\r\n * - 默认 trailing 触发;当 `leading=true` 时,首次调用或超过等待间隔会立即执行。\r\n * - 支持手动取消。\r\n *\r\n * @template {(...args: any[]) => any} T\r\n * @param {T} fn - 要防抖的原始函数\r\n * @param {number} wait - 防抖等待时间(毫秒)\r\n * @param {boolean} [leading=false] - 是否启用立即执行(leading edge)\r\n * @returns {T & { cancel(): void }} 返回经过防抖包装的函数,并附带 `cancel` 方法\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\nexport function debounce(fn, wait, leading = false) {\r\n if (typeof fn !== \"function\") throw new TypeError(\"fn must be function\");\r\n wait = Math.max(0, Number(wait) || 0);\r\n let timeoutId;\r\n let lastCall = 0; // 0 表示从未调用过\r\n\r\n function debounced(...args) {\r\n const isFirst = lastCall === 0;\r\n const isOverWait = Date.now() - lastCall >= wait;\r\n\r\n clearTimeout(timeoutId);\r\n\r\n // 首次调用 || 已达到等待间隔\r\n if (leading && (isFirst || isOverWait)) {\r\n lastCall = Date.now();\r\n return fn.apply(this, args);\r\n }\r\n\r\n timeoutId = setTimeout(() => {\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n }, wait);\r\n }\r\n\r\n debounced.cancel = () => {\r\n clearTimeout(timeoutId);\r\n lastCall = 0; // 恢复初始状态\r\n };\r\n\r\n return debounced;\r\n}\r\n\r\n/**\r\n * 创建 throttled(节流)函数。\r\n * 支持 leading/trailing 边缘触发,可手动取消。\r\n *\r\n * @template {(...args: any[]) => any} T\r\n * @param {T} fn - 要节流的原始函数\r\n * @param {number} wait - 节流间隔(毫秒)\r\n * @param {object} [options] - 配置项\r\n * @param {boolean} [options.leading=true] - 是否在 leading 边缘执行\r\n * @param {boolean} [options.trailing=true] - 是否在 trailing 边缘执行\r\n * @returns {T & { cancel(): void }} 返回经过节流包装的函数,并附带 `cancel` 方法\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\nexport function throttle(fn, wait, { leading = true, trailing = true } = {}) {\r\n if (typeof fn !== \"function\") throw new TypeError(\"fn must be function\");\r\n wait = Math.max(0, Number(wait) || 0);\r\n\r\n let timeoutId = null,\r\n lastCall = 0;\r\n\r\n function throttled(...args) {\r\n const remaining = wait - (Date.now() - lastCall);\r\n\r\n if (leading && (lastCall === 0 || remaining <= 0)) {\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n } else if (trailing && !timeoutId) {\r\n timeoutId = setTimeout(\r\n () => {\r\n timeoutId = null;\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n },\r\n remaining > 0 ? remaining : wait\r\n );\r\n }\r\n }\r\n\r\n throttled.cancel = () => {\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastCall = 0;\r\n };\r\n\r\n return throttled;\r\n}\r\n\r\n//#endregion\r\n\r\n/**\r\n * 利用 JSON 序列化/反序列化实现**深拷贝**。\r\n * 注意:会丢失 `undefined`、函数、循环引用、特殊包装对象等。\r\n *\r\n * @template T\r\n * @param {T} obj - 待拷贝的 JSON 兼容值\r\n * @returns {T} 深拷贝后的值\r\n */\r\nexport function deepCloneByJSON(obj) {\r\n return JSON.parse(JSON.stringify(obj));\r\n}\r\n\r\n/**\r\n * **安全**地将源对象中**已存在**的属性赋值到目标对象。\r\n * 不会新增键,也不会复制原型链上的属性。\r\n *\r\n * @template {Record<PropertyKey, any>} T\r\n * @param {T} target - 目标对象(将被就地修改)\r\n * @param {...Partial<T>} sources - 一个或多个源对象\r\n * @returns {T} 修改后的目标对象(即第一个参数本身)\r\n *\r\n * @example\r\n * const defaults = { a: 1, b: 2 };\r\n * assignExisting(defaults, { a: 9, c: 99 }); // defaults 变为 { a: 9, b: 2 }\r\n */\r\nexport function assignExisting(target, ...sources) {\r\n sources.forEach((source) => {\r\n Object.keys(source).forEach((key) => {\r\n if (target.hasOwnProperty(key)) {\r\n target[key] = source[key];\r\n }\r\n });\r\n });\r\n return target;\r\n}\r\n\r\n/**\r\n * 提取任意函数(含箭头函数、普通函数、async、class 构造器)的形参名称列表。\r\n * 通过源码正则解析,不支持解构参数、默认参数、剩余参数等复杂语法;\r\n * 若出现上述场景将返回空数组或部分名称。\r\n *\r\n * @param {Function} fn - 目标函数\r\n * @returns {string[]} 按声明顺序排列的参数名数组;解析失败时返回空数组\r\n *\r\n * @example\r\n * getFunctionArgNames(function (a, b) {}) // [\"a\", \"b\"]\r\n * getFunctionArgNames((foo, bar) => {}) // [\"foo\", \"bar\"]\r\n * getFunctionArgNames(async function x({a} = {}) {}) // [] (解构无法识别)\r\n */\r\nexport function getFunctionArgNames(fn) {\r\n const FN_ARG_SPLIT = /,/,\r\n FN_ARG = /^\\s*(_?)(\\S+?)\\1\\s*$/,\r\n FN_ARGS = /^[^(]*\\(\\s*([^)]*)\\)/m,\r\n ARROW_ARG = /^([^(]+?)=>/,\r\n STRIP_COMMENTS = /((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/gm;\r\n\r\n const fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, \"\");\r\n const argDecl = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);\r\n const retArgNames = [];\r\n [].forEach.call(argDecl[1].split(FN_ARG_SPLIT), function (arg) {\r\n arg.replace(FN_ARG, function (all, underscore, name) {\r\n retArgNames.push(name);\r\n });\r\n });\r\n return retArgNames;\r\n}\r\n\r\n/**\r\n * 将 Blob(或 File)读取为文本,并可选择自动执行 `JSON.parse`。\r\n * 当 `isParse=true` 且内容非法 JSON 时,会回退为返回原始文本。\r\n *\r\n * @param {Blob} blob - 待读取的 Blob/File 对象\r\n * @param {boolean} [isParse=true] - 是否尝试将结果按 JSON 解析\r\n * @returns {Promise<string | any>} 解析后的 JSON 对象或原始文本\r\n *\r\n * @example\r\n * const json = await readBlobAsText(blob); // 自动 JSON.parse\r\n * const text = await readBlobAsText(blob, false); // 仅返回文本\r\n */\r\nexport function readBlobAsText(blob, isParse = true) {\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = (evt) => {\r\n const result = evt.target.result;\r\n if (isParse) {\r\n try {\r\n resolve(JSON.parse(result));\r\n } catch (error) {\r\n console.error(error);\r\n resolve(result);\r\n }\r\n } else {\r\n resolve(result);\r\n }\r\n };\r\n reader.onerror = reject;\r\n reader.readAsText(blob);\r\n });\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"common.js","sources":["../src/source/common.js"],"sourcesContent":["//#region 数据类型判断\r\n\r\n/**\r\n * 返回任意值的运行时类型字符串(小写形式)。\r\n * \r\n * @param {*} obj 待检测的值\r\n * @returns {keyof globalThis|\"blob\"|\"file\"|\"formdata\"|string} 小写类型名\r\n */\r\nexport function getDataType(obj) {\r\n return Object.prototype.toString\r\n .call(obj)\r\n .replace(/^\\[object\\s(\\w+)\\]$/, \"$1\")\r\n .toLowerCase();\r\n}\r\n\r\n/**\r\n * 判断值是否为原生 Blob(含 File)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Blob}\r\n */\r\nexport function isBlob(obj) {\r\n return getDataType(obj) === \"blob\";\r\n}\r\n\r\n/**\r\n * 判断值是否为**纯粹**的 Object(即 `{}` 或 `new Object()`,不含数组、null、自定义类等)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Record<PropertyKey, any>}\r\n */\r\nexport function isPlainObject(obj) {\r\n return getDataType(obj) === \"object\";\r\n}\r\n\r\n/**\r\n * 判断值是否为 Promise(含 Promise 子类)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Promise<any>}\r\n */\r\nexport function isPromise(obj) {\r\n return getDataType(obj) === \"promise\";\r\n}\r\n\r\n/**\r\n * 判断值是否为合法 Date 对象(含 Invalid Date 返回 false)。\r\n *\r\n * @param {*} t - 待检测值\r\n * @returns {t is Date}\r\n */\r\nexport function isDate(t) {\r\n return getDataType(t) === \"date\";\r\n}\r\n\r\n/**\r\n * 判断值是否为函数(含异步函数、生成器函数、类)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Function}\r\n */\r\nexport function isFunction(obj) {\r\n return typeof obj === \"function\";\r\n}\r\n\r\n/**\r\n * 判断值是否为**非空**字符串。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is string}\r\n */\r\nexport function isNonEmptyString(obj) {\r\n return getDataType(obj) === \"string\" && obj.length > 0;\r\n}\r\n\r\n//#endregion\r\n\r\n//#region 随机数据\r\n\r\n/**\r\n * 在闭区间 [min, max] 内生成一个均匀分布的随机整数。\r\n * 若 min > max 则自动交换。\r\n *\r\n * @param {number} min - 整数下界(包含)\r\n * @param {number} max - 整数上界(包含)\r\n * @returns {number}\r\n * @throws {TypeError} 当 min 或 max 不是整数时抛出\r\n */\r\nexport function randomIntInRange(min, max) {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new TypeError(\"Arguments must be integers\");\r\n }\r\n if (min > max) [min, max] = [max, min];\r\n // 注意加 1,否则 max 永远取不到;Math.floor 保证均匀\r\n return Math.floor(Math.random() * (max - min + 1)) + min;\r\n}\r\n\r\n/**\r\n * 随机生成一个汉字(可控制范围)。\r\n *\r\n * @param {boolean} [base=true] - 是否启用基本区(0x4E00-0x9FA5)\r\n * @param {boolean} [extA=false] - 是否启用扩展 A 区(0x3400-0x4DBF)\r\n * @param {boolean} [extBH=false] - 是否启用扩展 B~H 区(0x20000-0x2EBEF,代理对)\r\n * @returns {string} 单个汉字字符\r\n * @throws {RangeError} 未启用任何区段时抛出\r\n */\r\nexport function randomHan(base = true, extA = false, extBH = false) {\r\n // 1. 收集已启用的“区段”\r\n const ranges = [];\r\n if (base) ranges.push({ min: 0x4e00, max: 0x9fa5, surrogate: false });\r\n if (extA) ranges.push({ min: 0x3400, max: 0x4dbf, surrogate: false });\r\n if (extBH) ranges.push({ min: 0x20000, max: 0x2ebef, surrogate: true });\r\n\r\n if (ranges.length === 0) {\r\n throw new RangeError(\"At least one range must be enabled\");\r\n }\r\n\r\n // 2. 按总码位数抽号\r\n const total = ranges.reduce((sum, r) => sum + (r.max - r.min + 1), 0);\r\n let n = randomIntInRange(0, total - 1);\r\n\r\n // 3. 定位落在哪个区段\r\n for (const { min, max, surrogate } of ranges) {\r\n const size = max - min + 1;\r\n if (n < size) {\r\n const code = min + n;\r\n if (!surrogate) return String.fromCharCode(code);\r\n // 代理对\r\n const offset = code - 0x10000;\r\n const hi = (offset >> 10) + 0xd800;\r\n const lo = (offset & 0x3ff) + 0xdc00;\r\n return String.fromCharCode(hi, lo);\r\n }\r\n n -= size;\r\n }\r\n}\r\n\r\n/**\r\n * 随机生成一个英文字母。\r\n *\r\n * @param {'lower'|'upper'} [type] - 指定大小写;留空则随机\r\n * @returns {string} 单个字母\r\n */\r\nexport function randomEnLetter(type) {\r\n const lower = \"abcdefghijklmnopqrstuvwxyz\";\r\n const upper = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\r\n const randomNum = randomIntInRange(0, 25);\r\n\r\n switch (type) {\r\n case \"lower\":\r\n return lower[randomNum];\r\n case \"upper\":\r\n return upper[randomNum];\r\n default:\r\n return (Math.random() < 0.5 ? lower : upper)[randomNum];\r\n }\r\n}\r\n\r\n/**\r\n * 生成指定长度的随机“中英混合”字符串。\r\n *\r\n * @param {number} [len=1] - 目标长度(≥1,自动取整)\r\n * @param {number} [zhProb=0.5] - 每个位置选择汉字的概率,默认 0.5\r\n * @returns {string}\r\n */\r\nexport function randomHanOrEn(len, zhProb = 0.5) {\r\n len = Math.max(1, Math.floor(len));\r\n const buf = [];\r\n for (let i = 0; i < len; i++) {\r\n buf.push(Math.random() < zhProb ? randomHan() : randomEnLetter());\r\n }\r\n return buf.join(\"\");\r\n}\r\n\r\n//#endregion\r\n\r\n//#region 防抖节流\r\n\r\n/**\r\n * 创建 debounced(防抖)函数。\r\n * - 默认 trailing 触发;当 `leading=true` 时,首次调用或超过等待间隔会立即执行。\r\n * - 支持手动取消。\r\n *\r\n * @template {(...args: any[]) => any} T\r\n * @param {T} fn - 要防抖的原始函数\r\n * @param {number} wait - 防抖等待时间(毫秒)\r\n * @param {boolean} [leading=false] - 是否启用立即执行(leading edge)\r\n * @returns {T & { cancel(): void }} 返回经过防抖包装的函数,并附带 `cancel` 方法\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\nexport function debounce(fn, wait, leading = false) {\r\n if (typeof fn !== \"function\") throw new TypeError(\"fn must be function\");\r\n wait = Math.max(0, Number(wait) || 0);\r\n let timeoutId;\r\n let lastCall = 0; // 0 表示从未调用过\r\n\r\n function debounced(...args) {\r\n const isFirst = lastCall === 0;\r\n const isOverWait = Date.now() - lastCall >= wait;\r\n\r\n clearTimeout(timeoutId);\r\n\r\n // 首次调用 || 已达到等待间隔\r\n if (leading && (isFirst || isOverWait)) {\r\n lastCall = Date.now();\r\n return fn.apply(this, args);\r\n }\r\n\r\n timeoutId = setTimeout(() => {\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n }, wait);\r\n }\r\n\r\n debounced.cancel = () => {\r\n clearTimeout(timeoutId);\r\n lastCall = 0; // 恢复初始状态\r\n };\r\n\r\n return debounced;\r\n}\r\n\r\n/**\r\n * 创建 throttled(节流)函数。\r\n * 支持 leading/trailing 边缘触发,可手动取消。\r\n *\r\n * @template {(...args: any[]) => any} T\r\n * @param {T} fn - 要节流的原始函数\r\n * @param {number} wait - 节流间隔(毫秒)\r\n * @param {object} [options] - 配置项\r\n * @param {boolean} [options.leading=true] - 是否在 leading 边缘执行\r\n * @param {boolean} [options.trailing=true] - 是否在 trailing 边缘执行\r\n * @returns {T & { cancel(): void }} 返回经过节流包装的函数,并附带 `cancel` 方法\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\nexport function throttle(fn, wait, { leading = true, trailing = true } = {}) {\r\n if (typeof fn !== \"function\") throw new TypeError(\"fn must be function\");\r\n wait = Math.max(0, Number(wait) || 0);\r\n\r\n let timeoutId = null,\r\n lastCall = 0;\r\n\r\n function throttled(...args) {\r\n const remaining = wait - (Date.now() - lastCall);\r\n\r\n if (leading && (lastCall === 0 || remaining <= 0)) {\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n } else if (trailing && !timeoutId) {\r\n timeoutId = setTimeout(\r\n () => {\r\n timeoutId = null;\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n },\r\n remaining > 0 ? remaining : wait\r\n );\r\n }\r\n }\r\n\r\n throttled.cancel = () => {\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastCall = 0;\r\n };\r\n\r\n return throttled;\r\n}\r\n\r\n//#endregion\r\n\r\n/**\r\n * 利用 JSON 序列化/反序列化实现**深拷贝**。\r\n * 注意:会丢失 `undefined`、函数、循环引用、特殊包装对象等。\r\n *\r\n * @template T\r\n * @param {T} obj - 待拷贝的 JSON 兼容值\r\n * @returns {T} 深拷贝后的值\r\n */\r\nexport function deepCloneByJSON(obj) {\r\n return JSON.parse(JSON.stringify(obj));\r\n}\r\n\r\n/**\r\n * **安全**地将源对象中**已存在**的属性赋值到目标对象。\r\n * 不会新增键,也不会复制原型链上的属性。\r\n *\r\n * @template {Record<PropertyKey, any>} T\r\n * @param {T} target - 目标对象(将被就地修改)\r\n * @param {...Partial<T>} sources - 一个或多个源对象\r\n * @returns {T} 修改后的目标对象(即第一个参数本身)\r\n *\r\n * @example\r\n * const defaults = { a: 1, b: 2 };\r\n * assignExisting(defaults, { a: 9, c: 99 }); // defaults 变为 { a: 9, b: 2 }\r\n */\r\nexport function assignExisting(target, ...sources) {\r\n sources.forEach((source) => {\r\n Object.keys(source).forEach((key) => {\r\n if (target.hasOwnProperty(key)) {\r\n target[key] = source[key];\r\n }\r\n });\r\n });\r\n return target;\r\n}\r\n\r\n/**\r\n * 提取任意函数(含箭头函数、普通函数、async、class 构造器)的形参名称列表。\r\n * 通过源码正则解析,不支持解构参数、默认参数、剩余参数等复杂语法;\r\n * 若出现上述场景将返回空数组或部分名称。\r\n *\r\n * @param {Function} fn - 目标函数\r\n * @returns {string[]} 按声明顺序排列的参数名数组;解析失败时返回空数组\r\n *\r\n * @example\r\n * getFunctionArgNames(function (a, b) {}) // [\"a\", \"b\"]\r\n * getFunctionArgNames((foo, bar) => {}) // [\"foo\", \"bar\"]\r\n * getFunctionArgNames(async function x({a} = {}) {}) // [] (解构无法识别)\r\n */\r\nexport function getFunctionArgNames(fn) {\r\n const FN_ARG_SPLIT = /,/,\r\n FN_ARG = /^\\s*(_?)(\\S+?)\\1\\s*$/,\r\n FN_ARGS = /^[^(]*\\(\\s*([^)]*)\\)/m,\r\n ARROW_ARG = /^([^(]+?)=>/,\r\n STRIP_COMMENTS = /((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/gm;\r\n\r\n const fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, \"\");\r\n const argDecl = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);\r\n const retArgNames = [];\r\n [].forEach.call(argDecl[1].split(FN_ARG_SPLIT), function (arg) {\r\n arg.replace(FN_ARG, function (all, underscore, name) {\r\n retArgNames.push(name);\r\n });\r\n });\r\n return retArgNames;\r\n}\r\n\r\n/**\r\n * 将 Blob(或 File)读取为文本,并可选择自动执行 `JSON.parse`。\r\n * 当 `isParse=true` 且内容非法 JSON 时,会回退为返回原始文本。\r\n *\r\n * @param {Blob} blob - 待读取的 Blob/File 对象\r\n * @param {boolean} [isParse=true] - 是否尝试将结果按 JSON 解析\r\n * @returns {Promise<string | any>} 解析后的 JSON 对象或原始文本\r\n *\r\n * @example\r\n * const json = await readBlobAsText(blob); // 自动 JSON.parse\r\n * const text = await readBlobAsText(blob, false); // 仅返回文本\r\n */\r\nexport function readBlobAsText(blob, isParse = true) {\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = (evt) => {\r\n const result = evt.target.result;\r\n if (isParse) {\r\n try {\r\n resolve(JSON.parse(result));\r\n } catch (error) {\r\n console.error(error);\r\n resolve(result);\r\n }\r\n } else {\r\n resolve(result);\r\n }\r\n };\r\n reader.onerror = reject;\r\n reader.readAsText(blob);\r\n });\r\n}\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"common.js","sources":["../src/source/common.js"],"sourcesContent":["//#region 数据类型判断\r\n\r\n/**\r\n * 返回任意值的运行时类型字符串(小写形式)。\r\n * \r\n * @param {*} obj 待检测的值\r\n * @returns {keyof globalThis|\"blob\"|\"file\"|\"formdata\"|string} 小写类型名\r\n */\r\nexport function getDataType(obj) {\r\n return Object.prototype.toString\r\n .call(obj)\r\n .replace(/^\\[object\\s(\\w+)\\]$/, \"$1\")\r\n .toLowerCase();\r\n}\r\n\r\n/**\r\n * 判断值是否为原生 Blob(含 File)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Blob}\r\n */\r\nexport function isBlob(obj) {\r\n return getDataType(obj) === \"blob\";\r\n}\r\n\r\n/**\r\n * 判断值是否为**纯粹**的 Object(即 `{}` 或 `new Object()`,不含数组、null、自定义类等)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Record<PropertyKey, any>}\r\n */\r\nexport function isPlainObject(obj) {\r\n return getDataType(obj) === \"object\";\r\n}\r\n\r\n/**\r\n * 判断值是否为 Promise(含 Promise 子类)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Promise<any>}\r\n */\r\nexport function isPromise(obj) {\r\n return getDataType(obj) === \"promise\";\r\n}\r\n\r\n/**\r\n * 判断值是否为合法 Date 对象(含 Invalid Date 返回 false)。\r\n *\r\n * @param {*} t - 待检测值\r\n * @returns {t is Date}\r\n */\r\nexport function isDate(t) {\r\n return getDataType(t) === \"date\";\r\n}\r\n\r\n/**\r\n * 判断值是否为函数(含异步函数、生成器函数、类)。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is Function}\r\n */\r\nexport function isFunction(obj) {\r\n return typeof obj === \"function\";\r\n}\r\n\r\n/**\r\n * 判断值是否为**非空**字符串。\r\n * \r\n * @param {*} obj - 待检测的值\r\n * @returns {obj is string}\r\n */\r\nexport function isNonEmptyString(obj) {\r\n return getDataType(obj) === \"string\" && obj.length > 0;\r\n}\r\n\r\n//#endregion\r\n\r\n//#region 随机数据\r\n\r\n/**\r\n * 在闭区间 [min, max] 内生成一个均匀分布的随机整数。\r\n * 若 min > max 则自动交换。\r\n *\r\n * @param {number} min - 整数下界(包含)\r\n * @param {number} max - 整数上界(包含)\r\n * @returns {number}\r\n * @throws {TypeError} 当 min 或 max 不是整数时抛出\r\n */\r\nexport function randomIntInRange(min, max) {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new TypeError(\"Arguments must be integers\");\r\n }\r\n if (min > max) [min, max] = [max, min];\r\n // 注意加 1,否则 max 永远取不到;Math.floor 保证均匀\r\n return Math.floor(Math.random() * (max - min + 1)) + min;\r\n}\r\n\r\n/**\r\n * 随机生成一个汉字(可控制范围)。\r\n *\r\n * @param {boolean} [base=true] - 是否启用基本区(0x4E00-0x9FA5)\r\n * @param {boolean} [extA=false] - 是否启用扩展 A 区(0x3400-0x4DBF)\r\n * @param {boolean} [extBH=false] - 是否启用扩展 B~H 区(0x20000-0x2EBEF,代理对)\r\n * @returns {string} 单个汉字字符\r\n * @throws {RangeError} 未启用任何区段时抛出\r\n */\r\nexport function randomHan(base = true, extA = false, extBH = false) {\r\n // 1. 收集已启用的“区段”\r\n const ranges = [];\r\n if (base) ranges.push({ min: 0x4e00, max: 0x9fa5, surrogate: false });\r\n if (extA) ranges.push({ min: 0x3400, max: 0x4dbf, surrogate: false });\r\n if (extBH) ranges.push({ min: 0x20000, max: 0x2ebef, surrogate: true });\r\n\r\n if (ranges.length === 0) {\r\n throw new RangeError(\"At least one range must be enabled\");\r\n }\r\n\r\n // 2. 按总码位数抽号\r\n const total = ranges.reduce((sum, r) => sum + (r.max - r.min + 1), 0);\r\n let n = randomIntInRange(0, total - 1);\r\n\r\n // 3. 定位落在哪个区段\r\n for (const { min, max, surrogate } of ranges) {\r\n const size = max - min + 1;\r\n if (n < size) {\r\n const code = min + n;\r\n if (!surrogate) return String.fromCharCode(code);\r\n // 代理对\r\n const offset = code - 0x10000;\r\n const hi = (offset >> 10) + 0xd800;\r\n const lo = (offset & 0x3ff) + 0xdc00;\r\n return String.fromCharCode(hi, lo);\r\n }\r\n n -= size;\r\n }\r\n}\r\n\r\n/**\r\n * 随机生成一个英文字母。\r\n *\r\n * @param {'lower'|'upper'} [type] - 指定大小写;留空则随机\r\n * @returns {string} 单个字母\r\n */\r\nexport function randomEnLetter(type) {\r\n const lower = \"abcdefghijklmnopqrstuvwxyz\";\r\n const upper = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\";\r\n const randomNum = randomIntInRange(0, 25);\r\n\r\n switch (type) {\r\n case \"lower\":\r\n return lower[randomNum];\r\n case \"upper\":\r\n return upper[randomNum];\r\n default:\r\n return (Math.random() < 0.5 ? lower : upper)[randomNum];\r\n }\r\n}\r\n\r\n/**\r\n * 生成指定长度的随机“中英混合”字符串。\r\n *\r\n * @param {number} [len=1] - 目标长度(≥1,自动取整)\r\n * @param {number} [zhProb=0.5] - 每个位置选择汉字的概率,默认 0.5\r\n * @returns {string}\r\n */\r\nexport function randomHanOrEn(len, zhProb = 0.5) {\r\n len = Math.max(1, Math.floor(len));\r\n const buf = [];\r\n for (let i = 0; i < len; i++) {\r\n buf.push(Math.random() < zhProb ? randomHan() : randomEnLetter());\r\n }\r\n return buf.join(\"\");\r\n}\r\n\r\n//#endregion\r\n\r\n//#region 防抖节流\r\n\r\n/**\r\n * 创建 debounced(防抖)函数。\r\n * - 默认 trailing 触发;当 `leading=true` 时,首次调用或超过等待间隔会立即执行。\r\n * - 支持手动取消。\r\n *\r\n * @template {(...args: any[]) => any} T\r\n * @param {T} fn - 要防抖的原始函数\r\n * @param {number} wait - 防抖等待时间(毫秒)\r\n * @param {boolean} [leading=false] - 是否启用立即执行(leading edge)\r\n * @returns {T & { cancel(): void }} 返回经过防抖包装的函数,并附带 `cancel` 方法\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\nexport function debounce(fn, wait, leading = false) {\r\n if (typeof fn !== \"function\") throw new TypeError(\"fn must be function\");\r\n wait = Math.max(0, Number(wait) || 0);\r\n let timeoutId;\r\n let lastCall = 0; // 0 表示从未调用过\r\n\r\n function debounced(...args) {\r\n const isFirst = lastCall === 0;\r\n const isOverWait = Date.now() - lastCall >= wait;\r\n\r\n clearTimeout(timeoutId);\r\n\r\n // 首次调用 || 已达到等待间隔\r\n if (leading && (isFirst || isOverWait)) {\r\n lastCall = Date.now();\r\n return fn.apply(this, args);\r\n }\r\n\r\n timeoutId = setTimeout(() => {\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n }, wait);\r\n }\r\n\r\n debounced.cancel = () => {\r\n clearTimeout(timeoutId);\r\n lastCall = 0; // 恢复初始状态\r\n };\r\n\r\n return debounced;\r\n}\r\n\r\n/**\r\n * 创建 throttled(节流)函数。\r\n * 支持 leading/trailing 边缘触发,可手动取消。\r\n *\r\n * @template {(...args: any[]) => any} T\r\n * @param {T} fn - 要节流的原始函数\r\n * @param {number} wait - 节流间隔(毫秒)\r\n * @param {object} [options] - 配置项\r\n * @param {boolean} [options.leading=true] - 是否在 leading 边缘执行\r\n * @param {boolean} [options.trailing=true] - 是否在 trailing 边缘执行\r\n * @returns {T & { cancel(): void }} 返回经过节流包装的函数,并附带 `cancel` 方法\r\n * @throws {TypeError} 当 `fn` 不是函数时抛出\r\n */\r\nexport function throttle(fn, wait, { leading = true, trailing = true } = {}) {\r\n if (typeof fn !== \"function\") throw new TypeError(\"fn must be function\");\r\n wait = Math.max(0, Number(wait) || 0);\r\n\r\n let timeoutId = null,\r\n lastCall = 0;\r\n\r\n function throttled(...args) {\r\n const remaining = wait - (Date.now() - lastCall);\r\n\r\n if (leading && (lastCall === 0 || remaining <= 0)) {\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n } else if (trailing && !timeoutId) {\r\n timeoutId = setTimeout(\r\n () => {\r\n timeoutId = null;\r\n lastCall = Date.now();\r\n fn.apply(this, args);\r\n },\r\n remaining > 0 ? remaining : wait\r\n );\r\n }\r\n }\r\n\r\n throttled.cancel = () => {\r\n clearTimeout(timeoutId);\r\n timeoutId = null;\r\n lastCall = 0;\r\n };\r\n\r\n return throttled;\r\n}\r\n\r\n//#endregion\r\n\r\n/**\r\n * 利用 JSON 序列化/反序列化实现**深拷贝**。\r\n * 注意:会丢失 `undefined`、函数、循环引用、特殊包装对象等。\r\n *\r\n * @template T\r\n * @param {T} obj - 待拷贝的 JSON 兼容值\r\n * @returns {T} 深拷贝后的值\r\n */\r\nexport function deepCloneByJSON(obj) {\r\n return JSON.parse(JSON.stringify(obj));\r\n}\r\n\r\n/**\r\n * **安全**地将源对象中**已存在**的属性赋值到目标对象。\r\n * 不会新增键,也不会复制原型链上的属性。\r\n *\r\n * @template {Record<PropertyKey, any>} T\r\n * @param {T} target - 目标对象(将被就地修改)\r\n * @param {...Partial<T>} sources - 一个或多个源对象\r\n * @returns {T} 修改后的目标对象(即第一个参数本身)\r\n *\r\n * @example\r\n * const defaults = { a: 1, b: 2 };\r\n * assignExisting(defaults, { a: 9, c: 99 }); // defaults 变为 { a: 9, b: 2 }\r\n */\r\nexport function assignExisting(target, ...sources) {\r\n sources.forEach((source) => {\r\n Object.keys(source).forEach((key) => {\r\n if (target.hasOwnProperty(key)) {\r\n target[key] = source[key];\r\n }\r\n });\r\n });\r\n return target;\r\n}\r\n\r\n/**\r\n * 提取任意函数(含箭头函数、普通函数、async、class 构造器)的形参名称列表。\r\n * 通过源码正则解析,不支持解构参数、默认参数、剩余参数等复杂语法;\r\n * 若出现上述场景将返回空数组或部分名称。\r\n *\r\n * @param {Function} fn - 目标函数\r\n * @returns {string[]} 按声明顺序排列的参数名数组;解析失败时返回空数组\r\n *\r\n * @example\r\n * getFunctionArgNames(function (a, b) {}) // [\"a\", \"b\"]\r\n * getFunctionArgNames((foo, bar) => {}) // [\"foo\", \"bar\"]\r\n * getFunctionArgNames(async function x({a} = {}) {}) // [] (解构无法识别)\r\n */\r\nexport function getFunctionArgNames(fn) {\r\n const FN_ARG_SPLIT = /,/,\r\n FN_ARG = /^\\s*(_?)(\\S+?)\\1\\s*$/,\r\n FN_ARGS = /^[^(]*\\(\\s*([^)]*)\\)/m,\r\n ARROW_ARG = /^([^(]+?)=>/,\r\n STRIP_COMMENTS = /((\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/))/gm;\r\n\r\n const fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, \"\");\r\n const argDecl = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);\r\n const retArgNames = [];\r\n [].forEach.call(argDecl[1].split(FN_ARG_SPLIT), function (arg) {\r\n arg.replace(FN_ARG, function (all, underscore, name) {\r\n retArgNames.push(name);\r\n });\r\n });\r\n return retArgNames;\r\n}\r\n\r\n/**\r\n * 将 Blob(或 File)读取为文本,并可选择自动执行 `JSON.parse`。\r\n * 当 `isParse=true` 且内容非法 JSON 时,会回退为返回原始文本。\r\n *\r\n * @param {Blob} blob - 待读取的 Blob/File 对象\r\n * @param {boolean} [isParse=true] - 是否尝试将结果按 JSON 解析\r\n * @returns {Promise<string | any>} 解析后的 JSON 对象或原始文本\r\n *\r\n * @example\r\n * const json = await readBlobAsText(blob); // 自动 JSON.parse\r\n * const text = await readBlobAsText(blob, false); // 仅返回文本\r\n */\r\nexport function readBlobAsText(blob, isParse = true) {\r\n return new Promise((resolve, reject) => {\r\n const reader = new FileReader();\r\n reader.onload = (evt) => {\r\n const result = evt.target.result;\r\n if (isParse) {\r\n try {\r\n resolve(JSON.parse(result));\r\n } catch (error) {\r\n console.error(error);\r\n resolve(result);\r\n }\r\n } else {\r\n resolve(result);\r\n }\r\n };\r\n reader.onerror = reject;\r\n reader.readAsText(blob);\r\n });\r\n}\r\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,WAAW,CAAC,GAAG,EAAE;AACjC,IAAI,OAAO,MAAM,CAAC,SAAS,CAAC,QAAQ;AACpC,SAAS,IAAI,CAAC,GAAG,CAAC;AAClB,SAAS,OAAO,CAAC,qBAAqB,EAAE,IAAI,CAAC;AAC7C,SAAS,WAAW,EAAE,CAAC;AACvB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,MAAM,CAAC,GAAG,EAAE;AAC5B,IAAI,OAAO,WAAW,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC;AACvC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,GAAG,EAAE;AACnC,IAAI,OAAO,WAAW,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC;AACzC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,SAAS,CAAC,GAAG,EAAE;AAC/B,IAAI,OAAO,WAAW,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;AAC1C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,MAAM,CAAC,CAAC,EAAE;AAC1B,IAAI,OAAO,WAAW,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;AACrC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,UAAU,CAAC,GAAG,EAAE;AAChC,IAAI,OAAO,OAAO,GAAG,KAAK,UAAU,CAAC;AACrC,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,GAAG,EAAE;AACtC,IAAI,OAAO,WAAW,CAAC,GAAG,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;AAC3D,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,gBAAgB,CAAC,GAAG,EAAE,GAAG,EAAE;AAC3C,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE;AAC1D,QAAQ,MAAM,IAAI,SAAS,CAAC,4BAA4B,CAAC,CAAC;AAC1D,IAAI,CAAC;AACL,IAAI,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC3C;AACA,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC;AAC7D,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,SAAS,CAAC,IAAI,GAAG,IAAI,EAAE,IAAI,GAAG,KAAK,EAAE,KAAK,GAAG,KAAK,EAAE;AACpE;AACA,IAAI,MAAM,MAAM,GAAG,EAAE,CAAC;AACtB,IAAI,IAAI,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1E,IAAI,IAAI,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1E,IAAI,IAAI,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AAC5E;AACA,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;AAC7B,QAAQ,MAAM,IAAI,UAAU,CAAC,oCAAoC,CAAC,CAAC;AACnE,IAAI,CAAC;AACL;AACA;AACA,IAAI,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1E,IAAI,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;AAC3C;AACA;AACA,IAAI,KAAK,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,MAAM,EAAE;AAClD,QAAQ,MAAM,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AACnC,QAAQ,IAAI,CAAC,GAAG,IAAI,EAAE;AACtB,YAAY,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC;AACjC,YAAY,IAAI,CAAC,SAAS,EAAE,OAAO,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AAC7D;AACA,YAAY,MAAM,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;AAC1C,YAAY,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC;AAC/C,YAAY,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,KAAK,IAAI,MAAM,CAAC;AACjD,YAAY,OAAO,MAAM,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;AAC/C,QAAQ,CAAC;AACT,QAAQ,CAAC,IAAI,IAAI,CAAC;AAClB,IAAI,CAAC;AACL,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,IAAI,EAAE;AACrC,IAAI,MAAM,KAAK,GAAG,4BAA4B,CAAC;AAC/C,IAAI,MAAM,KAAK,GAAG,4BAA4B,CAAC;AAC/C,IAAI,MAAM,SAAS,GAAG,gBAAgB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC9C;AACA,IAAI,QAAQ,IAAI;AAChB,QAAQ,KAAK,OAAO;AACpB,YAAY,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC;AACpC,QAAQ,KAAK,OAAO;AACpB,YAAY,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC;AACpC,QAAQ;AACR,YAAY,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,KAAK,GAAG,KAAK,EAAE,SAAS,CAAC,CAAC;AACpE,KAAK;AACL,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE;AACjD,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACvC,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;AACnB,IAAI,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE;AAClC,QAAQ,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,EAAE,GAAG,cAAc,EAAE,CAAC,CAAC;AAC1E,IAAI,CAAC;AACL,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,GAAG,KAAK,EAAE;AACpD,IAAI,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAC;AAC7E,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1C,IAAI,IAAI,SAAS,CAAC;AAClB,IAAI,IAAI,QAAQ,GAAG,CAAC,CAAC;AACrB;AACA,IAAI,SAAS,SAAS,CAAC,GAAG,IAAI,EAAE;AAChC,QAAQ,MAAM,OAAO,GAAG,QAAQ,KAAK,CAAC,CAAC;AACvC,QAAQ,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,IAAI,IAAI,CAAC;AACzD;AACA,QAAQ,YAAY,CAAC,SAAS,CAAC,CAAC;AAChC;AACA;AACA,QAAQ,IAAI,OAAO,KAAK,OAAO,IAAI,UAAU,CAAC,EAAE;AAChD,YAAY,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAClC,YAAY,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACxC,QAAQ,CAAC;AACT;AACA,QAAQ,SAAS,GAAG,UAAU,CAAC,MAAM;AACrC,YAAY,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAClC,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACjC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;AACjB,IAAI,CAAC;AACL;AACA,IAAI,SAAS,CAAC,MAAM,GAAG,MAAM;AAC7B,QAAQ,YAAY,CAAC,SAAS,CAAC,CAAC;AAChC,QAAQ,QAAQ,GAAG,CAAC,CAAC;AACrB,IAAI,CAAC,CAAC;AACN;AACA,IAAI,OAAO,SAAS,CAAC;AACrB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,GAAG,IAAI,EAAE,QAAQ,GAAG,IAAI,EAAE,GAAG,EAAE,EAAE;AAC7E,IAAI,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,IAAI,SAAS,CAAC,qBAAqB,CAAC,CAAC;AAC7E,IAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1C;AACA,IAAI,IAAI,SAAS,GAAG,IAAI;AACxB,QAAQ,QAAQ,GAAG,CAAC,CAAC;AACrB;AACA,IAAI,SAAS,SAAS,CAAC,GAAG,IAAI,EAAE;AAChC,QAAQ,MAAM,SAAS,GAAG,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC;AACzD;AACA,QAAQ,IAAI,OAAO,KAAK,QAAQ,KAAK,CAAC,IAAI,SAAS,IAAI,CAAC,CAAC,EAAE;AAC3D,YAAY,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAClC,YAAY,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACjC,QAAQ,CAAC,MAAM,IAAI,QAAQ,IAAI,CAAC,SAAS,EAAE;AAC3C,YAAY,SAAS,GAAG,UAAU;AAClC,gBAAgB,MAAM;AACtB,oBAAoB,SAAS,GAAG,IAAI,CAAC;AACrC,oBAAoB,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAC1C,oBAAoB,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACzC,gBAAgB,CAAC;AACjB,gBAAgB,SAAS,GAAG,CAAC,GAAG,SAAS,GAAG,IAAI;AAChD,aAAa,CAAC;AACd,QAAQ,CAAC;AACT,IAAI,CAAC;AACL;AACA,IAAI,SAAS,CAAC,MAAM,GAAG,MAAM;AAC7B,QAAQ,YAAY,CAAC,SAAS,CAAC,CAAC;AAChC,QAAQ,SAAS,GAAG,IAAI,CAAC;AACzB,QAAQ,QAAQ,GAAG,CAAC,CAAC;AACrB,IAAI,CAAC,CAAC;AACN;AACA,IAAI,OAAO,SAAS,CAAC;AACrB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,eAAe,CAAC,GAAG,EAAE;AACrC,IAAI,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3C,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,MAAM,EAAE,GAAG,OAAO,EAAE;AACnD,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK;AAChC,QAAQ,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK;AAC7C,YAAY,IAAI,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;AAC5C,gBAAgB,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;AAC1C,YAAY,CAAC;AACb,QAAQ,CAAC,CAAC,CAAC;AACX,IAAI,CAAC,CAAC,CAAC;AACP,IAAI,OAAO,MAAM,CAAC;AAClB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,mBAAmB,CAAC,EAAE,EAAE;AACxC,IAAI,MAAM,YAAY,GAAG,GAAG;AAC5B,QAAQ,MAAM,GAAG,sBAAsB;AACvC,QAAQ,OAAO,GAAG,uBAAuB;AACzC,QAAQ,SAAS,GAAG,aAAa;AACjC,QAAQ,cAAc,GAAG,kCAAkC,CAAC;AAC5D;AACA,IAAI,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;AACpF,IAAI,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACrE,IAAI,MAAM,WAAW,GAAG,EAAE,CAAC;AAC3B,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,UAAU,GAAG,EAAE;AACnE,QAAQ,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE;AAC7D,YAAY,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,QAAQ,CAAC,CAAC,CAAC;AACX,IAAI,CAAC,CAAC,CAAC;AACP,IAAI,OAAO,WAAW,CAAC;AACvB,CAAC;AACD;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAAS,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE;AACrD,IAAI,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK;AAC5C,QAAQ,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;AACxC,QAAQ,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK;AACjC,YAAY,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;AAC7C,YAAY,IAAI,OAAO,EAAE;AACzB,gBAAgB,IAAI;AACpB,oBAAoB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;AAChD,gBAAgB,CAAC,CAAC,OAAO,KAAK,EAAE;AAChC,oBAAoB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACzC,oBAAoB,OAAO,CAAC,MAAM,CAAC,CAAC;AACpC,gBAAgB,CAAC;AACjB,YAAY,CAAC,MAAM;AACnB,gBAAgB,OAAO,CAAC,MAAM,CAAC,CAAC;AAChC,YAAY,CAAC;AACb,QAAQ,CAAC,CAAC;AACV,QAAQ,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC;AAChC,QAAQ,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAChC,IAAI,CAAC,CAAC,CAAC;AACP;;"}