ajax-hooker 1.0.9 → 1.1.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.
Files changed (50) hide show
  1. package/README.md +195 -77
  2. package/README.zh-CN.md +352 -0
  3. package/dist/cjs/index.js +1 -0
  4. package/dist/cjs/types/common.d.ts +9 -0
  5. package/dist/cjs/types/constant.d.ts +6 -0
  6. package/dist/cjs/types/fetch.d.ts +19 -0
  7. package/dist/cjs/types/index.d.ts +3 -0
  8. package/dist/cjs/types/interceptor.d.ts +16 -0
  9. package/dist/cjs/types/sse.d.ts +19 -0
  10. package/dist/cjs/types/type.d.ts +43 -0
  11. package/dist/cjs/types/utils.d.ts +11 -0
  12. package/dist/cjs/types/xhr.d.ts +26 -0
  13. package/dist/esm/index.js +1 -0
  14. package/dist/esm/types/common.d.ts +9 -0
  15. package/dist/esm/types/constant.d.ts +6 -0
  16. package/dist/esm/types/fetch.d.ts +19 -0
  17. package/dist/esm/types/index.d.ts +3 -0
  18. package/dist/esm/types/interceptor.d.ts +16 -0
  19. package/dist/esm/types/sse.d.ts +19 -0
  20. package/dist/esm/types/type.d.ts +43 -0
  21. package/dist/esm/types/utils.d.ts +11 -0
  22. package/dist/esm/types/xhr.d.ts +26 -0
  23. package/dist/iife/index.js +1 -0
  24. package/dist/iife/types/common.d.ts +9 -0
  25. package/dist/iife/types/constant.d.ts +6 -0
  26. package/dist/iife/types/fetch.d.ts +19 -0
  27. package/dist/iife/types/index.d.ts +3 -0
  28. package/dist/iife/types/interceptor.d.ts +16 -0
  29. package/dist/iife/types/sse.d.ts +19 -0
  30. package/dist/iife/types/type.d.ts +43 -0
  31. package/dist/iife/types/utils.d.ts +11 -0
  32. package/dist/iife/types/xhr.d.ts +26 -0
  33. package/package.json +64 -54
  34. package/.github/workflows/npm-publish.yml +0 -73
  35. package/README.en.md +0 -229
  36. package/output/cjs/index.js +0 -4084
  37. package/output/esm/index.js +0 -4080
  38. package/output/esm/index.js.map +0 -1
  39. package/output/iife/index.js +0 -4089
  40. package/rollup.config.js +0 -60
  41. package/src/constant.ts +0 -8
  42. package/src/demo/backup.ts +0 -0
  43. package/src/demo/index.ts +0 -42
  44. package/src/demo/teach.ts +0 -3
  45. package/src/index.ts +0 -692
  46. package/src/type.ts +0 -69
  47. package/src/utils.ts +0 -30
  48. package/test/interceptor.test.ts +0 -340
  49. package/tsconfig.json +0 -20
  50. package/vitest.config.ts +0 -22
package/README.md CHANGED
@@ -1,43 +1,45 @@
1
1
  # ajax-hooker
2
2
 
3
- 一个轻量级的 AJAX 请求拦截器,支持拦截和修改 XMLHttpRequest 和 Fetch 请求。
3
+ English | [中文](./README.zh-CN.md)
4
4
 
5
- ## 特性
5
+ A lightweight AJAX request interceptor that supports intercepting and modifying both XMLHttpRequest and Fetch requests.
6
6
 
7
- - 🎯 同时支持 XMLHttpRequest 和 Fetch API
8
- - 🔄 可拦截和修改请求参数(URL、Method、Headers、Body)
9
- - 📦 可捕获响应数据
10
- - 🌊 支持流式响应拦截(SSE、流式 JSON 等)
11
- - 🪝 支持多个钩子函数链式执行
12
- - 🔒 单例模式,确保全局唯一实例
13
- - 📝 完整的 TypeScript 类型支持
7
+ ## Features
14
8
 
