@huangqi_an/request 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defineAdapter
3
- } from "../chunk-GMJTG6KM.js";
3
+ } from "../chunk-4NADBDEH.js";
4
4
  import {
5
5
  createAbortError
6
6
  } from "../chunk-Z4N22SLG.js";
@@ -1,108 +1,8 @@
1
1
  import {
2
- defineAdapter
3
- } from "../chunk-GMJTG6KM.js";
4
- import {
5
- createAbortError
6
- } from "../chunk-Z4N22SLG.js";
7
-
8
- // src/adapters/fetch.ts
9
- function isPassthroughBody(data) {
10
- return typeof data === "string" || data instanceof Blob || data instanceof ArrayBuffer || data instanceof Uint8Array || data instanceof URLSearchParams || typeof FormData !== "undefined" && data instanceof FormData;
11
- }
12
- function prepareBody(config) {
13
- const method = config.method ?? "GET";
14
- const headers = { ...config.headers };
15
- const data = config.data;
16
- if (data === void 0 || data === null || method === "GET" || method === "HEAD") {
17
- return { headers };
18
- }
19
- if (isPassthroughBody(data)) {
20
- return { body: data, headers };
21
- }
22
- if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
23
- headers["Content-Type"] = "application/json";
24
- }
25
- return { body: JSON.stringify(data), headers };
26
- }
27
- function parseData(text, contentType) {
28
- if (contentType.includes("json") && text !== "") {
29
- try {
30
- return JSON.parse(text);
31
- } catch {
32
- }
33
- }
34
- return text;
35
- }
36
- function concatChunks(chunks, totalLength) {
37
- const merged = new Uint8Array(totalLength);
38
- let offset = 0;
39
- for (const chunk of chunks) {
40
- merged.set(chunk, offset);
41
- offset += chunk.byteLength;
42
- }
43
- return merged;
44
- }
45
- async function readWithProgress(res, onProgress) {
46
- const lengthHeader = Number(res.headers.get("content-length"));
47
- const total = Number.isFinite(lengthHeader) && lengthHeader > 0 ? lengthHeader : void 0;
48
- const reader = res.body.getReader();
49
- const chunks = [];
50
- let loaded = 0;
51
- for (; ; ) {
52
- const { done, value } = await reader.read();
53
- if (done) break;
54
- chunks.push(value);
55
- loaded += value.byteLength;
56
- onProgress({
57
- loaded,
58
- total,
59
- progress: total !== void 0 ? Math.min(loaded / total, 1) : void 0
60
- });
61
- }
62
- return new TextDecoder().decode(concatChunks(chunks, loaded));
63
- }
64
- var fetchAdapter = defineAdapter(
65
- async (config) => {
66
- const { signal, timeout } = config;
67
- if (signal?.aborted) throw createAbortError();
68
- const controller = new AbortController();
69
- let timedOut = false;
70
- let timer;
71
- const onExternalAbort = () => controller.abort();
72
- signal?.addEventListener("abort", onExternalAbort, { once: true });
73
- if (timeout !== void 0 && timeout > 0) {
74
- timer = setTimeout(() => {
75
- timedOut = true;
76
- controller.abort();
77
- }, timeout);
78
- }
79
- try {
80
- const { body, headers } = prepareBody(config);
81
- const { on } = config;
82
- const res = await fetch(config.url, {
83
- // 专有配置先展开(如 credentials / cache / redirect):契约派生字段在后覆盖
84
- ...config.adapterOptions?.fetch,
85
- method: config.method ?? "GET",
86
- headers,
87
- body,
88
- signal: controller.signal
89
- });
90
- const text = on?.downloadProgress && res.body ? await readWithProgress(res, on.downloadProgress) : await res.text();
91
- return {
92
- status: res.status,
93
- data: parseData(text, res.headers.get("content-type") ?? ""),
94
- headers: Object.fromEntries(res.headers.entries())
95
- };
96
- } catch (error) {
97
- if (signal?.aborted) throw createAbortError(error?.message);
98
- if (timedOut) throw new Error(`Request timed out after ${timeout}ms`);
99
- throw error;
100
- } finally {
101
- signal?.removeEventListener("abort", onExternalAbort);
102
- if (timer !== void 0) clearTimeout(timer);
103
- }
104
- }
105
- );
2
+ fetchAdapter
3
+ } from "../chunk-6VCNLK2D.js";
4
+ import "../chunk-4NADBDEH.js";
5
+ import "../chunk-Z4N22SLG.js";
106
6
  export {
107
7
  fetchAdapter
108
8
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adapters/fetch.ts"],"sourcesContent":["import { defineAdapter } from '../core'\nimport { createAbortError } from '../errors'\nimport type { RawResponse, RequestConfig, RequestProgress } from '../types'\n\n/** 判断 data 是否可不经序列化直接作为 fetch 的 body(字符串或内置二进制类型) */\nfunction isPassthroughBody(data: unknown): boolean {\n return (\n typeof data === 'string' ||\n data instanceof Blob ||\n data instanceof ArrayBuffer ||\n data instanceof Uint8Array ||\n data instanceof URLSearchParams ||\n (typeof FormData !== 'undefined' && data instanceof FormData)\n )\n}\n\n/**\n * 组装 body:\n * - GET/HEAD 不应有 body(fetch 对此直接抛错),data 被忽略,query 请用 params\n * - 字符串/内置二进制类型原样发送,不改动 Content-Type\n * - 对象/数组自动 JSON 序列化,未显式指定 Content-Type 时补 application/json(与 axios/uni 行为一致)\n */\nfunction prepareBody(config: RequestConfig): {\n body?: BodyInit\n headers: Record<string, string>\n} {\n const method = config.method ?? 'GET'\n const headers = { ...config.headers }\n const data = config.data\n\n if (data === undefined || data === null || method === 'GET' || method === 'HEAD') {\n return { headers }\n }\n if (isPassthroughBody(data)) {\n return { body: data as BodyInit, headers }\n }\n if (!Object.keys(headers).some((key) => key.toLowerCase() === 'content-type')) {\n headers['Content-Type'] = 'application/json'\n }\n return { body: JSON.stringify(data), headers }\n}\n\n/** 响应解析:content-type 含 json 时自动 parse,解析失败保留原文(交由业务处理) */\nfunction parseData(text: string, contentType: string): unknown {\n if (contentType.includes('json') && text !== '') {\n try {\n return JSON.parse(text)\n } catch {\n // 保留原文\n }\n }\n return text\n}\n\n/** 拼接分块字节后再整体解码:避免多字节 UTF-8 字符被块边界截断 */\nfunction concatChunks(chunks: Uint8Array[], totalLength: number): Uint8Array {\n const merged = new Uint8Array(totalLength)\n let offset = 0\n for (const chunk of chunks) {\n merged.set(chunk, offset)\n offset += chunk.byteLength\n }\n return merged\n}\n\n/**\n * 下载进度:走 res.body 流式读取并逐块上报。\n * Content-Length 缺失(chunked 等)时 total/progress 为 undefined。\n */\nasync function readWithProgress(\n res: Response,\n onProgress: (progress: RequestProgress) => void,\n): Promise<string> {\n const lengthHeader = Number(res.headers.get('content-length'))\n const total = Number.isFinite(lengthHeader) && lengthHeader > 0 ? lengthHeader : undefined\n const reader = res.body!.getReader()\n const chunks: Uint8Array[] = []\n let loaded = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n chunks.push(value)\n loaded += value.byteLength\n onProgress({\n loaded,\n total,\n progress: total !== undefined ? Math.min(loaded / total, 1) : undefined,\n })\n }\n return new TextDecoder().decode(concatChunks(chunks, loaded))\n}\n\n/**\n * Web/Node/SSR adapter 插件:基于全局 fetch(零依赖,Node 18+ / 现代浏览器)。\n * 经 defineAdapter 导出:可直接使用,也可 .use() 叠加装饰器。\n * - 任何状态码都 resolve,判断统一交给 core\n * - fetch 无内建 timeout,用 AbortController 桥接 signal 与超时;\n * 超时抛普通错误(core 归类 kind='network'),与 axios/uni 的超时归类一致\n */\nexport const fetchAdapter = defineAdapter(\n async (config: RequestConfig): Promise<RawResponse> => {\n const { signal, timeout } = config\n // 与其他 adapter 一致:发出前已取消直接终止\n if (signal?.aborted) throw createAbortError()\n\n const controller = new AbortController()\n let timedOut = false\n let timer: ReturnType<typeof setTimeout> | undefined\n const onExternalAbort = () => controller.abort()\n signal?.addEventListener('abort', onExternalAbort, { once: true })\n if (timeout !== undefined && timeout > 0) {\n timer = setTimeout(() => {\n timedOut = true\n controller.abort()\n }, timeout)\n }\n\n try {\n const { body, headers } = prepareBody(config)\n const { on } = config\n const res = await fetch(config.url, {\n // 专有配置先展开(如 credentials / cache / redirect):契约派生字段在后覆盖\n ...(config.adapterOptions?.fetch as RequestInit | undefined),\n method: config.method ?? 'GET',\n headers,\n body,\n signal: controller.signal,\n })\n // fetch 无上传进度(平台限制);下载进度走流式读取\n const text =\n on?.downloadProgress && res.body\n ? await readWithProgress(res, on.downloadProgress)\n : await res.text()\n return {\n status: res.status,\n data: parseData(text, res.headers.get('content-type') ?? ''),\n headers: Object.fromEntries(res.headers.entries()),\n }\n } catch (error) {\n // 用户取消 → 统一 AbortError;桥接超时 → 普通错误(交 core 归类 network)\n if (signal?.aborted) throw createAbortError((error as Error)?.message)\n if (timedOut) throw new Error(`Request timed out after ${timeout}ms`)\n throw error\n } finally {\n signal?.removeEventListener('abort', onExternalAbort)\n if (timer !== undefined) clearTimeout(timer)\n }\n },\n)\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defineAdapter
3
- } from "../chunk-GMJTG6KM.js";
3
+ } from "../chunk-4NADBDEH.js";
4
4
  import {
5
5
  createAbortError
6
6
  } from "../chunk-Z4N22SLG.js";
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-Z4N22SLG.js";
6
6
 
7
7
  // src/core.ts
8
- var ABSOLUTE_URL_RE = /^https?:\/\//;
8
+ var ABSOLUTE_URL_RE = /^(https?:)?\/\//;
9
9
  function buildUrl(baseURL, url, params) {
10
10
  let full = url;
11
11
  if (baseURL && !ABSOLUTE_URL_RE.test(url)) {
@@ -153,4 +153,4 @@ export {
153
153
  defineAdapter,
154
154
  createRequest
155
155
  };
156
- //# sourceMappingURL=chunk-GMJTG6KM.js.map
156
+ //# sourceMappingURL=chunk-4NADBDEH.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core.ts"],"sourcesContent":["import { createAbortError, isAbortError, RequestError } from './errors'\nimport type {\n AdapterEnhancer,\n ChainableAdapter,\n ErrorInterceptor,\n RawResponse,\n RequestAdapter,\n RequestConfig,\n RequestInterceptor,\n RequestMethod,\n ResponseInterceptor,\n} from './types'\n\nexport interface CreateRequestOptions {\n /** 必传:adapter 插件(axiosAdapter / fetchAdapter / uniappAdapter / 自定义 / 测试 mock) */\n adapter: RequestAdapter\n baseURL?: string\n headers?: Record<string, string>\n timeout?: number\n}\n\nexport interface RequestInstance {\n request<T = unknown>(config: RequestConfig): Promise<RawResponse<T>>\n get<T = unknown>(\n url: string,\n config?: Omit<RequestConfig, 'url' | 'method'>,\n ): Promise<RawResponse<T>>\n post<T = unknown>(\n url: string,\n data?: unknown,\n config?: Omit<RequestConfig, 'url' | 'method' | 'data'>,\n ): Promise<RawResponse<T>>\n put<T = unknown>(\n url: string,\n data?: unknown,\n config?: Omit<RequestConfig, 'url' | 'method' | 'data'>,\n ): Promise<RawResponse<T>>\n patch<T = unknown>(\n url: string,\n data?: unknown,\n config?: Omit<RequestConfig, 'url' | 'method' | 'data'>,\n ): Promise<RawResponse<T>>\n delete<T = unknown>(\n url: string,\n config?: Omit<RequestConfig, 'url' | 'method'>,\n ): Promise<RawResponse<T>>\n interceptors: {\n request: { use(interceptor: RequestInterceptor): void }\n /** 成功链:转换 / 剥壳 */\n response: { use(interceptor: ResponseInterceptor): void }\n /**\n * 错误链:所有失败都会经过(请求拦截器抛错 / 网络失败 / 非 2xx / 响应链抛错)。\n * 返回 RawResponse = 恢复;返回/抛出 RequestError = 替换;无返回值 = 放行(观察)。\n */\n error: { use(interceptor: ErrorInterceptor): void }\n }\n}\n\nconst ABSOLUTE_URL_RE = /^https?:\\/\\//\n\n/**\n * 拼接 baseURL + url,并把 params 序列化为 query。\n * - 绝对 URL 忽略 baseURL\n * - 跳过 null / undefined;数组值序列化为重复 key(?id=1&id=2)\n * - url 已带 ? 时用 & 追加\n * params 统一在这里处理,保证两端一致,adapter 无需各自实现。\n */\nexport function buildUrl(\n baseURL: string | undefined,\n url: string,\n params?: Record<string, unknown>,\n): string {\n let full = url\n if (baseURL && !ABSOLUTE_URL_RE.test(url)) {\n full = `${baseURL.replace(/\\/+$/, '')}/${url.replace(/^\\/+/, '')}`\n }\n if (!params) return full\n\n const pairs = Object.entries(params)\n .filter(([, value]) => value !== undefined && value !== null)\n .flatMap(([key, value]) =>\n Array.isArray(value)\n ? value.map((item) => [key, item] as const)\n : [[key, value] as const],\n )\n .map(\n ([key, value]) =>\n `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`,\n )\n if (pairs.length === 0) return full\n\n return full + (full.includes('?') ? '&' : '?') + pairs.join('&')\n}\n\nfunction isOkStatus(status: number): boolean {\n return status >= 200 && status < 300\n}\n\n/** 高精度计时;performance 不存在的运行时(部分小程序环境)退化为 Date.now */\nfunction now(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now()\n}\n\n/** 判断错误拦截器的返回值是否为恢复(RawResponse 形状) */\nfunction isRawResponseLike(value: unknown): value is RawResponse {\n return (\n typeof value === 'object' && value !== null && 'status' in value && 'data' in value\n )\n}\n\n/** 把任意抛出的错误归一化为 RequestError(已是则透传) */\nfunction toRequestError(error: unknown, config?: RequestConfig): RequestError {\n if (error instanceof RequestError) return error\n if (isAbortError(error)) {\n return new RequestError('abort', (error as Error).message, {\n config,\n raw: error,\n })\n }\n const message = error instanceof Error ? error.message : String(error)\n return new RequestError('network', message || 'Network request failed', {\n config,\n raw: error,\n })\n}\n\n/**\n * adapter 的显式扩展点:把任意(含第三方)adapter 变为可链式增强的 adapter,\n * 同时为自定义 adapter 的实现函数提供参数类型推断。\n *\n * const myAdapter = defineAdapter(async (config) => {\n * // config.url 已是 core 拼好 params 的完整地址\n * // 返回 RawResponse;取消时抛 name='AbortError' 的错误\n * }).use(withRetry()).use(withDedupe()) // 注册顺序 = 请求流经顺序\n *\n * 平台特有方法(如 upload/downloadFile)用 .extend 附加:\n * .extend((request) => ({ upload: (file) => ... }))\n * 工厂收到\"组合后的适配器\",附加方法可路由完整管道,并在后续 .use 链上保持不丢。\n *\n * 返回值本身仍是合法的 RequestAdapter,可直接传给 createRequest。\n */\nexport function defineAdapter(adapter: RequestAdapter): ChainableAdapter {\n const chain = <T extends object>(\n enhancers: AdapterEnhancer[],\n extras: T,\n ): ChainableAdapter<T> => {\n let composed: RequestAdapter | undefined\n const getComposed = (): RequestAdapter => {\n // 惰性组合并缓存:装饰器工厂只在首次请求时执行一次,.use 不触发重建\n if (composed === undefined) {\n composed = enhancers.reduceRight((acc, enhance) => enhance(acc), adapter)\n }\n return composed\n }\n\n const wrapped = ((config: RequestConfig) =>\n getComposed()(config)) as ChainableAdapter<T>\n wrapped.use = (enhancer) => chain([...enhancers, enhancer], extras)\n wrapped.extend = (<U extends object>(\n factory: (composed: RequestAdapter) => U,\n ) => {\n // extras 工厂以\"当前已组合的适配器\"执行一次,附加方法天然带上此前注册的装饰器\n const added = factory((config) => getComposed()(config))\n return chain([...enhancers], { ...extras, ...added })\n }) as ChainableAdapter<T>['extend']\n for (const [key, value] of Object.entries(extras)) {\n ;(wrapped as unknown as Record<string, unknown>)[key] = value\n }\n return wrapped\n }\n return chain([], {})\n}\n\nexport function createRequest(options: CreateRequestOptions): RequestInstance {\n const requestInterceptors: RequestInterceptor[] = []\n const responseInterceptors: ResponseInterceptor[] = []\n const errorInterceptors: ErrorInterceptor[] = []\n\n async function request<T = unknown>(\n input: RequestConfig,\n ): Promise<RawResponse<T>> {\n // 1. 合并实例默认值,请求级配置优先\n let config: RequestConfig = {\n ...input,\n method: (input.method ?? 'GET') as RequestMethod,\n baseURL: input.baseURL ?? options.baseURL,\n timeout: input.timeout ?? options.timeout,\n headers: { ...options.headers, ...input.headers },\n }\n\n try {\n // 2. 请求拦截器,按注册顺序执行\n for (const interceptor of requestInterceptors) {\n config = await interceptor(config)\n }\n\n // 3. params 统一在 core 拼进 url,并回写 config——错误链与响应链拿到的都是完整地址\n config = { ...config, url: buildUrl(config.baseURL, config.url, config.params) }\n\n // 4. dispatch 前检查取消,避免\"已取消还发请求\"的竞态\n if (config.signal?.aborted) throw createAbortError()\n\n // 5. 调 adapter,任何失败在此归一化;耗时与最终 config 由 core 统一注入响应\n const startedAt = now()\n let res: RawResponse\n try {\n res = await options.adapter({ ...config, params: undefined })\n } catch (error) {\n throw toRequestError(error, config)\n }\n res.durationMs = Math.round(now() - startedAt)\n res.config = { ...config }\n\n // 6. 状态码判断统一在 core:非 2xx → http 错误(两端行为一致的关键)\n if (!isOkStatus(res.status)) {\n throw new RequestError('http', `Request failed with status ${res.status}`, {\n status: res.status,\n data: res.data,\n headers: res.headers,\n config,\n })\n }\n\n // 7. 响应拦截器(成功链,按注册顺序执行)\n for (const onFulfilled of responseInterceptors) {\n res = await onFulfilled(res)\n }\n return res as RawResponse<T>\n } catch (error) {\n // 8. 统一错误出口:错误链按注册顺序执行\n // 返回 RawResponse = 恢复;返回/抛出 RequestError = 替换;无返回值 = 放行(观察)\n let current = toRequestError(error, config)\n for (const onError of errorInterceptors) {\n let out: RawResponse | RequestError | void\n try {\n out = await onError(current)\n } catch (thrown) {\n current = toRequestError(thrown, config)\n continue\n }\n if (isRawResponseLike(out)) return out as RawResponse<T>\n if (out instanceof RequestError) current = out\n }\n throw current\n }\n }\n\n return {\n request,\n get: (url, config) => request({ ...config, url, method: 'GET' }),\n post: (url, data, config) => request({ ...config, url, data, method: 'POST' }),\n put: (url, data, config) => request({ ...config, url, data, method: 'PUT' }),\n patch: (url, data, config) => request({ ...config, url, data, method: 'PATCH' }),\n delete: (url, config) => request({ ...config, url, method: 'DELETE' }),\n interceptors: {\n request: {\n use: (interceptor) => {\n requestInterceptors.push(interceptor)\n },\n },\n response: {\n use: (interceptor) => {\n responseInterceptors.push(interceptor)\n },\n },\n error: {\n use: (interceptor) => {\n errorInterceptors.push(interceptor)\n },\n },\n },\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/core.ts"],"sourcesContent":["import { createAbortError, isAbortError, RequestError } from './errors'\nimport type {\n AdapterEnhancer,\n ChainableAdapter,\n ErrorInterceptor,\n RawResponse,\n RequestAdapter,\n RequestConfig,\n RequestInterceptor,\n RequestMethod,\n ResponseInterceptor,\n} from './types'\n\nexport interface CreateRequestOptions {\n /** 必传:adapter 插件(axiosAdapter / fetchAdapter / uniappAdapter / 自定义 / 测试 mock) */\n adapter: RequestAdapter\n baseURL?: string\n headers?: Record<string, string>\n timeout?: number\n}\n\nexport interface RequestInstance {\n request<T = unknown>(config: RequestConfig): Promise<RawResponse<T>>\n get<T = unknown>(\n url: string,\n config?: Omit<RequestConfig, 'url' | 'method'>,\n ): Promise<RawResponse<T>>\n post<T = unknown>(\n url: string,\n data?: unknown,\n config?: Omit<RequestConfig, 'url' | 'method' | 'data'>,\n ): Promise<RawResponse<T>>\n put<T = unknown>(\n url: string,\n data?: unknown,\n config?: Omit<RequestConfig, 'url' | 'method' | 'data'>,\n ): Promise<RawResponse<T>>\n patch<T = unknown>(\n url: string,\n data?: unknown,\n config?: Omit<RequestConfig, 'url' | 'method' | 'data'>,\n ): Promise<RawResponse<T>>\n delete<T = unknown>(\n url: string,\n config?: Omit<RequestConfig, 'url' | 'method'>,\n ): Promise<RawResponse<T>>\n interceptors: {\n request: { use(interceptor: RequestInterceptor): void }\n /** 成功链:转换 / 剥壳 */\n response: { use(interceptor: ResponseInterceptor): void }\n /**\n * 错误链:所有失败都会经过(请求拦截器抛错 / 网络失败 / 非 2xx / 响应链抛错)。\n * 返回 RawResponse = 恢复;返回/抛出 RequestError = 替换;无返回值 = 放行(观察)。\n */\n error: { use(interceptor: ErrorInterceptor): void }\n }\n}\n\n// 绝对地址:http(s):// 与协议相对的 //host/path\nconst ABSOLUTE_URL_RE = /^(https?:)?\\/\\//\n\n/**\n * 拼接 baseURL + url,并把 params 序列化为 query。\n * - 绝对 URL 忽略 baseURL\n * - 跳过 null / undefined;数组值序列化为重复 key(?id=1&id=2)\n * - url 已带 ? 时用 & 追加\n * params 统一在这里处理,保证两端一致,adapter 无需各自实现。\n */\nexport function buildUrl(\n baseURL: string | undefined,\n url: string,\n params?: Record<string, unknown>,\n): string {\n let full = url\n if (baseURL && !ABSOLUTE_URL_RE.test(url)) {\n full = `${baseURL.replace(/\\/+$/, '')}/${url.replace(/^\\/+/, '')}`\n }\n if (!params) return full\n\n const pairs = Object.entries(params)\n .filter(([, value]) => value !== undefined && value !== null)\n .flatMap(([key, value]) =>\n Array.isArray(value)\n ? value.map((item) => [key, item] as const)\n : [[key, value] as const],\n )\n .map(\n ([key, value]) =>\n `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`,\n )\n if (pairs.length === 0) return full\n\n return full + (full.includes('?') ? '&' : '?') + pairs.join('&')\n}\n\nfunction isOkStatus(status: number): boolean {\n return status >= 200 && status < 300\n}\n\n/** 高精度计时;performance 不存在的运行时(部分小程序环境)退化为 Date.now */\nfunction now(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now()\n}\n\n/** 判断错误拦截器的返回值是否为恢复(RawResponse 形状) */\nfunction isRawResponseLike(value: unknown): value is RawResponse {\n return (\n typeof value === 'object' && value !== null && 'status' in value && 'data' in value\n )\n}\n\n/** 把任意抛出的错误归一化为 RequestError(已是则透传) */\nfunction toRequestError(error: unknown, config?: RequestConfig): RequestError {\n if (error instanceof RequestError) return error\n if (isAbortError(error)) {\n return new RequestError('abort', (error as Error).message, {\n config,\n raw: error,\n })\n }\n const message = error instanceof Error ? error.message : String(error)\n return new RequestError('network', message || 'Network request failed', {\n config,\n raw: error,\n })\n}\n\n/**\n * adapter 的显式扩展点:把任意(含第三方)adapter 变为可链式增强的 adapter,\n * 同时为自定义 adapter 的实现函数提供参数类型推断。\n *\n * const myAdapter = defineAdapter(async (config) => {\n * // config.url 已是 core 拼好 params 的完整地址\n * // 返回 RawResponse;取消时抛 name='AbortError' 的错误\n * }).use(withRetry()).use(withDedupe()) // 注册顺序 = 请求流经顺序\n *\n * 平台特有方法(如 upload/downloadFile)用 .extend 附加:\n * .extend((request) => ({ upload: (file) => ... }))\n * 工厂收到\"组合后的适配器\",附加方法可路由完整管道,并在后续 .use 链上保持不丢。\n *\n * 返回值本身仍是合法的 RequestAdapter,可直接传给 createRequest。\n */\nexport function defineAdapter(adapter: RequestAdapter): ChainableAdapter {\n const chain = <T extends object>(\n enhancers: AdapterEnhancer[],\n extras: T,\n ): ChainableAdapter<T> => {\n let composed: RequestAdapter | undefined\n const getComposed = (): RequestAdapter => {\n // 惰性组合并缓存:装饰器工厂只在首次请求时执行一次,.use 不触发重建\n if (composed === undefined) {\n composed = enhancers.reduceRight((acc, enhance) => enhance(acc), adapter)\n }\n return composed\n }\n\n const wrapped = ((config: RequestConfig) =>\n getComposed()(config)) as ChainableAdapter<T>\n wrapped.use = (enhancer) => chain([...enhancers, enhancer], extras)\n wrapped.extend = (<U extends object>(\n factory: (composed: RequestAdapter) => U,\n ) => {\n // extras 工厂以\"当前已组合的适配器\"执行一次,附加方法天然带上此前注册的装饰器\n const added = factory((config) => getComposed()(config))\n return chain([...enhancers], { ...extras, ...added })\n }) as ChainableAdapter<T>['extend']\n for (const [key, value] of Object.entries(extras)) {\n ;(wrapped as unknown as Record<string, unknown>)[key] = value\n }\n return wrapped\n }\n return chain([], {})\n}\n\nexport function createRequest(options: CreateRequestOptions): RequestInstance {\n const requestInterceptors: RequestInterceptor[] = []\n const responseInterceptors: ResponseInterceptor[] = []\n const errorInterceptors: ErrorInterceptor[] = []\n\n async function request<T = unknown>(\n input: RequestConfig,\n ): Promise<RawResponse<T>> {\n // 1. 合并实例默认值,请求级配置优先\n let config: RequestConfig = {\n ...input,\n method: (input.method ?? 'GET') as RequestMethod,\n baseURL: input.baseURL ?? options.baseURL,\n timeout: input.timeout ?? options.timeout,\n headers: { ...options.headers, ...input.headers },\n }\n\n try {\n // 2. 请求拦截器,按注册顺序执行\n for (const interceptor of requestInterceptors) {\n config = await interceptor(config)\n }\n\n // 3. params 统一在 core 拼进 url,并回写 config——错误链与响应链拿到的都是完整地址\n config = { ...config, url: buildUrl(config.baseURL, config.url, config.params) }\n\n // 4. dispatch 前检查取消,避免\"已取消还发请求\"的竞态\n if (config.signal?.aborted) throw createAbortError()\n\n // 5. 调 adapter,任何失败在此归一化;耗时与最终 config 由 core 统一注入响应\n const startedAt = now()\n let res: RawResponse\n try {\n res = await options.adapter({ ...config, params: undefined })\n } catch (error) {\n throw toRequestError(error, config)\n }\n res.durationMs = Math.round(now() - startedAt)\n res.config = { ...config }\n\n // 6. 状态码判断统一在 core:非 2xx → http 错误(两端行为一致的关键)\n if (!isOkStatus(res.status)) {\n throw new RequestError('http', `Request failed with status ${res.status}`, {\n status: res.status,\n data: res.data,\n headers: res.headers,\n config,\n })\n }\n\n // 7. 响应拦截器(成功链,按注册顺序执行)\n for (const onFulfilled of responseInterceptors) {\n res = await onFulfilled(res)\n }\n return res as RawResponse<T>\n } catch (error) {\n // 8. 统一错误出口:错误链按注册顺序执行\n // 返回 RawResponse = 恢复;返回/抛出 RequestError = 替换;无返回值 = 放行(观察)\n let current = toRequestError(error, config)\n for (const onError of errorInterceptors) {\n let out: RawResponse | RequestError | void\n try {\n out = await onError(current)\n } catch (thrown) {\n current = toRequestError(thrown, config)\n continue\n }\n if (isRawResponseLike(out)) return out as RawResponse<T>\n if (out instanceof RequestError) current = out\n }\n throw current\n }\n }\n\n return {\n request,\n get: (url, config) => request({ ...config, url, method: 'GET' }),\n post: (url, data, config) => request({ ...config, url, data, method: 'POST' }),\n put: (url, data, config) => request({ ...config, url, data, method: 'PUT' }),\n patch: (url, data, config) => request({ ...config, url, data, method: 'PATCH' }),\n delete: (url, config) => request({ ...config, url, method: 'DELETE' }),\n interceptors: {\n request: {\n use: (interceptor) => {\n requestInterceptors.push(interceptor)\n },\n },\n response: {\n use: (interceptor) => {\n responseInterceptors.push(interceptor)\n },\n },\n error: {\n use: (interceptor) => {\n errorInterceptors.push(interceptor)\n },\n },\n },\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,110 @@
1
+ import {
2
+ defineAdapter
3
+ } from "./chunk-4NADBDEH.js";
4
+ import {
5
+ createAbortError
6
+ } from "./chunk-Z4N22SLG.js";
7
+
8
+ // src/adapters/fetch.ts
9
+ function isPassthroughBody(data) {
10
+ return typeof data === "string" || data instanceof Blob || data instanceof ArrayBuffer || data instanceof Uint8Array || data instanceof URLSearchParams || typeof FormData !== "undefined" && data instanceof FormData;
11
+ }
12
+ function prepareBody(config) {
13
+ const method = config.method ?? "GET";
14
+ const headers = { ...config.headers };
15
+ const data = config.data;
16
+ if (data === void 0 || data === null || method === "GET" || method === "HEAD") {
17
+ return { headers };
18
+ }
19
+ if (isPassthroughBody(data)) {
20
+ return { body: data, headers };
21
+ }
22
+ if (!Object.keys(headers).some((key) => key.toLowerCase() === "content-type")) {
23
+ headers["Content-Type"] = "application/json";
24
+ }
25
+ return { body: JSON.stringify(data), headers };
26
+ }
27
+ function parseData(text, contentType) {
28
+ if (contentType.includes("json") && text !== "") {
29
+ try {
30
+ return JSON.parse(text);
31
+ } catch {
32
+ }
33
+ }
34
+ return text;
35
+ }
36
+ function concatChunks(chunks, totalLength) {
37
+ const merged = new Uint8Array(totalLength);
38
+ let offset = 0;
39
+ for (const chunk of chunks) {
40
+ merged.set(chunk, offset);
41
+ offset += chunk.byteLength;
42
+ }
43
+ return merged;
44
+ }
45
+ async function readWithProgress(res, onProgress) {
46
+ const lengthHeader = Number(res.headers.get("content-length"));
47
+ const total = Number.isFinite(lengthHeader) && lengthHeader > 0 ? lengthHeader : void 0;
48
+ const reader = res.body.getReader();
49
+ const chunks = [];
50
+ let loaded = 0;
51
+ for (; ; ) {
52
+ const { done, value } = await reader.read();
53
+ if (done) break;
54
+ chunks.push(value);
55
+ loaded += value.byteLength;
56
+ onProgress({
57
+ loaded,
58
+ total,
59
+ progress: total !== void 0 ? Math.min(loaded / total, 1) : void 0
60
+ });
61
+ }
62
+ return new TextDecoder().decode(concatChunks(chunks, loaded));
63
+ }
64
+ var fetchAdapter = defineAdapter(
65
+ async (config) => {
66
+ const { signal, timeout } = config;
67
+ if (signal?.aborted) throw createAbortError();
68
+ const controller = new AbortController();
69
+ let timedOut = false;
70
+ let timer;
71
+ const onExternalAbort = () => controller.abort();
72
+ signal?.addEventListener("abort", onExternalAbort, { once: true });
73
+ if (timeout !== void 0 && timeout > 0) {
74
+ timer = setTimeout(() => {
75
+ timedOut = true;
76
+ controller.abort();
77
+ }, timeout);
78
+ }
79
+ try {
80
+ const { body, headers } = prepareBody(config);
81
+ const { on } = config;
82
+ const res = await fetch(config.url, {
83
+ // 专有配置先展开(如 credentials / cache / redirect):契约派生字段在后覆盖
84
+ ...config.adapterOptions?.fetch,
85
+ method: config.method ?? "GET",
86
+ headers,
87
+ body,
88
+ signal: controller.signal
89
+ });
90
+ const text = on?.downloadProgress && res.body ? await readWithProgress(res, on.downloadProgress) : await res.text();
91
+ return {
92
+ status: res.status,
93
+ data: parseData(text, res.headers.get("content-type") ?? ""),
94
+ headers: Object.fromEntries(res.headers.entries())
95
+ };
96
+ } catch (error) {
97
+ if (signal?.aborted) throw createAbortError(error?.message);
98
+ if (timedOut) throw new Error(`Request timed out after ${timeout}ms`);
99
+ throw error;
100
+ } finally {
101
+ signal?.removeEventListener("abort", onExternalAbort);
102
+ if (timer !== void 0) clearTimeout(timer);
103
+ }
104
+ }
105
+ );
106
+
107
+ export {
108
+ fetchAdapter
109
+ };
110
+ //# sourceMappingURL=chunk-6VCNLK2D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/fetch.ts"],"sourcesContent":["import { defineAdapter } from '../core'\nimport { createAbortError } from '../errors'\nimport type { RawResponse, RequestConfig, RequestProgress } from '../types'\n\n/** 判断 data 是否可不经序列化直接作为 fetch 的 body(字符串或内置二进制类型) */\nfunction isPassthroughBody(data: unknown): boolean {\n return (\n typeof data === 'string' ||\n data instanceof Blob ||\n data instanceof ArrayBuffer ||\n data instanceof Uint8Array ||\n data instanceof URLSearchParams ||\n (typeof FormData !== 'undefined' && data instanceof FormData)\n )\n}\n\n/**\n * 组装 body:\n * - GET/HEAD 不应有 body(fetch 对此直接抛错),data 被忽略,query 请用 params\n * - 字符串/内置二进制类型原样发送,不改动 Content-Type\n * - 对象/数组自动 JSON 序列化,未显式指定 Content-Type 时补 application/json(与 axios/uni 行为一致)\n */\nfunction prepareBody(config: RequestConfig): {\n body?: BodyInit\n headers: Record<string, string>\n} {\n const method = config.method ?? 'GET'\n const headers = { ...config.headers }\n const data = config.data\n\n if (data === undefined || data === null || method === 'GET' || method === 'HEAD') {\n return { headers }\n }\n if (isPassthroughBody(data)) {\n return { body: data as BodyInit, headers }\n }\n if (!Object.keys(headers).some((key) => key.toLowerCase() === 'content-type')) {\n headers['Content-Type'] = 'application/json'\n }\n return { body: JSON.stringify(data), headers }\n}\n\n/** 响应解析:content-type 含 json 时自动 parse,解析失败保留原文(交由业务处理) */\nfunction parseData(text: string, contentType: string): unknown {\n if (contentType.includes('json') && text !== '') {\n try {\n return JSON.parse(text)\n } catch {\n // 保留原文\n }\n }\n return text\n}\n\n/** 拼接分块字节后再整体解码:避免多字节 UTF-8 字符被块边界截断 */\nfunction concatChunks(chunks: Uint8Array[], totalLength: number): Uint8Array {\n const merged = new Uint8Array(totalLength)\n let offset = 0\n for (const chunk of chunks) {\n merged.set(chunk, offset)\n offset += chunk.byteLength\n }\n return merged\n}\n\n/**\n * 下载进度:走 res.body 流式读取并逐块上报。\n * Content-Length 缺失(chunked 等)时 total/progress 为 undefined。\n */\nasync function readWithProgress(\n res: Response,\n onProgress: (progress: RequestProgress) => void,\n): Promise<string> {\n const lengthHeader = Number(res.headers.get('content-length'))\n const total = Number.isFinite(lengthHeader) && lengthHeader > 0 ? lengthHeader : undefined\n const reader = res.body!.getReader()\n const chunks: Uint8Array[] = []\n let loaded = 0\n for (;;) {\n const { done, value } = await reader.read()\n if (done) break\n chunks.push(value)\n loaded += value.byteLength\n onProgress({\n loaded,\n total,\n progress: total !== undefined ? Math.min(loaded / total, 1) : undefined,\n })\n }\n return new TextDecoder().decode(concatChunks(chunks, loaded))\n}\n\n/**\n * Web/Node/SSR adapter 插件:基于全局 fetch(零依赖,Node 18+ / 现代浏览器)。\n * 经 defineAdapter 导出:可直接使用,也可 .use() 叠加装饰器。\n * - 任何状态码都 resolve,判断统一交给 core\n * - fetch 无内建 timeout,用 AbortController 桥接 signal 与超时;\n * 超时抛普通错误(core 归类 kind='network'),与 axios/uni 的超时归类一致\n */\nexport const fetchAdapter = defineAdapter(\n async (config: RequestConfig): Promise<RawResponse> => {\n const { signal, timeout } = config\n // 与其他 adapter 一致:发出前已取消直接终止\n if (signal?.aborted) throw createAbortError()\n\n const controller = new AbortController()\n let timedOut = false\n let timer: ReturnType<typeof setTimeout> | undefined\n const onExternalAbort = () => controller.abort()\n signal?.addEventListener('abort', onExternalAbort, { once: true })\n if (timeout !== undefined && timeout > 0) {\n timer = setTimeout(() => {\n timedOut = true\n controller.abort()\n }, timeout)\n }\n\n try {\n const { body, headers } = prepareBody(config)\n const { on } = config\n const res = await fetch(config.url, {\n // 专有配置先展开(如 credentials / cache / redirect):契约派生字段在后覆盖\n ...(config.adapterOptions?.fetch as RequestInit | undefined),\n method: config.method ?? 'GET',\n headers,\n body,\n signal: controller.signal,\n })\n // fetch 无上传进度(平台限制);下载进度走流式读取\n const text =\n on?.downloadProgress && res.body\n ? await readWithProgress(res, on.downloadProgress)\n : await res.text()\n return {\n status: res.status,\n data: parseData(text, res.headers.get('content-type') ?? ''),\n headers: Object.fromEntries(res.headers.entries()),\n }\n } catch (error) {\n // 用户取消 → 统一 AbortError;桥接超时 → 普通错误(交 core 归类 network)\n if (signal?.aborted) throw createAbortError((error as Error)?.message)\n if (timedOut) throw new Error(`Request timed out after ${timeout}ms`)\n throw error\n } finally {\n signal?.removeEventListener('abort', onExternalAbort)\n if (timer !== undefined) clearTimeout(timer)\n }\n },\n)\n"],"mappings":"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","names":[]}
@@ -0,0 +1,38 @@
1
+ // src/enhancers/dedupe.ts
2
+ var SAFE_METHODS = ["GET", "HEAD"];
3
+ function defaultKey(config) {
4
+ let dataKey = "";
5
+ if (config.data !== void 0) {
6
+ try {
7
+ dataKey = JSON.stringify(config.data);
8
+ } catch {
9
+ dataKey = `${Math.random()}`;
10
+ }
11
+ }
12
+ return `${config.method ?? "GET"} ${config.url} ${dataKey}`;
13
+ }
14
+ function withDedupe(first, maybeOptions) {
15
+ const options = first !== void 0 && typeof first !== "function" ? first : maybeOptions ?? {};
16
+ const decorate = (adapter) => {
17
+ const inFlight = /* @__PURE__ */ new Map();
18
+ const methods = options.methods ?? SAFE_METHODS;
19
+ const keyOf = options.key ?? defaultKey;
20
+ return (config) => {
21
+ if (!methods.includes(config.method ?? "GET")) return adapter(config);
22
+ const key = keyOf(config);
23
+ const pending = inFlight.get(key);
24
+ if (pending) return pending;
25
+ const promise = adapter(config).finally(() => {
26
+ inFlight.delete(key);
27
+ });
28
+ inFlight.set(key, promise);
29
+ return promise;
30
+ };
31
+ };
32
+ return typeof first === "function" ? decorate(first) : decorate;
33
+ }
34
+
35
+ export {
36
+ withDedupe
37
+ };
38
+ //# sourceMappingURL=chunk-HCW5TODA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/enhancers/dedupe.ts"],"sourcesContent":["import type { AdapterEnhancer, RawResponse, RequestAdapter, RequestConfig } from '../types'\n\n/**\n * 默认只去重幂等的安全方法。\n * 写方法(POST/PUT/DELETE)默认放行:去重的 share 语义不会\"多发\",但会\"少发\"——\n * 对故意并发的非幂等请求(如计数 +1)静默合并等于丢操作。\n */\nconst SAFE_METHODS = ['GET', 'HEAD']\n\nfunction defaultKey(config: RequestConfig): string {\n let dataKey = ''\n if (config.data !== undefined) {\n try {\n dataKey = JSON.stringify(config.data)\n } catch {\n // 无法稳定序列化的 data 不参与去重:宁可漏去重,不可错合并\n dataKey = `${Math.random()}`\n }\n }\n return `${config.method ?? 'GET'} ${config.url} ${dataKey}`\n}\n\nexport interface DedupeOptions {\n /**\n * 参与去重的方法,默认 ['GET', 'HEAD'](幂等)。\n * 防双击提交等场景显式传入(如 methods: ['POST']),此时相同载荷的并发提交共享同一结果。\n */\n methods?: string[]\n /**\n * 去重键,默认 `${method} ${url} ${JSON.stringify(data)}`。\n * params 已拼进 url,无需重复纳入;data 键序不同会被视为不同请求(宁可漏去重,不可错合并)。\n */\n key?: (config: RequestConfig) => string\n}\n\n/**\n * adapter 装饰器:进行中的相同请求共享同一个 Promise。\n * - 默认只对幂等方法(GET/HEAD)生效,写方法默认放行\n * - 请求 settle 后自动清理,不影响后续新请求\n * - 共享的是同一个 RawResponse 对象;响应链拦截器用 {...res} 展开产生新对象,不会互相污染\n *\n * 两种形态:\n * - withDedupe(adapter, options):直接包装\n * - withDedupe(options):返回装饰器,交给 adapter.use() 组合\n */\nexport function withDedupe(options?: DedupeOptions): AdapterEnhancer\nexport function withDedupe(adapter: RequestAdapter, options?: DedupeOptions): RequestAdapter\nexport function withDedupe(\n first?: RequestAdapter | DedupeOptions,\n maybeOptions?: DedupeOptions,\n): RequestAdapter | AdapterEnhancer {\n const options: DedupeOptions =\n first !== undefined && typeof first !== 'function' ? first : (maybeOptions ?? {})\n\n const decorate = (adapter: RequestAdapter): RequestAdapter => {\n const inFlight = new Map<string, Promise<RawResponse>>()\n const methods = options.methods ?? SAFE_METHODS\n const keyOf = options.key ?? defaultKey\n\n return (config) => {\n // 非幂等方法直接放行,不参与去重\n if (!methods.includes(config.method ?? 'GET')) return adapter(config)\n\n const key = keyOf(config)\n const pending = inFlight.get(key)\n if (pending) return pending\n\n const promise = adapter(config).finally(() => {\n inFlight.delete(key)\n })\n inFlight.set(key, promise)\n return promise\n }\n }\n\n return typeof first === 'function' ? decorate(first) : decorate\n}\n"],"mappings":";AAOA,IAAM,eAAe,CAAC,OAAO,MAAM;AAEnC,SAAS,WAAW,QAA+B;AACjD,MAAI,UAAU;AACd,MAAI,OAAO,SAAS,QAAW;AAC7B,QAAI;AACF,gBAAU,KAAK,UAAU,OAAO,IAAI;AAAA,IACtC,QAAQ;AAEN,gBAAU,GAAG,KAAK,OAAO,CAAC;AAAA,IAC5B;AAAA,EACF;AACA,SAAO,GAAG,OAAO,UAAU,KAAK,IAAI,OAAO,GAAG,IAAI,OAAO;AAC3D;AA2BO,SAAS,WACd,OACA,cACkC;AAClC,QAAM,UACJ,UAAU,UAAa,OAAO,UAAU,aAAa,QAAS,gBAAgB,CAAC;AAEjF,QAAM,WAAW,CAAC,YAA4C;AAC5D,UAAM,WAAW,oBAAI,IAAkC;AACvD,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,QAAQ,QAAQ,OAAO;AAE7B,WAAO,CAAC,WAAW;AAEjB,UAAI,CAAC,QAAQ,SAAS,OAAO,UAAU,KAAK,EAAG,QAAO,QAAQ,MAAM;AAEpE,YAAM,MAAM,MAAM,MAAM;AACxB,YAAM,UAAU,SAAS,IAAI,GAAG;AAChC,UAAI,QAAS,QAAO;AAEpB,YAAM,UAAU,QAAQ,MAAM,EAAE,QAAQ,MAAM;AAC5C,iBAAS,OAAO,GAAG;AAAA,MACrB,CAAC;AACD,eAAS,IAAI,KAAK,OAAO;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,OAAO,UAAU,aAAa,SAAS,KAAK,IAAI;AACzD;","names":[]}
@@ -0,0 +1,48 @@
1
+ import {
2
+ isAbortError
3
+ } from "./chunk-Z4N22SLG.js";
4
+
5
+ // src/enhancers/retry.ts
6
+ var SAFE_METHODS = ["GET", "HEAD"];
7
+ function withRetry(first, maybeOptions) {
8
+ const options = first !== void 0 && typeof first !== "function" ? first : maybeOptions ?? {};
9
+ const decorate = (adapter) => {
10
+ const { retries = 2, shouldRetry, delayMs } = options;
11
+ const methods = options.methods ?? SAFE_METHODS;
12
+ const defaultShouldRetry = (failure, _attempt, config) => failure.error !== void 0 && methods.includes(config.method ?? "GET");
13
+ const decideRetry = shouldRetry ?? defaultShouldRetry;
14
+ return async (config) => {
15
+ let attempt = 0;
16
+ for (; ; ) {
17
+ let failure;
18
+ try {
19
+ const res = await adapter(config);
20
+ if (res.status >= 200 && res.status < 300) return res;
21
+ failure = { response: res };
22
+ } catch (error) {
23
+ if (isAbortError(error)) throw error;
24
+ failure = { error };
25
+ }
26
+ const giveUp = () => {
27
+ if (failure.error !== void 0) throw failure.error;
28
+ return failure.response;
29
+ };
30
+ if (attempt >= retries) return giveUp();
31
+ if (!decideRetry(failure, attempt + 1, config)) {
32
+ return giveUp();
33
+ }
34
+ attempt++;
35
+ const delay = delayMs?.(attempt);
36
+ if (delay && delay > 0) {
37
+ await new Promise((resolve) => setTimeout(resolve, delay));
38
+ }
39
+ }
40
+ };
41
+ };
42
+ return typeof first === "function" ? decorate(first) : decorate;
43
+ }
44
+
45
+ export {
46
+ withRetry
47
+ };
48
+ //# sourceMappingURL=chunk-RFWI3BY6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/enhancers/retry.ts"],"sourcesContent":["import { isAbortError } from '../errors'\nimport type { AdapterEnhancer, RawResponse, RequestAdapter, RequestConfig } from '../types'\n\n/**\n * 默认只重试幂等的安全方法。\n * 写方法(POST/PUT/DELETE)默认不重试:网络错误可能是\"请求已到达服务端但响应丢失\",\n * 自动重发会重复执行非幂等操作(如重复扣款)。\n */\nconst SAFE_METHODS = ['GET', 'HEAD']\n\nexport interface RetryFailure {\n /** adapter 抛出的错误(取消为 name='AbortError') */\n error?: unknown\n /** adapter 返回的非 2xx 响应 */\n response?: RawResponse\n}\n\nexport interface RetryOptions {\n /** 最大重试次数(不含首次),默认 2 */\n retries?: number\n /**\n * 默认重试规则覆盖的方法,默认 ['GET', 'HEAD'](幂等)。\n * 只约束默认规则;提供 shouldRetry 后完全由 shouldRetry 决定。\n */\n methods?: string[]\n /**\n * 自定义是否重试,提供后完全接管默认规则(methods 不再生效)。\n * 默认:抛错且方法幂等 → 重试;非 2xx 响应 / 取消 → 不重试。\n * 需要对写方法重试时自行判断(如请求已带幂等键 Idempotency-Key)。\n */\n shouldRetry?: (\n failure: RetryFailure,\n attempt: number,\n config: RequestConfig,\n ) => boolean\n /** 第 attempt 次重试前的等待毫秒,默认不等待 */\n delayMs?: (attempt: number) => number\n}\n\n/**\n * adapter 装饰器:失败自动重试(不引 core,可与任何 adapter 组合)。\n * 与错误链恢复不同:重试成功的响应会正常走完整 core 管道(响应链剥壳等)。\n *\n * 两种形态:\n * - withRetry(adapter, options):直接包装\n * - withRetry(options):返回装饰器,交给 adapter.use() 组合\n */\nexport function withRetry(options?: RetryOptions): AdapterEnhancer\nexport function withRetry(adapter: RequestAdapter, options?: RetryOptions): RequestAdapter\nexport function withRetry(\n first?: RequestAdapter | RetryOptions,\n maybeOptions?: RetryOptions,\n): RequestAdapter | AdapterEnhancer {\n const options: RetryOptions =\n first !== undefined && typeof first !== 'function' ? first : (maybeOptions ?? {})\n\n const decorate = (adapter: RequestAdapter): RequestAdapter => {\n const { retries = 2, shouldRetry, delayMs } = options\n const methods = options.methods ?? SAFE_METHODS\n const defaultShouldRetry = (failure: RetryFailure, _attempt: number, config: RequestConfig) =>\n failure.error !== undefined && methods.includes(config.method ?? 'GET')\n const decideRetry: (\n failure: RetryFailure,\n attempt: number,\n config: RequestConfig,\n ) => boolean = shouldRetry ?? defaultShouldRetry\n\n return async (config) => {\n let attempt = 0\n for (;;) {\n let failure: RetryFailure\n try {\n const res = await adapter(config)\n if (res.status >= 200 && res.status < 300) return res\n failure = { response: res }\n } catch (error) {\n // 取消语义永不重试\n if (isAbortError(error)) throw error\n failure = { error }\n }\n\n const giveUp = () => {\n if (failure.error !== undefined) throw failure.error\n return failure.response as RawResponse\n }\n if (attempt >= retries) return giveUp()\n if (!decideRetry(failure, attempt + 1, config)) {\n return giveUp()\n }\n\n attempt++\n const delay = delayMs?.(attempt)\n if (delay && delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n }\n }\n\n return typeof first === 'function' ? decorate(first) : decorate\n}\n"],"mappings":";;;;;AAQA,IAAM,eAAe,CAAC,OAAO,MAAM;AAyC5B,SAAS,UACd,OACA,cACkC;AAClC,QAAM,UACJ,UAAU,UAAa,OAAO,UAAU,aAAa,QAAS,gBAAgB,CAAC;AAEjF,QAAM,WAAW,CAAC,YAA4C;AAC5D,UAAM,EAAE,UAAU,GAAG,aAAa,QAAQ,IAAI;AAC9C,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,qBAAqB,CAAC,SAAuB,UAAkB,WACnE,QAAQ,UAAU,UAAa,QAAQ,SAAS,OAAO,UAAU,KAAK;AACxE,UAAM,cAIS,eAAe;AAE9B,WAAO,OAAO,WAAW;AACvB,UAAI,UAAU;AACd,iBAAS;AACP,YAAI;AACJ,YAAI;AACF,gBAAM,MAAM,MAAM,QAAQ,MAAM;AAChC,cAAI,IAAI,UAAU,OAAO,IAAI,SAAS,IAAK,QAAO;AAClD,oBAAU,EAAE,UAAU,IAAI;AAAA,QAC5B,SAAS,OAAO;AAEd,cAAI,aAAa,KAAK,EAAG,OAAM;AAC/B,oBAAU,EAAE,MAAM;AAAA,QACpB;AAEA,cAAM,SAAS,MAAM;AACnB,cAAI,QAAQ,UAAU,OAAW,OAAM,QAAQ;AAC/C,iBAAO,QAAQ;AAAA,QACjB;AACA,YAAI,WAAW,QAAS,QAAO,OAAO;AACtC,YAAI,CAAC,YAAY,SAAS,UAAU,GAAG,MAAM,GAAG;AAC9C,iBAAO,OAAO;AAAA,QAChB;AAEA;AACA,cAAM,QAAQ,UAAU,OAAO;AAC/B,YAAI,SAAS,QAAQ,GAAG;AACtB,gBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,OAAO,UAAU,aAAa,SAAS,KAAK,IAAI;AACzD;","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=chunk-Y45YDTWD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/core.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  buildUrl,
3
3
  createRequest,
4
4
  defineAdapter
5
- } from "./chunk-GMJTG6KM.js";
5
+ } from "./chunk-4NADBDEH.js";
6
6
  import "./chunk-Z4N22SLG.js";
7
7
  export {
8
8
  buildUrl,
@@ -1,36 +1,6 @@
1
- // src/enhancers/dedupe.ts
2
- var SAFE_METHODS = ["GET", "HEAD"];
3
- function defaultKey(config) {
4
- let dataKey = "";
5
- if (config.data !== void 0) {
6
- try {
7
- dataKey = JSON.stringify(config.data);
8
- } catch {
9
- dataKey = `${Math.random()}`;
10
- }
11
- }
12
- return `${config.method ?? "GET"} ${config.url} ${dataKey}`;
13
- }
14
- function withDedupe(first, maybeOptions) {
15
- const options = first !== void 0 && typeof first !== "function" ? first : maybeOptions ?? {};
16
- const decorate = (adapter) => {
17
- const inFlight = /* @__PURE__ */ new Map();
18
- const methods = options.methods ?? SAFE_METHODS;
19
- const keyOf = options.key ?? defaultKey;
20
- return (config) => {
21
- if (!methods.includes(config.method ?? "GET")) return adapter(config);
22
- const key = keyOf(config);
23
- const pending = inFlight.get(key);
24
- if (pending) return pending;
25
- const promise = adapter(config).finally(() => {
26
- inFlight.delete(key);
27
- });
28
- inFlight.set(key, promise);
29
- return promise;
30
- };
31
- };
32
- return typeof first === "function" ? decorate(first) : decorate;
33
- }
1
+ import {
2
+ withDedupe
3
+ } from "../chunk-HCW5TODA.js";
34
4
  export {
35
5
  withDedupe
36
6
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/enhancers/dedupe.ts"],"sourcesContent":["import type { AdapterEnhancer, RawResponse, RequestAdapter, RequestConfig } from '../types'\n\n/**\n * 默认只去重幂等的安全方法。\n * 写方法(POST/PUT/DELETE)默认放行:去重的 share 语义不会\"多发\",但会\"少发\"——\n * 对故意并发的非幂等请求(如计数 +1)静默合并等于丢操作。\n */\nconst SAFE_METHODS = ['GET', 'HEAD']\n\nfunction defaultKey(config: RequestConfig): string {\n let dataKey = ''\n if (config.data !== undefined) {\n try {\n dataKey = JSON.stringify(config.data)\n } catch {\n // 无法稳定序列化的 data 不参与去重:宁可漏去重,不可错合并\n dataKey = `${Math.random()}`\n }\n }\n return `${config.method ?? 'GET'} ${config.url} ${dataKey}`\n}\n\nexport interface DedupeOptions {\n /**\n * 参与去重的方法,默认 ['GET', 'HEAD'](幂等)。\n * 防双击提交等场景显式传入(如 methods: ['POST']),此时相同载荷的并发提交共享同一结果。\n */\n methods?: string[]\n /**\n * 去重键,默认 `${method} ${url} ${JSON.stringify(data)}`。\n * params 已拼进 url,无需重复纳入;data 键序不同会被视为不同请求(宁可漏去重,不可错合并)。\n */\n key?: (config: RequestConfig) => string\n}\n\n/**\n * adapter 装饰器:进行中的相同请求共享同一个 Promise。\n * - 默认只对幂等方法(GET/HEAD)生效,写方法默认放行\n * - 请求 settle 后自动清理,不影响后续新请求\n * - 共享的是同一个 RawResponse 对象;响应链拦截器用 {...res} 展开产生新对象,不会互相污染\n *\n * 两种形态:\n * - withDedupe(adapter, options):直接包装\n * - withDedupe(options):返回装饰器,交给 adapter.use() 组合\n */\nexport function withDedupe(options?: DedupeOptions): AdapterEnhancer\nexport function withDedupe(adapter: RequestAdapter, options?: DedupeOptions): RequestAdapter\nexport function withDedupe(\n first?: RequestAdapter | DedupeOptions,\n maybeOptions?: DedupeOptions,\n): RequestAdapter | AdapterEnhancer {\n const options: DedupeOptions =\n first !== undefined && typeof first !== 'function' ? first : (maybeOptions ?? {})\n\n const decorate = (adapter: RequestAdapter): RequestAdapter => {\n const inFlight = new Map<string, Promise<RawResponse>>()\n const methods = options.methods ?? SAFE_METHODS\n const keyOf = options.key ?? defaultKey\n\n return (config) => {\n // 非幂等方法直接放行,不参与去重\n if (!methods.includes(config.method ?? 'GET')) return adapter(config)\n\n const key = keyOf(config)\n const pending = inFlight.get(key)\n if (pending) return pending\n\n const promise = adapter(config).finally(() => {\n inFlight.delete(key)\n })\n inFlight.set(key, promise)\n return promise\n }\n }\n\n return typeof first === 'function' ? decorate(first) : decorate\n}\n"],"mappings":";AAOA,IAAM,eAAe,CAAC,OAAO,MAAM;AAEnC,SAAS,WAAW,QAA+B;AACjD,MAAI,UAAU;AACd,MAAI,OAAO,SAAS,QAAW;AAC7B,QAAI;AACF,gBAAU,KAAK,UAAU,OAAO,IAAI;AAAA,IACtC,QAAQ;AAEN,gBAAU,GAAG,KAAK,OAAO,CAAC;AAAA,IAC5B;AAAA,EACF;AACA,SAAO,GAAG,OAAO,UAAU,KAAK,IAAI,OAAO,GAAG,IAAI,OAAO;AAC3D;AA2BO,SAAS,WACd,OACA,cACkC;AAClC,QAAM,UACJ,UAAU,UAAa,OAAO,UAAU,aAAa,QAAS,gBAAgB,CAAC;AAEjF,QAAM,WAAW,CAAC,YAA4C;AAC5D,UAAM,WAAW,oBAAI,IAAkC;AACvD,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,QAAQ,QAAQ,OAAO;AAE7B,WAAO,CAAC,WAAW;AAEjB,UAAI,CAAC,QAAQ,SAAS,OAAO,UAAU,KAAK,EAAG,QAAO,QAAQ,MAAM;AAEpE,YAAM,MAAM,MAAM,MAAM;AACxB,YAAM,UAAU,SAAS,IAAI,GAAG;AAChC,UAAI,QAAS,QAAO;AAEpB,YAAM,UAAU,QAAQ,MAAM,EAAE,QAAQ,MAAM;AAC5C,iBAAS,OAAO,GAAG;AAAA,MACrB,CAAC;AACD,eAAS,IAAI,KAAK,OAAO;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO,OAAO,UAAU,aAAa,SAAS,KAAK,IAAI;AACzD;","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,46 +1,7 @@
1
1
  import {
2
- isAbortError
3
- } from "../chunk-Z4N22SLG.js";
4
-
5
- // src/enhancers/retry.ts
6
- var SAFE_METHODS = ["GET", "HEAD"];
7
- function withRetry(first, maybeOptions) {
8
- const options = first !== void 0 && typeof first !== "function" ? first : maybeOptions ?? {};
9
- const decorate = (adapter) => {
10
- const { retries = 2, shouldRetry, delayMs } = options;
11
- const methods = options.methods ?? SAFE_METHODS;
12
- const defaultShouldRetry = (failure, _attempt, config) => failure.error !== void 0 && methods.includes(config.method ?? "GET");
13
- const decideRetry = shouldRetry ?? defaultShouldRetry;
14
- return async (config) => {
15
- let attempt = 0;
16
- for (; ; ) {
17
- let failure;
18
- try {
19
- const res = await adapter(config);
20
- if (res.status >= 200 && res.status < 300) return res;
21
- failure = { response: res };
22
- } catch (error) {
23
- if (isAbortError(error)) throw error;
24
- failure = { error };
25
- }
26
- const giveUp = () => {
27
- if (failure.error !== void 0) throw failure.error;
28
- return failure.response;
29
- };
30
- if (attempt >= retries) return giveUp();
31
- if (!decideRetry(failure, attempt + 1, config)) {
32
- return giveUp();
33
- }
34
- attempt++;
35
- const delay = delayMs?.(attempt);
36
- if (delay && delay > 0) {
37
- await new Promise((resolve) => setTimeout(resolve, delay));
38
- }
39
- }
40
- };
41
- };
42
- return typeof first === "function" ? decorate(first) : decorate;
43
- }
2
+ withRetry
3
+ } from "../chunk-RFWI3BY6.js";
4
+ import "../chunk-Z4N22SLG.js";
44
5
  export {
45
6
  withRetry
46
7
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/enhancers/retry.ts"],"sourcesContent":["import { isAbortError } from '../errors'\nimport type { AdapterEnhancer, RawResponse, RequestAdapter, RequestConfig } from '../types'\n\n/**\n * 默认只重试幂等的安全方法。\n * 写方法(POST/PUT/DELETE)默认不重试:网络错误可能是\"请求已到达服务端但响应丢失\",\n * 自动重发会重复执行非幂等操作(如重复扣款)。\n */\nconst SAFE_METHODS = ['GET', 'HEAD']\n\nexport interface RetryFailure {\n /** adapter 抛出的错误(取消为 name='AbortError') */\n error?: unknown\n /** adapter 返回的非 2xx 响应 */\n response?: RawResponse\n}\n\nexport interface RetryOptions {\n /** 最大重试次数(不含首次),默认 2 */\n retries?: number\n /**\n * 默认重试规则覆盖的方法,默认 ['GET', 'HEAD'](幂等)。\n * 只约束默认规则;提供 shouldRetry 后完全由 shouldRetry 决定。\n */\n methods?: string[]\n /**\n * 自定义是否重试,提供后完全接管默认规则(methods 不再生效)。\n * 默认:抛错且方法幂等 → 重试;非 2xx 响应 / 取消 → 不重试。\n * 需要对写方法重试时自行判断(如请求已带幂等键 Idempotency-Key)。\n */\n shouldRetry?: (\n failure: RetryFailure,\n attempt: number,\n config: RequestConfig,\n ) => boolean\n /** 第 attempt 次重试前的等待毫秒,默认不等待 */\n delayMs?: (attempt: number) => number\n}\n\n/**\n * adapter 装饰器:失败自动重试(不引 core,可与任何 adapter 组合)。\n * 与错误链恢复不同:重试成功的响应会正常走完整 core 管道(响应链剥壳等)。\n *\n * 两种形态:\n * - withRetry(adapter, options):直接包装\n * - withRetry(options):返回装饰器,交给 adapter.use() 组合\n */\nexport function withRetry(options?: RetryOptions): AdapterEnhancer\nexport function withRetry(adapter: RequestAdapter, options?: RetryOptions): RequestAdapter\nexport function withRetry(\n first?: RequestAdapter | RetryOptions,\n maybeOptions?: RetryOptions,\n): RequestAdapter | AdapterEnhancer {\n const options: RetryOptions =\n first !== undefined && typeof first !== 'function' ? first : (maybeOptions ?? {})\n\n const decorate = (adapter: RequestAdapter): RequestAdapter => {\n const { retries = 2, shouldRetry, delayMs } = options\n const methods = options.methods ?? SAFE_METHODS\n const defaultShouldRetry = (failure: RetryFailure, _attempt: number, config: RequestConfig) =>\n failure.error !== undefined && methods.includes(config.method ?? 'GET')\n const decideRetry: (\n failure: RetryFailure,\n attempt: number,\n config: RequestConfig,\n ) => boolean = shouldRetry ?? defaultShouldRetry\n\n return async (config) => {\n let attempt = 0\n for (;;) {\n let failure: RetryFailure\n try {\n const res = await adapter(config)\n if (res.status >= 200 && res.status < 300) return res\n failure = { response: res }\n } catch (error) {\n // 取消语义永不重试\n if (isAbortError(error)) throw error\n failure = { error }\n }\n\n const giveUp = () => {\n if (failure.error !== undefined) throw failure.error\n return failure.response as RawResponse\n }\n if (attempt >= retries) return giveUp()\n if (!decideRetry(failure, attempt + 1, config)) {\n return giveUp()\n }\n\n attempt++\n const delay = delayMs?.(attempt)\n if (delay && delay > 0) {\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n }\n }\n\n return typeof first === 'function' ? decorate(first) : decorate\n}\n"],"mappings":";;;;;AAQA,IAAM,eAAe,CAAC,OAAO,MAAM;AAyC5B,SAAS,UACd,OACA,cACkC;AAClC,QAAM,UACJ,UAAU,UAAa,OAAO,UAAU,aAAa,QAAS,gBAAgB,CAAC;AAEjF,QAAM,WAAW,CAAC,YAA4C;AAC5D,UAAM,EAAE,UAAU,GAAG,aAAa,QAAQ,IAAI;AAC9C,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,qBAAqB,CAAC,SAAuB,UAAkB,WACnE,QAAQ,UAAU,UAAa,QAAQ,SAAS,OAAO,UAAU,KAAK;AACxE,UAAM,cAIS,eAAe;AAE9B,WAAO,OAAO,WAAW;AACvB,UAAI,UAAU;AACd,iBAAS;AACP,YAAI;AACJ,YAAI;AACF,gBAAM,MAAM,MAAM,QAAQ,MAAM;AAChC,cAAI,IAAI,UAAU,OAAO,IAAI,SAAS,IAAK,QAAO;AAClD,oBAAU,EAAE,UAAU,IAAI;AAAA,QAC5B,SAAS,OAAO;AAEd,cAAI,aAAa,KAAK,EAAG,OAAM;AAC/B,oBAAU,EAAE,MAAM;AAAA,QACpB;AAEA,cAAM,SAAS,MAAM;AACnB,cAAI,QAAQ,UAAU,OAAW,OAAM,QAAQ;AAC/C,iBAAO,QAAQ;AAAA,QACjB;AACA,YAAI,WAAW,QAAS,QAAO,OAAO;AACtC,YAAI,CAAC,YAAY,SAAS,UAAU,GAAG,MAAM,GAAG;AAC9C,iBAAO,OAAO;AAAA,QAChB;AAEA;AACA,cAAM,QAAQ,UAAU,OAAO;AAC/B,YAAI,SAAS,QAAQ,GAAG;AACtB,gBAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,KAAK,CAAC;AAAA,QAC3D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,OAAO,UAAU,aAAa,SAAS,KAAK,IAAI;AACzD;","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/errors.d.ts CHANGED
@@ -1 +1 @@
1
- export { f as RequestError, g as RequestErrorKind, h as RequestErrorOptions, l as createAbortError, m as isAbortError } from './types-DSmcNU2O.js';
1
+ export { d as RequestError, e as RequestErrorKind, f as RequestErrorOptions, l as createAbortError, m as isAbortError } from './types-DLO2d3bV.js';
package/dist/index.d.ts CHANGED
@@ -1,2 +1,9 @@
1
- export { A as AdapterEnhancer, e as AdapterOptions, C as ChainableAdapter, E as ErrorInterceptor, b as RawResponse, a as RequestAdapter, R as RequestConfig, f as RequestError, g as RequestErrorKind, h as RequestErrorOptions, i as RequestEvents, c as RequestInterceptor, j as RequestMethod, k as RequestProgress, d as ResponseInterceptor, l as createAbortError, m as isAbortError } from './types-DSmcNU2O.js';
1
+ export { A as AdapterEnhancer, a as AdapterOptions, C as ChainableAdapter, E as ErrorInterceptor, R as RawResponse, b as RequestAdapter, c as RequestConfig, d as RequestError, e as RequestErrorKind, f as RequestErrorOptions, g as RequestEvents, h as RequestInterceptor, i as RequestMethod, j as RequestProgress, k as ResponseInterceptor, l as createAbortError, m as isAbortError } from './types-DLO2d3bV.js';
2
2
  export { CreateRequestOptions, RequestInstance, buildUrl, createRequest, defineAdapter } from './core.js';
3
+ export { fetchAdapter } from './adapters/fetch.js';
4
+ export { AuthInterceptorOptions, createAuthInterceptor } from './interceptors/auth.js';
5
+ export { UnwrapInterceptorOptions, createUnwrapInterceptor } from './interceptors/unwrap.js';
6
+ export { LogFn, LogLevel, LoggerInterceptorsOptions, createLoggerInterceptors } from './interceptors/logger.js';
7
+ export { RequestIdInterceptorOptions, createRequestIdInterceptor } from './interceptors/request-id.js';
8
+ export { withRetry } from './enhancers/retry.js';
9
+ export { withDedupe } from './enhancers/dedupe.js';