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.
- package/README.md +195 -77
- package/README.zh-CN.md +352 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/types/common.d.ts +9 -0
- package/dist/cjs/types/constant.d.ts +6 -0
- package/dist/cjs/types/fetch.d.ts +19 -0
- package/dist/cjs/types/index.d.ts +3 -0
- package/dist/cjs/types/interceptor.d.ts +16 -0
- package/dist/cjs/types/sse.d.ts +19 -0
- package/dist/cjs/types/type.d.ts +43 -0
- package/dist/cjs/types/utils.d.ts +11 -0
- package/dist/cjs/types/xhr.d.ts +26 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/types/common.d.ts +9 -0
- package/dist/esm/types/constant.d.ts +6 -0
- package/dist/esm/types/fetch.d.ts +19 -0
- package/dist/esm/types/index.d.ts +3 -0
- package/dist/esm/types/interceptor.d.ts +16 -0
- package/dist/esm/types/sse.d.ts +19 -0
- package/dist/esm/types/type.d.ts +43 -0
- package/dist/esm/types/utils.d.ts +11 -0
- package/dist/esm/types/xhr.d.ts +26 -0
- package/dist/iife/index.js +1 -0
- package/dist/iife/types/common.d.ts +9 -0
- package/dist/iife/types/constant.d.ts +6 -0
- package/dist/iife/types/fetch.d.ts +19 -0
- package/dist/iife/types/index.d.ts +3 -0
- package/dist/iife/types/interceptor.d.ts +16 -0
- package/dist/iife/types/sse.d.ts +19 -0
- package/dist/iife/types/type.d.ts +43 -0
- package/dist/iife/types/utils.d.ts +11 -0
- package/dist/iife/types/xhr.d.ts +26 -0
- package/package.json +64 -54
- package/.github/workflows/npm-publish.yml +0 -73
- package/README.en.md +0 -229
- package/output/cjs/index.js +0 -4084
- package/output/esm/index.js +0 -4080
- package/output/esm/index.js.map +0 -1
- package/output/iife/index.js +0 -4089
- package/rollup.config.js +0 -60
- package/src/constant.ts +0 -8
- package/src/demo/backup.ts +0 -0
- package/src/demo/index.ts +0 -42
- package/src/demo/teach.ts +0 -3
- package/src/index.ts +0 -692
- package/src/type.ts +0 -69
- package/src/utils.ts +0 -30
- package/test/interceptor.test.ts +0 -340
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -22
package/README.md
CHANGED
|
@@ -1,43 +1,45 @@
|
|
|
1
1
|
# ajax-hooker
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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('
|
|
40
|
-
console.log('
|
|
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
|
-
|
|
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`:
|
|
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('
|
|
103
|
+
console.log('Request:', request.url);
|
|
85
104
|
return request;
|
|
86
105
|
});
|
|
87
106
|
|
|
88
|
-
//
|
|
107
|
+
// Only XHR
|
|
89
108
|
interceptor.hook((request) => {
|
|
90
|
-
console.log('XHR
|
|
109
|
+
console.log('XHR:', request.url);
|
|
91
110
|
return request;
|
|
92
111
|
}, 'xhr');
|
|
93
112
|
|
|
94
|
-
//
|
|
113
|
+
// Only Fetch
|
|
95
114
|
interceptor.hook((request) => {
|
|
96
|
-
console.log('Fetch
|
|
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;
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
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('
|
|
168
|
-
|
|
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('
|
|
181
|
-
console.log('
|
|
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
|
-
//
|
|
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
|
-
|
|
334
|
+
# Install dependencies
|
|
335
|
+
pnpm install
|
|
218
336
|
|
|
219
|
-
#
|
|
220
|
-
|
|
337
|
+
# Dev mode
|
|
338
|
+
pnpm dev
|
|
221
339
|
|
|
222
|
-
#
|
|
223
|
-
|
|
340
|
+
# Build
|
|
341
|
+
pnpm build
|
|
224
342
|
|
|
225
|
-
#
|
|
226
|
-
|
|
343
|
+
# Test
|
|
344
|
+
pnpm test
|
|
227
345
|
|
|
228
|
-
#
|
|
229
|
-
|
|
346
|
+
# Test coverage
|
|
347
|
+
pnpm test:coverage
|
|
230
348
|
```
|
|
231
349
|
|
|
232
350
|
## License
|
|
233
351
|
|
|
234
|
-
MIT
|
|
352
|
+
MIT
|