15
- ## 安装
9
+ - Works with both XMLHttpRequest and Fetch API
10
+ - Intercepts and modifies request parameters (URL, Method, Headers, Body)
11
+ - Captures response data
12
+ - Supports streaming response interception (SSE, NDJSON, streaming JSON, etc.)
13
+ - Chain multiple hook functions
14
+ - Singleton pattern ensures a single global instance
15
+ - Full TypeScript type support
16
+
17
+ ## Installation
16
18
 
17
19
  ```bash
18
20
  npm install ajax-hooker
19
21
  ```
20
22
 
21
- ## 快速开始
23
+ ## Quick Start
22
24
 
23
25
  ```typescript
24
26
  import AjaxInterceptor from 'ajax-hooker';
25
27
 
26
- // 获取拦截器实例
28
+ // Get the interceptor instance
27
29
  const interceptor = AjaxInterceptor.getInstance();
28
30
 
29
- // 注入拦截器
31
+ // Inject the interceptor
30
32
  interceptor.inject();
31
33
 
32
- // 添加钩子函数
34
+ // Add a hook
33
35
  interceptor.hook((request) => {
34
- // 修改请求
36
+ // Modify the request
35
37
  request.headers.set('Authorization', 'Bearer token');
36
38
 
37
- // 捕获响应
39
+ // Capture the response
38
40
  request.response = async (response) => {
39
- console.log('响应状态:', response.status);
40
- console.log('响应数据:', response.json);
41
+ console.log('Status:', response.status);
42
+ console.log('Data:', response.json);
41
43
  };
42
44
 
43
45
  return request;
@@ -48,85 +50,143 @@ interceptor.hook((request) => {
48
50
 
49
51
  ### AjaxInterceptor.getInstance()
50
52
 
51
- 获取拦截器单例实例。
53
+ Get the singleton interceptor instance.
52
54
 
53
55
  ```typescript
54
56
  const interceptor = AjaxInterceptor.getInstance();
55
57
  ```
56
58
 
57
- ### inject()
59
+ ### inject(type?)
60
+
61
+ Inject the interceptor and start intercepting requests.
58
62
 
59
- 注入拦截器,开始拦截请求。
63
+ **Parameters:**
64
+ - `type`: Optional. Specify `'xhr'` or `'fetch'` to inject only one type. If omitted, both are injected.
60
65
 
61
66
  ```typescript
67
+ // Inject all
62
68
  interceptor.inject();
69
+
70
+ // Only XHR
71
+ interceptor.inject('xhr');
72
+
73
+ // Only Fetch
74
+ interceptor.inject('fetch');
63
75
  ```
64
76
 
65
- ### uninject()
77
+ ### uninject(type?)
66
78
 
67
- 移除拦截器,恢复原始的 XMLHttpRequest Fetch
79
+ Remove the interceptor and restore native XMLHttpRequest and Fetch.
80
+
81
+ **Parameters:**
82
+ - `type`: Optional. Specify `'xhr'` or `'fetch'` to remove only one type. If omitted, both are removed.
68
83
 
69
84
  ```typescript
85
+ // Remove all
70
86
  interceptor.uninject();
87
+
88
+ // Only remove XHR
89
+ interceptor.uninject('xhr');
71
90
  ```
72
91
 
73
92
  ### hook(fn, type?)
74
93
 
75
- 添加钩子函数。
94
+ Add a hook function.
76
95
 
77
- **参数:**
78
- - `fn`: 钩子函数,接收请求对象并返回修改后的请求对象
79
- - `type`: 可选,指定拦截类型 `'xhr'` `'fetch'`,不指定则同时拦截两者
96
+ **Parameters:**
97
+ - `fn`: Hook function that receives a request object and returns the modified request (can also return nothing, in which case the original request is kept unchanged)
98
+ - `type`: Optional. Specify `'xhr'` or `'fetch'` to intercept only one type. If omitted, both are intercepted.
80
99
 
81
100
  ```typescript
