/** * 清除全局 scope 注册表(用于测试) */ declare const clearPendingRegistry: () => void; /** * Scope 冲突错误 */ declare class PendingScopeConflictError extends Error { readonly scope: string; constructor(scope: string); } /** * pendingFn 回调函数接收的参数 */ type PendingFnParams = { /** 解析后的 scope key */ scope: string; /** 获取当前等待中的 caller 数量(实时值) */ getPendingCount: () => number; }; /** * pendingFn 的回调函数类型 */ type PendingCallbackFn = (params: PendingFnParams, ...args: Args) => T; /** * 感知 pending 状态的 inflight 去重 * * fn 的第一个参数为 PendingFnParams,包含 scope 和 getPendingCount, * 允许根据 pending 状态做自定义处理(如短路检查、日志等)。 * 返回的函数剥掉 PendingFnParams,只暴露业务参数。 * * @param scope 静态 scope 字符串,或基于参数动态生成 scope 的函数 * @param fn 接收 PendingFnParams 的回调函数 * @returns 只保留业务参数的包装函数 * * @example * ```ts * const mountScript = pendingFn( * (url: string) => `script:${url}`, * ({ scope, getPendingCount }, url: string) => { * // 短路:已挂载 * const existing = document.getElementById(scope); * if (existing) return { element: existing }; * * // 实际挂载,执行期间 getPendingCount() 可获取等待数 * return loadScript(url); * }, * ); * * // 并发调用,只执行一次挂载 * await Promise.all([mountScript('/lib.js'), mountScript('/lib.js')]); * ``` */ declare const pendingFn: (scope: string | ((...args: Args) => string), fn: PendingCallbackFn) => ((...args: Args) => Promise>); /** * 基于 scope 的 inflight 去重 * * 同一 scope 下并发调用只执行一次 fn,所有 callers 共享同一个 Promise 结果。 * 执行完毕后 scope 释放,下一次调用开启新的执行周期。 * * @param scope 静态 scope 字符串,或基于参数动态生成 scope 的函数 * @param fn 要包装的函数(同步或异步) * @returns 包装后的去重函数 * * @example 静态 scope * ```ts * const fetchConfig = pending('config', () => fetch('/api/config').then(r => r.json())); * * // 并发调用 3 次,只执行 1 次 fetch * const [a, b, c] = await Promise.all([fetchConfig(), fetchConfig(), fetchConfig()]); * // a === b === c * ``` * * @example 动态 scope * ```ts * const fetchUser = pending( * (id: string) => `user:${id}`, * (id: string) => fetch(`/api/user/${id}`).then(r => r.json()), * ); * * // 相同 id 去重,不同 id 独立执行 * await Promise.all([fetchUser('1'), fetchUser('1'), fetchUser('2')]); * // fetchUser('1') 只执行一次,fetchUser('2') 独立执行一次 * ``` */ declare const pending: (scope: string | ((...args: Parameters) => string), fn: F) => ((...args: Parameters) => Promise>>); /** * 异步工具模块 */ type AnyFn = (...args: any[]) => any; /** * 重试回调参数 */ type RetryFnParams = { /** 当前尝试次数(从 1 开始) */ attempt: number; /** 触发重试的错误(首次执行时为 undefined) */ error: unknown; /** 只读的重试选项 */ readonly options: RetryOptions; }; /** * 重试选项 */ type RetryOptions = { /** 最大尝试次数,默认 3 */ attempts?: number; /** 重试间隔(毫秒),支持固定值或基于 RetryFnParams 的动态计算 */ delay?: number | ((params: RetryFnParams) => number); /** 重试前的回调,可用于日志记录 */ onRetry?: (params: RetryFnParams) => void; }; /** * retryFn 的回调函数类型 */ type RetryCallbackFn = (params: RetryFnParams, ...args: Args) => T; /** * 重试耗尽错误 * * 当异步操作在达到最大重试次数后仍然失败时抛出 */ declare class RetryExhaustedError extends Error { readonly attempt: number; readonly error: unknown; readonly options: RetryOptions; constructor(params: RetryFnParams); } /** * 超时错误 * * 当异步操作超过指定时间仍未完成时抛出 */ declare class TimeoutError extends Error { readonly ms: number; /** * @param ms 超时毫秒数 */ constructor(ms: number); } /** * 透明包装异步/同步函数,返回自动重试的版本 * * 返回的函数保持原函数的参数签名,返回值统一为 Promise * * @param fn 要包装的函数(同步或异步) * @param options 重试选项 * @returns 包装后的函数 * * @example * ```ts * const fetchUserWithRetry = retry( * (id: string) => fetch(`/api/user/${id}`).then(r => r.json()), * { attempts: 3, delay: 1000 }, * ); * * const user = await fetchUserWithRetry('123'); * ``` * * @example 指数退避 * ```ts * const fetchWithBackoff = retry(fetchData, { * attempts: 5, * delay: ({ attempt }) => Math.min(1000 * 2 ** (attempt - 1), 30000), * }); * ``` */ declare const retry: (fn: F, options?: RetryOptions) => ((...args: Parameters) => Promise>>); /** * 创建感知重试状态的函数 * * fn 的第一个参数为 RetryFnParams,包含 attempt 和 options, * 允许根据重试次数做差异化处理。返回的函数剥掉 RetryFnParams, * 只暴露业务参数。 * * @param fn 接收 RetryFnParams 的回调函数 * @param options 重试选项 * @returns 只保留业务参数的包装函数 * * @example * ```ts * const fetchWithFallback = retryFn( * ({ attempt }, id: string) => { * const url = attempt === 1 ? '/api/primary' : '/api/fallback'; * return fetch(`${url}/${id}`).then(r => r.json()); * }, * { attempts: 3 }, * ); * * const data = await fetchWithFallback('123'); * ``` */ declare const retryFn: (fn: RetryCallbackFn, options?: RetryOptions) => ((...args: Args) => Promise>); /** * 包装函数,添加超时控制 * * 返回的函数保持原函数的参数签名,返回值统一为 Promise。 * 如果执行超过指定时间,将抛出 TimeoutError。 * * @param fn 要包装的函数(同步或异步) * @param ms 超时毫秒数 * @returns 包装后的函数 * * @example * ```ts * const fetchWithTimeout = timeout( * (url: string) => fetch(url).then(r => r.json()), * 5000, * ); * * const data = await fetchWithTimeout('/api/data'); * ``` */ declare const timeout: (fn: F, ms: number) => ((...args: Parameters) => Promise>>); /** * 延迟指定毫秒数,可选延迟后执行函数 * * @param ms 延迟毫秒数 * @param fn 延迟后执行的函数(可选) * @returns 无 fn 时返回 Promise,有 fn 时返回 Promise>> * * @example * ```ts * // 纯等待 * await sleep(1000); * * // 延迟后执行 * const data = await sleep(1000, () => fetchData()); * ``` */ declare function sleep(ms: number): Promise; declare function sleep(ms: number, fn: F): Promise>>; /** * Type Guards 和基础类型工具模块 */ /** * 判定 val 是否为字符串类型(含 infer) * * @see https://stackoverflow.com/questions/4059147/check-if-a-variable-is-a-string-in-javascript * @param val */ declare const isStr: (val: unknown) => val is string; /** * 检查 val 是否为非空字符串(含 infer) * * @param val */ declare const notEmptyStr: (val: unknown) => val is string; /** * 判断是否为有效的数字类型 * * @param val */ declare const isNumber: (val: unknown) => val is number; /** * 判断是否包含有效的数值 * * - 允许字符串包含数值,如 `"123"` * - 允许数字类型,如 `123` * * @param val */ declare const isNumberVal: (val: unknown) => boolean; /** * 将包含有效数值的 val 转换为对应的数字类型,只支持以下情形: * * - 字符串包含数值,如 `"123"`,转换为 `123` * - 数字类型,如 `123`,转换为 `123` * - 布尔类型,如 `true`,转换为 `1`,`false` 转换为 `0` * * @param val * @param dft 默认值,仅当 val 为 `null` 或 `undefined` 或 非包含有效数值时生效 */ declare const toNumber: (val: unknown, dft?: number) => number; /** * 限制 val 在最小值范围内 * * @param val * @param {number} min 最小值 * @param {number} dft 默认值,仅当 val 为 `null` 或 `undefined` 或 非包含有效数值时生效 */ declare const limitNumberMin: (val: unknown, min: number, dft?: number) => number; /** * 限制 val 在最大值范围内 * @param val * @param max 最大值 * @param dft 默认值,仅当 val 为 `null` 或 `undefined` 或 非包含有效数值时生效 */ declare const limitNumberMax: (val: unknown, max: number, dft?: number) => number; /** * 限制 val 在最小值和最大值范围内 * @param val * @param min 最小值 * @param max 最大值 * @param dft 默认值,仅当 val 为 `null` 或 `undefined` 或 非包含有效数值时生效 */ declare const limitNumberMinMax: (val: unknown, min: number, max: number, dft?: number) => number; /** * 数字精度调整,支持 `round`、`ceil`、`floor` 三种类型 * * @see https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/round#%E5%B0%8F%E6%95%B0%E8%88%8D%E5%85%A5 * @param {"round" | "ceil" | "floor"} type 调整类型 * @param {number} value * @param {number} exp 指数(10的 exp 次方 —— 10 进制位数,0 表示个位,1 表示十位,-1 表示小数点后一位,-2 表示小数点后两位,以此类推)。 * @returns {number} */ declare const decimalAdjust: (type: "round" | "ceil" | "floor", value: number, exp?: number) => number; declare const round10: (value: number, exp?: number) => number; declare const floor10: (value: number, exp?: number) => number; declare const ceil10: (value: number, exp?: number) => number; /** * 计算进度值,返回的结果为一个浮点值,表示进度比例,取值在 0 - 1 之间。 * * @param value * @param total */ declare const calcProgress: (value: number, total: number) => number; type RecordObj = Record; /** * 检查 obj 是否为 Object,结果为真时,推导 obj 为 T 类型 * * - 未指定泛型 T,则 T 默认为 `Record` * - 如果指定泛型 T ,而未传入 fn ,只要 obj 是 Object 即推断 obj 为 T * ```ts * type TestA = { * name?: string; * } * * if (isInferObj(obj)) { * // obj 推断为 TestA * console.log(obj.name || 'noname'); * } * ``` * - 若果传入了 fn ,则先检查是否 Object,再附加 fn 结果进行推断 * ```ts * type TestB = { * name: string; * } * * if (isInferObj(obj, it => typeof it.name === 'string')) { * // obj 推断为 TestB * console.log(obj.name); * } * ``` * - 通过传入 `x is T` 的 fn ,可省略指定泛型 T(一般用于复杂的结构判定) * ```ts * // 由 fn 的结果推导 isInferObj 的 T * type WithVersion = { * version: number; * }; * * const isWithVersion = (it: WithVersion): it is WithVersion => * typeof it.version === 'number'; * * const ver1 = { ver: 1 }; * const ver2 = { version: 2 }; * * if (isInferObj(ver1, isWithVersion)) { * // 不符合 * } * * if (isInferObj(ver2, isWithVersion)) { * // ver2 推断为 Version * ver2.version += 1; * } * ``` * * @param obj 任意类型变量 * @param fn 断言类型判断函数 */ declare const isInferObj: (obj: unknown, fn?: (it: T) => boolean) => obj is T; /** * 可能是一个包含错误消息的结构 */ type ErrLike = { message?: string; error?: string; }; /** * 判断目标是否是 {@link ErrLike} * * @param err */ declare const isErrLike: (err: unknown) => err is ErrLike; /** * 取回错误消息 * * @param err */ declare const errMsg: (err: unknown) => string; /** * 检查值是否为布尔值 * * @param val 任意值 * * @example * ```ts * if (isBool(value)) { * console.log(value ? 'yes' : 'no'); * } * ``` */ declare const isBool: (val: unknown) => val is boolean; /** * 检查值是否为 null * * @param val 任意值 */ declare const isNull: (val: unknown) => val is null; /** * 检查值是否为 undefined * * @param val 任意值 */ declare const isUndefined: (val: unknown) => val is undefined; /** * 检查值是否为 null 或 undefined * * @param val 任意值 */ declare const isNil: (val: unknown) => val is null | undefined; /** * 检查值是否不为 null 且不为 undefined * * @param val 任意值 * * @example * ```ts * if (isPresent(value)) { * console.log(value); * } * ``` */ declare const isPresent: (val: T | null | undefined) => val is T; /** * 检查值是否为一个普通对象(不包括数组、Date、RegExp 等特殊对象) * * @param val 任意值 * * @example * ```ts * if (isPlainObj(value)) { * console.log(Object.keys(value)); * } * ``` */ declare const isPlainObj: = Record>(val: unknown) => val is T; /** * 检查值是否为数组 * * @param val 任意值 */ declare const isAry: (val: unknown) => val is T[]; /** * 检查值是否为非空数组,支持可选的元素类型守卫 * * @param val 任意值 * @param guard 可选的元素类型守卫 * * @example * ```ts * if (notEmptyAry(value)) { * console.log(value[0]); // value is unknown[] * } * * if (notEmptyAry(value, isStr)) { * console.log(value[0].toUpperCase()); // value is string[] * } * ``` */ declare function notEmptyAry(val: unknown): val is T[]; declare function notEmptyAry(val: unknown, guard: (item: unknown) => item is T): val is T[]; /** * 柯里化的数组类型守卫 * * @param guard 元素类型守卫 * @returns 数组类型守卫函数 * * @example * ```ts * const isStrAry = aryGuard(isStr); * * if (isStrAry(value)) { * // value is string[] * } * * // 配合 and 使用 * const isNonEmptyStrAry = and(isStrAry, (arr) => arr.length > 0); * ``` */ declare const aryGuard: (guard: (item: unknown) => item is T) => TypeGuard; /** * 检查值是否为 Promise * * @param val 任意值 * * @example * ```ts * if (isPromise(value)) { * await value; * } * ``` */ declare const isPromise: (val: unknown) => val is Promise; /** * 类型守卫函数类型 */ type TypeGuard = (val: unknown) => val is T; /** * 组合守卫(AND) * * 第一个守卫收窄类型到 T,后续守卫在 T 上做进一步筛选 * * @param guards 第一个为类型守卫,后续为断言函数 * @returns 组合后的守卫函数 * * @example * ```ts * const isStrAry = aryGuard(isStr); * const isNonEmptyStrAry = and(isStrAry, (arr) => arr.length > 0); * ``` */ declare const and: (...guards: [(val: unknown) => val is T, ...((val: T) => boolean)[]]) => TypeGuard; /** * 从 TypeGuard 提取被守卫的类型 */ type InferGuard = G extends TypeGuard ? T : never; /** * 组合守卫(OR) * * 任意一个守卫通过即返回 true,自动推断联合类型 * * @param guards 一个或多个守卫函数 * @returns 组合后的守卫函数 * * @example * ```ts * const isStrOrNum = or(isStr, isNumber); * if (isStrOrNum(value)) { * // value: string | number * } * ``` */ declare const or: , ...TypeGuard[]]>(...guards: G) => TypeGuard>; /** * 守卫取反(NOT) * * 返回运行时取反函数。由于 TS 类型系统不支持否定类型, * 返回值为 `(val: unknown) => boolean`,不作为类型守卫使用。 * * @param guard 要取反的守卫函数 * @returns 取反后的判断函数 * * @example * ```ts * const isNotNull = not(isNull); * if (isNotNull(value)) { * // 运行时正确,但不会收窄类型 * } * ``` */ declare const not: (guard: TypeGuard) => ((val: unknown) => boolean); type DirectorySeparator = '/' | '\\'; type PathInput = string | undefined | null; declare const UnixDS = "/"; declare const WinDS = "\\"; type PathReplacementCallback = (path: string, separator: string) => string; type PathUtilsOptions = { separator: string; dangerReplace?: PathReplacementCallback; duplicateReplace?: PathReplacementCallback; }; /** * 创建路径处理工具 * * @param {PathUtilsOptions} options * @returns */ declare const createPathUtils: ({ separator: inputSeparator, dangerReplace, duplicateReplace, }: PathUtilsOptions) => { purgePath: (path: PathInput) => string; joinPath: (...paths: PathInput[]) => string; }; declare const purgeHttpPath: (path: PathInput) => string; declare const joinHttpPath: (...paths: PathInput[]) => string; /** * 单个 Http Header 的数组类型 * * ```ts * const header: HeaderAry = ['content-type', 'application/json']; * ``` */ type AryHeaderItem = [string, string]; type HeadersInitInput = HeadersInit | undefined | null; /** * 将任意 `HeadersInit` 转为多个 {@link AryHeaderItem} 的数组 * * 浏览器环境或 bun 环境下,`HeadersInit` 类型如下: * * `type HeadersInit = [string, string][] | Record | Headers;` * * 需要注意的是,在使用 `headers.entries()` 时,Header 的 Key 会转为小写: * * ```ts * const h = new Headers(); * h.set('Content-Type', 'application/json'); * console.log([...h.keys()]); // 将输出 => ['content-type'] * ``` * * 所以,当输入的 `headers` 为 `[string, string][]` 或 `Record` 类 * 型时,应该确保 Key 为小写。即: * * ```ts * const h1 = [['content-Type', 'application/json']]; * const h2 = { 'content-Type': 'application/json' }; * ``` * * @param headers * @returns {AryHeaderItem[]} */ declare const toAryHeaders: (headers?: HeadersInitInput) => AryHeaderItem[]; /** * 合并多个 {@link HeadersInitInput} 为 `Headers` 对象 * * Key(Header name) 会进行 trim 操作: * * - Key(Header name) 头尾的空格(制表符、回车符)会被去除 * - 当 Key(Header name) 为空字符(trim 后),将跳过该 Key * * 注意:当输入的 `headers` 为 `[string, string][]` 或 `Record` 类 * 型时,应该确保 Key(Header name) 为小写(以确保同 Key 的 Header 会被有效覆盖)。 * * @param inputs 多个 {@link HeadersInitInput} 对象,Key(Header name) 应为小写(中划线) * @returns {Headers} */ declare const mergeHeaders: (...inputs: HeadersInitInput[]) => Headers; /** * 合并多个 AbortSignals * * ```ts * const maybeSignal: AbortSignal | null = null; * mergeAbortSignals(new AbortController, AbortSignal.timeout(5000), maybeSignal); * // AbortSignal.any([new AbortController, AbortSignal.timeout(5000)]) * ``` * * @param signals * @returns {AbortSignal | undefined} */ declare const mergeAbortSignals: (...signals: (AbortSignal | undefined | null)[]) => AbortSignal | undefined; type HttpStatusCode = number; type ResponseInitInput = ResponseInit | HttpStatusCode | null | undefined; /** * 合并多个 {@link ResponseInitInput} 为 `ResponseInit` 对象 * * 主要针对: * - 允许传入 {@link HttpStatusCode} 作为 `ResponseInit`,即:`new Response(body, mergeRespInit(404))` * - 处理多个 `ResponseInit` 对象的 `headers` 合并 * - 合并多个 `ResponseInit` * * ```ts * new Response(body, * mergeRespInit( * 404, * { headers: { 'x-a': '1' } }, * { headers: { 'x-b': '2', 'x-a': 'replaced' } }, * { status: 502, statusText: 'Bad Gateway' }, * ) * ); * ``` * * @param opts * @returns */ declare const mergeRespInit: (...opts: ResponseInitInput[]) => ResponseInit; declare class MountRemoteError extends Error { readonly prev?: unknown | undefined; constructor(message: string, prev?: unknown | undefined); } type MountRemoteResult = Record> = { type: string; scope: string; } & Result; /** * Handler 上下文,继承 PendingFnParams */ type MountHandlerContext = Record> = PendingFnParams & { type: string; } & Options; /** * DOM 类型 handler 的通用选项 */ type MountDomOptions = { url: string; attrs?: Record; onLoad?: (el: HTMLElement, res: MountRemoteResult) => void | Promise; onError?: (err: unknown, ctx: MountHandlerContext, opts: MountDomOptions) => void; }; /** * DOM handler 的扩展返回字段(不含 MountRemoteResult 的基础字段) */ type MountDomResult = MountDomOptions & { id: string; el: HTMLElement; }; /** * 类型安全的 handler 函数 */ type MountHandlerFn = Record, Result extends Record = Record> = (ctx: MountHandlerContext, opts: Options) => Promise> | MountRemoteResult; /** * Handler 类型映射表 * * 通过 interface 声明合并扩展: * ```ts * declare module '@zenstone/ts-utils/remote' { * interface MountHandlerMap { * wasm: MountHandlerFn<{ url: string; importObject?: WebAssembly.Imports }>; * } * } * ``` */ interface MountHandlerMap { js: MountHandlerFn; css: MountHandlerFn; mjs: MountHandlerFn; } /** * 注册挂载类型处理器 * * @param type 资源类型标识 * @param handler 处理函数 * * @example * ```ts * registerMountHandler('wasm', async (ctx, opts) => { * const response = await fetch(opts.url); * const module = await WebAssembly.instantiateStreaming(response, opts.importObject); * return { type: ctx.type, scope: ctx.scope, url: opts.url, module }; * }); * ``` */ declare const registerMountHandler: (type: K, handler: MountHandlerMap[K]) => void; /** * 创建基于 DOM 元素的挂载处理器 * * 适用于通过 script/link 等标签加载的资源。自动处理: * - `getElementById` 短路检查 * - 创建元素并设置属性 * - 设置 id(使用 scope) * - 监听 load/error 事件 * - 挂载到 document.head * * @param tagName 标签名 * @param setup 元素初始化函数 * * @example * ```ts * registerMountHandler('img', createDomHandler('img', (el, ctx) => { * el.setAttribute('src', ctx.url); * })); * ``` */ declare const createDomHandler: (tagName: string, setup: (el: HTMLElement, ctx: MountHandlerContext) => void) => MountHandlerFn; /** * 挂载远程资源 * * 基于 scope 做 inflight 去重:并发调用同 scope 只执行一次,结果共享。 * Options 类型由 `type` 字段决定,通过 {@link MountHandlerMap} 接口扩展。 * * 内置支持 `js`、`mjs`、`css` 三种类型,其他类型需通过 * {@link registerMountHandler} 注册处理器。 * * @param scope 去重标识,同时用作 DOM 元素 id(对于 DOM handler) * @param options 挂载选项,类型由 `type` 决定 * * @example * ```ts * await mountRemote('jquery', { * type: 'js', * url: 'https://cdn.example.com/jquery.min.js', * }); * ``` * * @example 并发去重 * ```ts * await Promise.all([ * mountRemote('lib', { type: 'js', url }), * mountRemote('lib', { type: 'js', url }), * ]); * // 只加载一次 * ``` */ declare function mountRemote(scope: string, options: { type: K; } & Parameters[1]): Promise; /** * 基于 id 移除已挂载的远程资源 * * @param id 元素 ID * @param onRemove 移除后回调 */ declare const unmountDomRemote: (id: string, onRemove?: () => void) => void; type DownloadUrl = string | URL; type DownloadRequest = { url: DownloadUrl; } & RequestInit; type DownloadInput = DownloadUrl | DownloadRequest | Request | Promise; type DownloadFetchCallback = () => Promise; declare class DownloadTaskError extends Error { readonly task: DownloadTask; constructor(msg: string, task: DownloadTask); } declare enum DownloadTaskState { error = -1, init = 0, fetching = 1, reading = 2, complete = 3 } type DownloadTaskProcessCallback = (task: DownloadTask) => void | Promise; type DownloadTaskProcessOptions = { onFetch?: DownloadTaskProcessCallback; onHeaders?: DownloadTaskProcessCallback; onProgress?: DownloadTaskProcessCallback; onComplete?: DownloadTaskProcessCallback; onError?: DownloadTaskProcessCallback; }; declare class DownloadTask { #private; /** * 创建 {@link DownloadTask} 实例,允许多种 input 类型 * * ```ts * // URL 输入 * const task1 = new DownloadTask('url'); * const task2 = new DownloadTask(new URL('url')); * * // Request or DownloadRequest 结构 * const task3 = new DownloadTask(new Request('url')); * const task4 = new DownloadTask({ url: 'url' }); * * // Promise * const abort = new AbortController(); * const task5 = new DownloadTask(fetch('url', { signal: abort.signal })); * * // Response * const task6 = new DownloadTask(await fetch('url')); * ``` * * 下载的目标 url ,必须要输出有效的 `content-length`,否则将会抛出异常 * * @param input */ constructor(input: DownloadInput | Response); protected _initFetch: (input: DownloadInput) => void; /** * {@link DownloadTask} Id * * 用于给 {@link DownloadTask} 的 Key 使用 */ get id(): string; /** * 获取 response */ get resp(): Response | undefined; /** * 当前状态 {@link DownloadTaskState} */ get state(): DownloadTaskState; /** * 获取 Response Content-Length */ get contentLength(): number; /** * 获取 Response Content-Encoding */ get encoding(): string | null; /** * 获取 Response Content-Type */ get mimeType(): string | null; /** * 实际接收内容的大小 */ get size(): number; /** * Response 是否经过压缩(基于 Content-Encoding 判定) */ get isCompressed(): boolean; /** * 强制指定 compressed ,针对一些特殊的场合使用 * * @param isCompressed */ setCompressed: (isCompressed?: boolean) => this; /** * 已接收 Response body 大小 */ get received(): number; /** * 接收 Response body 进度小数(0 - 1) */ get progress(): number; /** * 接收 Response body 进度百分比(0 - 100) */ get percent(): number; /** * 获取 Response body 的 chunks */ get chunks(): Uint8Array | undefined; /** * 是否开始 read */ get isStarted(): boolean; /** * 是否已经读取(完毕) Response body */ get isReaded(): boolean; /** * 获取错误信息 */ get error(): unknown; /** * read 完成时间戳 * * - 如果下载未开始,返回 0 * - 如果下载已开始,但并未下载完成,则会返回当前时间的时间戳 */ get completeTs(): number; /** * read 数据经过多少时间(毫秒) * * 如果为实际完成,则返回当前时间的时间戳 * * 如果未开始,则返回 0 */ get elapsedMs(): number; /** * 接收速度,单位为字节/秒,需要自行转换 * * 如果未开始,返回 0 * * ```ts * import { filesize } from 'filesize'; * * const task = await fetchDownload().read(); * * console.log(`${filesize(task.speed, { bits: true })}/s`); // kbit/s * console.log(`${filesize(task.speed)}/s`); // KB/s * ``` */ get speed(): number; /** * read response body(下载) * * 主要的流程: * * 1. 如果创建 {@link DownloadTask} 实例时输入的非 Response 实例 {@link DownloadInput} * 会创建一个函数去 fetch 输入(主要是 fetch headers,如果传入的是 Response 实例,则跳过这一步)。 * - `onFetch` 对应 fetch 前 * 2. 解析 `Response.headers`,并提取 `content-length`、`content-type`、`content-encoding`, * 其中 `content-length` 为必须的(缺失或小于或等于0时将抛出错误)。 * - `onHeaders` * 3. `stream.read` 实际开始下载 * - `onProgress` 每一次接收 chunk 流 * - `onComplete` 下载完成 * - `onError` 出错时 * * 默认状态下,任意错误都会抛出异常,可通过 `opts.isNotThrow` 为 `true` 禁用抛出异常。 * * ```ts * const task = new DownloadTask('download_url'); * await task.read({ * onFetch: () => {}, // fetch 前 * onHeaders: () => {}, // 读取 headers 时,stream read 之前 * onProgress: () => {}, // 每一次接收 chunk 流时 * onComplete: () => {}, // 下载完成时 * onError: () => {}, // 出错时 * }); * ``` * * @param opts */ read: (opts?: DownloadTaskProcessCallback | DownloadTaskProcessOptions) => Promise; /** * 根据压缩后文件大小,推算出未压缩前的文件大小 * * 文本内容的压缩率,实际取决于源内容的重复率,所以无法一概而论。 * 这里只是尽可能放大压缩后的尺寸,以取得一个较为接近的值 * * @param compressedSize 压缩后的文件大小 */ inferUncompressedSize: (compressedSize: number) => number; newErr: (msg: string) => DownloadTaskError; } declare class DownloadQueueError extends Error { readonly queue: DownloadQueue; constructor(msg: string, queue: DownloadQueue); } type DownloadQueueInput = DownloadInput | Response | DownloadTask; type DownloadQueueProcessCallback = (queue: DownloadQueue, task: DownloadTask) => void | Promise; type DownloadQueueProcessOptions = { onFetch?: DownloadQueueProcessCallback; onHeaders?: DownloadQueueProcessCallback; onProgress?: DownloadQueueProcessCallback; onComplete?: DownloadQueueProcessCallback; onError?: DownloadQueueProcessCallback; onFinish?: (queue: DownloadQueue) => void | Promise; onQueueError?: (queue: DownloadQueue) => void | Promise; }; declare class DownloadQueue { #private; /** * 创建 {@link DownloadQueue} 实例,允许多种 input 类型 * * ```ts * // DownloadTask * const task1 = new DownloadQueue([new DownloadTask('url')]); * const task2 = new DownloadQueue([fetchDownload('url')]); * * // URL 输入 * const task3 = new DownloadQueue(['url']); * const task4 = new DownloadQueue([new URL('url')]); * * // Request or DownloadRequest 结构 * const task5 = new DownloadQueue([new Request('url')]); * const task6 = new DownloadQueue([{ url: 'url' }]); * * // Promise * const abort = new AbortController(); * const task7 = new DownloadQueue([ * fetch('url', { signal: abort.signal }), * ]); * * // Response * const task8 = new DownloadTask([ * await fetch('url'), * ]); * ``` * * @param tasks */ constructor(tasks: DownloadQueueInput[]); /** * {@link DownloadQueue} Id */ get id(): string; /** * 所有下载任务 */ get tasks(): DownloadTask[]; /** * 所有下载任务的总数 */ get tasksCount(): number; /** * 当前状态 {@link DownloadTaskState} * * 注意 {@link DownloadQueue} 不应该出现 `fetching` 状态 */ get state(): DownloadTaskState; /** * 所有下载任务的实际总大小 */ get size(): number; /** * 所有下载任务的 Content-Length 总和 */ get contentLength(): number; /** * 所有下载任务的已接收 Response body 大小 */ get received(): number; /** * 所有下载任务的接收 Response body 进度小数(0 - 1) */ get progress(): number; /** * 所有下载任务的接收 Response body 进度百分比(0 - 100) */ get percent(): number; /** * 是否开始 read */ get isStarted(): boolean; /** * 是否已经 read 完毕 */ get isReaded(): boolean; /** * 获取错误信息 */ get error(): unknown; /** * read 完成时间戳(所有下载任务都完成) * * - 如果下载未开始,返回 0 * - 如果下载已开始,但并未下载完成,则会返回当前时间的时间戳 */ get completeTs(): number; /** * read 数据经过多少时间(毫秒,所有下载任务都完成) * * 如果为实际完成,则返回当前时间的时间戳 * * 如果未开始,则返回 0 */ get elapsedMs(): number; /** * 接收速度(所有下载任务的平均值),单位为字节/秒,需要自行转换 * * 如果未开始,返回 0 * * ```ts * import { filesize } from 'filesize'; * * const task = await fetchDownload().read(); * * console.log(`${filesize(task.speed, { bits: true })}/s`); // kbit/s * console.log(`${filesize(task.speed)}/s`); // KB/s * ``` */ get speed(): number; protected _initTasksQueue: (opts?: DownloadQueueProcessOptions) => Promise[]; reduce: (fn: (acc: T, it: DownloadTask) => T, initValue: T) => T; /** * Queue 读取方法(调用每一个 task 的 read) * * 1. `onFetch`、`onHeaders`、`onProgress`、`onComplete`、`onError` 保留对应每一个 task 的事件 * 2. `onFinish` 对应 Queue 全部 tasks 读取完毕 * 3. `onQueueError` 对应 Queue 任意一个 task 读取过程中出错 * * @param opts */ read: (opts?: DownloadQueueProcessCallback | DownloadQueueProcessOptions) => Promise; newErr: (msg: string) => DownloadQueueError; } /** * 创建 {@link DownloadTask} 或 {@link DownloadQueue} 的快捷方法 * * ```ts * // DownloadTask * const task = fetchDownload('url'); * * // DownloadQueue * const queue = fetchDownload(['url-a', 'url-b', 'url-c']); * ``` * * @param input */ declare const fetchDownload: (input: T) => R; /** * 将 chunks 保存到本机,该方法只可在浏览器中执行 * * ```ts * const task = fetchDownload('url'); * await task.read(); * * saveChunks(task.chunks, 'test.js'); * ``` * * @param chunks * @param filename * @param mimeType */ declare const saveChunks: (chunks: Uint8Array, filename: string | (() => string), mimeType?: string | null) => void; export { DownloadQueue, DownloadQueueError, DownloadTask, DownloadTaskError, DownloadTaskState, MountRemoteError, PendingScopeConflictError, RetryExhaustedError, TimeoutError, UnixDS, WinDS, and, aryGuard, calcProgress, ceil10, clearPendingRegistry, createDomHandler, createPathUtils, decimalAdjust, errMsg, fetchDownload, floor10, isAry, isBool, isErrLike, isInferObj, isNil, isNull, isNumber, isNumberVal, isPlainObj, isPresent, isPromise, isStr, isUndefined, joinHttpPath, limitNumberMax, limitNumberMin, limitNumberMinMax, mergeAbortSignals, mergeHeaders, mergeRespInit, mountRemote, not, notEmptyAry, notEmptyStr, or, pending, pendingFn, purgeHttpPath, registerMountHandler, retry, retryFn, round10, saveChunks, sleep, timeout, toAryHeaders, toNumber, unmountDomRemote }; export type { AnyFn, AryHeaderItem, DirectorySeparator, DownloadFetchCallback, DownloadInput, DownloadQueueInput, DownloadQueueProcessCallback, DownloadQueueProcessOptions, DownloadRequest, DownloadTaskProcessCallback, DownloadTaskProcessOptions, DownloadUrl, ErrLike, HeadersInitInput, HttpStatusCode, InferGuard, MountDomOptions, MountDomResult, MountHandlerContext, MountHandlerFn, MountHandlerMap, MountRemoteResult, PathInput, PathReplacementCallback, PathUtilsOptions, PendingCallbackFn, PendingFnParams, RecordObj, ResponseInitInput, RetryCallbackFn, RetryFnParams, RetryOptions, TypeGuard };