iota-fetch 0.1.0 → 0.1.1

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/README.md CHANGED
@@ -1,107 +1,206 @@
1
- ## iota-fetch
1
+ # iota-fetch
2
2
 
3
- 基于 **axios** 的二次封装请求库,提供统一的 `fetch.get/post/delete/put/patch` 调用方式,并内置**请求队列**能力(可按 key 取消单个请求或取消全部请求)。本项目为 **纯 JS 版本**(不包含 TypeScript 类型声明)。
3
+ 一个基于 **axios** 的二次封装请求库,提供统一的 `get/post/delete/put/patch` 调用方式,并内置**请求队列**能力(可按 key 中断单个请求或中断全部请求)。支持自定义 axios 默认配置、请求/响应拦截器,以及可选的 loading 显示/隐藏钩子。
4
4
 
5
- ### 安装
5
+ ## 安装
6
6
 
7
7
  ```bash
8
8
  npm i iota-fetch axios
9
9
  ```
10
10
 
11
- > `iota-fetch` 运行时依赖 `axios`。如果你的业务项目里已经安装了 axios,可以只安装 `iota-fetch`,但建议保证 axios 版本满足本包要求。
11
+ > 本项目本身依赖 `axios`,如果你在业务工程里已安装 axios,可按你的依赖管理策略处理(避免重复安装/版本冲突)。
12
12
 
13
- ### 使用 ①
13
+ ## 快速开始
14
14
 
15
- ```js
15
+ ```ts
16
16
  import { createFetch } from "iota-fetch";
17
17
 
18
- // requestConfig:axios.create 的配置
19
- const requestConfig = {
20
- baseURL: "https://api.example.com",
21
- headers: { "Content-Type": "application/json;charset=utf-8" },
22
- };
18
+ const fetch = createFetch({
19
+ requestConfig: {
20
+ baseURL: "https://api.example.com",
21
+ headers: {
22
+ "Content-Type": "application/json;charset=utf-8",
23
+ },
24
+ },
25
+ });
23
26
 
24
- // 请求拦截
25
- function requestIntercept() {
26
- const intercept = (config) => config;
27
- const error = (error) => Promise.reject(error);
28
- return [intercept, error];
29
- }
27
+ // GET:第二个参数会作为 query params
28
+ const list = await fetch.get("/items", { page: 1, pageSize: 10 });
30
29
 
31
- // 响应拦截
32
- function responseIntercept() {
33
- const intercept = (response) => response;
34
- const error = (error) => Promise.reject(error);
35
- return [intercept, error];
36
- }
30
+ // POST:第二个参数会作为 body data
31
+ const created = await fetch.post("/items", { name: "foo" });
32
+ ```
37
33
 
38
- // createFetch 返回 { fetch, instance }
39
- const { fetch } = createFetch({ requestIntercept, responseIntercept, requestConfig });
34
+ ## API
40
35
 
41
- // GET/DELETE:第二个参数作为 query params
42
- fetch.get("/list", { page: 1 });
36
+ ### `createFetch(options)`
43
37
 
44
- // POST/PUT/PATCH:第二个参数作为 body data
45
- fetch.post("/create", { name: "foo" });
46
- ```
38
+ 创建一个包含 `get/post/delete/put/patch` 的请求对象。
39
+
40
+ #### 参数 `options`
41
+
42
+ - **`requestConfig`**: `CreateAxiosDefaults`
43
+ - axios 实例默认配置(如 `baseURL`、`timeout`、`headers` 等)
44
+ - **`requestIntercept?`**: `() => { onFulfilled, onRejected }`
45
+ - 请求拦截器工厂函数(内部会 `instance.interceptors.request.use(...)`)
46
+ - **`responseIntercept?`**: `() => { onFulfilled, onRejected }`
47
+ - 响应拦截器工厂函数(内部会 `instance.interceptors.response.use(...)`)
48
+ - **`loading?`**: `{ show(): void; hide(): void }`
49
+ - 传入后可配合单次请求的 `config.loading` 控制显示/隐藏
50
+
51
+ #### 返回值
47
52
 
48
- ### 使用
53
+ 返回 `fetch` 对象:
49
54
 