82
- // 拦截所有请求
101
+ // Intercept all requests
83
102
  interceptor.hook((request) => {
84
- console.log('请求:', request.url);
103
+ console.log('Request:', request.url);
85
104
  return request;
86
105
  });
87
106
 
88
- // 只拦截 XHR 请求
107
+ // Only XHR
89
108
  interceptor.hook((request) => {
90
- console.log('XHR 请求:', request.url);
109
+ console.log('XHR:', request.url);
91
110
  return request;
92
111
  }, 'xhr');
93
112
 
94
- // 只拦截 Fetch 请求
113
+ // Only Fetch
95
114
  interceptor.hook((request) => {
96
- console.log('Fetch 请求:', request.url);
115
+ console.log('Fetch:', request.url);
97
116
  return request;
98
117
  }, 'fetch');
99
118
  ```
100
119
 
101
- ## 请求对象结构
120
+ ## Request Object (AjaxInterceptorRequest)
102
121
 
103
- 钩子函数接收的请求对象包含以下属性:
122
+ The request object received by hook functions contains the following properties:
123
+
124
+ | Property | Type | Access | Description |
125
+ |----------|------|--------|-------------|
126
+ | `type` | `'xhr' \| 'fetch'` | Read-only | Request type, identifies the request source |
127
+ | `method` | `string` | **Writable** | HTTP method (GET, POST, etc.) |
128
+ | `url` | `string` | **Writable** | Request URL |
129
+ | `headers` | `Headers` | **Writable** | Request headers, standard [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) object |
130
+ | `data` | `any` | **Writable** | Request body |
131
+ | `response` | `(response: AjaxResponse) => void \| Promise<void>` | **Writable** | Response callback, invoked when the response is received |
132
+ | `onStreamChunk` | `(chunk: StreamChunk) => string \| void \| Promise<string \| void>` | **Writable** | Streaming response hook (optional), used to intercept each chunk of a streaming response |
133
+ | `responseType` | `XMLHttpRequestResponseType` | **Writable** | XHR only. Corresponds to `xhr.responseType` |
134
+ | `withCredentials` | `boolean` | **Writable** | XHR only. Corresponds to `xhr.withCredentials` |
135
+ | `timeout` | `number` | **Writable** | XHR only. Corresponds to `xhr.timeout` |
104
136
 
105
137
  ```typescript
106
138
  interface AjaxInterceptorRequest {
107
- type: 'xhr' | 'fetch'; // 请求类型
108
- method: string; // 请求方法
109
- url: string; // 请求 URL
110
- headers: Headers; // 请求头
111
- data: any; // 请求体
112
- response: (response: AjaxResponse) => void | Promise<void>; // 响应回调
113
- onStreamChunk?: (chunk: StreamChunk) => string | void | Promise<string | void>; // 流式响应钩子
139
+ type: 'xhr' | 'fetch';
140
+ method: string;
141
+ url: string;
142
+ headers: Headers;
143
+ data: any;
144
+ response: (response: AjaxResponse) => void | Promise<void>;
145
+ onStreamChunk?: (chunk: StreamChunk) => string | void | Promise<string | void>;
146
+ // XHR-specific properties
147
+ responseType?: XMLHttpRequestResponseType;
148
+ withCredentials?: boolean;
149
+ timeout?: number;
114
150
  }
