@ybgnb/utils 0.1.8 → 0.2.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.
Files changed (63) hide show
  1. package/LICENSE +1 -1
  2. package/dist/core.cjs +2 -0
  3. package/dist/core.cjs.map +1 -0
  4. package/dist/core.d.cts +369 -0
  5. package/dist/core.d.ts +369 -2
  6. package/dist/core.js +1 -298
  7. package/dist/core.js.map +1 -1
  8. package/dist/dom.cjs +2 -0
  9. package/dist/dom.cjs.map +1 -0
  10. package/dist/dom.d.cts +105 -0
  11. package/dist/dom.d.ts +105 -2
  12. package/dist/dom.js +1 -133
  13. package/dist/dom.js.map +1 -1
  14. package/dist/node.cjs +2 -0
  15. package/dist/node.cjs.map +1 -0
  16. package/dist/node.d.cts +153 -0
  17. package/dist/node.d.ts +153 -2
  18. package/dist/node.js +1 -200
  19. package/dist/node.js.map +1 -1
  20. package/package.json +11 -11
  21. package/dist/core/error/common-error.d.ts +0 -8
  22. package/dist/core/index.d.ts +0 -17
  23. package/dist/core/result/biz-result.d.ts +0 -17
  24. package/dist/core/result/exec-biz.d.ts +0 -6
  25. package/dist/core/types/helpers.d.ts +0 -54
  26. package/dist/core/types/physical.d.ts +0 -16
  27. package/dist/core/utils/array.d.ts +0 -12
  28. package/dist/core/utils/error.d.ts +0 -22
  29. package/dist/core/utils/fetch.d.ts +0 -13
  30. package/dist/core/utils/function.d.ts +0 -28
  31. package/dist/core/utils/github.d.ts +0 -31
  32. package/dist/core/utils/number/format.d.ts +0 -19
  33. package/dist/core/utils/random.d.ts +0 -28
  34. package/dist/core/utils/serialize.d.ts +0 -23
  35. package/dist/core/utils/sleep.d.ts +0 -13
  36. package/dist/core/utils/time.d.ts +0 -19
  37. package/dist/core/utils/type.d.ts +0 -9
  38. package/dist/core/utils/url.d.ts +0 -36
  39. package/dist/core.umd.cjs +0 -2
  40. package/dist/core.umd.cjs.map +0 -1
  41. package/dist/dom/index.d.ts +0 -6
  42. package/dist/dom/utils/color.d.ts +0 -32
  43. package/dist/dom/utils/css.d.ts +0 -4
  44. package/dist/dom/utils/img.d.ts +0 -14
  45. package/dist/dom/utils/network.d.ts +0 -25
  46. package/dist/dom/utils/page.d.ts +0 -10
  47. package/dist/dom/utils/scroll.d.ts +0 -13
  48. package/dist/dom.umd.cjs +0 -2
  49. package/dist/dom.umd.cjs.map +0 -1
  50. package/dist/node/index.d.ts +0 -8
  51. package/dist/node/utils/env.d.ts +0 -6
  52. package/dist/node/utils/file/base.d.ts +0 -20
  53. package/dist/node/utils/file/delete.d.ts +0 -12
  54. package/dist/node/utils/file/download.d.ts +0 -4
  55. package/dist/node/utils/file/find.d.ts +0 -14
  56. package/dist/node/utils/file/json.d.ts +0 -20
  57. package/dist/node/utils/file/size.d.ts +0 -19
  58. package/dist/node/utils/win/cmd.d.ts +0 -21
  59. package/dist/node/utils/win/copy.d.ts +0 -4
  60. package/dist/node/utils/win/explorer.d.ts +0 -5
  61. package/dist/node/utils/win/regedit.d.ts +0 -17
  62. package/dist/node.umd.cjs +0 -2
  63. package/dist/node.umd.cjs.map +0 -1
