@base-web-kits/base-tools-web 1.1.11 → 1.1.18-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/async/index.d.ts +26 -0
- package/dist/async/index.d.ts.map +1 -0
- package/dist/base-tools-web.umd.global.js +133 -19
- package/dist/base-tools-web.umd.global.js.map +1 -1
- package/dist/config/index.d.ts +3 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/index.cjs +137 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +134 -5
- package/dist/index.js.map +1 -1
- package/dist/network/{load.d.ts → download.d.ts} +1 -1
- package/dist/network/download.d.ts.map +1 -0
- package/dist/network/index.d.ts +2 -1
- package/dist/network/index.d.ts.map +1 -1
- package/dist/network/request.d.ts +11 -3
- package/dist/network/request.d.ts.map +1 -1
- package/dist/network/uploadFile.d.ts +57 -0
- package/dist/network/uploadFile.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/web/async/index.ts +82 -0
- package/src/web/config/index.ts +1 -1
- package/src/web/network/index.ts +2 -1
- package/src/web/network/request.ts +33 -12
- package/src/web/network/uploadFile.ts +152 -0
- package/dist/network/load.d.ts.map +0 -1
- /package/src/web/network/{load.ts → download.ts} +0 -0
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/web/clipboard/index.ts","../src/web/config/index.ts","../src/web/cookie/index.ts","../src/web/device/index.ts","../src/web/dom/index.ts","../src/web/network/load.ts","../src/ts/index.ts","../src/ts/array/index.ts","../src/ts/async/index.ts","../src/ts/bean/EventBus.ts","../src/ts/day/index.ts","../src/ts/number/big.ts","../src/ts/number/format.ts","../src/ts/number/random.ts","../src/ts/es-toolkit/index.ts","../src/ts/object/index.ts","../src/ts/string/format.ts","../src/ts/string/random.ts","../src/ts/string/other.ts","../src/ts/url/file/index.ts","../src/ts/url/oss/index.ts","../src/ts/url/param/index.ts","../src/ts/url/qn/index.ts","../src/ts/validator/index.ts","../src/web/network/request.ts","../src/web/storage/index.ts","../src/web/url/index.ts"],"sourcesContent":["/**\r\n * 复制文本到剪贴板(兼容移动端和PC)\r\n * @returns Promise<void> 复制成功时 resolve,失败时 reject。\r\n * @example\r\n * await copyText('hello');\r\n * toast('复制成功');\r\n */\r\nexport async function copyText(text: string): Promise<void> {\r\n if (typeof text !== 'string') text = String(text ?? '');\r\n\r\n // 现代 API\r\n if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {\r\n try {\r\n await navigator.clipboard.writeText(text);\r\n return;\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n } catch (e) {\r\n // 继续尝试回退方案\r\n }\r\n }\r\n\r\n // 回退方案:使用隐藏 textarea + execCommand('copy')\r\n return new Promise<void>((resolve, reject) => {\r\n try {\r\n const textarea = document.createElement('textarea');\r\n textarea.value = text;\r\n\r\n // 避免视觉影响与页面布局影响\r\n textarea.setAttribute('readonly', '');\r\n textarea.style.position = 'fixed';\r\n textarea.style.top = '0';\r\n textarea.style.right = '-9999px';\r\n textarea.style.opacity = '0';\r\n textarea.style.pointerEvents = 'none';\r\n\r\n document.body.appendChild(textarea);\r\n\r\n // 选中文本(移动端兼容)\r\n textarea.focus();\r\n textarea.select();\r\n\r\n // iOS 兼容:明确选区\r\n textarea.setSelectionRange(0, textarea.value.length);\r\n\r\n const ok = document.execCommand('copy');\r\n document.body.removeChild(textarea);\r\n\r\n if (ok) {\r\n resolve();\r\n } else {\r\n reject(new Error('Copy failed: clipboard unavailable'));\r\n }\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * 复制富文本 HTML 到剪贴板(移动端与 PC)\r\n * 使用场景:图文混排文章、带样式段落,保留格式粘贴。\r\n * @param html HTML字符串\r\n * @example\r\n * await copyHtml('<p><b>加粗</b> 与 <i>斜体</i></p>');\r\n */\r\nexport async function copyHtml(html: string): Promise<void> {\r\n const s = String(html ?? '');\r\n if (canWriteClipboard()) {\r\n const plain = htmlToText(s);\r\n await writeClipboard({\r\n 'text/html': new Blob([s], { type: 'text/html' }),\r\n 'text/plain': new Blob([plain], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n return execCopyFromHtml(s);\r\n}\r\n\r\n/**\r\n * 复制 DOM 节点到剪贴板(移动端与 PC)\r\n * 使用场景:页面已有区域的可视化复制;元素使用 `outerHTML`,非元素使用其文本内容。\r\n * @param node DOM 节点(元素或文本节点)\r\n * @example\r\n * const el = document.querySelector('#article')!;\r\n * await copyNode(el);\r\n */\r\nexport async function copyNode(node: Node): Promise<void> {\r\n if (canWriteClipboard()) {\r\n const { html, text } = nodeToHtmlText(node);\r\n await writeClipboard({\r\n 'text/html': new Blob([html], { type: 'text/html' }),\r\n 'text/plain': new Blob([text], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n const { html } = nodeToHtmlText(node);\r\n return execCopyFromHtml(html);\r\n}\r\n\r\n/**\r\n * 复制单张图片到剪贴板(移动端与 PC,需浏览器支持 `ClipboardItem`)\r\n * 使用场景:把本地 `canvas` 或 `Blob` 生成的图片直接粘贴到聊天/文档。\r\n * @param image 图片源(Blob/Canvas/ImageBitmap)\r\n * @example\r\n * const canvas = document.querySelector('canvas')!;\r\n * await copyImage(canvas);\r\n */\r\nexport async function copyImage(image: Blob | HTMLCanvasElement | ImageBitmap): Promise<void> {\r\n const blob = await toImageBlob(image);\r\n if (!blob) throw new Error('Unsupported image source');\r\n if (canWriteClipboard()) {\r\n const type = blob.type || 'image/png';\r\n await writeClipboard({ [type]: blob });\r\n return;\r\n }\r\n throw new Error('Clipboard image write not supported');\r\n}\r\n\r\n/**\r\n * 复制 URL 到剪贴板(移动端与 PC)\r\n * 写入 `text/uri-list` 与 `text/plain`,在支持 URI 列表的应用中可识别为链接。\r\n * @param url 完整的 URL 字符串\r\n * @example\r\n * await copyUrl('https://example.com/page');\r\n */\r\nexport async function copyUrl(url: string): Promise<void> {\r\n const s = String(url ?? '');\r\n if (canWriteClipboard()) {\r\n await writeClipboard({\r\n 'text/uri-list': new Blob([s], { type: 'text/uri-list' }),\r\n 'text/plain': new Blob([s], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n await copyText(s);\r\n}\r\n\r\n/**\r\n * 复制任意 Blob 到剪贴板(移动端与 PC,需 `ClipboardItem`)\r\n * 使用场景:原生格式粘贴(如 `image/svg+xml`、`application/pdf` 等)。\r\n * @param blob 任意 Blob 数据\r\n * @example\r\n * const svg = new Blob(['<svg></svg>'], { type: 'image/svg+xml' });\r\n * await copyBlob(svg);\r\n */\r\nexport async function copyBlob(blob: Blob): Promise<void> {\r\n if (canWriteClipboard()) {\r\n const type = blob.type || 'application/octet-stream';\r\n await writeClipboard({ [type]: blob });\r\n return;\r\n }\r\n throw new Error('Clipboard blob write not supported');\r\n}\r\n\r\n/**\r\n * 复制 RTF 富文本到剪贴板(移动端与 PC)\r\n * 同时写入 `text/plain`,增强与 Office/富文本编辑器的兼容性。\r\n * @param rtf RTF 字符串(如:`{\\\\rtf1\\\\ansi ...}`)\r\n * @example\r\n * await copyRtf('{\\\\rtf1\\\\ansi Hello \\\\b World}');\r\n */\r\nexport async function copyRtf(rtf: string): Promise<void> {\r\n const s = String(rtf ?? '');\r\n if (canWriteClipboard()) {\r\n const plain = s\r\n .replace(/\\\\par[\\s]?/g, '\\n')\r\n .replace(/\\{[^}]*\\}/g, '')\r\n .replace(/\\\\[a-zA-Z]+[0-9'-]*/g, '')\r\n .replace(/\\r?\\n/g, '\\n')\r\n .trim();\r\n await writeClipboard({\r\n 'text/rtf': new Blob([s], { type: 'text/rtf' }),\r\n 'text/plain': new Blob([plain], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n await copyText(s);\r\n}\r\n\r\n/**\r\n * 复制表格到剪贴板(移动端与 PC)\r\n * 同时写入多种 MIME:`text/html`(表格)、`text/tab-separated-values`(TSV)、`text/csv`、`text/plain`(TSV)。\r\n * 使用场景:优化粘贴到 Excel/Google Sheets/Docs 的体验\r\n * @param rows 二维数组,每行一个数组(字符串/数字)\r\n * @example\r\n * await copyTable([\r\n * ['姓名', '分数'],\r\n * ['张三', 95],\r\n * ['李四', 88],\r\n * ]);\r\n */\r\nexport async function copyTable(rows: Array<Array<string | number>>): Promise<void> {\r\n const data = Array.isArray(rows) ? rows : [];\r\n const escapeHtml = (t: string) =>\r\n t\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n const html = (() => {\r\n const trs = data\r\n .map((r) => `<tr>${r.map((c) => `<td>${escapeHtml(String(c))}</td>`).join('')}</tr>`)\r\n .join('');\r\n return `<table>${trs}</table>`;\r\n })();\r\n const tsv = data.map((r) => r.map((c) => String(c)).join('\\t')).join('\\n');\r\n const csv = data\r\n .map((r) =>\r\n r\r\n .map((c) => {\r\n const s = String(c);\r\n const needQuote = /[\",\\n]/.test(s);\r\n const escaped = s.replace(/\"/g, '\"\"');\r\n return needQuote ? `\"${escaped}\"` : escaped;\r\n })\r\n .join(','),\r\n )\r\n .join('\\n');\r\n if (canWriteClipboard()) {\r\n await writeClipboard({\r\n 'text/html': new Blob([html], { type: 'text/html' }),\r\n 'text/tab-separated-values': new Blob([tsv], { type: 'text/tab-separated-values' }),\r\n 'text/csv': new Blob([csv], { type: 'text/csv' }),\r\n 'text/plain': new Blob([tsv], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n await copyText(tsv);\r\n}\r\n\r\nasync function toImageBlob(image: Blob | HTMLCanvasElement | ImageBitmap) {\r\n if (image instanceof Blob) return image;\r\n if (image instanceof HTMLCanvasElement)\r\n return await new Promise<Blob>((resolve, reject) => {\r\n image.toBlob(\r\n (b) => (b ? resolve(b) : reject(new Error('Canvas toBlob failed'))),\r\n 'image/png',\r\n );\r\n });\r\n const isBitmap = typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap;\r\n if (isBitmap) {\r\n const cnv = document.createElement('canvas');\r\n cnv.width = (image as ImageBitmap).width;\r\n cnv.height = (image as ImageBitmap).height;\r\n const ctx = cnv.getContext('2d');\r\n ctx?.drawImage(image as ImageBitmap, 0, 0);\r\n return await new Promise<Blob>((resolve, reject) => {\r\n cnv.toBlob((b) => (b ? resolve(b) : reject(new Error('Canvas toBlob failed'))), 'image/png');\r\n });\r\n }\r\n return null;\r\n}\r\n\r\nfunction canWriteClipboard() {\r\n return !!(\r\n navigator.clipboard &&\r\n typeof navigator.clipboard.write === 'function' &&\r\n typeof ClipboardItem !== 'undefined'\r\n );\r\n}\r\n\r\nasync function writeClipboard(items: Record<string, Blob>) {\r\n await navigator.clipboard!.write([new ClipboardItem(items)]);\r\n}\r\n\r\nfunction htmlToText(html: string) {\r\n const div = document.createElement('div');\r\n div.innerHTML = html;\r\n return div.textContent || '';\r\n}\r\n\r\nfunction nodeToHtmlText(node: Node) {\r\n const container = document.createElement('div');\r\n container.appendChild(node.cloneNode(true));\r\n const html =\r\n node instanceof Element ? (node.outerHTML ?? container.innerHTML) : container.innerHTML;\r\n const text = container.textContent || '';\r\n return { html, text };\r\n}\r\n\r\nfunction execCopyFromHtml(html: string) {\r\n return new Promise<void>((resolve, reject) => {\r\n try {\r\n const div = document.createElement('div');\r\n div.contentEditable = 'true';\r\n div.style.position = 'fixed';\r\n div.style.top = '0';\r\n div.style.right = '-9999px';\r\n div.style.opacity = '0';\r\n div.style.pointerEvents = 'none';\r\n div.innerHTML = html;\r\n document.body.appendChild(div);\r\n const selection = window.getSelection();\r\n const range = document.createRange();\r\n range.selectNodeContents(div);\r\n selection?.removeAllRanges();\r\n selection?.addRange(range);\r\n const ok = document.execCommand('copy');\r\n document.body.removeChild(div);\r\n selection?.removeAllRanges();\r\n if (ok) {\r\n resolve();\r\n } else {\r\n reject(new Error('Copy failed: clipboard unavailable'));\r\n }\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n}\r\n","export type AppConfig = {\r\n /** 全局 Toast 提示 */\r\n toast?: (option: { msg: string; status: 'success' | 'fail' }) => void;\r\n /** 显示全局 Loading */\r\n showLoading?: () => void;\r\n /** 隐藏全局 Loading */\r\n hideLoading?: () => void;\r\n /** 跳转登录页的方法 */\r\n toLogin?: () => void;\r\n /** 日志记录函数 */\r\n log?: (level: 'info' | 'error' | 'warn' | 'debug', data: AppLogInfo) => void;\r\n};\r\n\r\nexport type AppLogInfo = {\r\n /** 调用函数的名称 */\r\n name: string;\r\n\r\n /** 函数的调用状态 */\r\n status?: 'success' | 'fail';\r\n\r\n /** 函数的调用参数 */\r\n option?: unknown;\r\n\r\n /** 函数的调用结果 */\r\n res?: unknown;\r\n\r\n /** 函数的调用错误 */\r\n e?: unknown;\r\n\r\n /** 日志描述 */\r\n desc?: string;\r\n\r\n // 其他自定义属性\r\n [key: string]: unknown;\r\n};\r\n\r\nconst appConfig: AppConfig = {};\r\n\r\n/**\r\n * 获取应用配置\r\n */\r\nexport function getBaseToolsConfig() {\r\n return appConfig;\r\n}\r\n\r\n/**\r\n * 初始化应用配置 (在入口文件设置)\r\n * @example\r\n * setBaseToolsConfig({\r\n * toast: ({ msg, status }) => (status === 'fail' ? message.error(msg) : message.success(msg)),\r\n * showLoading: () => message.loading('加载中...'),\r\n * hideLoading: () => message.destroy(),\r\n * toLogin: () => reLogin(),\r\n * log(level, data) {\r\n * if (data.name === 'request') {\r\n * sendLog('request', data); // 请求日志\r\n * } else if (level === 'error') {\r\n * sendLog('error', data); // 错误日志\r\n * } else {\r\n * sendLog('action', data); // 操作日志\r\n * }\r\n * },\r\n * });\r\n */\r\nexport function setBaseToolsConfig(newConfig: AppConfig) {\r\n Object.assign(appConfig, newConfig);\r\n}\r\n","/**\r\n * 设置 Cookie(路径默认为 `/`)\r\n * @param name Cookie 名称\r\n * @param value Cookie 值(内部已使用 `encodeURIComponent` 编码)\r\n * @param days 过期天数(从当前时间起算)\r\n * @example\r\n * setCookie('token', 'abc', 7);\r\n */\r\nexport function setCookie(name: string, value: string, days: number) {\r\n const date = new Date();\r\n date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);\r\n const expires = `expires=${date.toUTCString()}; path=/`;\r\n document.cookie = `${name}=${encodeURIComponent(value)}; ${expires}`;\r\n}\r\n\r\n/**\r\n * 获取 Cookie\r\n * @param name Cookie 名称\r\n * @returns 若存在返回解码后的值,否则 `null`\r\n * @example\r\n * const token = getCookie('token');\r\n */\r\nexport function getCookie(name: string): string | null {\r\n const value = `; ${document.cookie}`;\r\n const parts = value.split(`; ${name}=`);\r\n if (parts.length === 2) {\r\n const v = parts.pop()?.split(';').shift();\r\n return v ? decodeURIComponent(v) : null;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * 移除 Cookie(通过设置过期时间为过去)\r\n * 路径固定为 `/`,确保与默认写入路径一致。\r\n * @param name Cookie 名称\r\n * @example\r\n * removeCookie('token');\r\n */\r\nexport function removeCookie(name: string) {\r\n document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;\r\n}\r\n","/**\r\n * 获取用户代理字符串(UA)\r\n * @returns navigator.userAgent.toLowerCase();\r\n */\r\nexport function getUA(): string {\r\n if (typeof navigator === 'undefined') return ''; // SSR无 navigator\r\n return (navigator.userAgent || '').toLowerCase();\r\n}\r\n\r\n/**\r\n * 是否为移动端设备(含平板)\r\n */\r\nexport function isMobile(): boolean {\r\n const ua = getUA();\r\n return /android|webos|iphone|ipod|blackberry|iemobile|opera mini|mobile/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否为平板设备\r\n */\r\nexport function isTablet(): boolean {\r\n const ua = getUA();\r\n return /ipad|android(?!.*mobile)|tablet/i.test(ua) && !/mobile/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否为 PC 设备\r\n */\r\nexport function isPC(): boolean {\r\n return !isMobile() && !isTablet();\r\n}\r\n\r\n/**\r\n * 是否为 iOS 系统\r\n */\r\nexport function isIOS(): boolean {\r\n const ua = getUA();\r\n return /iphone|ipad|ipod/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否为 Android 系统\r\n */\r\nexport function isAndroid(): boolean {\r\n const ua = getUA();\r\n return /android/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否微信内置浏览器\r\n */\r\nexport function isWeChat(): boolean {\r\n const ua = getUA();\r\n return /micromessenger/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否为 Chrome 浏览器\r\n * 已排除 Edge、Opera 等基于 Chromium 的浏览器\r\n */\r\nexport function isChrome(): boolean {\r\n const ua = getUA();\r\n return /chrome\\//i.test(ua) && !/edg\\//i.test(ua) && !/opr\\//i.test(ua) && !/whale\\//i.test(ua);\r\n}\r\n\r\n/**\r\n * 检测是否支持触摸事件\r\n */\r\nexport function isTouchSupported(): boolean {\r\n if (typeof window === 'undefined') return false;\r\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0;\r\n}\r\n\r\n/**\r\n * 获取设备像素比\r\n */\r\nexport function getDevicePixelRatio(): number {\r\n if (typeof window === 'undefined') return 1;\r\n return window.devicePixelRatio || 1;\r\n}\r\n\r\n/**\r\n * 获取浏览器名字\r\n */\r\nexport function getBrowserName(): string | null {\r\n const ua = getUA();\r\n\r\n if (/chrome\\//i.test(ua)) return 'chrome';\r\n if (/safari\\//i.test(ua)) return 'safari';\r\n if (/firefox\\//i.test(ua)) return 'firefox';\r\n if (/opr\\//i.test(ua)) return 'opera';\r\n if (/edg\\//i.test(ua)) return 'edge';\r\n if (/msie|trident/i.test(ua)) return 'ie';\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * 获取浏览器版本号\r\n */\r\nexport function getBrowserVersion(): string | null {\r\n const ua = getUA();\r\n\r\n const versionPatterns = [\r\n /(?:edg|edge)\\/([0-9.]+)/i,\r\n /(?:opr|opera)\\/([0-9.]+)/i,\r\n /chrome\\/([0-9.]+)/i,\r\n /firefox\\/([0-9.]+)/i,\r\n /version\\/([0-9.]+).*safari/i,\r\n /(?:msie |rv:)([0-9.]+)/i,\r\n ];\r\n\r\n for (const pattern of versionPatterns) {\r\n const matches = ua.match(pattern);\r\n if (matches && matches[1]) {\r\n return matches[1];\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * 获取操作系统信息\r\n */\r\nexport function getOS(): string {\r\n const ua = getUA();\r\n\r\n if (/windows/i.test(ua)) return 'windows';\r\n if (/mac os/i.test(ua)) return 'macos';\r\n if (/linux/i.test(ua)) return 'linux';\r\n if (/iphone|ipad|ipod/i.test(ua)) return 'ios';\r\n if (/android/i.test(ua)) return 'android';\r\n\r\n return 'unknown';\r\n}\r\n","/**\r\n * 获取窗口宽度(不含滚动条)\r\n * @returns 窗口宽度\r\n */\r\nexport function getWindowWidth() {\r\n return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;\r\n}\r\n\r\n/**\r\n * 获取窗口高度(不含滚动条)\r\n * @returns 窗口高度\r\n */\r\nexport function getWindowHeight() {\r\n return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;\r\n}\r\n\r\n/**\r\n * 获取文档垂直滚动位置\r\n * @example\r\n * const top = getWindowScrollTop();\r\n */\r\nexport function getWindowScrollTop() {\r\n const doc = document.documentElement;\r\n const body = document.body;\r\n return window.pageYOffset || doc.scrollTop || body.scrollTop || 0;\r\n}\r\n\r\n/**\r\n * 获取文档水平滚动位置\r\n * @example\r\n * const left = getWindowScrollLeft();\r\n */\r\nexport function getWindowScrollLeft() {\r\n const doc = document.documentElement;\r\n const body = document.body;\r\n return window.pageXOffset || doc.scrollLeft || body.scrollLeft || 0;\r\n}\r\n\r\n/**\r\n * 平滑滚动到指定位置\r\n * @param top 目标纵向滚动位置\r\n * @param behavior 滚动行为,默认 'smooth'\r\n * @example\r\n * windowScrollTo(0);\r\n */\r\nexport function windowScrollTo(top: number, behavior: ScrollBehavior = 'smooth') {\r\n if ('scrollBehavior' in document.documentElement.style) {\r\n window.scrollTo({ top, behavior });\r\n } else {\r\n window.scrollTo(0, top);\r\n }\r\n}\r\n\r\n/**\r\n * 元素是否在视口内(可设置阈值)\r\n * @param el 目标元素\r\n * @param offset 额外判定偏移(像素,正数放宽,负数收紧)\r\n * @returns 是否在视口内\r\n */\r\nexport function isInViewport(el: Element, offset = 0) {\r\n const rect = el.getBoundingClientRect();\r\n const width = getWindowWidth();\r\n const height = getWindowHeight();\r\n return (\r\n rect.bottom >= -offset &&\r\n rect.right >= -offset &&\r\n rect.top <= height + offset &&\r\n rect.left <= width + offset\r\n );\r\n}\r\n\r\n/**\r\n * 锁定页面滚动(移动端/PC)\r\n * 使用 `body{ position: fixed }` 技术消除滚动条抖动,记录并恢复滚动位置。\r\n * @example\r\n * lockBodyScroll();\r\n */\r\nexport function lockBodyScroll() {\r\n const body = document.body;\r\n if (body.dataset.scrollLock === 'true') return;\r\n const y = Math.round(window.scrollY || window.pageYOffset || 0);\r\n body.dataset.scrollLock = 'true';\r\n body.dataset.scrollLockY = String(y);\r\n body.style.position = 'fixed';\r\n body.style.top = `-${y}px`;\r\n body.style.left = '0';\r\n body.style.right = '0';\r\n body.style.width = '100%';\r\n}\r\n\r\n/**\r\n * 解除页面滚动锁定,恢复原始滚动位置\r\n * @example\r\n * unlockBodyScroll();\r\n */\r\nexport function unlockBodyScroll() {\r\n const body = document.body;\r\n if (body.dataset.scrollLock !== 'true') return;\r\n const y = Number(body.dataset.scrollLockY || 0);\r\n body.style.position = '';\r\n body.style.top = '';\r\n body.style.left = '';\r\n body.style.right = '';\r\n body.style.width = '';\r\n delete body.dataset.scrollLock;\r\n delete body.dataset.scrollLockY;\r\n window.scrollTo(0, y);\r\n}\r\n","import type { AxiosResponse } from 'axios';\r\n\r\n/**\r\n * 下载文件\r\n * @param url 完整的下载地址 | base64字符串 | Blob对象\r\n * @param fileName 自定义文件名(需含后缀)\r\n * @example\r\n * download('https://xx/xx.pdf');\r\n * download('https://xx/xx.pdf', 'xx.pdf');\r\n * download(blob, '图片.jpg');\r\n */\r\nexport async function download(url: string | Blob, fileName = '') {\r\n if (!url) return;\r\n\r\n let blobUrl = '';\r\n let needRevoke = false; // createObjectURL必须revoke,否则内存泄露,刷新页面都不释放\r\n try {\r\n if (url instanceof Blob) {\r\n // Blob对象\r\n blobUrl = URL.createObjectURL(url);\r\n needRevoke = true;\r\n } else if (url.includes(';base64,')) {\r\n // base64字符串\r\n blobUrl = url;\r\n } else {\r\n if (fileName) {\r\n // 自定义文件名:跨域的url无法自定义文件名,此处统一转为blob\r\n const res = await fetch(url);\r\n if (!res.ok) throw new Error(`fetch error ${res.status}:${url}`); // 拦截错误页(404/500 等 HTML)\r\n const blob = await res.blob();\r\n blobUrl = URL.createObjectURL(blob);\r\n needRevoke = true;\r\n } else {\r\n // 非自定义文件名的普通链接\r\n blobUrl = url;\r\n }\r\n }\r\n\r\n // window.location.href = fileUrl // 可能会关闭当前页面\r\n // window.open(fileUrl, '_blank') // 不支持下载图片\r\n // 通过a标签模拟点击下载\r\n const a = document.createElement('a');\r\n a.href = blobUrl;\r\n a.download = fileName; // 若为空字符串,则会自动取url的文件名(跨域url无法自定义文件名,需转为blob)\r\n document.body.appendChild(a);\r\n a.click();\r\n document.body.removeChild(a);\r\n } finally {\r\n if (needRevoke) {\r\n setTimeout(() => URL.revokeObjectURL(blobUrl), 100); // Safari 需要延迟 revoke\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 解析Axios返回的Blob数据\r\n * @param res Axios响应对象 (responseType='blob')\r\n * @returns 包含blob数据和文件名的对象 { blob, fileName }\r\n * @example\r\n * const res = await axios.get(url, { responseType: 'blob' });\r\n * const { blob, fileName } = await parseAxiosBlob(res);\r\n * download(blob, fileName);\r\n */\r\nexport async function parseAxiosBlob(res: AxiosResponse<Blob>) {\r\n const { data, headers, status, statusText, config } = res;\r\n\r\n if (status < 200 || status >= 300) throw new Error(`${status},${statusText}:${config.url}`);\r\n\r\n // 抛出json错误\r\n if (data.type.includes('application/json')) {\r\n const txt = await data.text();\r\n throw JSON.parse(txt);\r\n }\r\n\r\n // 解析文件名\r\n const fileName = getDispositionFileName(headers['content-disposition']);\r\n return { blob: data, fileName };\r\n}\r\n\r\n/**\r\n * 获取文件名\r\n * @param disposition content-disposition头值\r\n * @returns content-disposition中的filename\r\n * @example\r\n * const fileName = getDispositionFileName(headers['content-disposition']);\r\n */\r\nexport function getDispositionFileName(disposition?: string) {\r\n if (!disposition) return '';\r\n\r\n // 1. RFC5987 filename* 优先\r\n const rfc5987 = /filename\\*\\s*=\\s*([^']*)''([^;]*)/i.exec(disposition);\r\n if (rfc5987?.[2]) {\r\n try {\r\n return decodeURIComponent(rfc5987[2].trim()).replace(/[\\r\\n]+/g, '');\r\n } catch {\r\n return rfc5987[2].trim().replace(/[\\r\\n]+/g, '');\r\n }\r\n }\r\n\r\n // 2. 旧式 filename=\r\n const old = /filename\\s*=\\s*(?:\"([^\"]*)\"|([^\";]*))(?=;|$)/i.exec(disposition);\r\n if (old) return (old[1] ?? old[2]).trim().replace(/[\\r\\n]+/g, '');\r\n\r\n return '';\r\n}\r\n\r\n/**\r\n * 动态加载 JS(重复执行不会重复加载,内部已排重)\r\n * @param src js 文件路径\r\n * @param attrs 可选的脚本属性,如 async、defer、crossOrigin\r\n * @example\r\n * await loadJs('https://xx/xx.js');\r\n * await loadJs('/a.js', { defer: true });\r\n */\r\nexport async function loadJs(\r\n src: string,\r\n attrs?: Pick<HTMLScriptElement, 'async' | 'defer' | 'crossOrigin'>,\r\n) {\r\n return new Promise<void>((resolve, reject) => {\r\n if (hasJs(src)) return resolve();\r\n\r\n const script = document.createElement('script');\r\n script.type = 'text/javascript';\r\n script.src = src;\r\n\r\n if (attrs) {\r\n const keys = Object.keys(attrs) as Array<keyof typeof attrs>;\r\n keys.forEach((key) => {\r\n const v = attrs[key];\r\n if (v === null || v === undefined || v === false) return;\r\n script.setAttribute(key, typeof v === 'boolean' ? '' : v);\r\n });\r\n }\r\n\r\n script.onload = () => resolve();\r\n script.onerror = (e) => reject(e);\r\n\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * 判断某个 JS 地址是否已在页面中加载过\r\n * @param src 相对、绝对路径的 JS 地址\r\n * @returns 是否已加载过\r\n * @example\r\n * hasJs('https://xx/xx.js'); // boolean\r\n * hasJs('/xx.js'); // boolean\r\n * hasJs('xx.js'); // boolean\r\n */\r\nexport function hasJs(src: string) {\r\n const target = new URL(src, document.baseURI).href;\r\n const jsList = Array.from(document.querySelectorAll('script[src]'));\r\n return jsList.some((e) => {\r\n const src = e.getAttribute('src');\r\n return src && new URL(src, document.baseURI).href === target;\r\n });\r\n}\r\n\r\n/**\r\n * 动态加载 CSS(重复执行不会重复加载,内部已排重)\r\n * @param href css 文件地址\r\n * @param attrs 可选属性,如 crossOrigin、media\r\n * @example\r\n * await loadCss('https://xx/xx.css');\r\n * await loadCss('/a.css', { media: 'print' });\r\n */\r\nexport async function loadCss(\r\n href: string,\r\n attrs?: Pick<HTMLLinkElement, 'crossOrigin' | 'media'>,\r\n) {\r\n return new Promise<void>((resolve, reject) => {\r\n if (hasCss(href)) return resolve();\r\n\r\n const link = document.createElement('link');\r\n link.rel = 'stylesheet';\r\n link.href = href;\r\n\r\n if (attrs) {\r\n const keys = Object.keys(attrs) as Array<keyof typeof attrs>;\r\n keys.forEach((key) => {\r\n const v = attrs[key];\r\n if (v === null || v === undefined) return;\r\n link.setAttribute(key, String(v));\r\n });\r\n }\r\n\r\n link.onload = () => resolve();\r\n link.onerror = (e) => reject(e);\r\n\r\n document.head.appendChild(link);\r\n });\r\n}\r\n\r\n/**\r\n * 判断某个 CSS 地址是否已在页面中加载过\r\n * @param href 相对、绝对路径的 CSS 地址\r\n * @returns 是否已加载过\r\n * @example\r\n * hasCss('https://xx/xx.css'); // boolean\r\n */\r\nexport function hasCss(href: string) {\r\n const target = new URL(href, document.baseURI).href;\r\n const list = Array.from(document.querySelectorAll('link[rel=\"stylesheet\"][href]'));\r\n return list.some((e) => {\r\n const h = e.getAttribute('href');\r\n return h && new URL(h, document.baseURI).href === target;\r\n });\r\n}\r\n\r\n/**\r\n * 预加载图片\r\n * @param src 图片地址\r\n * @returns Promise<HTMLImageElement>\r\n * @example\r\n * await preloadImage('/a.png');\r\n */\r\nexport function preloadImage(src: string) {\r\n return new Promise<HTMLImageElement>((resolve, reject) => {\r\n const img = new Image();\r\n img.onload = () => resolve(img);\r\n img.onerror = (e) => reject(e);\r\n img.src = src;\r\n });\r\n}\r\n","/**\r\n * 内部统一导出, 外部快捷引入: import {xx} from 'base-tools/ts'\r\n */\r\nexport * from './array';\r\nexport * from './async';\r\nexport * from './bean';\r\nexport * from './day';\r\nexport * from './es-toolkit';\r\nexport * from './number';\r\nexport * from './object';\r\nexport * from './string';\r\nexport * from './typing';\r\nexport * from './url';\r\nexport * from './validator';\r\n","/**\r\n * 拖拽排序 (不改变原数组)\r\n * @param list 原始数组\r\n * @param fromIndex 要移动的元素的原始索引\r\n * @param toIndex 要移动到的目标索引\r\n * @returns 移动元素后的新数组\r\n */\r\nexport function arrayMove<T>(list: T[], fromIndex: number, toIndex: number) {\r\n const newList = [...list]; // 创建新数组副本\r\n const [removed] = newList.splice(fromIndex, 1); // 移除 fromIndex 处的元素\r\n newList.splice(toIndex, 0, removed); // 插入到 toIndex 处\r\n return newList;\r\n}\r\n","/**\r\n * 将 Promise 包装为 [data, error] 形式, 减少 try-catch 代码量\r\n * @param p 要包装的 Promise\r\n * @returns 一个 Promise,其结果为 [data, error] 形式\r\n * @example\r\n * const [data, err] = await toAsync(fetch('https://api.example.com/data'));\r\n * if (err) {\r\n * console.error(err);\r\n * return;\r\n * }\r\n * console.log(data);\r\n */\r\nexport async function toAsync<T>(p: Promise<T>): Promise<[T | null, unknown]> {\r\n try {\r\n const data = await p;\r\n return [data, null];\r\n } catch (err) {\r\n return [null, err];\r\n }\r\n}\r\n","import mitt, { type Emitter, type EventType } from 'mitt';\r\n\r\ntype Events = Record<EventType, unknown>;\r\n\r\n/**\r\n * 总线式发布订阅\r\n * @example\r\n * const emitter = new EventBus(); // 支持链式调用\r\n * emitter.on('xx', fn); // 订阅事件 xx\r\n * emitter.once('xx', fn); // 订阅事件 xx 一次\r\n * emitter.emit('xx', any); // 发布事件 xx,参数任意\r\n * emitter.off('xx'); // 移除事件 xx 下全部监听\r\n * emitter.off('xx', fn); // 移除事件 xx 下指定监听\r\n * emitter.clear(); // 移除所有事件\r\n *\r\n * @example 类型约束\r\n * type T = { a: number; b: string };\r\n * const emitter = new EventBus<{ xx: T; yy: void }>();\r\n * const fn = (arg: T) => {}\r\n * emitter.on('xx', fn);\r\n * emitter.off('xx', fn);\r\n * emitter.emit('xx', { a: 123, b: '123' });\r\n * emitter.emit('yy');\r\n */\r\nexport class EventBus<T extends Events = Events> {\r\n private readonly _emitter: Emitter<T> = mitt<T>();\r\n\r\n /** 订阅 */\r\n on<K extends keyof T>(type: K, fn: (event: T[K]) => void): this {\r\n this._emitter.on(type, fn);\r\n return this;\r\n }\r\n\r\n /** 订阅一次 */\r\n once<K extends keyof T>(type: K, fn: (event: T[K]) => void): this {\r\n const wrap = (event: T[K]) => {\r\n this._emitter.off(type, wrap);\r\n fn(event);\r\n };\r\n this._emitter.on(type, wrap);\r\n return this;\r\n }\r\n\r\n /** 发布 */\r\n emit<K extends keyof T>(type: K, event?: T[K]): this {\r\n this._emitter.emit(type, event as T[K]);\r\n return this;\r\n }\r\n\r\n /** 移除 */\r\n off<K extends keyof T>(type: K, fn?: (event: T[K]) => void): this {\r\n this._emitter.off(type, fn);\r\n return this;\r\n }\r\n\r\n /** 清空 */\r\n clear(): this {\r\n this._emitter.all.clear();\r\n return this;\r\n }\r\n}\r\n","import dayjs from 'dayjs';\r\nimport customParseFormat from 'dayjs/plugin/customParseFormat';\r\nimport utc from 'dayjs/plugin/utc';\r\nimport timezone from 'dayjs/plugin/timezone';\r\nimport relativeTime from 'dayjs/plugin/relativeTime';\r\nimport advancedFormat from 'dayjs/plugin/advancedFormat';\r\nimport 'dayjs/locale/zh-cn';\r\nimport { zeroPad } from '../number';\r\n\r\ndayjs.extend(customParseFormat);\r\ndayjs.extend(utc);\r\ndayjs.extend(timezone);\r\ndayjs.extend(relativeTime);\r\ndayjs.extend(advancedFormat);\r\ndayjs.locale('zh-cn');\r\n\r\ntype BaseTime = number | string | Date | dayjs.Dayjs | null | undefined;\r\n\r\n/**\r\n * 创建 dayjs 实例\r\n * 文档: https://day.js.org/zh-CN/\r\n * @param t 各种规范或不规范的时间\r\n * @returns dayjs 实例\r\n * @example\r\n * const d = toDayjs('2021-01-01'); // dayjs 实例 (无参,则默认当前时间)\r\n * d.format('YYYY-MM-DD HH:mm:ss'); // \"2025-12-10 11:33:16\"\r\n * d.valueOf(); // 毫秒时间戳,如 1765337596913\r\n * d.unix(); // 秒时间戳,如 1765337596\r\n * d.millisecond(); // 毫秒 913\r\n * d.second(); // 秒 16\r\n * d.minute(); // 分 33\r\n * d.hour(); // 时 11\r\n * d.date(); // 日 10\r\n * d.day(); // 星期几 5(周日=0)\r\n * d.month() + 1; // 月 12\r\n * d.year(); // 年 2025\r\n * d.startOf('day').valueOf(); // 当日零点\r\n * d.startOf('month').format('YYYY-MM-DD HH:mm:ss'); // 月初 \"2025-12-01 00:00:00\"\r\n * d.endOf('month').format('YYYY-MM-DD HH:mm:ss'); // 月末 \"2025-12-31 23:59:59\"\r\n * d.fromNow(); // “刚刚”、“x分钟前/后”、“x小时前/后”、“x天前/后”、“x月前/后”、“x年前/后”\r\n * d.isSame(t, 'day'); // 是否与t在同一天\r\n * d.diff(); // 与当前时间相差的毫秒数\r\n * d.diff(t); // 与t相差的毫秒数\r\n * d.diff(t, 'second'); // 与t相差的秒数\r\n * d.diff(t, 'minute'); // 与t相差的分钟数\r\n * d.diff(t, 'hour'); // 与t相差的小时数\r\n * d.diff(t, 'day'); // 与t相差的天数\r\n * d.diff(t, 'week'); // 与t相差的周数\r\n * d.diff(t, 'month'); // 与t相差的月数\r\n * d.diff(t, 'quarter'); // 与t相差的季度数\r\n * d.diff(t, 'year'); // 与t相差的年数\r\n */\r\nexport function toDayjs(t?: BaseTime, fmt?: dayjs.OptionType) {\r\n if (t === null || t === undefined) return dayjs();\r\n if (typeof t === 'number') {\r\n const s = String(Math.trunc(t));\r\n return dayjs(s.length === 10 ? t * 1000 : t, fmt);\r\n }\r\n if (typeof t === 'string') {\r\n const s = t.trim();\r\n if (/^\\d{10}$/.test(s)) return dayjs(Number(s) * 1000, fmt);\r\n if (/^\\d{13}$/.test(s)) return dayjs(Number(s), fmt);\r\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(s)) return dayjs(s, fmt || 'YYYY-MM-DD');\r\n if (/^\\d{4}\\/\\d{2}\\/\\d{2}$/.test(s)) return dayjs(s, fmt || 'YYYY/MM/DD');\r\n if (/^\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}$/.test(s))\r\n return dayjs(s, fmt || 'YYYY-MM-DD HH:mm:ss');\r\n if (/^\\d{4}\\/\\d{2}\\/\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}$/.test(s))\r\n return dayjs(s, fmt || 'YYYY/MM/DD HH:mm:ss');\r\n return dayjs(s, fmt);\r\n }\r\n return dayjs(t, fmt);\r\n}\r\n\r\n/**\r\n * 获取“前几天”的日期范围\r\n * @param offset 正整数天数\r\n * @param fmt 日期格式,默认 `YYYY-MM-DD`\r\n * @returns `[start, end]` 日期字符串数组\r\n * @example\r\n * 若今天为 2025-11-19:\r\n * getDateRangeBefore(1) // ['2025-11-18', '2025-11-19']\r\n * getDateRangeBefore(1, 'YYYY-MM-DD HH:mm:ss') // ['2025-11-18 00:00:00', '2025-11-19 23:59:59']\r\n */\r\nexport function getDateRangeBefore(offset: number, fmt = 'YYYY-MM-DD') {\r\n const now = toDayjs(Date.now());\r\n const n = Math.max(0, Math.trunc(offset));\r\n const hasTime = /H|h|m|s|S|A|a|x|X/.test(fmt);\r\n const startDay = now.add(-n, 'day');\r\n const endDay = now;\r\n const start = (hasTime ? startDay.startOf('day') : startDay).format(fmt);\r\n const end = (hasTime ? endDay.endOf('day') : endDay).format(fmt);\r\n return [start, end];\r\n}\r\n\r\n/**\r\n * 获取“后几天”的日期范围\r\n * - 起点:今天;终点:`offset` 天后\r\n * - 若 `fmt` 含时间令牌(如 `HH:mm:ss`),则返回整日范围:起点为当日零点,终点为当日末尾\r\n * @param offset 正整数天数\r\n * @param fmt 日期格式,默认 `YYYY-MM-DD`\r\n * @returns `[start, end]` 日期字符串数组\r\n * @example\r\n * 若今天为 2025-11-19:\r\n * getDateRangeAfter(1) // ['2025-11-19', '2025-11-20']\r\n * getDateRangeAfter(1, 'YYYY-MM-DD HH:mm:ss') // ['2025-11-19 00:00:00', '2025-11-20 23:59:59']\r\n */\r\nexport function getDateRangeAfter(offset: number, fmt = 'YYYY-MM-DD') {\r\n const now = toDayjs(Date.now());\r\n const n = Math.max(0, Math.trunc(offset));\r\n const hasTime = /H|h|m|s|S|A|a|x|X/.test(fmt);\r\n const startDay = now;\r\n const endDay = now.add(n, 'day');\r\n const start = (hasTime ? startDay.startOf('day') : startDay).format(fmt);\r\n const end = (hasTime ? endDay.endOf('day') : endDay).format(fmt);\r\n return [start, end];\r\n}\r\n\r\n/**\r\n * 获取倒计时的时间分解(零填充字符串)\r\n * @param diff 毫秒差值(正数表示剩余时间,负数/0表示已到期)\r\n * @returns 包含天、时、分、秒、毫秒的零填充对象\r\n * @example\r\n * const diff = toDayjs(t).diff(); // 毫秒差值\r\n * const parts = getCountdownParts(diff); // { d: '00', h: '00', m: '00', s: '00', ms: '000' }\r\n */\r\nexport function getCountdownParts(diff: number) {\r\n if (diff <= 0) return { d: '00', h: '00', m: '00', s: '00', ms: '000' };\r\n\r\n const d = Math.floor(diff / (1000 * 60 * 60 * 24));\r\n const h = Math.floor((diff / (1000 * 60 * 60)) % 24);\r\n const m = Math.floor((diff / (1000 * 60)) % 60);\r\n const s = Math.floor((diff / 1000) % 60);\r\n const ms = diff % 1000;\r\n\r\n return {\r\n d: zeroPad(d),\r\n h: zeroPad(h),\r\n m: zeroPad(m),\r\n s: zeroPad(s),\r\n ms: zeroPad(ms, 3),\r\n };\r\n}\r\n\r\n/**\r\n * 通过出生日期计算年龄\r\n * @param birthdate 生日日期,支持多种格式(会被自动解析)\r\n * @returns 年龄对象,包含 `age`(年龄数值)和 `type`(年龄单位,'year' 表示年,'month' 表示月)\r\n * @example\r\n * // 假设当前日期为 2025-11-19\r\n * getAgeByBirthdate('2025-05-10'); // { age: 6, type: 'month' }\r\n * getAgeByBirthdate('2020-11-19'); // { age: 5, type: 'year' }\r\n * getAgeByBirthdate('2020-12-01'); // { age: 4, type: 'year' }(生日还没到, 所以年龄是4岁)\r\n */\r\nexport function getAgeByBirthdate(birthdate: string) {\r\n const birth = toDayjs(birthdate, 'YYYY-MM-DD');\r\n const now = toDayjs(Date.now());\r\n\r\n // 精确的月份计算\r\n const totalMonths = (now.year() - birth.year()) * 12 + (now.month() - birth.month());\r\n\r\n // 如果当前日期小于出生日期,月份减1\r\n const adjustedMonths = now.date() < birth.date() ? totalMonths - 1 : totalMonths;\r\n\r\n if (adjustedMonths >= 12) {\r\n let age = Math.floor(adjustedMonths / 12);\r\n // 检查生日是否已过\r\n const birthdayThisYear = birth.add(age, 'year');\r\n if (now.isBefore(birthdayThisYear)) {\r\n age--;\r\n }\r\n return { age, type: 'year' };\r\n }\r\n\r\n return { age: adjustedMonths, type: 'month' };\r\n}\r\n\r\n/**\r\n * 对外抛出 dayjs 以便全局配置\r\n * @example\r\n * 切换语言\r\n * dayjs.locale('en'); // 切换为英文\r\n * dayjs.locale('zh-cn'); // 切换为中文 (默认)\r\n */\r\nexport { dayjs };\r\n","import BigNumber from 'bignumber.js';\r\n\r\n/**\r\n * 数值入参类型。\r\n * 支持原生 `number`、`string`(包含小数、科学计数法等)以及 `BigNumber`。\r\n */\r\nexport type NumLike = string | number | BigNumber;\r\n\r\n/**\r\n * 将任意 `NumLike` 统一转换为 `BigNumber` 实例。\r\n * @param x 任意支持的数值入参。\r\n * @returns `BigNumber` 实例。\r\n * @example\r\n * big('0.1'); // => BigNumber\r\n * big(0.2); // => BigNumber\r\n */\r\nfunction big(x: NumLike): BigNumber {\r\n return x instanceof BigNumber ? x : new BigNumber(x);\r\n}\r\n\r\n/**\r\n * 高精度加法(支持多个参数连加)。\r\n * @example\r\n * mathPlus(0.1, 0.2); // => 0.3\r\n * mathPlus('0.1', '0.2'); // => 0.3\r\n * mathPlus(1, 2, 3, 4); // => 10 // 多参数连加: 1+2+3+4\r\n */\r\nexport function mathPlus(...rest: NumLike[]) {\r\n let acc = big(rest[0]);\r\n for (const x of rest.slice(1)) acc = acc.plus(big(x));\r\n return acc.toNumber();\r\n}\r\n\r\n/**\r\n * 高精度减法(支持多个参数连减)。\r\n * @example\r\n * mathMinus(1, 0.9); // => 0.1\r\n * mathMinus('1.1', '0.2'); // => 0.9\r\n * mathMinus(10, 1, 2, 3); // => 4 // 多参数连减: 10-1-2-3\r\n */\r\nexport function mathMinus(...rest: NumLike[]) {\r\n let acc = big(rest[0]);\r\n for (const x of rest.slice(1)) acc = acc.minus(big(x));\r\n return acc.toNumber();\r\n}\r\n\r\n/**\r\n * 高精度乘法(支持多个参数连乘)。\r\n * @example\r\n * mathTimes(0.1, 0.2); // => 0.02\r\n * mathTimes('1.5', '3'); // => 4.5\r\n * mathTimes(2, 3, 4); // => 24 // 多参数连乘: 2*3*4\r\n */\r\nexport function mathTimes(...rest: NumLike[]) {\r\n let acc = big(rest[0]);\r\n for (const x of rest.slice(1)) acc = acc.times(big(x));\r\n return acc.toNumber();\r\n}\r\n\r\n/**\r\n * 高精度除法(支持多个参数连除)。\r\n * @example\r\n * mathDiv(1, 3); // => 0.333333...\r\n * mathDiv('10', '4'); // => 2.5\r\n * mathDiv(100, 5, 2); // => 10 // 多参数连除: 100/5/2\r\n */\r\nexport function mathDiv(...rest: NumLike[]) {\r\n let acc = big(rest[0]);\r\n for (const x of rest.slice(1)) acc = acc.div(big(x));\r\n return acc.toNumber();\r\n}\r\n\r\n/**\r\n * 指数运算\r\n * @param x 底数。\r\n * @param y 指数。\r\n * @returns 计算结果。\r\n * @example\r\n * mathPow(2, 3); // => 8\r\n * mathPow('2.5', 2); // => 6.25\r\n */\r\nexport function mathPow(x: NumLike, y: NumLike) {\r\n return big(x).pow(big(y)).toNumber();\r\n}\r\n\r\n/**\r\n * 四舍五入到指定小数位数\r\n * @param x 需要舍入的数值。\r\n * @param dp 保留的小数位数,默认 `0`(取整)。\r\n * @param rm 舍入模式,默认 `ROUND_HALF_UP`(四舍五入)。\r\n * @returns 舍入后的数值。\r\n * @example\r\n * mathRound(1.6); // => 2\r\n * mathRound('1.234', 2); // => 1.23\r\n * mathRound('1.235', 2); // => 1.24\r\n * mathRound('1.299', 2, BigNumber.ROUND_DOWN); // => 1.29\r\n */\r\nexport function mathRound(\r\n x: NumLike,\r\n dp = 0,\r\n rm: BigNumber.RoundingMode = BigNumber.ROUND_HALF_UP,\r\n) {\r\n return big(x).decimalPlaces(dp, rm).toNumber();\r\n}\r\n\r\n/**\r\n * 将数值按指定位数格式化为字符串(保留小数位)。\r\n * @param x 需要格式化的数值。\r\n * @param dp 保留的小数位数,默认 `2`。\r\n * @param rm 舍入模式,默认 `ROUND_HALF_UP`(四舍五入)。\r\n * @returns 格式化后的字符串。\r\n * @example\r\n * mathFixed('1'); // => '1.00'\r\n * +mathFixed('1'); // => 1\r\n * mathFixed(1.2345); // => '1.23'\r\n * mathFixed(1.2345, 3); // => '1.235'\r\n * mathFixed('1.2345', 0, BigNumber.ROUND_UP); // => '2'\r\n */\r\nexport function mathFixed(\r\n x: NumLike,\r\n dp = 2,\r\n rm: BigNumber.RoundingMode = BigNumber.ROUND_HALF_UP,\r\n): string {\r\n return big(x).toFixed(dp, rm);\r\n}\r\n\r\n/**\r\n * 比较两个数值大小。\r\n * @example\r\n * mathCompare('2', '10'); // => -1\r\n * mathCompare(3, 3); // => 0\r\n * mathCompare('10', 2); // => 1\r\n */\r\nexport function mathCompare(a: NumLike, b: NumLike): -1 | 0 | 1 | null {\r\n return big(a).comparedTo(big(b));\r\n}\r\n\r\n/**\r\n * 判断两个数值是否相等。\r\n * @example\r\n * mathEqual('1.0', 1); // => true\r\n * mathEqual(2, 1); // => false\r\n */\r\nexport function mathEqual(a: NumLike, b: NumLike): boolean {\r\n return big(a).isEqualTo(big(b));\r\n}\r\n\r\n/**\r\n * 判断 a 是否大于 b。\r\n * @example\r\n * mathGreaterThan(2, 1); // => true\r\n * mathGreaterThan(1, 2); // => false\r\n */\r\nexport function mathGreaterThan(a: NumLike, b: NumLike): boolean {\r\n return big(a).isGreaterThan(big(b));\r\n}\r\n\r\n/**\r\n * 判断 a 是否大于等于 b。\r\n * @example\r\n * mathGreaterThanOrEqual(2, 2); // => true\r\n * mathGreaterThanOrEqual(1, 2); // => false\r\n */\r\nexport function mathGreaterThanOrEqual(a: NumLike, b: NumLike): boolean {\r\n return big(a).isGreaterThanOrEqualTo(big(b));\r\n}\r\n\r\n/**\r\n * 判断 a 是否小于 b。\r\n * @example\r\n * mathLessThan(1, 2); // => true\r\n * mathLessThan(2, 1); // => false\r\n */\r\nexport function mathLessThan(a: NumLike, b: NumLike): boolean {\r\n return big(a).isLessThan(big(b));\r\n}\r\n\r\n/**\r\n * 判断 a 是否小于等于 b。\r\n * @example\r\n * mathLessThanOrEqual(2, 2); // => true\r\n * mathLessThanOrEqual(1, 2); // => true\r\n * mathLessThanOrEqual(2, 1); // => false\r\n */\r\nexport function mathLessThanOrEqual(a: NumLike, b: NumLike): boolean {\r\n return big(a).isLessThanOrEqualTo(big(b));\r\n}\r\n\r\n/**\r\n * 导出 BigNumber 类\r\n * @example\r\n * BigNumber.ROUND_HALF_UP; // 使用类型\r\n * BigNumber.set(config); // 设置全局配置\r\n * const bn = new BigNumber('123456.789'); // 创建实例\r\n */\r\nexport { BigNumber };\r\n","import { BigNumber } from './big';\r\n\r\n/**\r\n * 开头补零\r\n * @param n 数字\r\n * @param len 总长度,默认 2\r\n * @returns 零填充后的字符串\r\n * @example\r\n * zeroPad(1) // '01'\r\n * zeroPad(12) // '12'\r\n * zeroPad(12, 4) // '0012'\r\n */\r\nexport function zeroPad(n: number | string, len = 2) {\r\n return String(n).padStart(len, '0');\r\n}\r\n\r\n/**\r\n * 给数字添加指定单位 (支持数字字符串,其他非法字符返回原值)\r\n * @param unit 单位\r\n * @param num 数字\r\n * @example\r\n * withUnit(0, 'px') // \"0px\"\r\n * withUnit(1, 'px') // \"1px\"\r\n * withUnit('1', 'rpx') // \"1rpx\"\r\n * withUnit('1', '%') // \"1%\"\r\n * withUnit('auto', 'px') // \"auto\"\r\n * withUnit(null | undefined | '') // \"\"\r\n */\r\nexport function withUnit(num?: number | string, unit = '') {\r\n if (num === null || num === undefined || num === '') return ''; // 注意不能排除0\r\n\r\n if (typeof num === 'number') return `${num}${unit}`;\r\n\r\n const str = String(num).trim();\r\n\r\n if (str === '') return '';\r\n\r\n return isNaN(+str) ? str : `${str}${unit}`;\r\n}\r\n\r\n/**\r\n * 给数字添加px单位 (支持数字字符串,其他非法字符返回原值)\r\n * @example\r\n * withUnitPx(10) // \"10px\"\r\n * withUnitPx('10') // \"10px\"\r\n * withUnitPx('10px') // \"10px\"\r\n * withUnitPx(\"auto\") // \"auto\"\r\n * withUnitPx(\"30%\") // \"30%\"\r\n * withUnitPx(null | undefined | '') // \"\"\r\n * withUnitPx(0) // \"0px\"\r\n */\r\nexport function withUnitPx(num?: string | number) {\r\n return withUnit(num, 'px');\r\n}\r\n\r\n/**\r\n * 给数字添加距离单位:当数值大于等于 1000m 时转换为 km,否则显示 m(最多两位小数、无无意义补零)\r\n * @example\r\n * withDistance(5); // => '5m'\r\n * withDistance(999.456); // => '999.46m'\r\n * withDistance(1000); // => '1km'\r\n * withDistance('1500'); // => '1.5km'\r\n * withDistance('1728'); // => '1.73km'\r\n */\r\nexport function withDistance(m: number | string): string {\r\n const n = Number(m ?? 0);\r\n if (!Number.isFinite(n)) return '0m';\r\n return n >= 1000 ? `${+(n / 1000).toFixed(2)}km` : `${+n.toFixed(2)}m`;\r\n}\r\n\r\n/**\r\n * 数字转千分位字符串(保留小数与符号)。\r\n * @example\r\n * toThousandth(1234567); // => '1,234,567'\r\n * toThousandth('1234567.89'); // => '1,234,567.89'\r\n * toThousandth('-987654'); // => '-987,654'\r\n */\r\nexport function toThousandth(str: number | string): string {\r\n const v = String(str ?? '').trim();\r\n if (v === '') return '';\r\n\r\n // 处理符号\r\n let sign = '';\r\n let num = v;\r\n if (num[0] === '-' || num[0] === '+') {\r\n sign = num[0];\r\n num = num.slice(1);\r\n }\r\n\r\n // 拆分整数与小数部分\r\n const [intPart, decPart] = num.split('.');\r\n\r\n // 仅对整数部分进行千分位分组\r\n const groupedInt = intPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\r\n\r\n return decPart !== undefined && decPart !== ''\r\n ? `${sign}${groupedInt}.${decPart}`\r\n : `${sign}${groupedInt}`;\r\n}\r\n\r\n/**\r\n * 阿拉伯数字转中文整数(忽略小数;支持负数)。\r\n * @param num 输入的数字或数字字符串(小数部分将被丢弃)\r\n * @returns 中文数字字符串;非法输入返回空字符串\r\n * @example\r\n * toChineseNum(123456); // \"十二万三千四百五十六\"\r\n * toChineseNum(-10008); // \"负一万零八\"\r\n * `第${toChineseNum(123)}条` // \"第一百二十三条\"\r\n */\r\nexport function toChineseNum(num: number | string) {\r\n const numInt = Math.trunc(+num);\r\n if (numInt === 0) return '零';\r\n\r\n const digit = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];\r\n const unit = ['', '十', '百', '千'];\r\n const bigUnit = ['', '万', '亿', '兆'];\r\n\r\n const section4 = (n: number) => {\r\n let str = '',\r\n zeroFlag = false;\r\n for (let i = 0; i < 4; i++) {\r\n const d = n % 10;\r\n n = Math.floor(n / 10);\r\n if (d === 0) {\r\n zeroFlag = true;\r\n continue;\r\n }\r\n if (zeroFlag) str = digit[0] + str;\r\n str = digit[d] + unit[i] + str;\r\n zeroFlag = false;\r\n }\r\n return str;\r\n };\r\n\r\n let res = '';\r\n let sectionIndex = 0;\r\n let n = Math.abs(numInt);\r\n\r\n while (n > 0) {\r\n const seg = n % 10000;\r\n n = Math.floor(n / 10000);\r\n if (seg) {\r\n const segStr = section4(seg);\r\n res = segStr + (sectionIndex ? bigUnit[sectionIndex] : '') + res;\r\n } else if (res && !res.startsWith('零')) {\r\n res = `零${res}`;\r\n }\r\n sectionIndex++;\r\n }\r\n\r\n res = res.replace(/^一十/, '十');\r\n\r\n return numInt < 0 ? `负${res}` : res;\r\n}\r\n\r\n/**\r\n * 金额转中文大写(支持角/分/厘,精度控制)。\r\n * @param amount 金额(支持 number | string),非法或非有限数返回空串\r\n * @param opts 配置项\r\n * @param opts.precision 保留小数位(0~3),对应:0无小数、1角、2角分、3角分厘;默认 2\r\n * @param opts.rm 舍入模式,默认 `BigNumber.ROUND_HALF_UP`(四舍五入)\r\n * @param opts.yuanChar 元单位字符(`'元' | '圆'`),默认 `'元'`\r\n * @returns 中文大写金额字符串;示例:`壹佰贰拾叁元肆角伍分`\r\n * @example\r\n * toChineseCurrency(0) // '零元整'\r\n * toChineseCurrency(10) // '拾元整'\r\n * toChineseCurrency(101) // '壹佰零壹元整'\r\n * toChineseCurrency(1001000) // '壹佰万零壹仟元整'\r\n * toChineseCurrency(1001.01) // '壹仟零壹元壹分'\r\n * toChineseCurrency('1234.5679', { precision: 3 }) // '壹仟贰佰叁拾肆元伍角陆分捌厘'\r\n * toChineseCurrency(-1.2) // '负壹元贰角'\r\n */\r\nexport function toChineseCurrency(\r\n amount: number | string,\r\n opts: {\r\n precision?: 0 | 1 | 2 | 3;\r\n rm?: BigNumber.RoundingMode;\r\n yuanChar?: '元' | '圆';\r\n } = {},\r\n): string {\r\n const dp = opts.precision ?? 2;\r\n const rm = opts.rm ?? BigNumber.ROUND_HALF_UP;\r\n const yuan = opts.yuanChar ?? '元';\r\n\r\n // 1. 非法输入直接返回空串\r\n if (amount === null || amount === undefined) return '';\r\n const bn = new BigNumber(amount);\r\n if (!bn.isFinite()) return '';\r\n\r\n // 2. 转字符串并拆分整数/小数\r\n const s = bn.toFixed(dp, rm);\r\n const sign = s.startsWith('-') ? '负' : '';\r\n const [intStr, decStr = ''] = s.replace(/^-/, '').split('.');\r\n\r\n // 3. 数字与大写映射\r\n const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];\r\n const unit = ['', '拾', '佰', '仟'];\r\n const bigUnit = ['', '万', '亿', '兆'];\r\n const smallUnit = ['角', '分', '厘'];\r\n\r\n // 4. 四位一段转换整数\r\n const section4 = (n: BigNumber): string => {\r\n let str = '';\r\n let zeroFlag = false;\r\n for (let i = 0; i < 4; i++) {\r\n const d = n.mod(10).toNumber();\r\n n = n.idiv(10);\r\n if (d === 0) {\r\n zeroFlag = true;\r\n continue;\r\n }\r\n if (zeroFlag) str = digit[0] + str;\r\n str = digit[d] + unit[i] + str;\r\n zeroFlag = false;\r\n }\r\n return str.replace(/零+$/g, '');\r\n };\r\n\r\n const intNum = new BigNumber(intStr);\r\n let res = '';\r\n if (intNum.isZero()) {\r\n res = digit[0];\r\n } else {\r\n let n = intNum.abs();\r\n let sectionIndex = 0;\r\n while (n.gt(0)) {\r\n const seg = n.mod(10000);\r\n n = n.idiv(10000);\r\n if (seg.gt(0)) {\r\n const segStr = section4(seg);\r\n const needZero =\r\n res && !res.startsWith(digit[0]) && (seg.lt(1000) || seg.mod(1000).isZero());\r\n const bu = sectionIndex ? bigUnit[sectionIndex] : '';\r\n res = segStr + bu + (needZero ? digit[0] : '') + res;\r\n } else if (res && !res.startsWith(digit[0])) {\r\n res = digit[0] + res;\r\n }\r\n sectionIndex++;\r\n }\r\n res = res.replace(/^壹拾/, '拾');\r\n }\r\n\r\n // 5. 小数部分(角分厘)\r\n let frac = '';\r\n for (let i = 0; i < Math.min(3, dp); i++) {\r\n const ch = decStr[i] || '0';\r\n const d = ch.charCodeAt(0) - 48;\r\n if (d > 0) frac += digit[d] + smallUnit[i];\r\n }\r\n\r\n // 6. 拼接\r\n return frac ? `${sign}${res}${yuan}${frac}` : `${sign}${res}${yuan}整`;\r\n}\r\n","/**\r\n * 随机生成一个布尔值。\r\n * @returns 随机布尔值。\r\n * @example\r\n * randomBoolean(); // => 随机返回 true 或 false\r\n */\r\nexport function randomBoolean(): boolean {\r\n return Math.random() < 0.5;\r\n}\r\n","/**\r\n * re-export 全量 es-toolkit\r\n * 文档: https://es-toolkit.dev/\r\n * 目的: 提供常用工具,收敛依赖版本\r\n */\r\nexport * from 'es-toolkit';\r\n","import { get, set } from 'es-toolkit/compat';\r\n\r\n/**\r\n * 获取对象键名数组(类型安全)。\r\n * 注:内置 `Object.keys` 与 `es-toolkit` 的 `keys` 在 TS 中通常返回 `string[]`,无法精确到 `keyof T`。\r\n * @param obj 目标对象\r\n * @returns 类型精确的 `Array<keyof T>`\r\n * @example\r\n * const o = { a: 1, b: 'x' };\r\n * const keys = getObjectKeys(o); // type: ('a' | 'b')[], value: ['a','b']\r\n */\r\nexport function getObjectKeys<T extends object>(obj: T): Array<keyof T> {\r\n return Object.keys(obj) as (keyof T)[];\r\n}\r\n\r\n/**\r\n * 获取对象值。\r\n * @param obj 目标对象\r\n * @param path 路径。(点路径:'a.b'、'a[0].b';数组路径:['a',0,'b'])\r\n * @returns 值\r\n * @example\r\n * const o = { b: { c: 'x' }, users: [{ name: 'john' }, { name: 'jane' }] };\r\n * const c = getObjectValue(o, 'b.c'); // 点路径: 'x'\r\n * const name0 = getObjectValue(o, 'users[0].name'); // 数组字符: 'john'\r\n * const name1 = getObjectValue(o, ['users', 1, 'name']); // 数组路径: 'jane'\r\n */\r\nexport const getObjectValue = get;\r\n\r\n/**\r\n * 设置对象值。\r\n * @param obj 目标对象\r\n * @param path 路径。(点路径:'a.b'、'a[0].b';数组路径:['a',0,'b'])\r\n * @param value 值\r\n * @example\r\n * const o = { b: { c: 'x' }, users: [{ name: 'john' }, { name: 'jane' }] };\r\n * setObjectValue(o, 'b.c', 'y'); // 点路径\r\n * setObjectValue(o, ['users', 1, 'name'], 'jane-doe'); // 数组路径\r\n */\r\nexport const setObjectValue = set;\r\n","/**\r\n * 文本脱敏\r\n * @param s 原始文本\r\n * @param keepLeft 保留左侧字符数(默认 1)\r\n * @param keepRight 保留右侧字符数(默认 0)\r\n * @param maskChar 脱敏字符(默认 `*`)\r\n * @returns 脱敏文本\r\n * @example\r\n * toMaskText('王小明', 1, 0) // => '王*'\r\n * toMaskText('王小明', 1, 1) // => '王*明'\r\n * toMaskText('13800138000', 3, 4) // => '138****8000'\r\n */\r\nexport function toMaskText(s: string, keepLeft = 1, keepRight = 0, maskChar = '*') {\r\n if (!s) return '';\r\n const v = String(s);\r\n const l = Math.max(0, keepLeft);\r\n const r = Math.max(0, keepRight);\r\n const len = v.length;\r\n const left = Math.min(l, len);\r\n const right = Math.min(r, len - left);\r\n const mid = len - left - right;\r\n if (mid <= 0) return v;\r\n const m = maskChar && maskChar.length > 0 ? maskChar : '*';\r\n return v.slice(0, left) + m.repeat(mid) + v.slice(len - right);\r\n}\r\n\r\n/**\r\n * 手机号中间打星:保留前三位与后四位,中间打 `*`。\r\n * @param phone 手机号字符串\r\n * @returns 遮蔽后的手机号\r\n * @example\r\n * toMaskPhone('13800138000') // => '138****8000'\r\n */\r\nexport function toMaskPhone(phone: string) {\r\n return toMaskText(phone, 3, 4);\r\n}\r\n\r\n/**\r\n * 姓名打星:\r\n * - 长度 ≤ 2:保留首字,其余打 `*`\r\n * - 长度 ≥ 3:保留首尾,各打星中间\r\n * @param name 姓名字符串\r\n * @returns 遮蔽后的姓名\r\n * @example\r\n * toMaskName('张三') // => '张*'\r\n * toMaskName('王小明') // => '王*明'\r\n */\r\nexport function toMaskName(name: string) {\r\n if (!name) return '';\r\n const v = String(name);\r\n return v.length <= 2 ? toMaskText(v, 1, 0) : toMaskText(v, 1, 1);\r\n}\r\n","/**\r\n * 生成UUID\r\n * @returns UUID字符串\r\n * @example\r\n * const uuid = createUUID() // '7982fcfe-5721-4632-bede-6000885be57d'\r\n */\r\nexport function createUUID() {\r\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = (Math.random() * 16) | 0,\r\n v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n}\r\n\r\n/**\r\n * 生成随机字符串id\r\n * - 常用于生成元素标签的id (默认加上'id_'前缀, 避免小程序中数字开头的id导致查询节点信息失败)\r\n * @param prefix 前缀, 默认 'id_'\r\n * @returns 随机字符串\r\n * @example\r\n * const id = createViewRandId(); // 'id_0rjuuuqv60xi'\r\n * const id = createViewRandId('canvas_'); // 'canvas_v82a7ctm09q'\r\n */\r\nexport function createViewRandId(prefix = 'id_') {\r\n return `${prefix}${Math.random().toString(36).substring(2, 16)}`;\r\n}\r\n\r\n/**\r\n * 时间+固定位数的随机数字字符串\r\n * @param digits 随机部分的位数,默认 6\r\n * @returns 时间+随机数字字符串\r\n * @example\r\n * const traceId = createTimeRandId(); // '1763002648039123456'\r\n * const traceId = createTimeRandId(8); // '176300264803912345678'\r\n */\r\nexport function createTimeRandId(digits: number = 6) {\r\n const base = 10 ** (digits - 1);\r\n const range = 9 * base;\r\n const int = Math.floor(Math.random() * range) + base;\r\n\r\n return `${Date.now()}${int}`;\r\n}\r\n","/**\r\n * 获取字节长度 (支持字符串、Buffer/Uint8Array、File/Blob 等类型)\r\n * - 字符串按 UTF-8 编码计算字节长度(每个字符 1-4 字节)\r\n * - Buffer/Uint8Array 直接返回字节长度(每个元素 1 字节)\r\n * - File/Blob 返回文件/Blob 大小(字节数)\r\n * @param data 输入的数据\r\n * @returns 数据的字节长度\r\n * @example\r\n * getByteLength('abc') // 3\r\n * getByteLength('中文') // 6\r\n * getByteLength('😊') // 4\r\n * getByteLength(new Uint8Array([0x41, 0x42, 0x43])) // 3\r\n * getByteLength(new File(['abc'], 'test.txt')) // 3\r\n * getByteLength(new Blob(['中文'], { type: 'text/plain' })) // 6\r\n */\r\nexport function getByteLength(data: string | ArrayBuffer | ArrayBufferView | File | Blob): number {\r\n if (typeof data === 'string') {\r\n let byteLen = 0;\r\n\r\n for (let i = 0; i < data.length; i++) {\r\n const code = data.charCodeAt(i);\r\n\r\n if (code <= 0x7f) {\r\n byteLen += 1; // (ASCII 基本拉丁)→ 包含数字 0-9、英文字母 A-Z/a-z、常见符号\r\n } else if (code <= 0x7ff) {\r\n byteLen += 2; // (拉丁扩展)→ 包含拉丁字母(含变音符)、希腊文、俄文/西里尔文、希伯来文、阿拉伯文等\r\n } else if (code >= 0xd800 && code <= 0xdbff) {\r\n byteLen += 4; // (UTF-16 代理项)→ 包含 emoji、稀有汉字(扩展区)、音乐符号等\r\n i++;\r\n } else {\r\n byteLen += 3; // (BMP 绝大部分)→ 包含中文/日文/韩文的大多数字符(CJK 统一汉字)、以及大量其它脚本\r\n }\r\n }\r\n\r\n return byteLen;\r\n }\r\n\r\n // Buffer/Uint8Array\r\n if ('byteLength' in data) return data.byteLength;\r\n\r\n // File/Blob\r\n if ('size' in data) return data.size;\r\n\r\n throw new TypeError('getByteLength: Unsupported type');\r\n}\r\n","import { getObjectKeys } from '../../object';\r\n\r\n/**\r\n * 跨端 100% 原生可解码文件后缀表\r\n * 覆盖:iOS 14+ / Android 5+ WebView、Chrome/Edge/Safari/Firefox\r\n * 剔除:所有需要 polyfill 或转码的容器/编码\r\n */\r\nconst FILE_TYPE = {\r\n img: ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp'],\r\n video: ['mp4', 'mov', 'm4v'],\r\n voice: ['mp3', 'wav', 'm4a'],\r\n excel: ['csv', 'xls', 'xlsx', 'xlsm', 'ods'],\r\n word: ['txt', 'doc', 'docx', 'pdf', 'md', 'wps'],\r\n zip: ['zip', 'gz', 'tar', 'rar', '7z'],\r\n ppt: ['ppt', 'pptx', 'odp'],\r\n app: ['apk', 'ipa'],\r\n};\r\n\r\n/**\r\n * 获取文件后缀(不含点,返回小写)。\r\n * 当文件名不包含点('.')时,返回空字符串。\r\n * @param fileName 文件名,例如 `avatar.PNG`\r\n * @returns 后缀字符串,例如 `png`\r\n * @example getFileSuffix('avatar.PNG') // 'png'\r\n * @example getFileSuffix('a.tar.gz') // 'gz'\r\n * @example getFileSuffix('.ignore') // ''\r\n * @example getFileSuffix('abc') // ''\r\n */\r\nexport function getFileSuffix(fileName: string) {\r\n if (fileName.startsWith('.')) return ''; // 隐藏文件,返回空串\r\n\r\n const idx = fileName.lastIndexOf('.');\r\n return idx > 0 ? fileName.slice(idx + 1).toLowerCase() : '';\r\n}\r\n\r\n/**\r\n * 根据文件后缀判断文件类型。\r\n * 会将后缀转换为小写后与 `FILE_TYPE` 映射匹配;若未匹配到则返回 `'unknown'`。\r\n * @param fileName 文件名\r\n * @returns 文件类型字符串(如 'img' | 'video' | 'voice' | 'excel' | 'word' | 'zip' | 'ppt' | 'app' | 'unknown')\r\n * @example getFileType('avatar.PNG') // 'img'\r\n * @example getFileType('archive.tar') // 'zip'\r\n * @example getFileType('abc') // 'unknown'\r\n */\r\nexport function getFileType(fileName: string) {\r\n const suffix = getFileSuffix(fileName);\r\n if (!suffix) return 'unknown';\r\n\r\n const keys = getObjectKeys(FILE_TYPE);\r\n for (const key of keys) {\r\n if (FILE_TYPE[key].includes(suffix)) {\r\n return key;\r\n }\r\n }\r\n\r\n return 'unknown';\r\n}\r\n","import type {\r\n OSSOption,\r\n OSSAudioOption,\r\n OSSHlsOption,\r\n OSSImgOption,\r\n OSSVideoOption,\r\n OSSWatermarkOption,\r\n} from './index.d';\r\n\r\nexport * from './index.d';\r\n\r\n/**\r\n * oss图片处理\r\n * 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/img-parameters/?spm=a2c4g.11186623.help-menu-31815.d_0_11_2_1.572824a1a1W5Pf&scm=20140722.H_144582._.OR_help-T_cn~zh-V_1\r\n * @param src 原始图片URL\r\n * @param option 图片处理选项\r\n * @returns 处理后的图片URL(格式: `{src}?x-oss-process=image/xx`)\r\n * @example\r\n * 缩放: getOSSImg('xx.jpg', { resize: { w: 100, h: 100 } })\r\n * 水印: getOSSImg('xx.jpg', { watermark: { text: '水印' } });\r\n * 翻转: getOSSImg('xx.jpg', { flip: 1 });\r\n * 裁剪: getOSSImg('xx.jpg', { crop: { w: 100, h: 100 } });\r\n * 质量: getOSSImg('xx.jpg', { quality: { q: 80 } });\r\n * 格式转换: getOSSImg('xx.jpg', { format: 'jpg' });\r\n * 获取信息: getOSSImg('xx.jpg', { info: true });\r\n * 自适应方向: getOSSImg('xx.jpg', { 'auto-orient': 1 });\r\n * 内切圆: getOSSImg('xx.jpg', { circle: { r: 100 } });\r\n * 索引切割: getOSSImg('xx.jpg', { indexcrop: { x: 100 } });\r\n * 圆角: getOSSImg('xx.jpg', { 'rounded-corners': { r: 10 } });\r\n * 模糊: getOSSImg('xx.jpg', { blur: { r: 10, s: 10 } });\r\n * 旋转: getOSSImg('xx.jpg', { rotate: 90 });\r\n * 渐进显示: getOSSImg('xx.jpg', { interlace: 1 });\r\n * 主色调: getOSSImg('xx.jpg', { 'average-hue': true });\r\n * 亮度: getOSSImg('xx.jpg', { bright: 10 });\r\n * 锐化: getOSSImg('xx.jpg', { sharpen: 100 });\r\n * 对比度: getOSSImg('xx.jpg', { contrast: 100 });\r\n */\r\nexport function getOSSImg(src: string, option: OSSImgOption) {\r\n return buildOSSUrl(src, 'image', option);\r\n}\r\n\r\n/**\r\n * oss视频处理\r\n * 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/audio-and-video-processing/\r\n * @param src 原始视频URL\r\n * @param option 视频处理选项\r\n * @returns 处理后的URL(格式: `{src}?x-oss-process=video/xx`)\r\n * @example\r\n * 视频转码: getOSSVideo('xx.mp4', { convert: { format: 'mp4' } })\r\n * 转为动图: getOSSVideo('xx.mp4', { animation: { format: 'gif' } })\r\n * 雪碧图: getOSSVideo('xx.mp4', { sprite: { format: 'png' } })\r\n * 多帧截取: getOSSVideo('xx.mp4', { snapshots: { count: 3 } })\r\n * 视频拼接: getOSSVideo('xx.mp4', { concat: { list: 'a.mp4,b.mp4' } })\r\n * 信息查询: getOSSVideo('xx.mp4', { info: true })\r\n * 组合操作: getOSSVideo('xx.mp4', { convert: { format: 'mp4' }, snapshots: { count: 3 } })\r\n */\r\nexport function getOSSVideo(src: string, option: OSSVideoOption) {\r\n return buildOSSUrl(src, 'video', option);\r\n}\r\n\r\n/**\r\n * oss音频处理\r\n * 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/audio-and-video-processing/\r\n * @param src 原始音频URL\r\n * @param option 音频处理选项\r\n * @returns 处理后的URL(格式: `{src}?x-oss-process=audio/xx`)\r\n * @example\r\n * 音频转码: getOSSAudio('xx.mp3', { 'convert': { format: 'mp3' } })\r\n * 音频拼接: getOSSAudio('xx.mp3', { 'concat': { list: 'a.mp3,b.mp3' } })\r\n * 信息查询: getOSSAudio('xx.mp3', { 'info': true })\r\n */\r\nexport function getOSSAudio(src: string, option: OSSAudioOption) {\r\n return buildOSSUrl(src, 'audio', option);\r\n}\r\n\r\n/**\r\n * oss直播处理(边转边播 HLS)\r\n * 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/audio-and-video-processing/\r\n * @param src 原始视频URL\r\n * @param option HLS 选项(或布尔)\r\n * @returns 处理后的URL(格式: `{src}?x-oss-process=hls/xx`)\r\n * @example\r\n * 生成播放列表: getOSSHls('xx.mp4', { 'm3u8': true })\r\n * 配置参数: getOSSHls('xx.mp4', { 'm3u8': { playlist: 1, segtime: 6 } })\r\n */\r\nexport function getOSSHls(src: string, option: OSSHlsOption) {\r\n return buildOSSUrl(src, 'hls', option);\r\n}\r\n\r\n/**\r\n * 构造oss处理地址\r\n * @param src 原始地址\r\n * @param type oss处理类型,如`image`, `audio`, `video`, `hls`\r\n * @param option oss处理选项\r\n * @returns 处理后的URL(格式: `{src}?x-oss-process={type}/{segs.join('/')}`)\r\n */\r\nexport function buildOSSUrl(src: string, type: string, option: OSSOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n if (src.includes('.svg')) return src;\r\n\r\n const segs: string[] = [];\r\n\r\n // 遍历选项,构造处理参数\r\n for (const [k, v] of Object.entries(option)) {\r\n const seg = k === 'watermark' ? getWatermark(v as OSSWatermarkOption) : getOSSSegs(k, v);\r\n if (seg) segs.push(seg);\r\n }\r\n\r\n if (!segs.length) return src;\r\n\r\n // 拼接处理参数(先移除查询参数,避免重复拼接)\r\n const base = src.split('?')[0];\r\n return `${base}?x-oss-process=${type}/${segs.join('/')}`;\r\n}\r\n\r\n/**\r\n * 构造图片处理参数\r\n * @param type 图片处理类型,如`resize`, `flip`, `format`, `info`\r\n * @param option 图片处理选项\r\n * @returns `object`返回格式为`resize,w_100,h_100`\r\n * @returns `number`返回格式为`flip,1`\r\n * @returns `string`返回格式为`format,jpg`\r\n * @returns `true`返回格式为`info`, `false`返回空字符串\r\n */\r\nfunction getOSSSegs(type: string, option?: Record<string, unknown> | number | string | boolean) {\r\n if (!option && option !== 0) return '';\r\n\r\n if (option === true) return type;\r\n\r\n if (typeof option === 'number' || typeof option === 'string') return `${type},${option}`;\r\n\r\n const segs = Object.entries(option)\r\n .map(([k, v]) => `${k}_${v}`)\r\n .join(',');\r\n\r\n return segs ? `${type},${segs}` : '';\r\n}\r\n\r\n/**\r\n * 图片水印 (文本和图片已Base64编码)\r\n * @returns 格式: `watermark,text_xxx`\r\n */\r\nfunction getWatermark(w?: OSSWatermarkOption) {\r\n if (!w) return '';\r\n if (w.image) w.image = toBase64Url(w.image);\r\n if (w.text) w.text = toBase64Url(w.text);\r\n if (w.type) w.type = toBase64Url(w.type);\r\n return getOSSSegs('watermark', w);\r\n}\r\n\r\n/**\r\n * Base64编码\r\n */\r\nfunction toBase64Url(s: string) {\r\n let b64 = '';\r\n if (typeof Buffer !== 'undefined') {\r\n const buf = Buffer.from(s, 'utf-8');\r\n b64 = buf.toString('base64');\r\n } else {\r\n try {\r\n b64 = btoa(unescape(encodeURIComponent(s)));\r\n } catch {\r\n b64 = '';\r\n }\r\n }\r\n return b64.replace(/=+$/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\r\n}\r\n","/**\r\n * 将对象参数拼接到 URL\r\n * - 采用纯JS拼接,因为小程序不支持URLSearchParams\r\n * @param url 基础地址\r\n * @param param 将要追加的参数对象;`null/undefined` 值会被忽略,Object 会使用 `JSON.stringify`\r\n * @returns 拼接后的完整 URL(保留原有哈希片段)\r\n * @example\r\n * const url = appendUrlParam('https://a.com', { q: '测试', list: [1, 2], a: null, b: undefined }); // 'https://a.com/?q=%E6%B5%8B%E8%AF%95&list=[1,2]'\r\n */\r\nexport function appendUrlParam(url: string, param: Record<string, unknown>) {\r\n if (!param || typeof param !== 'object') return url;\r\n\r\n const hashIndex = url.indexOf('#');\r\n const baseWithoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;\r\n const hash = hashIndex >= 0 ? url.slice(hashIndex) : '';\r\n\r\n const [base, existingQs] = baseWithoutHash.split('?');\r\n const parts: string[] = [];\r\n if (existingQs) parts.push(existingQs);\r\n for (const key in param) {\r\n const rawVal = param[key];\r\n if (rawVal === null || rawVal === undefined) continue;\r\n const val = typeof rawVal === 'object' ? JSON.stringify(rawVal) : String(rawVal);\r\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`);\r\n }\r\n const qs = parts.filter(Boolean).join('&');\r\n return base + (qs ? `?${qs}` : '') + hash;\r\n}\r\n","import type { QnImgOption, QnMogr2Option, QnWatermarkOption, QnImageView2Option } from './index.d';\r\n\r\nexport * from './index.d';\r\n\r\n/**\r\n * qn图片处理\r\n * 参考官方文档: https://developer.qiniu.com/kodo/8623/dev-the-picture-style\r\n * @param src 原始图片URL\r\n * @param option 图片处理选项\r\n * @returns 处理后的图片URL(格式: `{src}?imageView2/...|imageMogr2/...|watermark/...|imageslim|imageInfo`)\r\n * @example\r\n * 缩略: getQnImg('xx.jpg', { imageView2: { mode: 2, w: 100, h: 100 } })\r\n * 高级缩放: getQnImg('xx.jpg', { thumbnail: '!50p' })\r\n * 裁剪: getQnImg('xx.jpg', { crop: '100x100' })\r\n * 旋转: getQnImg('xx.jpg', { rotate: 90 })\r\n * 自适应方向: getQnImg('xx.jpg', { 'auto-orient': true })\r\n * 格式转换: getQnImg('xx.jpg', { format: 'webp' })\r\n * 质量: getQnImg('xx.jpg', { q: 80 })\r\n * 渐进显示: getQnImg('xx.jpg', { interlace: 1 })\r\n * 背景色填充: getQnImg('xx.jpg', { background: 'white' })\r\n * 模糊: getQnImg('xx.jpg', { blur: { r: 10, s: 10 } })\r\n * GIF颜色控制: getQnImg('xx.jpg', { colors: 64 })\r\n * 图片瘦身: getQnImg('xx.jpg', { imageslim: true })\r\n * 图片信息: getQnImg('xx.jpg', { imageInfo: true })\r\n * 图片水印: getQnImg('xx.jpg', { watermark: { type: 'image', image: 'http://example.com/logo.png' } })\r\n * 文字水印: getQnImg('xx.jpg', { watermark: { type: 'text', text: '水印', fontsize: 18 } })\r\n */\r\nexport function getQnImg(src: string, option: QnImgOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n if (src.includes('.svg')) return src;\r\n\r\n const segs: string[] = [];\r\n\r\n if (option.imageslim) segs.push('imageslim');\r\n\r\n if (option.imageView2) segs.push(getImageView2(option.imageView2));\r\n\r\n const mogr = getImageMogr2(option.imageMogr2 ?? option);\r\n if (mogr) segs.push(mogr);\r\n\r\n if (option.watermark) segs.push(getWatermark(option.watermark));\r\n\r\n if (option.imageInfo) segs.push('imageInfo');\r\n\r\n if (!segs.length) return src;\r\n\r\n const base = src.split('?')[0];\r\n return `${base}?${segs.join('|')}`;\r\n}\r\n\r\n/**\r\n * qn视频处理\r\n * 参考官方文档: https://developer.qiniu.com/kodo/12654/video-process\r\n * @param src 原始视频URL\r\n * @param option 视频处理选项(支持 avthumb、vframe)\r\n * @returns 处理后的URL(格式: `{src}?avthumb/...|vframe/...`)\r\n * @example\r\n * 视频转码: getQnVideo('xx.mp4', { avthumb: { format: 'mp4', s: '1280x720', vcodec: 'libx264', vb: '1.25m' } })\r\n * 截帧: getQnVideo('xx.mp4', { vframe: { format: 'jpg', offset: 3, w: 480, h: 360 } })\r\n */\r\nexport function getQnVideo(src: string, option: import('.').QnVideoOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n if (src.includes('.svg')) return src;\r\n const segs: string[] = [];\r\n if (option.avthumb) segs.push(getAvthumb(option.avthumb));\r\n if (option.vframe) segs.push(getVframe(option.vframe));\r\n if (!segs.length) return src;\r\n const base = src.split('?')[0];\r\n return `${base}?${segs.join('|')}`;\r\n}\r\n\r\n/**\r\n * qn音频处理\r\n * 参考官方文档: https://developer.qiniu.com/kodo/12654/video-process\r\n * @param src 原始音频URL\r\n * @param option 音频处理选项(通过 avthumb 转码)\r\n * @returns 处理后的URL(格式: `{src}?avthumb/...`)\r\n * @example\r\n * 音频转码: getQnAudio('xx.aac', { avthumb: { format: 'mp3', ab: '128k', ar: 44100, acodec: 'libmp3lame' } })\r\n */\r\nexport function getQnAudio(src: string, option: import('.').QnAudioOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n const segs: string[] = [];\r\n if (option.avthumb) segs.push(getAvthumb(option.avthumb));\r\n if (!segs.length) return src;\r\n const base = src.split('?')[0];\r\n return `${base}?${segs.join('|')}`;\r\n}\r\n\r\n/**\r\n * qn实时转码(HLS 边转边播)\r\n * 参考官方文档: https://developer.qiniu.com/kodo/12654/video-process、实时转码\r\n * @param src 原始视频URL\r\n * @param option HLS 选项\r\n * @returns 处理后的URL(格式: `{src}?avcvt/{level}/format/m3u8/...`)\r\n * @example\r\n * 720P HLS: getQnHls('xx.mp4', { level: 3, format: 'm3u8', segtime: 6 })\r\n */\r\nexport function getQnHls(src: string, option: import('.').QnHlsOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n const seg = getAvcvt(option);\r\n if (!seg) return src;\r\n const base = src.split('?')[0];\r\n return `${base}?${seg}`;\r\n}\r\n\r\nfunction getImageView2(opt?: QnImageView2Option) {\r\n if (!opt) return '';\r\n const mode = typeof opt.mode === 'number' ? opt.mode : 0;\r\n const kv: string[] = [];\r\n for (const [k, v] of Object.entries(opt)) {\r\n if (k === 'mode') continue;\r\n if (typeof v === 'boolean') {\r\n if (v) kv.push(`${k}/1`);\r\n } else if (typeof v === 'number' || typeof v === 'string') {\r\n kv.push(`${k}/${v}`);\r\n }\r\n }\r\n return kv.length ? `imageView2/${mode}/${kv.join('/')}` : `imageView2/${mode}`;\r\n}\r\n\r\nfunction getImageMogr2(opt?: QnMogr2Option | QnImgOption) {\r\n if (!opt) return '';\r\n const parts: string[] = [];\r\n\r\n const tn = (opt as QnMogr2Option).thumbnail;\r\n if (typeof tn !== 'undefined') parts.push(`thumbnail/${tn}`);\r\n\r\n const cp = (opt as QnMogr2Option).crop;\r\n if (typeof cp !== 'undefined') parts.push(`crop/${cp}`);\r\n\r\n const rot = (opt as QnMogr2Option).rotate;\r\n if (typeof rot === 'number') parts.push(`rotate/${rot}`);\r\n\r\n const ao = (opt as QnMogr2Option)['auto-orient'];\r\n if (ao) parts.push('auto-orient');\r\n\r\n const fmt = (opt as QnMogr2Option).format;\r\n if (typeof fmt === 'string') parts.push(`format/${fmt}`);\r\n\r\n const il = (opt as QnMogr2Option).interlace;\r\n if (il === 0 || il === 1) parts.push(`interlace/${il}`);\r\n\r\n const bg = (opt as QnMogr2Option).background;\r\n if (typeof bg === 'string') parts.push(`background/${bg}`);\r\n\r\n const q = (opt as QnMogr2Option).q;\r\n if (typeof q === 'number') parts.push(`q/${q}`);\r\n\r\n const blur = (opt as QnMogr2Option).blur;\r\n if (typeof blur !== 'undefined') {\r\n if (typeof blur === 'string') parts.push(`blur/${blur}`);\r\n else parts.push(`blur/${blur.r}x${blur.s}`);\r\n }\r\n\r\n const colors = (opt as QnMogr2Option).colors;\r\n if (typeof colors === 'number') parts.push(`colors/${colors}`);\r\n\r\n return parts.length ? `imageMogr2/${parts.join('/')}` : '';\r\n}\r\n\r\nfunction getWatermark(w?: QnWatermarkOption) {\r\n if (!w) return '';\r\n const mode =\r\n w.type === 'image' ? 1 : w.type === 'text' ? 2 : typeof w.type === 'number' ? w.type : 2;\r\n const segs: string[] = [`watermark/${mode}`];\r\n if (mode === 1 && w.image) segs.push(`image/${toBase64Url(w.image)}`);\r\n if (mode === 2 && w.text) segs.push(`text/${toBase64Url(w.text)}`);\r\n if (w.font) segs.push(`font/${toBase64Url(w.font)}`);\r\n if (typeof w.fontsize === 'number') segs.push(`fontsize/${w.fontsize}`);\r\n if (w.fill) segs.push(`fill/${toBase64Url(w.fill)}`);\r\n if (w.gravity) segs.push(`gravity/${w.gravity}`);\r\n if (typeof w.dx === 'number') segs.push(`dx/${w.dx}`);\r\n if (typeof w.dy === 'number') segs.push(`dy/${w.dy}`);\r\n if (typeof w.dissolve === 'number') segs.push(`dissolve/${w.dissolve}`);\r\n return segs.join('/');\r\n}\r\n\r\nfunction toBase64Url(s: string) {\r\n let b64 = '';\r\n if (typeof Buffer !== 'undefined') {\r\n const buf = Buffer.from(s, 'utf-8');\r\n b64 = buf.toString('base64');\r\n } else {\r\n try {\r\n b64 = btoa(unescape(encodeURIComponent(s)));\r\n } catch {\r\n b64 = '';\r\n }\r\n }\r\n return b64.replace(/=+$/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\r\n}\r\n\r\nfunction getAvthumb(opt: import('.').QnAvthumbOption) {\r\n const parts: string[] = [];\r\n if (opt.format) parts.push(`avthumb/${opt.format}`);\r\n else parts.push('avthumb');\r\n if (opt.s) parts.push(`s/${opt.s}`);\r\n if (opt.vcodec) parts.push(`vcodec/${opt.vcodec}`);\r\n if (typeof opt.vb !== 'undefined') parts.push(`vb/${opt.vb}`);\r\n if (typeof opt.r === 'number') parts.push(`r/${opt.r}`);\r\n if (typeof opt.ab !== 'undefined') parts.push(`ab/${opt.ab}`);\r\n if (typeof opt.ar === 'number') parts.push(`ar/${opt.ar}`);\r\n if (opt.acodec) parts.push(`acodec/${opt.acodec}`);\r\n return parts.join('/');\r\n}\r\n\r\nfunction getVframe(opt: import('.').QnVframeOption) {\r\n const parts: string[] = [];\r\n parts.push(`vframe/${opt.format || 'jpg'}`);\r\n if (typeof opt.offset === 'number') parts.push(`offset/${opt.offset}`);\r\n if (typeof opt.w === 'number') parts.push(`w/${opt.w}`);\r\n if (typeof opt.h === 'number') parts.push(`h/${opt.h}`);\r\n return parts.join('/');\r\n}\r\n\r\nfunction getAvcvt(opt: import('.').QnHlsOption) {\r\n const parts: string[] = [];\r\n const level = typeof opt.level === 'number' ? `/${opt.level}` : '/3';\r\n parts.push(`avcvt${level}`);\r\n parts.push(`format/${opt.format || 'm3u8'}`);\r\n if (typeof opt.segtime === 'number') parts.push(`segtime/${opt.segtime}`);\r\n if (typeof opt.t === 'string') parts.push(`t/${opt.t}`);\r\n if (opt.vcodec) parts.push(`vcodec/${opt.vcodec}`);\r\n if (typeof opt.vb !== 'undefined') parts.push(`vb/${opt.vb}`);\r\n if (typeof opt.r === 'number') parts.push(`r/${opt.r}`);\r\n if (typeof opt.s === 'string') parts.push(`s/${opt.s}`);\r\n if (opt.acodec) parts.push(`acodec/${opt.acodec}`);\r\n if (typeof opt.ab !== 'undefined') parts.push(`ab/${opt.ab}`);\r\n if (typeof opt.ar === 'number') parts.push(`ar/${opt.ar}`);\r\n if (typeof opt.output === 'string') parts.push(`output/${toBase64Url(opt.output)}`);\r\n return parts.join('/');\r\n}\r\n","/**\r\n * 纯字母(不含空格与符号)。\r\n * @param s 字符串\r\n * @returns 是否为字母\r\n * @example\r\n * isLetter('abc') // true\r\n * isLetter('123') // false\r\n * isLetter('abc123') // false\r\n */\r\nexport function isLetter(s: string) {\r\n return /^[a-zA-Z]*$/.test(s);\r\n}\r\n\r\n/**\r\n * 纯中文(不含空格与符号)。\r\n * @param s 字符串\r\n * @returns 是否为纯中文\r\n * @example\r\n * isChinese('你好') // true\r\n */\r\nexport function isChinese(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^[\\u4E00-\\u9FA5]+$/.test(v);\r\n}\r\n\r\n/**\r\n * 纯数字(非负整数,不含空格与符号)。\r\n * @param s 字符串\r\n * @returns 是否为数字\r\n * @example\r\n * isDigits('12') // true\r\n * isDigits('1.2') // false\r\n * isDigits('-12') // false\r\n * isDigits('a12') // false\r\n */\r\nexport function isDigits(s: string) {\r\n return /^[0-9]+$/.test(s);\r\n}\r\n\r\n/**\r\n * 数字字符串格式校验\r\n * @param value 待验证值(字符串或数字)\r\n * @param options 可选项\r\n * @param options.negative 是否允许负数,默认 false\r\n * @param options.decimal 小数位数,默认 2(0 表示必须整数)\r\n * @param options.thousands 是否允许千分位逗号,默认 false\r\n * @param options.leadZero 是否允许整数部分出现多余前导0 (默认false, 即禁止'007',但允许'0.50')\r\n * @example\r\n * isNumeric('123.45'); // true\r\n * isNumeric('123.45', { decimal: 0 }); // false\r\n * isNumeric('-1,234.5', { negative: true, thousands: true }); // true\r\n * isNumeric('0123', { leadZero: true }); // true(现在允许)\r\n * isNumeric('0123'); // false(默认禁止)\r\n * isNumeric('0.50'); // true(始终允许)\r\n * isNumeric('.5'); // false(整数部分不能省)\r\n * isNumeric('123.'); // false(小数部分不能省)\r\n */\r\nexport function isNumeric(\r\n value: string | number,\r\n options?: {\r\n negative?: boolean;\r\n decimal?: number;\r\n thousands?: boolean;\r\n leadZero?: boolean;\r\n },\r\n): boolean {\r\n const { negative = false, decimal = 2, thousands = false, leadZero = false } = options || {};\r\n\r\n if (value === null || value === undefined || value === '') return false;\r\n const str = String(value).trim();\r\n\r\n const sign = negative && str.startsWith('-') ? '-' : '';\r\n const body = sign ? str.slice(1) : str;\r\n\r\n const thousandsPart = thousands ? '(?:[1-9]\\\\d{0,2}(,\\\\d{3})*|0)' : '(?:\\\\d+)';\r\n\r\n const intPart = thousands ? thousandsPart : leadZero ? '(?:\\\\d+)' : '(?:0|[1-9]\\\\d*)';\r\n\r\n const fracPart = decimal === 0 ? '' : `(\\\\.\\\\d{1,${decimal}})`;\r\n\r\n const pattern = `^${intPart}${fracPart}$`;\r\n const reg = new RegExp(pattern);\r\n return reg.test(body);\r\n}\r\n\r\n/**\r\n * 是否为中国大陆手机号(11 位,以 1 开头,第二位 3-9)。\r\n * @param s 待校验的号码\r\n * @returns 是否为合法手机号\r\n * @example\r\n * isMobilePhone('13800138000') // true\r\n * isMobilePhone('12800138000') // false\r\n */\r\nexport function isMobilePhone(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^1[3-9]\\d{9}$/.test(v);\r\n}\r\n\r\n/**\r\n * 是否为中国大陆座机号(区号-号码,可选分机)。\r\n * 格式:`0AA-BBBBBBBB(-EXT)`,其中区号 2-3 位、号码 7-8 位、分机 1-6 位。\r\n * @param s 待校验的号码\r\n * @returns 是否为合法座机号\r\n * @example\r\n * isLandline('010-88888888') // true\r\n * isLandline('0371-12345678-123') // true\r\n */\r\nexport function isLandline(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^0\\d{2,3}-?\\d{7,8}(?:-\\d{1,6})?$/.test(v);\r\n}\r\n\r\n/**\r\n * 联系电话:是否为中国大陆“手机号或座机号”。\r\n * @param s 待校验的号码\r\n * @returns 是否为合法的手机号或座机号\r\n * @example\r\n * isPhone('13800138000') // true\r\n * isPhone('010-88888888') // true\r\n */\r\nexport function isPhone(s: string) {\r\n return isMobilePhone(s) || isLandline(s);\r\n}\r\n\r\n/**\r\n * 校验邮箱地址(基于 RFC 5322 的常用子集)。\r\n * @param s 待校验的邮箱字符串\r\n * @returns 是否为合法邮箱\r\n * @example\r\n * isEmail('user@example.com') // true\r\n * isEmail('invalid@') // false\r\n */\r\nexport function isEmail(s: string) {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n const emailRegex =\r\n /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$/;\r\n return emailRegex.test(v);\r\n}\r\n\r\n/**\r\n * 中文姓名(允许中间点 `·`),长度 2-20。\r\n * @param s 姓名\r\n * @returns 是否为合法中文姓名\r\n * @example\r\n * isChineseName('张三') // true\r\n * isChineseName('阿·娜') // true\r\n */\r\nexport function isChineseName(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^[\\u4E00-\\u9FA5·]{2,20}$/.test(v);\r\n}\r\n\r\n/**\r\n * 身份证校验(支持中国大陆严格校验;台湾/香港/澳门做格式校验)。\r\n * 规则:\r\n * - 中国大陆(严格):18 位校验位 + 出生日期合法性;兼容 15 位旧号(日期校验);\r\n * - 台湾(格式):`^[A-Z][12]\\d{8}$`(首字母 + 性别位 1/2 + 8 位数字);不含校验位算法;\r\n * - 香港(格式):`^[A-Z]{1,2}\\d{6}\\(?[0-9A]\\)?$`(1-2 字母 + 6 位数字 + 校验位,可带括号);\r\n * - 澳门(格式):常见为 `^[157]\\d{6}\\(?\\d\\)?$`(类别位 1/5/7 + 7 位数字 + 校验位,可带括号)。\r\n * @param code 身份证号码\r\n * @returns 是否为合法身份证号\r\n * @example\r\n * isIdentityCard('11010519491231002X') // true(大陆)\r\n * isIdentityCard('A123456789') // true(台湾格式)\r\n * isIdentityCard('A123456(3)') // true(香港格式)\r\n * isIdentityCard('1234567(8)') // true(澳门格式)\r\n */\r\nexport function isIdentityCard(code: string) {\r\n const v = String(code ?? '').trim();\r\n if (v === '') return false;\r\n\r\n const isValidDate = (yyyymmdd: string) => {\r\n const y = Number(yyyymmdd.slice(0, 4));\r\n const m = Number(yyyymmdd.slice(4, 6));\r\n const d = Number(yyyymmdd.slice(6, 8));\r\n if (y < 1900 || y > 2100) return false;\r\n const date = new Date(y, m - 1, d);\r\n return date.getFullYear() === y && date.getMonth() === m - 1 && date.getDate() === d;\r\n };\r\n\r\n // 18位校验\r\n if (/^\\d{17}[\\dXx]$/.test(v)) {\r\n const birth = v.slice(6, 14);\r\n if (!isValidDate(birth)) return false;\r\n const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];\r\n const checkMap = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];\r\n let sum = 0;\r\n for (let i = 0; i < 17; i++) sum += Number(v[i]) * weights[i];\r\n const mod = sum % 11;\r\n const code18 = v[17].toUpperCase();\r\n return checkMap[mod] === code18;\r\n }\r\n\r\n // 15位旧号:仅校验日期(第7~12位),不做校验位\r\n if (/^\\d{15}$/.test(v)) {\r\n const birth = v.slice(6, 12);\r\n const y = Number(`19${birth.slice(0, 2)}`);\r\n const m = Number(birth.slice(2, 4));\r\n const d = Number(birth.slice(4, 6));\r\n const date = new Date(y, m - 1, d);\r\n return date.getFullYear() === y && date.getMonth() === m - 1 && date.getDate() === d;\r\n }\r\n\r\n // 台湾(格式校验)\r\n if (/^[A-Za-z][12]\\d{8}$/.test(v)) return true;\r\n\r\n // 香港(格式校验):支持最后一位带括号与不带括号\r\n if (/^[A-Za-z]{1,2}\\d{6}\\(?[0-9A]\\)?$/.test(v)) return true;\r\n\r\n // 澳门(格式校验):常见 1/5/7 开头 + 7位数字 + 校验位,可带括号\r\n if (/^[157]\\d{6}\\(?\\d\\)?$/.test(v)) return true;\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * 护照号码校验(宽松通用格式,适合单输入框无法确认国家的场景)。\r\n * 说明:各国护照格式差异较大,此函数提供常见模式的格式校验;不做校验位算法。\r\n * 包含:\r\n * - 中国护照常见:`E/G` 开头 + 8 位数字;`D/P/S` 开头 + 7 位数字;\r\n * - 台湾护照常见:首字母 + 8 位数字;\r\n * - 通用兜底:6-9 位的字母数字组合;移除输入中的空格与 `-` 后再校验。\r\n * @param s 护照号码\r\n * @returns 是否匹配常见护照格式\r\n * @example\r\n * isPassport('E12345678') // true\r\n * isPassport('P1234567') // true\r\n * isPassport('A12345678') // true(台湾常见)\r\n * isPassport('AB-1234567') // true(移除分隔符后匹配)\r\n */\r\nexport function isPassport(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim();\r\n if (t === '') return false;\r\n if (/^[EG]\\d{8}$/.test(t)) return true; // CN:E/G + 8 digits\r\n if (/^[DPS]\\d{7}$/.test(t)) return true; // CN:D/P/S + 7 digits\r\n if (/^[A-Za-z]\\d{8}$/.test(t)) return true; // TW:letter + 8 digits\r\n if (/^[A-Za-z0-9]{6,9}$/.test(t)) return true; // 通用宽松兜底\r\n return false;\r\n}\r\n\r\n/**\r\n * 港澳通行证(回乡证)号码校验。\r\n * 说明:常见为 `H/M` 开头 + 8~10 位数字;自动移除输入中的空格与 `-`。\r\n * @param s 证件号\r\n * @returns 是否为港澳通行证格式\r\n * @example\r\n * isHKMOPermit('H12345678') // true\r\n * isHKMOPermit('M1234567890') // true\r\n */\r\nexport function isHKMOPermit(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim()\r\n .toUpperCase();\r\n return /^[HM]\\d{8,10}$/.test(t);\r\n}\r\n\r\n/**\r\n * 台湾居民来往大陆通行证(台胞证)号码校验。\r\n * 说明:常见为 8 位纯数字;或首字母 + 8 位数字;部分场景存在 10 位数字。\r\n * @param s 证件号\r\n * @returns 是否为台胞证格式\r\n * @example\r\n * isTaiwanPermit('12345678') // true\r\n * isTaiwanPermit('T12345678') // true\r\n * isTaiwanPermit('1234567890') // true\r\n */\r\nexport function isTaiwanPermit(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim()\r\n .toUpperCase();\r\n if (/^\\d{8}$/.test(t)) return true;\r\n if (/^[A-Z]\\d{8}$/.test(t)) return true;\r\n if (/^\\d{10}$/.test(t)) return true;\r\n return false;\r\n}\r\n\r\n/**\r\n * 军官证号码校验:字母数字组合,长度 7-18。\r\n * @param s 证件号\r\n * @returns 是否为军官证宽松格式\r\n * @example\r\n * isOfficerIdLoose('JX1234567') // true\r\n */\r\nexport function isOfficerId(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim()\r\n .toUpperCase();\r\n return /^[A-Z0-9]{7,18}$/.test(t);\r\n}\r\n\r\n/**\r\n * 士兵证号码校验:字母数字组合,长度 7-18。\r\n * @param s 证件号\r\n * @returns 是否为士兵证格式\r\n * @example\r\n * isSoldierId('SB12345678') // true\r\n */\r\nexport function isSoldierId(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim()\r\n .toUpperCase();\r\n return /^[A-Z0-9]{7,18}$/.test(t);\r\n}\r\n\r\n/**\r\n * 中国军证(军官证/士兵证)组合校验。\r\n * @param s 证件号\r\n * @returns 是否为军官证或士兵证格式\r\n * @example\r\n * isCnMilitaryId('JX1234567') // true\r\n */\r\nexport function isMilitaryId(s: string) {\r\n return isOfficerId(s) || isSoldierId(s);\r\n}\r\n\r\n/**\r\n * 银行卡号校验(Luhn 校验)。\r\n * 说明:移除空格与 `-` 后进行 Luhn 校验;长度通常为 12-19 位。\r\n * @param s 银行卡号\r\n * @returns 是否通过 Luhn 校验\r\n * @example\r\n * isBankCard('6222 0201 2345 6789') // true\r\n */\r\nexport function isBankCard(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim();\r\n if (!/^\\d{12,19}$/.test(t)) return false;\r\n let sum = 0;\r\n let shouldDouble = false;\r\n for (let i = t.length - 1; i >= 0; i--) {\r\n let digit = Number(t[i]);\r\n if (shouldDouble) {\r\n digit *= 2;\r\n if (digit > 9) digit -= 9;\r\n }\r\n sum += digit;\r\n shouldDouble = !shouldDouble;\r\n }\r\n return sum % 10 === 0;\r\n}\r\n\r\n/**\r\n * 中国车牌号校验(含普通与新能源)。\r\n * @param s 车牌号码\r\n * @returns 是否为合法中国车牌\r\n * @example\r\n * isLicensePlate('京A12345') // true\r\n * isLicensePlate('沪A12345D') // 新能源(末位 D/F)\r\n * isLicensePlate('粤BDF12345') // 新能源(第三位 D/F)\r\n */\r\nexport function isLicensePlate(s: string) {\r\n const v = String(s ?? '')\r\n .trim()\r\n .toUpperCase();\r\n const prov = '京沪津渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵青藏川宁琼粤';\r\n const std = new RegExp(`^[${prov}][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]$`);\r\n const ne1 = new RegExp(`^[${prov}][A-HJ-NP-Z][DF][A-HJ-NP-Z0-9]{5}$`);\r\n const ne2 = new RegExp(`^[${prov}][A-HJ-NP-Z][A-HJ-NP-Z0-9]{5}[DF]$`);\r\n return std.test(v) || ne1.test(v) || ne2.test(v);\r\n}\r\n\r\n/**\r\n * 校验统一社会信用代码(中国税号常用:18 位,含校验位)。\r\n * 规则:\r\n * - 字符集:数字与大写字母(不含 I/O/Z/S/V),即 `[0-9A-HJ-NPQRTUWXY]`;\r\n * - 前 17 位参与加权求和,最后一位为校验码(取值 0-9 或 大写字母)。\r\n * @param code 税号/统一社会信用代码\r\n * @returns 是否为合法税号\r\n * @example\r\n * isTaxID('91350100M000100Y43') // true/false 取决于校验位\r\n */\r\nexport function isTaxID(code: string) {\r\n const v = String(code ?? '').trim();\r\n if (!/^[0-9A-HJ-NPQRTUWXY]{18}$/.test(v)) return false;\r\n const charset = '0123456789ABCDEFGHJKLMNPQRTUWXY'; // 31 字符集\r\n const weights = [1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28];\r\n const map: Record<string, number> = {};\r\n for (let i = 0; i < charset.length; i++) map[charset[i]] = i;\r\n let sum = 0;\r\n for (let i = 0; i < 17; i++) {\r\n sum += map[v[i]] * weights[i];\r\n }\r\n const logicCheck = (31 - (sum % 31)) % 31;\r\n const expected = charset[logicCheck];\r\n return v[17] === expected;\r\n}\r\n\r\n/**\r\n * HEX 颜色值(支持 `#RGB`、`#RRGGBB`、`#RRGGBBAA`)。\r\n * @param s 颜色字符串\r\n * @returns 是否为合法 HEX 颜色\r\n * @example\r\n * isHexColor('#fff') // true\r\n * isHexColor('#00ff00') // true\r\n * isHexColor('#11223344') // true\r\n */\r\nexport function isHexColor(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(v);\r\n}\r\n\r\n/**\r\n * 校验 URL(要求含协议,支持 http/https/ftp)。\r\n * @param s 待校验的地址\r\n * @returns 是否为合法 URL\r\n * @example\r\n * isURL('https://example.com/path?a=1') // true\r\n * isURL('example.com') // false(缺少协议)\r\n */\r\nexport function isURL(s: string) {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n try {\r\n const u = new URL(v);\r\n return ['http:', 'https:', 'ftp:'].includes(u.protocol) && !!u.hostname;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 判断是否为合法 IPv4 地址。\r\n * @param s IP 字符串\r\n * @returns 是否为合法 IPv4\r\n * @example\r\n * isIPv4('192.168.0.1') // true\r\n * isIPv4('256.0.0.1') // false\r\n */\r\nfunction isIPv4(s: string) {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n const parts = v.split('.');\r\n if (parts.length !== 4) return false;\r\n for (const p of parts) {\r\n if (!/^\\d+$/.test(p)) return false;\r\n if (p.length > 1 && p.startsWith('0')) return false;\r\n const n = Number(p);\r\n if (n < 0 || n > 255) return false;\r\n }\r\n return true;\r\n}\r\n\r\n/**\r\n * 判断是否为合法 IPv6 地址(支持压缩表示与 IPv4 映射)。\r\n * 规则:\r\n * - 由 8 组 1~4 位十六进制数组成,允许一次 `::` 压缩;\r\n * - 允许最后一组使用 IPv4 映射(如 `::ffff:192.168.0.1`)。\r\n * @param s IP 字符串\r\n * @returns 是否为合法 IPv6\r\n * @example\r\n * isIPv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334') // true\r\n * isIPv6('2001:db8::8a2e:370:7334') // true\r\n * isIPv6('2001:::370:7334') // false\r\n */\r\nexport function isIPv6(s: string): boolean {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n\r\n const lastColon = v.lastIndexOf(':');\r\n if (lastColon !== -1 && v.includes('.')) {\r\n const ipv6Part = v.slice(0, lastColon);\r\n const ipv4Part = v.slice(lastColon + 1);\r\n return isIPv6(ipv6Part) && isIPv4(ipv4Part);\r\n }\r\n\r\n const dblColonCount = (v.match(/::/g) || []).length;\r\n if (dblColonCount > 1) return false;\r\n\r\n const segments = v.split(':');\r\n if (v.startsWith('::')) segments.shift();\r\n if (v.endsWith('::')) segments.pop();\r\n const segmentsFiltered = segments.filter((seg) => seg !== '');\r\n\r\n if (dblColonCount === 0 && segmentsFiltered.length !== 8) return false;\r\n if (dblColonCount === 1 && segmentsFiltered.length >= 1 && segmentsFiltered.length <= 7) {\r\n // ok\r\n } else if (dblColonCount === 1 && segments.length === 0) {\r\n // :: 表示全部为 0\r\n return true;\r\n } else if (dblColonCount === 0 && segmentsFiltered.length === 8) {\r\n // ok\r\n } else {\r\n return false;\r\n }\r\n\r\n // 每段 1~4 位十六进制\r\n return segmentsFiltered.every(\r\n (seg) => seg.length >= 1 && seg.length <= 4 && /^[0-9a-fA-F]{1,4}$/.test(seg),\r\n );\r\n}\r\n\r\n/**\r\n * 校验 IP(支持 IPv4 与 IPv6)。\r\n * @param s IP 字符串\r\n * @param version 指定版本:传 `4` 仅校验 IPv4,传 `6` 仅校验 IPv6;缺省同时校验两者\r\n * @returns 是否为合法 IP\r\n * @example\r\n * isIP('127.0.0.1') // true\r\n * isIP('::1') // true\r\n * isIP('127.0.0.1', 6) // false\r\n */\r\nexport function isIP(s: string, version?: 4 | 6 | '4' | '6') {\r\n if (version === 4 || version === '4') return isIPv4(s);\r\n if (version === 6 || version === '6') return isIPv6(s);\r\n return isIPv4(s) || isIPv6(s);\r\n}\r\n\r\n/**\r\n * 校验 CIDR IP 段(支持 IPv4/IPv6),形如 `IP/前缀长度`。\r\n * @param s CIDR 字符串,如 `192.168.0.0/24`、`2001:db8::/32`\r\n * @returns 是否为合法 CIDR\r\n * @example\r\n * isIPRange('10.0.0.0/8') // true\r\n * isIPRange('2001:db8::/129') // false\r\n */\r\nexport function isIPRange(s: string) {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n const parts = v.split('/');\r\n if (parts.length !== 2) return false;\r\n const [ip, prefixStr] = parts;\r\n if (!/^\\d+$/.test(prefixStr)) return false;\r\n const prefix = Number(prefixStr);\r\n if (ip.includes(':')) {\r\n if (!isIPv6(ip)) return false;\r\n return prefix >= 0 && prefix <= 128;\r\n }\r\n if (!isIPv4(ip)) return false;\r\n return prefix >= 0 && prefix <= 32;\r\n}\r\n\r\n/**\r\n * 端口号校验(0 ~ 65535,整数)。\r\n * @param s 端口(字符串或数字)\r\n * @returns 是否为合法端口范围\r\n * @example\r\n * isPortNumber(80) // true\r\n * isPortNumber('65535') // true\r\n * isPortNumber(70000) // false\r\n */\r\nexport function isPortNumber(s: string | number) {\r\n const v = typeof s === 'number' ? s : Number(String(s ?? '').trim());\r\n return Number.isInteger(v) && v >= 0 && v <= 65535;\r\n}\r\n\r\n/**\r\n * 纬度校验(-90 ~ 90)。\r\n * @param s 纬度值(字符串或数字)\r\n * @returns 是否为合法纬度\r\n * @example\r\n * isLatitude('31.2304') // true\r\n */\r\nexport function isLatitude(s: string | number) {\r\n const v = typeof s === 'number' ? s : Number(String(s ?? '').trim());\r\n return Number.isFinite(v) && v >= -90 && v <= 90;\r\n}\r\n\r\n/**\r\n * 经度校验(-180 ~ 180)。\r\n * @param s 经度值(字符串或数字)\r\n * @returns 是否为合法经度\r\n * @example\r\n * isLongitude('121.4737') // true\r\n */\r\nexport function isLongitude(s: string | number) {\r\n const v = typeof s === 'number' ? s : Number(String(s ?? '').trim());\r\n return Number.isFinite(v) && v >= -180 && v <= 180;\r\n}\r\n","import {\r\n appendUrlParam,\r\n cloneDeep,\r\n getObjectValue,\r\n isPlainObject,\r\n pickBy,\r\n toDayjs,\r\n} from '@base-web-kits/base-tools-ts';\r\nimport { getBaseToolsConfig } from '../config';\r\nimport type { AppLogInfo } from '../config';\r\n\r\n/** 请求方法类型 */\r\nexport type RequestMethod =\r\n | 'GET'\r\n | 'POST'\r\n | 'PUT'\r\n | 'DELETE'\r\n | 'CONNECT'\r\n | 'HEAD'\r\n | 'OPTIONS'\r\n | 'TRACE'\r\n | 'PATCH';\r\n\r\n/**\r\n * 请求参数类型\r\n * 包含 fetch 原生支持的 BodyInit 类型,以及支持自动 JSON 序列化的对象和数组\r\n */\r\nexport type RequestData =\r\n | string\r\n | ArrayBuffer\r\n | ArrayBufferView\r\n | Blob\r\n | FormData\r\n | URLSearchParams\r\n | ReadableStream<Uint8Array>\r\n | Record<string, unknown>\r\n | unknown[]\r\n | null;\r\n\r\n/**\r\n * 响应数据类型\r\n */\r\nexport type ResponseData = string | ArrayBuffer | Blob | Record<string, unknown> | unknown[] | null;\r\n\r\n/**\r\n * 发起请求的配置 (对外,参数可选)\r\n */\r\nexport type RequestConfig<D extends RequestData = RequestData> = Partial<RequestConfigBase<D>>;\r\n\r\n/**\r\n * 自定义请求的配置 (接口字段参数必填)\r\n */\r\nexport type RequestConfigBase<D extends RequestData = RequestData> = {\r\n /** 接口地址 */\r\n url: string;\r\n /** 请求方法 */\r\n method?: RequestMethod;\r\n /** 请求头(会自动过滤undefined, null, \"\";不过滤0和false; 数字和布尔值会自动转换为字符串) */\r\n header?: Record<string, string | number | boolean | null | undefined>;\r\n /** 请求参数 */\r\n data?: D;\r\n /** 超时时间 (毫秒), 默认 60000 */\r\n timeout?: number;\r\n\r\n /** 接口返回响应数据的字段, 支持\"a[0].b.c\"的格式, 当配置false时返回完整的响应数据 */\r\n resKey: string | false;\r\n\r\n /** 接口返回响应消息的字段, 支持\"a[0].b.c\"的格式 */\r\n msgKey: string;\r\n\r\n /** 接口返回响应状态码的字段, 支持\"a[0].b.c\"的格式 */\r\n codeKey: string;\r\n\r\n /** 接口返回成功状态码的字段, 支持\"a[0].b.c\"的格式 (默认取 codeKey) */\r\n successKey?: string;\r\n\r\n /** 成功状态码 */\r\n successCode: (number | string)[];\r\n\r\n /** 登录过期状态码 */\r\n reloginCode: (number | string)[];\r\n\r\n /** 是否显示进度条 (默认true) */\r\n showLoading?: boolean;\r\n\r\n /** 是否提示接口异常 (默认true) */\r\n toastError?: boolean;\r\n\r\n /** 是否输出日志 (默认true) */\r\n isLog?: boolean;\r\n\r\n /** 额外输出的日志数据 */\r\n extraLog?: Record<string, unknown>;\r\n\r\n /** 响应数据的缓存时间, 单位毫秒。仅在成功时缓存;仅缓存在内存,应用退出,缓存消失。(默认0,不开启缓存) */\r\n cacheTime?: number;\r\n\r\n /** 是否开启流式传输 (如 SSE) */\r\n enableChunked?: boolean;\r\n\r\n /** 响应类型 (默认 json, enableChunked为true时忽略) */\r\n responseType?: 'text' | 'arraybuffer' | 'json';\r\n\r\n /** 响应拦截 */\r\n responseInterceptor?: (data: ResponseData) => ResponseData;\r\n};\r\n\r\n/**\r\n * 请求任务对象 (用于取消请求或监听流式数据)\r\n */\r\nexport interface RequestTask {\r\n /** 取消请求 */\r\n abort: () => void;\r\n\r\n /** 监听流式数据块接收事件 */\r\n onChunkReceived: (callback: ChunkCallback) => void;\r\n\r\n /** 取消监听流式数据块接收事件 */\r\n offChunkReceived: () => void;\r\n}\r\n\r\n/**\r\n * 流式数据块接收事件回调\r\n */\r\nexport type ChunkCallback = (response: { data: ArrayBuffer }) => void;\r\n\r\n/** 请求缓存 */\r\nconst requestCache = new Map<string, { res: unknown; expire: number }>();\r\n\r\n/**\r\n * 基础请求 (返回 Promise 和 Task 对象)\r\n * 基于 fetch API 封装,支持流式请求\r\n * @param config 请求配置\r\n * @returns Promise<T> & { task?: RequestTask }\r\n * @example\r\n * // 在入口文件完成配置 (确保请求失败有toast提示,登录过期能够触发重新登录,log有日志输出)\r\n * setBaseToolsConfig({\r\n * toast: ({ msg, status }) => (status === 'fail' ? message.error(msg) : message.success(msg)),\r\n * showLoading: () => message.loading('加载中...'),\r\n * hideLoading: () => message.destroy(),\r\n * toLogin: () => reLogin(),\r\n * log(level, data) {\r\n * if (data.name === 'request') {\r\n * sendLog('request', data); // 请求日志\r\n * } else if (level === 'error') {\r\n * sendLog('error', data); // 错误日志\r\n * } else {\r\n * sendLog('action', data); // 操作日志\r\n * }\r\n * },\r\n * });\r\n *\r\n * // 封装项目的基础请求\r\n * export function requestApi<T>(config: RequestConfig) {\r\n * return request<T>({\r\n * header: { token: 'xx', version: 'xx', tid: 'xx' }, // 会自动过滤空值\r\n * // responseInterceptor: (res) => res, // 响应拦截,可预处理响应数据,如解密 (可选)\r\n * resKey: 'data',\r\n * msgKey: 'message',\r\n * codeKey: 'status',\r\n * successCode: [1],\r\n * reloginCode: [-10],\r\n * ...config,\r\n * });\r\n * }\r\n *\r\n * // 1. 基于上面 requestApi 的普通接口\r\n * export function apiGoodList(data: { page: number, size: number }) {\r\n * return requestApi<GoodItem[]>({ url: '/goods/list', data, resKey: 'data.list' });\r\n * }\r\n *\r\n * const goodList = await apiGoodList({ page:1, size:10 });\r\n *\r\n * // 2. 参数泛型的写法\r\n * export function apiGoodList(config: RequestConfig<{ page: number, size: number }>) {\r\n * return requestApi<GoodItem[]>({ url: '/goods/list', resKey: 'data.list', ...config });\r\n * }\r\n *\r\n * const goodList = await apiGoodList({ data: { page:1, size:10 }, showLoading: false });\r\n *\r\n * // 3. 基于上面 requestApi 的流式接口\r\n * export function apiChatStream(data: { question: string }) {\r\n * return requestApi<T>({\r\n * url: '/sse/chatStream',\r\n * data,\r\n * resKey: false,\r\n * showLoading: false,\r\n * responseType: 'arraybuffer',\r\n * enableChunked: true,\r\n * });\r\n * }\r\n *\r\n * const { task } = apiChatStream({question: '你好'}); // 发起流式请求\r\n *\r\n * task.onChunkReceived((res) => {\r\n * console.log('ArrayBuffer', res.data); // 接收流式数据\r\n * });\r\n *\r\n * task.offChunkReceived(); // 取消监听,中断流式接收 (调用时机:流式结束,组件销毁,页面关闭)\r\n * task.abort(); // 取消请求 (若流式传输中,会中断流并抛出异常)\r\n */\r\nexport function request<T, D extends RequestData = RequestData>(config: RequestConfigBase<D>) {\r\n // 1. 初始化控制对象\r\n const controller = new AbortController();\r\n const signal = controller.signal;\r\n let chunkCallback: ChunkCallback | null = null;\r\n\r\n // 构造 Task 对象\r\n const task: RequestTask = {\r\n abort: () => controller.abort(),\r\n onChunkReceived: (cb) => {\r\n chunkCallback = cb;\r\n },\r\n offChunkReceived: () => {\r\n chunkCallback = null;\r\n },\r\n };\r\n\r\n // 2. 创建 Promise\r\n const promise = new Promise<T>((resolve, reject) => {\r\n const execute = async () => {\r\n const {\r\n url,\r\n data,\r\n header,\r\n method = 'GET',\r\n resKey,\r\n msgKey,\r\n codeKey,\r\n successKey,\r\n successCode,\r\n reloginCode,\r\n showLoading = true,\r\n toastError = true,\r\n enableChunked = false,\r\n cacheTime,\r\n responseInterceptor,\r\n responseType = 'json',\r\n timeout = 60000,\r\n } = config;\r\n\r\n const isGet = method === 'GET';\r\n const isObjectData = isPlainObject(data);\r\n const isArrayData = !isObjectData && Array.isArray(data);\r\n\r\n // 2.1 参数处理\r\n // 参数: 过滤undefined, 避免接口处理异常 (不可过滤 null 、 \"\" 、 false 、 0 这些有效值)\r\n const fillData = isObjectData ? pickBy(data, (val) => val !== undefined) : data;\r\n\r\n // 请求头: 过滤空值 (undefined, null, \"\"), 不过滤0和false\r\n const emptyValue: unknown[] = [undefined, null, ''];\r\n const fillHeader = (\r\n header ? pickBy(header, (val) => !emptyValue.includes(val)) : {}\r\n ) as Record<string, string>;\r\n\r\n // 获取 Content-Type (忽略大小写)\r\n const contentTypeKey = Object.keys(fillHeader).find(\r\n (k) => k.toLowerCase() === 'content-type',\r\n );\r\n const contentType = contentTypeKey ? String(fillHeader[contentTypeKey]).toLowerCase() : '';\r\n\r\n if (!isGet && fillData && (isObjectData || isArrayData) && !contentType) {\r\n fillHeader['Content-Type'] = 'application/json';\r\n }\r\n\r\n // 2.2 处理 URL 和 Body\r\n const fillUrl =\r\n isGet && isObjectData ? appendUrlParam(url, fillData as Record<string, unknown>) : url;\r\n\r\n let fillBody: BodyInit | null | undefined;\r\n\r\n if (!isGet && fillData) {\r\n if (isObjectData && contentType.includes('application/x-www-form-urlencoded')) {\r\n // application/x-www-form-urlencoded: 转换为 URLSearchParams\r\n fillBody = toSearchParams(fillData as Record<string, unknown>);\r\n } else if (isObjectData && contentType.includes('multipart/form-data')) {\r\n // multipart/form-data: 转换为 FormData\r\n fillBody = toFormData(fillData as Record<string, unknown>);\r\n // 删除 Content-Type, 让 fetch 自动生成 boundary\r\n if (contentTypeKey) delete fillHeader[contentTypeKey];\r\n } else if (isObjectData || isArrayData) {\r\n fillBody = JSON.stringify(fillData);\r\n } else {\r\n fillBody = fillData as BodyInit;\r\n }\r\n }\r\n\r\n // 2.3 日志与缓存配置\r\n const logConfig = { ...config, data: fillData, header: fillHeader, url: fillUrl };\r\n const startTime = Date.now();\r\n\r\n // 2.4 检查缓存\r\n const isCache = cacheTime && cacheTime > 0;\r\n const cacheKey = isCache ? JSON.stringify({ url: fillUrl, data: fillData }) : '';\r\n\r\n if (isCache) {\r\n const res = checkCache(cacheKey);\r\n if (res) {\r\n logRequestInfo({\r\n status: 'success',\r\n config: logConfig,\r\n fromCache: true,\r\n startTime,\r\n res,\r\n });\r\n resolve(getResult(res, resKey) as T);\r\n return;\r\n }\r\n }\r\n\r\n // 2.5 UI 反馈\r\n const appConfig = getBaseToolsConfig();\r\n if (showLoading) appConfig.showLoading?.();\r\n\r\n // 2.6 设置超时\r\n let isTimeout = false;\r\n const timeoutId = setTimeout(() => {\r\n isTimeout = true;\r\n controller.abort();\r\n }, timeout);\r\n\r\n try {\r\n // 2.7 发起请求\r\n const response = await fetch(fillUrl, {\r\n method,\r\n headers: fillHeader,\r\n body: fillBody,\r\n signal,\r\n });\r\n\r\n if (!response.ok) {\r\n if (showLoading) appConfig.hideLoading?.();\r\n throw new Error(`HTTP Error ${response.status}: ${response.statusText}`);\r\n }\r\n\r\n // 2.8 处理流式响应\r\n if (enableChunked) {\r\n if (showLoading) appConfig.hideLoading?.();\r\n\r\n const res = await handleStreamResponse(response, chunkCallback);\r\n\r\n logRequestInfo({ status: 'success', config: logConfig, startTime, res });\r\n\r\n resolve(res as T);\r\n return;\r\n }\r\n\r\n // 2.9 处理普通响应\r\n const resData = await parseResponse(response, responseType);\r\n\r\n // 隐藏 Loading\r\n if (showLoading) appConfig.hideLoading?.();\r\n\r\n // 响应拦截\r\n const res = responseInterceptor ? responseInterceptor(resData) : resData;\r\n\r\n // 2.10 业务状态码解析\r\n const code = getObjectValue(res, codeKey);\r\n const scode = successKey ? getObjectValue(res, successKey) : code;\r\n const msg = getObjectValue(res, msgKey);\r\n const isSuccess = successCode.includes(scode);\r\n const isRelogin = reloginCode.includes(code);\r\n\r\n logRequestInfo({ status: 'success', config: logConfig, startTime, res });\r\n\r\n // 2.11 结果处理\r\n if (isSuccess) {\r\n // 业务正常\r\n if (isCache) requestCache.set(cacheKey, { res, expire: Date.now() + cacheTime });\r\n resolve(getResult(res, resKey) as T);\r\n } else if (isRelogin) {\r\n // 登录失效\r\n reject(res);\r\n appConfig.toLogin?.(); // 放在后面,确保reject执行后再跳转登录\r\n } else {\r\n // 业务错误\r\n if (toastError && msg) appConfig.toast?.({ status: 'fail', msg });\r\n reject(res);\r\n }\r\n } catch (e) {\r\n const status = 'fail';\r\n const isAbortError = e instanceof DOMException && e.name === 'AbortError'; // 取消请求不视为错误\r\n\r\n if (isAbortError && isTimeout) {\r\n if (toastError) appConfig.toast?.({ status, msg: '请求超时' });\r\n const timeoutError = new Error('Request Timeout');\r\n logRequestInfo({ status, config: logConfig, startTime, e: timeoutError });\r\n reject(timeoutError);\r\n return;\r\n }\r\n\r\n if (!isAbortError && toastError) appConfig.toast?.({ status, msg: '网络请求失败' });\r\n logRequestInfo({ status, config: logConfig, startTime, e });\r\n reject(e);\r\n } finally {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n }\r\n };\r\n\r\n execute();\r\n }) as Promise<T> & { task?: RequestTask };\r\n\r\n // 3. 挂载 Task\r\n promise.task = task;\r\n\r\n return promise;\r\n}\r\n\r\n/**\r\n * 日志输出\r\n */\r\nfunction logRequestInfo(options: {\r\n config: RequestConfigBase<RequestData> & { url?: string };\r\n fromCache?: boolean;\r\n startTime: number;\r\n status: 'success' | 'fail';\r\n res?: unknown;\r\n e?: unknown;\r\n}) {\r\n const { log } = getBaseToolsConfig();\r\n const { isLog = true } = options.config;\r\n\r\n if (!log || !isLog) return;\r\n\r\n const { config, res, fromCache = false, startTime, status, e } = options;\r\n const { url, data, header, method, extraLog } = config;\r\n const endTime = Date.now();\r\n const fmt = 'YYYY-MM-DD HH:mm:ss.SSS';\r\n\r\n const info: AppLogInfo = {\r\n name: 'request',\r\n status,\r\n url,\r\n data,\r\n method,\r\n header,\r\n fromCache,\r\n startTime: toDayjs(startTime).format(fmt),\r\n endTime: toDayjs(endTime).format(fmt),\r\n duration: endTime - startTime,\r\n ...extraLog,\r\n };\r\n\r\n if (status === 'success') {\r\n info.res = cloneDeep(res); // 深拷贝,避免外部修改对象,造成输出不一致\r\n log('info', info);\r\n } else {\r\n info.e = e;\r\n log('error', info);\r\n }\r\n}\r\n\r\n/**\r\n * 获取 resKey 对应的数据\r\n */\r\nfunction getResult(res: unknown, resKey?: RequestConfigBase['resKey']) {\r\n if (!res || !resKey || typeof res !== 'object') return res;\r\n return getObjectValue(res, resKey);\r\n}\r\n\r\n/**\r\n * 检查缓存\r\n */\r\nfunction checkCache(cacheKey: string) {\r\n const cached = requestCache.get(cacheKey);\r\n if (!cached) return null;\r\n if (cached.expire <= Date.now()) {\r\n requestCache.delete(cacheKey);\r\n return null;\r\n }\r\n return cached.res;\r\n}\r\n\r\n/**\r\n * 处理流式响应\r\n */\r\nasync function handleStreamResponse(response: Response, chunkCallback: ChunkCallback | null) {\r\n if (!response.body) throw new Error('Response body is null');\r\n\r\n const reader = response.body.getReader();\r\n\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n if (chunkCallback && value) {\r\n chunkCallback({ data: value.buffer });\r\n }\r\n }\r\n\r\n return 'Stream Finished';\r\n}\r\n\r\n/**\r\n * 解析响应数据\r\n */\r\nasync function parseResponse(response: Response, responseType: string) {\r\n let resData: ResponseData;\r\n if (responseType === 'arraybuffer') {\r\n resData = await response.arrayBuffer();\r\n } else if (responseType === 'text') {\r\n resData = await response.text();\r\n } else {\r\n const text = await response.text();\r\n try {\r\n resData = JSON.parse(text);\r\n } catch {\r\n resData = text;\r\n }\r\n }\r\n return resData;\r\n}\r\n\r\n/**\r\n * 转换为 URLSearchParams\r\n */\r\nfunction toSearchParams(data: Record<string, unknown>) {\r\n const params = new URLSearchParams();\r\n for (const key in data) {\r\n const val = data[key];\r\n // undefined 已在 fillData 阶段过滤,此处仅需判断 null\r\n // null 在 Form 中会被转为字符串 \"null\",通常不符合预期,故过滤\r\n if (val === null) continue;\r\n if (Array.isArray(val)) {\r\n val.forEach((v) => params.append(key, typeof v === 'object' ? JSON.stringify(v) : String(v)));\r\n } else {\r\n params.append(key, typeof val === 'object' ? JSON.stringify(val) : String(val));\r\n }\r\n }\r\n return params;\r\n}\r\n\r\n/**\r\n * 转换为 FormData\r\n */\r\nfunction toFormData(data: Record<string, unknown>) {\r\n const formData = new FormData();\r\n for (const key in data) {\r\n const val = data[key];\r\n // undefined 已在 fillData 阶段过滤,此处仅需判断 null\r\n // null 在 Form 中会被转为字符串 \"null\",通常不符合预期,故过滤\r\n if (val === null) continue;\r\n if (Array.isArray(val)) {\r\n val.forEach((v) =>\r\n formData.append(\r\n key,\r\n v instanceof Blob ? v : typeof v === 'object' ? JSON.stringify(v) : String(v),\r\n ),\r\n );\r\n } else {\r\n formData.append(\r\n key,\r\n val instanceof Blob ? val : typeof val === 'object' ? JSON.stringify(val) : String(val),\r\n );\r\n }\r\n }\r\n return formData;\r\n}\r\n","const WK = {\r\n val: '__l_val',\r\n exp: '__l_exp',\r\n wrap: '__l_wrap',\r\n} as const;\r\n\r\n/**\r\n * 写入 localStorage(自动 JSON 序列化)\r\n * 当 `value` 为 `null` 或 `undefined` 时,会移除该键。\r\n * 支持保存:对象、数组、字符串、数字、布尔值。\r\n * @param key 键名\r\n * @param value 任意可序列化的值\r\n * @param days 过期天数(从当前时间起算)\r\n * @example\r\n * setLocalStorage('user', { id: 1, name: 'Alice' }); // 对象\r\n * setLocalStorage('age', 18); // 数字\r\n * setLocalStorage('vip', true); // 布尔值\r\n * setLocalStorage('token', 'abc123', 7); // 7 天后过期\r\n */\r\nexport function setLocalStorage(key: string, value: unknown, days?: number) {\r\n if (value === undefined || value === null) {\r\n removeLocalStorage(key);\r\n return;\r\n }\r\n\r\n let toStore: unknown = value;\r\n if (typeof days === 'number' && days > 0) {\r\n const ms = days * 24 * 60 * 60 * 1000;\r\n toStore = {\r\n [WK.wrap]: true,\r\n [WK.val]: value,\r\n [WK.exp]: Date.now() + ms,\r\n };\r\n }\r\n\r\n localStorage.setItem(key, JSON.stringify(toStore));\r\n}\r\n\r\n/**\r\n * 读取 localStorage(自动 JSON 反序列化)\r\n * 若值为合法 JSON,则返回反序列化后的数据;\r\n * 若值非 JSON(如外部写入的纯字符串),则原样返回字符串。\r\n * 不存在时返回 `null`。\r\n * @param key 键名\r\n * @returns 解析后的值或 `null`\r\n * @example\r\n * const user = getLocalStorage<{ id: number; name: string }>('user');\r\n * const age = getLocalStorage<number>('age');\r\n * const vip = getLocalStorage<boolean>('vip');\r\n */\r\nexport function getLocalStorage<T = unknown>(key: string): T | null {\r\n const raw = localStorage.getItem(key);\r\n if (raw === null) return null;\r\n try {\r\n const parsed = JSON.parse(raw);\r\n\r\n if (parsed && typeof parsed === 'object' && WK.wrap in parsed && WK.exp in parsed) {\r\n if (Date.now() > parsed[WK.exp]) {\r\n removeLocalStorage(key);\r\n return null;\r\n }\r\n return parsed[WK.val] as T;\r\n }\r\n return parsed as T;\r\n } catch {\r\n return raw as T;\r\n }\r\n}\r\n\r\n/**\r\n * 移除 localStorage 指定键\r\n * @param key 键名\r\n * @example\r\n * removeLocalStorage('token');\r\n */\r\nexport function removeLocalStorage(key: string) {\r\n localStorage.removeItem(key);\r\n}\r\n","/**\r\n * 获取url的查询参数值\r\n * @param key 参数名\r\n * @param url 完整 URL 或仅查询串(如 \"a=1&b=2\")\r\n * @returns 解码后的参数值 (若不存在|\"null\"|\"undefined\",则返回 null)\r\n * @example\r\n * const q = getUrlParam('q'); // 默认当前地址\r\n * const q = getUrlParam('q', 'https://a.com/?q=%E6%B5%8B%E8%AF%95'); // \"测试\"\r\n * const a = getUrlParam('a', 'a=1'); // \"1\"\r\n * const list = getUrlParam('list', 'list=[1,2]'); // \"[1,2]\"\r\n * const list = getUrlParam('list', 'list=null'); // null\r\n * const list = getUrlParam('list', 'list=undefined'); // null\r\n */\r\nexport function getUrlParam(key: string, url = window.location.href) {\r\n const searchParams = new URL(url.includes('?') ? url : `?${url}`, 'http://localhost')\r\n .searchParams;\r\n const value = searchParams.get(key);\r\n return value === 'null' || value === 'undefined' ? null : value;\r\n}\r\n\r\n/**\r\n * 获取url的查询参数值,并转为number类型\r\n * @param key 参数名\r\n * @param url 完整 URL 或仅查询串(如 \"a=1&b=2\")\r\n * @returns 解码后的参数值 (若不存在|\"非数字字符串\",则返回 null)\r\n * @example\r\n * const a = getUrlNumber('a'); // 默认当前地址\r\n * const a = getUrlNumber('a', 'https://a.com/?a=1'); // 1\r\n * const a = getUrlNumber('a', 'a=1'); // 1\r\n * const a = getUrlNumber('a', 'a=1.2'); // 1.2\r\n * const a = getUrlNumber('a', 'a=abc'); // null\r\n */\r\nexport function getUrlNumber(key: string, url = window.location.href) {\r\n const str = getUrlParam(key, url);\r\n if (!str) return null;\r\n\r\n const num = Number(str);\r\n return isNaN(num) ? null : num;\r\n}\r\n\r\n/**\r\n * 获取url的所有查询参数值\r\n * @param url 完整 URL 或仅查询串(如 \"a=1&b=2\")\r\n * @returns 解码后的键值对象(无参数返回空对象; \"null\"|\"undefined\"的参数会被忽略)\r\n * @example\r\n * const params = getUrlParams(); // 默认当前地址\r\n * const params = getUrlParams('a=1&b=2'); // { a: \"1\", b: \"2\" }\r\n * const params = getUrlParams('https://a.com/?a=1&b=2'); // { a: \"1\", b: \"2\" }\r\n * const params = getUrlParams('a=1&b=null'); // { a: \"1\" }\r\n * const params = getUrlParams('a=1&b=undefined'); // { a: \"1\" }\r\n */\r\nexport function getUrlParams(url = window.location.href) {\r\n const searchParams = new URL(url.includes('?') ? url : `?${url}`, 'http://localhost')\r\n .searchParams;\r\n const result: Record<string, string> = {};\r\n\r\n for (const [key, value] of searchParams.entries()) {\r\n if (value !== 'null' && value !== 'undefined') {\r\n result[key] = value;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n"],"mappings":"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","names":["html","src","n","getWatermark","toBase64Url","appConfig","res"]}
|
|
1
|
+
{"version":3,"sources":["../src/web/clipboard/index.ts","../src/web/config/index.ts","../src/web/cookie/index.ts","../src/web/device/index.ts","../src/web/dom/index.ts","../src/web/network/download.ts","../src/ts/index.ts","../src/ts/array/index.ts","../src/ts/async/index.ts","../src/ts/bean/EventBus.ts","../src/ts/day/index.ts","../src/ts/number/big.ts","../src/ts/number/format.ts","../src/ts/number/random.ts","../src/ts/es-toolkit/index.ts","../src/ts/object/index.ts","../src/ts/string/format.ts","../src/ts/string/random.ts","../src/ts/string/other.ts","../src/ts/url/file/index.ts","../src/ts/url/oss/index.ts","../src/ts/url/param/index.ts","../src/ts/url/qn/index.ts","../src/ts/validator/index.ts","../src/web/network/request.ts","../src/web/async/index.ts","../src/web/network/uploadFile.ts","../src/web/storage/index.ts","../src/web/url/index.ts"],"sourcesContent":["/**\r\n * 复制文本到剪贴板(兼容移动端和PC)\r\n * @returns Promise<void> 复制成功时 resolve,失败时 reject。\r\n * @example\r\n * await copyText('hello');\r\n * toast('复制成功');\r\n */\r\nexport async function copyText(text: string): Promise<void> {\r\n if (typeof text !== 'string') text = String(text ?? '');\r\n\r\n // 现代 API\r\n if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {\r\n try {\r\n await navigator.clipboard.writeText(text);\r\n return;\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n } catch (e) {\r\n // 继续尝试回退方案\r\n }\r\n }\r\n\r\n // 回退方案:使用隐藏 textarea + execCommand('copy')\r\n return new Promise<void>((resolve, reject) => {\r\n try {\r\n const textarea = document.createElement('textarea');\r\n textarea.value = text;\r\n\r\n // 避免视觉影响与页面布局影响\r\n textarea.setAttribute('readonly', '');\r\n textarea.style.position = 'fixed';\r\n textarea.style.top = '0';\r\n textarea.style.right = '-9999px';\r\n textarea.style.opacity = '0';\r\n textarea.style.pointerEvents = 'none';\r\n\r\n document.body.appendChild(textarea);\r\n\r\n // 选中文本(移动端兼容)\r\n textarea.focus();\r\n textarea.select();\r\n\r\n // iOS 兼容:明确选区\r\n textarea.setSelectionRange(0, textarea.value.length);\r\n\r\n const ok = document.execCommand('copy');\r\n document.body.removeChild(textarea);\r\n\r\n if (ok) {\r\n resolve();\r\n } else {\r\n reject(new Error('Copy failed: clipboard unavailable'));\r\n }\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n}\r\n\r\n/**\r\n * 复制富文本 HTML 到剪贴板(移动端与 PC)\r\n * 使用场景:图文混排文章、带样式段落,保留格式粘贴。\r\n * @param html HTML字符串\r\n * @example\r\n * await copyHtml('<p><b>加粗</b> 与 <i>斜体</i></p>');\r\n */\r\nexport async function copyHtml(html: string): Promise<void> {\r\n const s = String(html ?? '');\r\n if (canWriteClipboard()) {\r\n const plain = htmlToText(s);\r\n await writeClipboard({\r\n 'text/html': new Blob([s], { type: 'text/html' }),\r\n 'text/plain': new Blob([plain], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n return execCopyFromHtml(s);\r\n}\r\n\r\n/**\r\n * 复制 DOM 节点到剪贴板(移动端与 PC)\r\n * 使用场景:页面已有区域的可视化复制;元素使用 `outerHTML`,非元素使用其文本内容。\r\n * @param node DOM 节点(元素或文本节点)\r\n * @example\r\n * const el = document.querySelector('#article')!;\r\n * await copyNode(el);\r\n */\r\nexport async function copyNode(node: Node): Promise<void> {\r\n if (canWriteClipboard()) {\r\n const { html, text } = nodeToHtmlText(node);\r\n await writeClipboard({\r\n 'text/html': new Blob([html], { type: 'text/html' }),\r\n 'text/plain': new Blob([text], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n const { html } = nodeToHtmlText(node);\r\n return execCopyFromHtml(html);\r\n}\r\n\r\n/**\r\n * 复制单张图片到剪贴板(移动端与 PC,需浏览器支持 `ClipboardItem`)\r\n * 使用场景:把本地 `canvas` 或 `Blob` 生成的图片直接粘贴到聊天/文档。\r\n * @param image 图片源(Blob/Canvas/ImageBitmap)\r\n * @example\r\n * const canvas = document.querySelector('canvas')!;\r\n * await copyImage(canvas);\r\n */\r\nexport async function copyImage(image: Blob | HTMLCanvasElement | ImageBitmap): Promise<void> {\r\n const blob = await toImageBlob(image);\r\n if (!blob) throw new Error('Unsupported image source');\r\n if (canWriteClipboard()) {\r\n const type = blob.type || 'image/png';\r\n await writeClipboard({ [type]: blob });\r\n return;\r\n }\r\n throw new Error('Clipboard image write not supported');\r\n}\r\n\r\n/**\r\n * 复制 URL 到剪贴板(移动端与 PC)\r\n * 写入 `text/uri-list` 与 `text/plain`,在支持 URI 列表的应用中可识别为链接。\r\n * @param url 完整的 URL 字符串\r\n * @example\r\n * await copyUrl('https://example.com/page');\r\n */\r\nexport async function copyUrl(url: string): Promise<void> {\r\n const s = String(url ?? '');\r\n if (canWriteClipboard()) {\r\n await writeClipboard({\r\n 'text/uri-list': new Blob([s], { type: 'text/uri-list' }),\r\n 'text/plain': new Blob([s], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n await copyText(s);\r\n}\r\n\r\n/**\r\n * 复制任意 Blob 到剪贴板(移动端与 PC,需 `ClipboardItem`)\r\n * 使用场景:原生格式粘贴(如 `image/svg+xml`、`application/pdf` 等)。\r\n * @param blob 任意 Blob 数据\r\n * @example\r\n * const svg = new Blob(['<svg></svg>'], { type: 'image/svg+xml' });\r\n * await copyBlob(svg);\r\n */\r\nexport async function copyBlob(blob: Blob): Promise<void> {\r\n if (canWriteClipboard()) {\r\n const type = blob.type || 'application/octet-stream';\r\n await writeClipboard({ [type]: blob });\r\n return;\r\n }\r\n throw new Error('Clipboard blob write not supported');\r\n}\r\n\r\n/**\r\n * 复制 RTF 富文本到剪贴板(移动端与 PC)\r\n * 同时写入 `text/plain`,增强与 Office/富文本编辑器的兼容性。\r\n * @param rtf RTF 字符串(如:`{\\\\rtf1\\\\ansi ...}`)\r\n * @example\r\n * await copyRtf('{\\\\rtf1\\\\ansi Hello \\\\b World}');\r\n */\r\nexport async function copyRtf(rtf: string): Promise<void> {\r\n const s = String(rtf ?? '');\r\n if (canWriteClipboard()) {\r\n const plain = s\r\n .replace(/\\\\par[\\s]?/g, '\\n')\r\n .replace(/\\{[^}]*\\}/g, '')\r\n .replace(/\\\\[a-zA-Z]+[0-9'-]*/g, '')\r\n .replace(/\\r?\\n/g, '\\n')\r\n .trim();\r\n await writeClipboard({\r\n 'text/rtf': new Blob([s], { type: 'text/rtf' }),\r\n 'text/plain': new Blob([plain], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n await copyText(s);\r\n}\r\n\r\n/**\r\n * 复制表格到剪贴板(移动端与 PC)\r\n * 同时写入多种 MIME:`text/html`(表格)、`text/tab-separated-values`(TSV)、`text/csv`、`text/plain`(TSV)。\r\n * 使用场景:优化粘贴到 Excel/Google Sheets/Docs 的体验\r\n * @param rows 二维数组,每行一个数组(字符串/数字)\r\n * @example\r\n * await copyTable([\r\n * ['姓名', '分数'],\r\n * ['张三', 95],\r\n * ['李四', 88],\r\n * ]);\r\n */\r\nexport async function copyTable(rows: Array<Array<string | number>>): Promise<void> {\r\n const data = Array.isArray(rows) ? rows : [];\r\n const escapeHtml = (t: string) =>\r\n t\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n const html = (() => {\r\n const trs = data\r\n .map((r) => `<tr>${r.map((c) => `<td>${escapeHtml(String(c))}</td>`).join('')}</tr>`)\r\n .join('');\r\n return `<table>${trs}</table>`;\r\n })();\r\n const tsv = data.map((r) => r.map((c) => String(c)).join('\\t')).join('\\n');\r\n const csv = data\r\n .map((r) =>\r\n r\r\n .map((c) => {\r\n const s = String(c);\r\n const needQuote = /[\",\\n]/.test(s);\r\n const escaped = s.replace(/\"/g, '\"\"');\r\n return needQuote ? `\"${escaped}\"` : escaped;\r\n })\r\n .join(','),\r\n )\r\n .join('\\n');\r\n if (canWriteClipboard()) {\r\n await writeClipboard({\r\n 'text/html': new Blob([html], { type: 'text/html' }),\r\n 'text/tab-separated-values': new Blob([tsv], { type: 'text/tab-separated-values' }),\r\n 'text/csv': new Blob([csv], { type: 'text/csv' }),\r\n 'text/plain': new Blob([tsv], { type: 'text/plain' }),\r\n });\r\n return;\r\n }\r\n await copyText(tsv);\r\n}\r\n\r\nasync function toImageBlob(image: Blob | HTMLCanvasElement | ImageBitmap) {\r\n if (image instanceof Blob) return image;\r\n if (image instanceof HTMLCanvasElement)\r\n return await new Promise<Blob>((resolve, reject) => {\r\n image.toBlob(\r\n (b) => (b ? resolve(b) : reject(new Error('Canvas toBlob failed'))),\r\n 'image/png',\r\n );\r\n });\r\n const isBitmap = typeof ImageBitmap !== 'undefined' && image instanceof ImageBitmap;\r\n if (isBitmap) {\r\n const cnv = document.createElement('canvas');\r\n cnv.width = (image as ImageBitmap).width;\r\n cnv.height = (image as ImageBitmap).height;\r\n const ctx = cnv.getContext('2d');\r\n ctx?.drawImage(image as ImageBitmap, 0, 0);\r\n return await new Promise<Blob>((resolve, reject) => {\r\n cnv.toBlob((b) => (b ? resolve(b) : reject(new Error('Canvas toBlob failed'))), 'image/png');\r\n });\r\n }\r\n return null;\r\n}\r\n\r\nfunction canWriteClipboard() {\r\n return !!(\r\n navigator.clipboard &&\r\n typeof navigator.clipboard.write === 'function' &&\r\n typeof ClipboardItem !== 'undefined'\r\n );\r\n}\r\n\r\nasync function writeClipboard(items: Record<string, Blob>) {\r\n await navigator.clipboard!.write([new ClipboardItem(items)]);\r\n}\r\n\r\nfunction htmlToText(html: string) {\r\n const div = document.createElement('div');\r\n div.innerHTML = html;\r\n return div.textContent || '';\r\n}\r\n\r\nfunction nodeToHtmlText(node: Node) {\r\n const container = document.createElement('div');\r\n container.appendChild(node.cloneNode(true));\r\n const html =\r\n node instanceof Element ? (node.outerHTML ?? container.innerHTML) : container.innerHTML;\r\n const text = container.textContent || '';\r\n return { html, text };\r\n}\r\n\r\nfunction execCopyFromHtml(html: string) {\r\n return new Promise<void>((resolve, reject) => {\r\n try {\r\n const div = document.createElement('div');\r\n div.contentEditable = 'true';\r\n div.style.position = 'fixed';\r\n div.style.top = '0';\r\n div.style.right = '-9999px';\r\n div.style.opacity = '0';\r\n div.style.pointerEvents = 'none';\r\n div.innerHTML = html;\r\n document.body.appendChild(div);\r\n const selection = window.getSelection();\r\n const range = document.createRange();\r\n range.selectNodeContents(div);\r\n selection?.removeAllRanges();\r\n selection?.addRange(range);\r\n const ok = document.execCommand('copy');\r\n document.body.removeChild(div);\r\n selection?.removeAllRanges();\r\n if (ok) {\r\n resolve();\r\n } else {\r\n reject(new Error('Copy failed: clipboard unavailable'));\r\n }\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n}\r\n","export type AppConfig = {\r\n /** 全局 Toast 提示 */\r\n toast?: (option: { msg: string; status: 'success' | 'fail' }) => void;\r\n /** 显示全局 Loading */\r\n showLoading?: (option?: { title?: string }) => void;\r\n /** 隐藏全局 Loading */\r\n hideLoading?: () => void;\r\n /** 跳转登录页的方法 */\r\n toLogin?: () => void;\r\n /** 日志记录函数 */\r\n log?: (level: 'info' | 'error' | 'warn' | 'debug', data: AppLogInfo) => void;\r\n};\r\n\r\nexport type AppLogInfo = {\r\n /** 调用函数的名称 */\r\n name: string;\r\n\r\n /** 函数的调用状态 */\r\n status?: 'success' | 'fail';\r\n\r\n /** 函数的调用参数 */\r\n option?: unknown;\r\n\r\n /** 函数的调用结果 */\r\n res?: unknown;\r\n\r\n /** 函数的调用错误 */\r\n e?: unknown;\r\n\r\n /** 日志描述 */\r\n desc?: string;\r\n\r\n // 其他自定义属性\r\n [key: string]: unknown;\r\n};\r\n\r\nconst appConfig: AppConfig = {};\r\n\r\n/**\r\n * 获取应用配置\r\n */\r\nexport function getBaseToolsConfig() {\r\n return appConfig;\r\n}\r\n\r\n/**\r\n * 初始化应用配置 (在入口文件设置)\r\n * @example\r\n * setBaseToolsConfig({\r\n * toast: ({ msg, status }) => (status === 'fail' ? message.error(msg) : message.success(msg)),\r\n * showLoading: () => message.loading('加载中...'),\r\n * hideLoading: () => message.destroy(),\r\n * toLogin: () => reLogin(),\r\n * log(level, data) {\r\n * if (data.name === 'request') {\r\n * sendLog('request', data); // 请求日志\r\n * } else if (level === 'error') {\r\n * sendLog('error', data); // 错误日志\r\n * } else {\r\n * sendLog('action', data); // 操作日志\r\n * }\r\n * },\r\n * });\r\n */\r\nexport function setBaseToolsConfig(newConfig: AppConfig) {\r\n Object.assign(appConfig, newConfig);\r\n}\r\n","/**\r\n * 设置 Cookie(路径默认为 `/`)\r\n * @param name Cookie 名称\r\n * @param value Cookie 值(内部已使用 `encodeURIComponent` 编码)\r\n * @param days 过期天数(从当前时间起算)\r\n * @example\r\n * setCookie('token', 'abc', 7);\r\n */\r\nexport function setCookie(name: string, value: string, days: number) {\r\n const date = new Date();\r\n date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);\r\n const expires = `expires=${date.toUTCString()}; path=/`;\r\n document.cookie = `${name}=${encodeURIComponent(value)}; ${expires}`;\r\n}\r\n\r\n/**\r\n * 获取 Cookie\r\n * @param name Cookie 名称\r\n * @returns 若存在返回解码后的值,否则 `null`\r\n * @example\r\n * const token = getCookie('token');\r\n */\r\nexport function getCookie(name: string): string | null {\r\n const value = `; ${document.cookie}`;\r\n const parts = value.split(`; ${name}=`);\r\n if (parts.length === 2) {\r\n const v = parts.pop()?.split(';').shift();\r\n return v ? decodeURIComponent(v) : null;\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * 移除 Cookie(通过设置过期时间为过去)\r\n * 路径固定为 `/`,确保与默认写入路径一致。\r\n * @param name Cookie 名称\r\n * @example\r\n * removeCookie('token');\r\n */\r\nexport function removeCookie(name: string) {\r\n document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;\r\n}\r\n","/**\r\n * 获取用户代理字符串(UA)\r\n * @returns navigator.userAgent.toLowerCase();\r\n */\r\nexport function getUA(): string {\r\n if (typeof navigator === 'undefined') return ''; // SSR无 navigator\r\n return (navigator.userAgent || '').toLowerCase();\r\n}\r\n\r\n/**\r\n * 是否为移动端设备(含平板)\r\n */\r\nexport function isMobile(): boolean {\r\n const ua = getUA();\r\n return /android|webos|iphone|ipod|blackberry|iemobile|opera mini|mobile/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否为平板设备\r\n */\r\nexport function isTablet(): boolean {\r\n const ua = getUA();\r\n return /ipad|android(?!.*mobile)|tablet/i.test(ua) && !/mobile/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否为 PC 设备\r\n */\r\nexport function isPC(): boolean {\r\n return !isMobile() && !isTablet();\r\n}\r\n\r\n/**\r\n * 是否为 iOS 系统\r\n */\r\nexport function isIOS(): boolean {\r\n const ua = getUA();\r\n return /iphone|ipad|ipod/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否为 Android 系统\r\n */\r\nexport function isAndroid(): boolean {\r\n const ua = getUA();\r\n return /android/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否微信内置浏览器\r\n */\r\nexport function isWeChat(): boolean {\r\n const ua = getUA();\r\n return /micromessenger/i.test(ua);\r\n}\r\n\r\n/**\r\n * 是否为 Chrome 浏览器\r\n * 已排除 Edge、Opera 等基于 Chromium 的浏览器\r\n */\r\nexport function isChrome(): boolean {\r\n const ua = getUA();\r\n return /chrome\\//i.test(ua) && !/edg\\//i.test(ua) && !/opr\\//i.test(ua) && !/whale\\//i.test(ua);\r\n}\r\n\r\n/**\r\n * 检测是否支持触摸事件\r\n */\r\nexport function isTouchSupported(): boolean {\r\n if (typeof window === 'undefined') return false;\r\n return 'ontouchstart' in window || navigator.maxTouchPoints > 0;\r\n}\r\n\r\n/**\r\n * 获取设备像素比\r\n */\r\nexport function getDevicePixelRatio(): number {\r\n if (typeof window === 'undefined') return 1;\r\n return window.devicePixelRatio || 1;\r\n}\r\n\r\n/**\r\n * 获取浏览器名字\r\n */\r\nexport function getBrowserName(): string | null {\r\n const ua = getUA();\r\n\r\n if (/chrome\\//i.test(ua)) return 'chrome';\r\n if (/safari\\//i.test(ua)) return 'safari';\r\n if (/firefox\\//i.test(ua)) return 'firefox';\r\n if (/opr\\//i.test(ua)) return 'opera';\r\n if (/edg\\//i.test(ua)) return 'edge';\r\n if (/msie|trident/i.test(ua)) return 'ie';\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * 获取浏览器版本号\r\n */\r\nexport function getBrowserVersion(): string | null {\r\n const ua = getUA();\r\n\r\n const versionPatterns = [\r\n /(?:edg|edge)\\/([0-9.]+)/i,\r\n /(?:opr|opera)\\/([0-9.]+)/i,\r\n /chrome\\/([0-9.]+)/i,\r\n /firefox\\/([0-9.]+)/i,\r\n /version\\/([0-9.]+).*safari/i,\r\n /(?:msie |rv:)([0-9.]+)/i,\r\n ];\r\n\r\n for (const pattern of versionPatterns) {\r\n const matches = ua.match(pattern);\r\n if (matches && matches[1]) {\r\n return matches[1];\r\n }\r\n }\r\n\r\n return null;\r\n}\r\n\r\n/**\r\n * 获取操作系统信息\r\n */\r\nexport function getOS(): string {\r\n const ua = getUA();\r\n\r\n if (/windows/i.test(ua)) return 'windows';\r\n if (/mac os/i.test(ua)) return 'macos';\r\n if (/linux/i.test(ua)) return 'linux';\r\n if (/iphone|ipad|ipod/i.test(ua)) return 'ios';\r\n if (/android/i.test(ua)) return 'android';\r\n\r\n return 'unknown';\r\n}\r\n","/**\r\n * 获取窗口宽度(不含滚动条)\r\n * @returns 窗口宽度\r\n */\r\nexport function getWindowWidth() {\r\n return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;\r\n}\r\n\r\n/**\r\n * 获取窗口高度(不含滚动条)\r\n * @returns 窗口高度\r\n */\r\nexport function getWindowHeight() {\r\n return window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;\r\n}\r\n\r\n/**\r\n * 获取文档垂直滚动位置\r\n * @example\r\n * const top = getWindowScrollTop();\r\n */\r\nexport function getWindowScrollTop() {\r\n const doc = document.documentElement;\r\n const body = document.body;\r\n return window.pageYOffset || doc.scrollTop || body.scrollTop || 0;\r\n}\r\n\r\n/**\r\n * 获取文档水平滚动位置\r\n * @example\r\n * const left = getWindowScrollLeft();\r\n */\r\nexport function getWindowScrollLeft() {\r\n const doc = document.documentElement;\r\n const body = document.body;\r\n return window.pageXOffset || doc.scrollLeft || body.scrollLeft || 0;\r\n}\r\n\r\n/**\r\n * 平滑滚动到指定位置\r\n * @param top 目标纵向滚动位置\r\n * @param behavior 滚动行为,默认 'smooth'\r\n * @example\r\n * windowScrollTo(0);\r\n */\r\nexport function windowScrollTo(top: number, behavior: ScrollBehavior = 'smooth') {\r\n if ('scrollBehavior' in document.documentElement.style) {\r\n window.scrollTo({ top, behavior });\r\n } else {\r\n window.scrollTo(0, top);\r\n }\r\n}\r\n\r\n/**\r\n * 元素是否在视口内(可设置阈值)\r\n * @param el 目标元素\r\n * @param offset 额外判定偏移(像素,正数放宽,负数收紧)\r\n * @returns 是否在视口内\r\n */\r\nexport function isInViewport(el: Element, offset = 0) {\r\n const rect = el.getBoundingClientRect();\r\n const width = getWindowWidth();\r\n const height = getWindowHeight();\r\n return (\r\n rect.bottom >= -offset &&\r\n rect.right >= -offset &&\r\n rect.top <= height + offset &&\r\n rect.left <= width + offset\r\n );\r\n}\r\n\r\n/**\r\n * 锁定页面滚动(移动端/PC)\r\n * 使用 `body{ position: fixed }` 技术消除滚动条抖动,记录并恢复滚动位置。\r\n * @example\r\n * lockBodyScroll();\r\n */\r\nexport function lockBodyScroll() {\r\n const body = document.body;\r\n if (body.dataset.scrollLock === 'true') return;\r\n const y = Math.round(window.scrollY || window.pageYOffset || 0);\r\n body.dataset.scrollLock = 'true';\r\n body.dataset.scrollLockY = String(y);\r\n body.style.position = 'fixed';\r\n body.style.top = `-${y}px`;\r\n body.style.left = '0';\r\n body.style.right = '0';\r\n body.style.width = '100%';\r\n}\r\n\r\n/**\r\n * 解除页面滚动锁定,恢复原始滚动位置\r\n * @example\r\n * unlockBodyScroll();\r\n */\r\nexport function unlockBodyScroll() {\r\n const body = document.body;\r\n if (body.dataset.scrollLock !== 'true') return;\r\n const y = Number(body.dataset.scrollLockY || 0);\r\n body.style.position = '';\r\n body.style.top = '';\r\n body.style.left = '';\r\n body.style.right = '';\r\n body.style.width = '';\r\n delete body.dataset.scrollLock;\r\n delete body.dataset.scrollLockY;\r\n window.scrollTo(0, y);\r\n}\r\n","import type { AxiosResponse } from 'axios';\r\n\r\n/**\r\n * 下载文件\r\n * @param url 完整的下载地址 | base64字符串 | Blob对象\r\n * @param fileName 自定义文件名(需含后缀)\r\n * @example\r\n * download('https://xx/xx.pdf');\r\n * download('https://xx/xx.pdf', 'xx.pdf');\r\n * download(blob, '图片.jpg');\r\n */\r\nexport async function download(url: string | Blob, fileName = '') {\r\n if (!url) return;\r\n\r\n let blobUrl = '';\r\n let needRevoke = false; // createObjectURL必须revoke,否则内存泄露,刷新页面都不释放\r\n try {\r\n if (url instanceof Blob) {\r\n // Blob对象\r\n blobUrl = URL.createObjectURL(url);\r\n needRevoke = true;\r\n } else if (url.includes(';base64,')) {\r\n // base64字符串\r\n blobUrl = url;\r\n } else {\r\n if (fileName) {\r\n // 自定义文件名:跨域的url无法自定义文件名,此处统一转为blob\r\n const res = await fetch(url);\r\n if (!res.ok) throw new Error(`fetch error ${res.status}:${url}`); // 拦截错误页(404/500 等 HTML)\r\n const blob = await res.blob();\r\n blobUrl = URL.createObjectURL(blob);\r\n needRevoke = true;\r\n } else {\r\n // 非自定义文件名的普通链接\r\n blobUrl = url;\r\n }\r\n }\r\n\r\n // window.location.href = fileUrl // 可能会关闭当前页面\r\n // window.open(fileUrl, '_blank') // 不支持下载图片\r\n // 通过a标签模拟点击下载\r\n const a = document.createElement('a');\r\n a.href = blobUrl;\r\n a.download = fileName; // 若为空字符串,则会自动取url的文件名(跨域url无法自定义文件名,需转为blob)\r\n document.body.appendChild(a);\r\n a.click();\r\n document.body.removeChild(a);\r\n } finally {\r\n if (needRevoke) {\r\n setTimeout(() => URL.revokeObjectURL(blobUrl), 100); // Safari 需要延迟 revoke\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 解析Axios返回的Blob数据\r\n * @param res Axios响应对象 (responseType='blob')\r\n * @returns 包含blob数据和文件名的对象 { blob, fileName }\r\n * @example\r\n * const res = await axios.get(url, { responseType: 'blob' });\r\n * const { blob, fileName } = await parseAxiosBlob(res);\r\n * download(blob, fileName);\r\n */\r\nexport async function parseAxiosBlob(res: AxiosResponse<Blob>) {\r\n const { data, headers, status, statusText, config } = res;\r\n\r\n if (status < 200 || status >= 300) throw new Error(`${status},${statusText}:${config.url}`);\r\n\r\n // 抛出json错误\r\n if (data.type.includes('application/json')) {\r\n const txt = await data.text();\r\n throw JSON.parse(txt);\r\n }\r\n\r\n // 解析文件名\r\n const fileName = getDispositionFileName(headers['content-disposition']);\r\n return { blob: data, fileName };\r\n}\r\n\r\n/**\r\n * 获取文件名\r\n * @param disposition content-disposition头值\r\n * @returns content-disposition中的filename\r\n * @example\r\n * const fileName = getDispositionFileName(headers['content-disposition']);\r\n */\r\nexport function getDispositionFileName(disposition?: string) {\r\n if (!disposition) return '';\r\n\r\n // 1. RFC5987 filename* 优先\r\n const rfc5987 = /filename\\*\\s*=\\s*([^']*)''([^;]*)/i.exec(disposition);\r\n if (rfc5987?.[2]) {\r\n try {\r\n return decodeURIComponent(rfc5987[2].trim()).replace(/[\\r\\n]+/g, '');\r\n } catch {\r\n return rfc5987[2].trim().replace(/[\\r\\n]+/g, '');\r\n }\r\n }\r\n\r\n // 2. 旧式 filename=\r\n const old = /filename\\s*=\\s*(?:\"([^\"]*)\"|([^\";]*))(?=;|$)/i.exec(disposition);\r\n if (old) return (old[1] ?? old[2]).trim().replace(/[\\r\\n]+/g, '');\r\n\r\n return '';\r\n}\r\n\r\n/**\r\n * 动态加载 JS(重复执行不会重复加载,内部已排重)\r\n * @param src js 文件路径\r\n * @param attrs 可选的脚本属性,如 async、defer、crossOrigin\r\n * @example\r\n * await loadJs('https://xx/xx.js');\r\n * await loadJs('/a.js', { defer: true });\r\n */\r\nexport async function loadJs(\r\n src: string,\r\n attrs?: Pick<HTMLScriptElement, 'async' | 'defer' | 'crossOrigin'>,\r\n) {\r\n return new Promise<void>((resolve, reject) => {\r\n if (hasJs(src)) return resolve();\r\n\r\n const script = document.createElement('script');\r\n script.type = 'text/javascript';\r\n script.src = src;\r\n\r\n if (attrs) {\r\n const keys = Object.keys(attrs) as Array<keyof typeof attrs>;\r\n keys.forEach((key) => {\r\n const v = attrs[key];\r\n if (v === null || v === undefined || v === false) return;\r\n script.setAttribute(key, typeof v === 'boolean' ? '' : v);\r\n });\r\n }\r\n\r\n script.onload = () => resolve();\r\n script.onerror = (e) => reject(e);\r\n\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * 判断某个 JS 地址是否已在页面中加载过\r\n * @param src 相对、绝对路径的 JS 地址\r\n * @returns 是否已加载过\r\n * @example\r\n * hasJs('https://xx/xx.js'); // boolean\r\n * hasJs('/xx.js'); // boolean\r\n * hasJs('xx.js'); // boolean\r\n */\r\nexport function hasJs(src: string) {\r\n const target = new URL(src, document.baseURI).href;\r\n const jsList = Array.from(document.querySelectorAll('script[src]'));\r\n return jsList.some((e) => {\r\n const src = e.getAttribute('src');\r\n return src && new URL(src, document.baseURI).href === target;\r\n });\r\n}\r\n\r\n/**\r\n * 动态加载 CSS(重复执行不会重复加载,内部已排重)\r\n * @param href css 文件地址\r\n * @param attrs 可选属性,如 crossOrigin、media\r\n * @example\r\n * await loadCss('https://xx/xx.css');\r\n * await loadCss('/a.css', { media: 'print' });\r\n */\r\nexport async function loadCss(\r\n href: string,\r\n attrs?: Pick<HTMLLinkElement, 'crossOrigin' | 'media'>,\r\n) {\r\n return new Promise<void>((resolve, reject) => {\r\n if (hasCss(href)) return resolve();\r\n\r\n const link = document.createElement('link');\r\n link.rel = 'stylesheet';\r\n link.href = href;\r\n\r\n if (attrs) {\r\n const keys = Object.keys(attrs) as Array<keyof typeof attrs>;\r\n keys.forEach((key) => {\r\n const v = attrs[key];\r\n if (v === null || v === undefined) return;\r\n link.setAttribute(key, String(v));\r\n });\r\n }\r\n\r\n link.onload = () => resolve();\r\n link.onerror = (e) => reject(e);\r\n\r\n document.head.appendChild(link);\r\n });\r\n}\r\n\r\n/**\r\n * 判断某个 CSS 地址是否已在页面中加载过\r\n * @param href 相对、绝对路径的 CSS 地址\r\n * @returns 是否已加载过\r\n * @example\r\n * hasCss('https://xx/xx.css'); // boolean\r\n */\r\nexport function hasCss(href: string) {\r\n const target = new URL(href, document.baseURI).href;\r\n const list = Array.from(document.querySelectorAll('link[rel=\"stylesheet\"][href]'));\r\n return list.some((e) => {\r\n const h = e.getAttribute('href');\r\n return h && new URL(h, document.baseURI).href === target;\r\n });\r\n}\r\n\r\n/**\r\n * 预加载图片\r\n * @param src 图片地址\r\n * @returns Promise<HTMLImageElement>\r\n * @example\r\n * await preloadImage('/a.png');\r\n */\r\nexport function preloadImage(src: string) {\r\n return new Promise<HTMLImageElement>((resolve, reject) => {\r\n const img = new Image();\r\n img.onload = () => resolve(img);\r\n img.onerror = (e) => reject(e);\r\n img.src = src;\r\n });\r\n}\r\n","/**\r\n * 内部统一导出, 外部快捷引入: import {xx} from 'base-tools/ts'\r\n */\r\nexport * from './array';\r\nexport * from './async';\r\nexport * from './bean';\r\nexport * from './day';\r\nexport * from './es-toolkit';\r\nexport * from './number';\r\nexport * from './object';\r\nexport * from './string';\r\nexport * from './typing';\r\nexport * from './url';\r\nexport * from './validator';\r\n","/**\r\n * 拖拽排序 (不改变原数组)\r\n * @param list 原始数组\r\n * @param fromIndex 要移动的元素的原始索引\r\n * @param toIndex 要移动到的目标索引\r\n * @returns 移动元素后的新数组\r\n */\r\nexport function arrayMove<T>(list: T[], fromIndex: number, toIndex: number) {\r\n const newList = [...list]; // 创建新数组副本\r\n const [removed] = newList.splice(fromIndex, 1); // 移除 fromIndex 处的元素\r\n newList.splice(toIndex, 0, removed); // 插入到 toIndex 处\r\n return newList;\r\n}\r\n","/**\r\n * 将 Promise 包装为 [data, error] 形式, 减少 try-catch 代码量\r\n * @param p 要包装的 Promise\r\n * @returns 一个 Promise,其结果为 [data, error] 形式\r\n * @example\r\n * const [data, err] = await toAsync(fetch('https://api.example.com/data'));\r\n * if (err) {\r\n * console.error(err);\r\n * return;\r\n * }\r\n * console.log(data);\r\n */\r\nexport async function toAsync<T>(p: Promise<T>): Promise<[T | null, unknown]> {\r\n try {\r\n const data = await p;\r\n return [data, null];\r\n } catch (err) {\r\n return [null, err];\r\n }\r\n}\r\n","import mitt, { type Emitter, type EventType } from 'mitt';\r\n\r\ntype Events = Record<EventType, unknown>;\r\n\r\n/**\r\n * 总线式发布订阅\r\n * @example 创建实例调用 (支持类型约束)\r\n * type T = { a: number; b: string };\r\n * const emitter = new EventBus<{ xx: T; yy: void }>();\r\n * const fn = (arg: T) => {}\r\n * emitter.on('xx', fn);\r\n * emitter.off('xx', fn);\r\n * emitter.emit('xx', { a: 123, b: '123' });\r\n * emitter.emit('yy');\r\n */\r\nexport class EventBus<T extends Events = Events> {\r\n private readonly _emitter: Emitter<T> = mitt<T>();\r\n\r\n /** 订阅 */\r\n on<K extends keyof T>(type: K, fn: (event: T[K]) => void): this {\r\n this._emitter.on(type, fn);\r\n return this;\r\n }\r\n\r\n /** 订阅一次 */\r\n once<K extends keyof T>(type: K, fn: (event: T[K]) => void): this {\r\n const wrap = (event: T[K]) => {\r\n this._emitter.off(type, wrap);\r\n fn(event);\r\n };\r\n this._emitter.on(type, wrap);\r\n return this;\r\n }\r\n\r\n /** 发布 */\r\n emit<K extends keyof T>(type: K, event?: T[K]): this {\r\n this._emitter.emit(type, event as T[K]);\r\n return this;\r\n }\r\n\r\n /** 移除 */\r\n off<K extends keyof T>(type: K, fn?: (event: T[K]) => void): this {\r\n this._emitter.off(type, fn);\r\n return this;\r\n }\r\n\r\n /** 清空 */\r\n clear(): this {\r\n this._emitter.all.clear();\r\n return this;\r\n }\r\n}\r\n\r\n/**\r\n * 全局事件总线\r\n * @example 静态调用 (支持链式)\r\n * EventBus.on('xx', fn); // 订阅事件 xx\r\n * EventBus.once('xx', fn); // 订阅事件 xx 一次\r\n * EventBus.emit('xx', any); // 发布事件 xx,参数任意\r\n * EventBus.off('xx'); // 移除事件 xx 下全部监听\r\n * EventBus.off('xx', fn); // 移除事件 xx 下指定监听\r\n * EventBus.clear(); // 移除所有事件\r\n */\r\nexport default new EventBus();\r\n","import dayjs from 'dayjs';\r\nimport customParseFormat from 'dayjs/plugin/customParseFormat';\r\nimport utc from 'dayjs/plugin/utc';\r\nimport timezone from 'dayjs/plugin/timezone';\r\nimport relativeTime from 'dayjs/plugin/relativeTime';\r\nimport advancedFormat from 'dayjs/plugin/advancedFormat';\r\nimport 'dayjs/locale/zh-cn';\r\nimport { zeroPad } from '../number';\r\n\r\ndayjs.extend(customParseFormat);\r\ndayjs.extend(utc);\r\ndayjs.extend(timezone);\r\ndayjs.extend(relativeTime);\r\ndayjs.extend(advancedFormat);\r\ndayjs.locale('zh-cn');\r\n\r\ntype BaseTime = number | string | Date | dayjs.Dayjs | null | undefined;\r\n\r\n/**\r\n * 创建 dayjs 实例\r\n * 文档: https://day.js.org/zh-CN/\r\n * @param t 各种规范或不规范的时间\r\n * @returns dayjs 实例\r\n * @example\r\n * const d = toDayjs('2021-01-01'); // dayjs 实例 (无参,则默认当前时间)\r\n * d.format('YYYY-MM-DD HH:mm:ss'); // \"2025-12-10 11:33:16\"\r\n * d.valueOf(); // 毫秒时间戳,如 1765337596913\r\n * d.unix(); // 秒时间戳,如 1765337596\r\n * d.millisecond(); // 毫秒 913\r\n * d.second(); // 秒 16\r\n * d.minute(); // 分 33\r\n * d.hour(); // 时 11\r\n * d.date(); // 日 10\r\n * d.day(); // 星期几 5(周日=0)\r\n * d.month() + 1; // 月 12\r\n * d.year(); // 年 2025\r\n * d.startOf('day').valueOf(); // 当日零点\r\n * d.startOf('month').format('YYYY-MM-DD HH:mm:ss'); // 月初 \"2025-12-01 00:00:00\"\r\n * d.endOf('month').format('YYYY-MM-DD HH:mm:ss'); // 月末 \"2025-12-31 23:59:59\"\r\n * d.fromNow(); // “刚刚”、“x分钟前/后”、“x小时前/后”、“x天前/后”、“x月前/后”、“x年前/后”\r\n * d.isSame(t, 'day'); // 是否与t在同一天\r\n * d.diff(); // 与当前时间相差的毫秒数\r\n * d.diff(t); // 与t相差的毫秒数\r\n * d.diff(t, 'second'); // 与t相差的秒数\r\n * d.diff(t, 'minute'); // 与t相差的分钟数\r\n * d.diff(t, 'hour'); // 与t相差的小时数\r\n * d.diff(t, 'day'); // 与t相差的天数\r\n * d.diff(t, 'week'); // 与t相差的周数\r\n * d.diff(t, 'month'); // 与t相差的月数\r\n * d.diff(t, 'quarter'); // 与t相差的季度数\r\n * d.diff(t, 'year'); // 与t相差的年数\r\n */\r\nexport function toDayjs(t?: BaseTime, fmt?: dayjs.OptionType) {\r\n if (t === null || t === undefined) return dayjs();\r\n if (typeof t === 'number') {\r\n const s = String(Math.trunc(t));\r\n return dayjs(s.length === 10 ? t * 1000 : t, fmt);\r\n }\r\n if (typeof t === 'string') {\r\n const s = t.trim();\r\n if (/^\\d{10}$/.test(s)) return dayjs(Number(s) * 1000, fmt);\r\n if (/^\\d{13}$/.test(s)) return dayjs(Number(s), fmt);\r\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(s)) return dayjs(s, fmt || 'YYYY-MM-DD');\r\n if (/^\\d{4}\\/\\d{2}\\/\\d{2}$/.test(s)) return dayjs(s, fmt || 'YYYY/MM/DD');\r\n if (/^\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}$/.test(s))\r\n return dayjs(s, fmt || 'YYYY-MM-DD HH:mm:ss');\r\n if (/^\\d{4}\\/\\d{2}\\/\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}$/.test(s))\r\n return dayjs(s, fmt || 'YYYY/MM/DD HH:mm:ss');\r\n return dayjs(s, fmt);\r\n }\r\n return dayjs(t, fmt);\r\n}\r\n\r\n/**\r\n * 获取“前几天”的日期范围\r\n * @param offset 正整数天数\r\n * @param fmt 日期格式,默认 `YYYY-MM-DD`\r\n * @returns `[start, end]` 日期字符串数组\r\n * @example\r\n * 若今天为 2025-11-19:\r\n * getDateRangeBefore(1) // ['2025-11-18', '2025-11-19']\r\n * getDateRangeBefore(1, 'YYYY-MM-DD HH:mm:ss') // ['2025-11-18 00:00:00', '2025-11-19 23:59:59']\r\n */\r\nexport function getDateRangeBefore(offset: number, fmt = 'YYYY-MM-DD') {\r\n const now = toDayjs(Date.now());\r\n const n = Math.max(0, Math.trunc(offset));\r\n const hasTime = /H|h|m|s|S|A|a|x|X/.test(fmt);\r\n const startDay = now.add(-n, 'day');\r\n const endDay = now;\r\n const start = (hasTime ? startDay.startOf('day') : startDay).format(fmt);\r\n const end = (hasTime ? endDay.endOf('day') : endDay).format(fmt);\r\n return [start, end];\r\n}\r\n\r\n/**\r\n * 获取“后几天”的日期范围\r\n * - 起点:今天;终点:`offset` 天后\r\n * - 若 `fmt` 含时间令牌(如 `HH:mm:ss`),则返回整日范围:起点为当日零点,终点为当日末尾\r\n * @param offset 正整数天数\r\n * @param fmt 日期格式,默认 `YYYY-MM-DD`\r\n * @returns `[start, end]` 日期字符串数组\r\n * @example\r\n * 若今天为 2025-11-19:\r\n * getDateRangeAfter(1) // ['2025-11-19', '2025-11-20']\r\n * getDateRangeAfter(1, 'YYYY-MM-DD HH:mm:ss') // ['2025-11-19 00:00:00', '2025-11-20 23:59:59']\r\n */\r\nexport function getDateRangeAfter(offset: number, fmt = 'YYYY-MM-DD') {\r\n const now = toDayjs(Date.now());\r\n const n = Math.max(0, Math.trunc(offset));\r\n const hasTime = /H|h|m|s|S|A|a|x|X/.test(fmt);\r\n const startDay = now;\r\n const endDay = now.add(n, 'day');\r\n const start = (hasTime ? startDay.startOf('day') : startDay).format(fmt);\r\n const end = (hasTime ? endDay.endOf('day') : endDay).format(fmt);\r\n return [start, end];\r\n}\r\n\r\n/**\r\n * 获取倒计时的时间分解(零填充字符串)\r\n * @param diff 毫秒差值(正数表示剩余时间,负数/0表示已到期)\r\n * @returns 包含天、时、分、秒、毫秒的零填充对象\r\n * @example\r\n * const diff = toDayjs(t).diff(); // 毫秒差值\r\n * const parts = getCountdownParts(diff); // { d: '00', h: '00', m: '00', s: '00', ms: '000' }\r\n */\r\nexport function getCountdownParts(diff: number) {\r\n if (diff <= 0) return { d: '00', h: '00', m: '00', s: '00', ms: '000' };\r\n\r\n const d = Math.floor(diff / (1000 * 60 * 60 * 24));\r\n const h = Math.floor((diff / (1000 * 60 * 60)) % 24);\r\n const m = Math.floor((diff / (1000 * 60)) % 60);\r\n const s = Math.floor((diff / 1000) % 60);\r\n const ms = diff % 1000;\r\n\r\n return {\r\n d: zeroPad(d),\r\n h: zeroPad(h),\r\n m: zeroPad(m),\r\n s: zeroPad(s),\r\n ms: zeroPad(ms, 3),\r\n };\r\n}\r\n\r\n/**\r\n * 通过出生日期计算年龄\r\n * @param birthdate 生日日期,支持多种格式(会被自动解析)\r\n * @returns 年龄对象,包含 `age`(年龄数值)和 `type`(年龄单位,'year' 表示年,'month' 表示月)\r\n * @example\r\n * // 假设当前日期为 2025-11-19\r\n * getAgeByBirthdate('2025-05-10'); // { age: 6, type: 'month' }\r\n * getAgeByBirthdate('2020-11-19'); // { age: 5, type: 'year' }\r\n * getAgeByBirthdate('2020-12-01'); // { age: 4, type: 'year' }(生日还没到, 所以年龄是4岁)\r\n */\r\nexport function getAgeByBirthdate(birthdate: string) {\r\n const birth = toDayjs(birthdate, 'YYYY-MM-DD');\r\n const now = toDayjs(Date.now());\r\n\r\n // 精确的月份计算\r\n const totalMonths = (now.year() - birth.year()) * 12 + (now.month() - birth.month());\r\n\r\n // 如果当前日期小于出生日期,月份减1\r\n const adjustedMonths = now.date() < birth.date() ? totalMonths - 1 : totalMonths;\r\n\r\n if (adjustedMonths >= 12) {\r\n let age = Math.floor(adjustedMonths / 12);\r\n // 检查生日是否已过\r\n const birthdayThisYear = birth.add(age, 'year');\r\n if (now.isBefore(birthdayThisYear)) {\r\n age--;\r\n }\r\n return { age, type: 'year' };\r\n }\r\n\r\n return { age: adjustedMonths, type: 'month' };\r\n}\r\n\r\n/**\r\n * 对外抛出 dayjs 以便全局配置\r\n * @example\r\n * 切换语言\r\n * dayjs.locale('en'); // 切换为英文\r\n * dayjs.locale('zh-cn'); // 切换为中文 (默认)\r\n */\r\nexport { dayjs };\r\n","import BigNumber from 'bignumber.js';\r\n\r\n/**\r\n * 数值入参类型。\r\n * 支持原生 `number`、`string`(包含小数、科学计数法等)以及 `BigNumber`。\r\n */\r\nexport type NumLike = string | number | BigNumber;\r\n\r\n/**\r\n * 将任意 `NumLike` 统一转换为 `BigNumber` 实例。\r\n * @param x 任意支持的数值入参。\r\n * @returns `BigNumber` 实例。\r\n * @example\r\n * big('0.1'); // => BigNumber\r\n * big(0.2); // => BigNumber\r\n */\r\nfunction big(x: NumLike): BigNumber {\r\n return x instanceof BigNumber ? x : new BigNumber(x);\r\n}\r\n\r\n/**\r\n * 高精度加法(支持多个参数连加)。\r\n * @example\r\n * mathPlus(0.1, 0.2); // => 0.3\r\n * mathPlus('0.1', '0.2'); // => 0.3\r\n * mathPlus(1, 2, 3, 4); // => 10 // 多参数连加: 1+2+3+4\r\n */\r\nexport function mathPlus(...rest: NumLike[]) {\r\n let acc = big(rest[0]);\r\n for (const x of rest.slice(1)) acc = acc.plus(big(x));\r\n return acc.toNumber();\r\n}\r\n\r\n/**\r\n * 高精度减法(支持多个参数连减)。\r\n * @example\r\n * mathMinus(1, 0.9); // => 0.1\r\n * mathMinus('1.1', '0.2'); // => 0.9\r\n * mathMinus(10, 1, 2, 3); // => 4 // 多参数连减: 10-1-2-3\r\n */\r\nexport function mathMinus(...rest: NumLike[]) {\r\n let acc = big(rest[0]);\r\n for (const x of rest.slice(1)) acc = acc.minus(big(x));\r\n return acc.toNumber();\r\n}\r\n\r\n/**\r\n * 高精度乘法(支持多个参数连乘)。\r\n * @example\r\n * mathTimes(0.1, 0.2); // => 0.02\r\n * mathTimes('1.5', '3'); // => 4.5\r\n * mathTimes(2, 3, 4); // => 24 // 多参数连乘: 2*3*4\r\n */\r\nexport function mathTimes(...rest: NumLike[]) {\r\n let acc = big(rest[0]);\r\n for (const x of rest.slice(1)) acc = acc.times(big(x));\r\n return acc.toNumber();\r\n}\r\n\r\n/**\r\n * 高精度除法(支持多个参数连除)。\r\n * @example\r\n * mathDiv(1, 3); // => 0.333333...\r\n * mathDiv('10', '4'); // => 2.5\r\n * mathDiv(100, 5, 2); // => 10 // 多参数连除: 100/5/2\r\n */\r\nexport function mathDiv(...rest: NumLike[]) {\r\n let acc = big(rest[0]);\r\n for (const x of rest.slice(1)) acc = acc.div(big(x));\r\n return acc.toNumber();\r\n}\r\n\r\n/**\r\n * 指数运算\r\n * @param x 底数。\r\n * @param y 指数。\r\n * @returns 计算结果。\r\n * @example\r\n * mathPow(2, 3); // => 8\r\n * mathPow('2.5', 2); // => 6.25\r\n */\r\nexport function mathPow(x: NumLike, y: NumLike) {\r\n return big(x).pow(big(y)).toNumber();\r\n}\r\n\r\n/**\r\n * 四舍五入到指定小数位数\r\n * @param x 需要舍入的数值。\r\n * @param dp 保留的小数位数,默认 `0`(取整)。\r\n * @param rm 舍入模式,默认 `ROUND_HALF_UP`(四舍五入)。\r\n * @returns 舍入后的数值。\r\n * @example\r\n * mathRound(1.6); // => 2\r\n * mathRound('1.234', 2); // => 1.23\r\n * mathRound('1.235', 2); // => 1.24\r\n * mathRound('1.299', 2, BigNumber.ROUND_DOWN); // => 1.29\r\n */\r\nexport function mathRound(\r\n x: NumLike,\r\n dp = 0,\r\n rm: BigNumber.RoundingMode = BigNumber.ROUND_HALF_UP,\r\n) {\r\n return big(x).decimalPlaces(dp, rm).toNumber();\r\n}\r\n\r\n/**\r\n * 将数值按指定位数格式化为字符串(保留小数位)。\r\n * @param x 需要格式化的数值。\r\n * @param dp 保留的小数位数,默认 `2`。\r\n * @param rm 舍入模式,默认 `ROUND_HALF_UP`(四舍五入)。\r\n * @returns 格式化后的字符串。\r\n * @example\r\n * mathFixed('1'); // => '1.00'\r\n * +mathFixed('1'); // => 1\r\n * mathFixed(1.2345); // => '1.23'\r\n * mathFixed(1.2345, 3); // => '1.235'\r\n * mathFixed('1.2345', 0, BigNumber.ROUND_UP); // => '2'\r\n */\r\nexport function mathFixed(\r\n x: NumLike,\r\n dp = 2,\r\n rm: BigNumber.RoundingMode = BigNumber.ROUND_HALF_UP,\r\n): string {\r\n return big(x).toFixed(dp, rm);\r\n}\r\n\r\n/**\r\n * 比较两个数值大小。\r\n * @example\r\n * mathCompare('2', '10'); // => -1\r\n * mathCompare(3, 3); // => 0\r\n * mathCompare('10', 2); // => 1\r\n */\r\nexport function mathCompare(a: NumLike, b: NumLike): -1 | 0 | 1 | null {\r\n return big(a).comparedTo(big(b));\r\n}\r\n\r\n/**\r\n * 判断两个数值是否相等。\r\n * @example\r\n * mathEqual('1.0', 1); // => true\r\n * mathEqual(2, 1); // => false\r\n */\r\nexport function mathEqual(a: NumLike, b: NumLike): boolean {\r\n return big(a).isEqualTo(big(b));\r\n}\r\n\r\n/**\r\n * 判断 a 是否大于 b。\r\n * @example\r\n * mathGreaterThan(2, 1); // => true\r\n * mathGreaterThan(1, 2); // => false\r\n */\r\nexport function mathGreaterThan(a: NumLike, b: NumLike): boolean {\r\n return big(a).isGreaterThan(big(b));\r\n}\r\n\r\n/**\r\n * 判断 a 是否大于等于 b。\r\n * @example\r\n * mathGreaterThanOrEqual(2, 2); // => true\r\n * mathGreaterThanOrEqual(1, 2); // => false\r\n */\r\nexport function mathGreaterThanOrEqual(a: NumLike, b: NumLike): boolean {\r\n return big(a).isGreaterThanOrEqualTo(big(b));\r\n}\r\n\r\n/**\r\n * 判断 a 是否小于 b。\r\n * @example\r\n * mathLessThan(1, 2); // => true\r\n * mathLessThan(2, 1); // => false\r\n */\r\nexport function mathLessThan(a: NumLike, b: NumLike): boolean {\r\n return big(a).isLessThan(big(b));\r\n}\r\n\r\n/**\r\n * 判断 a 是否小于等于 b。\r\n * @example\r\n * mathLessThanOrEqual(2, 2); // => true\r\n * mathLessThanOrEqual(1, 2); // => true\r\n * mathLessThanOrEqual(2, 1); // => false\r\n */\r\nexport function mathLessThanOrEqual(a: NumLike, b: NumLike): boolean {\r\n return big(a).isLessThanOrEqualTo(big(b));\r\n}\r\n\r\n/**\r\n * 导出 BigNumber 类\r\n * @example\r\n * BigNumber.ROUND_HALF_UP; // 使用类型\r\n * BigNumber.set(config); // 设置全局配置\r\n * const bn = new BigNumber('123456.789'); // 创建实例\r\n */\r\nexport { BigNumber };\r\n","import { BigNumber } from './big';\r\n\r\n/**\r\n * 开头补零\r\n * @param n 数字\r\n * @param len 总长度,默认 2\r\n * @returns 零填充后的字符串\r\n * @example\r\n * zeroPad(1) // '01'\r\n * zeroPad(12) // '12'\r\n * zeroPad(12, 4) // '0012'\r\n */\r\nexport function zeroPad(n: number | string, len = 2) {\r\n return String(n).padStart(len, '0');\r\n}\r\n\r\n/**\r\n * 给数字添加指定单位 (支持数字字符串,其他非法字符返回原值)\r\n * @param unit 单位\r\n * @param num 数字\r\n * @example\r\n * withUnit(0, 'px') // \"0px\"\r\n * withUnit(1, 'px') // \"1px\"\r\n * withUnit('1', 'rpx') // \"1rpx\"\r\n * withUnit('1', '%') // \"1%\"\r\n * withUnit('auto', 'px') // \"auto\"\r\n * withUnit(null | undefined | '') // \"\"\r\n */\r\nexport function withUnit(num?: number | string, unit = '') {\r\n if (num === null || num === undefined || num === '') return ''; // 注意不能排除0\r\n\r\n if (typeof num === 'number') return `${num}${unit}`;\r\n\r\n const str = String(num).trim();\r\n\r\n if (str === '') return '';\r\n\r\n return isNaN(+str) ? str : `${str}${unit}`;\r\n}\r\n\r\n/**\r\n * 给数字添加px单位 (支持数字字符串,其他非法字符返回原值)\r\n * @example\r\n * withUnitPx(10) // \"10px\"\r\n * withUnitPx('10') // \"10px\"\r\n * withUnitPx('10px') // \"10px\"\r\n * withUnitPx(\"auto\") // \"auto\"\r\n * withUnitPx(\"30%\") // \"30%\"\r\n * withUnitPx(null | undefined | '') // \"\"\r\n * withUnitPx(0) // \"0px\"\r\n */\r\nexport function withUnitPx(num?: string | number) {\r\n return withUnit(num, 'px');\r\n}\r\n\r\n/**\r\n * 给数字添加距离单位:当数值大于等于 1000m 时转换为 km,否则显示 m(最多两位小数、无无意义补零)\r\n * @example\r\n * withDistance(5); // => '5m'\r\n * withDistance(999.456); // => '999.46m'\r\n * withDistance(1000); // => '1km'\r\n * withDistance('1500'); // => '1.5km'\r\n * withDistance('1728'); // => '1.73km'\r\n */\r\nexport function withDistance(m: number | string): string {\r\n const n = Number(m ?? 0);\r\n if (!Number.isFinite(n)) return '0m';\r\n return n >= 1000 ? `${+(n / 1000).toFixed(2)}km` : `${+n.toFixed(2)}m`;\r\n}\r\n\r\n/**\r\n * 数字转千分位字符串(保留小数与符号)。\r\n * @example\r\n * toThousandth(1234567); // => '1,234,567'\r\n * toThousandth('1234567.89'); // => '1,234,567.89'\r\n * toThousandth('-987654'); // => '-987,654'\r\n */\r\nexport function toThousandth(str: number | string): string {\r\n const v = String(str ?? '').trim();\r\n if (v === '') return '';\r\n\r\n // 处理符号\r\n let sign = '';\r\n let num = v;\r\n if (num[0] === '-' || num[0] === '+') {\r\n sign = num[0];\r\n num = num.slice(1);\r\n }\r\n\r\n // 拆分整数与小数部分\r\n const [intPart, decPart] = num.split('.');\r\n\r\n // 仅对整数部分进行千分位分组\r\n const groupedInt = intPart.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',');\r\n\r\n return decPart !== undefined && decPart !== ''\r\n ? `${sign}${groupedInt}.${decPart}`\r\n : `${sign}${groupedInt}`;\r\n}\r\n\r\n/**\r\n * 阿拉伯数字转中文整数(忽略小数;支持负数)。\r\n * @param num 输入的数字或数字字符串(小数部分将被丢弃)\r\n * @returns 中文数字字符串;非法输入返回空字符串\r\n * @example\r\n * toChineseNum(123456); // \"十二万三千四百五十六\"\r\n * toChineseNum(-10008); // \"负一万零八\"\r\n * `第${toChineseNum(123)}条` // \"第一百二十三条\"\r\n */\r\nexport function toChineseNum(num: number | string) {\r\n const numInt = Math.trunc(+num);\r\n if (numInt === 0) return '零';\r\n\r\n const digit = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];\r\n const unit = ['', '十', '百', '千'];\r\n const bigUnit = ['', '万', '亿', '兆'];\r\n\r\n const section4 = (n: number) => {\r\n let str = '',\r\n zeroFlag = false;\r\n for (let i = 0; i < 4; i++) {\r\n const d = n % 10;\r\n n = Math.floor(n / 10);\r\n if (d === 0) {\r\n zeroFlag = true;\r\n continue;\r\n }\r\n if (zeroFlag) str = digit[0] + str;\r\n str = digit[d] + unit[i] + str;\r\n zeroFlag = false;\r\n }\r\n return str;\r\n };\r\n\r\n let res = '';\r\n let sectionIndex = 0;\r\n let n = Math.abs(numInt);\r\n\r\n while (n > 0) {\r\n const seg = n % 10000;\r\n n = Math.floor(n / 10000);\r\n if (seg) {\r\n const segStr = section4(seg);\r\n res = segStr + (sectionIndex ? bigUnit[sectionIndex] : '') + res;\r\n } else if (res && !res.startsWith('零')) {\r\n res = `零${res}`;\r\n }\r\n sectionIndex++;\r\n }\r\n\r\n res = res.replace(/^一十/, '十');\r\n\r\n return numInt < 0 ? `负${res}` : res;\r\n}\r\n\r\n/**\r\n * 金额转中文大写(支持角/分/厘,精度控制)。\r\n * @param amount 金额(支持 number | string),非法或非有限数返回空串\r\n * @param opts 配置项\r\n * @param opts.precision 保留小数位(0~3),对应:0无小数、1角、2角分、3角分厘;默认 2\r\n * @param opts.rm 舍入模式,默认 `BigNumber.ROUND_HALF_UP`(四舍五入)\r\n * @param opts.yuanChar 元单位字符(`'元' | '圆'`),默认 `'元'`\r\n * @returns 中文大写金额字符串;示例:`壹佰贰拾叁元肆角伍分`\r\n * @example\r\n * toChineseCurrency(0) // '零元整'\r\n * toChineseCurrency(10) // '拾元整'\r\n * toChineseCurrency(101) // '壹佰零壹元整'\r\n * toChineseCurrency(1001000) // '壹佰万零壹仟元整'\r\n * toChineseCurrency(1001.01) // '壹仟零壹元壹分'\r\n * toChineseCurrency('1234.5679', { precision: 3 }) // '壹仟贰佰叁拾肆元伍角陆分捌厘'\r\n * toChineseCurrency(-1.2) // '负壹元贰角'\r\n */\r\nexport function toChineseCurrency(\r\n amount: number | string,\r\n opts: {\r\n precision?: 0 | 1 | 2 | 3;\r\n rm?: BigNumber.RoundingMode;\r\n yuanChar?: '元' | '圆';\r\n } = {},\r\n): string {\r\n const dp = opts.precision ?? 2;\r\n const rm = opts.rm ?? BigNumber.ROUND_HALF_UP;\r\n const yuan = opts.yuanChar ?? '元';\r\n\r\n // 1. 非法输入直接返回空串\r\n if (amount === null || amount === undefined) return '';\r\n const bn = new BigNumber(amount);\r\n if (!bn.isFinite()) return '';\r\n\r\n // 2. 转字符串并拆分整数/小数\r\n const s = bn.toFixed(dp, rm);\r\n const sign = s.startsWith('-') ? '负' : '';\r\n const [intStr, decStr = ''] = s.replace(/^-/, '').split('.');\r\n\r\n // 3. 数字与大写映射\r\n const digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];\r\n const unit = ['', '拾', '佰', '仟'];\r\n const bigUnit = ['', '万', '亿', '兆'];\r\n const smallUnit = ['角', '分', '厘'];\r\n\r\n // 4. 四位一段转换整数\r\n const section4 = (n: BigNumber): string => {\r\n let str = '';\r\n let zeroFlag = false;\r\n for (let i = 0; i < 4; i++) {\r\n const d = n.mod(10).toNumber();\r\n n = n.idiv(10);\r\n if (d === 0) {\r\n zeroFlag = true;\r\n continue;\r\n }\r\n if (zeroFlag) str = digit[0] + str;\r\n str = digit[d] + unit[i] + str;\r\n zeroFlag = false;\r\n }\r\n return str.replace(/零+$/g, '');\r\n };\r\n\r\n const intNum = new BigNumber(intStr);\r\n let res = '';\r\n if (intNum.isZero()) {\r\n res = digit[0];\r\n } else {\r\n let n = intNum.abs();\r\n let sectionIndex = 0;\r\n while (n.gt(0)) {\r\n const seg = n.mod(10000);\r\n n = n.idiv(10000);\r\n if (seg.gt(0)) {\r\n const segStr = section4(seg);\r\n const needZero =\r\n res && !res.startsWith(digit[0]) && (seg.lt(1000) || seg.mod(1000).isZero());\r\n const bu = sectionIndex ? bigUnit[sectionIndex] : '';\r\n res = segStr + bu + (needZero ? digit[0] : '') + res;\r\n } else if (res && !res.startsWith(digit[0])) {\r\n res = digit[0] + res;\r\n }\r\n sectionIndex++;\r\n }\r\n res = res.replace(/^壹拾/, '拾');\r\n }\r\n\r\n // 5. 小数部分(角分厘)\r\n let frac = '';\r\n for (let i = 0; i < Math.min(3, dp); i++) {\r\n const ch = decStr[i] || '0';\r\n const d = ch.charCodeAt(0) - 48;\r\n if (d > 0) frac += digit[d] + smallUnit[i];\r\n }\r\n\r\n // 6. 拼接\r\n return frac ? `${sign}${res}${yuan}${frac}` : `${sign}${res}${yuan}整`;\r\n}\r\n","/**\r\n * 随机生成一个布尔值。\r\n * @returns 随机布尔值。\r\n * @example\r\n * randomBoolean(); // => 随机返回 true 或 false\r\n */\r\nexport function randomBoolean(): boolean {\r\n return Math.random() < 0.5;\r\n}\r\n","/**\r\n * re-export 全量 es-toolkit\r\n * 文档: https://es-toolkit.dev/\r\n * 目的: 提供常用工具,收敛依赖版本\r\n */\r\nexport * from 'es-toolkit';\r\n","import { get, set } from 'es-toolkit/compat';\r\n\r\n/**\r\n * 获取对象键名数组(类型安全)。\r\n * 注:内置 `Object.keys` 与 `es-toolkit` 的 `keys` 在 TS 中通常返回 `string[]`,无法精确到 `keyof T`。\r\n * @param obj 目标对象\r\n * @returns 类型精确的 `Array<keyof T>`\r\n * @example\r\n * const o = { a: 1, b: 'x' };\r\n * const keys = getObjectKeys(o); // type: ('a' | 'b')[], value: ['a','b']\r\n */\r\nexport function getObjectKeys<T extends object>(obj: T): Array<keyof T> {\r\n return Object.keys(obj) as (keyof T)[];\r\n}\r\n\r\n/**\r\n * 获取对象值。\r\n * @param obj 目标对象\r\n * @param path 路径。(点路径:'a.b'、'a[0].b';数组路径:['a',0,'b'])\r\n * @returns 值\r\n * @example\r\n * const o = { b: { c: 'x' }, users: [{ name: 'john' }, { name: 'jane' }] };\r\n * const c = getObjectValue(o, 'b.c'); // 点路径: 'x'\r\n * const name0 = getObjectValue(o, 'users[0].name'); // 数组字符: 'john'\r\n * const name1 = getObjectValue(o, ['users', 1, 'name']); // 数组路径: 'jane'\r\n */\r\nexport const getObjectValue = get;\r\n\r\n/**\r\n * 设置对象值。\r\n * @param obj 目标对象\r\n * @param path 路径。(点路径:'a.b'、'a[0].b';数组路径:['a',0,'b'])\r\n * @param value 值\r\n * @example\r\n * const o = { b: { c: 'x' }, users: [{ name: 'john' }, { name: 'jane' }] };\r\n * setObjectValue(o, 'b.c', 'y'); // 点路径\r\n * setObjectValue(o, ['users', 1, 'name'], 'jane-doe'); // 数组路径\r\n */\r\nexport const setObjectValue = set;\r\n","/**\r\n * 文本脱敏\r\n * @param s 原始文本\r\n * @param keepLeft 保留左侧字符数(默认 1)\r\n * @param keepRight 保留右侧字符数(默认 0)\r\n * @param maskChar 脱敏字符(默认 `*`)\r\n * @returns 脱敏文本\r\n * @example\r\n * toMaskText('王小明', 1, 0) // => '王*'\r\n * toMaskText('王小明', 1, 1) // => '王*明'\r\n * toMaskText('13800138000', 3, 4) // => '138****8000'\r\n */\r\nexport function toMaskText(s: string, keepLeft = 1, keepRight = 0, maskChar = '*') {\r\n if (!s) return '';\r\n const v = String(s);\r\n const l = Math.max(0, keepLeft);\r\n const r = Math.max(0, keepRight);\r\n const len = v.length;\r\n const left = Math.min(l, len);\r\n const right = Math.min(r, len - left);\r\n const mid = len - left - right;\r\n if (mid <= 0) return v;\r\n const m = maskChar && maskChar.length > 0 ? maskChar : '*';\r\n return v.slice(0, left) + m.repeat(mid) + v.slice(len - right);\r\n}\r\n\r\n/**\r\n * 手机号中间打星:保留前三位与后四位,中间打 `*`。\r\n * @param phone 手机号字符串\r\n * @returns 遮蔽后的手机号\r\n * @example\r\n * toMaskPhone('13800138000') // => '138****8000'\r\n */\r\nexport function toMaskPhone(phone: string) {\r\n return toMaskText(phone, 3, 4);\r\n}\r\n\r\n/**\r\n * 姓名打星:\r\n * - 长度 ≤ 2:保留首字,其余打 `*`\r\n * - 长度 ≥ 3:保留首尾,各打星中间\r\n * @param name 姓名字符串\r\n * @returns 遮蔽后的姓名\r\n * @example\r\n * toMaskName('张三') // => '张*'\r\n * toMaskName('王小明') // => '王*明'\r\n */\r\nexport function toMaskName(name: string) {\r\n if (!name) return '';\r\n const v = String(name);\r\n return v.length <= 2 ? toMaskText(v, 1, 0) : toMaskText(v, 1, 1);\r\n}\r\n","/**\r\n * 生成UUID\r\n * @returns UUID字符串\r\n * @example\r\n * const uuid = createUUID() // '7982fcfe-5721-4632-bede-6000885be57d'\r\n */\r\nexport function createUUID() {\r\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {\r\n const r = (Math.random() * 16) | 0,\r\n v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return v.toString(16);\r\n });\r\n}\r\n\r\n/**\r\n * 生成随机字符串id\r\n * - 常用于生成元素标签的id (默认加上'id_'前缀, 避免小程序中数字开头的id导致查询节点信息失败)\r\n * @param prefix 前缀, 默认 'id_'\r\n * @returns 随机字符串\r\n * @example\r\n * const id = createViewRandId(); // 'id_0rjuuuqv60xi'\r\n * const id = createViewRandId('canvas_'); // 'canvas_v82a7ctm09q'\r\n */\r\nexport function createViewRandId(prefix = 'id_') {\r\n return `${prefix}${Math.random().toString(36).substring(2, 16)}`;\r\n}\r\n\r\n/**\r\n * 时间+固定位数的随机数字字符串\r\n * @param digits 随机部分的位数,默认 6\r\n * @returns 时间+随机数字字符串\r\n * @example\r\n * const traceId = createTimeRandId(); // '1763002648039123456'\r\n * const traceId = createTimeRandId(8); // '176300264803912345678'\r\n */\r\nexport function createTimeRandId(digits: number = 6) {\r\n const base = 10 ** (digits - 1);\r\n const range = 9 * base;\r\n const int = Math.floor(Math.random() * range) + base;\r\n\r\n return `${Date.now()}${int}`;\r\n}\r\n","/**\r\n * 获取字节长度 (支持字符串、Buffer/Uint8Array、File/Blob 等类型)\r\n * - 字符串按 UTF-8 编码计算字节长度(每个字符 1-4 字节)\r\n * - Buffer/Uint8Array 直接返回字节长度(每个元素 1 字节)\r\n * - File/Blob 返回文件/Blob 大小(字节数)\r\n * @param data 输入的数据\r\n * @returns 数据的字节长度\r\n * @example\r\n * getByteLength('abc') // 3\r\n * getByteLength('中文') // 6\r\n * getByteLength('😊') // 4\r\n * getByteLength(new Uint8Array([0x41, 0x42, 0x43])) // 3\r\n * getByteLength(new File(['abc'], 'test.txt')) // 3\r\n * getByteLength(new Blob(['中文'], { type: 'text/plain' })) // 6\r\n */\r\nexport function getByteLength(data: string | ArrayBuffer | ArrayBufferView | File | Blob): number {\r\n if (typeof data === 'string') {\r\n let byteLen = 0;\r\n\r\n for (let i = 0; i < data.length; i++) {\r\n const code = data.charCodeAt(i);\r\n\r\n if (code <= 0x7f) {\r\n byteLen += 1; // (ASCII 基本拉丁)→ 包含数字 0-9、英文字母 A-Z/a-z、常见符号\r\n } else if (code <= 0x7ff) {\r\n byteLen += 2; // (拉丁扩展)→ 包含拉丁字母(含变音符)、希腊文、俄文/西里尔文、希伯来文、阿拉伯文等\r\n } else if (code >= 0xd800 && code <= 0xdbff) {\r\n byteLen += 4; // (UTF-16 代理项)→ 包含 emoji、稀有汉字(扩展区)、音乐符号等\r\n i++;\r\n } else {\r\n byteLen += 3; // (BMP 绝大部分)→ 包含中文/日文/韩文的大多数字符(CJK 统一汉字)、以及大量其它脚本\r\n }\r\n }\r\n\r\n return byteLen;\r\n }\r\n\r\n // Buffer/Uint8Array\r\n if ('byteLength' in data) return data.byteLength;\r\n\r\n // File/Blob\r\n if ('size' in data) return data.size;\r\n\r\n throw new TypeError('getByteLength: Unsupported type');\r\n}\r\n","import { getObjectKeys } from '../../object';\r\n\r\n/**\r\n * 跨端 100% 原生可解码文件后缀表\r\n * 覆盖:iOS 14+ / Android 5+ WebView、Chrome/Edge/Safari/Firefox\r\n * 剔除:所有需要 polyfill 或转码的容器/编码\r\n */\r\nconst FILE_TYPE = {\r\n img: ['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp'],\r\n video: ['mp4', 'mov', 'm4v'],\r\n voice: ['mp3', 'wav', 'm4a'],\r\n excel: ['csv', 'xls', 'xlsx', 'xlsm', 'ods'],\r\n word: ['txt', 'doc', 'docx', 'pdf', 'md', 'wps'],\r\n zip: ['zip', 'gz', 'tar', 'rar', '7z'],\r\n ppt: ['ppt', 'pptx', 'odp'],\r\n app: ['apk', 'ipa'],\r\n};\r\n\r\n/**\r\n * 获取文件后缀(不含点,返回小写)。\r\n * 当文件名不包含点('.')时,返回空字符串。\r\n * @param fileName 文件名,例如 `avatar.PNG`\r\n * @returns 后缀字符串,例如 `png`\r\n * @example getFileSuffix('avatar.PNG') // 'png'\r\n * @example getFileSuffix('a.tar.gz') // 'gz'\r\n * @example getFileSuffix('.ignore') // ''\r\n * @example getFileSuffix('abc') // ''\r\n */\r\nexport function getFileSuffix(fileName: string) {\r\n if (fileName.startsWith('.')) return ''; // 隐藏文件,返回空串\r\n\r\n const idx = fileName.lastIndexOf('.');\r\n return idx > 0 ? fileName.slice(idx + 1).toLowerCase() : '';\r\n}\r\n\r\n/**\r\n * 根据文件后缀判断文件类型。\r\n * 会将后缀转换为小写后与 `FILE_TYPE` 映射匹配;若未匹配到则返回 `'unknown'`。\r\n * @param fileName 文件名\r\n * @returns 文件类型字符串(如 'img' | 'video' | 'voice' | 'excel' | 'word' | 'zip' | 'ppt' | 'app' | 'unknown')\r\n * @example getFileType('avatar.PNG') // 'img'\r\n * @example getFileType('archive.tar') // 'zip'\r\n * @example getFileType('abc') // 'unknown'\r\n */\r\nexport function getFileType(fileName: string) {\r\n const suffix = getFileSuffix(fileName);\r\n if (!suffix) return 'unknown';\r\n\r\n const keys = getObjectKeys(FILE_TYPE);\r\n for (const key of keys) {\r\n if (FILE_TYPE[key].includes(suffix)) {\r\n return key;\r\n }\r\n }\r\n\r\n return 'unknown';\r\n}\r\n","import type {\r\n OSSOption,\r\n OSSAudioOption,\r\n OSSHlsOption,\r\n OSSImgOption,\r\n OSSVideoOption,\r\n OSSWatermarkOption,\r\n} from './index.d';\r\n\r\nexport * from './index.d';\r\n\r\n/**\r\n * oss图片处理\r\n * 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/img-parameters/?spm=a2c4g.11186623.help-menu-31815.d_0_11_2_1.572824a1a1W5Pf&scm=20140722.H_144582._.OR_help-T_cn~zh-V_1\r\n * @param src 原始图片URL\r\n * @param option 图片处理选项\r\n * @returns 处理后的图片URL(格式: `{src}?x-oss-process=image/xx`)\r\n * @example\r\n * 缩放: getOSSImg('xx.jpg', { resize: { w: 100, h: 100 } })\r\n * 水印: getOSSImg('xx.jpg', { watermark: { text: '水印' } });\r\n * 翻转: getOSSImg('xx.jpg', { flip: 1 });\r\n * 裁剪: getOSSImg('xx.jpg', { crop: { w: 100, h: 100 } });\r\n * 质量: getOSSImg('xx.jpg', { quality: { q: 80 } });\r\n * 格式转换: getOSSImg('xx.jpg', { format: 'jpg' });\r\n * 获取信息: getOSSImg('xx.jpg', { info: true });\r\n * 自适应方向: getOSSImg('xx.jpg', { 'auto-orient': 1 });\r\n * 内切圆: getOSSImg('xx.jpg', { circle: { r: 100 } });\r\n * 索引切割: getOSSImg('xx.jpg', { indexcrop: { x: 100 } });\r\n * 圆角: getOSSImg('xx.jpg', { 'rounded-corners': { r: 10 } });\r\n * 模糊: getOSSImg('xx.jpg', { blur: { r: 10, s: 10 } });\r\n * 旋转: getOSSImg('xx.jpg', { rotate: 90 });\r\n * 渐进显示: getOSSImg('xx.jpg', { interlace: 1 });\r\n * 主色调: getOSSImg('xx.jpg', { 'average-hue': true });\r\n * 亮度: getOSSImg('xx.jpg', { bright: 10 });\r\n * 锐化: getOSSImg('xx.jpg', { sharpen: 100 });\r\n * 对比度: getOSSImg('xx.jpg', { contrast: 100 });\r\n */\r\nexport function getOSSImg(src: string, option: OSSImgOption) {\r\n return buildOSSUrl(src, 'image', option);\r\n}\r\n\r\n/**\r\n * oss视频处理\r\n * 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/audio-and-video-processing/\r\n * @param src 原始视频URL\r\n * @param option 视频处理选项\r\n * @returns 处理后的URL(格式: `{src}?x-oss-process=video/xx`)\r\n * @example\r\n * 视频转码: getOSSVideo('xx.mp4', { convert: { format: 'mp4' } })\r\n * 转为动图: getOSSVideo('xx.mp4', { animation: { format: 'gif' } })\r\n * 雪碧图: getOSSVideo('xx.mp4', { sprite: { format: 'png' } })\r\n * 多帧截取: getOSSVideo('xx.mp4', { snapshots: { count: 3 } })\r\n * 视频拼接: getOSSVideo('xx.mp4', { concat: { list: 'a.mp4,b.mp4' } })\r\n * 信息查询: getOSSVideo('xx.mp4', { info: true })\r\n * 组合操作: getOSSVideo('xx.mp4', { convert: { format: 'mp4' }, snapshots: { count: 3 } })\r\n */\r\nexport function getOSSVideo(src: string, option: OSSVideoOption) {\r\n return buildOSSUrl(src, 'video', option);\r\n}\r\n\r\n/**\r\n * oss音频处理\r\n * 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/audio-and-video-processing/\r\n * @param src 原始音频URL\r\n * @param option 音频处理选项\r\n * @returns 处理后的URL(格式: `{src}?x-oss-process=audio/xx`)\r\n * @example\r\n * 音频转码: getOSSAudio('xx.mp3', { 'convert': { format: 'mp3' } })\r\n * 音频拼接: getOSSAudio('xx.mp3', { 'concat': { list: 'a.mp3,b.mp3' } })\r\n * 信息查询: getOSSAudio('xx.mp3', { 'info': true })\r\n */\r\nexport function getOSSAudio(src: string, option: OSSAudioOption) {\r\n return buildOSSUrl(src, 'audio', option);\r\n}\r\n\r\n/**\r\n * oss直播处理(边转边播 HLS)\r\n * 参考官方文档: https://help.aliyun.com/zh/oss/user-guide/audio-and-video-processing/\r\n * @param src 原始视频URL\r\n * @param option HLS 选项(或布尔)\r\n * @returns 处理后的URL(格式: `{src}?x-oss-process=hls/xx`)\r\n * @example\r\n * 生成播放列表: getOSSHls('xx.mp4', { 'm3u8': true })\r\n * 配置参数: getOSSHls('xx.mp4', { 'm3u8': { playlist: 1, segtime: 6 } })\r\n */\r\nexport function getOSSHls(src: string, option: OSSHlsOption) {\r\n return buildOSSUrl(src, 'hls', option);\r\n}\r\n\r\n/**\r\n * 构造oss处理地址\r\n * @param src 原始地址\r\n * @param type oss处理类型,如`image`, `audio`, `video`, `hls`\r\n * @param option oss处理选项\r\n * @returns 处理后的URL(格式: `{src}?x-oss-process={type}/{segs.join('/')}`)\r\n */\r\nexport function buildOSSUrl(src: string, type: string, option: OSSOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n if (src.includes('.svg')) return src;\r\n\r\n const segs: string[] = [];\r\n\r\n // 遍历选项,构造处理参数\r\n for (const [k, v] of Object.entries(option)) {\r\n const seg = k === 'watermark' ? getWatermark(v as OSSWatermarkOption) : getOSSSegs(k, v);\r\n if (seg) segs.push(seg);\r\n }\r\n\r\n if (!segs.length) return src;\r\n\r\n // 拼接处理参数(先移除查询参数,避免重复拼接)\r\n const base = src.split('?')[0];\r\n return `${base}?x-oss-process=${type}/${segs.join('/')}`;\r\n}\r\n\r\n/**\r\n * 构造图片处理参数\r\n * @param type 图片处理类型,如`resize`, `flip`, `format`, `info`\r\n * @param option 图片处理选项\r\n * @returns `object`返回格式为`resize,w_100,h_100`\r\n * @returns `number`返回格式为`flip,1`\r\n * @returns `string`返回格式为`format,jpg`\r\n * @returns `true`返回格式为`info`, `false`返回空字符串\r\n */\r\nfunction getOSSSegs(type: string, option?: Record<string, unknown> | number | string | boolean) {\r\n if (!option && option !== 0) return '';\r\n\r\n if (option === true) return type;\r\n\r\n if (typeof option === 'number' || typeof option === 'string') return `${type},${option}`;\r\n\r\n const segs = Object.entries(option)\r\n .map(([k, v]) => `${k}_${v}`)\r\n .join(',');\r\n\r\n return segs ? `${type},${segs}` : '';\r\n}\r\n\r\n/**\r\n * 图片水印 (文本和图片已Base64编码)\r\n * @returns 格式: `watermark,text_xxx`\r\n */\r\nfunction getWatermark(w?: OSSWatermarkOption) {\r\n if (!w) return '';\r\n if (w.image) w.image = toBase64Url(w.image);\r\n if (w.text) w.text = toBase64Url(w.text);\r\n if (w.type) w.type = toBase64Url(w.type);\r\n return getOSSSegs('watermark', w);\r\n}\r\n\r\n/**\r\n * Base64编码\r\n */\r\nfunction toBase64Url(s: string) {\r\n let b64 = '';\r\n if (typeof Buffer !== 'undefined') {\r\n const buf = Buffer.from(s, 'utf-8');\r\n b64 = buf.toString('base64');\r\n } else {\r\n try {\r\n b64 = btoa(unescape(encodeURIComponent(s)));\r\n } catch {\r\n b64 = '';\r\n }\r\n }\r\n return b64.replace(/=+$/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\r\n}\r\n","/**\r\n * 将对象参数拼接到 URL\r\n * - 采用纯JS拼接,因为小程序不支持URLSearchParams\r\n * @param url 基础地址\r\n * @param param 将要追加的参数对象;`null/undefined` 值会被忽略,Object 会使用 `JSON.stringify`\r\n * @returns 拼接后的完整 URL(保留原有哈希片段)\r\n * @example\r\n * const url = appendUrlParam('https://a.com', { q: '测试', list: [1, 2], a: null, b: undefined }); // 'https://a.com/?q=%E6%B5%8B%E8%AF%95&list=[1,2]'\r\n */\r\nexport function appendUrlParam(url: string, param: Record<string, unknown>) {\r\n if (!param || typeof param !== 'object') return url;\r\n\r\n const hashIndex = url.indexOf('#');\r\n const baseWithoutHash = hashIndex >= 0 ? url.slice(0, hashIndex) : url;\r\n const hash = hashIndex >= 0 ? url.slice(hashIndex) : '';\r\n\r\n const [base, existingQs] = baseWithoutHash.split('?');\r\n const parts: string[] = [];\r\n if (existingQs) parts.push(existingQs);\r\n for (const key in param) {\r\n const rawVal = param[key];\r\n if (rawVal === null || rawVal === undefined) continue;\r\n const val = typeof rawVal === 'object' ? JSON.stringify(rawVal) : String(rawVal);\r\n parts.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`);\r\n }\r\n const qs = parts.filter(Boolean).join('&');\r\n return base + (qs ? `?${qs}` : '') + hash;\r\n}\r\n","import type { QnImgOption, QnMogr2Option, QnWatermarkOption, QnImageView2Option } from './index.d';\r\n\r\nexport * from './index.d';\r\n\r\n/**\r\n * qn图片处理\r\n * 参考官方文档: https://developer.qiniu.com/kodo/8623/dev-the-picture-style\r\n * @param src 原始图片URL\r\n * @param option 图片处理选项\r\n * @returns 处理后的图片URL(格式: `{src}?imageView2/...|imageMogr2/...|watermark/...|imageslim|imageInfo`)\r\n * @example\r\n * 缩略: getQnImg('xx.jpg', { imageView2: { mode: 2, w: 100, h: 100 } })\r\n * 高级缩放: getQnImg('xx.jpg', { thumbnail: '!50p' })\r\n * 裁剪: getQnImg('xx.jpg', { crop: '100x100' })\r\n * 旋转: getQnImg('xx.jpg', { rotate: 90 })\r\n * 自适应方向: getQnImg('xx.jpg', { 'auto-orient': true })\r\n * 格式转换: getQnImg('xx.jpg', { format: 'webp' })\r\n * 质量: getQnImg('xx.jpg', { q: 80 })\r\n * 渐进显示: getQnImg('xx.jpg', { interlace: 1 })\r\n * 背景色填充: getQnImg('xx.jpg', { background: 'white' })\r\n * 模糊: getQnImg('xx.jpg', { blur: { r: 10, s: 10 } })\r\n * GIF颜色控制: getQnImg('xx.jpg', { colors: 64 })\r\n * 图片瘦身: getQnImg('xx.jpg', { imageslim: true })\r\n * 图片信息: getQnImg('xx.jpg', { imageInfo: true })\r\n * 图片水印: getQnImg('xx.jpg', { watermark: { type: 'image', image: 'http://example.com/logo.png' } })\r\n * 文字水印: getQnImg('xx.jpg', { watermark: { type: 'text', text: '水印', fontsize: 18 } })\r\n */\r\nexport function getQnImg(src: string, option: QnImgOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n if (src.includes('.svg')) return src;\r\n\r\n const segs: string[] = [];\r\n\r\n if (option.imageslim) segs.push('imageslim');\r\n\r\n if (option.imageView2) segs.push(getImageView2(option.imageView2));\r\n\r\n const mogr = getImageMogr2(option.imageMogr2 ?? option);\r\n if (mogr) segs.push(mogr);\r\n\r\n if (option.watermark) segs.push(getWatermark(option.watermark));\r\n\r\n if (option.imageInfo) segs.push('imageInfo');\r\n\r\n if (!segs.length) return src;\r\n\r\n const base = src.split('?')[0];\r\n return `${base}?${segs.join('|')}`;\r\n}\r\n\r\n/**\r\n * qn视频处理\r\n * 参考官方文档: https://developer.qiniu.com/kodo/12654/video-process\r\n * @param src 原始视频URL\r\n * @param option 视频处理选项(支持 avthumb、vframe)\r\n * @returns 处理后的URL(格式: `{src}?avthumb/...|vframe/...`)\r\n * @example\r\n * 视频转码: getQnVideo('xx.mp4', { avthumb: { format: 'mp4', s: '1280x720', vcodec: 'libx264', vb: '1.25m' } })\r\n * 截帧: getQnVideo('xx.mp4', { vframe: { format: 'jpg', offset: 3, w: 480, h: 360 } })\r\n */\r\nexport function getQnVideo(src: string, option: import('.').QnVideoOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n if (src.includes('.svg')) return src;\r\n const segs: string[] = [];\r\n if (option.avthumb) segs.push(getAvthumb(option.avthumb));\r\n if (option.vframe) segs.push(getVframe(option.vframe));\r\n if (!segs.length) return src;\r\n const base = src.split('?')[0];\r\n return `${base}?${segs.join('|')}`;\r\n}\r\n\r\n/**\r\n * qn音频处理\r\n * 参考官方文档: https://developer.qiniu.com/kodo/12654/video-process\r\n * @param src 原始音频URL\r\n * @param option 音频处理选项(通过 avthumb 转码)\r\n * @returns 处理后的URL(格式: `{src}?avthumb/...`)\r\n * @example\r\n * 音频转码: getQnAudio('xx.aac', { avthumb: { format: 'mp3', ab: '128k', ar: 44100, acodec: 'libmp3lame' } })\r\n */\r\nexport function getQnAudio(src: string, option: import('.').QnAudioOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n const segs: string[] = [];\r\n if (option.avthumb) segs.push(getAvthumb(option.avthumb));\r\n if (!segs.length) return src;\r\n const base = src.split('?')[0];\r\n return `${base}?${segs.join('|')}`;\r\n}\r\n\r\n/**\r\n * qn实时转码(HLS 边转边播)\r\n * 参考官方文档: https://developer.qiniu.com/kodo/12654/video-process、实时转码\r\n * @param src 原始视频URL\r\n * @param option HLS 选项\r\n * @returns 处理后的URL(格式: `{src}?avcvt/{level}/format/m3u8/...`)\r\n * @example\r\n * 720P HLS: getQnHls('xx.mp4', { level: 3, format: 'm3u8', segtime: 6 })\r\n */\r\nexport function getQnHls(src: string, option: import('.').QnHlsOption) {\r\n if (!src || !option) return src;\r\n if (src.startsWith('blob:')) return src;\r\n const seg = getAvcvt(option);\r\n if (!seg) return src;\r\n const base = src.split('?')[0];\r\n return `${base}?${seg}`;\r\n}\r\n\r\nfunction getImageView2(opt?: QnImageView2Option) {\r\n if (!opt) return '';\r\n const mode = typeof opt.mode === 'number' ? opt.mode : 0;\r\n const kv: string[] = [];\r\n for (const [k, v] of Object.entries(opt)) {\r\n if (k === 'mode') continue;\r\n if (typeof v === 'boolean') {\r\n if (v) kv.push(`${k}/1`);\r\n } else if (typeof v === 'number' || typeof v === 'string') {\r\n kv.push(`${k}/${v}`);\r\n }\r\n }\r\n return kv.length ? `imageView2/${mode}/${kv.join('/')}` : `imageView2/${mode}`;\r\n}\r\n\r\nfunction getImageMogr2(opt?: QnMogr2Option | QnImgOption) {\r\n if (!opt) return '';\r\n const parts: string[] = [];\r\n\r\n const tn = (opt as QnMogr2Option).thumbnail;\r\n if (typeof tn !== 'undefined') parts.push(`thumbnail/${tn}`);\r\n\r\n const cp = (opt as QnMogr2Option).crop;\r\n if (typeof cp !== 'undefined') parts.push(`crop/${cp}`);\r\n\r\n const rot = (opt as QnMogr2Option).rotate;\r\n if (typeof rot === 'number') parts.push(`rotate/${rot}`);\r\n\r\n const ao = (opt as QnMogr2Option)['auto-orient'];\r\n if (ao) parts.push('auto-orient');\r\n\r\n const fmt = (opt as QnMogr2Option).format;\r\n if (typeof fmt === 'string') parts.push(`format/${fmt}`);\r\n\r\n const il = (opt as QnMogr2Option).interlace;\r\n if (il === 0 || il === 1) parts.push(`interlace/${il}`);\r\n\r\n const bg = (opt as QnMogr2Option).background;\r\n if (typeof bg === 'string') parts.push(`background/${bg}`);\r\n\r\n const q = (opt as QnMogr2Option).q;\r\n if (typeof q === 'number') parts.push(`q/${q}`);\r\n\r\n const blur = (opt as QnMogr2Option).blur;\r\n if (typeof blur !== 'undefined') {\r\n if (typeof blur === 'string') parts.push(`blur/${blur}`);\r\n else parts.push(`blur/${blur.r}x${blur.s}`);\r\n }\r\n\r\n const colors = (opt as QnMogr2Option).colors;\r\n if (typeof colors === 'number') parts.push(`colors/${colors}`);\r\n\r\n return parts.length ? `imageMogr2/${parts.join('/')}` : '';\r\n}\r\n\r\nfunction getWatermark(w?: QnWatermarkOption) {\r\n if (!w) return '';\r\n const mode =\r\n w.type === 'image' ? 1 : w.type === 'text' ? 2 : typeof w.type === 'number' ? w.type : 2;\r\n const segs: string[] = [`watermark/${mode}`];\r\n if (mode === 1 && w.image) segs.push(`image/${toBase64Url(w.image)}`);\r\n if (mode === 2 && w.text) segs.push(`text/${toBase64Url(w.text)}`);\r\n if (w.font) segs.push(`font/${toBase64Url(w.font)}`);\r\n if (typeof w.fontsize === 'number') segs.push(`fontsize/${w.fontsize}`);\r\n if (w.fill) segs.push(`fill/${toBase64Url(w.fill)}`);\r\n if (w.gravity) segs.push(`gravity/${w.gravity}`);\r\n if (typeof w.dx === 'number') segs.push(`dx/${w.dx}`);\r\n if (typeof w.dy === 'number') segs.push(`dy/${w.dy}`);\r\n if (typeof w.dissolve === 'number') segs.push(`dissolve/${w.dissolve}`);\r\n return segs.join('/');\r\n}\r\n\r\nfunction toBase64Url(s: string) {\r\n let b64 = '';\r\n if (typeof Buffer !== 'undefined') {\r\n const buf = Buffer.from(s, 'utf-8');\r\n b64 = buf.toString('base64');\r\n } else {\r\n try {\r\n b64 = btoa(unescape(encodeURIComponent(s)));\r\n } catch {\r\n b64 = '';\r\n }\r\n }\r\n return b64.replace(/=+$/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\r\n}\r\n\r\nfunction getAvthumb(opt: import('.').QnAvthumbOption) {\r\n const parts: string[] = [];\r\n if (opt.format) parts.push(`avthumb/${opt.format}`);\r\n else parts.push('avthumb');\r\n if (opt.s) parts.push(`s/${opt.s}`);\r\n if (opt.vcodec) parts.push(`vcodec/${opt.vcodec}`);\r\n if (typeof opt.vb !== 'undefined') parts.push(`vb/${opt.vb}`);\r\n if (typeof opt.r === 'number') parts.push(`r/${opt.r}`);\r\n if (typeof opt.ab !== 'undefined') parts.push(`ab/${opt.ab}`);\r\n if (typeof opt.ar === 'number') parts.push(`ar/${opt.ar}`);\r\n if (opt.acodec) parts.push(`acodec/${opt.acodec}`);\r\n return parts.join('/');\r\n}\r\n\r\nfunction getVframe(opt: import('.').QnVframeOption) {\r\n const parts: string[] = [];\r\n parts.push(`vframe/${opt.format || 'jpg'}`);\r\n if (typeof opt.offset === 'number') parts.push(`offset/${opt.offset}`);\r\n if (typeof opt.w === 'number') parts.push(`w/${opt.w}`);\r\n if (typeof opt.h === 'number') parts.push(`h/${opt.h}`);\r\n return parts.join('/');\r\n}\r\n\r\nfunction getAvcvt(opt: import('.').QnHlsOption) {\r\n const parts: string[] = [];\r\n const level = typeof opt.level === 'number' ? `/${opt.level}` : '/3';\r\n parts.push(`avcvt${level}`);\r\n parts.push(`format/${opt.format || 'm3u8'}`);\r\n if (typeof opt.segtime === 'number') parts.push(`segtime/${opt.segtime}`);\r\n if (typeof opt.t === 'string') parts.push(`t/${opt.t}`);\r\n if (opt.vcodec) parts.push(`vcodec/${opt.vcodec}`);\r\n if (typeof opt.vb !== 'undefined') parts.push(`vb/${opt.vb}`);\r\n if (typeof opt.r === 'number') parts.push(`r/${opt.r}`);\r\n if (typeof opt.s === 'string') parts.push(`s/${opt.s}`);\r\n if (opt.acodec) parts.push(`acodec/${opt.acodec}`);\r\n if (typeof opt.ab !== 'undefined') parts.push(`ab/${opt.ab}`);\r\n if (typeof opt.ar === 'number') parts.push(`ar/${opt.ar}`);\r\n if (typeof opt.output === 'string') parts.push(`output/${toBase64Url(opt.output)}`);\r\n return parts.join('/');\r\n}\r\n","/**\r\n * 纯字母(不含空格与符号)。\r\n * @param s 字符串\r\n * @returns 是否为字母\r\n * @example\r\n * isLetter('abc') // true\r\n * isLetter('123') // false\r\n * isLetter('abc123') // false\r\n */\r\nexport function isLetter(s: string) {\r\n return /^[a-zA-Z]*$/.test(s);\r\n}\r\n\r\n/**\r\n * 纯中文(不含空格与符号)。\r\n * @param s 字符串\r\n * @returns 是否为纯中文\r\n * @example\r\n * isChinese('你好') // true\r\n */\r\nexport function isChinese(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^[\\u4E00-\\u9FA5]+$/.test(v);\r\n}\r\n\r\n/**\r\n * 纯数字(非负整数,不含空格与符号)。\r\n * @param s 字符串\r\n * @returns 是否为数字\r\n * @example\r\n * isDigits('12') // true\r\n * isDigits('1.2') // false\r\n * isDigits('-12') // false\r\n * isDigits('a12') // false\r\n */\r\nexport function isDigits(s: string) {\r\n return /^[0-9]+$/.test(s);\r\n}\r\n\r\n/**\r\n * 数字字符串格式校验\r\n * @param value 待验证值(字符串或数字)\r\n * @param options 可选项\r\n * @param options.negative 是否允许负数,默认 false\r\n * @param options.decimal 小数位数,默认 2(0 表示必须整数)\r\n * @param options.thousands 是否允许千分位逗号,默认 false\r\n * @param options.leadZero 是否允许整数部分出现多余前导0 (默认false, 即禁止'007',但允许'0.50')\r\n * @example\r\n * isNumeric('123.45'); // true\r\n * isNumeric('123.45', { decimal: 0 }); // false\r\n * isNumeric('-1,234.5', { negative: true, thousands: true }); // true\r\n * isNumeric('0123', { leadZero: true }); // true(现在允许)\r\n * isNumeric('0123'); // false(默认禁止)\r\n * isNumeric('0.50'); // true(始终允许)\r\n * isNumeric('.5'); // false(整数部分不能省)\r\n * isNumeric('123.'); // false(小数部分不能省)\r\n */\r\nexport function isNumeric(\r\n value: string | number,\r\n options?: {\r\n negative?: boolean;\r\n decimal?: number;\r\n thousands?: boolean;\r\n leadZero?: boolean;\r\n },\r\n): boolean {\r\n const { negative = false, decimal = 2, thousands = false, leadZero = false } = options || {};\r\n\r\n if (value === null || value === undefined || value === '') return false;\r\n const str = String(value).trim();\r\n\r\n const sign = negative && str.startsWith('-') ? '-' : '';\r\n const body = sign ? str.slice(1) : str;\r\n\r\n const thousandsPart = thousands ? '(?:[1-9]\\\\d{0,2}(,\\\\d{3})*|0)' : '(?:\\\\d+)';\r\n\r\n const intPart = thousands ? thousandsPart : leadZero ? '(?:\\\\d+)' : '(?:0|[1-9]\\\\d*)';\r\n\r\n const fracPart = decimal === 0 ? '' : `(\\\\.\\\\d{1,${decimal}})`;\r\n\r\n const pattern = `^${intPart}${fracPart}$`;\r\n const reg = new RegExp(pattern);\r\n return reg.test(body);\r\n}\r\n\r\n/**\r\n * 是否为中国大陆手机号(11 位,以 1 开头,第二位 3-9)。\r\n * @param s 待校验的号码\r\n * @returns 是否为合法手机号\r\n * @example\r\n * isMobilePhone('13800138000') // true\r\n * isMobilePhone('12800138000') // false\r\n */\r\nexport function isMobilePhone(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^1[3-9]\\d{9}$/.test(v);\r\n}\r\n\r\n/**\r\n * 是否为中国大陆座机号(区号-号码,可选分机)。\r\n * 格式:`0AA-BBBBBBBB(-EXT)`,其中区号 2-3 位、号码 7-8 位、分机 1-6 位。\r\n * @param s 待校验的号码\r\n * @returns 是否为合法座机号\r\n * @example\r\n * isLandline('010-88888888') // true\r\n * isLandline('0371-12345678-123') // true\r\n */\r\nexport function isLandline(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^0\\d{2,3}-?\\d{7,8}(?:-\\d{1,6})?$/.test(v);\r\n}\r\n\r\n/**\r\n * 联系电话:是否为中国大陆“手机号或座机号”。\r\n * @param s 待校验的号码\r\n * @returns 是否为合法的手机号或座机号\r\n * @example\r\n * isPhone('13800138000') // true\r\n * isPhone('010-88888888') // true\r\n */\r\nexport function isPhone(s: string) {\r\n return isMobilePhone(s) || isLandline(s);\r\n}\r\n\r\n/**\r\n * 校验邮箱地址(基于 RFC 5322 的常用子集)。\r\n * @param s 待校验的邮箱字符串\r\n * @returns 是否为合法邮箱\r\n * @example\r\n * isEmail('user@example.com') // true\r\n * isEmail('invalid@') // false\r\n */\r\nexport function isEmail(s: string) {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n const emailRegex =\r\n /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$/;\r\n return emailRegex.test(v);\r\n}\r\n\r\n/**\r\n * 中文姓名(允许中间点 `·`),长度 2-20。\r\n * @param s 姓名\r\n * @returns 是否为合法中文姓名\r\n * @example\r\n * isChineseName('张三') // true\r\n * isChineseName('阿·娜') // true\r\n */\r\nexport function isChineseName(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^[\\u4E00-\\u9FA5·]{2,20}$/.test(v);\r\n}\r\n\r\n/**\r\n * 身份证校验(支持中国大陆严格校验;台湾/香港/澳门做格式校验)。\r\n * 规则:\r\n * - 中国大陆(严格):18 位校验位 + 出生日期合法性;兼容 15 位旧号(日期校验);\r\n * - 台湾(格式):`^[A-Z][12]\\d{8}$`(首字母 + 性别位 1/2 + 8 位数字);不含校验位算法;\r\n * - 香港(格式):`^[A-Z]{1,2}\\d{6}\\(?[0-9A]\\)?$`(1-2 字母 + 6 位数字 + 校验位,可带括号);\r\n * - 澳门(格式):常见为 `^[157]\\d{6}\\(?\\d\\)?$`(类别位 1/5/7 + 7 位数字 + 校验位,可带括号)。\r\n * @param code 身份证号码\r\n * @returns 是否为合法身份证号\r\n * @example\r\n * isIdentityCard('11010519491231002X') // true(大陆)\r\n * isIdentityCard('A123456789') // true(台湾格式)\r\n * isIdentityCard('A123456(3)') // true(香港格式)\r\n * isIdentityCard('1234567(8)') // true(澳门格式)\r\n */\r\nexport function isIdentityCard(code: string) {\r\n const v = String(code ?? '').trim();\r\n if (v === '') return false;\r\n\r\n const isValidDate = (yyyymmdd: string) => {\r\n const y = Number(yyyymmdd.slice(0, 4));\r\n const m = Number(yyyymmdd.slice(4, 6));\r\n const d = Number(yyyymmdd.slice(6, 8));\r\n if (y < 1900 || y > 2100) return false;\r\n const date = new Date(y, m - 1, d);\r\n return date.getFullYear() === y && date.getMonth() === m - 1 && date.getDate() === d;\r\n };\r\n\r\n // 18位校验\r\n if (/^\\d{17}[\\dXx]$/.test(v)) {\r\n const birth = v.slice(6, 14);\r\n if (!isValidDate(birth)) return false;\r\n const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];\r\n const checkMap = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];\r\n let sum = 0;\r\n for (let i = 0; i < 17; i++) sum += Number(v[i]) * weights[i];\r\n const mod = sum % 11;\r\n const code18 = v[17].toUpperCase();\r\n return checkMap[mod] === code18;\r\n }\r\n\r\n // 15位旧号:仅校验日期(第7~12位),不做校验位\r\n if (/^\\d{15}$/.test(v)) {\r\n const birth = v.slice(6, 12);\r\n const y = Number(`19${birth.slice(0, 2)}`);\r\n const m = Number(birth.slice(2, 4));\r\n const d = Number(birth.slice(4, 6));\r\n const date = new Date(y, m - 1, d);\r\n return date.getFullYear() === y && date.getMonth() === m - 1 && date.getDate() === d;\r\n }\r\n\r\n // 台湾(格式校验)\r\n if (/^[A-Za-z][12]\\d{8}$/.test(v)) return true;\r\n\r\n // 香港(格式校验):支持最后一位带括号与不带括号\r\n if (/^[A-Za-z]{1,2}\\d{6}\\(?[0-9A]\\)?$/.test(v)) return true;\r\n\r\n // 澳门(格式校验):常见 1/5/7 开头 + 7位数字 + 校验位,可带括号\r\n if (/^[157]\\d{6}\\(?\\d\\)?$/.test(v)) return true;\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * 护照号码校验(宽松通用格式,适合单输入框无法确认国家的场景)。\r\n * 说明:各国护照格式差异较大,此函数提供常见模式的格式校验;不做校验位算法。\r\n * 包含:\r\n * - 中国护照常见:`E/G` 开头 + 8 位数字;`D/P/S` 开头 + 7 位数字;\r\n * - 台湾护照常见:首字母 + 8 位数字;\r\n * - 通用兜底:6-9 位的字母数字组合;移除输入中的空格与 `-` 后再校验。\r\n * @param s 护照号码\r\n * @returns 是否匹配常见护照格式\r\n * @example\r\n * isPassport('E12345678') // true\r\n * isPassport('P1234567') // true\r\n * isPassport('A12345678') // true(台湾常见)\r\n * isPassport('AB-1234567') // true(移除分隔符后匹配)\r\n */\r\nexport function isPassport(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim();\r\n if (t === '') return false;\r\n if (/^[EG]\\d{8}$/.test(t)) return true; // CN:E/G + 8 digits\r\n if (/^[DPS]\\d{7}$/.test(t)) return true; // CN:D/P/S + 7 digits\r\n if (/^[A-Za-z]\\d{8}$/.test(t)) return true; // TW:letter + 8 digits\r\n if (/^[A-Za-z0-9]{6,9}$/.test(t)) return true; // 通用宽松兜底\r\n return false;\r\n}\r\n\r\n/**\r\n * 港澳通行证(回乡证)号码校验。\r\n * 说明:常见为 `H/M` 开头 + 8~10 位数字;自动移除输入中的空格与 `-`。\r\n * @param s 证件号\r\n * @returns 是否为港澳通行证格式\r\n * @example\r\n * isHKMOPermit('H12345678') // true\r\n * isHKMOPermit('M1234567890') // true\r\n */\r\nexport function isHKMOPermit(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim()\r\n .toUpperCase();\r\n return /^[HM]\\d{8,10}$/.test(t);\r\n}\r\n\r\n/**\r\n * 台湾居民来往大陆通行证(台胞证)号码校验。\r\n * 说明:常见为 8 位纯数字;或首字母 + 8 位数字;部分场景存在 10 位数字。\r\n * @param s 证件号\r\n * @returns 是否为台胞证格式\r\n * @example\r\n * isTaiwanPermit('12345678') // true\r\n * isTaiwanPermit('T12345678') // true\r\n * isTaiwanPermit('1234567890') // true\r\n */\r\nexport function isTaiwanPermit(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim()\r\n .toUpperCase();\r\n if (/^\\d{8}$/.test(t)) return true;\r\n if (/^[A-Z]\\d{8}$/.test(t)) return true;\r\n if (/^\\d{10}$/.test(t)) return true;\r\n return false;\r\n}\r\n\r\n/**\r\n * 军官证号码校验:字母数字组合,长度 7-18。\r\n * @param s 证件号\r\n * @returns 是否为军官证宽松格式\r\n * @example\r\n * isOfficerIdLoose('JX1234567') // true\r\n */\r\nexport function isOfficerId(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim()\r\n .toUpperCase();\r\n return /^[A-Z0-9]{7,18}$/.test(t);\r\n}\r\n\r\n/**\r\n * 士兵证号码校验:字母数字组合,长度 7-18。\r\n * @param s 证件号\r\n * @returns 是否为士兵证格式\r\n * @example\r\n * isSoldierId('SB12345678') // true\r\n */\r\nexport function isSoldierId(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim()\r\n .toUpperCase();\r\n return /^[A-Z0-9]{7,18}$/.test(t);\r\n}\r\n\r\n/**\r\n * 中国军证(军官证/士兵证)组合校验。\r\n * @param s 证件号\r\n * @returns 是否为军官证或士兵证格式\r\n * @example\r\n * isCnMilitaryId('JX1234567') // true\r\n */\r\nexport function isMilitaryId(s: string) {\r\n return isOfficerId(s) || isSoldierId(s);\r\n}\r\n\r\n/**\r\n * 银行卡号校验(Luhn 校验)。\r\n * 说明:移除空格与 `-` 后进行 Luhn 校验;长度通常为 12-19 位。\r\n * @param s 银行卡号\r\n * @returns 是否通过 Luhn 校验\r\n * @example\r\n * isBankCard('6222 0201 2345 6789') // true\r\n */\r\nexport function isBankCard(s: string) {\r\n const t = String(s ?? '')\r\n .replace(/[-\\s]/g, '')\r\n .trim();\r\n if (!/^\\d{12,19}$/.test(t)) return false;\r\n let sum = 0;\r\n let shouldDouble = false;\r\n for (let i = t.length - 1; i >= 0; i--) {\r\n let digit = Number(t[i]);\r\n if (shouldDouble) {\r\n digit *= 2;\r\n if (digit > 9) digit -= 9;\r\n }\r\n sum += digit;\r\n shouldDouble = !shouldDouble;\r\n }\r\n return sum % 10 === 0;\r\n}\r\n\r\n/**\r\n * 中国车牌号校验(含普通与新能源)。\r\n * @param s 车牌号码\r\n * @returns 是否为合法中国车牌\r\n * @example\r\n * isLicensePlate('京A12345') // true\r\n * isLicensePlate('沪A12345D') // 新能源(末位 D/F)\r\n * isLicensePlate('粤BDF12345') // 新能源(第三位 D/F)\r\n */\r\nexport function isLicensePlate(s: string) {\r\n const v = String(s ?? '')\r\n .trim()\r\n .toUpperCase();\r\n const prov = '京沪津渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵青藏川宁琼粤';\r\n const std = new RegExp(`^[${prov}][A-HJ-NP-Z][A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]$`);\r\n const ne1 = new RegExp(`^[${prov}][A-HJ-NP-Z][DF][A-HJ-NP-Z0-9]{5}$`);\r\n const ne2 = new RegExp(`^[${prov}][A-HJ-NP-Z][A-HJ-NP-Z0-9]{5}[DF]$`);\r\n return std.test(v) || ne1.test(v) || ne2.test(v);\r\n}\r\n\r\n/**\r\n * 校验统一社会信用代码(中国税号常用:18 位,含校验位)。\r\n * 规则:\r\n * - 字符集:数字与大写字母(不含 I/O/Z/S/V),即 `[0-9A-HJ-NPQRTUWXY]`;\r\n * - 前 17 位参与加权求和,最后一位为校验码(取值 0-9 或 大写字母)。\r\n * @param code 税号/统一社会信用代码\r\n * @returns 是否为合法税号\r\n * @example\r\n * isTaxID('91350100M000100Y43') // true/false 取决于校验位\r\n */\r\nexport function isTaxID(code: string) {\r\n const v = String(code ?? '').trim();\r\n if (!/^[0-9A-HJ-NPQRTUWXY]{18}$/.test(v)) return false;\r\n const charset = '0123456789ABCDEFGHJKLMNPQRTUWXY'; // 31 字符集\r\n const weights = [1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28];\r\n const map: Record<string, number> = {};\r\n for (let i = 0; i < charset.length; i++) map[charset[i]] = i;\r\n let sum = 0;\r\n for (let i = 0; i < 17; i++) {\r\n sum += map[v[i]] * weights[i];\r\n }\r\n const logicCheck = (31 - (sum % 31)) % 31;\r\n const expected = charset[logicCheck];\r\n return v[17] === expected;\r\n}\r\n\r\n/**\r\n * HEX 颜色值(支持 `#RGB`、`#RRGGBB`、`#RRGGBBAA`)。\r\n * @param s 颜色字符串\r\n * @returns 是否为合法 HEX 颜色\r\n * @example\r\n * isHexColor('#fff') // true\r\n * isHexColor('#00ff00') // true\r\n * isHexColor('#11223344') // true\r\n */\r\nexport function isHexColor(s: string) {\r\n const v = String(s ?? '').trim();\r\n return /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(v);\r\n}\r\n\r\n/**\r\n * 校验 URL(要求含协议,支持 http/https/ftp)。\r\n * @param s 待校验的地址\r\n * @returns 是否为合法 URL\r\n * @example\r\n * isURL('https://example.com/path?a=1') // true\r\n * isURL('example.com') // false(缺少协议)\r\n */\r\nexport function isURL(s: string) {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n try {\r\n const u = new URL(v);\r\n return ['http:', 'https:', 'ftp:'].includes(u.protocol) && !!u.hostname;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * 判断是否为合法 IPv4 地址。\r\n * @param s IP 字符串\r\n * @returns 是否为合法 IPv4\r\n * @example\r\n * isIPv4('192.168.0.1') // true\r\n * isIPv4('256.0.0.1') // false\r\n */\r\nfunction isIPv4(s: string) {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n const parts = v.split('.');\r\n if (parts.length !== 4) return false;\r\n for (const p of parts) {\r\n if (!/^\\d+$/.test(p)) return false;\r\n if (p.length > 1 && p.startsWith('0')) return false;\r\n const n = Number(p);\r\n if (n < 0 || n > 255) return false;\r\n }\r\n return true;\r\n}\r\n\r\n/**\r\n * 判断是否为合法 IPv6 地址(支持压缩表示与 IPv4 映射)。\r\n * 规则:\r\n * - 由 8 组 1~4 位十六进制数组成,允许一次 `::` 压缩;\r\n * - 允许最后一组使用 IPv4 映射(如 `::ffff:192.168.0.1`)。\r\n * @param s IP 字符串\r\n * @returns 是否为合法 IPv6\r\n * @example\r\n * isIPv6('2001:0db8:85a3:0000:0000:8a2e:0370:7334') // true\r\n * isIPv6('2001:db8::8a2e:370:7334') // true\r\n * isIPv6('2001:::370:7334') // false\r\n */\r\nexport function isIPv6(s: string): boolean {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n\r\n const lastColon = v.lastIndexOf(':');\r\n if (lastColon !== -1 && v.includes('.')) {\r\n const ipv6Part = v.slice(0, lastColon);\r\n const ipv4Part = v.slice(lastColon + 1);\r\n return isIPv6(ipv6Part) && isIPv4(ipv4Part);\r\n }\r\n\r\n const dblColonCount = (v.match(/::/g) || []).length;\r\n if (dblColonCount > 1) return false;\r\n\r\n const segments = v.split(':');\r\n if (v.startsWith('::')) segments.shift();\r\n if (v.endsWith('::')) segments.pop();\r\n const segmentsFiltered = segments.filter((seg) => seg !== '');\r\n\r\n if (dblColonCount === 0 && segmentsFiltered.length !== 8) return false;\r\n if (dblColonCount === 1 && segmentsFiltered.length >= 1 && segmentsFiltered.length <= 7) {\r\n // ok\r\n } else if (dblColonCount === 1 && segments.length === 0) {\r\n // :: 表示全部为 0\r\n return true;\r\n } else if (dblColonCount === 0 && segmentsFiltered.length === 8) {\r\n // ok\r\n } else {\r\n return false;\r\n }\r\n\r\n // 每段 1~4 位十六进制\r\n return segmentsFiltered.every(\r\n (seg) => seg.length >= 1 && seg.length <= 4 && /^[0-9a-fA-F]{1,4}$/.test(seg),\r\n );\r\n}\r\n\r\n/**\r\n * 校验 IP(支持 IPv4 与 IPv6)。\r\n * @param s IP 字符串\r\n * @param version 指定版本:传 `4` 仅校验 IPv4,传 `6` 仅校验 IPv6;缺省同时校验两者\r\n * @returns 是否为合法 IP\r\n * @example\r\n * isIP('127.0.0.1') // true\r\n * isIP('::1') // true\r\n * isIP('127.0.0.1', 6) // false\r\n */\r\nexport function isIP(s: string, version?: 4 | 6 | '4' | '6') {\r\n if (version === 4 || version === '4') return isIPv4(s);\r\n if (version === 6 || version === '6') return isIPv6(s);\r\n return isIPv4(s) || isIPv6(s);\r\n}\r\n\r\n/**\r\n * 校验 CIDR IP 段(支持 IPv4/IPv6),形如 `IP/前缀长度`。\r\n * @param s CIDR 字符串,如 `192.168.0.0/24`、`2001:db8::/32`\r\n * @returns 是否为合法 CIDR\r\n * @example\r\n * isIPRange('10.0.0.0/8') // true\r\n * isIPRange('2001:db8::/129') // false\r\n */\r\nexport function isIPRange(s: string) {\r\n const v = String(s ?? '').trim();\r\n if (v === '') return false;\r\n const parts = v.split('/');\r\n if (parts.length !== 2) return false;\r\n const [ip, prefixStr] = parts;\r\n if (!/^\\d+$/.test(prefixStr)) return false;\r\n const prefix = Number(prefixStr);\r\n if (ip.includes(':')) {\r\n if (!isIPv6(ip)) return false;\r\n return prefix >= 0 && prefix <= 128;\r\n }\r\n if (!isIPv4(ip)) return false;\r\n return prefix >= 0 && prefix <= 32;\r\n}\r\n\r\n/**\r\n * 端口号校验(0 ~ 65535,整数)。\r\n * @param s 端口(字符串或数字)\r\n * @returns 是否为合法端口范围\r\n * @example\r\n * isPortNumber(80) // true\r\n * isPortNumber('65535') // true\r\n * isPortNumber(70000) // false\r\n */\r\nexport function isPortNumber(s: string | number) {\r\n const v = typeof s === 'number' ? s : Number(String(s ?? '').trim());\r\n return Number.isInteger(v) && v >= 0 && v <= 65535;\r\n}\r\n\r\n/**\r\n * 纬度校验(-90 ~ 90)。\r\n * @param s 纬度值(字符串或数字)\r\n * @returns 是否为合法纬度\r\n * @example\r\n * isLatitude('31.2304') // true\r\n */\r\nexport function isLatitude(s: string | number) {\r\n const v = typeof s === 'number' ? s : Number(String(s ?? '').trim());\r\n return Number.isFinite(v) && v >= -90 && v <= 90;\r\n}\r\n\r\n/**\r\n * 经度校验(-180 ~ 180)。\r\n * @param s 经度值(字符串或数字)\r\n * @returns 是否为合法经度\r\n * @example\r\n * isLongitude('121.4737') // true\r\n */\r\nexport function isLongitude(s: string | number) {\r\n const v = typeof s === 'number' ? s : Number(String(s ?? '').trim());\r\n return Number.isFinite(v) && v >= -180 && v <= 180;\r\n}\r\n","import {\r\n appendUrlParam,\r\n cloneDeep,\r\n getObjectValue,\r\n isPlainObject,\r\n toDayjs,\r\n} from '@base-web-kits/base-tools-ts';\r\nimport { getBaseToolsConfig } from '../config';\r\nimport type { AppLogInfo } from '../config';\r\n\r\n/** 请求方法类型 */\r\nexport type RequestMethod =\r\n | 'GET'\r\n | 'POST'\r\n | 'PUT'\r\n | 'DELETE'\r\n | 'CONNECT'\r\n | 'HEAD'\r\n | 'OPTIONS'\r\n | 'TRACE'\r\n | 'PATCH';\r\n\r\n/**\r\n * 请求参数类型\r\n * 包含 fetch 原生支持的 BodyInit 类型,以及支持自动 JSON 序列化的对象和数组\r\n */\r\nexport type RequestData =\r\n | string\r\n | ArrayBuffer\r\n | ArrayBufferView\r\n | Blob\r\n | FormData\r\n | URLSearchParams\r\n | ReadableStream<Uint8Array>\r\n | Record<string, unknown>\r\n | unknown[]\r\n | null;\r\n\r\n/**\r\n * 响应数据类型\r\n */\r\nexport type ResponseData = string | ArrayBuffer | Blob | Record<string, unknown> | unknown[] | null;\r\n\r\n/**\r\n * 发起请求的配置 (对外,参数可选)\r\n */\r\nexport type RequestConfig<D extends RequestData = RequestData> = Partial<RequestConfigBase<D>>;\r\n\r\n/**\r\n * 自定义请求的配置 (接口字段参数必填)\r\n */\r\nexport type RequestConfigBase<D extends RequestData = RequestData> = {\r\n /** 接口地址 */\r\n url: string;\r\n /** 请求方法 */\r\n method?: RequestMethod;\r\n /** 请求头(会自动过滤undefined, null, \"\";不过滤0和false; 数字和布尔值会自动转换为字符串) */\r\n header?: Record<string, string | number | boolean | null | undefined>;\r\n /** 请求参数 */\r\n data?: D;\r\n /** 超时时间 (毫秒), 默认 60000 */\r\n timeout?: number;\r\n\r\n /** 接口返回响应数据的字段, 支持\"a[0].b.c\"的格式, 当配置false时返回完整的响应数据 */\r\n resKey: string | false;\r\n\r\n /** 接口返回响应消息的字段, 支持\"a[0].b.c\"的格式 */\r\n msgKey: string;\r\n\r\n /** 接口返回响应状态码的字段, 支持\"a[0].b.c\"的格式 */\r\n codeKey: string;\r\n\r\n /** 接口返回成功状态码的字段, 支持\"a[0].b.c\"的格式 (默认取 codeKey) */\r\n successKey?: string;\r\n\r\n /** 成功状态码 */\r\n successCode: (number | string)[];\r\n\r\n /** 登录过期状态码 */\r\n reloginCode: (number | string)[];\r\n\r\n /** 是否显示进度条: 支持字符串,自定义文本 (默认true) */\r\n showLoading?: boolean | string;\r\n\r\n /** 是否提示接口异常 (默认true) */\r\n toastError?: boolean;\r\n\r\n /** 是否输出日志 (默认true) */\r\n isLog?: boolean;\r\n\r\n /** 额外输出的日志数据 */\r\n extraLog?: Record<string, unknown>;\r\n\r\n /** 响应数据的缓存时间, 单位毫秒。仅在成功时缓存;仅缓存在内存,应用退出,缓存消失。(默认0,不开启缓存) */\r\n cacheTime?: number;\r\n\r\n /** 是否开启流式传输 (如 SSE) */\r\n enableChunked?: boolean;\r\n\r\n /** 响应类型 (默认 json, enableChunked为true时忽略) */\r\n responseType?: 'text' | 'arraybuffer' | 'json';\r\n\r\n /** 响应拦截 */\r\n responseInterceptor?: (data: ResponseData) => ResponseData;\r\n};\r\n\r\n/**\r\n * 请求任务对象 (用于取消请求或监听流式数据)\r\n */\r\nexport interface RequestTask {\r\n /** 取消请求 */\r\n abort: () => void;\r\n\r\n /** 监听流式数据块接收事件 */\r\n onChunkReceived: (callback: ChunkCallback) => void;\r\n\r\n /** 取消监听流式数据块接收事件 */\r\n offChunkReceived: () => void;\r\n}\r\n\r\n/**\r\n * 流式数据块接收事件回调\r\n */\r\nexport type ChunkCallback = (response: { data: ArrayBuffer }) => void;\r\n\r\n/** 请求缓存 */\r\nconst requestCache = new Map<string, { res: unknown; expire: number }>();\r\n\r\n/**\r\n * 基础请求 (返回 Promise 和 Task 对象)\r\n * 基于 fetch API 封装,支持流式请求\r\n * @param config 请求配置\r\n * @returns Promise<T> & { task?: RequestTask }\r\n * @example\r\n * // 在入口文件完成配置 (确保请求失败有toast提示,登录过期能够触发重新登录,log有日志输出)\r\n * setBaseToolsConfig({\r\n * toast: ({ msg, status }) => (status === 'fail' ? message.error(msg) : message.success(msg)),\r\n * showLoading: ({ title }) => message.loading(title || '加载中...'),\r\n * hideLoading: () => message.destroy(),\r\n * toLogin: () => reLogin(),\r\n * log(level, data) {\r\n * if (data.name === 'request') {\r\n * sendLog('request', data); // 请求日志\r\n * } else if (level === 'error') {\r\n * sendLog('error', data); // 错误日志\r\n * } else {\r\n * sendLog('action', data); // 操作日志\r\n * }\r\n * },\r\n * });\r\n *\r\n * // 封装项目的基础请求\r\n * export function requestApi<T>(config: RequestConfig) {\r\n * return request<T>({\r\n * header: { token: 'xx', version: 'xx', tid: 'xx' }, // 会自动过滤空值\r\n * // responseInterceptor: (res) => res, // 响应拦截,可预处理响应数据,如解密 (可选)\r\n * resKey: 'data',\r\n * msgKey: 'message',\r\n * codeKey: 'status',\r\n * successCode: [1],\r\n * reloginCode: [-10],\r\n * ...config,\r\n * });\r\n * }\r\n *\r\n * // 1. 基于上面 requestApi 的普通接口\r\n * export function apiGoodList(data: { page: number, size: number }) {\r\n * return requestApi<GoodItem[]>({ url: '/goods/list', data, resKey: 'data.list' });\r\n * }\r\n *\r\n * const goodList = await apiGoodList({ page:1, size:10 });\r\n *\r\n * // 2. 参数泛型的写法\r\n * export function apiGoodList(config: RequestConfig<{ page: number, size: number }>) {\r\n * return requestApi<GoodItem[]>({ url: '/goods/list', resKey: 'data.list', ...config });\r\n * }\r\n *\r\n * const goodList = await apiGoodList({ data: { page:1, size:10 }, showLoading: false });\r\n *\r\n * // 3. 基于上面 requestApi 的流式接口\r\n * export function apiChatStream(data: { question: string }) {\r\n * return requestApi<T>({\r\n * url: '/sse/chatStream',\r\n * data,\r\n * resKey: false,\r\n * showLoading: false,\r\n * responseType: 'arraybuffer',\r\n * enableChunked: true,\r\n * });\r\n * }\r\n *\r\n * const { task } = apiChatStream({question: '你好'}); // 发起流式请求\r\n *\r\n * task.onChunkReceived((res) => {\r\n * console.log('ArrayBuffer', res.data); // 接收流式数据\r\n * });\r\n *\r\n * task.offChunkReceived(); // 取消监听,中断流式接收 (调用时机:流式结束,组件销毁,页面关闭)\r\n * task.abort(); // 取消请求 (若流式传输中,会中断流并抛出异常)\r\n */\r\nexport function request<T, D extends RequestData = RequestData>(config: RequestConfigBase<D>) {\r\n // 1. 初始化控制对象\r\n const controller = new AbortController();\r\n const signal = controller.signal;\r\n let chunkCallback: ChunkCallback | null = null;\r\n\r\n // 构造 Task 对象\r\n const task: RequestTask = {\r\n abort: () => controller.abort(),\r\n onChunkReceived: (cb) => {\r\n chunkCallback = cb;\r\n },\r\n offChunkReceived: () => {\r\n chunkCallback = null;\r\n },\r\n };\r\n\r\n // 2. 创建 Promise\r\n const promise = new Promise<T>((resolve, reject) => {\r\n const execute = async () => {\r\n const {\r\n url,\r\n data,\r\n header,\r\n method = 'GET',\r\n resKey,\r\n msgKey,\r\n codeKey,\r\n successKey,\r\n successCode,\r\n reloginCode,\r\n showLoading = true,\r\n toastError = true,\r\n enableChunked = false,\r\n cacheTime,\r\n responseInterceptor,\r\n responseType = 'json',\r\n timeout = 60000,\r\n } = config;\r\n\r\n const isGet = method === 'GET';\r\n const isObjectData = isPlainObject(data);\r\n const isArrayData = !isObjectData && Array.isArray(data);\r\n\r\n // 2.1 参数处理\r\n // 参数过滤 undefined\r\n const fillData = isObjectData ? filterRequestData(data) : data;\r\n\r\n // 请求头过滤空值 (undefined, null, \"\")\r\n const fillHeader = filterRequestHeader(header);\r\n\r\n // 获取 Content-Type (忽略大小写)\r\n const contentTypeKey = Object.keys(fillHeader).find(\r\n (k) => k.toLowerCase() === 'content-type',\r\n );\r\n const contentType = contentTypeKey ? String(fillHeader[contentTypeKey]).toLowerCase() : '';\r\n\r\n if (!isGet && fillData && (isObjectData || isArrayData) && !contentType) {\r\n fillHeader['Content-Type'] = 'application/json';\r\n }\r\n\r\n // 2.2 处理 URL 和 Body\r\n const fillUrl =\r\n isGet && isObjectData ? appendUrlParam(url, fillData as Record<string, unknown>) : url;\r\n\r\n let fillBody: BodyInit | null | undefined;\r\n\r\n if (!isGet && fillData) {\r\n if (isObjectData && contentType.includes('application/x-www-form-urlencoded')) {\r\n // application/x-www-form-urlencoded: 转换为 URLSearchParams\r\n fillBody = toSearchParams(fillData as Record<string, unknown>);\r\n } else if (isObjectData && contentType.includes('multipart/form-data')) {\r\n // multipart/form-data: 转换为 FormData\r\n fillBody = toFormData(fillData as Record<string, unknown>);\r\n // 删除 Content-Type, 让 fetch 自动生成 boundary\r\n if (contentTypeKey) delete fillHeader[contentTypeKey];\r\n } else if (isObjectData || isArrayData) {\r\n fillBody = JSON.stringify(fillData);\r\n } else {\r\n fillBody = fillData as BodyInit;\r\n }\r\n }\r\n\r\n // 2.3 日志与缓存配置\r\n const logConfig = { ...config, data: fillData, header: fillHeader, url: fillUrl };\r\n const startTime = Date.now();\r\n\r\n // 2.4 检查缓存\r\n const isCache = cacheTime && cacheTime > 0;\r\n const cacheKey = isCache ? JSON.stringify({ url: fillUrl, data: fillData }) : '';\r\n\r\n if (isCache) {\r\n const res = checkCache(cacheKey);\r\n if (res) {\r\n logRequestInfo({\r\n status: 'success',\r\n config: logConfig,\r\n fromCache: true,\r\n startTime,\r\n res,\r\n });\r\n resolve(getResult(res, resKey) as T);\r\n return;\r\n }\r\n }\r\n\r\n // 2.5 UI 反馈\r\n const appConfig = getBaseToolsConfig();\r\n if (showLoading)\r\n appConfig.showLoading?.(typeof showLoading === 'string' ? { title: showLoading } : {});\r\n\r\n // 2.6 设置超时\r\n let isTimeout = false;\r\n const timeoutId = setTimeout(() => {\r\n isTimeout = true;\r\n controller.abort();\r\n }, timeout);\r\n\r\n try {\r\n // 2.7 发起请求\r\n const response = await fetch(fillUrl, {\r\n method,\r\n headers: fillHeader,\r\n body: fillBody,\r\n signal,\r\n });\r\n\r\n if (!response.ok) {\r\n if (showLoading) appConfig.hideLoading?.();\r\n throw new Error(`HTTP Error ${response.status}: ${response.statusText}`);\r\n }\r\n\r\n // 2.8 处理流式响应\r\n if (enableChunked) {\r\n if (showLoading) appConfig.hideLoading?.();\r\n\r\n const res = await handleStreamResponse(response, chunkCallback);\r\n\r\n logRequestInfo({ status: 'success', config: logConfig, startTime, res });\r\n\r\n resolve(res as T);\r\n return;\r\n }\r\n\r\n // 2.9 处理普通响应\r\n const resData = await parseResponse(response, responseType);\r\n\r\n // 隐藏 Loading\r\n if (showLoading) appConfig.hideLoading?.();\r\n\r\n // 响应拦截\r\n const res = responseInterceptor ? responseInterceptor(resData) : resData;\r\n\r\n // 2.10 业务状态码解析\r\n const code = getObjectValue(res, codeKey);\r\n const scode = successKey ? getObjectValue(res, successKey) : code;\r\n const msg = getObjectValue(res, msgKey);\r\n const isSuccess = successCode.includes(scode);\r\n const isRelogin = reloginCode.includes(code);\r\n\r\n logRequestInfo({ status: 'success', config: logConfig, startTime, res });\r\n\r\n // 2.11 结果处理\r\n if (isSuccess) {\r\n // 业务正常\r\n if (isCache) requestCache.set(cacheKey, { res, expire: Date.now() + cacheTime });\r\n resolve(getResult(res, resKey) as T);\r\n } else if (isRelogin) {\r\n // 登录失效\r\n reject(res);\r\n appConfig.toLogin?.(); // 放在后面,确保reject执行后再跳转登录\r\n } else {\r\n // 业务错误\r\n if (toastError && msg) appConfig.toast?.({ status: 'fail', msg });\r\n reject(res);\r\n }\r\n } catch (e) {\r\n const status = 'fail';\r\n const isAbortError = e instanceof DOMException && e.name === 'AbortError'; // 取消请求不视为错误\r\n\r\n if (isAbortError && isTimeout) {\r\n if (toastError) appConfig.toast?.({ status, msg: '请求超时' });\r\n const timeoutError = new Error('Request Timeout');\r\n logRequestInfo({ status, config: logConfig, startTime, e: timeoutError });\r\n reject(timeoutError);\r\n return;\r\n }\r\n\r\n if (!isAbortError && toastError) appConfig.toast?.({ status, msg: '网络请求失败' });\r\n logRequestInfo({ status, config: logConfig, startTime, e });\r\n reject(e);\r\n } finally {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n }\r\n };\r\n\r\n execute();\r\n }) as Promise<T> & { task?: RequestTask };\r\n\r\n // 3. 挂载 Task\r\n promise.task = task;\r\n\r\n return promise;\r\n}\r\n\r\n/**\r\n * 参数过滤undefined, 避免接口处理异常 (不可过滤 null 、 \"\" 、 false 、 0 这些有效值)\r\n */\r\nexport function filterRequestData(data: Record<string, any>) {\r\n const res: Record<string, any> = {};\r\n Object.entries(data).forEach(([k, v]) => {\r\n if (v !== undefined) res[k] = v;\r\n });\r\n return res;\r\n}\r\n\r\n/**\r\n * 请求头过滤空值 (undefined, null, \"\"), 不过滤0和false\r\n */\r\nexport function filterRequestHeader(header: RequestConfigBase['header']) {\r\n const newHeader: Record<string, string> = {};\r\n if (header) {\r\n Object.entries(header).forEach(([k, v]) => {\r\n if (v !== undefined && v !== null && v !== '') newHeader[k] = String(v);\r\n });\r\n }\r\n return newHeader;\r\n}\r\n\r\n/**\r\n * 日志输出\r\n */\r\nfunction logRequestInfo(options: {\r\n config: RequestConfigBase<RequestData> & { url?: string };\r\n fromCache?: boolean;\r\n startTime: number;\r\n status: 'success' | 'fail';\r\n res?: unknown;\r\n e?: unknown;\r\n}) {\r\n const { log } = getBaseToolsConfig();\r\n const { isLog = true } = options.config;\r\n\r\n if (!log || !isLog) return;\r\n\r\n const { config, res, fromCache = false, startTime, status, e } = options;\r\n const { url, data, header, method, extraLog } = config;\r\n const endTime = Date.now();\r\n const fmt = 'YYYY-MM-DD HH:mm:ss.SSS';\r\n\r\n const info: AppLogInfo = {\r\n name: 'request',\r\n status,\r\n url,\r\n data,\r\n method,\r\n header,\r\n fromCache,\r\n startTime: toDayjs(startTime).format(fmt),\r\n endTime: toDayjs(endTime).format(fmt),\r\n duration: endTime - startTime,\r\n ...extraLog,\r\n };\r\n\r\n if (status === 'success') {\r\n info.res = cloneDeep(res); // 深拷贝,避免外部修改对象,造成输出不一致\r\n log('info', info);\r\n } else {\r\n info.e = e;\r\n log('error', info);\r\n }\r\n}\r\n\r\n/**\r\n * 获取 resKey 对应的数据\r\n */\r\nfunction getResult(res: unknown, resKey?: RequestConfigBase['resKey']) {\r\n if (!res || !resKey || typeof res !== 'object') return res;\r\n return getObjectValue(res, resKey);\r\n}\r\n\r\n/**\r\n * 检查缓存\r\n */\r\nfunction checkCache(cacheKey: string) {\r\n const cached = requestCache.get(cacheKey);\r\n if (!cached) return null;\r\n if (cached.expire <= Date.now()) {\r\n requestCache.delete(cacheKey);\r\n return null;\r\n }\r\n return cached.res;\r\n}\r\n\r\n/**\r\n * 处理流式响应\r\n */\r\nasync function handleStreamResponse(response: Response, chunkCallback: ChunkCallback | null) {\r\n if (!response.body) throw new Error('Response body is null');\r\n\r\n const reader = response.body.getReader();\r\n\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n if (chunkCallback && value) {\r\n chunkCallback({ data: value.buffer });\r\n }\r\n }\r\n\r\n return 'Stream Finished';\r\n}\r\n\r\n/**\r\n * 解析响应数据\r\n */\r\nasync function parseResponse(response: Response, responseType: string) {\r\n let resData: ResponseData;\r\n if (responseType === 'arraybuffer') {\r\n resData = await response.arrayBuffer();\r\n } else if (responseType === 'text') {\r\n resData = await response.text();\r\n } else {\r\n const text = await response.text();\r\n try {\r\n resData = JSON.parse(text);\r\n } catch {\r\n resData = text;\r\n }\r\n }\r\n return resData;\r\n}\r\n\r\n/**\r\n * 转换为 URLSearchParams\r\n */\r\nfunction toSearchParams(data: Record<string, unknown>) {\r\n const params = new URLSearchParams();\r\n for (const key in data) {\r\n const val = data[key];\r\n // undefined 已在 fillData 阶段过滤,此处仅需判断 null\r\n // null 在 Form 中会被转为字符串 \"null\",通常不符合预期,故过滤\r\n if (val === null) continue;\r\n if (Array.isArray(val)) {\r\n val.forEach((v) => params.append(key, typeof v === 'object' ? JSON.stringify(v) : String(v)));\r\n } else {\r\n params.append(key, typeof val === 'object' ? JSON.stringify(val) : String(val));\r\n }\r\n }\r\n return params;\r\n}\r\n\r\n/**\r\n * 转换为 FormData\r\n */\r\nfunction toFormData(data: Record<string, unknown>) {\r\n const formData = new FormData();\r\n for (const key in data) {\r\n const val = data[key];\r\n // undefined 已在 fillData 阶段过滤,此处仅需判断 null\r\n // null 在 Form 中会被转为字符串 \"null\",通常不符合预期,故过滤\r\n if (val === null) continue;\r\n if (Array.isArray(val)) {\r\n val.forEach((v) =>\r\n formData.append(\r\n key,\r\n v instanceof Blob ? v : typeof v === 'object' ? JSON.stringify(v) : String(v),\r\n ),\r\n );\r\n } else {\r\n formData.append(\r\n key,\r\n val instanceof Blob ? val : typeof val === 'object' ? JSON.stringify(val) : String(val),\r\n );\r\n }\r\n }\r\n return formData;\r\n}\r\n","import { getBaseToolsConfig } from '../index';\r\n\r\ntype WebApi<Option, Res, Config = unknown> = (option: Option, config?: Config) => Promise<Res>;\r\n\r\n/**\r\n * web api 的调用配置\r\n */\r\nexport type WebApiConfig<Res, Err> = {\r\n /** 是否显示加载提示, 默认 false. (支持字符串,自定义文本) */\r\n showLoading?: boolean | string;\r\n\r\n /** 操作成功的toast提示, 默认不显示 */\r\n toastSuccess?: ((res: Res) => false | string) | false | string;\r\n\r\n /** 是否显示操作失败的详细错误信息, 默认 true. (支持字符串,自定义文本; 支持根据errMsg判断是否显示, 例如: (e) => !e.errMsg.includes('cancel') */\r\n toastError?: ((e: Err) => boolean | string) | boolean | string;\r\n\r\n /** 是否显示日志, 默认 true */\r\n showLog?: boolean;\r\n};\r\n\r\n/**\r\n * 拓展 web api, 使其支持loading,toast,log,任务对象等能力\r\n * @param webApi web api\r\n * @param apiName web api 名称 (可选, 用于日志输出, 默认'promisifyWebApi')\r\n * @return 注入拓展能力的promise (默认提示异常和输出日志,不显示进度条和操作成功)\r\n * @example\r\n * const promise = promisifyWebApi(downloadFile, 'downloadFile');\r\n * await promise({ url: 'xx' }, {showLoading: '下载中', toastSuccess: '下载成功'});\r\n */\r\nexport function promisifyWebApi<Option, Res, Err, Config = unknown>(\r\n webApi: WebApi<Option, Res, Config>,\r\n apiName?: string,\r\n) {\r\n return (option: Option, config?: WebApiConfig<Res, Err> & Config) => {\r\n const finalConfig = config || ({} as WebApiConfig<Res, Err> & Config);\r\n const {\r\n showLoading = false,\r\n toastSuccess = false,\r\n toastError = true,\r\n showLog = true,\r\n } = finalConfig;\r\n const {\r\n log,\r\n toast,\r\n showLoading: showLoadingFn,\r\n hideLoading: hideLoadingFn,\r\n } = getBaseToolsConfig();\r\n const fname = apiName || 'promisifyWebApi'; // webApi.name经过打包后取不到原函数名,不如默认'promisifyWebApi'\r\n\r\n if (showLoading) {\r\n const title = typeof showLoading === 'string' ? showLoading : '';\r\n showLoadingFn?.({ title });\r\n }\r\n\r\n return new Promise<Res>((resolve, reject) => {\r\n webApi(option, finalConfig)\r\n .then((res) => {\r\n if (showLoading) hideLoadingFn?.();\r\n if (showLog) log?.('info', { name: fname, status: 'success', option, res });\r\n resolve(res);\r\n\r\n const msg = typeof toastSuccess === 'function' ? toastSuccess(res) : toastSuccess;\r\n if (msg) toast?.({ msg, status: 'success' });\r\n })\r\n .catch((e) => {\r\n if (showLoading) hideLoadingFn?.();\r\n if (showLog) log?.('error', { name: fname, status: 'fail', option, e });\r\n\r\n const msg = typeof toastError === 'function' ? toastError(e) : toastError;\r\n if (msg) {\r\n toast?.({\r\n msg: typeof msg === 'string' ? msg : `${fname} fail: ${JSON.stringify(e)}`,\r\n status: 'fail',\r\n });\r\n }\r\n\r\n reject(e);\r\n });\r\n });\r\n };\r\n}\r\n","import { promisifyWebApi } from '../async';\r\nimport type { WebApiConfig } from '../async';\r\n\r\n/**\r\n * 上传文件的选项\r\n */\r\nexport type UploadFileOption = {\r\n /** 上传接口地址 */\r\n url: string;\r\n\r\n /** 要上传的文件对象 */\r\n file: File;\r\n\r\n /** 文件对应的 key, 默认'file' (服务端通过这个 key 获取文件的二进制内容) */\r\n name?: string;\r\n\r\n /** 请求头 */\r\n header?: Record<string, string | number>;\r\n\r\n /** 额外的formData参数 */\r\n formData?: Record<string, string | number>;\r\n\r\n /** 超时时间,单位 ms,默认 0(不超时) */\r\n timeout?: number;\r\n};\r\n\r\nexport type OnUploadProgressUpdate = (res: UploadProgressEvent) => void;\r\n\r\nexport type UploadProgressEvent = {\r\n /** 上传进度百分比: 0-100 */\r\n progress: number;\r\n /** 已上传字节数 */\r\n loaded: number;\r\n /** 总字节数 */\r\n total: number;\r\n};\r\n\r\nexport type UploadTask = {\r\n /** 上传进度 */\r\n onProgressUpdate: (callback: OnUploadProgressUpdate) => void;\r\n /** 取消上传 */\r\n abort: () => void;\r\n};\r\n\r\nexport type UploadConfig = {\r\n /** 获取task对象 */\r\n onTaskReady?: (task: UploadTask) => void;\r\n};\r\n\r\nexport type UploadFail = {\r\n message: string;\r\n status: number;\r\n};\r\n\r\nfunction upload<T>(option: UploadFileOption, config?: UploadConfig) {\r\n return new Promise<T>((resolve, reject) => {\r\n const xhr = new XMLHttpRequest();\r\n const { url, file, name = 'file', header, formData, timeout = 0 } = option;\r\n\r\n const fail = (error: UploadFail) => reject(error);\r\n\r\n const success = (responseText: string) => {\r\n try {\r\n resolve(JSON.parse(responseText) as T);\r\n } catch (e) {\r\n resolve(responseText as T);\r\n }\r\n };\r\n\r\n // 构造任务对象\r\n let onProgressUpdate: OnUploadProgressUpdate;\r\n const task: UploadTask = {\r\n onProgressUpdate: (callback) => {\r\n onProgressUpdate = callback;\r\n },\r\n abort: () => xhr.abort(),\r\n };\r\n config?.onTaskReady?.(task);\r\n\r\n // 监听进度\r\n xhr.upload.onprogress = (e) => {\r\n if (!e.lengthComputable) return;\r\n const ev: UploadProgressEvent = {\r\n progress: Math.round((e.loaded / e.total) * 100),\r\n loaded: e.loaded,\r\n total: e.total,\r\n };\r\n onProgressUpdate?.(ev);\r\n };\r\n\r\n // 监听事件\r\n xhr.onload = () => {\r\n if (xhr.status >= 200 && xhr.status < 300) {\r\n success(xhr.responseText);\r\n } else {\r\n fail({ message: `上传失败`, status: xhr.status });\r\n }\r\n };\r\n xhr.onerror = () => fail({ message: '网络错误', status: 0 });\r\n xhr.ontimeout = () => fail({ message: '上传超时', status: -1 });\r\n xhr.onabort = () => fail({ message: '用户取消', status: -2 });\r\n\r\n // 设置请求方法和 URL\r\n xhr.open('POST', url);\r\n\r\n // 设置请求头\r\n if (header) {\r\n Object.entries(header).forEach(([k, v]) => {\r\n if (v !== undefined && v !== null && v !== '') xhr.setRequestHeader(k, String(v));\r\n });\r\n }\r\n\r\n // 设置超时时间\r\n xhr.timeout = timeout;\r\n\r\n // 组装 FormData\r\n const data = new FormData();\r\n data.append(name, file);\r\n if (formData) {\r\n Object.entries(formData).forEach(([k, v]) => {\r\n if (v !== undefined) data.append(k, String(v));\r\n });\r\n }\r\n\r\n // 发送请求\r\n xhr.send(data);\r\n });\r\n}\r\n\r\n/**\r\n * 上传文件\r\n * @param option 上传文件的选项\r\n * @param config 配置项\r\n * @example\r\n * // 上传\r\n * await uploadFile({ url: 'https://xx', file: file});\r\n *\r\n * // 监听上传进度\r\n * await uploadFile({ url: 'https://xx', file: file}, {\r\n * onTaskReady: (task) =>\r\n * task.onProgressUpdate((res) => console.log('上传进度:', res.progress)),\r\n * });\r\n */\r\nexport function uploadFile<T = any>(\r\n option: UploadFileOption,\r\n config?: UploadConfig & WebApiConfig<T, UploadFail>,\r\n) {\r\n return promisifyWebApi<UploadFileOption, T, UploadFail, UploadConfig>(upload, 'uploadFile')(\r\n option,\r\n config,\r\n );\r\n}\r\n","const WK = {\r\n val: '__l_val',\r\n exp: '__l_exp',\r\n wrap: '__l_wrap',\r\n} as const;\r\n\r\n/**\r\n * 写入 localStorage(自动 JSON 序列化)\r\n * 当 `value` 为 `null` 或 `undefined` 时,会移除该键。\r\n * 支持保存:对象、数组、字符串、数字、布尔值。\r\n * @param key 键名\r\n * @param value 任意可序列化的值\r\n * @param days 过期天数(从当前时间起算)\r\n * @example\r\n * setLocalStorage('user', { id: 1, name: 'Alice' }); // 对象\r\n * setLocalStorage('age', 18); // 数字\r\n * setLocalStorage('vip', true); // 布尔值\r\n * setLocalStorage('token', 'abc123', 7); // 7 天后过期\r\n */\r\nexport function setLocalStorage(key: string, value: unknown, days?: number) {\r\n if (value === undefined || value === null) {\r\n removeLocalStorage(key);\r\n return;\r\n }\r\n\r\n let toStore: unknown = value;\r\n if (typeof days === 'number' && days > 0) {\r\n const ms = days * 24 * 60 * 60 * 1000;\r\n toStore = {\r\n [WK.wrap]: true,\r\n [WK.val]: value,\r\n [WK.exp]: Date.now() + ms,\r\n };\r\n }\r\n\r\n localStorage.setItem(key, JSON.stringify(toStore));\r\n}\r\n\r\n/**\r\n * 读取 localStorage(自动 JSON 反序列化)\r\n * 若值为合法 JSON,则返回反序列化后的数据;\r\n * 若值非 JSON(如外部写入的纯字符串),则原样返回字符串。\r\n * 不存在时返回 `null`。\r\n * @param key 键名\r\n * @returns 解析后的值或 `null`\r\n * @example\r\n * const user = getLocalStorage<{ id: number; name: string }>('user');\r\n * const age = getLocalStorage<number>('age');\r\n * const vip = getLocalStorage<boolean>('vip');\r\n */\r\nexport function getLocalStorage<T = unknown>(key: string): T | null {\r\n const raw = localStorage.getItem(key);\r\n if (raw === null) return null;\r\n try {\r\n const parsed = JSON.parse(raw);\r\n\r\n if (parsed && typeof parsed === 'object' && WK.wrap in parsed && WK.exp in parsed) {\r\n if (Date.now() > parsed[WK.exp]) {\r\n removeLocalStorage(key);\r\n return null;\r\n }\r\n return parsed[WK.val] as T;\r\n }\r\n return parsed as T;\r\n } catch {\r\n return raw as T;\r\n }\r\n}\r\n\r\n/**\r\n * 移除 localStorage 指定键\r\n * @param key 键名\r\n * @example\r\n * removeLocalStorage('token');\r\n */\r\nexport function removeLocalStorage(key: string) {\r\n localStorage.removeItem(key);\r\n}\r\n","/**\r\n * 获取url的查询参数值\r\n * @param key 参数名\r\n * @param url 完整 URL 或仅查询串(如 \"a=1&b=2\")\r\n * @returns 解码后的参数值 (若不存在|\"null\"|\"undefined\",则返回 null)\r\n * @example\r\n * const q = getUrlParam('q'); // 默认当前地址\r\n * const q = getUrlParam('q', 'https://a.com/?q=%E6%B5%8B%E8%AF%95'); // \"测试\"\r\n * const a = getUrlParam('a', 'a=1'); // \"1\"\r\n * const list = getUrlParam('list', 'list=[1,2]'); // \"[1,2]\"\r\n * const list = getUrlParam('list', 'list=null'); // null\r\n * const list = getUrlParam('list', 'list=undefined'); // null\r\n */\r\nexport function getUrlParam(key: string, url = window.location.href) {\r\n const searchParams = new URL(url.includes('?') ? url : `?${url}`, 'http://localhost')\r\n .searchParams;\r\n const value = searchParams.get(key);\r\n return value === 'null' || value === 'undefined' ? null : value;\r\n}\r\n\r\n/**\r\n * 获取url的查询参数值,并转为number类型\r\n * @param key 参数名\r\n * @param url 完整 URL 或仅查询串(如 \"a=1&b=2\")\r\n * @returns 解码后的参数值 (若不存在|\"非数字字符串\",则返回 null)\r\n * @example\r\n * const a = getUrlNumber('a'); // 默认当前地址\r\n * const a = getUrlNumber('a', 'https://a.com/?a=1'); // 1\r\n * const a = getUrlNumber('a', 'a=1'); // 1\r\n * const a = getUrlNumber('a', 'a=1.2'); // 1.2\r\n * const a = getUrlNumber('a', 'a=abc'); // null\r\n */\r\nexport function getUrlNumber(key: string, url = window.location.href) {\r\n const str = getUrlParam(key, url);\r\n if (!str) return null;\r\n\r\n const num = Number(str);\r\n return isNaN(num) ? null : num;\r\n}\r\n\r\n/**\r\n * 获取url的所有查询参数值\r\n * @param url 完整 URL 或仅查询串(如 \"a=1&b=2\")\r\n * @returns 解码后的键值对象(无参数返回空对象; \"null\"|\"undefined\"的参数会被忽略)\r\n * @example\r\n * const params = getUrlParams(); // 默认当前地址\r\n * const params = getUrlParams('a=1&b=2'); // { a: \"1\", b: \"2\" }\r\n * const params = getUrlParams('https://a.com/?a=1&b=2'); // { a: \"1\", b: \"2\" }\r\n * const params = getUrlParams('a=1&b=null'); // { a: \"1\" }\r\n * const params = getUrlParams('a=1&b=undefined'); // { a: \"1\" }\r\n */\r\nexport function getUrlParams(url = window.location.href) {\r\n const searchParams = new URL(url.includes('?') ? url : `?${url}`, 'http://localhost')\r\n .searchParams;\r\n const result: Record<string, string> = {};\r\n\r\n for (const [key, value] of searchParams.entries()) {\r\n if (value !== 'null' && value !== 'undefined') {\r\n result[key] = value;\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,SAAsB,SAAS,MAA6B;AAAA;AAC1D,QAAI,OAAO,SAAS,SAAU,QAAO,OAAO,sBAAQ,EAAE;AAGtD,QAAI,UAAU,aAAa,OAAO,UAAU,UAAU,cAAc,YAAY;AAC9E,UAAI;AACF,cAAM,UAAU,UAAU,UAAU,IAAI;AACxC;AAAA,MAEF,SAAS,GAAG;AAAA,MAEZ;AAAA,IACF;AAGA,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,UAAI;AACF,cAAM,WAAW,SAAS,cAAc,UAAU;AAClD,iBAAS,QAAQ;AAGjB,iBAAS,aAAa,YAAY,EAAE;AACpC,iBAAS,MAAM,WAAW;AAC1B,iBAAS,MAAM,MAAM;AACrB,iBAAS,MAAM,QAAQ;AACvB,iBAAS,MAAM,UAAU;AACzB,iBAAS,MAAM,gBAAgB;AAE/B,iBAAS,KAAK,YAAY,QAAQ;AAGlC,iBAAS,MAAM;AACf,iBAAS,OAAO;AAGhB,iBAAS,kBAAkB,GAAG,SAAS,MAAM,MAAM;AAEnD,cAAM,KAAK,SAAS,YAAY,MAAM;AACtC,iBAAS,KAAK,YAAY,QAAQ;AAElC,YAAI,IAAI;AACN,kBAAQ;AAAA,QACV,OAAO;AACL,iBAAO,IAAI,MAAM,oCAAoC,CAAC;AAAA,QACxD;AAAA,MACF,SAAS,GAAG;AACV,eAAO,CAAC;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AASA,SAAsB,SAAS,MAA6B;AAAA;AAC1D,UAAM,IAAI,OAAO,sBAAQ,EAAE;AAC3B,QAAI,kBAAkB,GAAG;AACvB,YAAM,QAAQ,WAAW,CAAC;AAC1B,YAAM,eAAe;AAAA,QACnB,aAAa,IAAI,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,YAAY,CAAC;AAAA,QAChD,cAAc,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAAA,MACxD,CAAC;AACD;AAAA,IACF;AACA,WAAO,iBAAiB,CAAC;AAAA,EAC3B;AAAA;AAUA,SAAsB,SAAS,MAA2B;AAAA;AACxD,QAAI,kBAAkB,GAAG;AACvB,YAAM,EAAE,MAAAA,OAAM,KAAK,IAAI,eAAe,IAAI;AAC1C,YAAM,eAAe;AAAA,QACnB,aAAa,IAAI,KAAK,CAACA,KAAI,GAAG,EAAE,MAAM,YAAY,CAAC;AAAA,QACnD,cAAc,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,aAAa,CAAC;AAAA,MACvD,CAAC;AACD;AAAA,IACF;AACA,UAAM,EAAE,KAAK,IAAI,eAAe,IAAI;AACpC,WAAO,iBAAiB,IAAI;AAAA,EAC9B;AAAA;AAUA,SAAsB,UAAU,OAA8D;AAAA;AAC5F,UAAM,OAAO,MAAM,YAAY,KAAK;AACpC,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,0BAA0B;AACrD,QAAI,kBAAkB,GAAG;AACvB,YAAM,OAAO,KAAK,QAAQ;AAC1B,YAAM,eAAe,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC;AACrC;AAAA,IACF;AACA,UAAM,IAAI,MAAM,qCAAqC;AAAA,EACvD;AAAA;AASA,SAAsB,QAAQ,KAA4B;AAAA;AACxD,UAAM,IAAI,OAAO,oBAAO,EAAE;AAC1B,QAAI,kBAAkB,GAAG;AACvB,YAAM,eAAe;AAAA,QACnB,iBAAiB,IAAI,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAAA,QACxD,cAAc,IAAI,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,aAAa,CAAC;AAAA,MACpD,CAAC;AACD;AAAA,IACF;AACA,UAAM,SAAS,CAAC;AAAA,EAClB;AAAA;AAUA,SAAsB,SAAS,MAA2B;AAAA;AACxD,QAAI,kBAAkB,GAAG;AACvB,YAAM,OAAO,KAAK,QAAQ;AAC1B,YAAM,eAAe,EAAE,CAAC,IAAI,GAAG,KAAK,CAAC;AACrC;AAAA,IACF;AACA,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AAAA;AASA,SAAsB,QAAQ,KAA4B;AAAA;AACxD,UAAM,IAAI,OAAO,oBAAO,EAAE;AAC1B,QAAI,kBAAkB,GAAG;AACvB,YAAM,QAAQ,EACX,QAAQ,eAAe,IAAI,EAC3B,QAAQ,cAAc,EAAE,EACxB,QAAQ,wBAAwB,EAAE,EAClC,QAAQ,UAAU,IAAI,EACtB,KAAK;AACR,YAAM,eAAe;AAAA,QACnB,YAAY,IAAI,KAAK,CAAC,CAAC,GAAG,EAAE,MAAM,WAAW,CAAC;AAAA,QAC9C,cAAc,IAAI,KAAK,CAAC,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAAA,MACxD,CAAC;AACD;AAAA,IACF;AACA,UAAM,SAAS,CAAC;AAAA,EAClB;AAAA;AAcA,SAAsB,UAAU,MAAoD;AAAA;AAClF,UAAM,OAAO,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC;AAC3C,UAAM,aAAa,CAAC,MAClB,EACG,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO;AAC1B,UAAM,QAAQ,MAAM;AAClB,YAAM,MAAM,KACT,IAAI,CAAC,MAAM,OAAO,EAAE,IAAI,CAAC,MAAM,OAAO,WAAW,OAAO,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,OAAO,EACnF,KAAK,EAAE;AACV,aAAO,UAAU,GAAG;AAAA,IACtB,GAAG;AACH,UAAM,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,OAAO,CAAC,CAAC,EAAE,KAAK,GAAI,CAAC,EAAE,KAAK,IAAI;AACzE,UAAM,MAAM,KACT;AAAA,MAAI,CAAC,MACJ,EACG,IAAI,CAAC,MAAM;AACV,cAAM,IAAI,OAAO,CAAC;AAClB,cAAM,YAAY,SAAS,KAAK,CAAC;AACjC,cAAM,UAAU,EAAE,QAAQ,MAAM,IAAI;AACpC,eAAO,YAAY,IAAI,OAAO,MAAM;AAAA,MACtC,CAAC,EACA,KAAK,GAAG;AAAA,IACb,EACC,KAAK,IAAI;AACZ,QAAI,kBAAkB,GAAG;AACvB,YAAM,eAAe;AAAA,QACnB,aAAa,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,YAAY,CAAC;AAAA,QACnD,6BAA6B,IAAI,KAAK,CAAC,GAAG,GAAG,EAAE,MAAM,4BAA4B,CAAC;AAAA,QAClF,YAAY,IAAI,KAAK,CAAC,GAAG,GAAG,EAAE,MAAM,WAAW,CAAC;AAAA,QAChD,cAAc,IAAI,KAAK,CAAC,GAAG,GAAG,EAAE,MAAM,aAAa,CAAC;AAAA,MACtD,CAAC;AACD;AAAA,IACF;AACA,UAAM,SAAS,GAAG;AAAA,EACpB;AAAA;AAEA,SAAe,YAAY,OAA+C;AAAA;AACxE,QAAI,iBAAiB,KAAM,QAAO;AAClC,QAAI,iBAAiB;AACnB,aAAO,MAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAClD,cAAM;AAAA,UACJ,CAAC,MAAO,IAAI,QAAQ,CAAC,IAAI,OAAO,IAAI,MAAM,sBAAsB,CAAC;AAAA,UACjE;AAAA,QACF;AAAA,MACF,CAAC;AACH,UAAM,WAAW,OAAO,gBAAgB,eAAe,iBAAiB;AACxE,QAAI,UAAU;AACZ,YAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,UAAI,QAAS,MAAsB;AACnC,UAAI,SAAU,MAAsB;AACpC,YAAM,MAAM,IAAI,WAAW,IAAI;AAC/B,iCAAK,UAAU,OAAsB,GAAG;AACxC,aAAO,MAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAClD,YAAI,OAAO,CAAC,MAAO,IAAI,QAAQ,CAAC,IAAI,OAAO,IAAI,MAAM,sBAAsB,CAAC,GAAI,WAAW;AAAA,MAC7F,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT;AAAA;AAEA,SAAS,oBAAoB;AAC3B,SAAO,CAAC,EACN,UAAU,aACV,OAAO,UAAU,UAAU,UAAU,cACrC,OAAO,kBAAkB;AAE7B;AAEA,SAAe,eAAe,OAA6B;AAAA;AACzD,UAAM,UAAU,UAAW,MAAM,CAAC,IAAI,cAAc,KAAK,CAAC,CAAC;AAAA,EAC7D;AAAA;AAEA,SAAS,WAAW,MAAc;AAChC,QAAM,MAAM,SAAS,cAAc,KAAK;AACxC,MAAI,YAAY;AAChB,SAAO,IAAI,eAAe;AAC5B;AAEA,SAAS,eAAe,MAAY;AAhRpC;AAiRE,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY,KAAK,UAAU,IAAI,CAAC;AAC1C,QAAM,OACJ,gBAAgB,WAAW,UAAK,cAAL,YAAkB,UAAU,YAAa,UAAU;AAChF,QAAM,OAAO,UAAU,eAAe;AACtC,SAAO,EAAE,MAAM,KAAK;AACtB;AAEA,SAAS,iBAAiB,MAAc;AACtC,SAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,QAAI;AACF,YAAM,MAAM,SAAS,cAAc,KAAK;AACxC,UAAI,kBAAkB;AACtB,UAAI,MAAM,WAAW;AACrB,UAAI,MAAM,MAAM;AAChB,UAAI,MAAM,QAAQ;AAClB,UAAI,MAAM,UAAU;AACpB,UAAI,MAAM,gBAAgB;AAC1B,UAAI,YAAY;AAChB,eAAS,KAAK,YAAY,GAAG;AAC7B,YAAM,YAAY,OAAO,aAAa;AACtC,YAAM,QAAQ,SAAS,YAAY;AACnC,YAAM,mBAAmB,GAAG;AAC5B,6CAAW;AACX,6CAAW,SAAS;AACpB,YAAM,KAAK,SAAS,YAAY,MAAM;AACtC,eAAS,KAAK,YAAY,GAAG;AAC7B,6CAAW;AACX,UAAI,IAAI;AACN,gBAAQ;AAAA,MACV,OAAO;AACL,eAAO,IAAI,MAAM,oCAAoC,CAAC;AAAA,MACxD;AAAA,IACF,SAAS,GAAG;AACV,aAAO,CAAC;AAAA,IACV;AAAA,EACF,CAAC;AACH;;;AClRA,IAAM,YAAuB,CAAC;AAKvB,SAAS,qBAAqB;AACnC,SAAO;AACT;AAqBO,SAAS,mBAAmB,WAAsB;AACvD,SAAO,OAAO,WAAW,SAAS;AACpC;;;AC1DO,SAAS,UAAU,MAAc,OAAe,MAAc;AACnE,QAAM,OAAO,oBAAI,KAAK;AACtB,OAAK,QAAQ,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,KAAK,GAAI;AACxD,QAAM,UAAU,WAAW,KAAK,YAAY,CAAC;AAC7C,WAAS,SAAS,GAAG,IAAI,IAAI,mBAAmB,KAAK,CAAC,KAAK,OAAO;AACpE;AASO,SAAS,UAAU,MAA6B;AAtBvD;AAuBE,QAAM,QAAQ,KAAK,SAAS,MAAM;AAClC,QAAM,QAAQ,MAAM,MAAM,KAAK,IAAI,GAAG;AACtC,MAAI,MAAM,WAAW,GAAG;AACtB,UAAM,KAAI,WAAM,IAAI,MAAV,mBAAa,MAAM,KAAK;AAClC,WAAO,IAAI,mBAAmB,CAAC,IAAI;AAAA,EACrC;AACA,SAAO;AACT;AASO,SAAS,aAAa,MAAc;AACzC,WAAS,SAAS,GAAG,IAAI;AAC3B;;;ACrCO,SAAS,QAAgB;AAC9B,MAAI,OAAO,cAAc,YAAa,QAAO;AAC7C,UAAQ,UAAU,aAAa,IAAI,YAAY;AACjD;AAKO,SAAS,WAAoB;AAClC,QAAM,KAAK,MAAM;AACjB,SAAO,mEAAmE,KAAK,EAAE;AACnF;AAKO,SAAS,WAAoB;AAClC,QAAM,KAAK,MAAM;AACjB,SAAO,mCAAmC,KAAK,EAAE,KAAK,CAAC,UAAU,KAAK,EAAE;AAC1E;AAKO,SAAS,OAAgB;AAC9B,SAAO,CAAC,SAAS,KAAK,CAAC,SAAS;AAClC;AAKO,SAAS,QAAiB;AAC/B,QAAM,KAAK,MAAM;AACjB,SAAO,oBAAoB,KAAK,EAAE;AACpC;AAKO,SAAS,YAAqB;AACnC,QAAM,KAAK,MAAM;AACjB,SAAO,WAAW,KAAK,EAAE;AAC3B;AAKO,SAAS,WAAoB;AAClC,QAAM,KAAK,MAAM;AACjB,SAAO,kBAAkB,KAAK,EAAE;AAClC;AAMO,SAAS,WAAoB;AAClC,QAAM,KAAK,MAAM;AACjB,SAAO,YAAY,KAAK,EAAE,KAAK,CAAC,SAAS,KAAK,EAAE,KAAK,CAAC,SAAS,KAAK,EAAE,KAAK,CAAC,WAAW,KAAK,EAAE;AAChG;AAKO,SAAS,mBAA4B;AAC1C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,kBAAkB,UAAU,UAAU,iBAAiB;AAChE;AAKO,SAAS,sBAA8B;AAC5C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,SAAO,OAAO,oBAAoB;AACpC;AAKO,SAAS,iBAAgC;AAC9C,QAAM,KAAK,MAAM;AAEjB,MAAI,YAAY,KAAK,EAAE,EAAG,QAAO;AACjC,MAAI,YAAY,KAAK,EAAE,EAAG,QAAO;AACjC,MAAI,aAAa,KAAK,EAAE,EAAG,QAAO;AAClC,MAAI,SAAS,KAAK,EAAE,EAAG,QAAO;AAC9B,MAAI,SAAS,KAAK,EAAE,EAAG,QAAO;AAC9B,MAAI,gBAAgB,KAAK,EAAE,EAAG,QAAO;AAErC,SAAO;AACT;AAKO,SAAS,oBAAmC;AACjD,QAAM,KAAK,MAAM;AAEjB,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,aAAW,WAAW,iBAAiB;AACrC,UAAM,UAAU,GAAG,MAAM,OAAO;AAChC,QAAI,WAAW,QAAQ,CAAC,GAAG;AACzB,aAAO,QAAQ,CAAC;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,QAAgB;AAC9B,QAAM,KAAK,MAAM;AAEjB,MAAI,WAAW,KAAK,EAAE,EAAG,QAAO;AAChC,MAAI,UAAU,KAAK,EAAE,EAAG,QAAO;AAC/B,MAAI,SAAS,KAAK,EAAE,EAAG,QAAO;AAC9B,MAAI,oBAAoB,KAAK,EAAE,EAAG,QAAO;AACzC,MAAI,WAAW,KAAK,EAAE,EAAG,QAAO;AAEhC,SAAO;AACT;;;ACnIO,SAAS,iBAAiB;AAC/B,SAAO,OAAO,cAAc,SAAS,gBAAgB,eAAe,SAAS,KAAK;AACpF;AAMO,SAAS,kBAAkB;AAChC,SAAO,OAAO,eAAe,SAAS,gBAAgB,gBAAgB,SAAS,KAAK;AACtF;AAOO,SAAS,qBAAqB;AACnC,QAAM,MAAM,SAAS;AACrB,QAAM,OAAO,SAAS;AACtB,SAAO,OAAO,eAAe,IAAI,aAAa,KAAK,aAAa;AAClE;AAOO,SAAS,sBAAsB;AACpC,QAAM,MAAM,SAAS;AACrB,QAAM,OAAO,SAAS;AACtB,SAAO,OAAO,eAAe,IAAI,cAAc,KAAK,cAAc;AACpE;AASO,SAAS,eAAe,KAAa,WAA2B,UAAU;AAC/E,MAAI,oBAAoB,SAAS,gBAAgB,OAAO;AACtD,WAAO,SAAS,EAAE,KAAK,SAAS,CAAC;AAAA,EACnC,OAAO;AACL,WAAO,SAAS,GAAG,GAAG;AAAA,EACxB;AACF;AAQO,SAAS,aAAa,IAAa,SAAS,GAAG;AACpD,QAAM,OAAO,GAAG,sBAAsB;AACtC,QAAM,QAAQ,eAAe;AAC7B,QAAM,SAAS,gBAAgB;AAC/B,SACE,KAAK,UAAU,CAAC,UAChB,KAAK,SAAS,CAAC,UACf,KAAK,OAAO,SAAS,UACrB,KAAK,QAAQ,QAAQ;AAEzB;AAQO,SAAS,iBAAiB;AAC/B,QAAM,OAAO,SAAS;AACtB,MAAI,KAAK,QAAQ,eAAe,OAAQ;AACxC,QAAM,IAAI,KAAK,MAAM,OAAO,WAAW,OAAO,eAAe,CAAC;AAC9D,OAAK,QAAQ,aAAa;AAC1B,OAAK,QAAQ,cAAc,OAAO,CAAC;AACnC,OAAK,MAAM,WAAW;AACtB,OAAK,MAAM,MAAM,IAAI,CAAC;AACtB,OAAK,MAAM,OAAO;AAClB,OAAK,MAAM,QAAQ;AACnB,OAAK,MAAM,QAAQ;AACrB;AAOO,SAAS,mBAAmB;AACjC,QAAM,OAAO,SAAS;AACtB,MAAI,KAAK,QAAQ,eAAe,OAAQ;AACxC,QAAM,IAAI,OAAO,KAAK,QAAQ,eAAe,CAAC;AAC9C,OAAK,MAAM,WAAW;AACtB,OAAK,MAAM,MAAM;AACjB,OAAK,MAAM,OAAO;AAClB,OAAK,MAAM,QAAQ;AACnB,OAAK,MAAM,QAAQ;AACnB,SAAO,KAAK,QAAQ;AACpB,SAAO,KAAK,QAAQ;AACpB,SAAO,SAAS,GAAG,CAAC;AACtB;;;AChGA,SAAsB,SAAS,KAAoB,WAAW,IAAI;AAAA;AAChE,QAAI,CAAC,IAAK;AAEV,QAAI,UAAU;AACd,QAAI,aAAa;AACjB,QAAI;AACF,UAAI,eAAe,MAAM;AAEvB,kBAAU,IAAI,gBAAgB,GAAG;AACjC,qBAAa;AAAA,MACf,WAAW,IAAI,SAAS,UAAU,GAAG;AAEnC,kBAAU;AAAA,MACZ,OAAO;AACL,YAAI,UAAU;AAEZ,gBAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,cAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,eAAe,IAAI,MAAM,SAAI,GAAG,EAAE;AAC/D,gBAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,oBAAU,IAAI,gBAAgB,IAAI;AAClC,uBAAa;AAAA,QACf,OAAO;AAEL,oBAAU;AAAA,QACZ;AAAA,MACF;AAKA,YAAM,IAAI,SAAS,cAAc,GAAG;AACpC,QAAE,OAAO;AACT,QAAE,WAAW;AACb,eAAS,KAAK,YAAY,CAAC;AAC3B,QAAE,MAAM;AACR,eAAS,KAAK,YAAY,CAAC;AAAA,IAC7B,UAAE;AACA,UAAI,YAAY;AACd,mBAAW,MAAM,IAAI,gBAAgB,OAAO,GAAG,GAAG;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA;AAWA,SAAsB,eAAe,KAA0B;AAAA;AAC7D,UAAM,EAAE,MAAM,SAAS,QAAQ,YAAY,OAAO,IAAI;AAEtD,QAAI,SAAS,OAAO,UAAU,IAAK,OAAM,IAAI,MAAM,GAAG,MAAM,SAAI,UAAU,SAAI,OAAO,GAAG,EAAE;AAG1F,QAAI,KAAK,KAAK,SAAS,kBAAkB,GAAG;AAC1C,YAAM,MAAM,MAAM,KAAK,KAAK;AAC5B,YAAM,KAAK,MAAM,GAAG;AAAA,IACtB;AAGA,UAAM,WAAW,uBAAuB,QAAQ,qBAAqB,CAAC;AACtE,WAAO,EAAE,MAAM,MAAM,SAAS;AAAA,EAChC;AAAA;AASO,SAAS,uBAAuB,aAAsB;AAtF7D;AAuFE,MAAI,CAAC,YAAa,QAAO;AAGzB,QAAM,UAAU,qCAAqC,KAAK,WAAW;AACrE,MAAI,mCAAU,IAAI;AAChB,QAAI;AACF,aAAO,mBAAmB,QAAQ,CAAC,EAAE,KAAK,CAAC,EAAE,QAAQ,YAAY,EAAE;AAAA,IACrE,SAAQ;AACN,aAAO,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,YAAY,EAAE;AAAA,IACjD;AAAA,EACF;AAGA,QAAM,MAAM,gDAAgD,KAAK,WAAW;AAC5E,MAAI,IAAK,UAAQ,SAAI,CAAC,MAAL,YAAU,IAAI,CAAC,GAAG,KAAK,EAAE,QAAQ,YAAY,EAAE;AAEhE,SAAO;AACT;AAUA,SAAsB,OACpB,KACA,OACA;AAAA;AACA,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,UAAI,MAAM,GAAG,EAAG,QAAO,QAAQ;AAE/B,YAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,aAAO,OAAO;AACd,aAAO,MAAM;AAEb,UAAI,OAAO;AACT,cAAM,OAAO,OAAO,KAAK,KAAK;AAC9B,aAAK,QAAQ,CAAC,QAAQ;AACpB,gBAAM,IAAI,MAAM,GAAG;AACnB,cAAI,MAAM,QAAQ,MAAM,UAAa,MAAM,MAAO;AAClD,iBAAO,aAAa,KAAK,OAAO,MAAM,YAAY,KAAK,CAAC;AAAA,QAC1D,CAAC;AAAA,MACH;AAEA,aAAO,SAAS,MAAM,QAAQ;AAC9B,aAAO,UAAU,CAAC,MAAM,OAAO,CAAC;AAEhC,eAAS,KAAK,YAAY,MAAM;AAAA,IAClC,CAAC;AAAA,EACH;AAAA;AAWO,SAAS,MAAM,KAAa;AACjC,QAAM,SAAS,IAAI,IAAI,KAAK,SAAS,OAAO,EAAE;AAC9C,QAAM,SAAS,MAAM,KAAK,SAAS,iBAAiB,aAAa,CAAC;AAClE,SAAO,OAAO,KAAK,CAAC,MAAM;AACxB,UAAMC,OAAM,EAAE,aAAa,KAAK;AAChC,WAAOA,QAAO,IAAI,IAAIA,MAAK,SAAS,OAAO,EAAE,SAAS;AAAA,EACxD,CAAC;AACH;AAUA,SAAsB,QACpB,MACA,OACA;AAAA;AACA,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,UAAI,OAAO,IAAI,EAAG,QAAO,QAAQ;AAEjC,YAAM,OAAO,SAAS,cAAc,MAAM;AAC1C,WAAK,MAAM;AACX,WAAK,OAAO;AAEZ,UAAI,OAAO;AACT,cAAM,OAAO,OAAO,KAAK,KAAK;AAC9B,aAAK,QAAQ,CAAC,QAAQ;AACpB,gBAAM,IAAI,MAAM,GAAG;AACnB,cAAI,MAAM,QAAQ,MAAM,OAAW;AACnC,eAAK,aAAa,KAAK,OAAO,CAAC,CAAC;AAAA,QAClC,CAAC;AAAA,MACH;AAEA,WAAK,SAAS,MAAM,QAAQ;AAC5B,WAAK,UAAU,CAAC,MAAM,OAAO,CAAC;AAE9B,eAAS,KAAK,YAAY,IAAI;AAAA,IAChC,CAAC;AAAA,EACH;AAAA;AASO,SAAS,OAAO,MAAc;AACnC,QAAM,SAAS,IAAI,IAAI,MAAM,SAAS,OAAO,EAAE;AAC/C,QAAM,OAAO,MAAM,KAAK,SAAS,iBAAiB,8BAA8B,CAAC;AACjF,SAAO,KAAK,KAAK,CAAC,MAAM;AACtB,UAAM,IAAI,EAAE,aAAa,MAAM;AAC/B,WAAO,KAAK,IAAI,IAAI,GAAG,SAAS,OAAO,EAAE,SAAS;AAAA,EACpD,CAAC;AACH;AASO,SAAS,aAAa,KAAa;AACxC,SAAO,IAAI,QAA0B,CAAC,SAAS,WAAW;AACxD,UAAM,MAAM,IAAI,MAAM;AACtB,QAAI,SAAS,MAAM,QAAQ,GAAG;AAC9B,QAAI,UAAU,CAAC,MAAM,OAAO,CAAC;AAC7B,QAAI,MAAM;AAAA,EACZ,CAAC;AACH;;;AChOA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOO,SAAS,UAAa,MAAW,WAAmB,SAAiB;AAC1E,QAAM,UAAU,CAAC,GAAG,IAAI;AACxB,QAAM,CAAC,OAAO,IAAI,QAAQ,OAAO,WAAW,CAAC;AAC7C,UAAQ,OAAO,SAAS,GAAG,OAAO;AAClC,SAAO;AACT;;;ACAA,SAAsB,QAAW,GAA6C;AAAA;AAC5E,QAAI;AACF,YAAM,OAAO,MAAM;AACnB,aAAO,CAAC,MAAM,IAAI;AAAA,IACpB,SAAS,KAAK;AACZ,aAAO,CAAC,MAAM,GAAG;AAAA,IACnB;AAAA,EACF;AAAA;;;ACnBA,OAAO,UAA4C;AAe5C,IAAM,WAAN,MAA0C;AAAA,EAA1C;AACL,wBAAiB,YAAuB,KAAQ;AAAA;AAAA;AAAA,EAGhD,GAAsB,MAAS,IAAiC;AAC9D,SAAK,SAAS,GAAG,MAAM,EAAE;AACzB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,KAAwB,MAAS,IAAiC;AAChE,UAAM,OAAO,CAAC,UAAgB;AAC5B,WAAK,SAAS,IAAI,MAAM,IAAI;AAC5B,SAAG,KAAK;AAAA,IACV;AACA,SAAK,SAAS,GAAG,MAAM,IAAI;AAC3B,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,KAAwB,MAAS,OAAoB;AACnD,SAAK,SAAS,KAAK,MAAM,KAAa;AACtC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAuB,MAAS,IAAkC;AAChE,SAAK,SAAS,IAAI,MAAM,EAAE;AAC1B,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,QAAc;AACZ,SAAK,SAAS,IAAI,MAAM;AACxB,WAAO;AAAA,EACT;AACF;AAYA,IAAO,mBAAQ,IAAI,SAAS;;;AC/D5B,OAAO,WAAW;AAClB,OAAO,uBAAuB;AAC9B,OAAO,SAAS;AAChB,OAAO,cAAc;AACrB,OAAO,kBAAkB;AACzB,OAAO,oBAAoB;AAC3B,OAAO;;;ACNP,OAAO,eAAe;AAgBtB,SAAS,IAAI,GAAuB;AAClC,SAAO,aAAa,YAAY,IAAI,IAAI,UAAU,CAAC;AACrD;AASO,SAAS,YAAY,MAAiB;AAC3C,MAAI,MAAM,IAAI,KAAK,CAAC,CAAC;AACrB,aAAW,KAAK,KAAK,MAAM,CAAC,EAAG,OAAM,IAAI,KAAK,IAAI,CAAC,CAAC;AACpD,SAAO,IAAI,SAAS;AACtB;AASO,SAAS,aAAa,MAAiB;AAC5C,MAAI,MAAM,IAAI,KAAK,CAAC,CAAC;AACrB,aAAW,KAAK,KAAK,MAAM,CAAC,EAAG,OAAM,IAAI,MAAM,IAAI,CAAC,CAAC;AACrD,SAAO,IAAI,SAAS;AACtB;AASO,SAAS,aAAa,MAAiB;AAC5C,MAAI,MAAM,IAAI,KAAK,CAAC,CAAC;AACrB,aAAW,KAAK,KAAK,MAAM,CAAC,EAAG,OAAM,IAAI,MAAM,IAAI,CAAC,CAAC;AACrD,SAAO,IAAI,SAAS;AACtB;AASO,SAAS,WAAW,MAAiB;AAC1C,MAAI,MAAM,IAAI,KAAK,CAAC,CAAC;AACrB,aAAW,KAAK,KAAK,MAAM,CAAC,EAAG,OAAM,IAAI,IAAI,IAAI,CAAC,CAAC;AACnD,SAAO,IAAI,SAAS;AACtB;AAWO,SAAS,QAAQ,GAAY,GAAY;AAC9C,SAAO,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC,EAAE,SAAS;AACrC;AAcO,SAAS,UACd,GACA,KAAK,GACL,KAA6B,UAAU,eACvC;AACA,SAAO,IAAI,CAAC,EAAE,cAAc,IAAI,EAAE,EAAE,SAAS;AAC/C;AAeO,SAAS,UACd,GACA,KAAK,GACL,KAA6B,UAAU,eAC/B;AACR,SAAO,IAAI,CAAC,EAAE,QAAQ,IAAI,EAAE;AAC9B;AASO,SAAS,YAAY,GAAY,GAA+B;AACrE,SAAO,IAAI,CAAC,EAAE,WAAW,IAAI,CAAC,CAAC;AACjC;AAQO,SAAS,UAAU,GAAY,GAAqB;AACzD,SAAO,IAAI,CAAC,EAAE,UAAU,IAAI,CAAC,CAAC;AAChC;AAQO,SAAS,gBAAgB,GAAY,GAAqB;AAC/D,SAAO,IAAI,CAAC,EAAE,cAAc,IAAI,CAAC,CAAC;AACpC;AAQO,SAAS,uBAAuB,GAAY,GAAqB;AACtE,SAAO,IAAI,CAAC,EAAE,uBAAuB,IAAI,CAAC,CAAC;AAC7C;AAQO,SAAS,aAAa,GAAY,GAAqB;AAC5D,SAAO,IAAI,CAAC,EAAE,WAAW,IAAI,CAAC,CAAC;AACjC;AASO,SAAS,oBAAoB,GAAY,GAAqB;AACnE,SAAO,IAAI,CAAC,EAAE,oBAAoB,IAAI,CAAC,CAAC;AAC1C;;;AC9KO,SAAS,QAAQ,GAAoB,MAAM,GAAG;AACnD,SAAO,OAAO,CAAC,EAAE,SAAS,KAAK,GAAG;AACpC;AAcO,SAAS,SAAS,KAAuB,OAAO,IAAI;AACzD,MAAI,QAAQ,QAAQ,QAAQ,UAAa,QAAQ,GAAI,QAAO;AAE5D,MAAI,OAAO,QAAQ,SAAU,QAAO,GAAG,GAAG,GAAG,IAAI;AAEjD,QAAM,MAAM,OAAO,GAAG,EAAE,KAAK;AAE7B,MAAI,QAAQ,GAAI,QAAO;AAEvB,SAAO,MAAM,CAAC,GAAG,IAAI,MAAM,GAAG,GAAG,GAAG,IAAI;AAC1C;AAaO,SAAS,WAAW,KAAuB;AAChD,SAAO,SAAS,KAAK,IAAI;AAC3B;AAWO,SAAS,aAAa,GAA4B;AACvD,QAAM,IAAI,OAAO,gBAAK,CAAC;AACvB,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,MAAO,GAAG,EAAE,IAAI,KAAM,QAAQ,CAAC,CAAC,OAAO,GAAG,CAAC,EAAE,QAAQ,CAAC,CAAC;AACrE;AASO,SAAS,aAAa,KAA8B;AACzD,QAAM,IAAI,OAAO,oBAAO,EAAE,EAAE,KAAK;AACjC,MAAI,MAAM,GAAI,QAAO;AAGrB,MAAI,OAAO;AACX,MAAI,MAAM;AACV,MAAI,IAAI,CAAC,MAAM,OAAO,IAAI,CAAC,MAAM,KAAK;AACpC,WAAO,IAAI,CAAC;AACZ,UAAM,IAAI,MAAM,CAAC;AAAA,EACnB;AAGA,QAAM,CAAC,SAAS,OAAO,IAAI,IAAI,MAAM,GAAG;AAGxC,QAAM,aAAa,QAAQ,QAAQ,yBAAyB,GAAG;AAE/D,SAAO,YAAY,UAAa,YAAY,KACxC,GAAG,IAAI,GAAG,UAAU,IAAI,OAAO,KAC/B,GAAG,IAAI,GAAG,UAAU;AAC1B;AAWO,SAAS,aAAa,KAAsB;AACjD,QAAM,SAAS,KAAK,MAAM,CAAC,GAAG;AAC9B,MAAI,WAAW,EAAG,QAAO;AAEzB,QAAM,QAAQ,CAAC,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,QAAG;AAC/D,QAAM,OAAO,CAAC,IAAI,UAAK,UAAK,QAAG;AAC/B,QAAM,UAAU,CAAC,IAAI,UAAK,UAAK,QAAG;AAElC,QAAM,WAAW,CAACC,OAAc;AAC9B,QAAI,MAAM,IACR,WAAW;AACb,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,IAAIA,KAAI;AACd,MAAAA,KAAI,KAAK,MAAMA,KAAI,EAAE;AACrB,UAAI,MAAM,GAAG;AACX,mBAAW;AACX;AAAA,MACF;AACA,UAAI,SAAU,OAAM,MAAM,CAAC,IAAI;AAC/B,YAAM,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI;AAC3B,iBAAW;AAAA,IACb;AACA,WAAO;AAAA,EACT;AAEA,MAAI,MAAM;AACV,MAAI,eAAe;AACnB,MAAI,IAAI,KAAK,IAAI,MAAM;AAEvB,SAAO,IAAI,GAAG;AACZ,UAAM,MAAM,IAAI;AAChB,QAAI,KAAK,MAAM,IAAI,GAAK;AACxB,QAAI,KAAK;AACP,YAAM,SAAS,SAAS,GAAG;AAC3B,YAAM,UAAU,eAAe,QAAQ,YAAY,IAAI,MAAM;AAAA,IAC/D,WAAW,OAAO,CAAC,IAAI,WAAW,QAAG,GAAG;AACtC,YAAM,SAAI,GAAG;AAAA,IACf;AACA;AAAA,EACF;AAEA,QAAM,IAAI,QAAQ,OAAO,QAAG;AAE5B,SAAO,SAAS,IAAI,SAAI,GAAG,KAAK;AAClC;AAmBO,SAAS,kBACd,QACA,OAII,CAAC,GACG;AAnLV;AAoLE,QAAM,MAAK,UAAK,cAAL,YAAkB;AAC7B,QAAM,MAAK,UAAK,OAAL,YAAW,UAAU;AAChC,QAAM,QAAO,UAAK,aAAL,YAAiB;AAG9B,MAAI,WAAW,QAAQ,WAAW,OAAW,QAAO;AACpD,QAAM,KAAK,IAAI,UAAU,MAAM;AAC/B,MAAI,CAAC,GAAG,SAAS,EAAG,QAAO;AAG3B,QAAM,IAAI,GAAG,QAAQ,IAAI,EAAE;AAC3B,QAAM,OAAO,EAAE,WAAW,GAAG,IAAI,WAAM;AACvC,QAAM,CAAC,QAAQ,SAAS,EAAE,IAAI,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG;AAG3D,QAAM,QAAQ,CAAC,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,QAAG;AAC/D,QAAM,OAAO,CAAC,IAAI,UAAK,UAAK,QAAG;AAC/B,QAAM,UAAU,CAAC,IAAI,UAAK,UAAK,QAAG;AAClC,QAAM,YAAY,CAAC,UAAK,UAAK,QAAG;AAGhC,QAAM,WAAW,CAAC,MAAyB;AACzC,QAAI,MAAM;AACV,QAAI,WAAW;AACf,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,YAAM,IAAI,EAAE,IAAI,EAAE,EAAE,SAAS;AAC7B,UAAI,EAAE,KAAK,EAAE;AACb,UAAI,MAAM,GAAG;AACX,mBAAW;AACX;AAAA,MACF;AACA,UAAI,SAAU,OAAM,MAAM,CAAC,IAAI;AAC/B,YAAM,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI;AAC3B,iBAAW;AAAA,IACb;AACA,WAAO,IAAI,QAAQ,QAAQ,EAAE;AAAA,EAC/B;AAEA,QAAM,SAAS,IAAI,UAAU,MAAM;AACnC,MAAI,MAAM;AACV,MAAI,OAAO,OAAO,GAAG;AACnB,UAAM,MAAM,CAAC;AAAA,EACf,OAAO;AACL,QAAI,IAAI,OAAO,IAAI;AACnB,QAAI,eAAe;AACnB,WAAO,EAAE,GAAG,CAAC,GAAG;AACd,YAAM,MAAM,EAAE,IAAI,GAAK;AACvB,UAAI,EAAE,KAAK,GAAK;AAChB,UAAI,IAAI,GAAG,CAAC,GAAG;AACb,cAAM,SAAS,SAAS,GAAG;AAC3B,cAAM,WACJ,OAAO,CAAC,IAAI,WAAW,MAAM,CAAC,CAAC,MAAM,IAAI,GAAG,GAAI,KAAK,IAAI,IAAI,GAAI,EAAE,OAAO;AAC5E,cAAM,KAAK,eAAe,QAAQ,YAAY,IAAI;AAClD,cAAM,SAAS,MAAM,WAAW,MAAM,CAAC,IAAI,MAAM;AAAA,MACnD,WAAW,OAAO,CAAC,IAAI,WAAW,MAAM,CAAC,CAAC,GAAG;AAC3C,cAAM,MAAM,CAAC,IAAI;AAAA,MACnB;AACA;AAAA,IACF;AACA,UAAM,IAAI,QAAQ,OAAO,QAAG;AAAA,EAC9B;AAGA,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,KAAK,IAAI,GAAG,EAAE,GAAG,KAAK;AACxC,UAAM,KAAK,OAAO,CAAC,KAAK;AACxB,UAAM,IAAI,GAAG,WAAW,CAAC,IAAI;AAC7B,QAAI,IAAI,EAAG,SAAQ,MAAM,CAAC,IAAI,UAAU,CAAC;AAAA,EAC3C;AAGA,SAAO,OAAO,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,KAAK,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI;AACpE;;;ACtPO,SAAS,gBAAyB;AACvC,SAAO,KAAK,OAAO,IAAI;AACzB;;;AHCA,MAAM,OAAO,iBAAiB;AAC9B,MAAM,OAAO,GAAG;AAChB,MAAM,OAAO,QAAQ;AACrB,MAAM,OAAO,YAAY;AACzB,MAAM,OAAO,cAAc;AAC3B,MAAM,OAAO,OAAO;AAsCb,SAAS,QAAQ,GAAc,KAAwB;AAC5D,MAAI,MAAM,QAAQ,MAAM,OAAW,QAAO,MAAM;AAChD,MAAI,OAAO,MAAM,UAAU;AACzB,UAAM,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC;AAC9B,WAAO,MAAM,EAAE,WAAW,KAAK,IAAI,MAAO,GAAG,GAAG;AAAA,EAClD;AACA,MAAI,OAAO,MAAM,UAAU;AACzB,UAAM,IAAI,EAAE,KAAK;AACjB,QAAI,WAAW,KAAK,CAAC,EAAG,QAAO,MAAM,OAAO,CAAC,IAAI,KAAM,GAAG;AAC1D,QAAI,WAAW,KAAK,CAAC,EAAG,QAAO,MAAM,OAAO,CAAC,GAAG,GAAG;AACnD,QAAI,sBAAsB,KAAK,CAAC,EAAG,QAAO,MAAM,GAAG,OAAO,YAAY;AACtE,QAAI,wBAAwB,KAAK,CAAC,EAAG,QAAO,MAAM,GAAG,OAAO,YAAY;AACxE,QAAI,0CAA0C,KAAK,CAAC;AAClD,aAAO,MAAM,GAAG,OAAO,qBAAqB;AAC9C,QAAI,4CAA4C,KAAK,CAAC;AACpD,aAAO,MAAM,GAAG,OAAO,qBAAqB;AAC9C,WAAO,MAAM,GAAG,GAAG;AAAA,EACrB;AACA,SAAO,MAAM,GAAG,GAAG;AACrB;AAYO,SAAS,mBAAmB,QAAgB,MAAM,cAAc;AACrE,QAAM,MAAM,QAAQ,KAAK,IAAI,CAAC;AAC9B,QAAM,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC;AACxC,QAAM,UAAU,oBAAoB,KAAK,GAAG;AAC5C,QAAM,WAAW,IAAI,IAAI,CAAC,GAAG,KAAK;AAClC,QAAM,SAAS;AACf,QAAM,SAAS,UAAU,SAAS,QAAQ,KAAK,IAAI,UAAU,OAAO,GAAG;AACvE,QAAM,OAAO,UAAU,OAAO,MAAM,KAAK,IAAI,QAAQ,OAAO,GAAG;AAC/D,SAAO,CAAC,OAAO,GAAG;AACpB;AAcO,SAAS,kBAAkB,QAAgB,MAAM,cAAc;AACpE,QAAM,MAAM,QAAQ,KAAK,IAAI,CAAC;AAC9B,QAAM,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,MAAM,CAAC;AACxC,QAAM,UAAU,oBAAoB,KAAK,GAAG;AAC5C,QAAM,WAAW;AACjB,QAAM,SAAS,IAAI,IAAI,GAAG,KAAK;AAC/B,QAAM,SAAS,UAAU,SAAS,QAAQ,KAAK,IAAI,UAAU,OAAO,GAAG;AACvE,QAAM,OAAO,UAAU,OAAO,MAAM,KAAK,IAAI,QAAQ,OAAO,GAAG;AAC/D,SAAO,CAAC,OAAO,GAAG;AACpB;AAUO,SAAS,kBAAkB,MAAc;AAC9C,MAAI,QAAQ,EAAG,QAAO,EAAE,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,IAAI,MAAM;AAEtE,QAAM,IAAI,KAAK,MAAM,QAAQ,MAAO,KAAK,KAAK,GAAG;AACjD,QAAM,IAAI,KAAK,MAAO,QAAQ,MAAO,KAAK,MAAO,EAAE;AACnD,QAAM,IAAI,KAAK,MAAO,QAAQ,MAAO,MAAO,EAAE;AAC9C,QAAM,IAAI,KAAK,MAAO,OAAO,MAAQ,EAAE;AACvC,QAAM,KAAK,OAAO;AAElB,SAAO;AAAA,IACL,GAAG,QAAQ,CAAC;AAAA,IACZ,GAAG,QAAQ,CAAC;AAAA,IACZ,GAAG,QAAQ,CAAC;AAAA,IACZ,GAAG,QAAQ,CAAC;AAAA,IACZ,IAAI,QAAQ,IAAI,CAAC;AAAA,EACnB;AACF;AAYO,SAAS,kBAAkB,WAAmB;AACnD,QAAM,QAAQ,QAAQ,WAAW,YAAY;AAC7C,QAAM,MAAM,QAAQ,KAAK,IAAI,CAAC;AAG9B,QAAM,eAAe,IAAI,KAAK,IAAI,MAAM,KAAK,KAAK,MAAM,IAAI,MAAM,IAAI,MAAM,MAAM;AAGlF,QAAM,iBAAiB,IAAI,KAAK,IAAI,MAAM,KAAK,IAAI,cAAc,IAAI;AAErE,MAAI,kBAAkB,IAAI;AACxB,QAAI,MAAM,KAAK,MAAM,iBAAiB,EAAE;AAExC,UAAM,mBAAmB,MAAM,IAAI,KAAK,MAAM;AAC9C,QAAI,IAAI,SAAS,gBAAgB,GAAG;AAClC;AAAA,IACF;AACA,WAAO,EAAE,KAAK,MAAM,OAAO;AAAA,EAC7B;AAEA,SAAO,EAAE,KAAK,gBAAgB,MAAM,QAAQ;AAC9C;;;AI9KA;AAKA;AAAA,iCAAc;;;AREd,uBAAc;;;ASPd,SAAS,KAAK,WAAW;AAWlB,SAAS,cAAgC,KAAwB;AACtE,SAAO,OAAO,KAAK,GAAG;AACxB;AAaO,IAAM,iBAAiB;AAYvB,IAAM,iBAAiB;;;AC1BvB,SAAS,WAAW,GAAW,WAAW,GAAG,YAAY,GAAG,WAAW,KAAK;AACjF,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,IAAI,OAAO,CAAC;AAClB,QAAM,IAAI,KAAK,IAAI,GAAG,QAAQ;AAC9B,QAAM,IAAI,KAAK,IAAI,GAAG,SAAS;AAC/B,QAAM,MAAM,EAAE;AACd,QAAM,OAAO,KAAK,IAAI,GAAG,GAAG;AAC5B,QAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI;AACpC,QAAM,MAAM,MAAM,OAAO;AACzB,MAAI,OAAO,EAAG,QAAO;AACrB,QAAM,IAAI,YAAY,SAAS,SAAS,IAAI,WAAW;AACvD,SAAO,EAAE,MAAM,GAAG,IAAI,IAAI,EAAE,OAAO,GAAG,IAAI,EAAE,MAAM,MAAM,KAAK;AAC/D;AASO,SAAS,YAAY,OAAe;AACzC,SAAO,WAAW,OAAO,GAAG,CAAC;AAC/B;AAYO,SAAS,WAAW,MAAc;AACvC,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,IAAI,OAAO,IAAI;AACrB,SAAO,EAAE,UAAU,IAAI,WAAW,GAAG,GAAG,CAAC,IAAI,WAAW,GAAG,GAAG,CAAC;AACjE;;;AC7CO,SAAS,aAAa;AAC3B,SAAO,uCAAuC,QAAQ,SAAS,SAAU,GAAG;AAC1E,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM,GAC/B,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AAClC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;AAWO,SAAS,iBAAiB,SAAS,OAAO;AAC/C,SAAO,GAAG,MAAM,GAAG,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE,CAAC;AAChE;AAUO,SAAS,iBAAiB,SAAiB,GAAG;AACnD,QAAM,OAAO,UAAO,SAAS;AAC7B,QAAM,QAAQ,IAAI;AAClB,QAAM,MAAM,KAAK,MAAM,KAAK,OAAO,IAAI,KAAK,IAAI;AAEhD,SAAO,GAAG,KAAK,IAAI,CAAC,GAAG,GAAG;AAC5B;;;AC1BO,SAAS,cAAc,MAAoE;AAChG,MAAI,OAAO,SAAS,UAAU;AAC5B,QAAI,UAAU;AAEd,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,YAAM,OAAO,KAAK,WAAW,CAAC;AAE9B,UAAI,QAAQ,KAAM;AAChB,mBAAW;AAAA,MACb,WAAW,QAAQ,MAAO;AACxB,mBAAW;AAAA,MACb,WAAW,QAAQ,SAAU,QAAQ,OAAQ;AAC3C,mBAAW;AACX;AAAA,MACF,OAAO;AACL,mBAAW;AAAA,MACb;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAGA,MAAI,gBAAgB,KAAM,QAAO,KAAK;AAGtC,MAAI,UAAU,KAAM,QAAO,KAAK;AAEhC,QAAM,IAAI,UAAU,iCAAiC;AACvD;;;ACrCA,IAAM,YAAY;AAAA,EAChB,KAAK,CAAC,OAAO,OAAO,QAAQ,OAAO,OAAO,MAAM;AAAA,EAChD,OAAO,CAAC,OAAO,OAAO,KAAK;AAAA,EAC3B,OAAO,CAAC,OAAO,OAAO,KAAK;AAAA,EAC3B,OAAO,CAAC,OAAO,OAAO,QAAQ,QAAQ,KAAK;AAAA,EAC3C,MAAM,CAAC,OAAO,OAAO,QAAQ,OAAO,MAAM,KAAK;AAAA,EAC/C,KAAK,CAAC,OAAO,MAAM,OAAO,OAAO,IAAI;AAAA,EACrC,KAAK,CAAC,OAAO,QAAQ,KAAK;AAAA,EAC1B,KAAK,CAAC,OAAO,KAAK;AACpB;AAYO,SAAS,cAAc,UAAkB;AAC9C,MAAI,SAAS,WAAW,GAAG,EAAG,QAAO;AAErC,QAAM,MAAM,SAAS,YAAY,GAAG;AACpC,SAAO,MAAM,IAAI,SAAS,MAAM,MAAM,CAAC,EAAE,YAAY,IAAI;AAC3D;AAWO,SAAS,YAAY,UAAkB;AAC5C,QAAM,SAAS,cAAc,QAAQ;AACrC,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,OAAO,cAAc,SAAS;AACpC,aAAW,OAAO,MAAM;AACtB,QAAI,UAAU,GAAG,EAAE,SAAS,MAAM,GAAG;AACnC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;;;ACnBO,SAAS,UAAU,KAAa,QAAsB;AAC3D,SAAO,YAAY,KAAK,SAAS,MAAM;AACzC;AAiBO,SAAS,YAAY,KAAa,QAAwB;AAC/D,SAAO,YAAY,KAAK,SAAS,MAAM;AACzC;AAaO,SAAS,YAAY,KAAa,QAAwB;AAC/D,SAAO,YAAY,KAAK,SAAS,MAAM;AACzC;AAYO,SAAS,UAAU,KAAa,QAAsB;AAC3D,SAAO,YAAY,KAAK,OAAO,MAAM;AACvC;AASO,SAAS,YAAY,KAAa,MAAc,QAAmB;AACxE,MAAI,CAAC,OAAO,CAAC,OAAQ,QAAO;AAC5B,MAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AACpC,MAAI,IAAI,SAAS,MAAM,EAAG,QAAO;AAEjC,QAAM,OAAiB,CAAC;AAGxB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,UAAM,MAAM,MAAM,cAAc,aAAa,CAAuB,IAAI,WAAW,GAAG,CAAC;AACvF,QAAI,IAAK,MAAK,KAAK,GAAG;AAAA,EACxB;AAEA,MAAI,CAAC,KAAK,OAAQ,QAAO;AAGzB,QAAM,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AAC7B,SAAO,GAAG,IAAI,kBAAkB,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC;AACxD;AAWA,SAAS,WAAW,MAAc,QAA8D;AAC9F,MAAI,CAAC,UAAU,WAAW,EAAG,QAAO;AAEpC,MAAI,WAAW,KAAM,QAAO;AAE5B,MAAI,OAAO,WAAW,YAAY,OAAO,WAAW,SAAU,QAAO,GAAG,IAAI,IAAI,MAAM;AAEtF,QAAM,OAAO,OAAO,QAAQ,MAAM,EAC/B,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,EAC3B,KAAK,GAAG;AAEX,SAAO,OAAO,GAAG,IAAI,IAAI,IAAI,KAAK;AACpC;AAMA,SAAS,aAAa,GAAwB;AAC5C,MAAI,CAAC,EAAG,QAAO;AACf,MAAI,EAAE,MAAO,GAAE,QAAQ,YAAY,EAAE,KAAK;AAC1C,MAAI,EAAE,KAAM,GAAE,OAAO,YAAY,EAAE,IAAI;AACvC,MAAI,EAAE,KAAM,GAAE,OAAO,YAAY,EAAE,IAAI;AACvC,SAAO,WAAW,aAAa,CAAC;AAClC;AAKA,SAAS,YAAY,GAAW;AAC9B,MAAI,MAAM;AACV,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,MAAM,OAAO,KAAK,GAAG,OAAO;AAClC,UAAM,IAAI,SAAS,QAAQ;AAAA,EAC7B,OAAO;AACL,QAAI;AACF,YAAM,KAAK,SAAS,mBAAmB,CAAC,CAAC,CAAC;AAAA,IAC5C,SAAQ;AACN,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO,IAAI,QAAQ,QAAQ,EAAE,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG;AACvE;;;AC9JO,SAAS,eAAe,KAAa,OAAgC;AAC1E,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAEhD,QAAM,YAAY,IAAI,QAAQ,GAAG;AACjC,QAAM,kBAAkB,aAAa,IAAI,IAAI,MAAM,GAAG,SAAS,IAAI;AACnE,QAAM,OAAO,aAAa,IAAI,IAAI,MAAM,SAAS,IAAI;AAErD,QAAM,CAAC,MAAM,UAAU,IAAI,gBAAgB,MAAM,GAAG;AACpD,QAAM,QAAkB,CAAC;AACzB,MAAI,WAAY,OAAM,KAAK,UAAU;AACrC,aAAW,OAAO,OAAO;AACvB,UAAM,SAAS,MAAM,GAAG;AACxB,QAAI,WAAW,QAAQ,WAAW,OAAW;AAC7C,UAAM,MAAM,OAAO,WAAW,WAAW,KAAK,UAAU,MAAM,IAAI,OAAO,MAAM;AAC/E,UAAM,KAAK,GAAG,mBAAmB,GAAG,CAAC,IAAI,mBAAmB,GAAG,CAAC,EAAE;AAAA,EACpE;AACA,QAAM,KAAK,MAAM,OAAO,OAAO,EAAE,KAAK,GAAG;AACzC,SAAO,QAAQ,KAAK,IAAI,EAAE,KAAK,MAAM;AACvC;;;ACAO,SAAS,SAAS,KAAa,QAAqB;AA3B3D;AA4BE,MAAI,CAAC,OAAO,CAAC,OAAQ,QAAO;AAC5B,MAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AACpC,MAAI,IAAI,SAAS,MAAM,EAAG,QAAO;AAEjC,QAAM,OAAiB,CAAC;AAExB,MAAI,OAAO,UAAW,MAAK,KAAK,WAAW;AAE3C,MAAI,OAAO,WAAY,MAAK,KAAK,cAAc,OAAO,UAAU,CAAC;AAEjE,QAAM,OAAO,eAAc,YAAO,eAAP,YAAqB,MAAM;AACtD,MAAI,KAAM,MAAK,KAAK,IAAI;AAExB,MAAI,OAAO,UAAW,MAAK,KAAKC,cAAa,OAAO,SAAS,CAAC;AAE9D,MAAI,OAAO,UAAW,MAAK,KAAK,WAAW;AAE3C,MAAI,CAAC,KAAK,OAAQ,QAAO;AAEzB,QAAM,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AAC7B,SAAO,GAAG,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC;AAClC;AAYO,SAAS,WAAW,KAAa,QAAmC;AACzE,MAAI,CAAC,OAAO,CAAC,OAAQ,QAAO;AAC5B,MAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AACpC,MAAI,IAAI,SAAS,MAAM,EAAG,QAAO;AACjC,QAAM,OAAiB,CAAC;AACxB,MAAI,OAAO,QAAS,MAAK,KAAK,WAAW,OAAO,OAAO,CAAC;AACxD,MAAI,OAAO,OAAQ,MAAK,KAAK,UAAU,OAAO,MAAM,CAAC;AACrD,MAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,QAAM,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AAC7B,SAAO,GAAG,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC;AAClC;AAWO,SAAS,WAAW,KAAa,QAAmC;AACzE,MAAI,CAAC,OAAO,CAAC,OAAQ,QAAO;AAC5B,MAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AACpC,QAAM,OAAiB,CAAC;AACxB,MAAI,OAAO,QAAS,MAAK,KAAK,WAAW,OAAO,OAAO,CAAC;AACxD,MAAI,CAAC,KAAK,OAAQ,QAAO;AACzB,QAAM,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AAC7B,SAAO,GAAG,IAAI,IAAI,KAAK,KAAK,GAAG,CAAC;AAClC;AAWO,SAAS,SAAS,KAAa,QAAiC;AACrE,MAAI,CAAC,OAAO,CAAC,OAAQ,QAAO;AAC5B,MAAI,IAAI,WAAW,OAAO,EAAG,QAAO;AACpC,QAAM,MAAM,SAAS,MAAM;AAC3B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,OAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AAC7B,SAAO,GAAG,IAAI,IAAI,GAAG;AACvB;AAEA,SAAS,cAAc,KAA0B;AAC/C,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,OAAO,OAAO,IAAI,SAAS,WAAW,IAAI,OAAO;AACvD,QAAM,KAAe,CAAC;AACtB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,GAAG;AACxC,QAAI,MAAM,OAAQ;AAClB,QAAI,OAAO,MAAM,WAAW;AAC1B,UAAI,EAAG,IAAG,KAAK,GAAG,CAAC,IAAI;AAAA,IACzB,WAAW,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;AACzD,SAAG,KAAK,GAAG,CAAC,IAAI,CAAC,EAAE;AAAA,IACrB;AAAA,EACF;AACA,SAAO,GAAG,SAAS,cAAc,IAAI,IAAI,GAAG,KAAK,GAAG,CAAC,KAAK,cAAc,IAAI;AAC9E;AAEA,SAAS,cAAc,KAAmC;AACxD,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAM,IAAsB;AAClC,MAAI,OAAO,OAAO,YAAa,OAAM,KAAK,aAAa,EAAE,EAAE;AAE3D,QAAM,KAAM,IAAsB;AAClC,MAAI,OAAO,OAAO,YAAa,OAAM,KAAK,QAAQ,EAAE,EAAE;AAEtD,QAAM,MAAO,IAAsB;AACnC,MAAI,OAAO,QAAQ,SAAU,OAAM,KAAK,UAAU,GAAG,EAAE;AAEvD,QAAM,KAAM,IAAsB,aAAa;AAC/C,MAAI,GAAI,OAAM,KAAK,aAAa;AAEhC,QAAM,MAAO,IAAsB;AACnC,MAAI,OAAO,QAAQ,SAAU,OAAM,KAAK,UAAU,GAAG,EAAE;AAEvD,QAAM,KAAM,IAAsB;AAClC,MAAI,OAAO,KAAK,OAAO,EAAG,OAAM,KAAK,aAAa,EAAE,EAAE;AAEtD,QAAM,KAAM,IAAsB;AAClC,MAAI,OAAO,OAAO,SAAU,OAAM,KAAK,cAAc,EAAE,EAAE;AAEzD,QAAM,IAAK,IAAsB;AACjC,MAAI,OAAO,MAAM,SAAU,OAAM,KAAK,KAAK,CAAC,EAAE;AAE9C,QAAM,OAAQ,IAAsB;AACpC,MAAI,OAAO,SAAS,aAAa;AAC/B,QAAI,OAAO,SAAS,SAAU,OAAM,KAAK,QAAQ,IAAI,EAAE;AAAA,QAClD,OAAM,KAAK,QAAQ,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE;AAAA,EAC5C;AAEA,QAAM,SAAU,IAAsB;AACtC,MAAI,OAAO,WAAW,SAAU,OAAM,KAAK,UAAU,MAAM,EAAE;AAE7D,SAAO,MAAM,SAAS,cAAc,MAAM,KAAK,GAAG,CAAC,KAAK;AAC1D;AAEA,SAASA,cAAa,GAAuB;AAC3C,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,OACJ,EAAE,SAAS,UAAU,IAAI,EAAE,SAAS,SAAS,IAAI,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO;AACzF,QAAM,OAAiB,CAAC,aAAa,IAAI,EAAE;AAC3C,MAAI,SAAS,KAAK,EAAE,MAAO,MAAK,KAAK,SAASC,aAAY,EAAE,KAAK,CAAC,EAAE;AACpE,MAAI,SAAS,KAAK,EAAE,KAAM,MAAK,KAAK,QAAQA,aAAY,EAAE,IAAI,CAAC,EAAE;AACjE,MAAI,EAAE,KAAM,MAAK,KAAK,QAAQA,aAAY,EAAE,IAAI,CAAC,EAAE;AACnD,MAAI,OAAO,EAAE,aAAa,SAAU,MAAK,KAAK,YAAY,EAAE,QAAQ,EAAE;AACtE,MAAI,EAAE,KAAM,MAAK,KAAK,QAAQA,aAAY,EAAE,IAAI,CAAC,EAAE;AACnD,MAAI,EAAE,QAAS,MAAK,KAAK,WAAW,EAAE,OAAO,EAAE;AAC/C,MAAI,OAAO,EAAE,OAAO,SAAU,MAAK,KAAK,MAAM,EAAE,EAAE,EAAE;AACpD,MAAI,OAAO,EAAE,OAAO,SAAU,MAAK,KAAK,MAAM,EAAE,EAAE,EAAE;AACpD,MAAI,OAAO,EAAE,aAAa,SAAU,MAAK,KAAK,YAAY,EAAE,QAAQ,EAAE;AACtE,SAAO,KAAK,KAAK,GAAG;AACtB;AAEA,SAASA,aAAY,GAAW;AAC9B,MAAI,MAAM;AACV,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,MAAM,OAAO,KAAK,GAAG,OAAO;AAClC,UAAM,IAAI,SAAS,QAAQ;AAAA,EAC7B,OAAO;AACL,QAAI;AACF,YAAM,KAAK,SAAS,mBAAmB,CAAC,CAAC,CAAC;AAAA,IAC5C,SAAQ;AACN,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO,IAAI,QAAQ,QAAQ,EAAE,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG;AACvE;AAEA,SAAS,WAAW,KAAkC;AACpD,QAAM,QAAkB,CAAC;AACzB,MAAI,IAAI,OAAQ,OAAM,KAAK,WAAW,IAAI,MAAM,EAAE;AAAA,MAC7C,OAAM,KAAK,SAAS;AACzB,MAAI,IAAI,EAAG,OAAM,KAAK,KAAK,IAAI,CAAC,EAAE;AAClC,MAAI,IAAI,OAAQ,OAAM,KAAK,UAAU,IAAI,MAAM,EAAE;AACjD,MAAI,OAAO,IAAI,OAAO,YAAa,OAAM,KAAK,MAAM,IAAI,EAAE,EAAE;AAC5D,MAAI,OAAO,IAAI,MAAM,SAAU,OAAM,KAAK,KAAK,IAAI,CAAC,EAAE;AACtD,MAAI,OAAO,IAAI,OAAO,YAAa,OAAM,KAAK,MAAM,IAAI,EAAE,EAAE;AAC5D,MAAI,OAAO,IAAI,OAAO,SAAU,OAAM,KAAK,MAAM,IAAI,EAAE,EAAE;AACzD,MAAI,IAAI,OAAQ,OAAM,KAAK,UAAU,IAAI,MAAM,EAAE;AACjD,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,UAAU,KAAiC;AAClD,QAAM,QAAkB,CAAC;AACzB,QAAM,KAAK,UAAU,IAAI,UAAU,KAAK,EAAE;AAC1C,MAAI,OAAO,IAAI,WAAW,SAAU,OAAM,KAAK,UAAU,IAAI,MAAM,EAAE;AACrE,MAAI,OAAO,IAAI,MAAM,SAAU,OAAM,KAAK,KAAK,IAAI,CAAC,EAAE;AACtD,MAAI,OAAO,IAAI,MAAM,SAAU,OAAM,KAAK,KAAK,IAAI,CAAC,EAAE;AACtD,SAAO,MAAM,KAAK,GAAG;AACvB;AAEA,SAAS,SAAS,KAA8B;AAC9C,QAAM,QAAkB,CAAC;AACzB,QAAM,QAAQ,OAAO,IAAI,UAAU,WAAW,IAAI,IAAI,KAAK,KAAK;AAChE,QAAM,KAAK,QAAQ,KAAK,EAAE;AAC1B,QAAM,KAAK,UAAU,IAAI,UAAU,MAAM,EAAE;AAC3C,MAAI,OAAO,IAAI,YAAY,SAAU,OAAM,KAAK,WAAW,IAAI,OAAO,EAAE;AACxE,MAAI,OAAO,IAAI,MAAM,SAAU,OAAM,KAAK,KAAK,IAAI,CAAC,EAAE;AACtD,MAAI,IAAI,OAAQ,OAAM,KAAK,UAAU,IAAI,MAAM,EAAE;AACjD,MAAI,OAAO,IAAI,OAAO,YAAa,OAAM,KAAK,MAAM,IAAI,EAAE,EAAE;AAC5D,MAAI,OAAO,IAAI,MAAM,SAAU,OAAM,KAAK,KAAK,IAAI,CAAC,EAAE;AACtD,MAAI,OAAO,IAAI,MAAM,SAAU,OAAM,KAAK,KAAK,IAAI,CAAC,EAAE;AACtD,MAAI,IAAI,OAAQ,OAAM,KAAK,UAAU,IAAI,MAAM,EAAE;AACjD,MAAI,OAAO,IAAI,OAAO,YAAa,OAAM,KAAK,MAAM,IAAI,EAAE,EAAE;AAC5D,MAAI,OAAO,IAAI,OAAO,SAAU,OAAM,KAAK,MAAM,IAAI,EAAE,EAAE;AACzD,MAAI,OAAO,IAAI,WAAW,SAAU,OAAM,KAAK,UAAUA,aAAY,IAAI,MAAM,CAAC,EAAE;AAClF,SAAO,MAAM,KAAK,GAAG;AACvB;;;ACnOO,SAAS,SAAS,GAAW;AAClC,SAAO,cAAc,KAAK,CAAC;AAC7B;AASO,SAAS,UAAU,GAAW;AACnC,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,SAAO,qBAAqB,KAAK,CAAC;AACpC;AAYO,SAAS,SAAS,GAAW;AAClC,SAAO,WAAW,KAAK,CAAC;AAC1B;AAoBO,SAAS,UACd,OACA,SAMS;AACT,QAAM,EAAE,WAAW,OAAO,UAAU,GAAG,YAAY,OAAO,WAAW,MAAM,IAAI,WAAW,CAAC;AAE3F,MAAI,UAAU,QAAQ,UAAU,UAAa,UAAU,GAAI,QAAO;AAClE,QAAM,MAAM,OAAO,KAAK,EAAE,KAAK;AAE/B,QAAM,OAAO,YAAY,IAAI,WAAW,GAAG,IAAI,MAAM;AACrD,QAAM,OAAO,OAAO,IAAI,MAAM,CAAC,IAAI;AAEnC,QAAM,gBAAgB,YAAY,kCAAkC;AAEpE,QAAM,UAAU,YAAY,gBAAgB,WAAW,aAAa;AAEpE,QAAM,WAAW,YAAY,IAAI,KAAK,aAAa,OAAO;AAE1D,QAAM,UAAU,IAAI,OAAO,GAAG,QAAQ;AACtC,QAAM,MAAM,IAAI,OAAO,OAAO;AAC9B,SAAO,IAAI,KAAK,IAAI;AACtB;AAUO,SAAS,cAAc,GAAW;AACvC,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,SAAO,gBAAgB,KAAK,CAAC;AAC/B;AAWO,SAAS,WAAW,GAAW;AACpC,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,SAAO,mCAAmC,KAAK,CAAC;AAClD;AAUO,SAAS,QAAQ,GAAW;AACjC,SAAO,cAAc,CAAC,KAAK,WAAW,CAAC;AACzC;AAUO,SAAS,QAAQ,GAAW;AACjC,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,MAAI,MAAM,GAAI,QAAO;AACrB,QAAM,aACJ;AACF,SAAO,WAAW,KAAK,CAAC;AAC1B;AAUO,SAAS,cAAc,GAAW;AACvC,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,SAAO,2BAA2B,KAAK,CAAC;AAC1C;AAiBO,SAAS,eAAe,MAAc;AAC3C,QAAM,IAAI,OAAO,sBAAQ,EAAE,EAAE,KAAK;AAClC,MAAI,MAAM,GAAI,QAAO;AAErB,QAAM,cAAc,CAAC,aAAqB;AACxC,UAAM,IAAI,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC;AACrC,UAAM,IAAI,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC;AACrC,UAAM,IAAI,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC;AACrC,QAAI,IAAI,QAAQ,IAAI,KAAM,QAAO;AACjC,UAAM,OAAO,IAAI,KAAK,GAAG,IAAI,GAAG,CAAC;AACjC,WAAO,KAAK,YAAY,MAAM,KAAK,KAAK,SAAS,MAAM,IAAI,KAAK,KAAK,QAAQ,MAAM;AAAA,EACrF;AAGA,MAAI,iBAAiB,KAAK,CAAC,GAAG;AAC5B,UAAM,QAAQ,EAAE,MAAM,GAAG,EAAE;AAC3B,QAAI,CAAC,YAAY,KAAK,EAAG,QAAO;AAChC,UAAM,UAAU,CAAC,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,CAAC;AACpE,UAAM,WAAW,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AACvE,QAAI,MAAM;AACV,aAAS,IAAI,GAAG,IAAI,IAAI,IAAK,QAAO,OAAO,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC;AAC5D,UAAM,MAAM,MAAM;AAClB,UAAM,SAAS,EAAE,EAAE,EAAE,YAAY;AACjC,WAAO,SAAS,GAAG,MAAM;AAAA,EAC3B;AAGA,MAAI,WAAW,KAAK,CAAC,GAAG;AACtB,UAAM,QAAQ,EAAE,MAAM,GAAG,EAAE;AAC3B,UAAM,IAAI,OAAO,KAAK,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE;AACzC,UAAM,IAAI,OAAO,MAAM,MAAM,GAAG,CAAC,CAAC;AAClC,UAAM,IAAI,OAAO,MAAM,MAAM,GAAG,CAAC,CAAC;AAClC,UAAM,OAAO,IAAI,KAAK,GAAG,IAAI,GAAG,CAAC;AACjC,WAAO,KAAK,YAAY,MAAM,KAAK,KAAK,SAAS,MAAM,IAAI,KAAK,KAAK,QAAQ,MAAM;AAAA,EACrF;AAGA,MAAI,sBAAsB,KAAK,CAAC,EAAG,QAAO;AAG1C,MAAI,mCAAmC,KAAK,CAAC,EAAG,QAAO;AAGvD,MAAI,uBAAuB,KAAK,CAAC,EAAG,QAAO;AAE3C,SAAO;AACT;AAiBO,SAAS,WAAW,GAAW;AACpC,QAAM,IAAI,OAAO,gBAAK,EAAE,EACrB,QAAQ,UAAU,EAAE,EACpB,KAAK;AACR,MAAI,MAAM,GAAI,QAAO;AACrB,MAAI,cAAc,KAAK,CAAC,EAAG,QAAO;AAClC,MAAI,eAAe,KAAK,CAAC,EAAG,QAAO;AACnC,MAAI,kBAAkB,KAAK,CAAC,EAAG,QAAO;AACtC,MAAI,qBAAqB,KAAK,CAAC,EAAG,QAAO;AACzC,SAAO;AACT;AAWO,SAAS,aAAa,GAAW;AACtC,QAAM,IAAI,OAAO,gBAAK,EAAE,EACrB,QAAQ,UAAU,EAAE,EACpB,KAAK,EACL,YAAY;AACf,SAAO,iBAAiB,KAAK,CAAC;AAChC;AAYO,SAAS,eAAe,GAAW;AACxC,QAAM,IAAI,OAAO,gBAAK,EAAE,EACrB,QAAQ,UAAU,EAAE,EACpB,KAAK,EACL,YAAY;AACf,MAAI,UAAU,KAAK,CAAC,EAAG,QAAO;AAC9B,MAAI,eAAe,KAAK,CAAC,EAAG,QAAO;AACnC,MAAI,WAAW,KAAK,CAAC,EAAG,QAAO;AAC/B,SAAO;AACT;AASO,SAAS,YAAY,GAAW;AACrC,QAAM,IAAI,OAAO,gBAAK,EAAE,EACrB,QAAQ,UAAU,EAAE,EACpB,KAAK,EACL,YAAY;AACf,SAAO,mBAAmB,KAAK,CAAC;AAClC;AASO,SAAS,YAAY,GAAW;AACrC,QAAM,IAAI,OAAO,gBAAK,EAAE,EACrB,QAAQ,UAAU,EAAE,EACpB,KAAK,EACL,YAAY;AACf,SAAO,mBAAmB,KAAK,CAAC;AAClC;AASO,SAAS,aAAa,GAAW;AACtC,SAAO,YAAY,CAAC,KAAK,YAAY,CAAC;AACxC;AAUO,SAAS,WAAW,GAAW;AACpC,QAAM,IAAI,OAAO,gBAAK,EAAE,EACrB,QAAQ,UAAU,EAAE,EACpB,KAAK;AACR,MAAI,CAAC,cAAc,KAAK,CAAC,EAAG,QAAO;AACnC,MAAI,MAAM;AACV,MAAI,eAAe;AACnB,WAAS,IAAI,EAAE,SAAS,GAAG,KAAK,GAAG,KAAK;AACtC,QAAI,QAAQ,OAAO,EAAE,CAAC,CAAC;AACvB,QAAI,cAAc;AAChB,eAAS;AACT,UAAI,QAAQ,EAAG,UAAS;AAAA,IAC1B;AACA,WAAO;AACP,mBAAe,CAAC;AAAA,EAClB;AACA,SAAO,MAAM,OAAO;AACtB;AAWO,SAAS,eAAe,GAAW;AACxC,QAAM,IAAI,OAAO,gBAAK,EAAE,EACrB,KAAK,EACL,YAAY;AACf,QAAM,OAAO;AACb,QAAM,MAAM,IAAI,OAAO,KAAK,IAAI,4EAAmD;AACnF,QAAM,MAAM,IAAI,OAAO,KAAK,IAAI,oCAAoC;AACpE,QAAM,MAAM,IAAI,OAAO,KAAK,IAAI,oCAAoC;AACpE,SAAO,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC;AACjD;AAYO,SAAS,QAAQ,MAAc;AACpC,QAAM,IAAI,OAAO,sBAAQ,EAAE,EAAE,KAAK;AAClC,MAAI,CAAC,4BAA4B,KAAK,CAAC,EAAG,QAAO;AACjD,QAAM,UAAU;AAChB,QAAM,UAAU,CAAC,GAAG,GAAG,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,EAAE;AAC/E,QAAM,MAA8B,CAAC;AACrC,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,IAAK,KAAI,QAAQ,CAAC,CAAC,IAAI;AAC3D,MAAI,MAAM;AACV,WAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,WAAO,IAAI,EAAE,CAAC,CAAC,IAAI,QAAQ,CAAC;AAAA,EAC9B;AACA,QAAM,cAAc,KAAM,MAAM,MAAO;AACvC,QAAM,WAAW,QAAQ,UAAU;AACnC,SAAO,EAAE,EAAE,MAAM;AACnB;AAWO,SAAS,WAAW,GAAW;AACpC,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,SAAO,sDAAsD,KAAK,CAAC;AACrE;AAUO,SAAS,MAAM,GAAW;AAC/B,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,MAAI,MAAM,GAAI,QAAO;AACrB,MAAI;AACF,UAAM,IAAI,IAAI,IAAI,CAAC;AACnB,WAAO,CAAC,SAAS,UAAU,MAAM,EAAE,SAAS,EAAE,QAAQ,KAAK,CAAC,CAAC,EAAE;AAAA,EACjE,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUA,SAAS,OAAO,GAAW;AACzB,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,MAAI,MAAM,GAAI,QAAO;AACrB,QAAM,QAAQ,EAAE,MAAM,GAAG;AACzB,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,aAAW,KAAK,OAAO;AACrB,QAAI,CAAC,QAAQ,KAAK,CAAC,EAAG,QAAO;AAC7B,QAAI,EAAE,SAAS,KAAK,EAAE,WAAW,GAAG,EAAG,QAAO;AAC9C,UAAM,IAAI,OAAO,CAAC;AAClB,QAAI,IAAI,KAAK,IAAI,IAAK,QAAO;AAAA,EAC/B;AACA,SAAO;AACT;AAcO,SAAS,OAAO,GAAoB;AACzC,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,MAAI,MAAM,GAAI,QAAO;AAErB,QAAM,YAAY,EAAE,YAAY,GAAG;AACnC,MAAI,cAAc,MAAM,EAAE,SAAS,GAAG,GAAG;AACvC,UAAM,WAAW,EAAE,MAAM,GAAG,SAAS;AACrC,UAAM,WAAW,EAAE,MAAM,YAAY,CAAC;AACtC,WAAO,OAAO,QAAQ,KAAK,OAAO,QAAQ;AAAA,EAC5C;AAEA,QAAM,iBAAiB,EAAE,MAAM,KAAK,KAAK,CAAC,GAAG;AAC7C,MAAI,gBAAgB,EAAG,QAAO;AAE9B,QAAM,WAAW,EAAE,MAAM,GAAG;AAC5B,MAAI,EAAE,WAAW,IAAI,EAAG,UAAS,MAAM;AACvC,MAAI,EAAE,SAAS,IAAI,EAAG,UAAS,IAAI;AACnC,QAAM,mBAAmB,SAAS,OAAO,CAAC,QAAQ,QAAQ,EAAE;AAE5D,MAAI,kBAAkB,KAAK,iBAAiB,WAAW,EAAG,QAAO;AACjE,MAAI,kBAAkB,KAAK,iBAAiB,UAAU,KAAK,iBAAiB,UAAU,GAAG;AAAA,EAEzF,WAAW,kBAAkB,KAAK,SAAS,WAAW,GAAG;AAEvD,WAAO;AAAA,EACT,WAAW,kBAAkB,KAAK,iBAAiB,WAAW,GAAG;AAAA,EAEjE,OAAO;AACL,WAAO;AAAA,EACT;AAGA,SAAO,iBAAiB;AAAA,IACtB,CAAC,QAAQ,IAAI,UAAU,KAAK,IAAI,UAAU,KAAK,qBAAqB,KAAK,GAAG;AAAA,EAC9E;AACF;AAYO,SAAS,KAAK,GAAW,SAA6B;AAC3D,MAAI,YAAY,KAAK,YAAY,IAAK,QAAO,OAAO,CAAC;AACrD,MAAI,YAAY,KAAK,YAAY,IAAK,QAAO,OAAO,CAAC;AACrD,SAAO,OAAO,CAAC,KAAK,OAAO,CAAC;AAC9B;AAUO,SAAS,UAAU,GAAW;AACnC,QAAM,IAAI,OAAO,gBAAK,EAAE,EAAE,KAAK;AAC/B,MAAI,MAAM,GAAI,QAAO;AACrB,QAAM,QAAQ,EAAE,MAAM,GAAG;AACzB,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,CAAC,IAAI,SAAS,IAAI;AACxB,MAAI,CAAC,QAAQ,KAAK,SAAS,EAAG,QAAO;AACrC,QAAM,SAAS,OAAO,SAAS;AAC/B,MAAI,GAAG,SAAS,GAAG,GAAG;AACpB,QAAI,CAAC,OAAO,EAAE,EAAG,QAAO;AACxB,WAAO,UAAU,KAAK,UAAU;AAAA,EAClC;AACA,MAAI,CAAC,OAAO,EAAE,EAAG,QAAO;AACxB,SAAO,UAAU,KAAK,UAAU;AAClC;AAWO,SAAS,aAAa,GAAoB;AAC/C,QAAM,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,OAAO,gBAAK,EAAE,EAAE,KAAK,CAAC;AACnE,SAAO,OAAO,UAAU,CAAC,KAAK,KAAK,KAAK,KAAK;AAC/C;AASO,SAAS,WAAW,GAAoB;AAC7C,QAAM,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,OAAO,gBAAK,EAAE,EAAE,KAAK,CAAC;AACnE,SAAO,OAAO,SAAS,CAAC,KAAK,KAAK,OAAO,KAAK;AAChD;AASO,SAAS,YAAY,GAAoB;AAC9C,QAAM,IAAI,OAAO,MAAM,WAAW,IAAI,OAAO,OAAO,gBAAK,EAAE,EAAE,KAAK,CAAC;AACnE,SAAO,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,KAAK;AACjD;;;ACjcA,IAAM,eAAe,oBAAI,IAA8C;AA0EhE,SAAS,QAAgD,QAA8B;AAE5F,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,SAAS,WAAW;AAC1B,MAAI,gBAAsC;AAG1C,QAAM,OAAoB;AAAA,IACxB,OAAO,MAAM,WAAW,MAAM;AAAA,IAC9B,iBAAiB,CAAC,OAAO;AACvB,sBAAgB;AAAA,IAClB;AAAA,IACA,kBAAkB,MAAM;AACtB,sBAAgB;AAAA,IAClB;AAAA,EACF;AAGA,QAAM,UAAU,IAAI,QAAW,CAAC,SAAS,WAAW;AAClD,UAAM,UAAU,MAAY;AA3NhC;AA4NM,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,cAAc;AAAA,QACd,aAAa;AAAA,QACb,gBAAgB;AAAA,QAChB;AAAA,QACA;AAAA,QACA,eAAe;AAAA,QACf,UAAU;AAAA,MACZ,IAAI;AAEJ,YAAM,QAAQ,WAAW;AACzB,YAAM,mBAAe,0BAAc,IAAI;AACvC,YAAM,cAAc,CAAC,gBAAgB,MAAM,QAAQ,IAAI;AAIvD,YAAM,WAAW,eAAe,kBAAkB,IAAI,IAAI;AAG1D,YAAM,aAAa,oBAAoB,MAAM;AAG7C,YAAM,iBAAiB,OAAO,KAAK,UAAU,EAAE;AAAA,QAC7C,CAAC,MAAM,EAAE,YAAY,MAAM;AAAA,MAC7B;AACA,YAAM,cAAc,iBAAiB,OAAO,WAAW,cAAc,CAAC,EAAE,YAAY,IAAI;AAExF,UAAI,CAAC,SAAS,aAAa,gBAAgB,gBAAgB,CAAC,aAAa;AACvE,mBAAW,cAAc,IAAI;AAAA,MAC/B;AAGA,YAAM,UACJ,SAAS,eAAe,eAAe,KAAK,QAAmC,IAAI;AAErF,UAAI;AAEJ,UAAI,CAAC,SAAS,UAAU;AACtB,YAAI,gBAAgB,YAAY,SAAS,mCAAmC,GAAG;AAE7E,qBAAW,eAAe,QAAmC;AAAA,QAC/D,WAAW,gBAAgB,YAAY,SAAS,qBAAqB,GAAG;AAEtE,qBAAW,WAAW,QAAmC;AAEzD,cAAI,eAAgB,QAAO,WAAW,cAAc;AAAA,QACtD,WAAW,gBAAgB,aAAa;AACtC,qBAAW,KAAK,UAAU,QAAQ;AAAA,QACpC,OAAO;AACL,qBAAW;AAAA,QACb;AAAA,MACF;AAGA,YAAM,YAAY,iCAAK,SAAL,EAAa,MAAM,UAAU,QAAQ,YAAY,KAAK,QAAQ;AAChF,YAAM,YAAY,KAAK,IAAI;AAG3B,YAAM,UAAU,aAAa,YAAY;AACzC,YAAM,WAAW,UAAU,KAAK,UAAU,EAAE,KAAK,SAAS,MAAM,SAAS,CAAC,IAAI;AAE9E,UAAI,SAAS;AACX,cAAM,MAAM,WAAW,QAAQ;AAC/B,YAAI,KAAK;AACP,yBAAe;AAAA,YACb,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,WAAW;AAAA,YACX;AAAA,YACA;AAAA,UACF,CAAC;AACD,kBAAQ,UAAU,KAAK,MAAM,CAAM;AACnC;AAAA,QACF;AAAA,MACF;AAGA,YAAMC,aAAY,mBAAmB;AACrC,UAAI;AACF,cAAAA,WAAU,gBAAV,wBAAAA,YAAwB,OAAO,gBAAgB,WAAW,EAAE,OAAO,YAAY,IAAI,CAAC;AAGtF,UAAI,YAAY;AAChB,YAAM,YAAY,WAAW,MAAM;AACjC,oBAAY;AACZ,mBAAW,MAAM;AAAA,MACnB,GAAG,OAAO;AAEV,UAAI;AAEF,cAAM,WAAW,MAAM,MAAM,SAAS;AAAA,UACpC;AAAA,UACA,SAAS;AAAA,UACT,MAAM;AAAA,UACN;AAAA,QACF,CAAC;AAED,YAAI,CAAC,SAAS,IAAI;AAChB,cAAI,YAAa,OAAAA,WAAU,gBAAV,wBAAAA;AACjB,gBAAM,IAAI,MAAM,cAAc,SAAS,MAAM,KAAK,SAAS,UAAU,EAAE;AAAA,QACzE;AAGA,YAAI,eAAe;AACjB,cAAI,YAAa,OAAAA,WAAU,gBAAV,wBAAAA;AAEjB,gBAAMC,OAAM,MAAM,qBAAqB,UAAU,aAAa;AAE9D,yBAAe,EAAE,QAAQ,WAAW,QAAQ,WAAW,WAAW,KAAAA,KAAI,CAAC;AAEvE,kBAAQA,IAAQ;AAChB;AAAA,QACF;AAGA,cAAM,UAAU,MAAM,cAAc,UAAU,YAAY;AAG1D,YAAI,YAAa,OAAAD,WAAU,gBAAV,wBAAAA;AAGjB,cAAM,MAAM,sBAAsB,oBAAoB,OAAO,IAAI;AAGjE,cAAM,OAAO,eAAe,KAAK,OAAO;AACxC,cAAM,QAAQ,aAAa,eAAe,KAAK,UAAU,IAAI;AAC7D,cAAM,MAAM,eAAe,KAAK,MAAM;AACtC,cAAM,YAAY,YAAY,SAAS,KAAK;AAC5C,cAAM,YAAY,YAAY,SAAS,IAAI;AAE3C,uBAAe,EAAE,QAAQ,WAAW,QAAQ,WAAW,WAAW,IAAI,CAAC;AAGvE,YAAI,WAAW;AAEb,cAAI,QAAS,cAAa,IAAI,UAAU,EAAE,KAAK,QAAQ,KAAK,IAAI,IAAI,UAAU,CAAC;AAC/E,kBAAQ,UAAU,KAAK,MAAM,CAAM;AAAA,QACrC,WAAW,WAAW;AAEpB,iBAAO,GAAG;AACV,gBAAAA,WAAU,YAAV,wBAAAA;AAAA,QACF,OAAO;AAEL,cAAI,cAAc,IAAK,OAAAA,WAAU,UAAV,wBAAAA,YAAkB,EAAE,QAAQ,QAAQ,IAAI;AAC/D,iBAAO,GAAG;AAAA,QACZ;AAAA,MACF,SAAS,GAAG;AACV,cAAM,SAAS;AACf,cAAM,eAAe,aAAa,gBAAgB,EAAE,SAAS;AAE7D,YAAI,gBAAgB,WAAW;AAC7B,cAAI,WAAY,OAAAA,WAAU,UAAV,wBAAAA,YAAkB,EAAE,QAAQ,KAAK,2BAAO;AACxD,gBAAM,eAAe,IAAI,MAAM,iBAAiB;AAChD,yBAAe,EAAE,QAAQ,QAAQ,WAAW,WAAW,GAAG,aAAa,CAAC;AACxE,iBAAO,YAAY;AACnB;AAAA,QACF;AAEA,YAAI,CAAC,gBAAgB,WAAY,OAAAA,WAAU,UAAV,wBAAAA,YAAkB,EAAE,QAAQ,KAAK,uCAAS;AAC3E,uBAAe,EAAE,QAAQ,QAAQ,WAAW,WAAW,EAAE,CAAC;AAC1D,eAAO,CAAC;AAAA,MACV,UAAE;AACA,YAAI,UAAW,cAAa,SAAS;AAAA,MACvC;AAAA,IACF;AAEA,YAAQ;AAAA,EACV,CAAC;AAGD,UAAQ,OAAO;AAEf,SAAO;AACT;AAKO,SAAS,kBAAkB,MAA2B;AAC3D,QAAM,MAA2B,CAAC;AAClC,SAAO,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM;AACvC,QAAI,MAAM,OAAW,KAAI,CAAC,IAAI;AAAA,EAChC,CAAC;AACD,SAAO;AACT;AAKO,SAAS,oBAAoB,QAAqC;AACvE,QAAM,YAAoC,CAAC;AAC3C,MAAI,QAAQ;AACV,WAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM;AACzC,UAAI,MAAM,UAAa,MAAM,QAAQ,MAAM,GAAI,WAAU,CAAC,IAAI,OAAO,CAAC;AAAA,IACxE,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAKA,SAAS,eAAe,SAOrB;AACD,QAAM,EAAE,IAAI,IAAI,mBAAmB;AACnC,QAAM,EAAE,QAAQ,KAAK,IAAI,QAAQ;AAEjC,MAAI,CAAC,OAAO,CAAC,MAAO;AAEpB,QAAM,EAAE,QAAQ,KAAK,YAAY,OAAO,WAAW,QAAQ,EAAE,IAAI;AACjE,QAAM,EAAE,KAAK,MAAM,QAAQ,QAAQ,SAAS,IAAI;AAChD,QAAM,UAAU,KAAK,IAAI;AACzB,QAAM,MAAM;AAEZ,QAAM,OAAmB;AAAA,IACvB,MAAM;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,QAAQ,SAAS,EAAE,OAAO,GAAG;AAAA,IACxC,SAAS,QAAQ,OAAO,EAAE,OAAO,GAAG;AAAA,IACpC,UAAU,UAAU;AAAA,KACjB;AAGL,MAAI,WAAW,WAAW;AACxB,SAAK,UAAM,sBAAU,GAAG;AACxB,QAAI,QAAQ,IAAI;AAAA,EAClB,OAAO;AACL,SAAK,IAAI;AACT,QAAI,SAAS,IAAI;AAAA,EACnB;AACF;AAKA,SAAS,UAAU,KAAc,QAAsC;AACrE,MAAI,CAAC,OAAO,CAAC,UAAU,OAAO,QAAQ,SAAU,QAAO;AACvD,SAAO,eAAe,KAAK,MAAM;AACnC;AAKA,SAAS,WAAW,UAAkB;AACpC,QAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,MAAI,CAAC,OAAQ,QAAO;AACpB,MAAI,OAAO,UAAU,KAAK,IAAI,GAAG;AAC/B,iBAAa,OAAO,QAAQ;AAC5B,WAAO;AAAA,EACT;AACA,SAAO,OAAO;AAChB;AAKA,SAAe,qBAAqB,UAAoB,eAAqC;AAAA;AAC3F,QAAI,CAAC,SAAS,KAAM,OAAM,IAAI,MAAM,uBAAuB;AAE3D,UAAM,SAAS,SAAS,KAAK,UAAU;AAEvC,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,KAAM;AACV,UAAI,iBAAiB,OAAO;AAC1B,sBAAc,EAAE,MAAM,MAAM,OAAO,CAAC;AAAA,MACtC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAKA,SAAe,cAAc,UAAoB,cAAsB;AAAA;AACrE,QAAI;AACJ,QAAI,iBAAiB,eAAe;AAClC,gBAAU,MAAM,SAAS,YAAY;AAAA,IACvC,WAAW,iBAAiB,QAAQ;AAClC,gBAAU,MAAM,SAAS,KAAK;AAAA,IAChC,OAAO;AACL,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,UAAI;AACF,kBAAU,KAAK,MAAM,IAAI;AAAA,MAC3B,SAAQ;AACN,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAKA,SAAS,eAAe,MAA+B;AACrD,QAAM,SAAS,IAAI,gBAAgB;AACnC,aAAW,OAAO,MAAM;AACtB,UAAM,MAAM,KAAK,GAAG;AAGpB,QAAI,QAAQ,KAAM;AAClB,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,UAAI,QAAQ,CAAC,MAAM,OAAO,OAAO,KAAK,OAAO,MAAM,WAAW,KAAK,UAAU,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC;AAAA,IAC9F,OAAO;AACL,aAAO,OAAO,KAAK,OAAO,QAAQ,WAAW,KAAK,UAAU,GAAG,IAAI,OAAO,GAAG,CAAC;AAAA,IAChF;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,WAAW,MAA+B;AACjD,QAAM,WAAW,IAAI,SAAS;AAC9B,aAAW,OAAO,MAAM;AACtB,UAAM,MAAM,KAAK,GAAG;AAGpB,QAAI,QAAQ,KAAM;AAClB,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,UAAI;AAAA,QAAQ,CAAC,MACX,SAAS;AAAA,UACP;AAAA,UACA,aAAa,OAAO,IAAI,OAAO,MAAM,WAAW,KAAK,UAAU,CAAC,IAAI,OAAO,CAAC;AAAA,QAC9E;AAAA,MACF;AAAA,IACF,OAAO;AACL,eAAS;AAAA,QACP;AAAA,QACA,eAAe,OAAO,MAAM,OAAO,QAAQ,WAAW,KAAK,UAAU,GAAG,IAAI,OAAO,GAAG;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;ACniBO,SAAS,gBACd,QACA,SACA;AACA,SAAO,CAAC,QAAgB,WAA6C;AACnE,UAAM,cAAc,UAAW,CAAC;AAChC,UAAM;AAAA,MACJ,cAAc;AAAA,MACd,eAAe;AAAA,MACf,aAAa;AAAA,MACb,UAAU;AAAA,IACZ,IAAI;AACJ,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,aAAa;AAAA,IACf,IAAI,mBAAmB;AACvB,UAAM,QAAQ,WAAW;AAEzB,QAAI,aAAa;AACf,YAAM,QAAQ,OAAO,gBAAgB,WAAW,cAAc;AAC9D,qDAAgB,EAAE,MAAM;AAAA,IAC1B;AAEA,WAAO,IAAI,QAAa,CAAC,SAAS,WAAW;AAC3C,aAAO,QAAQ,WAAW,EACvB,KAAK,CAAC,QAAQ;AACb,YAAI,YAAa;AACjB,YAAI,QAAS,4BAAM,QAAQ,EAAE,MAAM,OAAO,QAAQ,WAAW,QAAQ,IAAI;AACzE,gBAAQ,GAAG;AAEX,cAAM,MAAM,OAAO,iBAAiB,aAAa,aAAa,GAAG,IAAI;AACrE,YAAI,IAAK,gCAAQ,EAAE,KAAK,QAAQ,UAAU;AAAA,MAC5C,CAAC,EACA,MAAM,CAAC,MAAM;AACZ,YAAI,YAAa;AACjB,YAAI,QAAS,4BAAM,SAAS,EAAE,MAAM,OAAO,QAAQ,QAAQ,QAAQ,EAAE;AAErE,cAAM,MAAM,OAAO,eAAe,aAAa,WAAW,CAAC,IAAI;AAC/D,YAAI,KAAK;AACP,yCAAQ;AAAA,YACN,KAAK,OAAO,QAAQ,WAAW,MAAM,GAAG,KAAK,UAAU,KAAK,UAAU,CAAC,CAAC;AAAA,YACxE,QAAQ;AAAA,UACV;AAAA,QACF;AAEA,eAAO,CAAC;AAAA,MACV,CAAC;AAAA,IACL,CAAC;AAAA,EACH;AACF;;;AC3BA,SAAS,OAAU,QAA0B,QAAuB;AAClE,SAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AAvD7C;AAwDI,UAAM,MAAM,IAAI,eAAe;AAC/B,UAAM,EAAE,KAAK,MAAM,OAAO,QAAQ,QAAQ,UAAU,UAAU,EAAE,IAAI;AAEpE,UAAM,OAAO,CAAC,UAAsB,OAAO,KAAK;AAEhD,UAAM,UAAU,CAAC,iBAAyB;AACxC,UAAI;AACF,gBAAQ,KAAK,MAAM,YAAY,CAAM;AAAA,MACvC,SAAS,GAAG;AACV,gBAAQ,YAAiB;AAAA,MAC3B;AAAA,IACF;AAGA,QAAI;AACJ,UAAM,OAAmB;AAAA,MACvB,kBAAkB,CAAC,aAAa;AAC9B,2BAAmB;AAAA,MACrB;AAAA,MACA,OAAO,MAAM,IAAI,MAAM;AAAA,IACzB;AACA,2CAAQ,gBAAR,gCAAsB;AAGtB,QAAI,OAAO,aAAa,CAAC,MAAM;AAC7B,UAAI,CAAC,EAAE,iBAAkB;AACzB,YAAM,KAA0B;AAAA,QAC9B,UAAU,KAAK,MAAO,EAAE,SAAS,EAAE,QAAS,GAAG;AAAA,QAC/C,QAAQ,EAAE;AAAA,QACV,OAAO,EAAE;AAAA,MACX;AACA,2DAAmB;AAAA,IACrB;AAGA,QAAI,SAAS,MAAM;AACjB,UAAI,IAAI,UAAU,OAAO,IAAI,SAAS,KAAK;AACzC,gBAAQ,IAAI,YAAY;AAAA,MAC1B,OAAO;AACL,aAAK,EAAE,SAAS,4BAAQ,QAAQ,IAAI,OAAO,CAAC;AAAA,MAC9C;AAAA,IACF;AACA,QAAI,UAAU,MAAM,KAAK,EAAE,SAAS,4BAAQ,QAAQ,EAAE,CAAC;AACvD,QAAI,YAAY,MAAM,KAAK,EAAE,SAAS,4BAAQ,QAAQ,GAAG,CAAC;AAC1D,QAAI,UAAU,MAAM,KAAK,EAAE,SAAS,4BAAQ,QAAQ,GAAG,CAAC;AAGxD,QAAI,KAAK,QAAQ,GAAG;AAGpB,QAAI,QAAQ;AACV,aAAO,QAAQ,MAAM,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM;AACzC,YAAI,MAAM,UAAa,MAAM,QAAQ,MAAM,GAAI,KAAI,iBAAiB,GAAG,OAAO,CAAC,CAAC;AAAA,MAClF,CAAC;AAAA,IACH;AAGA,QAAI,UAAU;AAGd,UAAM,OAAO,IAAI,SAAS;AAC1B,SAAK,OAAO,MAAM,IAAI;AACtB,QAAI,UAAU;AACZ,aAAO,QAAQ,QAAQ,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM;AAC3C,YAAI,MAAM,OAAW,MAAK,OAAO,GAAG,OAAO,CAAC,CAAC;AAAA,MAC/C,CAAC;AAAA,IACH;AAGA,QAAI,KAAK,IAAI;AAAA,EACf,CAAC;AACH;AAgBO,SAAS,WACd,QACA,QACA;AACA,SAAO,gBAA+D,QAAQ,YAAY;AAAA,IACxF;AAAA,IACA;AAAA,EACF;AACF;;;ACvJA,IAAM,KAAK;AAAA,EACT,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AACR;AAeO,SAAS,gBAAgB,KAAa,OAAgB,MAAe;AAC1E,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,uBAAmB,GAAG;AACtB;AAAA,EACF;AAEA,MAAI,UAAmB;AACvB,MAAI,OAAO,SAAS,YAAY,OAAO,GAAG;AACxC,UAAM,KAAK,OAAO,KAAK,KAAK,KAAK;AACjC,cAAU;AAAA,MACR,CAAC,GAAG,IAAI,GAAG;AAAA,MACX,CAAC,GAAG,GAAG,GAAG;AAAA,MACV,CAAC,GAAG,GAAG,GAAG,KAAK,IAAI,IAAI;AAAA,IACzB;AAAA,EACF;AAEA,eAAa,QAAQ,KAAK,KAAK,UAAU,OAAO,CAAC;AACnD;AAcO,SAAS,gBAA6B,KAAuB;AAClE,QAAM,MAAM,aAAa,QAAQ,GAAG;AACpC,MAAI,QAAQ,KAAM,QAAO;AACzB,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,GAAG;AAE7B,QAAI,UAAU,OAAO,WAAW,YAAY,GAAG,QAAQ,UAAU,GAAG,OAAO,QAAQ;AACjF,UAAI,KAAK,IAAI,IAAI,OAAO,GAAG,GAAG,GAAG;AAC/B,2BAAmB,GAAG;AACtB,eAAO;AAAA,MACT;AACA,aAAO,OAAO,GAAG,GAAG;AAAA,IACtB;AACA,WAAO;AAAA,EACT,SAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,mBAAmB,KAAa;AAC9C,eAAa,WAAW,GAAG;AAC7B;;;AChEO,SAAS,YAAY,KAAa,MAAM,OAAO,SAAS,MAAM;AACnE,QAAM,eAAe,IAAI,IAAI,IAAI,SAAS,GAAG,IAAI,MAAM,IAAI,GAAG,IAAI,kBAAkB,EACjF;AACH,QAAM,QAAQ,aAAa,IAAI,GAAG;AAClC,SAAO,UAAU,UAAU,UAAU,cAAc,OAAO;AAC5D;AAcO,SAAS,aAAa,KAAa,MAAM,OAAO,SAAS,MAAM;AACpE,QAAM,MAAM,YAAY,KAAK,GAAG;AAChC,MAAI,CAAC,IAAK,QAAO;AAEjB,QAAM,MAAM,OAAO,GAAG;AACtB,SAAO,MAAM,GAAG,IAAI,OAAO;AAC7B;AAaO,SAAS,aAAa,MAAM,OAAO,SAAS,MAAM;AACvD,QAAM,eAAe,IAAI,IAAI,IAAI,SAAS,GAAG,IAAI,MAAM,IAAI,GAAG,IAAI,kBAAkB,EACjF;AACH,QAAM,SAAiC,CAAC;AAExC,aAAW,CAAC,KAAK,KAAK,KAAK,aAAa,QAAQ,GAAG;AACjD,QAAI,UAAU,UAAU,UAAU,aAAa;AAC7C,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AACT;","names":["html","src","n","getWatermark","toBase64Url","appConfig","res"]}
|