50
- ```js
55
+ - **`fetch.get(url, data?, config?)`**
56
+ - **`fetch.delete(url, data?, config?)`**
57
+ - **`fetch.post(url, data?, config?)`**
58
+ - **`fetch.put(url, data?, config?)`**
59
+ - **`fetch.patch(url, data?, config?)`**
60
+
61
+ 其中:
62
+ - `url: string`
63
+ - `data?: any`
64
+ - `config?: AxiosRequestConfig & { loading?: boolean }`
65
+
66
+ 行为规则:
67
+ - `get/delete`:`data` 会被放到 `params`
68
+ - `post/put/patch`:`data` 会被放到 `data`
69
+ - 返回值:默认 `Promise<R>`,内部 `resolve(res.data)`
70
+
71
+ ### 拦截器示例
72
+
73
+ ```ts
74
+ import type { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from "axios";
51
75
  import { createFetch } from "iota-fetch";
52
76
 
53
- // requestConfig:axios.create 的配置
54
- const requestConfig = {};
77
+ const fetch = createFetch({
78
+ requestConfig: { baseURL: "https://api.example.com" },
79
+ requestIntercept: () => ({
80
+ onFulfilled: (config: InternalAxiosRequestConfig) => {
81
+ config.headers.set("X-Token", "your-token");
82
+ return config;
83
+ },
84
+ onRejected: (error: AxiosError) => Promise.reject(error),
85
+ }),
86
+ responseIntercept: () => ({
87
+ onFulfilled: (resp: AxiosResponse) => resp,
88
+ onRejected: (error: AxiosError) => Promise.reject(error),
89
+ }),
90
+ });
91
+ ```
92
+
93
+ ### Loading 示例
94
+
95
+ ```ts
96
+ import { createFetch } from "iota-fetch";
55
97
 
56
- // loading:实现 loading.show() / loading.hide()
57
98
  const loading = {
58
99
  show: () => console.log("loading show"),
59
100
  hide: () => console.log("loading hide"),
60
101
  };
61
102
 
62
- const { fetch, instance } = createFetch({ requestConfig, loading });
63
-
64
- // fetch 请求方法 fetch.get fetch.post fetch.put ...
65
- // 创建的axios 实例 instance 添加拦截器
66
-
67
- // 添加请求拦截器
68
- instance.interceptors.request.use(function (config) {
69
- // 在发送请求之前做些什么
70
- return config;
71
- }, function (error) {
72
- // 对请求错误做些什么
73
- return Promise.reject(error);
74
- });
75
-
76
- // 添加响应拦截器
77
- instance.interceptors.response.use(function (response) {
78
- // 2xx 范围内的状态码都会触发该函数。
79
- return response;
80
- }, function (error) {
81
- // 超出 2xx 范围的状态码都会触发该函数。
82
- return Promise.reject(error);
83
- });
84
-
85
- // 单次请求开启 loading:第三个参数 config 传入 { loading: true }
86
- fetch.get("/list", { page: 1 }, { loading: true });
103
+ const fetch = createFetch({
104
+ requestConfig: { baseURL: "https://api.example.com" },
105
+ loading,
106
+ });
107
+
108
+ // 只有当 config.loading 为 true 才会触发 show/hide
109
+ await fetch.get("/items", { page: 1 }, { loading: true });
87
110
  ```
88
111
 
89
- ### 取消请求
112
+ ## 请求队列与取消请求
113
+
114
+ 包对外导出 `requestQueue`,内部用 `AbortController` 管理请求。
90
115
 
91
- ```js
116
+ ```ts
92
117
  import { requestQueue } from "iota-fetch";
93
118
 
94
- // 生成 key(method + url + data(JSON.stringify))
95
- const key = requestQueue.createMapKey("get", "/list", { page: 1 });
119
+ // 获取当前队列(Map<key, AbortController>)
120
+ const q = requestQueue.getQueue();
121
+ ```
122
+
123
+ ### key 规则
124
+
125
+ key 默认由以下规则生成:
126
+
127
+ 1. 基础部分:`method + "-" + url`
128
+ 2. 数据部分:如果存在 `data`,则对其进行以下处理:
129
+ - 递归排序对象键
130
+ - 清洗数据(过滤 `undefined` 和函数)
131
+ - 将处理后的数据转换为 JSON 字符串
132
+ - 使用 MD5 生成哈希值
133
+ 3. 最终 key:`基础部分 + "-" + MD5哈希值`
134
+
135
+ 这样处理的好处是:
136
+ - 相同的请求参数(无论顺序如何)会生成相同的 key
137
+ - 避免了复杂参数导致 key 过长的问题
138
+ - 提高了 key 的唯一性和安全性
96
139
 
97
- // 取消单个请求
140
+ 你也可以手动生成:
141
+
142
+ ```ts
143
+ const key = requestQueue.createKey("get", "/items", { page: 1 });
144
+ ```
145
+
146
+ 示例:
147
+ ```ts
148
+ // 以下两个请求会生成相同的 key
149
+ const key1 = requestQueue.createKey("get", "/items", { page: 1, size: 10 });
150
+ const key2 = requestQueue.createKey("get", "/items", { size: 10, page: 1 });
151
+
152
+ // 输出示例:"get-/items-5f4dcc3b5aa765d61d8327deb882cf99"
153
+ ```
154
+
155
+ ### 中断单个请求
156
+
157
+ ```ts
98
158
  requestQueue.abortive(key);
159
+ ```
160
+
161
+ ### 中断所有请求
99
162
 
100
- // 取消全部请求
163
+ ```ts
101
164
  requestQueue.removeResAll();
102
165
  ```
103
166
 
104
167
  ### 同 key 自动取消旧请求(内置行为)
105
168
 
106
- 当你发起新请求时,如果队列里已存在相同 key 的请求,会先取消旧请求再登记新请求,避免相同请求并发造成浪费。
169
+ 当发起新请求时,如果队列里已存在同一个 key,会先取消旧请求再登记新请求(避免相同请求并发造成的覆盖与浪费)。
170
+
171
+ > 注意:是否算“同一个请求”取决于 key(包含 `method/url/data` 的 JSON 字符串)。
172
+
173
+ ## 开发与构建
174
+
175
+ ```bash
176
+ # 构建产物到 lib/(CJS + ESM + d.ts)
177
+ npm run build
178
+
179
+ # 开发模式(watch + 本地静态服务 + livereload)
180
+ npm run start
181
+
182
+ # 自动发布脚本(检查代码更新、npm 登录状态、版本管理)
183
+ npm run release
184
+ ```
185
+
186
+ 构建输出(默认):
187
+ - `lib/bundle.cjs.js`
188
+ - `lib/bundle.esm.js`
189
+ - `lib/main.d.ts`
190
+
191
+ ## 发布流程
192
+
193
+ 1. 运行 `npm run release` 启动发布流程
194
+ 2. 脚本会检查代码是否有更新
195
+ 3. 检查 npm 是否登录
196
+ 4. 显示当前版本和仓库版本
197
+ 5. 选择版本升级类型(小版本/中版本/大版本)
198
+ 6. 执行构建和发布
199
+ 7. 显示远程包的最新版本
200
+
201
+ ## 版本管理
202
+
203
+ - **小版本**:修复 bug,向后兼容
204
+ - **中版本**:添加新功能,向后兼容
205
+ - **大版本**:不向后兼容的变更
107
206
 
@@ -0,0 +1,6 @@
1
+ import type { FetchSturt, Loading, Options } from "./models";
2
+ declare const createFetch: ({ requestConfig, requestIntercept, responseIntercept, loading, }: Options & {
3
+ loading?: Loading;
4
+ }) => FetchSturt;
5
+ export { createFetch };
6
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/axios/fetch.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAa,UAAU,EAAY,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAElF,QAAA,MAAM,WAAW,GAAI,kEAKlB,OAAO,GAAG;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,eAiBjC,CAAC;AAEF,OAAO,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { AxiosInstance } from "axios";
2
+ import { Loading, RequestConfig } from "./models";
3
+ declare const fetchData: <D, R = Record<string, any> | any>(instance: AxiosInstance, { url, method, data, config }: RequestConfig<D>, loading?: Loading) => Promise<R>;
4
+ export default fetchData;
5
+ //# sourceMappingURL=fetchFun.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetchFun.d.ts","sourceRoot":"","sources":["../../src/axios/fetchFun.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,OAAO,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGlD,QAAA,MAAM,SAAS,GAAI,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,EACjD,UAAU,aAAa,EACvB,+BAA+B,aAAa,CAAC,CAAC,CAAC,EAC/C,UAAU,OAAO,KAChB,OAAO,CAAC,CAAC,CAoCX,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { type AxiosInstance } from "axios";
2
+ import { Options } from "./models";
3
+ declare const createInstance: ({ requestConfig, requestIntercept, responseIntercept }: Options) => AxiosInstance;
4
+ export default createInstance;
5
+ //# sourceMappingURL=instance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/axios/instance.ts"],"names":[],"mappings":"AAAA,OAAc,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,QAAA,MAAM,cAAc,GAAI,wDAAwD,OAAO,KAAG,aAkBzF,CAAC;AAEF,eAAe,cAAc,CAAC"}
@@ -0,0 +1,32 @@
1
+ import type { AxiosError, AxiosRequestConfig, AxiosResponse, CreateAxiosDefaults, InternalAxiosRequestConfig, Method } from "axios";
2
+ export interface Options {
3
+ requestConfig: CreateAxiosDefaults;
4
+ requestIntercept?: () => {
5
+ onFulfilled: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig;
6
+ onRejected: (error: AxiosError) => Promise<AxiosError>;
7
+ };
8
+ responseIntercept?: () => {
9
+ onFulfilled: (response: AxiosResponse) => AxiosResponse;
10
+ onRejected: (error: AxiosError) => Promise<AxiosError>;
11
+ };
12
+ }
13
+ export type ConfigPorps = {
14
+ loading?: boolean;
15
+ } & AxiosRequestConfig;
16
+ export interface RequestConfig<D = any> {
17
+ url: string;
18
+ method: Method;
19
+ data?: D;
20
+ loading?: boolean;
21
+ config?: ConfigPorps;
22
+ }
23
+ export type Loading = {
24
+ show: () => void;
25
+ hide: () => void;
26
+ };
27
+ export type FuncName = "get" | "post" | "delete" | "put" | "patch";
28
+ export type FetchFunc = {
29
+ <D = any, R = Record<string, any>>(url: string, data?: D, config?: ConfigPorps): Promise<R>;
30
+ };
31
+ export type FetchSturt = Record<FuncName, FetchFunc>;
32
+ //# sourceMappingURL=models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/axios/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,kBAAkB,EAClB,aAAa,EACb,mBAAmB,EACnB,0BAA0B,EAC1B,MAAM,EACP,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,OAAO;IACtB,aAAa,EAAE,mBAAmB,CAAC;IAEnC,gBAAgB,CAAC,EAAE,MAAM;QACvB,WAAW,EAAE,CAAC,MAAM,EAAE,0BAA0B,KAAK,0BAA0B,CAAC;QAChF,UAAU,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;KACxD,CAAC;IAEF,iBAAiB,CAAC,EAAE,MAAM;QACxB,WAAW,EAAE,CAAC,QAAQ,EAAE,aAAa,KAAK,aAAa,CAAC;QACxD,UAAU,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;KACxD,CAAC;CACH;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,kBAAkB,CAAC;AAErE,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,GAAG;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB,CAAC;AAGF,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,GAAG,OAAO,CAAC;AAEnE,MAAM,MAAM,SAAS,GAAG;IACtB,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CAC7F,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ declare class RequestQueue {
2
+ private static instance;
3
+ private queue;
4
+ constructor();
5
+ private sortObjectKeys;
6
+ getQueue(): Map<string, AbortController>;
7
+ createKey<T>(method: string, path: string, data?: T): string;
8
+ setQueue(key: string, controller: AbortController): void;
9
+ deleteQueue(key: string): void;
10
+ hasQueue(key: string): boolean;
11
+ abortive(key: string): void;
12
+ removeResAll(): void;
13
+ }
14
+ declare let requestQueue: RequestQueue;
15
+ export { requestQueue };
16
+ //# sourceMappingURL=requestQueue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestQueue.d.ts","sourceRoot":"","sources":["../../src/axios/requestQueue.ts"],"names":[],"mappings":"AAEA,cAAM,YAAY;IAChB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAe;IACtC,OAAO,CAAC,KAAK,CAA+B;;IAe5C,OAAO,CAAC,cAAc;IAkBf,QAAQ,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC;IAKxC,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM;IAU5D,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,eAAe;IAKjD,WAAW,CAAC,GAAG,EAAE,MAAM;IAKvB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAK9B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAO3B,YAAY,IAAI,IAAI;CAI5B;AAED,QAAA,IAAI,YAAY,cAAqB,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,CAAC"}