@huangqi_an/request 0.1.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.
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-CoJSp-ar.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';
package/dist/index.js CHANGED
@@ -1,8 +1,30 @@
1
+ import "./chunk-Y45YDTWD.js";
2
+ import {
3
+ createAuthInterceptor
4
+ } from "./chunk-FY6YU3PV.js";
5
+ import {
6
+ createLoggerInterceptors
7
+ } from "./chunk-KLAI63DX.js";
8
+ import {
9
+ createRequestIdInterceptor
10
+ } from "./chunk-UCTOFBDY.js";
11
+ import {
12
+ createUnwrapInterceptor
13
+ } from "./chunk-OV6LXQTN.js";
14
+ import {
15
+ fetchAdapter
16
+ } from "./chunk-6VCNLK2D.js";
1
17
  import {
2
18
  buildUrl,
3
19
  createRequest,
4
20
  defineAdapter
5
- } from "./chunk-DQI4SM2Q.js";
21
+ } from "./chunk-4NADBDEH.js";
22
+ import {
23
+ withDedupe
24
+ } from "./chunk-HCW5TODA.js";
25
+ import {
26
+ withRetry
27
+ } from "./chunk-RFWI3BY6.js";
6
28
  import {
7
29
  RequestError,
8
30
  createAbortError,
@@ -12,8 +34,15 @@ export {
12
34
  RequestError,
13
35
  buildUrl,
14
36
  createAbortError,
37
+ createAuthInterceptor,
38
+ createLoggerInterceptors,
15
39
  createRequest,
40
+ createRequestIdInterceptor,
41
+ createUnwrapInterceptor,
16
42
  defineAdapter,
17
- isAbortError
43
+ fetchAdapter,
44
+ isAbortError,
45
+ withDedupe,
46
+ withRetry
18
47
  };
19
48
  //# sourceMappingURL=index.js.map
@@ -2,4 +2,4 @@ export { AuthInterceptorOptions, createAuthInterceptor } from './auth.js';
2
2
  export { UnwrapInterceptorOptions, createUnwrapInterceptor } from './unwrap.js';
3
3
  export { LogFn, LogLevel, LoggerInterceptorsOptions, createLoggerInterceptors } from './logger.js';
4
4
  export { RequestIdInterceptorOptions, createRequestIdInterceptor } from './request-id.js';
5
- import '../types-CoJSp-ar.js';
5
+ import '../types-DLO2d3bV.js';
@@ -1,3 +1,4 @@
1
+ import "../chunk-Y45YDTWD.js";
1
2
  import {
2
3
  createAuthInterceptor
3
4
  } from "../chunk-FY6YU3PV.js";
@@ -111,13 +111,22 @@ type RequestAdapter = (config: RequestConfig) => Promise<RawResponse>;
111
111
  /** adapter 装饰器:输入 adapter,输出增强后的 adapter(retry/dedupe 以该形态参与 .use 组合) */
112
112
  type AdapterEnhancer = (adapter: RequestAdapter) => RequestAdapter;
113
113
  /**
114
- * 可链式增强的 adapter:本身可直接当 RequestAdapter 用,同时提供 .use 叠加装饰器。
115
- * 注册顺序 = 请求流经顺序(与拦截器链一致):adapter.use(a).use(b) 时请求依次经过 a → b → adapter。
114
+ * 可链式增强的适配器:本身可直接当 RequestAdapter 用。
115
+ * - use:叠加装饰器(注册顺序 = 请求流经顺序)
116
+ * - extend:附加平台特有方法(如 upload / downloadFile)。工厂收到"组合后的适配器",
117
+ * 附加方法可路由完整管道(拦截器/装饰器);附加方法在后续 .use 链上保持不丢
118
+ *
119
+ * T 为已附加方法的类型形状(defineAdapter 默认为空)。
116
120
  */
117
- interface ChainableAdapter extends RequestAdapter {
118
- /** 在外层叠加一个装饰器,返回新的链式 adapter(原 adapter 不被修改) */
119
- use: (enhancer: AdapterEnhancer) => ChainableAdapter;
120
- }
121
+ type ChainableAdapter<T extends object = object> = RequestAdapter & T & {
122
+ /** 在外层叠加一个装饰器,返回新的链式适配器(原适配器不被修改) */
123
+ use: (enhancer: AdapterEnhancer) => ChainableAdapter<T>;
124
+ /**
125
+ * 附加平台特有方法。工厂以当前已组合的适配器执行一次——
126
+ * 附加方法天然带上此前注册的装饰器;之后的 .use 不影响已附加方法(先 use 后 extend)。
127
+ */
128
+ extend: <U extends object>(factory: (composed: RequestAdapter) => U) => ChainableAdapter<T & U>;
129
+ };
121
130
  /** 请求拦截器:返回新 config(可异步) */
122
131
  type RequestInterceptor = (config: RequestConfig) => RequestConfig | Promise<RequestConfig>;
123
132
  /** 响应拦截器(成功链):转换 / 剥壳 */
@@ -133,5 +142,5 @@ type ResponseInterceptor = (res: RawResponse) => RawResponse | Promise<RawRespon
133
142
  */
134
143
  type ErrorInterceptor = (error: RequestError) => RawResponse | RequestError | void | Promise<RawResponse | RequestError | void>;
135
144
 
136
- export { RequestError as f, createAbortError as l, isAbortError as m };
137
- export type { AdapterEnhancer as A, ChainableAdapter as C, ErrorInterceptor as E, RequestConfig as R, RequestAdapter as a, RawResponse as b, RequestInterceptor as c, ResponseInterceptor as d, AdapterOptions as e, RequestErrorKind as g, RequestErrorOptions as h, RequestEvents as i, RequestMethod as j, RequestProgress as k };
145
+ export { RequestError as d, createAbortError as l, isAbortError as m };
146
+ export type { AdapterEnhancer as A, ChainableAdapter as C, ErrorInterceptor as E, RawResponse as R, AdapterOptions as a, RequestAdapter as b, RequestConfig as c, RequestErrorKind as e, RequestErrorOptions as f, RequestEvents as g, RequestInterceptor as h, RequestMethod as i, RequestProgress as j, ResponseInterceptor as k };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huangqi_an/request",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "统一请求层:零依赖 core + 按需导入的 adapter 插件(fetch / axios / uni-app)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -65,6 +65,7 @@
65
65
  "files": [
66
66
  "dist"
67
67
  ],
68
+ "sideEffects": false,
68
69
  "engines": {
69
70
  "node": ">=18"
70
71
  },
@@ -1 +0,0 @@
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 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 * 返回值本身仍是合法的 RequestAdapter,可直接传给 createRequest。\n */\nexport function defineAdapter(adapter: RequestAdapter): ChainableAdapter {\n const chain = (enhancers: AdapterEnhancer[]): ChainableAdapter => {\n let composed: RequestAdapter | undefined\n const wrapped = ((config: RequestConfig) => {\n // 惰性组合并缓存:装饰器工厂只在首次请求时执行一次,.use 不触发重建\n if (composed === undefined) {\n composed = enhancers.reduceRight((acc, enhance) => enhance(acc), adapter)\n }\n return composed(config)\n }) as ChainableAdapter\n wrapped.use = (enhancer) => chain([...enhancers, enhancer])\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 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":[]}