@ybgnb/utils 0.2.3 → 0.2.5
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/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +61 -36
- package/dist/core.d.ts +61 -36
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/node.cjs +1 -1
- package/dist/node.cjs.map +1 -1
- package/dist/node.js +1 -1
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
package/dist/core.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var l=Object.defineProperty;var
|
|
1
|
+
"use strict";var l=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var z=(t,r)=>{for(var e in r)l(t,e,{get:r[e],enumerable:!0})},A=(t,r,e,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of k(r))!S.call(t,o)&&o!==e&&l(t,o,{get:()=>r[o],enumerable:!(n=B(r,o))||n.enumerable});return t};var D=t=>A(l({},"__esModule",{value:!0}),t);var gt={};z(gt,{CommonError:()=>c,bizErrorToError:()=>T,checkAbortSignal:()=>M,chunk:()=>G,convertToCommonError:()=>F,createAbortError:()=>a,createResponseError:()=>E,dynamicCall:()=>K,encodeURLParams:()=>ft,execBiz:()=>N,fetchWithFormat:()=>d,formatDate:()=>g,formatTime:()=>st,formatUnitSize:()=>pt,getErrorMessage:()=>u,getFormattedDate:()=>ot,getFormattedDateTime:()=>nt,getFormattedTime:()=>it,getGithubRawJson:()=>J,inArray:()=>H,invalidCharRegex:()=>at,isBizResult:()=>y,isCanceledError:()=>O,isCommonError:()=>w,isEmptyArr:()=>L,isHttpUrl:()=>ut,isObject:()=>C,isPlainObject:()=>h,mergeQueryParams:()=>mt,omitUndefined:()=>q,parseGithubRawUrl:()=>R,parseGithubRepoUrl:()=>_,parseUrl:()=>ct,processRangeInBatches:()=>W,random1ToN:()=>rt,randomFloat:()=>X,randomInt:()=>p,runRandomFunctions:()=>Q,sample:()=>Z,sampleOne:()=>tt,sampleWithReplacement:()=>v,serializeError:()=>m,shuffle:()=>P,sleep:()=>$,sleepRandom:()=>et,stripFunctions:()=>x,unwrapBizResult:()=>I,withRetry:()=>Y,withTimeout:()=>V});module.exports=D(gt);function O(t){return t instanceof Error?t.name==="AbortError"||t.name==="CanceledError"||"code"in t&&t.code==="ERR_CANCELED":!1}function a(t){let r=new Error(t??"\u64CD\u4F5C\u5DF2\u53D6\u6D88");return r.name="AbortError",r}function w(t){return t instanceof c||t instanceof Error&&t.name==="CommonError"}function U(t,r){return r&&(t.message=`${r} ${t.message}`),t}function F(t,r){return w(t)?U(t,r):new c(`${r} ${u(t)}`,t)}function u(t){return typeof t=="string"?t:t instanceof Error||t&&typeof t=="object"&&"message"in t&&typeof t.message=="string"?t.message:String(t)??"\u672A\u77E5\u9519\u8BEF"}var c=class extends Error{constructor(r,e){super(r),e!==void 0&&(this.cause=e instanceof Error?e:new Error(u(e))),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}toString(){return`${this.name}: ${this.message}${this.cause?` (caused by ${this.cause})`:""}`}};function M(t){if(t?.aborted)throw a()}function L(t){return Array.isArray(t)&&t.length===0}function G(t,r){if(!Number.isInteger(r)||r<1)throw new Error("size \u5FC5\u987B\u662F\u6B63\u6574\u6570");let e=[];for(let n=0;n<t.length;n+=r)e.push(t.slice(n,n+r));return e}function H(t,r){return r.includes(t)}function h(t){if(t===null||typeof t!="object")return!1;let r=Object.getPrototypeOf(t);return r===Object.prototype||r===null}function C(t,r){return t===null||typeof t!="object"||Array.isArray(t)?!1:r!==void 0?r in t:!0}function x(t,r=new WeakMap){if(typeof t=="function")return;if(t===null||typeof t!="object"||t instanceof Date||t instanceof RegExp||t instanceof Map||t instanceof Set||ArrayBuffer.isView(t)||t instanceof ArrayBuffer)return t;if(r.has(t))return r.get(t);if(Array.isArray(t)){let n=[];r.set(t,n);for(let o of t)n.push(x(o,r));return n}if(!h(t))return t;let e={};r.set(t,e);for(let[n,o]of Object.entries(t)){if(typeof o=="function")continue;let i=x(o,r);i!==void 0&&(e[n]=i)}return e}function m(t){return t instanceof Error?{name:t.name,message:t.message,stack:t.stack,cause:"cause"in t&&t.cause?m(t.cause):void 0}:{name:"UnknownError",message:u(t)}}function y(t){return t!=null&&typeof t=="object"&&"success"in t&&typeof t.success=="boolean"&&"msg"in t&&typeof t.msg=="string"}var N=async t=>{try{return{success:!0,msg:"\u64CD\u4F5C\u6210\u529F",data:await t()}}catch(r){return{success:!1,msg:"\u64CD\u4F5C\u5931\u8D25",error:m(r)}}};async function I(t){if(y(t)){if(t.success)return t.data;throw t.error?T(t.error):new Error(t.msg)}else return t}function T(t){return Object.assign(new Error,t)}function E(t){return new Error(`HTTP ${t.status}: ${t.statusText}`)}async function d(t,r,e){let n=await fetch(t,e);if(!n.ok)throw E(n);switch(r){case"json":return await n.json();case"text":return await n.text();case"blob":return await n.blob();case"arrayBuffer":return await n.arrayBuffer();case"formData":return await n.formData();default:return await n.text()}}function Q(t,r){r=r??t.length;let e=Math.floor(Math.random()*r)+1,n=[...t];for(let i=n.length-1;i>0;i--){let s=Math.floor(Math.random()*(i+1));[n[i],n[s]]=[n[s],n[i]]}let o=n.slice(0,e);for(let i of o)i()}async function W(t,r,e,n){for(let o=t;o<=r;o+=e){let i=r-o+1,s=Math.min(e,i),f=Array.from({length:s},(b,j)=>o+j);await n(f)}}function K(t,r,...e){let n=r.split("."),o=t,i=0;for(;i<n.length-1;i++)if(o=o[n[i]],o==null)throw new Error(`Path '${n.slice(0,i+1).join(".")}' is null/undefined`);let s=o[n[i]];if(typeof s!="function")throw new Error(`'${r}' is not a function`);return s.apply(o,e)}function _(t){t=t.replace(/^git\+/,"");let r=/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git|\/)?$/,e=/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)$/,n=t.match(r);if(n)return{owner:n[1],repo:n[2],branch:"main"};let o=t.match(e);if(o)return{owner:o[1],repo:o[2],branch:o[3]};throw new Error("\u89E3\u6790\u63D2\u4EF6\u4ED3\u5E93URL\u5931\u8D25")}function R(t){let{owner:r,repo:e,branch:n,filePath:o}=t;return`https://raw.githubusercontent.com/${r}/${e}/${n}/${o}`}async function J(t){return JSON.parse(await d(R(t),"json"))}var q=t=>Object.fromEntries(Object.entries(t).filter(([r,e])=>e!==void 0));async function V(t,r){return await Promise.race([t,new Promise((e,n)=>{setTimeout(()=>{n(new Error("Timeout"))},r)})])}async function Y(t,r=3,e=1e3){for(let n=0;n<r;n++)try{return await t()}catch(o){if(n===r-1)throw o;await new Promise(i=>setTimeout(i,e))}throw new Error("Unreachable")}function P(t){let r=t.slice();for(let e=r.length-1;e>0;e--){let n=Math.floor(Math.random()*(e+1));[r[e],r[n]]=[r[n],r[e]]}return r}function p(t,r){return Math.floor(Math.random()*(r-t+1))+t}function X(t,r){return Math.random()*(r-t)+t}function Z(t,r){return P(t).slice(0,r)}function v(t,r){let e=[];for(let n=0;n<r;n++){let o=p(0,t.length-1);e.push(t[o])}return e}function tt(t){return t[p(0,t.length-1)]}function rt(t){return p(1,t)}function $(t,r){return new Promise((e,n)=>{if(r?.aborted)return n(a());let o=setTimeout(()=>{s(),e()},t),i=()=>{clearTimeout(o),s(),n(a())},s=()=>{r?.removeEventListener("abort",i)};r?.addEventListener("abort",i)})}function et(t,r,e){let n=Math.random()*(r-t)+t;return $(n,e)}function g(t,r){let e=isNaN(t.getTime())?new Date:t,n=e.getFullYear(),o=String(e.getMonth()+1).padStart(2,"0"),i=String(e.getDate()).padStart(2,"0"),s=String(e.getHours()).padStart(2,"0"),f=String(e.getMinutes()).padStart(2,"0"),b=String(e.getSeconds()).padStart(2,"0");return r==="date"?`${n}-${o}-${i}`:r==="time"?`${s}:${f}:${b}`:`${n}-${o}-${i} ${s}:${f}:${b}`}function nt(t){return g(t??new Date,"datetime")}function ot(t){return g(t??new Date,"date")}function it(t){return g(t??new Date,"time")}function st(t){if(typeof t>"u")return"";if(t instanceof Date)return t.toLocaleString();{let r=typeof t=="string"?Number(t):t;return isNaN(r)?"":new Date(r<1e12?r*1e3:r).toLocaleString()}}var at=/[!'()*]/g;function ut(t){try{let r=new URL(t);return r.protocol==="http:"||r.protocol==="https:"}catch{return!1}}function ct(t){try{let{origin:r,pathname:e,searchParams:n}=new URL(t);return{baseUrl:r+e,searchParams:n}}catch{throw new Error("Invalid URL")}}function ft(t,r=!1){return Object.keys(t).sort().filter(e=>r||t[e]!=null).map(e=>{let n=t[e]??"";return`${encodeURIComponent(e)}=${encodeURIComponent(String(n))}`}).join("&")}function mt(t,r){let e={};return t.size>0&&t.forEach((n,o)=>{e[o]=n}),r&&(e={...e,...r}),e}function pt(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]}`}}
|
|
2
2
|
//# sourceMappingURL=core.cjs.map
|
package/dist/core.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/index.ts","../src/core/error/common-error.ts","../src/core/utils/error.ts","../src/core/result/biz-result.ts","../src/core/result/exec-biz.ts","../src/core/utils/array.ts","../src/core/utils/fetch.ts","../src/core/utils/function.ts","../src/core/utils/github.ts","../src/core/utils/promise.ts","../src/core/utils/random.ts","../src/core/utils/type.ts","../src/core/utils/serialize.ts","../src/core/utils/sleep.ts","../src/core/utils/time.ts","../src/core/utils/url.ts","../src/core/utils/number/format.ts"],"sourcesContent":["export * from \"./error/common-error.js\";\nexport * from \"./result/biz-result.js\";\nexport * from \"./result/exec-biz.js\";\nexport * from \"./types/helpers.js\";\nexport * from \"./types/physical.js\";\nexport * from \"./utils/array.js\";\nexport * from \"./utils/error.js\";\nexport * from \"./utils/fetch.js\";\nexport * from \"./utils/function.js\";\nexport * from \"./utils/github.js\";\nexport * from \"./utils/promise.js\";\nexport * from \"./utils/random.js\";\nexport * from \"./utils/serialize.js\";\nexport * from \"./utils/sleep.js\";\nexport * from \"./utils/time.js\";\nexport * from \"./utils/type.js\";\nexport * from \"./utils/url.js\";\nexport * from \"./utils/number/format.js\";\n","/**\n * 通用错误\n */\nexport class CommonError extends Error {\n // 原始错误\n rawError?: Error\n\n constructor(message: string, rawError?: unknown) {\n super(message)\n\n if (rawError !== undefined) {\n this.rawError = rawError instanceof Error ? rawError : new Error(String(rawError))\n }\n\n this.name = new.target.name\n Object.setPrototypeOf(this, new.target.prototype)\n }\n\n toString() {\n return `${this.name}: ${this.message}${this.rawError ? ` (caused by ${this.rawError})` : ''}`\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 { getErrorMessage } from '../utils/error.js'\n\n/**\n * 业务执行结果\n */\nexport class BizResult<T> {\n success: boolean\n msg: string\n data?: T\n errorName?: string\n\n constructor(success: boolean, msg: string, data?: T) {\n this.success = success\n this.msg = msg\n this.data = data\n }\n\n static createSuccess<T>(data: T) {\n return new BizResult(true, '操作成功', data)\n }\n\n static createFail<T>(msg: string = '操作失败', data?: T) {\n return new BizResult(false, msg, data)\n }\n\n static createError<T>(e: unknown) {\n const bizResult = new BizResult<T>(false, getErrorMessage(e))\n if (e && typeof e === 'object' && 'name' in e && typeof e.name === 'string') {\n bizResult.errorName = e.name\n }\n return bizResult\n }\n\n /**\n * 展开成Promise\n */\n async toPromise(): Promise<void>\n async toPromise<T>(): Promise<T>\n async toPromise<T = void>(): Promise<T> {\n if (this.success) {\n return this.data as T\n } else {\n const error = new Error(this.msg)\n if (this.errorName) error.name = this.errorName\n throw error\n }\n }\n}\n","import { BizResult } from './biz-result.js'\n\n/**\n * 执行业务(自动包裹BuResult)\n * @param run 执行方法\n */\nexport const execBiz = async <T>(run: () => Promise<T>): Promise<BizResult<T>> => {\n try {\n return BizResult.createSuccess<T>(await run())\n } catch (e) {\n return BizResult.createError(e)\n }\n}\n","/**\n * 是否为空数组\n * @param data\n */\nexport function isEmptyArr(data: unknown) {\n return Array.isArray(data) && data.length === 0\n}\n\n/**\n * 将数组分块为指定大小的多个子数组\n * @param arr 原数组\n * @param size 每个块的长度\n * @example chunk([1,2,3,4,5], 2) => [[1,2],[3,4],[5]]\n */\nexport function chunk<T>(arr: T[], size: number): T[][] {\n if (!Number.isInteger(size) || size < 1) throw new Error('size 必须是正整数')\n const result: T[][] = []\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size))\n }\n return result\n}\n","/**\n * 用于指定 `fetch` 响应体的解析方式。\n * 对应 `Response` 对象的不同解析方法(`.json()`、`.text()` 等)。\n */\nexport type ResponseBodyFormat =\n | 'json' // 解析为 JSON 对象\n | 'text' // 解析为原始字符串\n | 'blob' // 解析为 Blob 对象(适用于二进制文件,如图片、压缩包)\n | 'arrayBuffer' // 解析为 ArrayBuffer(适用于底层二进制操作)\n | 'formData' // 解析为 FormData 对象(适用于处理 multipart/form-data 响应)\n\n/**\n * 创建响应错误\n */\nexport function createResponseError(response: Response) {\n return new Error(`HTTP ${response.status}: ${response.statusText}`)\n}\n\n/**\n * 使用指定的响应解析格式发起 fetch 请求\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function fetchWithFormat<T = any>(\n url: string,\n format: ResponseBodyFormat,\n init?: RequestInit,\n): Promise<T> {\n const response = await fetch(url, init)\n if (!response.ok) {\n throw createResponseError(response)\n }\n switch (format) {\n case 'json':\n return (await response.json()) as T\n case 'text':\n return (await response.text()) as T\n case 'blob':\n return (await response.blob()) as T\n case 'arrayBuffer':\n return (await response.arrayBuffer()) as T\n case 'formData':\n return (await response.formData()) as T\n default:\n return (await response.text()) as T\n }\n}\n","/**\n * 随机运行参数\n * @param fns\n * @param maxCount 最多执行次数(1 ~ fns.length)\n */\nexport function runRandomFunctions(fns: Array<() => void>, maxCount?: number): void {\n maxCount = maxCount ?? fns.length\n const count = Math.floor(Math.random() * maxCount) + 1\n\n const shuffled = [...fns]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n\n const selected = shuffled.slice(0, count)\n\n for (const fn of selected) {\n fn()\n }\n}\n\n/**\n * 将数字范围按批次处理,每个批次调用一次异步函数\n *\n * @param start - 起始值(包含)\n * @param end - 结束值(包含)\n * @param batchSize - 每批最大元素个数\n * @param processor - 处理单批数字数组的异步函数\n *\n * @example\n * // 分批处理 1..10,每批最多 3 个数字\n * await processRangeInBatches(1, 10, 3, async (batch) => {\n * console.log(batch) // 输出: [1,2,3], [4,5,6], [7,8,9], [10]\n * })\n */\nexport async function processRangeInBatches(\n start: number,\n end: number,\n batchSize: number,\n processor: (batch: number[]) => Promise<void>,\n): Promise<void> {\n for (let current = start; current <= end; current += batchSize) {\n const remaining = end - current + 1\n const actualSize = Math.min(batchSize, remaining)\n const batch = Array.from({ length: actualSize }, (_, idx) => current + idx)\n\n await processor(batch)\n }\n}\n\n/**\n * 动态调用\n * @param root 对象\n * @param path 调用路径\n * @param args 参数\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport function dynamicCall<T = any>(root: any, path: string, ...args: any[]): T {\n const segments = path.split('.')\n let context = root\n let i = 0\n for (; i < segments.length - 1; i++) {\n context = context[segments[i]]\n if (context == null) {\n throw new Error(`Path '${segments.slice(0, i + 1).join('.')}' is null/undefined`)\n }\n }\n const fn = context[segments[i]]\n if (typeof fn !== 'function') {\n throw new Error(`'${path}' is not a function`)\n }\n return fn.apply(context, args)\n}\n","import { fetchWithFormat } from './fetch.js'\n\n/** github 仓库信息 */\nexport interface GitHubRepo {\n /** 仓库所有者(用户或组织名) */\n owner: string\n /** 仓库名称 */\n repo: string\n}\n\n/** github 仓库分支信息 */\nexport type GitHubRepoBranch = GitHubRepo & {\n /** 分支名称 */\n branch: string\n}\n\n/** github 仓库文件信息 */\nexport type GitHubRepoFile = GitHubRepoBranch & {\n /** 文件路径 */\n filePath: string\n}\n\n/**\n * 从 url 解析 github 仓库信息\n * @param repositoryUrl 仓库 URL(支持带有git+前缀、具体文件路径)\n */\nexport function parseGithubRepoUrl(repositoryUrl: string): GitHubRepoBranch {\n // 去掉 git+ 前缀\n repositoryUrl = repositoryUrl.replace(/^git\\+/, '')\n\n const regexBase = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git|\\/)?$/\n const regexFile = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+)\\/blob\\/([^/]+)$/\n\n // 情况 1:git clone 地址 或 repo 根路径\n const m1 = repositoryUrl.match(regexBase)\n if (m1) {\n return {\n owner: m1[1],\n repo: m1[2],\n branch: 'main', // 默认分支 main\n }\n }\n\n // 情况 2:具体文件路径\n const m2 = repositoryUrl.match(regexFile)\n if (m2) {\n return {\n owner: m2[1],\n repo: m2[2],\n branch: m2[3],\n }\n }\n\n throw new Error('解析插件仓库URL失败')\n}\n\n/**\n * 解析 github 仓库文件的原始内容的 url\n */\nexport function parseGithubRawUrl(gitHubRepoFile: GitHubRepoFile) {\n const { owner, repo, branch, filePath } = gitHubRepoFile\n return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`\n}\n\n/**\n * 获取 github 仓库文件的 json 内容并解析为对象 T\n * @param gitHubRepoFile\n */\nexport async function getGithubRawJson<O>(gitHubRepoFile: GitHubRepoFile): Promise<O> {\n return JSON.parse(await fetchWithFormat(parseGithubRawUrl(gitHubRepoFile), 'json')) as O\n}\n","/**\n * 为 Promise 添加超时控制\n * @param promise - 原始异步任务\n * @param timeout - 超时时间(毫秒)\n * @returns 若原 Promise 在超时前完成,则返回其结果;否则抛出超时错误\n * @throws {Error} 超时后抛出 'Timeout' 错误\n */\nexport async function withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error('Timeout'))\n }, timeout)\n }),\n ])\n}\n\n/**\n * 带重试机制的异步函数执行器\n * @param fn - 返回 Promise 的异步函数\n * @param retries - 最大重试次数(默认 3)\n * @param delay - 重试间隔毫秒(默认 1000)\n * @returns Promise<T>\n */\nexport async function withRetry<T>(fn: () => Promise<T>, retries: number = 3, delay: number = 1000): Promise<T> {\n for (let i = 0; i < retries; i++) {\n try {\n return await fn()\n } catch (error) {\n if (i === retries - 1) throw error\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n throw new Error('Unreachable')\n}\n","/**\n * 数组洗牌\n */\nexport function shuffle<T>(arr: T[]): T[] {\n const clone = arr.slice()\n for (let i = clone.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[clone[i], clone[j]] = [clone[j], clone[i]]\n }\n return clone\n}\n\n/**\n * 获取 [min, max] 的随机整数\n */\nexport function randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * 获取 [min, max) 的随机浮点数\n */\nexport function randomFloat(min: number, max: number): number {\n return Math.random() * (max - min) + min\n}\n\n/**\n * 从数组中随机取若干项,不可重复\n */\nexport function sample<T>(arr: T[], count: number): T[] {\n return shuffle(arr).slice(0, count)\n}\n\n/**\n * 从数组中随机取若干项,可重复\n */\nexport function sampleWithReplacement<T>(arr: T[], count: number): T[] {\n const result: T[] = []\n for (let i = 0; i < count; i++) {\n const index = randomInt(0, arr.length - 1)\n result.push(arr[index])\n }\n return result\n}\n\n/**\n * 从数组中获取随机项\n */\nexport function sampleOne<T>(arr: T[]): T {\n return arr[randomInt(0, arr.length - 1)]\n}\n\n/**\n * 获取 1~n 的随机数\n */\nexport function random1ToN(n: number): number {\n return randomInt(1, n)\n}\n","import type { ObjectWithKeys } from '../types/helpers.js'\n\n/**\n * 判断一个值是否为纯对象\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n/**\n * 包括类实例、普通对象,不包括 数组、null 和原始类型\n */\nexport function isObject<K extends string | symbol>(value: unknown, key?: K): value is ObjectWithKeys<K> {\n if (value === null || typeof value !== 'object') return false\n if (Array.isArray(value)) return false\n if (key !== undefined) {\n return key in value\n }\n return true\n}\n","import { isPlainObject } from './type.js'\n\n/**\n * 递归移除对象(或数组)中的所有函数属性,返回一个可安全序列化的副本。\n *\n * @typeParam T - 输入值的类型。\n * @param value - 待处理的值(基本类型、对象、数组等)。\n * @param seen - 内部使用的 WeakMap,用于记录已访问过的对象,防止循环引用。\n * 调用方通常无需传递此参数。\n * @returns 处理后的新副本,其中所有函数属性都被移除。\n * 基本类型(string, number, boolean, null, undefined)将原样返回。\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function stripFunctions<T>(value: T, seen = new WeakMap<object, any>()): T {\n if (typeof value === 'function') return undefined as T\n if (value === null || typeof value !== 'object') return value\n\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Map ||\n value instanceof Set ||\n ArrayBuffer.isView(value) ||\n value instanceof ArrayBuffer\n ) {\n return value\n }\n\n if (seen.has(value as object)) {\n return seen.get(value as object)\n }\n\n if (Array.isArray(value)) {\n const out: unknown[] = []\n seen.set(value as object, out)\n for (const item of value) {\n out.push(stripFunctions(item, seen))\n }\n return out as T\n }\n\n if (!isPlainObject(value)) {\n return value\n }\n\n const out: Record<string, unknown> = {}\n seen.set(value as object, out)\n\n for (const [key, item] of Object.entries(value)) {\n if (typeof item === 'function') continue\n const next = stripFunctions(item, seen)\n if (next !== undefined) out[key] = next\n }\n\n return out as T\n}\n\n/**\n * 将错误转换为可 JSON 序列化的普通对象。\n */\nexport function serializeError(err: unknown) {\n if (err instanceof Error) {\n return {\n name: err.name,\n message: err.message,\n stack: err.stack,\n }\n }\n\n return {\n name: 'Error',\n message: String(err),\n }\n}\n","import { createAbortError } from './error.js'\n\n/**\n * 支持取消的 sleep\n * @param ms 延迟毫秒数\n * @param signal 取消信号\n */\nexport function sleep(ms: number, signal?: AbortSignal) {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) return reject(createAbortError())\n\n const timer = setTimeout(() => {\n cleanup()\n resolve()\n }, ms)\n\n const onAbort = () => {\n clearTimeout(timer)\n cleanup()\n reject(createAbortError())\n }\n\n const cleanup = () => {\n signal?.removeEventListener('abort', onAbort)\n }\n\n signal?.addEventListener('abort', onAbort)\n })\n}\n\n/**\n * 支持取消的随机 sleep\n * @param minMS 最小延迟毫秒数\n * @param maxMS 最大延迟毫秒数\n * @param signal 取消信号\n */\nexport function sleepRandom(minMS: number, maxMS: number, signal?: AbortSignal) {\n const ms = Math.random() * (maxMS - minMS) + minMS\n return sleep(ms, signal)\n}\n","/**\n * 格式化日期\n */\nexport function formatDate(date: Date, pattern: 'date' | 'time' | 'datetime'): string {\n const d = !isNaN(date.getTime()) ? date : new Date()\n const year = d.getFullYear()\n const month = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n const hours = String(d.getHours()).padStart(2, '0')\n const minutes = String(d.getMinutes()).padStart(2, '0')\n const seconds = String(d.getSeconds()).padStart(2, '0')\n\n if (pattern === 'date') return `${year}-${month}-${day}`\n if (pattern === 'time') return `${hours}:${minutes}:${seconds}`\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\n/**\n * 格式化时间为 2020-02-02 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedDateTime(date?: Date): string {\n return formatDate(date ?? new Date(), 'datetime')\n}\n\n/**\n * 格式化时间为 2020-02-02 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedDate(date?: Date): string {\n return formatDate(date ?? new Date(), 'date')\n}\n\n/**\n * 格式化时间为 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedTime(date?: Date): string {\n return formatDate(date ?? new Date(), 'time')\n}\n\n/**\n * 格式化时间戳。可自动判断 10/13 位时间戳\n * @param timestamp\n */\nexport function formatTime(timestamp: number | string | Date | undefined): string {\n if (typeof timestamp === 'undefined') return ''\n if (timestamp instanceof Date) {\n return timestamp.toLocaleString()\n } else {\n const num = typeof timestamp === 'string' ? Number(timestamp) : timestamp\n if (isNaN(num)) return ''\n\n return new Date(num < 1e12 ? num * 1000 : num).toLocaleString()\n }\n}\n","/** URL query 中必须编码的保留字符 (RFC 3986) */\nexport const invalidCharRegex = /[!'()*]/g\n\n/** 查询参数 */\nexport type QueryParams = Record<string, string | number | boolean | null | undefined>\n\n/**\n * 已解析的 url\n */\nexport interface ParsedUrl {\n baseUrl: string\n searchParams: URLSearchParams\n}\n\n/**\n * 判断字符串是否为有效的 HTTP/HTTPS URL\n * @param path - 待检测的字符串\n */\nexport function isHttpUrl(path: string) {\n try {\n const url = new URL(path)\n return url.protocol === 'http:' || url.protocol === 'https:'\n } catch {\n return false\n }\n}\n\n/**\n * 解析 url\n * @param url\n */\nexport function parseUrl(url: string): ParsedUrl {\n try {\n const { origin, pathname, searchParams } = new URL(url)\n return {\n baseUrl: origin + pathname,\n searchParams,\n }\n } catch {\n throw new Error('Invalid URL')\n }\n}\n\n/**\n * 编码 URL 参数\n * @param params 查询参数\n * @param keepEmptyValues 保留 null/undefined的值(空字符串)\n */\nexport function encodeURLParams(params: QueryParams, keepEmptyValues: boolean = false) {\n return (\n Object.keys(params)\n // 排序\n .sort()\n // 过滤\n .filter((key) => {\n return keepEmptyValues || params[key] != null\n })\n // 编码 key 和 value\n .map((key) => {\n const raw = params[key] ?? ''\n return `${encodeURIComponent(key)}=${encodeURIComponent(String(raw))}`\n })\n // 拼接\n .join('&')\n )\n}\n\n/**\n *\n */\n/**\n * 合并查询参数\n * @param urlSearchParams url解析后查询参数\n * @param params 单独传的查询参数\n */\nexport function mergeQueryParams(urlSearchParams: URLSearchParams, params?: QueryParams): QueryParams {\n let mergedParams: QueryParams = {}\n if (urlSearchParams.size > 0) {\n // 合并url里的查询参数\n urlSearchParams.forEach((value, key) => {\n mergedParams[key] = value\n })\n }\n // 处理单独传的查询参数对象\n if (params) {\n mergedParams = { ...mergedParams, ...params }\n }\n return mergedParams\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"],"mappings":"yaAAA,IAAAA,GAAA,GAAAC,EAAAD,GAAA,eAAAE,EAAA,gBAAAC,EAAA,UAAAC,EAAA,yBAAAC,EAAA,qBAAAC,EAAA,wBAAAC,EAAA,gBAAAC,EAAA,oBAAAC,GAAA,YAAAC,EAAA,oBAAAC,EAAA,eAAAC,EAAA,eAAAC,GAAA,mBAAAC,GAAA,oBAAAC,EAAA,qBAAAC,EAAA,yBAAAC,EAAA,qBAAAC,GAAA,qBAAAC,EAAA,qBAAAC,GAAA,oBAAAC,EAAA,kBAAAC,EAAA,eAAAC,EAAA,cAAAC,GAAA,aAAAC,EAAA,kBAAAC,EAAA,qBAAAC,GAAA,sBAAAC,EAAA,uBAAAC,EAAA,aAAAC,GAAA,0BAAAC,EAAA,eAAAC,EAAA,gBAAAC,EAAA,cAAAC,EAAA,uBAAAC,EAAA,WAAAC,EAAA,cAAAC,EAAA,0BAAAC,EAAA,mBAAAC,EAAA,YAAAC,EAAA,UAAAC,EAAA,gBAAAC,EAAA,mBAAAC,EAAA,cAAAC,EAAA,gBAAAC,IAAA,eAAAC,EAAA9C,ICGO,IAAM+C,EAAN,cAA0B,KAAM,CAIrC,YAAYC,EAAiBC,EAAoB,CAC/C,MAAMD,CAAO,EAETC,IAAa,SACf,KAAK,SAAWA,aAAoB,MAAQA,EAAW,IAAI,MAAM,OAAOA,CAAQ,CAAC,GAGnF,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CAEA,UAAW,CACT,MAAO,GAAG,KAAK,IAAI,KAAK,KAAK,OAAO,GAAG,KAAK,SAAW,eAAe,KAAK,QAAQ,IAAM,EAAE,EAC7F,CACF,EChBO,SAASC,EAAgBC,EAAuB,CACrD,OAAMA,aAAe,MAEdA,EAAI,OAAS,cAAgBA,EAAI,OAAS,iBAAoB,SAAUA,GAAOA,EAAI,OAAS,eAF/D,EAGtC,CAMO,SAASC,EAAiBC,EAAqB,CACpD,IAAMC,EAAQ,IAAI,MAAMD,GAAO,gCAAO,EACtC,OAAAC,EAAM,KAAO,aACNA,CACT,CAKO,SAASC,EAAcD,EAAsC,CAClE,OAAOA,aAAiBE,GAAgBF,aAAiB,OAASA,EAAM,OAAS,aACnF,CAKA,SAASG,EAAYH,EAAcI,EAAwB,CACzD,OAAKA,IAELJ,EAAM,QAAU,GAAGI,CAAM,IAAIJ,EAAM,OAAO,IACnCA,CACT,CAKO,SAASK,EAAqBL,EAAgBI,EAAiB,CACpE,OAAIH,EAAcD,CAAK,EACdG,EAAYH,EAAOI,CAAM,EAE3B,IAAIF,EAAY,GAAGE,CAAM,IAAIE,EAAgBN,CAAK,CAAC,GAAIA,CAAK,CACrE,CAKO,SAASM,EAAgBN,EAAwB,CACtD,OAAI,OAAOA,GAAU,SACZA,EAELA,aAAiB,OAGjBA,GAAS,OAAOA,GAAU,UAAY,YAAaA,GAAS,OAAOA,EAAM,SAAY,SAChFA,EAAM,QAER,OAAOA,CAAK,GAAK,0BAC1B,CCzDO,IAAMO,EAAN,MAAMC,CAAa,CAMxB,YAAYC,EAAkBC,EAAaC,EAAU,CACnD,KAAK,QAAUF,EACf,KAAK,IAAMC,EACX,KAAK,KAAOC,CACd,CAEA,OAAO,cAAiBA,EAAS,CAC/B,OAAO,IAAIH,EAAU,GAAM,2BAAQG,CAAI,CACzC,CAEA,OAAO,WAAcD,EAAc,2BAAQC,EAAU,CACnD,OAAO,IAAIH,EAAU,GAAOE,EAAKC,CAAI,CACvC,CAEA,OAAO,YAAeC,EAAY,CAChC,IAAMC,EAAY,IAAIL,EAAa,GAAOM,EAAgBF,CAAC,CAAC,EAC5D,OAAIA,GAAK,OAAOA,GAAM,UAAY,SAAUA,GAAK,OAAOA,EAAE,MAAS,WACjEC,EAAU,UAAYD,EAAE,MAEnBC,CACT,CAOA,MAAM,WAAkC,CACtC,GAAI,KAAK,QACP,OAAO,KAAK,KACP,CACL,IAAME,EAAQ,IAAI,MAAM,KAAK,GAAG,EAChC,MAAI,KAAK,YAAWA,EAAM,KAAO,KAAK,WAChCA,CACR,CACF,CACF,ECzCO,IAAMC,EAAU,MAAUC,GAAiD,CAChF,GAAI,CACF,OAAOC,EAAU,cAAiB,MAAMD,EAAI,CAAC,CAC/C,OAASE,EAAG,CACV,OAAOD,EAAU,YAAYC,CAAC,CAChC,CACF,ECRO,SAASC,EAAWC,EAAe,CACxC,OAAO,MAAM,QAAQA,CAAI,GAAKA,EAAK,SAAW,CAChD,CAQO,SAASC,EAASC,EAAUC,EAAqB,CACtD,GAAI,CAAC,OAAO,UAAUA,CAAI,GAAKA,EAAO,EAAG,MAAM,IAAI,MAAM,2CAAa,EACtE,IAAMC,EAAgB,CAAC,EACvB,QAASC,EAAI,EAAGA,EAAIH,EAAI,OAAQG,GAAKF,EACnCC,EAAO,KAAKF,EAAI,MAAMG,EAAGA,EAAIF,CAAI,CAAC,EAEpC,OAAOC,CACT,CCPO,SAASE,EAAoBC,EAAoB,CACtD,OAAO,IAAI,MAAM,QAAQA,EAAS,MAAM,KAAKA,EAAS,UAAU,EAAE,CACpE,CAMA,eAAsBC,EACpBC,EACAC,EACAC,EACY,CACZ,IAAMJ,EAAW,MAAM,MAAME,EAAKE,CAAI,EACtC,GAAI,CAACJ,EAAS,GACZ,MAAMD,EAAoBC,CAAQ,EAEpC,OAAQG,EAAQ,CACd,IAAK,OACH,OAAQ,MAAMH,EAAS,KAAK,EAC9B,IAAK,OACH,OAAQ,MAAMA,EAAS,KAAK,EAC9B,IAAK,OACH,OAAQ,MAAMA,EAAS,KAAK,EAC9B,IAAK,cACH,OAAQ,MAAMA,EAAS,YAAY,EACrC,IAAK,WACH,OAAQ,MAAMA,EAAS,SAAS,EAClC,QACE,OAAQ,MAAMA,EAAS,KAAK,CAChC,CACF,CCxCO,SAASK,EAAmBC,EAAwBC,EAAyB,CAClFA,EAAWA,GAAYD,EAAI,OAC3B,IAAME,EAAQ,KAAK,MAAM,KAAK,OAAO,EAAID,CAAQ,EAAI,EAE/CE,EAAW,CAAC,GAAGH,CAAG,EACxB,QAAS,EAAIG,EAAS,OAAS,EAAG,EAAI,EAAG,IAAK,CAC5C,IAAMC,EAAI,KAAK,MAAM,KAAK,OAAO,GAAK,EAAI,EAAE,EAC3C,CAACD,EAAS,CAAC,EAAGA,EAASC,CAAC,CAAC,EAAI,CAACD,EAASC,CAAC,EAAGD,EAAS,CAAC,CAAC,CACzD,CAEA,IAAME,EAAWF,EAAS,MAAM,EAAGD,CAAK,EAExC,QAAWI,KAAMD,EACfC,EAAG,CAEP,CAgBA,eAAsBC,EACpBC,EACAC,EACAC,EACAC,EACe,CACf,QAASC,EAAUJ,EAAOI,GAAWH,EAAKG,GAAWF,EAAW,CAC9D,IAAMG,EAAYJ,EAAMG,EAAU,EAC5BE,EAAa,KAAK,IAAIJ,EAAWG,CAAS,EAC1CE,EAAQ,MAAM,KAAK,CAAE,OAAQD,CAAW,EAAG,CAACE,EAAGC,IAAQL,EAAUK,CAAG,EAE1E,MAAMN,EAAUI,CAAK,CACvB,CACF,CAUO,SAASG,EAAqBC,EAAWC,KAAiBC,EAAgB,CAC/E,IAAMC,EAAWF,EAAK,MAAM,GAAG,EAC3BG,EAAUJ,EACV,EAAI,EACR,KAAO,EAAIG,EAAS,OAAS,EAAG,IAE9B,GADAC,EAAUA,EAAQD,EAAS,CAAC,CAAC,EACzBC,GAAW,KACb,MAAM,IAAI,MAAM,SAASD,EAAS,MAAM,EAAG,EAAI,CAAC,EAAE,KAAK,GAAG,CAAC,qBAAqB,EAGpF,IAAMhB,EAAKiB,EAAQD,EAAS,CAAC,CAAC,EAC9B,GAAI,OAAOhB,GAAO,WAChB,MAAM,IAAI,MAAM,IAAIc,CAAI,qBAAqB,EAE/C,OAAOd,EAAG,MAAMiB,EAASF,CAAI,CAC/B,CChDO,SAASG,EAAmBC,EAAyC,CAE1EA,EAAgBA,EAAc,QAAQ,SAAU,EAAE,EAElD,IAAMC,EAAY,0DACZC,EAAY,2DAGZC,EAAKH,EAAc,MAAMC,CAAS,EACxC,GAAIE,EACF,MAAO,CACL,MAAOA,EAAG,CAAC,EACX,KAAMA,EAAG,CAAC,EACV,OAAQ,MACV,EAIF,IAAMC,EAAKJ,EAAc,MAAME,CAAS,EACxC,GAAIE,EACF,MAAO,CACL,MAAOA,EAAG,CAAC,EACX,KAAMA,EAAG,CAAC,EACV,OAAQA,EAAG,CAAC,CACd,EAGF,MAAM,IAAI,MAAM,qDAAa,CAC/B,CAKO,SAASC,EAAkBC,EAAgC,CAChE,GAAM,CAAE,MAAAC,EAAO,KAAAC,EAAM,OAAAC,EAAQ,SAAAC,CAAS,EAAIJ,EAC1C,MAAO,qCAAqCC,CAAK,IAAIC,CAAI,IAAIC,CAAM,IAAIC,CAAQ,EACjF,CAMA,eAAsBC,EAAoBL,EAA4C,CACpF,OAAO,KAAK,MAAM,MAAMM,EAAgBP,EAAkBC,CAAc,EAAG,MAAM,CAAC,CACpF,CC/DA,eAAsBO,EAAeC,EAAqBC,EAA6B,CACrF,OAAO,MAAM,QAAQ,KAAK,CACxBD,EACA,IAAI,QAAe,CAACE,EAAGC,IAAW,CAChC,WAAW,IAAM,CACfA,EAAO,IAAI,MAAM,SAAS,CAAC,CAC7B,EAAGF,CAAO,CACZ,CAAC,CACH,CAAC,CACH,CASA,eAAsBG,EAAaC,EAAsBC,EAAkB,EAAGC,EAAgB,IAAkB,CAC9G,QAASC,EAAI,EAAGA,EAAIF,EAASE,IAC3B,GAAI,CACF,OAAO,MAAMH,EAAG,CAClB,OAASI,EAAO,CACd,GAAID,IAAMF,EAAU,EAAG,MAAMG,EAC7B,MAAM,IAAI,QAASC,GAAY,WAAWA,EAASH,CAAK,CAAC,CAC3D,CAEF,MAAM,IAAI,MAAM,aAAa,CAC/B,CChCO,SAASI,EAAWC,EAAe,CACxC,IAAMC,EAAQD,EAAI,MAAM,EACxB,QAASE,EAAID,EAAM,OAAS,EAAGC,EAAI,EAAGA,IAAK,CACzC,IAAMC,EAAI,KAAK,MAAM,KAAK,OAAO,GAAKD,EAAI,EAAE,EAC3C,CAACD,EAAMC,CAAC,EAAGD,EAAME,CAAC,CAAC,EAAI,CAACF,EAAME,CAAC,EAAGF,EAAMC,CAAC,CAAC,CAC7C,CACA,OAAOD,CACT,CAKO,SAASG,EAAUC,EAAaC,EAAqB,CAC1D,OAAO,KAAK,MAAM,KAAK,OAAO,GAAKA,EAAMD,EAAM,EAAE,EAAIA,CACvD,CAKO,SAASE,EAAYF,EAAaC,EAAqB,CAC5D,OAAO,KAAK,OAAO,GAAKA,EAAMD,GAAOA,CACvC,CAKO,SAASG,EAAUR,EAAUS,EAAoB,CACtD,OAAOV,EAAQC,CAAG,EAAE,MAAM,EAAGS,CAAK,CACpC,CAKO,SAASC,EAAyBV,EAAUS,EAAoB,CACrE,IAAME,EAAc,CAAC,EACrB,QAAST,EAAI,EAAGA,EAAIO,EAAOP,IAAK,CAC9B,IAAMU,EAAQR,EAAU,EAAGJ,EAAI,OAAS,CAAC,EACzCW,EAAO,KAAKX,EAAIY,CAAK,CAAC,CACxB,CACA,OAAOD,CACT,CAKO,SAASE,EAAab,EAAa,CACxC,OAAOA,EAAII,EAAU,EAAGJ,EAAI,OAAS,CAAC,CAAC,CACzC,CAKO,SAASc,EAAWC,EAAmB,CAC5C,OAAOX,EAAU,EAAGW,CAAC,CACvB,CCpDO,SAASC,EAAcC,EAAkD,CAC9E,GAAIA,IAAU,MAAQ,OAAOA,GAAU,SAAU,MAAO,GACxD,IAAMC,EAAQ,OAAO,eAAeD,CAAK,EACzC,OAAOC,IAAU,OAAO,WAAaA,IAAU,IACjD,CAKO,SAASC,EAAoCF,EAAgBG,EAAqC,CAEvG,OADIH,IAAU,MAAQ,OAAOA,GAAU,UACnC,MAAM,QAAQA,CAAK,EAAU,GAC7BG,IAAQ,OACHA,KAAOH,EAET,EACT,CCRO,SAASI,EAAkBC,EAAUC,EAAO,IAAI,QAA2B,CAChF,GAAI,OAAOD,GAAU,WAAY,OAGjC,GAFIA,IAAU,MAAQ,OAAOA,GAAU,UAGrCA,aAAiB,MACjBA,aAAiB,QACjBA,aAAiB,KACjBA,aAAiB,KACjB,YAAY,OAAOA,CAAK,GACxBA,aAAiB,YAEjB,OAAOA,EAGT,GAAIC,EAAK,IAAID,CAAe,EAC1B,OAAOC,EAAK,IAAID,CAAe,EAGjC,GAAI,MAAM,QAAQA,CAAK,EAAG,CACxB,IAAME,EAAiB,CAAC,EACxBD,EAAK,IAAID,EAAiBE,CAAG,EAC7B,QAAWC,KAAQH,EACjBE,EAAI,KAAKH,EAAeI,EAAMF,CAAI,CAAC,EAErC,OAAOC,CACT,CAEA,GAAI,CAACE,EAAcJ,CAAK,EACtB,OAAOA,EAGT,IAAME,EAA+B,CAAC,EACtCD,EAAK,IAAID,EAAiBE,CAAG,EAE7B,OAAW,CAACG,EAAKF,CAAI,IAAK,OAAO,QAAQH,CAAK,EAAG,CAC/C,GAAI,OAAOG,GAAS,WAAY,SAChC,IAAMG,EAAOP,EAAeI,EAAMF,CAAI,EAClCK,IAAS,SAAWJ,EAAIG,CAAG,EAAIC,EACrC,CAEA,OAAOJ,CACT,CAKO,SAASK,EAAeC,EAAc,CAC3C,OAAIA,aAAe,MACV,CACL,KAAMA,EAAI,KACV,QAASA,EAAI,QACb,MAAOA,EAAI,KACb,EAGK,CACL,KAAM,QACN,QAAS,OAAOA,CAAG,CACrB,CACF,CClEO,SAASC,EAAMC,EAAYC,EAAsB,CACtD,OAAO,IAAI,QAAc,CAACC,EAASC,IAAW,CAC5C,GAAIF,GAAQ,QAAS,OAAOE,EAAOC,EAAiB,CAAC,EAErD,IAAMC,EAAQ,WAAW,IAAM,CAC7BC,EAAQ,EACRJ,EAAQ,CACV,EAAGF,CAAE,EAECO,EAAU,IAAM,CACpB,aAAaF,CAAK,EAClBC,EAAQ,EACRH,EAAOC,EAAiB,CAAC,CAC3B,EAEME,EAAU,IAAM,CACpBL,GAAQ,oBAAoB,QAASM,CAAO,CAC9C,EAEAN,GAAQ,iBAAiB,QAASM,CAAO,CAC3C,CAAC,CACH,CAQO,SAASC,EAAYC,EAAeC,EAAeT,EAAsB,CAC9E,IAAMD,EAAK,KAAK,OAAO,GAAKU,EAAQD,GAASA,EAC7C,OAAOV,EAAMC,EAAIC,CAAM,CACzB,CCpCO,SAASU,EAAWC,EAAYC,EAA+C,CACpF,IAAMC,EAAK,MAAMF,EAAK,QAAQ,CAAC,EAAW,IAAI,KAAXA,EAC7BG,EAAOD,EAAE,YAAY,EACrBE,EAAQ,OAAOF,EAAE,SAAS,EAAI,CAAC,EAAE,SAAS,EAAG,GAAG,EAChDG,EAAM,OAAOH,EAAE,QAAQ,CAAC,EAAE,SAAS,EAAG,GAAG,EACzCI,EAAQ,OAAOJ,EAAE,SAAS,CAAC,EAAE,SAAS,EAAG,GAAG,EAC5CK,EAAU,OAAOL,EAAE,WAAW,CAAC,EAAE,SAAS,EAAG,GAAG,EAChDM,EAAU,OAAON,EAAE,WAAW,CAAC,EAAE,SAAS,EAAG,GAAG,EAEtD,OAAID,IAAY,OAAe,GAAGE,CAAI,IAAIC,CAAK,IAAIC,CAAG,GAClDJ,IAAY,OAAe,GAAGK,CAAK,IAAIC,CAAO,IAAIC,CAAO,GACtD,GAAGL,CAAI,IAAIC,CAAK,IAAIC,CAAG,IAAIC,CAAK,IAAIC,CAAO,IAAIC,CAAO,EAC/D,CAMO,SAASC,EAAqBT,EAAqB,CACxD,OAAOD,EAAWC,GAAQ,IAAI,KAAQ,UAAU,CAClD,CAMO,SAASU,EAAiBV,EAAqB,CACpD,OAAOD,EAAWC,GAAQ,IAAI,KAAQ,MAAM,CAC9C,CAMO,SAASW,GAAiBX,EAAqB,CACpD,OAAOD,EAAWC,GAAQ,IAAI,KAAQ,MAAM,CAC9C,CAMO,SAASY,GAAWC,EAAuD,CAChF,GAAI,OAAOA,EAAc,IAAa,MAAO,GAC7C,GAAIA,aAAqB,KACvB,OAAOA,EAAU,eAAe,EAC3B,CACL,IAAMC,EAAM,OAAOD,GAAc,SAAW,OAAOA,CAAS,EAAIA,EAChE,OAAI,MAAMC,CAAG,EAAU,GAEhB,IAAI,KAAKA,EAAM,KAAOA,EAAM,IAAOA,CAAG,EAAE,eAAe,CAChE,CACF,CCtDO,IAAMC,GAAmB,WAiBzB,SAASC,GAAUC,EAAc,CACtC,GAAI,CACF,IAAMC,EAAM,IAAI,IAAID,CAAI,EACxB,OAAOC,EAAI,WAAa,SAAWA,EAAI,WAAa,QACtD,MAAQ,CACN,MAAO,EACT,CACF,CAMO,SAASC,GAASD,EAAwB,CAC/C,GAAI,CACF,GAAM,CAAE,OAAAE,EAAQ,SAAAC,EAAU,aAAAC,CAAa,EAAI,IAAI,IAAIJ,CAAG,EACtD,MAAO,CACL,QAASE,EAASC,EAClB,aAAAC,CACF,CACF,MAAQ,CACN,MAAM,IAAI,MAAM,aAAa,CAC/B,CACF,CAOO,SAASC,GAAgBC,EAAqBC,EAA2B,GAAO,CACrF,OACE,OAAO,KAAKD,CAAM,EAEf,KAAK,EAEL,OAAQE,GACAD,GAAmBD,EAAOE,CAAG,GAAK,IAC1C,EAEA,IAAKA,GAAQ,CACZ,IAAMC,EAAMH,EAAOE,CAAG,GAAK,GAC3B,MAAO,GAAG,mBAAmBA,CAAG,CAAC,IAAI,mBAAmB,OAAOC,CAAG,CAAC,CAAC,EACtE,CAAC,EAEA,KAAK,GAAG,CAEf,CAUO,SAASC,GAAiBC,EAAkCL,EAAmC,CACpG,IAAIM,EAA4B,CAAC,EACjC,OAAID,EAAgB,KAAO,GAEzBA,EAAgB,QAAQ,CAACE,EAAOL,IAAQ,CACtCI,EAAaJ,CAAG,EAAIK,CACtB,CAAC,EAGCP,IACFM,EAAe,CAAE,GAAGA,EAAc,GAAGN,CAAO,GAEvCM,CACT,CCrEO,SAASE,GAAeC,EAAeC,EAAcC,EAAiBC,EAAc,GAAuB,CAChH,GAAI,CAAC,OAAO,SAASH,CAAK,EACxB,MAAO,CAAE,KAAM,EAAG,KAAM,GAAI,KAAMG,CAAY,EAGhD,IAAIC,EAAOJ,EACPK,EAAY,EAEhB,KAAOD,GAAQH,GAAQI,EAAYH,EAAM,OAAS,GAChDE,GAAQH,EACRI,IAGF,MAAO,CACL,KAAAD,EACA,KAAMF,EAAMG,CAAS,EACrB,KAAM,GAAGD,EAAK,QAAQ,CAAC,EAAE,QAAQ,SAAU,EAAE,CAAC,IAAIF,EAAMG,CAAS,CAAC,EACpE,CACF","names":["core_exports","__export","BizResult","CommonError","chunk","convertToCommonError","createAbortError","createResponseError","dynamicCall","encodeURLParams","execBiz","fetchWithFormat","formatDate","formatTime","formatUnitSize","getErrorMessage","getFormattedDate","getFormattedDateTime","getFormattedTime","getGithubRawJson","invalidCharRegex","isCanceledError","isCommonError","isEmptyArr","isHttpUrl","isObject","isPlainObject","mergeQueryParams","parseGithubRawUrl","parseGithubRepoUrl","parseUrl","processRangeInBatches","random1ToN","randomFloat","randomInt","runRandomFunctions","sample","sampleOne","sampleWithReplacement","serializeError","shuffle","sleep","sleepRandom","stripFunctions","withRetry","withTimeout","__toCommonJS","CommonError","message","rawError","isCanceledError","err","createAbortError","msg","error","isCommonError","CommonError","prefixError","prefix","convertToCommonError","getErrorMessage","BizResult","_BizResult","success","msg","data","e","bizResult","getErrorMessage","error","execBiz","run","BizResult","e","isEmptyArr","data","chunk","arr","size","result","i","createResponseError","response","fetchWithFormat","url","format","init","runRandomFunctions","fns","maxCount","count","shuffled","j","selected","fn","processRangeInBatches","start","end","batchSize","processor","current","remaining","actualSize","batch","_","idx","dynamicCall","root","path","args","segments","context","parseGithubRepoUrl","repositoryUrl","regexBase","regexFile","m1","m2","parseGithubRawUrl","gitHubRepoFile","owner","repo","branch","filePath","getGithubRawJson","fetchWithFormat","withTimeout","promise","timeout","_","reject","withRetry","fn","retries","delay","i","error","resolve","shuffle","arr","clone","i","j","randomInt","min","max","randomFloat","sample","count","sampleWithReplacement","result","index","sampleOne","random1ToN","n","isPlainObject","value","proto","isObject","key","stripFunctions","value","seen","out","item","isPlainObject","key","next","serializeError","err","sleep","ms","signal","resolve","reject","createAbortError","timer","cleanup","onAbort","sleepRandom","minMS","maxMS","formatDate","date","pattern","d","year","month","day","hours","minutes","seconds","getFormattedDateTime","getFormattedDate","getFormattedTime","formatTime","timestamp","num","invalidCharRegex","isHttpUrl","path","url","parseUrl","origin","pathname","searchParams","encodeURLParams","params","keepEmptyValues","key","raw","mergeQueryParams","urlSearchParams","mergedParams","value","formatUnitSize","value","base","units","invalidText","size","unitIndex"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/index.ts","../src/core/utils/error.ts","../src/core/error/common-error.ts","../src/core/utils/abort.ts","../src/core/utils/array.ts","../src/core/utils/type.ts","../src/core/utils/serialize.ts","../src/core/utils/biz-result.ts","../src/core/utils/fetch.ts","../src/core/utils/function.ts","../src/core/utils/github.ts","../src/core/utils/object.ts","../src/core/utils/promise.ts","../src/core/utils/random.ts","../src/core/utils/sleep.ts","../src/core/utils/time.ts","../src/core/utils/url.ts","../src/core/utils/number/format.ts"],"sourcesContent":["export * from \"./error/common-error.js\";\nexport * from \"./types/biz-result.js\";\nexport * from \"./types/helpers.js\";\nexport * from \"./types/physical.js\";\nexport * from \"./utils/abort.js\";\nexport * from \"./utils/array.js\";\nexport * from \"./utils/biz-result.js\";\nexport * from \"./utils/error.js\";\nexport * from \"./utils/fetch.js\";\nexport * from \"./utils/function.js\";\nexport * from \"./utils/github.js\";\nexport * from \"./utils/object.js\";\nexport * from \"./utils/promise.js\";\nexport * from \"./utils/random.js\";\nexport * from \"./utils/serialize.js\";\nexport * from \"./utils/sleep.js\";\nexport * from \"./utils/time.js\";\nexport * from \"./utils/type.js\";\nexport * from \"./utils/url.js\";\nexport * from \"./utils/number/format.js\";\n","import { CommonError } from '../error/common-error.js'\n\n/**\n * 是否为取消操作的错误\n */\nexport function isCanceledError(err: unknown): err is Error {\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 { getErrorMessage } from '../utils/error.js'\n\n/**\n * 通用错误\n */\nexport class CommonError extends Error {\n // 原始错误\n cause?: Error\n\n constructor(message: string, cause?: unknown) {\n super(message)\n\n if (cause !== undefined) {\n this.cause = cause instanceof Error ? cause : new Error(getErrorMessage(cause))\n }\n\n this.name = new.target.name\n Object.setPrototypeOf(this, new.target.prototype)\n }\n\n toString() {\n return `${this.name}: ${this.message}${this.cause ? ` (caused by ${this.cause})` : ''}`\n }\n}\n","import { createAbortError } from './error.js'\n\n/**\n * 检查取消信号\n */\nexport function checkAbortSignal(abortSignal?: AbortSignal) {\n if (abortSignal?.aborted) {\n throw createAbortError()\n }\n}\n","/**\n * 是否为空数组\n * @param data\n */\nexport function isEmptyArr(data: unknown) {\n return Array.isArray(data) && data.length === 0\n}\n\n/**\n * 将数组分块为指定大小的多个子数组\n * @param arr 原数组\n * @param size 每个块的长度\n * @example chunk([1,2,3,4,5], 2) => [[1,2],[3,4],[5]]\n */\nexport function chunk<T>(arr: T[], size: number): T[][] {\n if (!Number.isInteger(size) || size < 1) throw new Error('size 必须是正整数')\n const result: T[][] = []\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size))\n }\n return result\n}\n\n/**\n * 判断数组是否存在某些项\n */\nexport function inArray<T>(value: T, array: readonly T[]): boolean {\n return array.includes(value)\n}\n","import type { ObjectWithKeys } from '../types/helpers.js'\n\n/**\n * 判断一个值是否为纯对象\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n/**\n * 包括类实例、普通对象,不包括 数组、null 和原始类型\n */\nexport function isObject<K extends string | symbol>(value: unknown, key?: K): value is ObjectWithKeys<K> {\n if (value === null || typeof value !== 'object') return false\n if (Array.isArray(value)) return false\n if (key !== undefined) {\n return key in value\n }\n return true\n}\n","import { isPlainObject } from './type.js'\nimport { getErrorMessage } from './error.js'\nimport type { BizError } from '../types/biz-result.js'\n\n/**\n * 递归移除对象(或数组)中的所有函数属性,返回一个可安全序列化的副本。\n *\n * @typeParam T - 输入值的类型。\n * @param value - 待处理的值(基本类型、对象、数组等)。\n * @param seen - 内部使用的 WeakMap,用于记录已访问过的对象,防止循环引用。\n * 调用方通常无需传递此参数。\n * @returns 处理后的新副本,其中所有函数属性都被移除。\n * 基本类型(string, number, boolean, null, undefined)将原样返回。\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function stripFunctions<T>(value: T, seen = new WeakMap<object, any>()): T {\n if (typeof value === 'function') return undefined as T\n if (value === null || typeof value !== 'object') return value\n\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Map ||\n value instanceof Set ||\n ArrayBuffer.isView(value) ||\n value instanceof ArrayBuffer\n ) {\n return value\n }\n\n if (seen.has(value as object)) {\n return seen.get(value as object)\n }\n\n if (Array.isArray(value)) {\n const out: unknown[] = []\n seen.set(value as object, out)\n for (const item of value) {\n out.push(stripFunctions(item, seen))\n }\n return out as T\n }\n\n if (!isPlainObject(value)) {\n return value\n }\n\n const out: Record<string, unknown> = {}\n seen.set(value as object, out)\n\n for (const [key, item] of Object.entries(value)) {\n if (typeof item === 'function') continue\n const next = stripFunctions(item, seen)\n if (next !== undefined) out[key] = next\n }\n\n return out as T\n}\n\n/**\n * 序列号错误对象 为 BizError\n */\nexport function serializeError(err: unknown): BizError {\n if (!(err instanceof Error)) {\n return {\n name: 'UnknownError',\n message: getErrorMessage(err),\n }\n }\n\n return {\n name: err.name,\n message: err.message,\n stack: err.stack,\n cause: 'cause' in err && err.cause ? serializeError(err.cause) : undefined,\n }\n}\n","import type { BizResult, BizError } from '../types/biz-result.js'\nimport { serializeError } from './serialize.js'\n\n/**\n * 是否为业务结果(序列化后)\n */\nexport function isBizResult<T = unknown>(result: unknown): result is BizResult<T> {\n return (\n result != null &&\n typeof result === 'object' &&\n 'success' in result &&\n typeof result.success === 'boolean' &&\n 'msg' in result &&\n typeof result.msg === 'string'\n )\n}\n\n/**\n * 执行异步函数,自动捕获异常并转换为 BizResult 对象\n * @param runFn 执行方法\n */\nexport const execBiz = async <T>(runFn: () => T | Promise<T>): Promise<BizResult<Awaited<T>>> => {\n try {\n return {\n success: true,\n msg: '操作成功',\n data: await runFn(),\n }\n } catch (e) {\n return {\n success: false,\n msg: '操作失败',\n error: serializeError(e),\n }\n }\n}\n\n/**\n * 解包 BizResult<T> 对象为 Promise<T>,非 BizResult 对象原样返回。\n */\nexport async function unwrapBizResult<T>(result: BizResult<T>): Promise<T>\nexport async function unwrapBizResult(result: unknown): Promise<unknown>\nexport async function unwrapBizResult<T>(result: BizResult<T> | unknown): Promise<T | unknown> {\n if (isBizResult<T>(result)) {\n if (result.success) {\n return result.data as T\n }\n\n if (result.error) {\n throw bizErrorToError(result.error)\n }\n\n throw new Error(result.msg)\n } else {\n return result\n }\n}\n\nexport function bizErrorToError(bizError: BizError) {\n return Object.assign(new Error(), bizError)\n}\n","/**\n * 用于指定 `fetch` 响应体的解析方式。\n * 对应 `Response` 对象的不同解析方法(`.json()`、`.text()` 等)。\n */\nexport type ResponseBodyFormat =\n | 'json' // 解析为 JSON 对象\n | 'text' // 解析为原始字符串\n | 'blob' // 解析为 Blob 对象(适用于二进制文件,如图片、压缩包)\n | 'arrayBuffer' // 解析为 ArrayBuffer(适用于底层二进制操作)\n | 'formData' // 解析为 FormData 对象(适用于处理 multipart/form-data 响应)\n\n/**\n * 创建响应错误\n */\nexport function createResponseError(response: Response) {\n return new Error(`HTTP ${response.status}: ${response.statusText}`)\n}\n\n/**\n * 使用指定的响应解析格式发起 fetch 请求\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function fetchWithFormat<T = any>(\n url: string,\n format: ResponseBodyFormat,\n init?: RequestInit,\n): Promise<T> {\n const response = await fetch(url, init)\n if (!response.ok) {\n throw createResponseError(response)\n }\n switch (format) {\n case 'json':\n return (await response.json()) as T\n case 'text':\n return (await response.text()) as T\n case 'blob':\n return (await response.blob()) as T\n case 'arrayBuffer':\n return (await response.arrayBuffer()) as T\n case 'formData':\n return (await response.formData()) as T\n default:\n return (await response.text()) as T\n }\n}\n","/**\n * 随机运行参数\n * @param fns\n * @param maxCount 最多执行次数(1 ~ fns.length)\n */\nexport function runRandomFunctions(fns: Array<() => void>, maxCount?: number): void {\n maxCount = maxCount ?? fns.length\n const count = Math.floor(Math.random() * maxCount) + 1\n\n const shuffled = [...fns]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n\n const selected = shuffled.slice(0, count)\n\n for (const fn of selected) {\n fn()\n }\n}\n\n/**\n * 将数字范围按批次处理,每个批次调用一次异步函数\n *\n * @param start - 起始值(包含)\n * @param end - 结束值(包含)\n * @param batchSize - 每批最大元素个数\n * @param processor - 处理单批数字数组的异步函数\n *\n * @example\n * // 分批处理 1..10,每批最多 3 个数字\n * await processRangeInBatches(1, 10, 3, async (batch) => {\n * console.log(batch) // 输出: [1,2,3], [4,5,6], [7,8,9], [10]\n * })\n */\nexport async function processRangeInBatches(\n start: number,\n end: number,\n batchSize: number,\n processor: (batch: number[]) => Promise<void>,\n): Promise<void> {\n for (let current = start; current <= end; current += batchSize) {\n const remaining = end - current + 1\n const actualSize = Math.min(batchSize, remaining)\n const batch = Array.from({ length: actualSize }, (_, idx) => current + idx)\n\n await processor(batch)\n }\n}\n\n/**\n * 动态调用\n * @param root 对象\n * @param path 调用路径\n * @param args 参数\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport function dynamicCall<T = any>(root: any, path: string, ...args: any[]): T {\n const segments = path.split('.')\n let context = root\n let i = 0\n for (; i < segments.length - 1; i++) {\n context = context[segments[i]]\n if (context == null) {\n throw new Error(`Path '${segments.slice(0, i + 1).join('.')}' is null/undefined`)\n }\n }\n const fn = context[segments[i]]\n if (typeof fn !== 'function') {\n throw new Error(`'${path}' is not a function`)\n }\n return fn.apply(context, args)\n}\n","import { fetchWithFormat } from './fetch.js'\n\n/** github 仓库信息 */\nexport interface GitHubRepo {\n /** 仓库所有者(用户或组织名) */\n owner: string\n /** 仓库名称 */\n repo: string\n}\n\n/** github 仓库分支信息 */\nexport type GitHubRepoBranch = GitHubRepo & {\n /** 分支名称 */\n branch: string\n}\n\n/** github 仓库文件信息 */\nexport type GitHubRepoFile = GitHubRepoBranch & {\n /** 文件路径 */\n filePath: string\n}\n\n/**\n * 从 url 解析 github 仓库信息\n * @param repositoryUrl 仓库 URL(支持带有git+前缀、具体文件路径)\n */\nexport function parseGithubRepoUrl(repositoryUrl: string): GitHubRepoBranch {\n // 去掉 git+ 前缀\n repositoryUrl = repositoryUrl.replace(/^git\\+/, '')\n\n const regexBase = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git|\\/)?$/\n const regexFile = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+)\\/blob\\/([^/]+)$/\n\n // 情况 1:git clone 地址 或 repo 根路径\n const m1 = repositoryUrl.match(regexBase)\n if (m1) {\n return {\n owner: m1[1],\n repo: m1[2],\n branch: 'main', // 默认分支 main\n }\n }\n\n // 情况 2:具体文件路径\n const m2 = repositoryUrl.match(regexFile)\n if (m2) {\n return {\n owner: m2[1],\n repo: m2[2],\n branch: m2[3],\n }\n }\n\n throw new Error('解析插件仓库URL失败')\n}\n\n/**\n * 解析 github 仓库文件的原始内容的 url\n */\nexport function parseGithubRawUrl(gitHubRepoFile: GitHubRepoFile) {\n const { owner, repo, branch, filePath } = gitHubRepoFile\n return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`\n}\n\n/**\n * 获取 github 仓库文件的 json 内容并解析为对象 T\n * @param gitHubRepoFile\n */\nexport async function getGithubRawJson<O>(gitHubRepoFile: GitHubRepoFile): Promise<O> {\n return JSON.parse(await fetchWithFormat(parseGithubRawUrl(gitHubRepoFile), 'json')) as O\n}\n","/**\n * 忽略 undefined 值的字段\n * @param obj\n */\nexport const omitUndefined = <T extends object>(obj: T): Partial<T> =>\n Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== undefined)) as Partial<T>\n","/**\n * 为 Promise 添加超时控制\n * @param promise - 原始异步任务\n * @param timeout - 超时时间(毫秒)\n * @returns 若原 Promise 在超时前完成,则返回其结果;否则抛出超时错误\n * @throws {Error} 超时后抛出 'Timeout' 错误\n */\nexport async function withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error('Timeout'))\n }, timeout)\n }),\n ])\n}\n\n/**\n * 带重试机制的异步函数执行器\n * @param fn - 返回 Promise 的异步函数\n * @param retries - 最大重试次数(默认 3)\n * @param delay - 重试间隔毫秒(默认 1000)\n * @returns Promise<T>\n */\nexport async function withRetry<T>(fn: () => Promise<T>, retries: number = 3, delay: number = 1000): Promise<T> {\n for (let i = 0; i < retries; i++) {\n try {\n return await fn()\n } catch (error) {\n if (i === retries - 1) throw error\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n throw new Error('Unreachable')\n}\n","/**\n * 数组洗牌\n */\nexport function shuffle<T>(arr: T[]): T[] {\n const clone = arr.slice()\n for (let i = clone.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[clone[i], clone[j]] = [clone[j], clone[i]]\n }\n return clone\n}\n\n/**\n * 获取 [min, max] 的随机整数\n */\nexport function randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * 获取 [min, max) 的随机浮点数\n */\nexport function randomFloat(min: number, max: number): number {\n return Math.random() * (max - min) + min\n}\n\n/**\n * 从数组中随机取若干项,不可重复\n */\nexport function sample<T>(arr: T[], count: number): T[] {\n return shuffle(arr).slice(0, count)\n}\n\n/**\n * 从数组中随机取若干项,可重复\n */\nexport function sampleWithReplacement<T>(arr: T[], count: number): T[] {\n const result: T[] = []\n for (let i = 0; i < count; i++) {\n const index = randomInt(0, arr.length - 1)\n result.push(arr[index])\n }\n return result\n}\n\n/**\n * 从数组中获取随机项\n */\nexport function sampleOne<T>(arr: T[]): T {\n return arr[randomInt(0, arr.length - 1)]\n}\n\n/**\n * 获取 1~n 的随机数\n */\nexport function random1ToN(n: number): number {\n return randomInt(1, n)\n}\n","import { createAbortError } from './error.js'\n\n/**\n * 支持取消的 sleep\n * @param ms 延迟毫秒数\n * @param signal 取消信号\n */\nexport function sleep(ms: number, signal?: AbortSignal) {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) return reject(createAbortError())\n\n const timer = setTimeout(() => {\n cleanup()\n resolve()\n }, ms)\n\n const onAbort = () => {\n clearTimeout(timer)\n cleanup()\n reject(createAbortError())\n }\n\n const cleanup = () => {\n signal?.removeEventListener('abort', onAbort)\n }\n\n signal?.addEventListener('abort', onAbort)\n })\n}\n\n/**\n * 支持取消的随机 sleep\n * @param minMS 最小延迟毫秒数\n * @param maxMS 最大延迟毫秒数\n * @param signal 取消信号\n */\nexport function sleepRandom(minMS: number, maxMS: number, signal?: AbortSignal) {\n const ms = Math.random() * (maxMS - minMS) + minMS\n return sleep(ms, signal)\n}\n","/**\n * 格式化日期\n */\nexport function formatDate(date: Date, pattern: 'date' | 'time' | 'datetime'): string {\n const d = !isNaN(date.getTime()) ? date : new Date()\n const year = d.getFullYear()\n const month = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n const hours = String(d.getHours()).padStart(2, '0')\n const minutes = String(d.getMinutes()).padStart(2, '0')\n const seconds = String(d.getSeconds()).padStart(2, '0')\n\n if (pattern === 'date') return `${year}-${month}-${day}`\n if (pattern === 'time') return `${hours}:${minutes}:${seconds}`\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\n/**\n * 格式化时间为 2020-02-02 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedDateTime(date?: Date): string {\n return formatDate(date ?? new Date(), 'datetime')\n}\n\n/**\n * 格式化时间为 2020-02-02 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedDate(date?: Date): string {\n return formatDate(date ?? new Date(), 'date')\n}\n\n/**\n * 格式化时间为 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedTime(date?: Date): string {\n return formatDate(date ?? new Date(), 'time')\n}\n\n/**\n * 格式化时间戳。可自动判断 10/13 位时间戳\n * @param timestamp\n */\nexport function formatTime(timestamp: number | string | Date | undefined): string {\n if (typeof timestamp === 'undefined') return ''\n if (timestamp instanceof Date) {\n return timestamp.toLocaleString()\n } else {\n const num = typeof timestamp === 'string' ? Number(timestamp) : timestamp\n if (isNaN(num)) return ''\n\n return new Date(num < 1e12 ? num * 1000 : num).toLocaleString()\n }\n}\n","/** URL query 中必须编码的保留字符 (RFC 3986) */\nexport const invalidCharRegex = /[!'()*]/g\n\n/** 查询参数 */\nexport type QueryParams = Record<string, string | number | boolean | null | undefined>\n\n/**\n * 已解析的 url\n */\nexport interface ParsedUrl {\n baseUrl: string\n searchParams: URLSearchParams\n}\n\n/**\n * 判断字符串是否为有效的 HTTP/HTTPS URL\n * @param path - 待检测的字符串\n */\nexport function isHttpUrl(path: string) {\n try {\n const url = new URL(path)\n return url.protocol === 'http:' || url.protocol === 'https:'\n } catch {\n return false\n }\n}\n\n/**\n * 解析 url\n * @param url\n */\nexport function parseUrl(url: string): ParsedUrl {\n try {\n const { origin, pathname, searchParams } = new URL(url)\n return {\n baseUrl: origin + pathname,\n searchParams,\n }\n } catch {\n throw new Error('Invalid URL')\n }\n}\n\n/**\n * 编码 URL 参数\n * @param params 查询参数\n * @param keepEmptyValues 保留 null/undefined的值(空字符串)\n */\nexport function encodeURLParams(params: QueryParams, keepEmptyValues: boolean = false) {\n return (\n Object.keys(params)\n // 排序\n .sort()\n // 过滤\n .filter((key) => {\n return keepEmptyValues || params[key] != null\n })\n // 编码 key 和 value\n .map((key) => {\n const raw = params[key] ?? ''\n return `${encodeURIComponent(key)}=${encodeURIComponent(String(raw))}`\n })\n // 拼接\n .join('&')\n )\n}\n\n/**\n *\n */\n/**\n * 合并查询参数\n * @param urlSearchParams url解析后查询参数\n * @param params 单独传的查询参数\n */\nexport function mergeQueryParams(urlSearchParams: URLSearchParams, params?: QueryParams): QueryParams {\n let mergedParams: QueryParams = {}\n if (urlSearchParams.size > 0) {\n // 合并url里的查询参数\n urlSearchParams.forEach((value, key) => {\n mergedParams[key] = value\n })\n }\n // 处理单独传的查询参数对象\n if (params) {\n mergedParams = { ...mergedParams, ...params }\n }\n return mergedParams\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"],"mappings":"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","names":["core_exports","__export","CommonError","bizErrorToError","checkAbortSignal","chunk","convertToCommonError","createAbortError","createResponseError","dynamicCall","encodeURLParams","execBiz","fetchWithFormat","formatDate","formatTime","formatUnitSize","getErrorMessage","getFormattedDate","getFormattedDateTime","getFormattedTime","getGithubRawJson","inArray","invalidCharRegex","isBizResult","isCanceledError","isCommonError","isEmptyArr","isHttpUrl","isObject","isPlainObject","mergeQueryParams","omitUndefined","parseGithubRawUrl","parseGithubRepoUrl","parseUrl","processRangeInBatches","random1ToN","randomFloat","randomInt","runRandomFunctions","sample","sampleOne","sampleWithReplacement","serializeError","shuffle","sleep","sleepRandom","stripFunctions","unwrapBizResult","withRetry","withTimeout","__toCommonJS","isCanceledError","err","createAbortError","msg","error","isCommonError","CommonError","prefixError","prefix","convertToCommonError","getErrorMessage","CommonError","message","cause","getErrorMessage","checkAbortSignal","abortSignal","createAbortError","isEmptyArr","data","chunk","arr","size","result","i","inArray","value","array","isPlainObject","value","proto","isObject","key","stripFunctions","value","seen","out","item","isPlainObject","key","next","serializeError","err","getErrorMessage","isBizResult","result","execBiz","runFn","e","serializeError","unwrapBizResult","bizErrorToError","bizError","createResponseError","response","fetchWithFormat","url","format","init","runRandomFunctions","fns","maxCount","count","shuffled","j","selected","fn","processRangeInBatches","start","end","batchSize","processor","current","remaining","actualSize","batch","_","idx","dynamicCall","root","path","args","segments","context","parseGithubRepoUrl","repositoryUrl","regexBase","regexFile","m1","m2","parseGithubRawUrl","gitHubRepoFile","owner","repo","branch","filePath","getGithubRawJson","fetchWithFormat","omitUndefined","obj","_","v","withTimeout","promise","timeout","_","reject","withRetry","fn","retries","delay","i","error","resolve","shuffle","arr","clone","i","j","randomInt","min","max","randomFloat","sample","count","sampleWithReplacement","result","index","sampleOne","random1ToN","n","sleep","ms","signal","resolve","reject","createAbortError","timer","cleanup","onAbort","sleepRandom","minMS","maxMS","formatDate","date","pattern","d","year","month","day","hours","minutes","seconds","getFormattedDateTime","getFormattedDate","getFormattedTime","formatTime","timestamp","num","invalidCharRegex","isHttpUrl","path","url","parseUrl","origin","pathname","searchParams","encodeURLParams","params","keepEmptyValues","key","raw","mergeQueryParams","urlSearchParams","mergedParams","value","formatUnitSize","value","base","units","invalidText","size","unitIndex"]}
|
package/dist/core.d.cts
CHANGED
|
@@ -2,35 +2,27 @@
|
|
|
2
2
|
* 通用错误
|
|
3
3
|
*/
|
|
4
4
|
declare class CommonError extends Error {
|
|
5
|
-
|
|
6
|
-
constructor(message: string,
|
|
5
|
+
cause?: Error;
|
|
6
|
+
constructor(message: string, cause?: unknown);
|
|
7
7
|
toString(): string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
interface BizError {
|
|
11
|
+
name: string;
|
|
12
|
+
message: string;
|
|
13
|
+
stack?: string;
|
|
14
|
+
cause?: BizError;
|
|
15
|
+
}
|
|
16
|
+
type BizResult<T = unknown> = {
|
|
17
|
+
success: true;
|
|
15
18
|
msg: string;
|
|
16
19
|
data?: T;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* 展开成Promise
|
|
24
|
-
*/
|
|
25
|
-
toPromise(): Promise<void>;
|
|
26
|
-
toPromise<T>(): Promise<T>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 执行业务(自动包裹BuResult)
|
|
31
|
-
* @param run 执行方法
|
|
32
|
-
*/
|
|
33
|
-
declare const execBiz: <T>(run: () => Promise<T>) => Promise<BizResult<T>>;
|
|
20
|
+
} | {
|
|
21
|
+
success: false;
|
|
22
|
+
msg: string;
|
|
23
|
+
data?: T;
|
|
24
|
+
error?: BizError;
|
|
25
|
+
};
|
|
34
26
|
|
|
35
27
|
/**
|
|
36
28
|
* 提取getter属性
|
|
@@ -86,6 +78,16 @@ type FunctionKeys<T> = {
|
|
|
86
78
|
type ObjectWithKeys<K extends string | symbol> = {
|
|
87
79
|
[key in K]: any;
|
|
88
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* 只存在一个字段
|
|
83
|
+
*/
|
|
84
|
+
type ExactlyOne<T> = {
|
|
85
|
+
[K in keyof T]: {
|
|
86
|
+
[P in K]: T[P];
|
|
87
|
+
} & {
|
|
88
|
+
[P in Exclude<keyof T, K>]?: never;
|
|
89
|
+
};
|
|
90
|
+
}[keyof T];
|
|
89
91
|
|
|
90
92
|
/**
|
|
91
93
|
* 物理像素的区域数据
|
|
@@ -104,6 +106,11 @@ interface PhysicalPoint {
|
|
|
104
106
|
y: number;
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
/**
|
|
110
|
+
* 检查取消信号
|
|
111
|
+
*/
|
|
112
|
+
declare function checkAbortSignal(abortSignal?: AbortSignal): void;
|
|
113
|
+
|
|
107
114
|
/**
|
|
108
115
|
* 是否为空数组
|
|
109
116
|
* @param data
|
|
@@ -116,11 +123,31 @@ declare function isEmptyArr(data: unknown): boolean;
|
|
|
116
123
|
* @example chunk([1,2,3,4,5], 2) => [[1,2],[3,4],[5]]
|
|
117
124
|
*/
|
|
118
125
|
declare function chunk<T>(arr: T[], size: number): T[][];
|
|
126
|
+
/**
|
|
127
|
+
* 判断数组是否存在某些项
|
|
128
|
+
*/
|
|
129
|
+
declare function inArray<T>(value: T, array: readonly T[]): boolean;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 是否为业务结果(序列化后)
|
|
133
|
+
*/
|
|
134
|
+
declare function isBizResult<T = unknown>(result: unknown): result is BizResult<T>;
|
|
135
|
+
/**
|
|
136
|
+
* 执行异步函数,自动捕获异常并转换为 BizResult 对象
|
|
137
|
+
* @param runFn 执行方法
|
|
138
|
+
*/
|
|
139
|
+
declare const execBiz: <T>(runFn: () => T | Promise<T>) => Promise<BizResult<Awaited<T>>>;
|
|
140
|
+
/**
|
|
141
|
+
* 解包 BizResult<T> 对象为 Promise<T>,非 BizResult 对象原样返回。
|
|
142
|
+
*/
|
|
143
|
+
declare function unwrapBizResult<T>(result: BizResult<T>): Promise<T>;
|
|
144
|
+
declare function unwrapBizResult(result: unknown): Promise<unknown>;
|
|
145
|
+
declare function bizErrorToError(bizError: BizError): Error & BizError;
|
|
119
146
|
|
|
120
147
|
/**
|
|
121
148
|
* 是否为取消操作的错误
|
|
122
149
|
*/
|
|
123
|
-
declare function isCanceledError(err: unknown):
|
|
150
|
+
declare function isCanceledError(err: unknown): err is Error;
|
|
124
151
|
/**
|
|
125
152
|
* 创建取消错误
|
|
126
153
|
* @param msg
|
|
@@ -214,6 +241,12 @@ declare function parseGithubRawUrl(gitHubRepoFile: GitHubRepoFile): string;
|
|
|
214
241
|
*/
|
|
215
242
|
declare function getGithubRawJson<O>(gitHubRepoFile: GitHubRepoFile): Promise<O>;
|
|
216
243
|
|
|
244
|
+
/**
|
|
245
|
+
* 忽略 undefined 值的字段
|
|
246
|
+
* @param obj
|
|
247
|
+
*/
|
|
248
|
+
declare const omitUndefined: <T extends object>(obj: T) => Partial<T>;
|
|
249
|
+
|
|
217
250
|
/**
|
|
218
251
|
* 为 Promise 添加超时控制
|
|
219
252
|
* @param promise - 原始异步任务
|
|
@@ -272,17 +305,9 @@ declare function random1ToN(n: number): number;
|
|
|
272
305
|
*/
|
|
273
306
|
declare function stripFunctions<T>(value: T, seen?: WeakMap<object, any>): T;
|
|
274
307
|
/**
|
|
275
|
-
*
|
|
308
|
+
* 序列号错误对象 为 BizError
|
|
276
309
|
*/
|
|
277
|
-
declare function serializeError(err: unknown):
|
|
278
|
-
name: string;
|
|
279
|
-
message: string;
|
|
280
|
-
stack: string | undefined;
|
|
281
|
-
} | {
|
|
282
|
-
name: string;
|
|
283
|
-
message: string;
|
|
284
|
-
stack?: undefined;
|
|
285
|
-
};
|
|
310
|
+
declare function serializeError(err: unknown): BizError;
|
|
286
311
|
|
|
287
312
|
/**
|
|
288
313
|
* 支持取消的 sleep
|
|
@@ -389,4 +414,4 @@ interface FormattedUnitSize {
|
|
|
389
414
|
*/
|
|
390
415
|
declare function formatUnitSize(value: number, base: number, units: string[], invalidText?: string): FormattedUnitSize;
|
|
391
416
|
|
|
392
|
-
export { type AnyFn, BizResult, CommonError, type ExtractGetterProperties, type FormattedUnitSize, type FunctionKeys, type GitHubRepo, type GitHubRepoBranch, type GitHubRepoFile, type KeysMatching, type MaxLengthArray, type MethodKeys, type MethodParams, type MethodReturn, type ObjectWithKeys, type ParsedUrl, type PhysicalPoint, type PhysicalRect, type QueryParams, type RequireAtLeastOne, type ResponseBodyFormat, chunk, convertToCommonError, createAbortError, createResponseError, dynamicCall, encodeURLParams, execBiz, fetchWithFormat, formatDate, formatTime, formatUnitSize, getErrorMessage, getFormattedDate, getFormattedDateTime, getFormattedTime, getGithubRawJson, invalidCharRegex, isCanceledError, isCommonError, isEmptyArr, isHttpUrl, isObject, isPlainObject, mergeQueryParams, parseGithubRawUrl, parseGithubRepoUrl, parseUrl, processRangeInBatches, random1ToN, randomFloat, randomInt, runRandomFunctions, sample, sampleOne, sampleWithReplacement, serializeError, shuffle, sleep, sleepRandom, stripFunctions, withRetry, withTimeout };
|
|
417
|
+
export { type AnyFn, type BizError, type BizResult, CommonError, type ExactlyOne, type ExtractGetterProperties, type FormattedUnitSize, type FunctionKeys, type GitHubRepo, type GitHubRepoBranch, type GitHubRepoFile, type KeysMatching, type MaxLengthArray, type MethodKeys, type MethodParams, type MethodReturn, type ObjectWithKeys, type ParsedUrl, type PhysicalPoint, type PhysicalRect, type QueryParams, type RequireAtLeastOne, type ResponseBodyFormat, bizErrorToError, checkAbortSignal, chunk, convertToCommonError, createAbortError, createResponseError, dynamicCall, encodeURLParams, execBiz, fetchWithFormat, formatDate, formatTime, formatUnitSize, getErrorMessage, getFormattedDate, getFormattedDateTime, getFormattedTime, getGithubRawJson, inArray, invalidCharRegex, isBizResult, isCanceledError, isCommonError, isEmptyArr, isHttpUrl, isObject, isPlainObject, mergeQueryParams, omitUndefined, parseGithubRawUrl, parseGithubRepoUrl, parseUrl, processRangeInBatches, random1ToN, randomFloat, randomInt, runRandomFunctions, sample, sampleOne, sampleWithReplacement, serializeError, shuffle, sleep, sleepRandom, stripFunctions, unwrapBizResult, withRetry, withTimeout };
|
package/dist/core.d.ts
CHANGED
|
@@ -2,35 +2,27 @@
|
|
|
2
2
|
* 通用错误
|
|
3
3
|
*/
|
|
4
4
|
declare class CommonError extends Error {
|
|
5
|
-
|
|
6
|
-
constructor(message: string,
|
|
5
|
+
cause?: Error;
|
|
6
|
+
constructor(message: string, cause?: unknown);
|
|
7
7
|
toString(): string;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
interface BizError {
|
|
11
|
+
name: string;
|
|
12
|
+
message: string;
|
|
13
|
+
stack?: string;
|
|
14
|
+
cause?: BizError;
|
|
15
|
+
}
|
|
16
|
+
type BizResult<T = unknown> = {
|
|
17
|
+
success: true;
|
|
15
18
|
msg: string;
|
|
16
19
|
data?: T;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* 展开成Promise
|
|
24
|
-
*/
|
|
25
|
-
toPromise(): Promise<void>;
|
|
26
|
-
toPromise<T>(): Promise<T>;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 执行业务(自动包裹BuResult)
|
|
31
|
-
* @param run 执行方法
|
|
32
|
-
*/
|
|
33
|
-
declare const execBiz: <T>(run: () => Promise<T>) => Promise<BizResult<T>>;
|
|
20
|
+
} | {
|
|
21
|
+
success: false;
|
|
22
|
+
msg: string;
|
|
23
|
+
data?: T;
|
|
24
|
+
error?: BizError;
|
|
25
|
+
};
|
|
34
26
|
|
|
35
27
|
/**
|
|
36
28
|
* 提取getter属性
|
|
@@ -86,6 +78,16 @@ type FunctionKeys<T> = {
|
|
|
86
78
|
type ObjectWithKeys<K extends string | symbol> = {
|
|
87
79
|
[key in K]: any;
|
|
88
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* 只存在一个字段
|
|
83
|
+
*/
|
|
84
|
+
type ExactlyOne<T> = {
|
|
85
|
+
[K in keyof T]: {
|
|
86
|
+
[P in K]: T[P];
|
|
87
|
+
} & {
|
|
88
|
+
[P in Exclude<keyof T, K>]?: never;
|
|
89
|
+
};
|
|
90
|
+
}[keyof T];
|
|
89
91
|
|
|
90
92
|
/**
|
|
91
93
|
* 物理像素的区域数据
|
|
@@ -104,6 +106,11 @@ interface PhysicalPoint {
|
|
|
104
106
|
y: number;
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
/**
|
|
110
|
+
* 检查取消信号
|
|
111
|
+
*/
|
|
112
|
+
declare function checkAbortSignal(abortSignal?: AbortSignal): void;
|
|
113
|
+
|
|
107
114
|
/**
|
|
108
115
|
* 是否为空数组
|
|
109
116
|
* @param data
|
|
@@ -116,11 +123,31 @@ declare function isEmptyArr(data: unknown): boolean;
|
|
|
116
123
|
* @example chunk([1,2,3,4,5], 2) => [[1,2],[3,4],[5]]
|
|
117
124
|
*/
|
|
118
125
|
declare function chunk<T>(arr: T[], size: number): T[][];
|
|
126
|
+
/**
|
|
127
|
+
* 判断数组是否存在某些项
|
|
128
|
+
*/
|
|
129
|
+
declare function inArray<T>(value: T, array: readonly T[]): boolean;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* 是否为业务结果(序列化后)
|
|
133
|
+
*/
|
|
134
|
+
declare function isBizResult<T = unknown>(result: unknown): result is BizResult<T>;
|
|
135
|
+
/**
|
|
136
|
+
* 执行异步函数,自动捕获异常并转换为 BizResult 对象
|
|
137
|
+
* @param runFn 执行方法
|
|
138
|
+
*/
|
|
139
|
+
declare const execBiz: <T>(runFn: () => T | Promise<T>) => Promise<BizResult<Awaited<T>>>;
|
|
140
|
+
/**
|
|
141
|
+
* 解包 BizResult<T> 对象为 Promise<T>,非 BizResult 对象原样返回。
|
|
142
|
+
*/
|
|
143
|
+
declare function unwrapBizResult<T>(result: BizResult<T>): Promise<T>;
|
|
144
|
+
declare function unwrapBizResult(result: unknown): Promise<unknown>;
|
|
145
|
+
declare function bizErrorToError(bizError: BizError): Error & BizError;
|
|
119
146
|
|
|
120
147
|
/**
|
|
121
148
|
* 是否为取消操作的错误
|
|
122
149
|
*/
|
|
123
|
-
declare function isCanceledError(err: unknown):
|
|
150
|
+
declare function isCanceledError(err: unknown): err is Error;
|
|
124
151
|
/**
|
|
125
152
|
* 创建取消错误
|
|
126
153
|
* @param msg
|
|
@@ -214,6 +241,12 @@ declare function parseGithubRawUrl(gitHubRepoFile: GitHubRepoFile): string;
|
|
|
214
241
|
*/
|
|
215
242
|
declare function getGithubRawJson<O>(gitHubRepoFile: GitHubRepoFile): Promise<O>;
|
|
216
243
|
|
|
244
|
+
/**
|
|
245
|
+
* 忽略 undefined 值的字段
|
|
246
|
+
* @param obj
|
|
247
|
+
*/
|
|
248
|
+
declare const omitUndefined: <T extends object>(obj: T) => Partial<T>;
|
|
249
|
+
|
|
217
250
|
/**
|
|
218
251
|
* 为 Promise 添加超时控制
|
|
219
252
|
* @param promise - 原始异步任务
|
|
@@ -272,17 +305,9 @@ declare function random1ToN(n: number): number;
|
|
|
272
305
|
*/
|
|
273
306
|
declare function stripFunctions<T>(value: T, seen?: WeakMap<object, any>): T;
|
|
274
307
|
/**
|
|
275
|
-
*
|
|
308
|
+
* 序列号错误对象 为 BizError
|
|
276
309
|
*/
|
|
277
|
-
declare function serializeError(err: unknown):
|
|
278
|
-
name: string;
|
|
279
|
-
message: string;
|
|
280
|
-
stack: string | undefined;
|
|
281
|
-
} | {
|
|
282
|
-
name: string;
|
|
283
|
-
message: string;
|
|
284
|
-
stack?: undefined;
|
|
285
|
-
};
|
|
310
|
+
declare function serializeError(err: unknown): BizError;
|
|
286
311
|
|
|
287
312
|
/**
|
|
288
313
|
* 支持取消的 sleep
|
|
@@ -389,4 +414,4 @@ interface FormattedUnitSize {
|
|
|
389
414
|
*/
|
|
390
415
|
declare function formatUnitSize(value: number, base: number, units: string[], invalidText?: string): FormattedUnitSize;
|
|
391
416
|
|
|
392
|
-
export { type AnyFn, BizResult, CommonError, type ExtractGetterProperties, type FormattedUnitSize, type FunctionKeys, type GitHubRepo, type GitHubRepoBranch, type GitHubRepoFile, type KeysMatching, type MaxLengthArray, type MethodKeys, type MethodParams, type MethodReturn, type ObjectWithKeys, type ParsedUrl, type PhysicalPoint, type PhysicalRect, type QueryParams, type RequireAtLeastOne, type ResponseBodyFormat, chunk, convertToCommonError, createAbortError, createResponseError, dynamicCall, encodeURLParams, execBiz, fetchWithFormat, formatDate, formatTime, formatUnitSize, getErrorMessage, getFormattedDate, getFormattedDateTime, getFormattedTime, getGithubRawJson, invalidCharRegex, isCanceledError, isCommonError, isEmptyArr, isHttpUrl, isObject, isPlainObject, mergeQueryParams, parseGithubRawUrl, parseGithubRepoUrl, parseUrl, processRangeInBatches, random1ToN, randomFloat, randomInt, runRandomFunctions, sample, sampleOne, sampleWithReplacement, serializeError, shuffle, sleep, sleepRandom, stripFunctions, withRetry, withTimeout };
|
|
417
|
+
export { type AnyFn, type BizError, type BizResult, CommonError, type ExactlyOne, type ExtractGetterProperties, type FormattedUnitSize, type FunctionKeys, type GitHubRepo, type GitHubRepoBranch, type GitHubRepoFile, type KeysMatching, type MaxLengthArray, type MethodKeys, type MethodParams, type MethodReturn, type ObjectWithKeys, type ParsedUrl, type PhysicalPoint, type PhysicalRect, type QueryParams, type RequireAtLeastOne, type ResponseBodyFormat, bizErrorToError, checkAbortSignal, chunk, convertToCommonError, createAbortError, createResponseError, dynamicCall, encodeURLParams, execBiz, fetchWithFormat, formatDate, formatTime, formatUnitSize, getErrorMessage, getFormattedDate, getFormattedDateTime, getFormattedTime, getGithubRawJson, inArray, invalidCharRegex, isBizResult, isCanceledError, isCommonError, isEmptyArr, isHttpUrl, isObject, isPlainObject, mergeQueryParams, omitUndefined, parseGithubRawUrl, parseGithubRepoUrl, parseUrl, processRangeInBatches, random1ToN, randomFloat, randomInt, runRandomFunctions, sample, sampleOne, sampleWithReplacement, serializeError, shuffle, sleep, sleepRandom, stripFunctions, unwrapBizResult, withRetry, withTimeout };
|
package/dist/core.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
function k(t){return t instanceof Error?t.name==="AbortError"||t.name==="CanceledError"||"code"in t&&t.code==="ERR_CANCELED":!1}function a(t){let r=new Error(t??"\u64CD\u4F5C\u5DF2\u53D6\u6D88");return r.name="AbortError",r}function w(t){return t instanceof c||t instanceof Error&&t.name==="CommonError"}function y(t,r){return r&&(t.message=`${r} ${t.message}`),t}function S(t,r){return w(t)?y(t,r):new c(`${r} ${u(t)}`,t)}function u(t){return typeof t=="string"?t:t instanceof Error||t&&typeof t=="object"&&"message"in t&&typeof t.message=="string"?t.message:String(t)??"\u672A\u77E5\u9519\u8BEF"}var c=class extends Error{constructor(r,n){super(r),n!==void 0&&(this.cause=n instanceof Error?n:new Error(u(n))),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}toString(){return`${this.name}: ${this.message}${this.cause?` (caused by ${this.cause})`:""}`}};function U(t){if(t?.aborted)throw a()}function M(t){return Array.isArray(t)&&t.length===0}function L(t,r){if(!Number.isInteger(r)||r<1)throw new Error("size \u5FC5\u987B\u662F\u6B63\u6574\u6570");let n=[];for(let e=0;e<t.length;e+=r)n.push(t.slice(e,e+r));return n}function G(t,r){return r.includes(t)}function l(t){if(t===null||typeof t!="object")return!1;let r=Object.getPrototypeOf(t);return r===Object.prototype||r===null}function C(t,r){return t===null||typeof t!="object"||Array.isArray(t)?!1:r!==void 0?r in t:!0}function h(t,r=new WeakMap){if(typeof t=="function")return;if(t===null||typeof t!="object"||t instanceof Date||t instanceof RegExp||t instanceof Map||t instanceof Set||ArrayBuffer.isView(t)||t instanceof ArrayBuffer)return t;if(r.has(t))return r.get(t);if(Array.isArray(t)){let e=[];r.set(t,e);for(let o of t)e.push(h(o,r));return e}if(!l(t))return t;let n={};r.set(t,n);for(let[e,o]of Object.entries(t)){if(typeof o=="function")continue;let i=h(o,r);i!==void 0&&(n[e]=i)}return n}function p(t){return t instanceof Error?{name:t.name,message:t.message,stack:t.stack,cause:"cause"in t&&t.cause?p(t.cause):void 0}:{name:"UnknownError",message:u(t)}}function T(t){return t!=null&&typeof t=="object"&&"success"in t&&typeof t.success=="boolean"&&"msg"in t&&typeof t.msg=="string"}var _=async t=>{try{return{success:!0,msg:"\u64CD\u4F5C\u6210\u529F",data:await t()}}catch(r){return{success:!1,msg:"\u64CD\u4F5C\u5931\u8D25",error:p(r)}}};async function J(t){if(T(t)){if(t.success)return t.data;throw t.error?E(t.error):new Error(t.msg)}else return t}function E(t){return Object.assign(new Error,t)}function R(t){return new Error(`HTTP ${t.status}: ${t.statusText}`)}async function x(t,r,n){let e=await fetch(t,n);if(!e.ok)throw R(e);switch(r){case"json":return await e.json();case"text":return await e.text();case"blob":return await e.blob();case"arrayBuffer":return await e.arrayBuffer();case"formData":return await e.formData();default:return await e.text()}}function Y(t,r){r=r??t.length;let n=Math.floor(Math.random()*r)+1,e=[...t];for(let i=e.length-1;i>0;i--){let s=Math.floor(Math.random()*(i+1));[e[i],e[s]]=[e[s],e[i]]}let o=e.slice(0,n);for(let i of o)i()}async function X(t,r,n,e){for(let o=t;o<=r;o+=n){let i=r-o+1,s=Math.min(n,i),f=Array.from({length:s},(m,d)=>o+d);await e(f)}}function Z(t,r,...n){let e=r.split("."),o=t,i=0;for(;i<e.length-1;i++)if(o=o[e[i]],o==null)throw new Error(`Path '${e.slice(0,i+1).join(".")}' is null/undefined`);let s=o[e[i]];if(typeof s!="function")throw new Error(`'${r}' is not a function`);return s.apply(o,n)}function rt(t){t=t.replace(/^git\+/,"");let r=/^https:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git|\/)?$/,n=/^https:\/\/github\.com\/([^/]+)\/([^/]+)\/blob\/([^/]+)$/,e=t.match(r);if(e)return{owner:e[1],repo:e[2],branch:"main"};let o=t.match(n);if(o)return{owner:o[1],repo:o[2],branch:o[3]};throw new Error("\u89E3\u6790\u63D2\u4EF6\u4ED3\u5E93URL\u5931\u8D25")}function P(t){let{owner:r,repo:n,branch:e,filePath:o}=t;return`https://raw.githubusercontent.com/${r}/${n}/${e}/${o}`}async function et(t){return JSON.parse(await x(P(t),"json"))}var ot=t=>Object.fromEntries(Object.entries(t).filter(([r,n])=>n!==void 0));async function st(t,r){return await Promise.race([t,new Promise((n,e)=>{setTimeout(()=>{e(new Error("Timeout"))},r)})])}async function at(t,r=3,n=1e3){for(let e=0;e<r;e++)try{return await t()}catch(o){if(e===r-1)throw o;await new Promise(i=>setTimeout(i,n))}throw new Error("Unreachable")}function $(t){let r=t.slice();for(let n=r.length-1;n>0;n--){let e=Math.floor(Math.random()*(n+1));[r[n],r[e]]=[r[e],r[n]]}return r}function g(t,r){return Math.floor(Math.random()*(r-t+1))+t}function ct(t,r){return Math.random()*(r-t)+t}function ft(t,r){return $(t).slice(0,r)}function mt(t,r){let n=[];for(let e=0;e<r;e++){let o=g(0,t.length-1);n.push(t[o])}return n}function pt(t){return t[g(0,t.length-1)]}function gt(t){return g(1,t)}function j(t,r){return new Promise((n,e)=>{if(r?.aborted)return e(a());let o=setTimeout(()=>{s(),n()},t),i=()=>{clearTimeout(o),s(),e(a())},s=()=>{r?.removeEventListener("abort",i)};r?.addEventListener("abort",i)})}function ht(t,r,n){let e=Math.random()*(r-t)+t;return j(e,n)}function b(t,r){let n=isNaN(t.getTime())?new Date:t,e=n.getFullYear(),o=String(n.getMonth()+1).padStart(2,"0"),i=String(n.getDate()).padStart(2,"0"),s=String(n.getHours()).padStart(2,"0"),f=String(n.getMinutes()).padStart(2,"0"),m=String(n.getSeconds()).padStart(2,"0");return r==="date"?`${e}-${o}-${i}`:r==="time"?`${s}:${f}:${m}`:`${e}-${o}-${i} ${s}:${f}:${m}`}function dt(t){return b(t??new Date,"datetime")}function wt(t){return b(t??new Date,"date")}function yt(t){return b(t??new Date,"time")}function Tt(t){if(typeof t>"u")return"";if(t instanceof Date)return t.toLocaleString();{let r=typeof t=="string"?Number(t):t;return isNaN(r)?"":new Date(r<1e12?r*1e3:r).toLocaleString()}}var Rt=/[!'()*]/g;function Pt(t){try{let r=new URL(t);return r.protocol==="http:"||r.protocol==="https:"}catch{return!1}}function $t(t){try{let{origin:r,pathname:n,searchParams:e}=new URL(t);return{baseUrl:r+n,searchParams:e}}catch{throw new Error("Invalid URL")}}function jt(t,r=!1){return Object.keys(t).sort().filter(n=>r||t[n]!=null).map(n=>{let e=t[n]??"";return`${encodeURIComponent(n)}=${encodeURIComponent(String(e))}`}).join("&")}function Bt(t,r){let n={};return t.size>0&&t.forEach((e,o)=>{n[o]=e}),r&&(n={...n,...r}),n}function St(t,r,n,e=""){if(!Number.isFinite(t))return{size:0,unit:"",text:e};let o=t,i=0;for(;o>=r&&i<n.length-1;)o/=r,i++;return{size:o,unit:n[i],text:`${o.toFixed(2).replace(/\.?0+$/,"")} ${n[i]}`}}export{c as CommonError,E as bizErrorToError,U as checkAbortSignal,L as chunk,S as convertToCommonError,a as createAbortError,R as createResponseError,Z as dynamicCall,jt as encodeURLParams,_ as execBiz,x as fetchWithFormat,b as formatDate,Tt as formatTime,St as formatUnitSize,u as getErrorMessage,wt as getFormattedDate,dt as getFormattedDateTime,yt as getFormattedTime,et as getGithubRawJson,G as inArray,Rt as invalidCharRegex,T as isBizResult,k as isCanceledError,w as isCommonError,M as isEmptyArr,Pt as isHttpUrl,C as isObject,l as isPlainObject,Bt as mergeQueryParams,ot as omitUndefined,P as parseGithubRawUrl,rt as parseGithubRepoUrl,$t as parseUrl,X as processRangeInBatches,gt as random1ToN,ct as randomFloat,g as randomInt,Y as runRandomFunctions,ft as sample,pt as sampleOne,mt as sampleWithReplacement,p as serializeError,$ as shuffle,j as sleep,ht as sleepRandom,h as stripFunctions,J as unwrapBizResult,at as withRetry,st as withTimeout};
|
|
2
2
|
//# sourceMappingURL=core.js.map
|
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/error/common-error.ts","../src/core/utils/error.ts","../src/core/result/biz-result.ts","../src/core/result/exec-biz.ts","../src/core/utils/array.ts","../src/core/utils/fetch.ts","../src/core/utils/function.ts","../src/core/utils/github.ts","../src/core/utils/promise.ts","../src/core/utils/random.ts","../src/core/utils/type.ts","../src/core/utils/serialize.ts","../src/core/utils/sleep.ts","../src/core/utils/time.ts","../src/core/utils/url.ts","../src/core/utils/number/format.ts"],"sourcesContent":["/**\n * 通用错误\n */\nexport class CommonError extends Error {\n // 原始错误\n rawError?: Error\n\n constructor(message: string, rawError?: unknown) {\n super(message)\n\n if (rawError !== undefined) {\n this.rawError = rawError instanceof Error ? rawError : new Error(String(rawError))\n }\n\n this.name = new.target.name\n Object.setPrototypeOf(this, new.target.prototype)\n }\n\n toString() {\n return `${this.name}: ${this.message}${this.rawError ? ` (caused by ${this.rawError})` : ''}`\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 { getErrorMessage } from '../utils/error.js'\n\n/**\n * 业务执行结果\n */\nexport class BizResult<T> {\n success: boolean\n msg: string\n data?: T\n errorName?: string\n\n constructor(success: boolean, msg: string, data?: T) {\n this.success = success\n this.msg = msg\n this.data = data\n }\n\n static createSuccess<T>(data: T) {\n return new BizResult(true, '操作成功', data)\n }\n\n static createFail<T>(msg: string = '操作失败', data?: T) {\n return new BizResult(false, msg, data)\n }\n\n static createError<T>(e: unknown) {\n const bizResult = new BizResult<T>(false, getErrorMessage(e))\n if (e && typeof e === 'object' && 'name' in e && typeof e.name === 'string') {\n bizResult.errorName = e.name\n }\n return bizResult\n }\n\n /**\n * 展开成Promise\n */\n async toPromise(): Promise<void>\n async toPromise<T>(): Promise<T>\n async toPromise<T = void>(): Promise<T> {\n if (this.success) {\n return this.data as T\n } else {\n const error = new Error(this.msg)\n if (this.errorName) error.name = this.errorName\n throw error\n }\n }\n}\n","import { BizResult } from './biz-result.js'\n\n/**\n * 执行业务(自动包裹BuResult)\n * @param run 执行方法\n */\nexport const execBiz = async <T>(run: () => Promise<T>): Promise<BizResult<T>> => {\n try {\n return BizResult.createSuccess<T>(await run())\n } catch (e) {\n return BizResult.createError(e)\n }\n}\n","/**\n * 是否为空数组\n * @param data\n */\nexport function isEmptyArr(data: unknown) {\n return Array.isArray(data) && data.length === 0\n}\n\n/**\n * 将数组分块为指定大小的多个子数组\n * @param arr 原数组\n * @param size 每个块的长度\n * @example chunk([1,2,3,4,5], 2) => [[1,2],[3,4],[5]]\n */\nexport function chunk<T>(arr: T[], size: number): T[][] {\n if (!Number.isInteger(size) || size < 1) throw new Error('size 必须是正整数')\n const result: T[][] = []\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size))\n }\n return result\n}\n","/**\n * 用于指定 `fetch` 响应体的解析方式。\n * 对应 `Response` 对象的不同解析方法(`.json()`、`.text()` 等)。\n */\nexport type ResponseBodyFormat =\n | 'json' // 解析为 JSON 对象\n | 'text' // 解析为原始字符串\n | 'blob' // 解析为 Blob 对象(适用于二进制文件,如图片、压缩包)\n | 'arrayBuffer' // 解析为 ArrayBuffer(适用于底层二进制操作)\n | 'formData' // 解析为 FormData 对象(适用于处理 multipart/form-data 响应)\n\n/**\n * 创建响应错误\n */\nexport function createResponseError(response: Response) {\n return new Error(`HTTP ${response.status}: ${response.statusText}`)\n}\n\n/**\n * 使用指定的响应解析格式发起 fetch 请求\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function fetchWithFormat<T = any>(\n url: string,\n format: ResponseBodyFormat,\n init?: RequestInit,\n): Promise<T> {\n const response = await fetch(url, init)\n if (!response.ok) {\n throw createResponseError(response)\n }\n switch (format) {\n case 'json':\n return (await response.json()) as T\n case 'text':\n return (await response.text()) as T\n case 'blob':\n return (await response.blob()) as T\n case 'arrayBuffer':\n return (await response.arrayBuffer()) as T\n case 'formData':\n return (await response.formData()) as T\n default:\n return (await response.text()) as T\n }\n}\n","/**\n * 随机运行参数\n * @param fns\n * @param maxCount 最多执行次数(1 ~ fns.length)\n */\nexport function runRandomFunctions(fns: Array<() => void>, maxCount?: number): void {\n maxCount = maxCount ?? fns.length\n const count = Math.floor(Math.random() * maxCount) + 1\n\n const shuffled = [...fns]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n\n const selected = shuffled.slice(0, count)\n\n for (const fn of selected) {\n fn()\n }\n}\n\n/**\n * 将数字范围按批次处理,每个批次调用一次异步函数\n *\n * @param start - 起始值(包含)\n * @param end - 结束值(包含)\n * @param batchSize - 每批最大元素个数\n * @param processor - 处理单批数字数组的异步函数\n *\n * @example\n * // 分批处理 1..10,每批最多 3 个数字\n * await processRangeInBatches(1, 10, 3, async (batch) => {\n * console.log(batch) // 输出: [1,2,3], [4,5,6], [7,8,9], [10]\n * })\n */\nexport async function processRangeInBatches(\n start: number,\n end: number,\n batchSize: number,\n processor: (batch: number[]) => Promise<void>,\n): Promise<void> {\n for (let current = start; current <= end; current += batchSize) {\n const remaining = end - current + 1\n const actualSize = Math.min(batchSize, remaining)\n const batch = Array.from({ length: actualSize }, (_, idx) => current + idx)\n\n await processor(batch)\n }\n}\n\n/**\n * 动态调用\n * @param root 对象\n * @param path 调用路径\n * @param args 参数\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport function dynamicCall<T = any>(root: any, path: string, ...args: any[]): T {\n const segments = path.split('.')\n let context = root\n let i = 0\n for (; i < segments.length - 1; i++) {\n context = context[segments[i]]\n if (context == null) {\n throw new Error(`Path '${segments.slice(0, i + 1).join('.')}' is null/undefined`)\n }\n }\n const fn = context[segments[i]]\n if (typeof fn !== 'function') {\n throw new Error(`'${path}' is not a function`)\n }\n return fn.apply(context, args)\n}\n","import { fetchWithFormat } from './fetch.js'\n\n/** github 仓库信息 */\nexport interface GitHubRepo {\n /** 仓库所有者(用户或组织名) */\n owner: string\n /** 仓库名称 */\n repo: string\n}\n\n/** github 仓库分支信息 */\nexport type GitHubRepoBranch = GitHubRepo & {\n /** 分支名称 */\n branch: string\n}\n\n/** github 仓库文件信息 */\nexport type GitHubRepoFile = GitHubRepoBranch & {\n /** 文件路径 */\n filePath: string\n}\n\n/**\n * 从 url 解析 github 仓库信息\n * @param repositoryUrl 仓库 URL(支持带有git+前缀、具体文件路径)\n */\nexport function parseGithubRepoUrl(repositoryUrl: string): GitHubRepoBranch {\n // 去掉 git+ 前缀\n repositoryUrl = repositoryUrl.replace(/^git\\+/, '')\n\n const regexBase = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git|\\/)?$/\n const regexFile = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+)\\/blob\\/([^/]+)$/\n\n // 情况 1:git clone 地址 或 repo 根路径\n const m1 = repositoryUrl.match(regexBase)\n if (m1) {\n return {\n owner: m1[1],\n repo: m1[2],\n branch: 'main', // 默认分支 main\n }\n }\n\n // 情况 2:具体文件路径\n const m2 = repositoryUrl.match(regexFile)\n if (m2) {\n return {\n owner: m2[1],\n repo: m2[2],\n branch: m2[3],\n }\n }\n\n throw new Error('解析插件仓库URL失败')\n}\n\n/**\n * 解析 github 仓库文件的原始内容的 url\n */\nexport function parseGithubRawUrl(gitHubRepoFile: GitHubRepoFile) {\n const { owner, repo, branch, filePath } = gitHubRepoFile\n return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`\n}\n\n/**\n * 获取 github 仓库文件的 json 内容并解析为对象 T\n * @param gitHubRepoFile\n */\nexport async function getGithubRawJson<O>(gitHubRepoFile: GitHubRepoFile): Promise<O> {\n return JSON.parse(await fetchWithFormat(parseGithubRawUrl(gitHubRepoFile), 'json')) as O\n}\n","/**\n * 为 Promise 添加超时控制\n * @param promise - 原始异步任务\n * @param timeout - 超时时间(毫秒)\n * @returns 若原 Promise 在超时前完成,则返回其结果;否则抛出超时错误\n * @throws {Error} 超时后抛出 'Timeout' 错误\n */\nexport async function withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error('Timeout'))\n }, timeout)\n }),\n ])\n}\n\n/**\n * 带重试机制的异步函数执行器\n * @param fn - 返回 Promise 的异步函数\n * @param retries - 最大重试次数(默认 3)\n * @param delay - 重试间隔毫秒(默认 1000)\n * @returns Promise<T>\n */\nexport async function withRetry<T>(fn: () => Promise<T>, retries: number = 3, delay: number = 1000): Promise<T> {\n for (let i = 0; i < retries; i++) {\n try {\n return await fn()\n } catch (error) {\n if (i === retries - 1) throw error\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n throw new Error('Unreachable')\n}\n","/**\n * 数组洗牌\n */\nexport function shuffle<T>(arr: T[]): T[] {\n const clone = arr.slice()\n for (let i = clone.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[clone[i], clone[j]] = [clone[j], clone[i]]\n }\n return clone\n}\n\n/**\n * 获取 [min, max] 的随机整数\n */\nexport function randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * 获取 [min, max) 的随机浮点数\n */\nexport function randomFloat(min: number, max: number): number {\n return Math.random() * (max - min) + min\n}\n\n/**\n * 从数组中随机取若干项,不可重复\n */\nexport function sample<T>(arr: T[], count: number): T[] {\n return shuffle(arr).slice(0, count)\n}\n\n/**\n * 从数组中随机取若干项,可重复\n */\nexport function sampleWithReplacement<T>(arr: T[], count: number): T[] {\n const result: T[] = []\n for (let i = 0; i < count; i++) {\n const index = randomInt(0, arr.length - 1)\n result.push(arr[index])\n }\n return result\n}\n\n/**\n * 从数组中获取随机项\n */\nexport function sampleOne<T>(arr: T[]): T {\n return arr[randomInt(0, arr.length - 1)]\n}\n\n/**\n * 获取 1~n 的随机数\n */\nexport function random1ToN(n: number): number {\n return randomInt(1, n)\n}\n","import type { ObjectWithKeys } from '../types/helpers.js'\n\n/**\n * 判断一个值是否为纯对象\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n/**\n * 包括类实例、普通对象,不包括 数组、null 和原始类型\n */\nexport function isObject<K extends string | symbol>(value: unknown, key?: K): value is ObjectWithKeys<K> {\n if (value === null || typeof value !== 'object') return false\n if (Array.isArray(value)) return false\n if (key !== undefined) {\n return key in value\n }\n return true\n}\n","import { isPlainObject } from './type.js'\n\n/**\n * 递归移除对象(或数组)中的所有函数属性,返回一个可安全序列化的副本。\n *\n * @typeParam T - 输入值的类型。\n * @param value - 待处理的值(基本类型、对象、数组等)。\n * @param seen - 内部使用的 WeakMap,用于记录已访问过的对象,防止循环引用。\n * 调用方通常无需传递此参数。\n * @returns 处理后的新副本,其中所有函数属性都被移除。\n * 基本类型(string, number, boolean, null, undefined)将原样返回。\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function stripFunctions<T>(value: T, seen = new WeakMap<object, any>()): T {\n if (typeof value === 'function') return undefined as T\n if (value === null || typeof value !== 'object') return value\n\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Map ||\n value instanceof Set ||\n ArrayBuffer.isView(value) ||\n value instanceof ArrayBuffer\n ) {\n return value\n }\n\n if (seen.has(value as object)) {\n return seen.get(value as object)\n }\n\n if (Array.isArray(value)) {\n const out: unknown[] = []\n seen.set(value as object, out)\n for (const item of value) {\n out.push(stripFunctions(item, seen))\n }\n return out as T\n }\n\n if (!isPlainObject(value)) {\n return value\n }\n\n const out: Record<string, unknown> = {}\n seen.set(value as object, out)\n\n for (const [key, item] of Object.entries(value)) {\n if (typeof item === 'function') continue\n const next = stripFunctions(item, seen)\n if (next !== undefined) out[key] = next\n }\n\n return out as T\n}\n\n/**\n * 将错误转换为可 JSON 序列化的普通对象。\n */\nexport function serializeError(err: unknown) {\n if (err instanceof Error) {\n return {\n name: err.name,\n message: err.message,\n stack: err.stack,\n }\n }\n\n return {\n name: 'Error',\n message: String(err),\n }\n}\n","import { createAbortError } from './error.js'\n\n/**\n * 支持取消的 sleep\n * @param ms 延迟毫秒数\n * @param signal 取消信号\n */\nexport function sleep(ms: number, signal?: AbortSignal) {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) return reject(createAbortError())\n\n const timer = setTimeout(() => {\n cleanup()\n resolve()\n }, ms)\n\n const onAbort = () => {\n clearTimeout(timer)\n cleanup()\n reject(createAbortError())\n }\n\n const cleanup = () => {\n signal?.removeEventListener('abort', onAbort)\n }\n\n signal?.addEventListener('abort', onAbort)\n })\n}\n\n/**\n * 支持取消的随机 sleep\n * @param minMS 最小延迟毫秒数\n * @param maxMS 最大延迟毫秒数\n * @param signal 取消信号\n */\nexport function sleepRandom(minMS: number, maxMS: number, signal?: AbortSignal) {\n const ms = Math.random() * (maxMS - minMS) + minMS\n return sleep(ms, signal)\n}\n","/**\n * 格式化日期\n */\nexport function formatDate(date: Date, pattern: 'date' | 'time' | 'datetime'): string {\n const d = !isNaN(date.getTime()) ? date : new Date()\n const year = d.getFullYear()\n const month = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n const hours = String(d.getHours()).padStart(2, '0')\n const minutes = String(d.getMinutes()).padStart(2, '0')\n const seconds = String(d.getSeconds()).padStart(2, '0')\n\n if (pattern === 'date') return `${year}-${month}-${day}`\n if (pattern === 'time') return `${hours}:${minutes}:${seconds}`\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\n/**\n * 格式化时间为 2020-02-02 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedDateTime(date?: Date): string {\n return formatDate(date ?? new Date(), 'datetime')\n}\n\n/**\n * 格式化时间为 2020-02-02 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedDate(date?: Date): string {\n return formatDate(date ?? new Date(), 'date')\n}\n\n/**\n * 格式化时间为 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedTime(date?: Date): string {\n return formatDate(date ?? new Date(), 'time')\n}\n\n/**\n * 格式化时间戳。可自动判断 10/13 位时间戳\n * @param timestamp\n */\nexport function formatTime(timestamp: number | string | Date | undefined): string {\n if (typeof timestamp === 'undefined') return ''\n if (timestamp instanceof Date) {\n return timestamp.toLocaleString()\n } else {\n const num = typeof timestamp === 'string' ? Number(timestamp) : timestamp\n if (isNaN(num)) return ''\n\n return new Date(num < 1e12 ? num * 1000 : num).toLocaleString()\n }\n}\n","/** URL query 中必须编码的保留字符 (RFC 3986) */\nexport const invalidCharRegex = /[!'()*]/g\n\n/** 查询参数 */\nexport type QueryParams = Record<string, string | number | boolean | null | undefined>\n\n/**\n * 已解析的 url\n */\nexport interface ParsedUrl {\n baseUrl: string\n searchParams: URLSearchParams\n}\n\n/**\n * 判断字符串是否为有效的 HTTP/HTTPS URL\n * @param path - 待检测的字符串\n */\nexport function isHttpUrl(path: string) {\n try {\n const url = new URL(path)\n return url.protocol === 'http:' || url.protocol === 'https:'\n } catch {\n return false\n }\n}\n\n/**\n * 解析 url\n * @param url\n */\nexport function parseUrl(url: string): ParsedUrl {\n try {\n const { origin, pathname, searchParams } = new URL(url)\n return {\n baseUrl: origin + pathname,\n searchParams,\n }\n } catch {\n throw new Error('Invalid URL')\n }\n}\n\n/**\n * 编码 URL 参数\n * @param params 查询参数\n * @param keepEmptyValues 保留 null/undefined的值(空字符串)\n */\nexport function encodeURLParams(params: QueryParams, keepEmptyValues: boolean = false) {\n return (\n Object.keys(params)\n // 排序\n .sort()\n // 过滤\n .filter((key) => {\n return keepEmptyValues || params[key] != null\n })\n // 编码 key 和 value\n .map((key) => {\n const raw = params[key] ?? ''\n return `${encodeURIComponent(key)}=${encodeURIComponent(String(raw))}`\n })\n // 拼接\n .join('&')\n )\n}\n\n/**\n *\n */\n/**\n * 合并查询参数\n * @param urlSearchParams url解析后查询参数\n * @param params 单独传的查询参数\n */\nexport function mergeQueryParams(urlSearchParams: URLSearchParams, params?: QueryParams): QueryParams {\n let mergedParams: QueryParams = {}\n if (urlSearchParams.size > 0) {\n // 合并url里的查询参数\n urlSearchParams.forEach((value, key) => {\n mergedParams[key] = value\n })\n }\n // 处理单独传的查询参数对象\n if (params) {\n mergedParams = { ...mergedParams, ...params }\n }\n return mergedParams\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"],"mappings":"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","names":["CommonError","message","rawError","isCanceledError","err","createAbortError","msg","error","isCommonError","CommonError","prefixError","prefix","convertToCommonError","getErrorMessage","BizResult","_BizResult","success","msg","data","e","bizResult","getErrorMessage","error","execBiz","run","BizResult","e","isEmptyArr","data","chunk","arr","size","result","i","createResponseError","response","fetchWithFormat","url","format","init","runRandomFunctions","fns","maxCount","count","shuffled","j","selected","fn","processRangeInBatches","start","end","batchSize","processor","current","remaining","actualSize","batch","_","idx","dynamicCall","root","path","args","segments","context","parseGithubRepoUrl","repositoryUrl","regexBase","regexFile","m1","m2","parseGithubRawUrl","gitHubRepoFile","owner","repo","branch","filePath","getGithubRawJson","fetchWithFormat","withTimeout","promise","timeout","_","reject","withRetry","fn","retries","delay","i","error","resolve","shuffle","arr","clone","i","j","randomInt","min","max","randomFloat","sample","count","sampleWithReplacement","result","index","sampleOne","random1ToN","n","isPlainObject","value","proto","isObject","key","stripFunctions","value","seen","out","item","isPlainObject","key","next","serializeError","err","sleep","ms","signal","resolve","reject","createAbortError","timer","cleanup","onAbort","sleepRandom","minMS","maxMS","formatDate","date","pattern","d","year","month","day","hours","minutes","seconds","getFormattedDateTime","getFormattedDate","getFormattedTime","formatTime","timestamp","num","invalidCharRegex","isHttpUrl","path","url","parseUrl","origin","pathname","searchParams","encodeURLParams","params","keepEmptyValues","key","raw","mergeQueryParams","urlSearchParams","mergedParams","value","formatUnitSize","value","base","units","invalidText","size","unitIndex"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/utils/error.ts","../src/core/error/common-error.ts","../src/core/utils/abort.ts","../src/core/utils/array.ts","../src/core/utils/type.ts","../src/core/utils/serialize.ts","../src/core/utils/biz-result.ts","../src/core/utils/fetch.ts","../src/core/utils/function.ts","../src/core/utils/github.ts","../src/core/utils/object.ts","../src/core/utils/promise.ts","../src/core/utils/random.ts","../src/core/utils/sleep.ts","../src/core/utils/time.ts","../src/core/utils/url.ts","../src/core/utils/number/format.ts"],"sourcesContent":["import { CommonError } from '../error/common-error.js'\n\n/**\n * 是否为取消操作的错误\n */\nexport function isCanceledError(err: unknown): err is Error {\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 { getErrorMessage } from '../utils/error.js'\n\n/**\n * 通用错误\n */\nexport class CommonError extends Error {\n // 原始错误\n cause?: Error\n\n constructor(message: string, cause?: unknown) {\n super(message)\n\n if (cause !== undefined) {\n this.cause = cause instanceof Error ? cause : new Error(getErrorMessage(cause))\n }\n\n this.name = new.target.name\n Object.setPrototypeOf(this, new.target.prototype)\n }\n\n toString() {\n return `${this.name}: ${this.message}${this.cause ? ` (caused by ${this.cause})` : ''}`\n }\n}\n","import { createAbortError } from './error.js'\n\n/**\n * 检查取消信号\n */\nexport function checkAbortSignal(abortSignal?: AbortSignal) {\n if (abortSignal?.aborted) {\n throw createAbortError()\n }\n}\n","/**\n * 是否为空数组\n * @param data\n */\nexport function isEmptyArr(data: unknown) {\n return Array.isArray(data) && data.length === 0\n}\n\n/**\n * 将数组分块为指定大小的多个子数组\n * @param arr 原数组\n * @param size 每个块的长度\n * @example chunk([1,2,3,4,5], 2) => [[1,2],[3,4],[5]]\n */\nexport function chunk<T>(arr: T[], size: number): T[][] {\n if (!Number.isInteger(size) || size < 1) throw new Error('size 必须是正整数')\n const result: T[][] = []\n for (let i = 0; i < arr.length; i += size) {\n result.push(arr.slice(i, i + size))\n }\n return result\n}\n\n/**\n * 判断数组是否存在某些项\n */\nexport function inArray<T>(value: T, array: readonly T[]): boolean {\n return array.includes(value)\n}\n","import type { ObjectWithKeys } from '../types/helpers.js'\n\n/**\n * 判断一个值是否为纯对象\n */\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') return false\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n/**\n * 包括类实例、普通对象,不包括 数组、null 和原始类型\n */\nexport function isObject<K extends string | symbol>(value: unknown, key?: K): value is ObjectWithKeys<K> {\n if (value === null || typeof value !== 'object') return false\n if (Array.isArray(value)) return false\n if (key !== undefined) {\n return key in value\n }\n return true\n}\n","import { isPlainObject } from './type.js'\nimport { getErrorMessage } from './error.js'\nimport type { BizError } from '../types/biz-result.js'\n\n/**\n * 递归移除对象(或数组)中的所有函数属性,返回一个可安全序列化的副本。\n *\n * @typeParam T - 输入值的类型。\n * @param value - 待处理的值(基本类型、对象、数组等)。\n * @param seen - 内部使用的 WeakMap,用于记录已访问过的对象,防止循环引用。\n * 调用方通常无需传递此参数。\n * @returns 处理后的新副本,其中所有函数属性都被移除。\n * 基本类型(string, number, boolean, null, undefined)将原样返回。\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function stripFunctions<T>(value: T, seen = new WeakMap<object, any>()): T {\n if (typeof value === 'function') return undefined as T\n if (value === null || typeof value !== 'object') return value\n\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Map ||\n value instanceof Set ||\n ArrayBuffer.isView(value) ||\n value instanceof ArrayBuffer\n ) {\n return value\n }\n\n if (seen.has(value as object)) {\n return seen.get(value as object)\n }\n\n if (Array.isArray(value)) {\n const out: unknown[] = []\n seen.set(value as object, out)\n for (const item of value) {\n out.push(stripFunctions(item, seen))\n }\n return out as T\n }\n\n if (!isPlainObject(value)) {\n return value\n }\n\n const out: Record<string, unknown> = {}\n seen.set(value as object, out)\n\n for (const [key, item] of Object.entries(value)) {\n if (typeof item === 'function') continue\n const next = stripFunctions(item, seen)\n if (next !== undefined) out[key] = next\n }\n\n return out as T\n}\n\n/**\n * 序列号错误对象 为 BizError\n */\nexport function serializeError(err: unknown): BizError {\n if (!(err instanceof Error)) {\n return {\n name: 'UnknownError',\n message: getErrorMessage(err),\n }\n }\n\n return {\n name: err.name,\n message: err.message,\n stack: err.stack,\n cause: 'cause' in err && err.cause ? serializeError(err.cause) : undefined,\n }\n}\n","import type { BizResult, BizError } from '../types/biz-result.js'\nimport { serializeError } from './serialize.js'\n\n/**\n * 是否为业务结果(序列化后)\n */\nexport function isBizResult<T = unknown>(result: unknown): result is BizResult<T> {\n return (\n result != null &&\n typeof result === 'object' &&\n 'success' in result &&\n typeof result.success === 'boolean' &&\n 'msg' in result &&\n typeof result.msg === 'string'\n )\n}\n\n/**\n * 执行异步函数,自动捕获异常并转换为 BizResult 对象\n * @param runFn 执行方法\n */\nexport const execBiz = async <T>(runFn: () => T | Promise<T>): Promise<BizResult<Awaited<T>>> => {\n try {\n return {\n success: true,\n msg: '操作成功',\n data: await runFn(),\n }\n } catch (e) {\n return {\n success: false,\n msg: '操作失败',\n error: serializeError(e),\n }\n }\n}\n\n/**\n * 解包 BizResult<T> 对象为 Promise<T>,非 BizResult 对象原样返回。\n */\nexport async function unwrapBizResult<T>(result: BizResult<T>): Promise<T>\nexport async function unwrapBizResult(result: unknown): Promise<unknown>\nexport async function unwrapBizResult<T>(result: BizResult<T> | unknown): Promise<T | unknown> {\n if (isBizResult<T>(result)) {\n if (result.success) {\n return result.data as T\n }\n\n if (result.error) {\n throw bizErrorToError(result.error)\n }\n\n throw new Error(result.msg)\n } else {\n return result\n }\n}\n\nexport function bizErrorToError(bizError: BizError) {\n return Object.assign(new Error(), bizError)\n}\n","/**\n * 用于指定 `fetch` 响应体的解析方式。\n * 对应 `Response` 对象的不同解析方法(`.json()`、`.text()` 等)。\n */\nexport type ResponseBodyFormat =\n | 'json' // 解析为 JSON 对象\n | 'text' // 解析为原始字符串\n | 'blob' // 解析为 Blob 对象(适用于二进制文件,如图片、压缩包)\n | 'arrayBuffer' // 解析为 ArrayBuffer(适用于底层二进制操作)\n | 'formData' // 解析为 FormData 对象(适用于处理 multipart/form-data 响应)\n\n/**\n * 创建响应错误\n */\nexport function createResponseError(response: Response) {\n return new Error(`HTTP ${response.status}: ${response.statusText}`)\n}\n\n/**\n * 使用指定的响应解析格式发起 fetch 请求\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport async function fetchWithFormat<T = any>(\n url: string,\n format: ResponseBodyFormat,\n init?: RequestInit,\n): Promise<T> {\n const response = await fetch(url, init)\n if (!response.ok) {\n throw createResponseError(response)\n }\n switch (format) {\n case 'json':\n return (await response.json()) as T\n case 'text':\n return (await response.text()) as T\n case 'blob':\n return (await response.blob()) as T\n case 'arrayBuffer':\n return (await response.arrayBuffer()) as T\n case 'formData':\n return (await response.formData()) as T\n default:\n return (await response.text()) as T\n }\n}\n","/**\n * 随机运行参数\n * @param fns\n * @param maxCount 最多执行次数(1 ~ fns.length)\n */\nexport function runRandomFunctions(fns: Array<() => void>, maxCount?: number): void {\n maxCount = maxCount ?? fns.length\n const count = Math.floor(Math.random() * maxCount) + 1\n\n const shuffled = [...fns]\n for (let i = shuffled.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]\n }\n\n const selected = shuffled.slice(0, count)\n\n for (const fn of selected) {\n fn()\n }\n}\n\n/**\n * 将数字范围按批次处理,每个批次调用一次异步函数\n *\n * @param start - 起始值(包含)\n * @param end - 结束值(包含)\n * @param batchSize - 每批最大元素个数\n * @param processor - 处理单批数字数组的异步函数\n *\n * @example\n * // 分批处理 1..10,每批最多 3 个数字\n * await processRangeInBatches(1, 10, 3, async (batch) => {\n * console.log(batch) // 输出: [1,2,3], [4,5,6], [7,8,9], [10]\n * })\n */\nexport async function processRangeInBatches(\n start: number,\n end: number,\n batchSize: number,\n processor: (batch: number[]) => Promise<void>,\n): Promise<void> {\n for (let current = start; current <= end; current += batchSize) {\n const remaining = end - current + 1\n const actualSize = Math.min(batchSize, remaining)\n const batch = Array.from({ length: actualSize }, (_, idx) => current + idx)\n\n await processor(batch)\n }\n}\n\n/**\n * 动态调用\n * @param root 对象\n * @param path 调用路径\n * @param args 参数\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport function dynamicCall<T = any>(root: any, path: string, ...args: any[]): T {\n const segments = path.split('.')\n let context = root\n let i = 0\n for (; i < segments.length - 1; i++) {\n context = context[segments[i]]\n if (context == null) {\n throw new Error(`Path '${segments.slice(0, i + 1).join('.')}' is null/undefined`)\n }\n }\n const fn = context[segments[i]]\n if (typeof fn !== 'function') {\n throw new Error(`'${path}' is not a function`)\n }\n return fn.apply(context, args)\n}\n","import { fetchWithFormat } from './fetch.js'\n\n/** github 仓库信息 */\nexport interface GitHubRepo {\n /** 仓库所有者(用户或组织名) */\n owner: string\n /** 仓库名称 */\n repo: string\n}\n\n/** github 仓库分支信息 */\nexport type GitHubRepoBranch = GitHubRepo & {\n /** 分支名称 */\n branch: string\n}\n\n/** github 仓库文件信息 */\nexport type GitHubRepoFile = GitHubRepoBranch & {\n /** 文件路径 */\n filePath: string\n}\n\n/**\n * 从 url 解析 github 仓库信息\n * @param repositoryUrl 仓库 URL(支持带有git+前缀、具体文件路径)\n */\nexport function parseGithubRepoUrl(repositoryUrl: string): GitHubRepoBranch {\n // 去掉 git+ 前缀\n repositoryUrl = repositoryUrl.replace(/^git\\+/, '')\n\n const regexBase = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git|\\/)?$/\n const regexFile = /^https:\\/\\/github\\.com\\/([^/]+)\\/([^/]+)\\/blob\\/([^/]+)$/\n\n // 情况 1:git clone 地址 或 repo 根路径\n const m1 = repositoryUrl.match(regexBase)\n if (m1) {\n return {\n owner: m1[1],\n repo: m1[2],\n branch: 'main', // 默认分支 main\n }\n }\n\n // 情况 2:具体文件路径\n const m2 = repositoryUrl.match(regexFile)\n if (m2) {\n return {\n owner: m2[1],\n repo: m2[2],\n branch: m2[3],\n }\n }\n\n throw new Error('解析插件仓库URL失败')\n}\n\n/**\n * 解析 github 仓库文件的原始内容的 url\n */\nexport function parseGithubRawUrl(gitHubRepoFile: GitHubRepoFile) {\n const { owner, repo, branch, filePath } = gitHubRepoFile\n return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`\n}\n\n/**\n * 获取 github 仓库文件的 json 内容并解析为对象 T\n * @param gitHubRepoFile\n */\nexport async function getGithubRawJson<O>(gitHubRepoFile: GitHubRepoFile): Promise<O> {\n return JSON.parse(await fetchWithFormat(parseGithubRawUrl(gitHubRepoFile), 'json')) as O\n}\n","/**\n * 忽略 undefined 值的字段\n * @param obj\n */\nexport const omitUndefined = <T extends object>(obj: T): Partial<T> =>\n Object.fromEntries(Object.entries(obj).filter(([_, v]) => v !== undefined)) as Partial<T>\n","/**\n * 为 Promise 添加超时控制\n * @param promise - 原始异步任务\n * @param timeout - 超时时间(毫秒)\n * @returns 若原 Promise 在超时前完成,则返回其结果;否则抛出超时错误\n * @throws {Error} 超时后抛出 'Timeout' 错误\n */\nexport async function withTimeout<T>(promise: Promise<T>, timeout: number): Promise<T> {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error('Timeout'))\n }, timeout)\n }),\n ])\n}\n\n/**\n * 带重试机制的异步函数执行器\n * @param fn - 返回 Promise 的异步函数\n * @param retries - 最大重试次数(默认 3)\n * @param delay - 重试间隔毫秒(默认 1000)\n * @returns Promise<T>\n */\nexport async function withRetry<T>(fn: () => Promise<T>, retries: number = 3, delay: number = 1000): Promise<T> {\n for (let i = 0; i < retries; i++) {\n try {\n return await fn()\n } catch (error) {\n if (i === retries - 1) throw error\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n throw new Error('Unreachable')\n}\n","/**\n * 数组洗牌\n */\nexport function shuffle<T>(arr: T[]): T[] {\n const clone = arr.slice()\n for (let i = clone.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[clone[i], clone[j]] = [clone[j], clone[i]]\n }\n return clone\n}\n\n/**\n * 获取 [min, max] 的随机整数\n */\nexport function randomInt(min: number, max: number): number {\n return Math.floor(Math.random() * (max - min + 1)) + min\n}\n\n/**\n * 获取 [min, max) 的随机浮点数\n */\nexport function randomFloat(min: number, max: number): number {\n return Math.random() * (max - min) + min\n}\n\n/**\n * 从数组中随机取若干项,不可重复\n */\nexport function sample<T>(arr: T[], count: number): T[] {\n return shuffle(arr).slice(0, count)\n}\n\n/**\n * 从数组中随机取若干项,可重复\n */\nexport function sampleWithReplacement<T>(arr: T[], count: number): T[] {\n const result: T[] = []\n for (let i = 0; i < count; i++) {\n const index = randomInt(0, arr.length - 1)\n result.push(arr[index])\n }\n return result\n}\n\n/**\n * 从数组中获取随机项\n */\nexport function sampleOne<T>(arr: T[]): T {\n return arr[randomInt(0, arr.length - 1)]\n}\n\n/**\n * 获取 1~n 的随机数\n */\nexport function random1ToN(n: number): number {\n return randomInt(1, n)\n}\n","import { createAbortError } from './error.js'\n\n/**\n * 支持取消的 sleep\n * @param ms 延迟毫秒数\n * @param signal 取消信号\n */\nexport function sleep(ms: number, signal?: AbortSignal) {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) return reject(createAbortError())\n\n const timer = setTimeout(() => {\n cleanup()\n resolve()\n }, ms)\n\n const onAbort = () => {\n clearTimeout(timer)\n cleanup()\n reject(createAbortError())\n }\n\n const cleanup = () => {\n signal?.removeEventListener('abort', onAbort)\n }\n\n signal?.addEventListener('abort', onAbort)\n })\n}\n\n/**\n * 支持取消的随机 sleep\n * @param minMS 最小延迟毫秒数\n * @param maxMS 最大延迟毫秒数\n * @param signal 取消信号\n */\nexport function sleepRandom(minMS: number, maxMS: number, signal?: AbortSignal) {\n const ms = Math.random() * (maxMS - minMS) + minMS\n return sleep(ms, signal)\n}\n","/**\n * 格式化日期\n */\nexport function formatDate(date: Date, pattern: 'date' | 'time' | 'datetime'): string {\n const d = !isNaN(date.getTime()) ? date : new Date()\n const year = d.getFullYear()\n const month = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n const hours = String(d.getHours()).padStart(2, '0')\n const minutes = String(d.getMinutes()).padStart(2, '0')\n const seconds = String(d.getSeconds()).padStart(2, '0')\n\n if (pattern === 'date') return `${year}-${month}-${day}`\n if (pattern === 'time') return `${hours}:${minutes}:${seconds}`\n return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`\n}\n\n/**\n * 格式化时间为 2020-02-02 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedDateTime(date?: Date): string {\n return formatDate(date ?? new Date(), 'datetime')\n}\n\n/**\n * 格式化时间为 2020-02-02 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedDate(date?: Date): string {\n return formatDate(date ?? new Date(), 'date')\n}\n\n/**\n * 格式化时间为 20:20:20 的字符串\n * @param date 需要格式化的时间,为空则获取当前时间\n */\nexport function getFormattedTime(date?: Date): string {\n return formatDate(date ?? new Date(), 'time')\n}\n\n/**\n * 格式化时间戳。可自动判断 10/13 位时间戳\n * @param timestamp\n */\nexport function formatTime(timestamp: number | string | Date | undefined): string {\n if (typeof timestamp === 'undefined') return ''\n if (timestamp instanceof Date) {\n return timestamp.toLocaleString()\n } else {\n const num = typeof timestamp === 'string' ? Number(timestamp) : timestamp\n if (isNaN(num)) return ''\n\n return new Date(num < 1e12 ? num * 1000 : num).toLocaleString()\n }\n}\n","/** URL query 中必须编码的保留字符 (RFC 3986) */\nexport const invalidCharRegex = /[!'()*]/g\n\n/** 查询参数 */\nexport type QueryParams = Record<string, string | number | boolean | null | undefined>\n\n/**\n * 已解析的 url\n */\nexport interface ParsedUrl {\n baseUrl: string\n searchParams: URLSearchParams\n}\n\n/**\n * 判断字符串是否为有效的 HTTP/HTTPS URL\n * @param path - 待检测的字符串\n */\nexport function isHttpUrl(path: string) {\n try {\n const url = new URL(path)\n return url.protocol === 'http:' || url.protocol === 'https:'\n } catch {\n return false\n }\n}\n\n/**\n * 解析 url\n * @param url\n */\nexport function parseUrl(url: string): ParsedUrl {\n try {\n const { origin, pathname, searchParams } = new URL(url)\n return {\n baseUrl: origin + pathname,\n searchParams,\n }\n } catch {\n throw new Error('Invalid URL')\n }\n}\n\n/**\n * 编码 URL 参数\n * @param params 查询参数\n * @param keepEmptyValues 保留 null/undefined的值(空字符串)\n */\nexport function encodeURLParams(params: QueryParams, keepEmptyValues: boolean = false) {\n return (\n Object.keys(params)\n // 排序\n .sort()\n // 过滤\n .filter((key) => {\n return keepEmptyValues || params[key] != null\n })\n // 编码 key 和 value\n .map((key) => {\n const raw = params[key] ?? ''\n return `${encodeURIComponent(key)}=${encodeURIComponent(String(raw))}`\n })\n // 拼接\n .join('&')\n )\n}\n\n/**\n *\n */\n/**\n * 合并查询参数\n * @param urlSearchParams url解析后查询参数\n * @param params 单独传的查询参数\n */\nexport function mergeQueryParams(urlSearchParams: URLSearchParams, params?: QueryParams): QueryParams {\n let mergedParams: QueryParams = {}\n if (urlSearchParams.size > 0) {\n // 合并url里的查询参数\n urlSearchParams.forEach((value, key) => {\n mergedParams[key] = value\n })\n }\n // 处理单独传的查询参数对象\n if (params) {\n mergedParams = { ...mergedParams, ...params }\n }\n return mergedParams\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"],"mappings":"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","names":["isCanceledError","err","createAbortError","msg","error","isCommonError","CommonError","prefixError","prefix","convertToCommonError","getErrorMessage","CommonError","message","cause","getErrorMessage","checkAbortSignal","abortSignal","createAbortError","isEmptyArr","data","chunk","arr","size","result","i","inArray","value","array","isPlainObject","value","proto","isObject","key","stripFunctions","value","seen","out","item","isPlainObject","key","next","serializeError","err","getErrorMessage","isBizResult","result","execBiz","runFn","e","serializeError","unwrapBizResult","bizErrorToError","bizError","createResponseError","response","fetchWithFormat","url","format","init","runRandomFunctions","fns","maxCount","count","shuffled","j","selected","fn","processRangeInBatches","start","end","batchSize","processor","current","remaining","actualSize","batch","_","idx","dynamicCall","root","path","args","segments","context","parseGithubRepoUrl","repositoryUrl","regexBase","regexFile","m1","m2","parseGithubRawUrl","gitHubRepoFile","owner","repo","branch","filePath","getGithubRawJson","fetchWithFormat","omitUndefined","obj","_","v","withTimeout","promise","timeout","_","reject","withRetry","fn","retries","delay","i","error","resolve","shuffle","arr","clone","i","j","randomInt","min","max","randomFloat","sample","count","sampleWithReplacement","result","index","sampleOne","random1ToN","n","sleep","ms","signal","resolve","reject","createAbortError","timer","cleanup","onAbort","sleepRandom","minMS","maxMS","formatDate","date","pattern","d","year","month","day","hours","minutes","seconds","getFormattedDateTime","getFormattedDate","getFormattedTime","formatTime","timestamp","num","invalidCharRegex","isHttpUrl","path","url","parseUrl","origin","pathname","searchParams","encodeURLParams","params","keepEmptyValues","key","raw","mergeQueryParams","urlSearchParams","mergedParams","value","formatUnitSize","value","base","units","invalidText","size","unitIndex"]}
|
package/dist/node.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var K=Object.create;var
|
|
1
|
+
"use strict";var K=Object.create;var d=Object.defineProperty;var Q=Object.getOwnPropertyDescriptor;var _=Object.getOwnPropertyNames;var V=Object.getPrototypeOf,q=Object.prototype.hasOwnProperty;var Y=(t,r)=>{for(var e in r)d(t,e,{get:r[e],enumerable:!0})},j=(t,r,e,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of _(r))!q.call(t,o)&&o!==e&&d(t,o,{get:()=>r[o],enumerable:!(n=Q(r,o))||n.enumerable});return t};var s=(t,r,e)=>(e=t!=null?K(V(t)):{},j(r||!t||!t.__esModule?d(e,"default",{value:t,enumerable:!0}):e,t)),X=t=>j(d({},"__esModule",{value:!0}),t);var wt={};Y(wt,{copyFile:()=>pt,deleteFiles:()=>et,downloadFile:()=>nt,emptyDirectory:()=>z,ensureDir:()=>m,ensureDirSync:()=>rt,execWinCmd:()=>c,existsFile:()=>tt,exportRegedit:()=>dt,findFiles:()=>O,findFilesByPrefixAndSuffix:()=>ot,formatFileSizeFromKB:()=>at,getDirSize:()=>S,getEnv:()=>Z,getFileSizeKB:()=>ft,importRegedit:()=>xt,isFile:()=>f,isFileNotFoundError:()=>h,openRegedit:()=>gt,readJSONFile:()=>L,readOrInitJSON:()=>st,showInExplorer:()=>lt,updateJSON:()=>it,writeJSONFile:()=>M});module.exports=X(wt);function Z(){let t={};for(let r in process.env)t[r]=process.env[r];return t}var x=s(require("fs/promises"),1),k=require("fs");async function f(t){try{return(await x.default.stat(t)).isFile()}catch{return!1}}async function tt(t,r){try{return x.default.access(t,r),!0}catch{return!1}}function rt(t){(0,k.mkdirSync)(t,{recursive:!0})}async function m(t){await x.default.mkdir(t,{recursive:!0})}function h(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}var p=s(require("fs/promises"),1),D=s(require("path"),1);async function z(t){let r=[],e=await p.default.readdir(t,{withFileTypes:!0});for(let n of e){let o=D.default.join(t,n.name);n.isDirectory()?(r.push(...await z(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 N=require("stream/promises"),B=require("fs"),A=s(require("path"),1);async function nt(t,r){let e=A.default.resolve(r),n=await fetch(t);if(!n.ok||!n.body)throw new Error(`\u4E0B\u8F7D\u5931\u8D25: ${n.status}`);await(0,N.pipeline)(n.body,(0,B.createWriteStream)(e))}var b=s(require("fs/promises"),1),T=s(require("path"),1);async function ot(t,r,e){return(await b.default.readdir(t,{withFileTypes:!0})).filter(n=>n.isFile()&&n.name.startsWith(r||"")&&n.name.endsWith(e||"")).map(n=>T.default.join(t,n.name))}async function O(t,r){let e=[],n=await b.default.readdir(t,{withFileTypes:!0});for(let o of n){let i=T.default.join(t,o.name);o.isDirectory()?e.push(...await O(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),E=s(require("path"),1);async function L(t){if(!await f(t))throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");let r=await u.default.readFile(E.default.resolve(t),"utf-8");return JSON.parse(r)}async function M(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 L(t),n=r(e);return await M(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(!h(e))throw e;let n=await r();return await m(E.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),C=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 S(t){let r;try{r=await l.default.readdir(t,{withFileTypes:!0})}catch{return 0}let e=[];for(let o of r){let i=C.default.join(t,o.name);if(o.isDirectory()){e.push(S(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 S(t)/1024:r.size/1024}catch{return 0}}function g(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 g();let o=null,i=(0,I.exec)(`${t}`,{encoding:"buffer"},(w,H,J)=>{let F=P.decode(H,"cp936"),W=P.decode(J,"cp936");o&&r?.signal?.removeEventListener("abort",o);let y=w?Number(w.code??0):0;(r?.codeIsSuccess?!r.codeIsSuccess(y):y!==0)?n(new Error(`\u547D\u4EE4\u884C\u6267\u884C\u51FA\u9519 (${y}): ${W||F}`)):e(F)}),a;o=()=>{i.kill("SIGTERM"),a=setTimeout(()=>{i.kill("SIGKILL")},3e3),n(g())},r?.signal&&r.signal.addEventListener("abort",o),i.on("close",()=>{a!==void 0&&clearTimeout(a),r?.signal?.removeEventListener("abort",o)})})}var R=s(require("path"),1);async function pt(t,r,e){let n=R.default.resolve(t),o=R.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 G=s(require("path"),1),v=s(require("fs/promises"),1);async function lt(t){t=G.default.resolve(t),(await v.stat(t)).isDirectory()?await c(`start "" "${t}"`):await c(`start "" explorer /select,"${t}"`)}var $=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=$.default.resolve(r),await m(t),await c(`reg export "${t}" "${r}" /y`,void 0)}async function xt(t,r){await c(`reg import "${$.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
2
|
//# sourceMappingURL=node.cjs.map
|
package/dist/node.cjs.map
CHANGED
|
@@ -1 +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"]}
|
|
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): err is Error {\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"]}
|
package/dist/node.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function
|
|
1
|
+
function K(){let t={};for(let r in process.env)t[r]=process.env[r];return t}import x from"fs/promises";import{mkdirSync as z}from"fs";async function f(t){try{return(await x.stat(t)).isFile()}catch{return!1}}async function q(t,r){try{return x.access(t,r),!0}catch{return!1}}function Y(t){z(t,{recursive:!0})}async function m(t){await x.mkdir(t,{recursive:!0})}function h(t){return typeof t=="object"&&t!==null&&"code"in t&&t.code==="ENOENT"}import p from"fs/promises";import N from"path";async function B(t){let r=[],e=await p.readdir(t,{withFileTypes:!0});for(let n of e){let o=N.join(t,n.name);n.isDirectory()?(r.push(...await B(o)),await p.rmdir(o)):await p.unlink(o),r.push(o)}return r}async function rt(t){return(await Promise.allSettled(t.map(async e=>(await p.unlink(e),e)))).filter(e=>e.status==="fulfilled").map(e=>e.value)}import{pipeline as A}from"stream/promises";import{createWriteStream as O}from"fs";import L from"path";async function st(t,r){let e=L.resolve(r),n=await fetch(t);if(!n.ok||!n.body)throw new Error(`\u4E0B\u8F7D\u5931\u8D25: ${n.status}`);await A(n.body,O(e))}import b from"fs/promises";import T from"path";async function ft(t,r,e){return(await b.readdir(t,{withFileTypes:!0})).filter(n=>n.isFile()&&n.name.startsWith(r||"")&&n.name.endsWith(e||"")).map(n=>T.join(t,n.name))}async function M(t,r){let e=[],n=await b.readdir(t,{withFileTypes:!0});for(let o of n){let i=T.join(t,o.name);o.isDirectory()?e.push(...await M(i,r)):(typeof r=="string"&&o.name===r||typeof r=="function"&&r(o.name,i))&&e.push(i)}return e}import c from"fs/promises";import E from"path";async function U(t){if(!await f(t))throw new Error("\u6587\u4EF6\u4E0D\u5B58\u5728");let r=await c.readFile(E.resolve(t),"utf-8");return JSON.parse(r)}async function C(t,r){let e=`${t}.tmp.${Date.now()}`,n=JSON.stringify(r,null,2);await c.writeFile(e,n,"utf-8"),await c.rename(e,t)}async function dt(t,r){let e=await U(t),n=r(e);return await C(t,n),n}async function xt(t,r){try{let e=await c.readFile(t,"utf-8");return JSON.parse(e)}catch(e){if(!h(e))throw e;let n=await r();return await m(E.dirname(t)),await c.writeFile(t,JSON.stringify(n,null,2),"utf-8"),n}}function S(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]}`}}import l from"fs/promises";import I from"path";function Et(t,r=""){let{size:e,unit:n,text:o}=S(t,1024,["KB","MB","GB"],r);if(o===r)return r;let i=Math.floor(e),s;return i>99?s=0:i>9?s=1:s=2,`${Math.round(e*10**s)/10**s} ${n}`}async function G(t){try{return(await l.stat(t)).size}catch{return 0}}async function v(t){try{return(await l.lstat(t)).size}catch{return 0}}async function P(t){let r;try{r=await l.readdir(t,{withFileTypes:!0})}catch{return 0}let e=[];for(let o of r){let i=I.join(t,o.name);if(o.isDirectory()){e.push(P(i));continue}if(o.isSymbolicLink()){e.push(v(i));continue}e.push(G(i))}return(await Promise.allSettled(e)).reduce((o,i)=>o+(i.status==="fulfilled"?i.value:0),0)}async function St(t){try{let r=await l.stat(t);return r.isDirectory()?await P(t)/1024:r.size/1024}catch{return 0}}function u(t){let r=new Error(t??"\u64CD\u4F5C\u5DF2\u53D6\u6D88");return r.name="AbortError",r}import{exec as J}from"child_process";import*as w from"iconv-lite";async function a(t,r){return new Promise((e,n)=>{if(r?.signal?.aborted)throw u();let o=null,i=J(`${t}`,{encoding:"buffer"},(g,j,k)=>{let y=w.decode(j,"cp936"),D=w.decode(k,"cp936");o&&r?.signal?.removeEventListener("abort",o);let d=g?Number(g.code??0):0;(r?.codeIsSuccess?!r.codeIsSuccess(d):d!==0)?n(new Error(`\u547D\u4EE4\u884C\u6267\u884C\u51FA\u9519 (${d}): ${D||y}`)):e(y)}),s;o=()=>{i.kill("SIGTERM"),s=setTimeout(()=>{i.kill("SIGKILL")},3e3),n(u())},r?.signal&&r.signal.addEventListener("abort",o),i.on("close",()=>{s!==void 0&&clearTimeout(s),r?.signal?.removeEventListener("abort",o)})})}import R from"path";async function $r(t,r,e){let n=R.resolve(t),o=R.resolve(r),i;await f(t)?(i=`copy "${n}" "${o}"`,await a(i,{signal:e})):(i=`robocopy "${n}" "${o}" /E /NFL /NDL /NJH /NJS /NC /NS /NP`,await a(i,{codeIsSuccess:s=>s<8,signal:e}))}import W from"path";import*as $ from"fs/promises";async function Dr(t){t=W.resolve(t),(await $.stat(t)).isDirectory()?await a(`start "" "${t}"`):await a(`start "" explorer /select,"${t}"`)}import F from"path";async function Or(t){return a(`taskkill /f /im regedit.exe & REG ADD "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Applets\\Regedit" /v "LastKey" /d "${t}" /f & regedit`)}async function Lr(t,r){r=F.resolve(r),await m(t),await a(`reg export "${t}" "${r}" /y`,void 0)}async function Mr(t,r){await a(`reg import "${F.resolve(r)}"`,void 0)}export{$r as copyFile,rt as deleteFiles,st as downloadFile,B as emptyDirectory,m as ensureDir,Y as ensureDirSync,a as execWinCmd,q as existsFile,Lr as exportRegedit,M as findFiles,ft as findFilesByPrefixAndSuffix,Et as formatFileSizeFromKB,P as getDirSize,K as getEnv,St as getFileSizeKB,Mr as importRegedit,f as isFile,h as isFileNotFoundError,Or as openRegedit,U as readJSONFile,xt as readOrInitJSON,Dr as showInExplorer,dt as updateJSON,C as writeJSONFile};
|
|
2
2
|
//# sourceMappingURL=node.js.map
|
package/dist/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../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":["/**\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":["getEnv","env","envKey","fs","mkdirSync","isFile","filePath","existsFile","file","mode","ensureDirSync","dirPath","ensureDir","isFileNotFoundError","error","fs","path","emptyDirectory","dir","deletedPaths","entries","entry","fullPath","deleteFiles","paths","file","r","pipeline","createWriteStream","path","downloadFile","url","filePath","file","res","fs","path","findFilesByPrefixAndSuffix","dir","prefix","suffix","dirent","findFiles","target","results","entries","entry","fullPath","fs","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","fs","path","formatFileSizeFromKB","sizeKB","invalidText","size","unit","text","formatUnitSize","integerPart","fractionDigits","getFileSize","filePath","getSymbolicLinkSize","linkPath","getDirSize","dir","entries","tasks","entry","fullPath","total","result","getFileSizeKB","stat","createAbortError","msg","error","exec","iconv","execWinCmd","cmd","options","resolve","reject","createAbortError","abortHandler","child","error","stdout","stderr","stdoutStr","stderrStr","exitCode","timeout","path","copyFile","source","destination","signal","src","path","dest","command","isFile","execWinCmd","number","path","fs","showInExplorer","fileOrDir","path","execWinCmd","path","openRegedit","path","execWinCmd","exportRegedit","regPath","filePath","ensureDir","importRegedit"]}
|
|
1
|
+
{"version":3,"sources":["../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":["/**\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): err is Error {\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":["getEnv","env","envKey","fs","mkdirSync","isFile","filePath","existsFile","file","mode","ensureDirSync","dirPath","ensureDir","isFileNotFoundError","error","fs","path","emptyDirectory","dir","deletedPaths","entries","entry","fullPath","deleteFiles","paths","file","r","pipeline","createWriteStream","path","downloadFile","url","filePath","file","res","fs","path","findFilesByPrefixAndSuffix","dir","prefix","suffix","dirent","findFiles","target","results","entries","entry","fullPath","fs","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","fs","path","formatFileSizeFromKB","sizeKB","invalidText","size","unit","text","formatUnitSize","integerPart","fractionDigits","getFileSize","filePath","getSymbolicLinkSize","linkPath","getDirSize","dir","entries","tasks","entry","fullPath","total","result","getFileSizeKB","stat","createAbortError","msg","error","exec","iconv","execWinCmd","cmd","options","resolve","reject","createAbortError","abortHandler","child","error","stdout","stderr","stdoutStr","stderrStr","exitCode","timeout","path","copyFile","source","destination","signal","src","path","dest","command","isFile","execWinCmd","number","path","fs","showInExplorer","fileOrDir","path","execWinCmd","path","openRegedit","path","execWinCmd","exportRegedit","regPath","filePath","ensureDir","importRegedit"]}
|