115
151
  ```
116
152
 
117
- ## 响应对象结构
153
+ ## Response Object (AjaxResponse)
154
+
155
+ The response object received by the response callback contains the following properties:
156
+
157
+ | Property | Type | Access | Description |
158
+ |----------|------|--------|-------------|
159
+ | `status` | `number` | **Writable** | HTTP status code |
160
+ | `statusText` | `string` | **Writable** | HTTP status text |
161
+ | `headers` | `Headers` | Read-only | Response headers |
162
+ | `finalUrl` | `string` | Read-only | Final URL (after redirects) |
163
+ | `response` | `any` | **Writable** | XHR only. Corresponds to `xhr.response` |
164
+ | `responseText` | `string` | **Writable** | XHR only. Corresponds to `xhr.responseText` |
165
+ | `responseXML` | `Document \| null` | **Writable** | XHR only. Corresponds to `xhr.responseXML` |
166
+ | `ok` | `boolean` | Read-only | Fetch only. Whether the request was successful (status 200-299) |
167
+ | `redirected` | `boolean` | Read-only | Fetch only. Whether the request was redirected |
168
+ | `json` | `any` | Read-only | Fetch only. Parsed JSON data |
169
+ | `text` | `string` | Read-only | Fetch only. Response text |
170
+ | `arrayBuffer` | `ArrayBuffer` | Read-only | Fetch only. Response ArrayBuffer |
171
+ | `blob` | `Blob` | Read-only | Fetch only. Response Blob |
172
+ | `formData` | `FormData` | Read-only | Fetch only. Response FormData |
173
+
174
+ > **Note:** For Fetch responses, `json`, `text`, `arrayBuffer`, `blob`, and `formData` are automatically parsed by the interceptor and available as properties. No need to call `.json()` or similar methods. If parsing fails, the corresponding property is `null`.
118
175
 
119
176
  ```typescript
