@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
@@ -1 +0,0 @@
1
- {"version":3,"file":"node.umd.cjs","names":[],"sources":["../src/node/utils/env.ts","../src/node/utils/file/base.ts","../src/node/utils/file/delete.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":["/**\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'\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 async function ensureDirSync(dirPath: string) {\n fs.mkdir(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 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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"}