package/dist/dom.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dom.js","names":[],"sources":["../src/dom/utils/color.ts","../src/dom/utils/css.ts","../src/dom/utils/img.ts","../src/dom/utils/network.ts","../src/dom/utils/page.ts","../src/dom/utils/scroll.ts"],"sourcesContent":["/**\n * 生成 CSS `color-mix()` 函数的字符串表示,用于动态计算两种颜色的混合结果\n *\n * @param {string} color1 - 参与混合的第一种颜色(支持 CSS 合法颜色值,如 HEX、RGB、HSL 等)\n * @param {string} color2 - 参与混合的第二种颜色\n * @param {number} percentage - 主颜色(color1)在混合中的占比(百分比数值,范围 0-100)\n * @param {'srgb' | 'hsl'} [colorSpace='srgb'] - 色彩空间选项:\n * - `'srgb'`: 标准 RGB 色彩空间(默认)\n * - `'hsl'`: 色相-饱和度-明度色彩空间\n * @param {number} [percentage2] - 可选参数:副颜色(color2)的独立占比。\n * 若未提供,则自动计算为 `100 - percentage`\n * @returns {string} 可直接用于 CSS 的 `color-mix()` 函数字符串(如 `color-mix(in srgb, red 30%, blue 70%)`)\n *\n * @example\n * // 基础用法(自动计算互补占比)\n * mixColor('red', 'blue', 30)\n * // 返回: \"color-mix(in srgb, red 30%, blue 70%)\"\n *\n * @example\n * // 显式指定双占比 + HSL 色彩空间\n * mixColor('#ff0000', '#0000ff', 40, 'hsl', 60)\n * // 返回: \"color-mix(in hsl, #ff0000 40%, #0000ff 60%)\"\n */\nexport const mixColor = (\n color1: string,\n color2: string,\n percentage: number,\n colorSpace: 'srgb' | 'hsl' = 'srgb',\n percentage2?: number,\n): string => {\n return `color-mix(in ${colorSpace}, ${color1} ${percentage}%, ${color2} ${percentage2 ? percentage2 : 100 - percentage}%)`\n}\n\n/**\n * 将十六进制颜色转换为 RGB\n */\nexport const hexToRgb = (hex: string): { r: number; g: number; b: number } => {\n const r = parseInt(hex.slice(1, 3), 16)\n const g = parseInt(hex.slice(3, 5), 16)\n const b = parseInt(hex.slice(5, 7), 16)\n return { r, g, b }\n}\n","/**\n * 设置 html 根元素的 css 变量\n */\nexport const setCssVar = (k: string, v: string) => {\n document.documentElement.style.setProperty(k, v)\n}\n","/**\n * 等待 <img> 加载完成\n */\nexport function whenImageLoaded(img: HTMLImageElement): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n if (img.dataset['state'] === 'loading') {\n img.src = img.dataset['src']!\n }\n\n if (img.complete && img.naturalWidth > 0) {\n resolve(img)\n return\n }\n\n const onLoad = () => {\n cleanup()\n resolve(img)\n }\n\n const onError = () => {\n cleanup()\n reject(new Error(`图片加载失败: ${img.src}`))\n }\n\n const cleanup = () => {\n img.removeEventListener('load', onLoad)\n img.removeEventListener('error', onError)\n }\n\n img.addEventListener('load', onLoad)\n img.addEventListener('error', onError)\n })\n}\n\n/**\n * 从 <img> 元素获取图片字节数据(ArrayBuffer)\n * @param img 已加载的 <img> 元素\n * @param type 图片类型,可选(默认 png)\n */\nexport async function getImageArrayBuffer(img: HTMLImageElement, type: string = 'image/png'): Promise<ArrayBuffer> {\n const canvas = document.createElement('canvas')\n canvas.width = img.naturalWidth\n canvas.height = img.naturalHeight\n const ctx = canvas.getContext('2d')\n if (!ctx) throw new Error('无法创建 Canvas 2D 上下文')\n ctx.drawImage(img, 0, 0)\n\n const blob: Blob = await new Promise((resolve, reject) => {\n canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('toBlob 失败'))), type)\n })\n\n return await blob.arrayBuffer()\n}\n\n/**\n * 等待加载完成并获取字节数据\n */\nexport async function getImgArrayBufferAfterLoad(img: HTMLImageElement, type?: string): Promise<ArrayBuffer> {\n const loaded = await whenImageLoaded(img)\n return await getImageArrayBuffer(loaded, type)\n}\n","/**\n * 网络信息\n */\nexport interface NetworkInfo extends EventTarget {\n // 带宽(估算)\n downlink: number\n // 延迟(估算)\n rtt: number\n // 类型(估算)\n effectiveType: 'slow-2g' | '2g' | '3g' | '4g'\n}\n\n/**\n * 网络状态\n * - 'offline': 无网络\n * - 'online': 在线但不支持 NetworkInformation API\n * - NetworkInformation: 含详细网络信息\n */\nexport type NetworkState = 'offline' | Omit<NetworkInfo, keyof EventTarget> | 'online'\n\nfunction isConnection(obj: unknown): obj is NetworkInfo {\n return obj !== null && typeof obj === 'object' && 'downlink' in obj && 'rtt' in obj && 'effectiveType' in obj\n}\n\n/**\n * 获取网络信息\n */\nexport function getNetworkInfo(): NetworkState {\n if (!navigator.onLine) {\n return 'offline'\n }\n\n if ('connection' in navigator && isConnection(navigator.connection)) {\n // 现代 Web API 直接获取网络连接的信息\n const connection = navigator.connection\n return {\n downlink: connection.downlink,\n rtt: connection.rtt,\n effectiveType: connection.effectiveType,\n }\n } else {\n return 'online'\n }\n}\n\n/**\n * 监听网络状态变化\n * @param listener 监听器\n * @returns 成功监听时返回解绑函数\n */\nexport function onNetworkChange(listener: (info: NetworkState) => void) {\n const handleChange = () => {\n listener(getNetworkInfo())\n }\n\n if ('connection' in navigator && isConnection(navigator.connection)) {\n // 现代 Web API\n const connection = navigator.connection\n connection.addEventListener('change', handleChange)\n return () => connection.removeEventListener('change', handleChange)\n } else {\n window.addEventListener('online', handleChange)\n window.addEventListener('offline', handleChange)\n return () => {\n window.removeEventListener('online', handleChange)\n window.removeEventListener('offline', handleChange)\n }\n }\n}\n","/** 可见性改变的监听器 */\nexport interface VisibilityChangeListener {\n (hidden: boolean): void\n}\n\n/**\n * 监听页面可见性变化(兼容旧的IE/Chrome)\n * @param listener 监听器\n * @returns 成功监听时返回解绑函数\n */\nexport function onVisibilityChange(listener: VisibilityChangeListener) {\n let hiddenPropName: string | undefined = undefined,\n hiddenEventName: string | undefined = undefined\n\n if (typeof document.hidden !== 'undefined') {\n // 现代 Web API\n hiddenPropName = 'hidden'\n hiddenEventName = 'visibilitychange'\n } else if ('msHidden' in document && typeof document.msHidden !== 'undefined') {\n // 旧 IE\n hiddenPropName = 'msHidden'\n hiddenEventName = 'msvisibilitychange'\n } else if ('webkitHidden' in document && typeof document.webkitHidden !== 'undefined') {\n // 旧 Chrome\n hiddenPropName = 'webkitHidden'\n hiddenEventName = 'webkitvisibilitychange'\n }\n\n if (!hiddenPropName || !hiddenEventName) {\n return null\n }\n\n const handler = () => {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n listener(document[hiddenPropName])\n }\n\n document.addEventListener(hiddenEventName, handler)\n\n return () => {\n document.removeEventListener(hiddenEventName, handler)\n }\n}\n","/**\n * 滚动元素到屏幕中心\n * @param el\n */\nexport async function humanScrollElIntoCenter(el: HTMLElement) {\n if (!el) return\n\n const rect = el.getBoundingClientRect()\n const doc = document.documentElement\n const body = document.body\n\n const scrollX = window.scrollX || doc.scrollLeft || body.scrollLeft\n const scrollY = window.scrollY || doc.scrollTop || body.scrollTop\n\n const viewW = window.innerWidth\n const viewH = window.innerHeight\n\n // 目标中心点相对页面的位置\n const targetX = rect.left + scrollX + rect.width / 2 - viewW / 2\n const targetY = rect.top + scrollY + rect.height / 2 - viewH / 2\n\n const maxX = Math.max(0, body.scrollWidth - viewW)\n const maxY = Math.max(0, body.scrollHeight - viewH)\n\n // 页面不需要滚动\n if (maxX === 0 && maxY === 0) return\n\n // 水平滚动:仅在可滚动时\n const newX = maxX > 0 ? Math.min(Math.max(targetX, 0), maxX) : scrollX\n // 垂直滚动\n const newY = maxY > 0 ? Math.min(Math.max(targetY, 0), maxY) : scrollY\n\n // 判断是否真正需要滚动\n const needScrollX = Math.abs(window.scrollX - newX) > 1\n const needScrollY = Math.abs(window.scrollY - newY) > 1\n if (!needScrollX && !needScrollY) return\n\n await humanScrollTo(newX, newY)\n}\n\n/**\n * 等待滚动\n */\nexport function waitScrollTo(targetX: number, targetY: number): Promise<void> {\n return new Promise((resolve) => {\n const currentX = window.scrollX\n const currentY = window.scrollY\n\n // 若无需滚动\n if (Math.abs(currentX - targetX) < 1 && Math.abs(currentY - targetY) < 1) {\n resolve()\n return\n }\n\n let timer: number | undefined\n let finished = false\n\n const cleanup = () => {\n if (finished) return\n finished = true\n window.removeEventListener('scroll', handler)\n if (timer) clearTimeout(timer)\n resolve()\n }\n\n const handler = () => {\n if (timer) clearTimeout(timer)\n // 若 50ms 内无新滚动事件,则视为滚动结束\n timer = window.setTimeout(cleanup, 50)\n }\n\n // 启动兜底超时(比如滚动事件根本不触发)\n const failSafe = window.setTimeout(() => {\n console.debug('[waitScrollTo] fallback timeout reached')\n cleanup()\n }, 1000)\n\n const cleanupWithTimeout = () => {\n cleanup()\n clearTimeout(failSafe)\n }\n\n // 替换 cleanup,确保清理超时器\n const finalHandler = () => {\n if (timer) clearTimeout(timer)\n timer = window.setTimeout(cleanupWithTimeout, 50)\n }\n\n // 注册事件\n window.addEventListener('scroll', finalHandler, { passive: true })\n\n // 立即触发滚动\n window.scrollTo({ left: targetX, top: targetY })\n\n // 有时浏览器同步滚动,不触发 scroll 事件\n requestAnimationFrame(() => {\n const nowX = window.scrollX\n const nowY = window.scrollY\n if (Math.abs(nowX - targetX) < 1 && Math.abs(nowY - targetY) < 1) {\n cleanupWithTimeout()\n }\n })\n })\n}\n\n/**\n * 模拟人类手感滚动到指定位置\n */\nexport async function humanScrollTo(targetX: number, targetY: number): Promise<void> {\n return new Promise<void>((resolve) => {\n let currentX = window.scrollX\n let currentY = window.scrollY\n\n async function step() {\n const dx = targetX - currentX\n const dy = targetY - currentY\n\n // 如果距离足够小,直接跳到目标结束\n if (Math.abs(dx) < 1 && Math.abs(dy) < 1) {\n window.scrollTo(targetX, targetY)\n resolve()\n return\n }\n\n // 随机步长 (最小 2px,最大剩余距离的 20%)\n const stepX = Math.sign(dx) * Math.min(Math.max(2, Math.random() * Math.abs(dx) * 0.2), Math.abs(dx))\n const stepY = Math.sign(dy) * Math.min(Math.max(2, Math.random() * Math.abs(dy) * 0.2), Math.abs(dy))\n\n currentX += stepX\n currentY += stepY\n await waitScrollTo(currentX, currentY)\n\n // 随机短暂停顿 10~30ms\n const delay = 10 + Math.random() * 20\n setTimeout(step, delay)\n }\n\n step()\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"sources":["../src/dom/utils/color.ts","../src/dom/utils/css.ts","../src/dom/utils/img.ts","../src/dom/utils/network.ts","../src/dom/utils/page.ts","../src/dom/utils/scroll.ts"],"sourcesContent":["/**\n * 生成 CSS `color-mix()` 函数的字符串表示,用于动态计算两种颜色的混合结果\n *\n * @param {string} color1 - 参与混合的第一种颜色(支持 CSS 合法颜色值,如 HEX、RGB、HSL 等)\n * @param {string} color2 - 参与混合的第二种颜色\n * @param {number} percentage - 主颜色(color1)在混合中的占比(百分比数值,范围 0-100)\n * @param {'srgb' | 'hsl'} [colorSpace='srgb'] - 色彩空间选项:\n * - `'srgb'`: 标准 RGB 色彩空间(默认)\n * - `'hsl'`: 色相-饱和度-明度色彩空间\n * @param {number} [percentage2] - 可选参数:副颜色(color2)的独立占比。\n * 若未提供,则自动计算为 `100 - percentage`\n * @returns {string} 可直接用于 CSS 的 `color-mix()` 函数字符串(如 `color-mix(in srgb, red 30%, blue 70%)`)\n *\n * @example\n * // 基础用法(自动计算互补占比)\n * mixColor('red', 'blue', 30)\n * // 返回: \"color-mix(in srgb, red 30%, blue 70%)\"\n *\n * @example\n * // 显式指定双占比 + HSL 色彩空间\n * mixColor('#ff0000', '#0000ff', 40, 'hsl', 60)\n * // 返回: \"color-mix(in hsl, #ff0000 40%, #0000ff 60%)\"\n */\nexport const mixColor = (\n color1: string,\n color2: string,\n percentage: number,\n colorSpace: 'srgb' | 'hsl' = 'srgb',\n percentage2?: number,\n): string => {\n return `color-mix(in ${colorSpace}, ${color1} ${percentage}%, ${color2} ${percentage2 ? percentage2 : 100 - percentage}%)`\n}\n\n/**\n * 将十六进制颜色转换为 RGB\n */\nexport const hexToRgb = (hex: string): { r: number; g: number; b: number } => {\n const r = parseInt(hex.slice(1, 3), 16)\n const g = parseInt(hex.slice(3, 5), 16)\n const b = parseInt(hex.slice(5, 7), 16)\n return { r, g, b }\n}\n","/**\n * 设置 html 根元素的 css 变量\n */\nexport const setCssVar = (k: string, v: string) => {\n document.documentElement.style.setProperty(k, v)\n}\n","/**\n * 等待 <img> 加载完成\n */\nexport function whenImageLoaded(img: HTMLImageElement): Promise<HTMLImageElement> {\n return new Promise((resolve, reject) => {\n if (img.dataset['state'] === 'loading') {\n img.src = img.dataset['src']!\n }\n\n if (img.complete && img.naturalWidth > 0) {\n resolve(img)\n return\n }\n\n const onLoad = () => {\n cleanup()\n resolve(img)\n }\n\n const onError = () => {\n cleanup()\n reject(new Error(`图片加载失败: ${img.src}`))\n }\n\n const cleanup = () => {\n img.removeEventListener('load', onLoad)\n img.removeEventListener('error', onError)\n }\n\n img.addEventListener('load', onLoad)\n img.addEventListener('error', onError)\n })\n}\n\n/**\n * 从 <img> 元素获取图片字节数据(ArrayBuffer)\n * @param img 已加载的 <img> 元素\n * @param type 图片类型,可选(默认 png)\n */\nexport async function getImageArrayBuffer(img: HTMLImageElement, type: string = 'image/png'): Promise<ArrayBuffer> {\n const canvas = document.createElement('canvas')\n canvas.width = img.naturalWidth\n canvas.height = img.naturalHeight\n const ctx = canvas.getContext('2d')\n if (!ctx) throw new Error('无法创建 Canvas 2D 上下文')\n ctx.drawImage(img, 0, 0)\n\n const blob: Blob = await new Promise((resolve, reject) => {\n canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('toBlob 失败'))), type)\n })\n\n return await blob.arrayBuffer()\n}\n\n/**\n * 等待加载完成并获取字节数据\n */\nexport async function getImgArrayBufferAfterLoad(img: HTMLImageElement, type?: string): Promise<ArrayBuffer> {\n const loaded = await whenImageLoaded(img)\n return await getImageArrayBuffer(loaded, type)\n}\n","/**\n * 网络信息\n */\nexport interface NetworkInfo extends EventTarget {\n // 带宽(估算)\n downlink: number\n // 延迟(估算)\n rtt: number\n // 类型(估算)\n effectiveType: 'slow-2g' | '2g' | '3g' | '4g'\n}\n\n/**\n * 网络状态\n * - 'offline': 无网络\n * - 'online': 在线但不支持 NetworkInformation API\n * - NetworkInformation: 含详细网络信息\n */\nexport type NetworkState = 'offline' | Omit<NetworkInfo, keyof EventTarget> | 'online'\n\nfunction isConnection(obj: unknown): obj is NetworkInfo {\n return obj !== null && typeof obj === 'object' && 'downlink' in obj && 'rtt' in obj && 'effectiveType' in obj\n}\n\n/**\n * 获取网络信息\n */\nexport function getNetworkInfo(): NetworkState {\n if (!navigator.onLine) {\n return 'offline'\n }\n\n if ('connection' in navigator && isConnection(navigator.connection)) {\n // 现代 Web API 直接获取网络连接的信息\n const connection = navigator.connection\n return {\n downlink: connection.downlink,\n rtt: connection.rtt,\n effectiveType: connection.effectiveType,\n }\n } else {\n return 'online'\n }\n}\n\n/**\n * 监听网络状态变化\n * @param listener 监听器\n * @returns 成功监听时返回解绑函数\n */\nexport function onNetworkChange(listener: (info: NetworkState) => void) {\n const handleChange = () => {\n listener(getNetworkInfo())\n }\n\n if ('connection' in navigator && isConnection(navigator.connection)) {\n // 现代 Web API\n const connection = navigator.connection\n connection.addEventListener('change', handleChange)\n return () => connection.removeEventListener('change', handleChange)\n } else {\n window.addEventListener('online', handleChange)\n window.addEventListener('offline', handleChange)\n return () => {\n window.removeEventListener('online', handleChange)\n window.removeEventListener('offline', handleChange)\n }\n }\n}\n","/** 可见性改变的监听器 */\nexport interface VisibilityChangeListener {\n (hidden: boolean): void\n}\n\n/**\n * 监听页面可见性变化(兼容旧的IE/Chrome)\n * @param listener 监听器\n * @returns 成功监听时返回解绑函数\n */\nexport function onVisibilityChange(listener: VisibilityChangeListener) {\n let hiddenPropName: string | undefined = undefined,\n hiddenEventName: string | undefined = undefined\n\n if (typeof document.hidden !== 'undefined') {\n // 现代 Web API\n hiddenPropName = 'hidden'\n hiddenEventName = 'visibilitychange'\n } else if ('msHidden' in document && typeof document.msHidden !== 'undefined') {\n // 旧 IE\n hiddenPropName = 'msHidden'\n hiddenEventName = 'msvisibilitychange'\n } else if ('webkitHidden' in document && typeof document.webkitHidden !== 'undefined') {\n // 旧 Chrome\n hiddenPropName = 'webkitHidden'\n hiddenEventName = 'webkitvisibilitychange'\n }\n\n if (!hiddenPropName || !hiddenEventName) {\n return null\n }\n\n const handler = () => {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n listener(document[hiddenPropName])\n }\n\n document.addEventListener(hiddenEventName, handler)\n\n return () => {\n document.removeEventListener(hiddenEventName, handler)\n }\n}\n","/**\n * 滚动元素到屏幕中心\n * @param el\n */\nexport async function humanScrollElIntoCenter(el: HTMLElement) {\n if (!el) return\n\n const rect = el.getBoundingClientRect()\n const doc = document.documentElement\n const body = document.body\n\n const scrollX = window.scrollX || doc.scrollLeft || body.scrollLeft\n const scrollY = window.scrollY || doc.scrollTop || body.scrollTop\n\n const viewW = window.innerWidth\n const viewH = window.innerHeight\n\n // 目标中心点相对页面的位置\n const targetX = rect.left + scrollX + rect.width / 2 - viewW / 2\n const targetY = rect.top + scrollY + rect.height / 2 - viewH / 2\n\n const maxX = Math.max(0, body.scrollWidth - viewW)\n const maxY = Math.max(0, body.scrollHeight - viewH)\n\n // 页面不需要滚动\n if (maxX === 0 && maxY === 0) return\n\n // 水平滚动:仅在可滚动时\n const newX = maxX > 0 ? Math.min(Math.max(targetX, 0), maxX) : scrollX\n // 垂直滚动\n const newY = maxY > 0 ? Math.min(Math.max(targetY, 0), maxY) : scrollY\n\n // 判断是否真正需要滚动\n const needScrollX = Math.abs(window.scrollX - newX) > 1\n const needScrollY = Math.abs(window.scrollY - newY) > 1\n if (!needScrollX && !needScrollY) return\n\n await humanScrollTo(newX, newY)\n}\n\n/**\n * 等待滚动\n */\nexport function waitScrollTo(targetX: number, targetY: number): Promise<void> {\n return new Promise((resolve) => {\n const currentX = window.scrollX\n const currentY = window.scrollY\n\n // 若无需滚动\n if (Math.abs(currentX - targetX) < 1 && Math.abs(currentY - targetY) < 1) {\n resolve()\n return\n }\n\n let timer: number | undefined\n let finished = false\n\n const cleanup = () => {\n if (finished) return\n finished = true\n window.removeEventListener('scroll', handler)\n if (timer) clearTimeout(timer)\n resolve()\n }\n\n const handler = () => {\n if (timer) clearTimeout(timer)\n // 若 50ms 内无新滚动事件,则视为滚动结束\n timer = window.setTimeout(cleanup, 50)\n }\n\n // 启动兜底超时(比如滚动事件根本不触发)\n const failSafe = window.setTimeout(() => {\n console.debug('[waitScrollTo] fallback timeout reached')\n cleanup()\n }, 1000)\n\n const cleanupWithTimeout = () => {\n cleanup()\n clearTimeout(failSafe)\n }\n\n // 替换 cleanup,确保清理超时器\n const finalHandler = () => {\n if (timer) clearTimeout(timer)\n timer = window.setTimeout(cleanupWithTimeout, 50)\n }\n\n // 注册事件\n window.addEventListener('scroll', finalHandler, { passive: true })\n\n // 立即触发滚动\n window.scrollTo({ left: targetX, top: targetY })\n\n // 有时浏览器同步滚动,不触发 scroll 事件\n requestAnimationFrame(() => {\n const nowX = window.scrollX\n const nowY = window.scrollY\n if (Math.abs(nowX - targetX) < 1 && Math.abs(nowY - targetY) < 1) {\n cleanupWithTimeout()\n }\n })\n })\n}\n\n/**\n * 模拟人类手感滚动到指定位置\n */\nexport async function humanScrollTo(targetX: number, targetY: number): Promise<void> {\n return new Promise<void>((resolve) => {\n let currentX = window.scrollX\n let currentY = window.scrollY\n\n async function step() {\n const dx = targetX - currentX\n const dy = targetY - currentY\n\n // 如果距离足够小,直接跳到目标结束\n if (Math.abs(dx) < 1 && Math.abs(dy) < 1) {\n window.scrollTo(targetX, targetY)\n resolve()\n return\n }\n\n // 随机步长 (最小 2px,最大剩余距离的 20%)\n const stepX = Math.sign(dx) * Math.min(Math.max(2, Math.random() * Math.abs(dx) * 0.2), Math.abs(dx))\n const stepY = Math.sign(dy) * Math.min(Math.max(2, Math.random() * Math.abs(dy) * 0.2), Math.abs(dy))\n\n currentX += stepX\n currentY += stepY\n await waitScrollTo(currentX, currentY)\n\n // 随机短暂停顿 10~30ms\n const delay = 10 + Math.random() * 20\n setTimeout(step, delay)\n }\n\n step()\n })\n}\n"],"mappings":"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","names":["mixColor","color1","color2","percentage","colorSpace","percentage2","hexToRgb","hex","r","g","b","setCssVar","k","v","whenImageLoaded","img","resolve","reject","onLoad","cleanup","onError","getImageArrayBuffer","type","canvas","ctx","b","getImgArrayBufferAfterLoad","loaded","isConnection","obj","getNetworkInfo","connection","onNetworkChange","listener","handleChange","onVisibilityChange","listener","hiddenPropName","hiddenEventName","handler","humanScrollElIntoCenter","el","rect","doc","body","scrollX","scrollY","viewW","viewH","targetX","targetY","maxX","maxY","newX","newY","needScrollX","needScrollY","humanScrollTo","waitScrollTo","resolve","currentX","currentY","timer","finished","cleanup","handler","failSafe","cleanupWithTimeout","finalHandler","nowX","nowY","step","dx","dy","stepX","stepY","delay"]}
package/dist/node.cjs ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";var K=Object.create;var g=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var q=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var X=(t,r)=>{for(var e in r)g(t,e,{get:r[e],enumerable:!0})},j=(t,r,e,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of V(r))!Y.call(t,o)&&o!==e&&g(t,o,{get:()=>r[o],enumerable:!(n=Q(r,o))||n.enumerable});return t};var s=(t,r,e)=>(e=t!=null?K(q(t)):{},j(r||!t||!t.__esModule?g(e,"default",{value:t,enumerable:!0}):e,t)),Z=t=>j(g({},"__esModule",{value:!0}),t);var ht={};X(ht,{copyFile:()=>pt,deleteFiles:()=>et,downloadFile:()=>nt,emptyDirectory:()=>N,ensureDir:()=>m,ensureDirSync:()=>rt,execWinCmd:()=>c,existsFile:()=>tt,exportRegedit:()=>dt,findFiles:()=>B,findFilesByPrefixAndSuffix:()=>ot,formatFileSizeFromKB:()=>at,getDirSize:()=>E,getEnv:()=>_,getFileSizeKB:()=>ft,importRegedit:()=>xt,isFile:()=>f,isFileNotFoundError:()=>w,openRegedit:()=>gt,readJSONFile:()=>C,readOrInitJSON:()=>st,showInExplorer:()=>lt,updateJSON:()=>it,writeJSONFile:()=>L});module.exports=Z(ht);function _(){let t={};for(let r in process.env)t[r]=process.env[r];return t}var d=s(require("fs/promises"),1),D=require("fs");async function f(t){try{return(await d.default.stat(t)).isFile()}catch{return!1}}async function tt(t,r){try{return d.default.access(t,r),!0}catch{return!1}}function rt(t){(0,D.mkdirSync)(t,{recursive:!0})}async function m(t){await d.default.mkdir(t,{recursive:!0})}function w(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}var p=s(require("fs/promises"),1),k=s(require("path"),1);async function N(t){let r=[],e=await p.default.readdir(t,{withFileTypes:!0});for(let n of e){let o=k.default.join(t,n.name);n.isDirectory()?(r.push(...await N(o)),await p.default.rmdir(o)):await p.default.unlink(o),r.push(o)}return r}async function et(t){return(await Promise.allSettled(t.map(async e=>(await p.default.unlink(e),e)))).filter(e=>e.status==="fulfilled").map(e=>e.value)}var O=require("stream/promises"),A=require("fs"),M=s(require("path"),1);async function nt(t,r){let e=M.default.resolve(r),n=await fetch(t);if(!n.ok||!n.body)throw new Error(`\u4E0B\u8F7D\u5931\u8D25: ${n.status}`);await(0,O.pipeline)(n.body,(0,A.createWriteStream)(e))}var y=s(require("fs/promises"),1),b=s(require("path"),1);async function ot(t,r,e){return(await y.default.readdir(t,{withFileTypes:!0})).filter(n=>n.isFile()&&n.name.startsWith(r||"")&&n.name.endsWith(e||"")).map(n=>b.default.join(t,n.name))}async function B(t,r){let e=[],n=await y.default.readdir(t,{withFileTypes:!0});for(let o of n){let i=b.default.join(t,o.name);o.isDirectory()?e.push(...await B(i,r)):(typeof r=="string"&&o.name===r||typeof r=="function"&&r(o.name,i))&&e.push(i)}return e}var u=s(require("fs/promises"),1),T=s(require("path"),1);async function C(t){if(!await f(t))throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");let r=await u.default.readFile(T.default.resolve(t),"utf-8");return JSON.parse(r)}async function L(t,r){let e=`${t}.tmp.${Date.now()}`,n=JSON.stringify(r,null,2);await u.default.writeFile(e,n,"utf-8"),await u.default.rename(e,t)}async function it(t,r){let e=await C(t),n=r(e);return await L(t,n),n}async function st(t,r){try{let e=await u.default.readFile(t,"utf-8");return JSON.parse(e)}catch(e){if(!w(e))throw e;let n=await r();return await m(T.default.dirname(t)),await u.default.writeFile(t,JSON.stringify(n,null,2),"utf-8"),n}}function U(t,r,e,n=""){if(!Number.isFinite(t))return{size:0,unit:"",text:n};let o=t,i=0;for(;o>=r&&i<e.length-1;)o/=r,i++;return{size:o,unit:e[i],text:`${o.toFixed(2).replace(/\.?0+$/,"")} ${e[i]}`}}var l=s(require("fs/promises"),1),z=s(require("path"),1);function at(t,r=""){let{size:e,unit:n,text:o}=U(t,1024,["KB","MB","GB"],r);if(o===r)return r;let i=Math.floor(e),a;return i>99?a=0:i>9?a=1:a=2,`${Math.round(e*10**a)/10**a} ${n}`}async function ct(t){try{return(await l.default.stat(t)).size}catch{return 0}}async function ut(t){try{return(await l.default.lstat(t)).size}catch{return 0}}async function E(t){let r;try{r=await l.default.readdir(t,{withFileTypes:!0})}catch{return 0}let e=[];for(let o of r){let i=z.default.join(t,o.name);if(o.isDirectory()){e.push(E(i));continue}if(o.isSymbolicLink()){e.push(ut(i));continue}e.push(ct(i))}return(await Promise.allSettled(e)).reduce((o,i)=>o+(i.status==="fulfilled"?i.value:0),0)}async function ft(t){try{let r=await l.default.stat(t);return r.isDirectory()?await E(t)/1024:r.size/1024}catch{return 0}}function S(t){let r=new Error(t??"\u64CD\u4F5C\u5DF2\u53D6\u6D88");return r.name="AbortError",r}var I=require("child_process"),P=s(require("iconv-lite"),1);async function c(t,r){return new Promise((e,n)=>{if(r?.signal?.aborted)throw S();let o=null,i=(0,I.exec)(`${t}`,{encoding:"buffer"},(x,H,J)=>{let R=P.decode(H,"cp936"),W=P.decode(J,"cp936");o&&r?.signal?.removeEventListener("abort",o);let h=x?Number(x.code??0):0;(r?.codeIsSuccess?!r.codeIsSuccess(h):h!==0)?n(new Error(`\u547D\u4EE4\u884C\u6267\u884C\u51FA\u9519 (${h}): ${W||R}`)):e(R)}),a;o=()=>{i.kill("SIGTERM"),a=setTimeout(()=>{i.kill("SIGKILL")},3e3),n(S())},r?.signal&&r.signal.addEventListener("abort",o),i.on("close",()=>{a!==void 0&&clearTimeout(a),r?.signal?.removeEventListener("abort",o)})})}var $=s(require("path"),1);async function pt(t,r,e){let n=$.default.resolve(t),o=$.default.resolve(r),i;await f(t)?(i=`copy "${n}" "${o}"`,await c(i,{signal:e})):(i=`robocopy "${n}" "${o}" /E /NFL /NDL /NJH /NJS /NC /NS /NP`,await c(i,{codeIsSuccess:a=>a<8,signal:e}))}var v=s(require("path"),1),G=s(require("fs/promises"),1);async function lt(t){t=v.default.resolve(t),(await G.stat(t)).isDirectory()?await c(`start "" "${t}"`):await c(`start "" explorer /select,"${t}"`)}var F=s(require("path"),1);async function gt(t){return c(`taskkill /f /im regedit.exe & REG ADD "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit" /v "LastKey" /d "${t}" /f & regedit`)}async function dt(t,r){r=F.default.resolve(r),await m(t),await c(`reg export "${t}" "${r}" /y`,void 0)}async function xt(t,r){await c(`reg import "${F.default.resolve(r)}"`,void 0)}0&&(module.exports={copyFile,deleteFiles,downloadFile,emptyDirectory,ensureDir,ensureDirSync,execWinCmd,existsFile,exportRegedit,findFiles,findFilesByPrefixAndSuffix,formatFileSizeFromKB,getDirSize,getEnv,getFileSizeKB,importRegedit,isFile,isFileNotFoundError,openRegedit,readJSONFile,readOrInitJSON,showInExplorer,updateJSON,writeJSONFile});
2
+ //# sourceMappingURL=node.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/node/index.ts","../src/node/utils/env.ts","../src/node/utils/file/base.ts","../src/node/utils/file/delete.ts","../src/node/utils/file/download.ts","../src/node/utils/file/find.ts","../src/node/utils/file/json.ts","../src/core/utils/number/format.ts","../src/node/utils/file/size.ts","../src/core/utils/error.ts","../src/node/utils/win/cmd.ts","../src/node/utils/win/copy.ts","../src/node/utils/win/explorer.ts","../src/node/utils/win/regedit.ts"],"sourcesContent":["export * from \"./utils/env.js\";\nexport * from \"./utils/file/base.js\";\nexport * from \"./utils/file/delete.js\";\nexport * from \"./utils/file/download.js\";\nexport * from \"./utils/file/find.js\";\nexport * from \"./utils/file/json.js\";\nexport * from \"./utils/file/size.js\";\nexport * from \"./utils/win/cmd.js\";\nexport * from \"./utils/win/copy.js\";\nexport * from \"./utils/win/explorer.js\";\nexport * from \"./utils/win/regedit.js\";\n","/**\n * 获取环境变量\n */\nexport function getEnv(): { [p: string]: string } {\n const env: { [p: string]: string } = {}\n for (const envKey in process.env) {\n env[envKey] = process.env[envKey] as string\n }\n return env\n}\n","import fs from 'node:fs/promises'\nimport { mkdirSync } from 'node:fs'\n\n/**\n * 判断路径是否是文件\n */\nexport async function isFile(filePath: string) {\n try {\n return (await fs.stat(filePath)).isFile()\n } catch {\n return false\n }\n}\n\n/**\n * 判断文件是否存在\n */\nexport async function existsFile(file: string, mode?: number) {\n try {\n fs.access(file, mode)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * 确保目录存在\n */\nexport function ensureDirSync(dirPath: string) {\n mkdirSync(dirPath, { recursive: true })\n}\n\n/**\n * 确保目录存在\n */\nexport async function ensureDir(dirPath: string) {\n await fs.mkdir(dirPath, { recursive: true })\n}\n\n/**\n * 判断错误是否为文件不存在\n */\nexport function isFileNotFoundError(error: unknown): boolean {\n return (\n typeof error === 'object' && error !== null && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT'\n )\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * 删除指定目录下的所有文件和子目录(保留指定目录)\n * @param dir 目标目录路径\n * @return 成功删除的文件或目录路径数组\n */\nexport async function emptyDirectory(dir: string) {\n const deletedPaths: string[] = []\n // 读取目录中的所有文件和子目录\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n // 如果是目录,递归删除其内容\n deletedPaths.push(...(await emptyDirectory(fullPath)))\n // 删除空目录\n await fs.rmdir(fullPath)\n } else {\n // 如果是文件,删除文件\n await fs.unlink(fullPath)\n }\n deletedPaths.push(fullPath)\n }\n return deletedPaths\n}\n\n/**\n * 删除文件(不删除目录)\n * @param paths 文件路径列表\n * @returns 成功删除的文件路径数组\n */\nexport async function deleteFiles(paths: string[]): Promise<string[]> {\n const results = await Promise.allSettled(\n paths.map(async (file) => {\n await fs.unlink(file)\n return file\n }),\n )\n\n return results.filter((r): r is PromiseFulfilledResult<string> => r.status === 'fulfilled').map((r) => r.value)\n}\n","import { pipeline } from 'node:stream/promises'\nimport { createWriteStream } from 'node:fs'\nimport path from 'node:path'\n\n/**\n * 下载文件\n */\nexport async function downloadFile(url: string, filePath: string) {\n const file = path.resolve(filePath)\n const res = await fetch(url)\n\n if (!res.ok || !res.body) {\n throw new Error(`下载失败: ${res.status}`)\n }\n\n await pipeline(res.body, createWriteStream(file))\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * 查找目录下符合条件的文件路径(不遍历子目录)\n * @param dir 目标目录路径\n * @param prefix 文件名前缀(需全字匹配)\n * @param suffix 文件名后缀(需全字匹配,如 \".txt\")\n * @return 符合条件的文件完整路径的数组\n */\nexport async function findFilesByPrefixAndSuffix(dir: string, prefix?: string, suffix?: string): Promise<string[]> {\n return (await fs.readdir(dir, { withFileTypes: true }))\n .filter(\n (dirent) =>\n // 仅保留文件(排除子目录)\n dirent.isFile() &&\n // 文件名匹配前缀\n dirent.name.startsWith(prefix || '') &&\n // 文件名匹配后缀\n dirent.name.endsWith(suffix || ''),\n )\n .map((dirent) => path.join(dir, dirent.name))\n}\n\n/**\n * 查找文件(包含子目录)\n * @param dir 目录\n * @param target 查找目标名称或者过滤函数\n */\nexport async function findFiles(\n dir: string,\n target: string | ((fileName: string, fullPath?: string) => boolean),\n): Promise<string[]> {\n const results: string[] = []\n const entries = await fs.readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n if (entry.isDirectory()) {\n results.push(...(await findFiles(fullPath, target)))\n } else if (typeof target === 'string' && entry.name === target) {\n results.push(fullPath)\n } else if (typeof target === 'function' && target(entry.name, fullPath)) {\n results.push(fullPath)\n }\n }\n return results\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport { isFileNotFoundError, ensureDir, isFile } from './base.js'\n\n/**\n * 读取 JSON 文件\n */\nexport async function readJSONFile<T>(filePath: string): Promise<T> {\n if (!(await isFile(filePath))) {\n throw new Error('文件不存在')\n }\n const jsonRaw = await fs.readFile(path.resolve(filePath), 'utf-8')\n return JSON.parse(jsonRaw) as T\n}\n\n/**\n * 将数据原子写入 JSON 文件(先写临时文件再替换)\n */\nexport async function writeJSONFile<T>(file: string, data: T): Promise<void> {\n const tempFile = `${file}.tmp.${Date.now()}`\n const jsonContent = JSON.stringify(data, null, 2)\n await fs.writeFile(tempFile, jsonContent, 'utf-8')\n // 直接替换(会覆原文件)\n await fs.rename(tempFile, file)\n}\n\n/**\n * 读取 JSON 文件,修改后写回(原子操作)\n */\nexport async function updateJSON<T>(file: string, updater: (oldData: T) => T): Promise<T> {\n const oldData = await readJSONFile<T>(file)\n const newData = updater(oldData)\n await writeJSONFile(file, newData)\n return newData\n}\n\n/**\n * 从文件读取 JSON,若文件不存在则调用 initData 初始化并写入\n *\n * @param file 文件绝对路径或相对路径\n * @param initData 返回要写入的数据的异步函数\n * @returns 解析后的数据\n */\nexport async function readOrInitJSON<T>(file: string, initData: () => Promise<T>): Promise<T> {\n try {\n const content = await fs.readFile(file, 'utf-8')\n // 解析 JSON,若失败则抛出错误(不会误判为“不存在”)\n return JSON.parse(content) as T\n } catch (error) {\n // 文件不存在等其他错误要透传\n if (!isFileNotFoundError(error)) {\n throw error\n }\n // 文件不存在,生成初始数据\n const data = await initData()\n // 确保目录存在\n await ensureDir(path.dirname(file))\n // 写入文件,格式化 JSON\n await fs.writeFile(file, JSON.stringify(data, null, 2), 'utf-8')\n return data\n }\n}\n","/**\n * 格式化后的单位数据(如文件大小)\n */\nexport interface FormattedUnitSize {\n /** 换算后该单位的数值(未四舍五入) */\n size: number\n /** 单位名称,如 'MB' */\n unit: string\n /** 显示文本:数值四舍五入保留两位小数 + 单位,如 '1.50 MB' */\n text: string\n}\n\n/**\n * 按进制自动格式化单位\n * @param value 数值\n * @param base 进制(1024 / 1000)\n * @param units 单位列表\n * @param invalidText 无效值返回文本\n */\nexport function formatUnitSize(value: number, base: number, units: string[], invalidText = ''): FormattedUnitSize {\n if (!Number.isFinite(value)) {\n return { size: 0, unit: '', text: invalidText }\n }\n\n let size = value\n let unitIndex = 0\n\n while (size >= base && unitIndex < units.length - 1) {\n size /= base\n unitIndex++\n }\n\n return {\n size,\n unit: units[unitIndex],\n text: `${size.toFixed(2).replace(/\\.?0+$/, '')} ${units[unitIndex]}`,\n }\n}\n","import { formatUnitSize } from '../../../core/utils/number/format.js'\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * 将 KB 文件大小格式化为字符串\n * @param sizeKB 文件大小(单位:KB)\n * @param invalidText 输入无效时返回文本\n */\nexport function formatFileSizeFromKB(sizeKB: number, invalidText: string = '') {\n const { size, unit, text } = formatUnitSize(sizeKB, 1024, ['KB', 'MB', 'GB'], invalidText)\n\n if (text === invalidText) {\n return invalidText\n }\n\n // 整数部分\n const integerPart = Math.floor(size)\n\n // 根据整数位数动态控制小数位\n let fractionDigits: number\n // 整数位 >= 3:不保留小数\n if (integerPart > 99) fractionDigits = 0\n // 整数位 = 2:保留 1 位小数\n else if (integerPart > 9) fractionDigits = 1\n // 整数位 = 1:保留 2 位小数\n else fractionDigits = 2\n\n const rounded = Math.round(size * 10 ** fractionDigits) / 10 ** fractionDigits\n return `${rounded} ${unit}`\n}\n\n/**\n * 获取单个文件的大小(字节)\n * @param filePath 文件路径\n * @returns 文件大小,读取失败返回 0\n */\nasync function getFileSize(filePath: string): Promise<number> {\n try {\n const stat = await fs.stat(filePath)\n return stat.size\n } catch {\n // 权限不足或文件不存在时忽略,当作 0\n return 0\n }\n}\n\n/**\n * 获取符号链接自身的大小(指向路径的字符串长度,不是目标文件大小)\n */\nasync function getSymbolicLinkSize(linkPath: string): Promise<number> {\n try {\n const stat = await fs.lstat(linkPath)\n return stat.size\n } catch {\n return 0\n }\n}\n\n/**\n * 获取目录总大小(字节),递归统计所有子文件\n * 符号链接:只计算链接文件本身的大小,不跟随指向目录\n * 权限错误:跳过该文件/目录,大小计为 0\n * @param dir 目录路径\n * @returns 总字节数\n */\nexport async function getDirSize(dir: string): Promise<number> {\n let entries\n try {\n entries = await fs.readdir(dir, { withFileTypes: true })\n } catch {\n // 无法读取目录时忽略,计为 0\n return 0\n }\n\n const tasks: Promise<number>[] = []\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name)\n\n // 目录:递归\n if (entry.isDirectory()) {\n tasks.push(getDirSize(fullPath))\n continue\n }\n\n // 符号链接:只计算链接自身大小(不跟随)\n if (entry.isSymbolicLink()) {\n tasks.push(getSymbolicLinkSize(fullPath))\n continue\n }\n\n // 普通文件\n tasks.push(getFileSize(fullPath))\n }\n\n const results = await Promise.allSettled(tasks)\n return results.reduce((total, result) => {\n return total + (result.status === 'fulfilled' ? result.value : 0)\n }, 0)\n}\n\n/**\n * 获取文件/文件夹大小\n * @param filePath 文件/文件夹路径\n */\nexport async function getFileSizeKB(filePath: string) {\n try {\n const stat = await fs.stat(filePath)\n if (stat.isDirectory()) {\n return (await getDirSize(filePath)) / 1024\n } else {\n return stat.size / 1024\n }\n } catch {\n return 0\n }\n}\n","import { CommonError } from '../error/common-error.js'\n\n/**\n * 是否为取消操作的错误\n */\nexport function isCanceledError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n\n return err.name === 'AbortError' || err.name === 'CanceledError' || ('code' in err && err.code === 'ERR_CANCELED')\n}\n\n/**\n * 创建取消错误\n * @param msg\n */\nexport function createAbortError(msg?: string): Error {\n const error = new Error(msg ?? '操作已取消')\n error.name = 'AbortError'\n return error\n}\n\n/**\n * 是否为通用错误对象\n */\nexport function isCommonError(error: unknown): error is CommonError {\n return error instanceof CommonError || (error instanceof Error && error.name === 'CommonError')\n}\n\n/**\n * 给错误消息添加前缀(空格隔开)\n */\nfunction prefixError(error: Error, prefix?: string): Error {\n if (!prefix) return error\n\n error.message = `${prefix} ${error.message}`\n return error\n}\n\n/**\n * 转换到通用异常\n */\nexport function convertToCommonError(error: unknown, prefix?: string) {\n if (isCommonError(error)) {\n return prefixError(error, prefix)\n }\n return new CommonError(`${prefix} ${getErrorMessage(error)}`, error)\n}\n\n/**\n * 获取异常信息\n */\nexport function getErrorMessage(error: unknown): string {\n if (typeof error === 'string') {\n return error\n }\n if (error instanceof Error) {\n return error.message\n }\n if (error && typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n return error.message\n }\n return String(error) ?? '未知错误'\n}\n","import { createAbortError } from '../../../core/index.js'\nimport { exec } from 'node:child_process'\nimport * as iconv from 'iconv-lite'\n\n/**\n * 命令执行的选项\n */\ninterface ExecOptions {\n /**\n * 判断结果码是否成功\n * @param number 结果码\n */\n codeIsSuccess?: (number: number) => boolean\n /**\n * 终止信号\n */\n signal?: AbortSignal\n}\n\n/**\n * 执行 windows 的命令\n * @param cmd 命令\n * @param options 执行选项\n */\nexport async function execWinCmd(cmd: string, options?: ExecOptions) {\n return new Promise<string>((resolve, reject) => {\n // 前置检查:如果 signal 已终止,直接拒绝\n if (options?.signal?.aborted) {\n throw createAbortError()\n }\n let abortHandler: (() => void) | null = null\n\n const child = exec(`${cmd}`, { encoding: 'buffer' }, (error, stdout, stderr) => {\n const stdoutStr = iconv.decode(stdout, 'cp936')\n const stderrStr = iconv.decode(stderr, 'cp936')\n\n // 清理 abort 监听\n if (abortHandler) {\n options?.signal?.removeEventListener('abort', abortHandler)\n }\n\n const exitCode = error ? Number((error as NodeJS.ErrnoException).code ?? 0) : 0\n if (options?.codeIsSuccess ? !options.codeIsSuccess(exitCode) : exitCode !== 0) {\n reject(new Error(`命令行执行出错 (${exitCode}): ${stderrStr || stdoutStr}`))\n } else {\n resolve(stdoutStr)\n }\n })\n // 进程取消后超时响应的处理定时器\n let timeout: ReturnType<typeof setTimeout>\n // 定义 abort 处理函数\n abortHandler = () => {\n // 请求终止,进程可拒绝或延迟\n child.kill('SIGTERM')\n timeout = setTimeout(() => {\n child.kill('SIGKILL')\n }, 3000)\n reject(createAbortError())\n }\n // 注册 abort 监听\n if (options?.signal) {\n options.signal.addEventListener('abort', abortHandler)\n }\n\n // 进程意外终止处理\n child.on('close', () => {\n if (timeout !== undefined) clearTimeout(timeout)\n options?.signal?.removeEventListener('abort', abortHandler)\n })\n })\n}\n","import path from 'node:path'\nimport { execWinCmd } from './cmd.js'\nimport { isFile } from '../file/base.js'\n\n/**\n * 文件拷贝工具,支持文件或文件夹\n */\nexport async function copyFile(source: string, destination: string, signal?: AbortSignal) {\n const src = path.resolve(source)\n const dest = path.resolve(destination)\n\n let command: string\n\n if (await isFile(source)) {\n // 文件用 copy 命令\n command = `copy \"${src}\" \"${dest}\"`\n await execWinCmd(command, { signal: signal })\n } else {\n // 文件夹用 robocopy\n command = `robocopy \"${src}\" \"${dest}\" /E /NFL /NDL /NJH /NJS /NC /NS /NP`\n await execWinCmd(command, {\n codeIsSuccess: (number) => number < 8,\n signal: signal,\n })\n }\n}\n","import path from 'node:path'\nimport * as fs from 'node:fs/promises'\nimport { execWinCmd } from './cmd.js'\n\n/**\n * 打开资源管理器并定位到目录或者文件\n * @param fileOrDir 定位的目录或文件\n */\nexport async function showInExplorer(fileOrDir: string) {\n fileOrDir = path.resolve(fileOrDir)\n if ((await fs.stat(fileOrDir)).isDirectory()) {\n await execWinCmd(`start \"\" \"${fileOrDir}\"`)\n } else {\n await execWinCmd(`start \"\" explorer /select,\"${fileOrDir}\"`)\n }\n}\n","import { execWinCmd } from './cmd.js'\nimport path from 'node:path'\nimport { ensureDir } from '../file/base.js'\n\n/**\n * 打开注册表\n * @param path 显示的路径\n */\nexport async function openRegedit(path: string) {\n return execWinCmd(\n `taskkill /f /im regedit.exe & REG ADD \"HKCU\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Applets\\\\Regedit\" /v \"LastKey\" /d \"${path}\" /f & regedit`,\n )\n}\n\n/**\n * 导出注册表\n * @param regPath 注册表路径\n * @param filePath 导出的文件路径\n */\nexport async function exportRegedit(regPath: string, filePath: string) {\n filePath = path.resolve(filePath)\n await ensureDir(regPath)\n await execWinCmd(`reg export \"${regPath}\" \"${filePath}\" /y`, undefined)\n}\n\n/**\n * 导入注册表\n * @param regPath 注册表路径\n * @param filePath 导入的文件路径\n */\nexport async function importRegedit(regPath: string, filePath: string) {\n await execWinCmd(`reg import \"${path.resolve(filePath)}\"`, undefined)\n}\n"],"mappings":"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","names":["node_exports","__export","copyFile","deleteFiles","downloadFile","emptyDirectory","ensureDir","ensureDirSync","execWinCmd","existsFile","exportRegedit","findFiles","findFilesByPrefixAndSuffix","formatFileSizeFromKB","getDirSize","getEnv","getFileSizeKB","importRegedit","isFile","isFileNotFoundError","openRegedit","readJSONFile","readOrInitJSON","showInExplorer","updateJSON","writeJSONFile","__toCommonJS","getEnv","env","envKey","import_promises","import_node_fs","isFile","filePath","fs","existsFile","file","mode","ensureDirSync","dirPath","ensureDir","isFileNotFoundError","error","import_promises","import_node_path","emptyDirectory","dir","deletedPaths","entries","fs","entry","fullPath","path","deleteFiles","paths","file","r","import_promises","import_node_fs","import_node_path","downloadFile","url","filePath","file","path","res","import_promises","import_node_path","findFilesByPrefixAndSuffix","dir","prefix","suffix","fs","dirent","path","findFiles","target","results","entries","entry","fullPath","import_promises","import_node_path","readJSONFile","filePath","isFile","jsonRaw","fs","path","writeJSONFile","file","data","tempFile","jsonContent","updateJSON","updater","oldData","newData","readOrInitJSON","initData","content","error","isFileNotFoundError","ensureDir","formatUnitSize","value","base","units","invalidText","size","unitIndex","import_promises","import_node_path","formatFileSizeFromKB","sizeKB","invalidText","size","unit","text","formatUnitSize","integerPart","fractionDigits","getFileSize","filePath","fs","getSymbolicLinkSize","linkPath","getDirSize","dir","entries","tasks","entry","fullPath","path","total","result","getFileSizeKB","stat","createAbortError","msg","error","import_node_child_process","iconv","execWinCmd","cmd","options","resolve","reject","createAbortError","abortHandler","child","error","stdout","stderr","stdoutStr","stderrStr","exitCode","timeout","import_node_path","copyFile","source","destination","signal","src","path","dest","command","isFile","execWinCmd","number","import_node_path","fs","showInExplorer","fileOrDir","path","execWinCmd","import_node_path","openRegedit","path","execWinCmd","exportRegedit","regPath","filePath","ensureDir","importRegedit"]}
@@ -0,0 +1,153 @@
1
+ /**
2
+ * 获取环境变量
3
+ */
4
+ declare function getEnv(): {
5
+ [p: string]: string;
6
+ };
7
+
8
+ /**
9
+ * 判断路径是否是文件
10
+ */
11
+ declare function isFile(filePath: string): Promise<boolean>;
12
+ /**
13
+ * 判断文件是否存在
14
+ */
15
+ declare function existsFile(file: string, mode?: number): Promise<boolean>;
16
+ /**
17
+ * 确保目录存在
18
+ */
19
+ declare function ensureDirSync(dirPath: string): void;
20
+ /**
21
+ * 确保目录存在
22
+ */
23
+ declare function ensureDir(dirPath: string): Promise<void>;
24
+ /**
25
+ * 判断错误是否为文件不存在
26
+ */
27
+ declare function isFileNotFoundError(error: unknown): boolean;
28
+
29
+ /**
30
+ * 删除指定目录下的所有文件和子目录(保留指定目录)
31
+ * @param dir 目标目录路径
32
+ * @return 成功删除的文件或目录路径数组
33
+ */
34
+ declare function emptyDirectory(dir: string): Promise<string[]>;
35
+ /**
36
+ * 删除文件(不删除目录)
37
+ * @param paths 文件路径列表
38
+ * @returns 成功删除的文件路径数组
39
+ */
40
+ declare function deleteFiles(paths: string[]): Promise<string[]>;
41
+
42
+ /**
43
+ * 下载文件
44
+ */
45
+ declare function downloadFile(url: string, filePath: string): Promise<void>;
46
+
47
+ /**
48
+ * 查找目录下符合条件的文件路径(不遍历子目录)
49
+ * @param dir 目标目录路径
50
+ * @param prefix 文件名前缀(需全字匹配)
51
+ * @param suffix 文件名后缀(需全字匹配,如 ".txt")
52
+ * @return 符合条件的文件完整路径的数组
53
+ */
54
+ declare function findFilesByPrefixAndSuffix(dir: string, prefix?: string, suffix?: string): Promise<string[]>;
55
+ /**
56
+ * 查找文件(包含子目录)
57
+ * @param dir 目录
58
+ * @param target 查找目标名称或者过滤函数
59
+ */
60
+ declare function findFiles(dir: string, target: string | ((fileName: string, fullPath?: string) => boolean)): Promise<string[]>;
61
+
62
+ /**
63
+ * 读取 JSON 文件
64
+ */
65
+ declare function readJSONFile<T>(filePath: string): Promise<T>;
66
+ /**
67
+ * 将数据原子写入 JSON 文件(先写临时文件再替换)
68
+ */
69
+ declare function writeJSONFile<T>(file: string, data: T): Promise<void>;
70
+ /**
71
+ * 读取 JSON 文件,修改后写回(原子操作)
72
+ */
73
+ declare function updateJSON<T>(file: string, updater: (oldData: T) => T): Promise<T>;
74
+ /**
75
+ * 从文件读取 JSON,若文件不存在则调用 initData 初始化并写入
76
+ *
77
+ * @param file 文件绝对路径或相对路径
78
+ * @param initData 返回要写入的数据的异步函数
79
+ * @returns 解析后的数据
80
+ */
81
+ declare function readOrInitJSON<T>(file: string, initData: () => Promise<T>): Promise<T>;
82
+
83
+ /**
84
+ * 将 KB 文件大小格式化为字符串
85
+ * @param sizeKB 文件大小(单位:KB)
86
+ * @param invalidText 输入无效时返回文本
87
+ */
88
+ declare function formatFileSizeFromKB(sizeKB: number, invalidText?: string): string;
89
+ /**
90
+ * 获取目录总大小(字节),递归统计所有子文件
91
+ * 符号链接:只计算链接文件本身的大小,不跟随指向目录
92
+ * 权限错误:跳过该文件/目录,大小计为 0
93
+ * @param dir 目录路径
94
+ * @returns 总字节数
95
+ */
96
+ declare function getDirSize(dir: string): Promise<number>;
97
+ /**
98
+ * 获取文件/文件夹大小
99
+ * @param filePath 文件/文件夹路径
100
+ */
101
+ declare function getFileSizeKB(filePath: string): Promise<number>;
102
+
103
+ /**
104
+ * 命令执行的选项
105
+ */
106
+ interface ExecOptions {
107
+ /**
108
+ * 判断结果码是否成功
109
+ * @param number 结果码
110
+ */
111
+ codeIsSuccess?: (number: number) => boolean;
112
+ /**
113
+ * 终止信号
114
+ */
115
+ signal?: AbortSignal;
116
+ }
117
+ /**
118
+ * 执行 windows 的命令
119
+ * @param cmd 命令
120
+ * @param options 执行选项
121
+ */
122
+ declare function execWinCmd(cmd: string, options?: ExecOptions): Promise<string>;
123
+
124
+ /**
125
+ * 文件拷贝工具,支持文件或文件夹
126
+ */
127
+ declare function copyFile(source: string, destination: string, signal?: AbortSignal): Promise<void>;
128
+
129
+ /**
130
+ * 打开资源管理器并定位到目录或者文件
131
+ * @param fileOrDir 定位的目录或文件
132
+ */
133
+ declare function showInExplorer(fileOrDir: string): Promise<void>;
134
+
135
+ /**
136
+ * 打开注册表
137
+ * @param path 显示的路径
138
+ */
139
+ declare function openRegedit(path: string): Promise<string>;
140
+ /**
141
+ * 导出注册表
142
+ * @param regPath 注册表路径
143
+ * @param filePath 导出的文件路径
144
+ */
145
+ declare function exportRegedit(regPath: string, filePath: string): Promise<void>;
146
+ /**
147
+ * 导入注册表
148
+ * @param regPath 注册表路径
149
+ * @param filePath 导入的文件路径
150
+ */
151
+ declare function importRegedit(regPath: string, filePath: string): Promise<void>;
152
+
153
+ export { copyFile, deleteFiles, downloadFile, emptyDirectory, ensureDir, ensureDirSync, execWinCmd, existsFile, exportRegedit, findFiles, findFilesByPrefixAndSuffix, formatFileSizeFromKB, getDirSize, getEnv, getFileSizeKB, importRegedit, isFile, isFileNotFoundError, openRegedit, readJSONFile, readOrInitJSON, showInExplorer, updateJSON, writeJSONFile };
package/dist/node.d.ts CHANGED
@@ -1,2 +1,153 @@
1
- export * from './node/index.js';
2
- export {};
1
+ /**
2
+ * 获取环境变量
3
+ */
4
+ declare function getEnv(): {
5
+ [p: string]: string;
6
+ };
7
+
8
+ /**
9
+ * 判断路径是否是文件
10
+ */
11
+ declare function isFile(filePath: string): Promise<boolean>;
12
+ /**
13
+ * 判断文件是否存在
14
+ */
15
+ declare function existsFile(file: string, mode?: number): Promise<boolean>;
16
+ /**
17
+ * 确保目录存在
18
+ */
19
+ declare function ensureDirSync(dirPath: string): void;
20
+ /**
21
+ * 确保目录存在
22
+ */
23
+ declare function ensureDir(dirPath: string): Promise<void>;
24
+ /**
25
+ * 判断错误是否为文件不存在
26
+ */
27
+ declare function isFileNotFoundError(error: unknown): boolean;
28
+
29
+ /**
30
+ * 删除指定目录下的所有文件和子目录(保留指定目录)
31
+ * @param dir 目标目录路径
32
+ * @return 成功删除的文件或目录路径数组
33
+ */
34
+ declare function emptyDirectory(dir: string): Promise<string[]>;
35
+ /**
36
+ * 删除文件(不删除目录)
37
+ * @param paths 文件路径列表
38
+ * @returns 成功删除的文件路径数组
39
+ */
40
+ declare function deleteFiles(paths: string[]): Promise<string[]>;
41
+
42
+ /**
43
+ * 下载文件
44
+ */
45
+ declare function downloadFile(url: string, filePath: string): Promise<void>;
46
+
47
+ /**
48
+ * 查找目录下符合条件的文件路径(不遍历子目录)
49
+ * @param dir 目标目录路径
50
+ * @param prefix 文件名前缀(需全字匹配)
51
+ * @param suffix 文件名后缀(需全字匹配,如 ".txt")
52
+ * @return 符合条件的文件完整路径的数组
53
+ */
54
+ declare function findFilesByPrefixAndSuffix(dir: string, prefix?: string, suffix?: string): Promise<string[]>;
55
+ /**
56
+ * 查找文件(包含子目录)
57
+ * @param dir 目录
58
+ * @param target 查找目标名称或者过滤函数
59
+ */
60
+ declare function findFiles(dir: string, target: string | ((fileName: string, fullPath?: string) => boolean)): Promise<string[]>;
61
+
62
+ /**
63
+ * 读取 JSON 文件
64
+ */
65
+ declare function readJSONFile<T>(filePath: string): Promise<T>;
66
+ /**
67
+ * 将数据原子写入 JSON 文件(先写临时文件再替换)
68
+ */
69
+ declare function writeJSONFile<T>(file: string, data: T): Promise<void>;
70
+ /**
71
+ * 读取 JSON 文件,修改后写回(原子操作)
72
+ */
73
+ declare function updateJSON<T>(file: string, updater: (oldData: T) => T): Promise<T>;
74
+ /**
75
+ * 从文件读取 JSON,若文件不存在则调用 initData 初始化并写入
76
+ *
77
+ * @param file 文件绝对路径或相对路径
78
+ * @param initData 返回要写入的数据的异步函数
79
+ * @returns 解析后的数据
80
+ */
81
+ declare function readOrInitJSON<T>(file: string, initData: () => Promise<T>): Promise<T>;
82
+
83
+ /**
84
+ * 将 KB 文件大小格式化为字符串
85
+ * @param sizeKB 文件大小(单位:KB)
86
+ * @param invalidText 输入无效时返回文本
87
+ */
88
+ declare function formatFileSizeFromKB(sizeKB: number, invalidText?: string): string;
89
+ /**
90
+ * 获取目录总大小(字节),递归统计所有子文件
91
+ * 符号链接:只计算链接文件本身的大小,不跟随指向目录
92
+ * 权限错误:跳过该文件/目录,大小计为 0
93
+ * @param dir 目录路径
94
+ * @returns 总字节数
95
+ */
96
+ declare function getDirSize(dir: string): Promise<number>;
97
+ /**
98
+ * 获取文件/文件夹大小
99
+ * @param filePath 文件/文件夹路径
100
+ */
101
+ declare function getFileSizeKB(filePath: string): Promise<number>;
102
+
103
+ /**
104
+ * 命令执行的选项
105
+ */
106
+ interface ExecOptions {
107
+ /**
108
+ * 判断结果码是否成功
109
+ * @param number 结果码
110
+ */
111
+ codeIsSuccess?: (number: number) => boolean;
112
+ /**
113
+ * 终止信号
114
+ */
115
+ signal?: AbortSignal;
116
+ }
117
+ /**
118
+ * 执行 windows 的命令
119
+ * @param cmd 命令
120
+ * @param options 执行选项
121
+ */
122
+ declare function execWinCmd(cmd: string, options?: ExecOptions): Promise<string>;
123
+
124
+ /**
125
+ * 文件拷贝工具,支持文件或文件夹
126
+ */
127
+ declare function copyFile(source: string, destination: string, signal?: AbortSignal): Promise<void>;
128
+
129
+ /**
130
+ * 打开资源管理器并定位到目录或者文件
131
+ * @param fileOrDir 定位的目录或文件
132
+ */
133
+ declare function showInExplorer(fileOrDir: string): Promise<void>;
134
+
135
+ /**
136
+ * 打开注册表
137
+ * @param path 显示的路径
138
+ */
139
+ declare function openRegedit(path: string): Promise<string>;
140
+ /**
141
+ * 导出注册表
142
+ * @param regPath 注册表路径
143
+ * @param filePath 导出的文件路径
144
+ */
145
+ declare function exportRegedit(regPath: string, filePath: string): Promise<void>;
146
+ /**
147
+ * 导入注册表
148
+ * @param regPath 注册表路径
149
+ * @param filePath 导入的文件路径
150
+ */
151
+ declare function importRegedit(regPath: string, filePath: string): Promise<void>;
152
+
153
+ export { copyFile, deleteFiles, downloadFile, emptyDirectory, ensureDir, ensureDirSync, execWinCmd, existsFile, exportRegedit, findFiles, findFilesByPrefixAndSuffix, formatFileSizeFromKB, getDirSize, getEnv, getFileSizeKB, importRegedit, isFile, isFileNotFoundError, openRegedit, readJSONFile, readOrInitJSON, showInExplorer, updateJSON, writeJSONFile };