120
177
  interface AjaxResponse {
121
- status: number; // 状态码
122
- statusText: string; // 状态文本
123
- headers: Headers; // 响应头
124
- finalUrl: string; // 最终 URL
178
+ // Common properties
179
+ status: number; // Writable
180
+ statusText: string; // Writable
181
+ headers: Headers; // Read-only
182
+ finalUrl: string;
125
183
 
126
- // XHR 响应
184
+ // XHR-specific (Writable)
127
185
  response?: any;
186
+ responseText?: string;
187
+ responseXML?: Document | null;
128
188
 
129
- // Fetch 响应
189
+ // Fetch-specific (Read-only, auto-parsed)
130
190
  ok?: boolean;
131
191
  redirected?: boolean;
132
192
  json?: any;
@@ -137,9 +197,44 @@ interface AjaxResponse {
137
197
  }
138
198
  ```
139
199
 
140
- ## 使用示例
200
+ ## Stream Chunk (StreamChunk)
201
+
202
+ The chunk object received by the `onStreamChunk` hook:
203
+
204
+ | Property | Type | Description |
205
+ |----------|------|-------------|
206
+ | `text` | `string` | Decoded text content |
207
+ | `raw` | `Uint8Array` | Raw byte data |
208
+ | `index` | `number` | Chunk index (starting from 0) |
209
+ | `timestamp` | `number` | Receive timestamp |
210
+
211
+ ```typescript
212
+ interface StreamChunk {
213
+ text: string;
214
+ raw: Uint8Array;
215
+ index: number;
216
+ timestamp: number;
217
+ }
218
+ ```
219
+
220
+ ## Streaming Response Auto-Detection
221
+
222
+ The interceptor automatically detects streaming responses based on the `Content-Type` response header. The following types are recognized as streaming responses:
141
223
 
142
- ### 修改请求 URL
224
+ - `text/event-stream` (SSE)
225
+ - `application/stream+json`
226
+ - `application/x-ndjson`
227
+ - `application/jsonl`
228
+ - `application/json-seq`
229
+
230
+ When a streaming response is detected:
231
+ 1. The `response` callback is invoked immediately (containing only `status`, `statusText`, `ok`, `headers`, `finalUrl`, `redirected` — no body data)
232
+ 2. Stream data is passed chunk by chunk via the `onStreamChunk` hook
233
+ 3. Returning a `string` from `onStreamChunk` modifies the chunk content; returning `void` or nothing keeps the original content
234
+
235
+ ## Examples
236
+
237
+ ### Rewrite Request URL
143
238
 
144
239
  ```typescript
145
240
  interceptor.hook((request) => {
@@ -150,7 +245,7 @@ interceptor.hook((request) => {
150
245
  });
151
246
  ```
152
247
 
153
- ### 添加认证 Token
248
+ ### Add Auth Token
154
249
 
155
250
  ```typescript
156
251
  interceptor.hook((request) => {
@@ -159,76 +254,99 @@ interceptor.hook((request) => {
159
254
  });
160
255
  ```
161
256
 
162
- ### 捕获响应数据
257
+ ### Capture Response Data
163
258
 
164
259
  ```typescript
165
260
  interceptor.hook((request) => {
166
261
  request.response = async (response) => {
167
- console.log('状态码:', response.status);
168
- console.log('响应数据:', response.json || response.response);
262
+ console.log('Status:', response.status);
263
+ // XHR uses response.response, Fetch uses response.json
264
+ console.log('Data:', response.json || response.response);
169
265
  };
170
266
  return request;
171
267
  });
172
268
  ```
173
269
 
174
- ### 拦截流式响应
270
+ ### Modify XHR Properties
175
271
 
176
272
  ```typescript
177
273
  interceptor.hook((request) => {
178
- // 拦截流式响应的每个数据块
274
+ // Change response type
275
+ request.responseType = 'json';
276
+ // Set timeout
277
+ request.timeout = 5000;
278
+ // Send credentials
279
+ request.withCredentials = true;
280
+ return request;
281
+ }, 'xhr');
282
+ ```
283
+
284
+ ### Intercept Streaming Responses
285
+
286
+ ```typescript
287
+ interceptor.hook((request) => {
288
+ // Response headers are available immediately when the stream starts
289
+ request.response = async (response) => {
290
+ console.log('Stream started, status:', response.status);
291
+ };
292
+
293
+ // Intercept each chunk of the streaming response
179
294
  request.onStreamChunk = async (chunk) => {
180
- console.log('收到数据块:', chunk.text);
181
- console.log('数据块索引:', chunk.index);
295
+ console.log('Chunk:', chunk.text);
296
+ console.log('Raw data:', chunk.raw);
297
+ console.log('Index:', chunk.index);
298
+ console.log('Timestamp:', chunk.timestamp);
182
299
 
183
- // 可以修改数据块内容
300
+ // Return modified text to replace the chunk content
184
301
  return chunk.text.replace('old', 'new');
302
+
303
+ // Return void or nothing to keep the original content
185
304
  };
186
305
 
187
306
  return request;
188
307
  });
189
308
  ```
190
309
 
191
- ### 多个钩子链式执行
310
+ ### Multiple Hooks in Sequence
192
311
 
193
312
  ```typescript
194
- // 第一个钩子:添加 token
313
+ // First hook: add token
195
314
  interceptor.hook((request) => {
196
315
  request.headers.set('Authorization', 'Bearer token');
197
316
  return request;
198
317
  });
199
318
 
200
- // 第二个钩子:添加时间戳
319
+ // Second hook: add timestamp
201
320
  interceptor.hook((request) => {
202
321
  request.headers.set('X-Timestamp', Date.now().toString());
203
322
  return request;
204
323
  });
205
324
 
206
- // 第三个钩子:记录日志
325
+ // Third hook: log (no return value, keeps original request)
207
326
  interceptor.hook((request) => {
208
327
  console.log(`${request.method} ${request.url}`);
209
- return request;
210
328
  });
211
329
  ```
212
330
 
213
- ## 开发
331
+ ## Development
214
332
 
215
333
  ```bash
216
- # 安装依赖
217
- npm install
334
+ # Install dependencies
335
+ pnpm install
218
336
 
219
- # 开发模式
220
- npm start
337
+ # Dev mode
338
+ pnpm dev
221
339
 
222
- # 构建
223
- npm run build
340
+ # Build
341
+ pnpm build
224
342
 
225
- # 测试
226
- npm test
343
+ # Test
344
+ pnpm test
227
345
 
228
- # 测试覆盖率
229
- npm run test:coverage
346
+ # Test coverage
347
+ pnpm test:coverage
230
348
  ```
231
349
 
232
350
  ## License
233
351
 
234
- MIT
352
+ MIT