@czf0613/http_client 0.1.0 → 0.1.2
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 +46 -5
- package/dist/http_client.d.ts +3 -2
- package/dist/http_client.js +19 -44
- package/dist/index.d.ts +1 -0
- package/dist/parser.d.ts +6 -0
- package/dist/parser.js +29 -0
- package/dist/response_ext.d.ts +61 -0
- package/dist/response_ext.js +150 -0
- package/dist/timer.d.ts +1 -0
- package/dist/timer.js +2 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A simple HTTP client library built with fetch API, supporting a modified SSE (Server-Sent Events) protocol for browser environments.
|
|
4
4
|
|
|
5
|
+
No any dependencies, it is very lightweight.
|
|
6
|
+
|
|
5
7
|
## Requirements
|
|
6
8
|
|
|
7
9
|
- **Runtime**: ES2018+ compatible browsers
|
|
@@ -68,11 +70,36 @@ Makes an HTTP request with default configuration using the Fetch API.
|
|
|
68
70
|
- String or number: sent as `text/plain`
|
|
69
71
|
- Object: sent as `application/json`
|
|
70
72
|
- FormData: sent as `multipart/form-data`
|
|
71
|
-
- `timeoutMs` (number): Timeout in milliseconds. Default: 5000
|
|
73
|
+
- `timeoutMs` (number): Timeout in milliseconds. Default: 5000. **Note: this value is the *connect timeout*, not the total request timeout. You need to handle the *read timeout* in your application logic.**
|
|
74
|
+
|
|
75
|
+
**Returns:** `Promise<ExtendedResponse>` - Extended Response object with additional helper methods. It's the same as the standard Response object, but with additional methods for convenience.
|
|
76
|
+
|
|
77
|
+
#### ExtendedResponse Methods
|
|
78
|
+
|
|
79
|
+
In addition to all standard Response methods (`json()`, `text()`, `arrayBuffer()`, `blob()`, `bytes()`), ExtendedResponse provides the following timeout-enabled methods:
|
|
80
|
+
|
|
81
|
+
**`jsonWithTimeout<T>(readTimeoutMs?: number): Promise<T>`**
|
|
82
|
+
Reads the response body as JSON with a configurable timeout. Default timeout is 5000ms.
|
|
83
|
+
- Throws `Error` with message `"JSON read timeout after Xms"` if timeout occurs
|
|
84
|
+
- Throws parsing errors if JSON is invalid
|
|
85
|
+
|
|
86
|
+
**`textWithTimeout(readTimeoutMs?: number): Promise<string>`**
|
|
87
|
+
Reads the response body as text with a configurable timeout. Default timeout is 5000ms.
|
|
88
|
+
- Throws `Error` with message `"Text read timeout after Xms"` if timeout occurs
|
|
89
|
+
|
|
90
|
+
**`arrayBufferWithTimeout(readTimeoutMs?: number): Promise<ArrayBuffer>`**
|
|
91
|
+
Reads the response body as ArrayBuffer with a configurable timeout. Default timeout is 5000ms.
|
|
92
|
+
- Throws `Error` with message `"ArrayBuffer read timeout after Xms"` if timeout occurs
|
|
72
93
|
|
|
73
|
-
|
|
94
|
+
**`blobWithTimeout(readTimeoutMs?: number): Promise<Blob>`**
|
|
95
|
+
Reads the response body as Blob with a configurable timeout. Default timeout is 5000ms.
|
|
96
|
+
- Throws `Error` with message `"Blob read timeout after Xms"` if timeout occurs
|
|
74
97
|
|
|
75
|
-
|
|
98
|
+
**`bytesWithTimeout(readTimeoutMs?: number): Promise<Uint8Array<ArrayBuffer>>`**
|
|
99
|
+
Reads the response body as Uint8Array with a configurable timeout. Default timeout is 5000ms.
|
|
100
|
+
- Throws `Error` with message `"Bytes read timeout after Xms"` if timeout occurs
|
|
101
|
+
|
|
102
|
+
**Note:** This function(including methods in `ExtendedResponse`) does not handle exceptions by default. You should wrap it in a try-catch block or using a promise chain.
|
|
76
103
|
|
|
77
104
|
**Example:**
|
|
78
105
|
|
|
@@ -128,10 +155,14 @@ Makes an SSE (Server-Sent Events) request and returns an async generator that yi
|
|
|
128
155
|
- `queryParams` (Record<string, string | number> | null): Query parameters to be appended to the URL. Default: null
|
|
129
156
|
- `customHeaders` (Record<string, string> | null): Custom headers. **Important: Do NOT include Content-Type in customHeaders.** Default: null
|
|
130
157
|
- `body` (any | null): Request body for POST/PUT requests. Default: null
|
|
158
|
+
- `connectTimeoutMs` (number): Connection timeout in milliseconds. Default: 30000
|
|
159
|
+
- `messageTimeoutMs` (number): Timeout for each message read in milliseconds. Default: 30000
|
|
131
160
|
|
|
132
161
|
**Note:** For detailed parameter descriptions, see [`makeHttpRequest`](#makehttprequest) above. This function only handles responses in the format `data: xxx\n\n`. It does not throw exceptions by default; success/failure is indicated in the generator's return value.
|
|
133
162
|
|
|
134
|
-
|
|
163
|
+
This method will **not** throw exceptions, you can get the success/failure status from the generator's return value.
|
|
164
|
+
|
|
165
|
+
**Returns:** `AsyncGenerator<string, boolean, undefined>` - An async generator that yields each message as a string, and returns a boolean indicating success (true) or failure (false)
|
|
135
166
|
|
|
136
167
|
**Example:**
|
|
137
168
|
|
|
@@ -144,7 +175,9 @@ async function streamChat() {
|
|
|
144
175
|
'POST',
|
|
145
176
|
null,
|
|
146
177
|
null,
|
|
147
|
-
{ message: 'Hello' }
|
|
178
|
+
{ message: 'Hello' },
|
|
179
|
+
30000, // connectTimeoutMs
|
|
180
|
+
10000 // messageTimeoutMs
|
|
148
181
|
);
|
|
149
182
|
|
|
150
183
|
// Manually iterate to receive chunks
|
|
@@ -166,6 +199,14 @@ async function streamChat() {
|
|
|
166
199
|
}
|
|
167
200
|
```
|
|
168
201
|
|
|
202
|
+
## Test
|
|
203
|
+
|
|
204
|
+
Run tests using:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
npm test
|
|
208
|
+
```
|
|
209
|
+
|
|
169
210
|
## License
|
|
170
211
|
|
|
171
212
|
MIT
|
package/dist/http_client.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ExtendedResponse } from './response_ext';
|
|
1
2
|
/**
|
|
2
3
|
* 拼接URL和查询参数,会自动处理转义
|
|
3
4
|
* @param url 基础URL,不要带任何查询参数
|
|
@@ -15,9 +16,9 @@ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD';
|
|
|
15
16
|
* @param customHeaders 自定义请求头,不需要写content-type这种会被自动处理的头
|
|
16
17
|
* @param body 请求体,适用于POST/PUT请求,传入字符串、数字会被处理成text/plain,传入对象会被处理成application/json,传入FormData会被处理成multipart/form-data,目前不建议直接发送二进制对象
|
|
17
18
|
* @param timeoutMs 超时时间,单位毫秒,默认5秒。这个超时的时间指的是发出请求,到接收到请求头的时间,不包含读取请求体的时间。如果需要控制读取请求体的超时,请自行用Promise.race实现。
|
|
18
|
-
* @returns 返回Fetch API的Response
|
|
19
|
+
* @returns 返回Fetch API的Response对象,用法完全一样,但是会被包装成ExtendedResponse,增加了一些方法
|
|
19
20
|
*/
|
|
20
|
-
export declare function makeHttpRequest(url: string, method?: HttpMethod, queryParams?: Record<string, string | number> | null, customHeaders?: Record<string, string> | null, body?: any | null, timeoutMs?: number): Promise<
|
|
21
|
+
export declare function makeHttpRequest(url: string, method?: HttpMethod, queryParams?: Record<string, string | number> | null, customHeaders?: Record<string, string> | null, body?: any | null, timeoutMs?: number): Promise<ExtendedResponse>;
|
|
21
22
|
/**
|
|
22
23
|
* 发起一个SSE请求,返回一个异步生成器,每次迭代返回一个字符串(流式响应)
|
|
23
24
|
* 这个方法是拿来补充浏览器里面的EventSource的,扩展了很多没有的功能,跟原版的SSE协议不完全兼容。
|
package/dist/http_client.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { sleep, TIMEOUT_MARKER } from './timer';
|
|
1
|
+
import { sleep, TIMEOUT_MARKER, DEFAULT_TIMEOUT } from './timer';
|
|
2
|
+
import { ExtendedResponse } from './response_ext';
|
|
3
|
+
import { extractSSELine } from './parser';
|
|
2
4
|
/**
|
|
3
5
|
* 拼接URL和查询参数,会自动处理转义
|
|
4
6
|
* @param url 基础URL,不要带任何查询参数
|
|
@@ -20,8 +22,6 @@ export function joinUrlWithParams(url, queryParams) {
|
|
|
20
22
|
});
|
|
21
23
|
return `${url}?${params.toString()}`;
|
|
22
24
|
}
|
|
23
|
-
// 默认超时时间,单位毫秒
|
|
24
|
-
const DEFAULT_TIMEOUT = 5000;
|
|
25
25
|
/**
|
|
26
26
|
* 发起一个带有默认配置的 HTTP 请求
|
|
27
27
|
* 默认不处理异常,需要try catch
|
|
@@ -31,7 +31,7 @@ const DEFAULT_TIMEOUT = 5000;
|
|
|
31
31
|
* @param customHeaders 自定义请求头,不需要写content-type这种会被自动处理的头
|
|
32
32
|
* @param body 请求体,适用于POST/PUT请求,传入字符串、数字会被处理成text/plain,传入对象会被处理成application/json,传入FormData会被处理成multipart/form-data,目前不建议直接发送二进制对象
|
|
33
33
|
* @param timeoutMs 超时时间,单位毫秒,默认5秒。这个超时的时间指的是发出请求,到接收到请求头的时间,不包含读取请求体的时间。如果需要控制读取请求体的超时,请自行用Promise.race实现。
|
|
34
|
-
* @returns 返回Fetch API的Response
|
|
34
|
+
* @returns 返回Fetch API的Response对象,用法完全一样,但是会被包装成ExtendedResponse,增加了一些方法
|
|
35
35
|
*/
|
|
36
36
|
export async function makeHttpRequest(url, method = 'GET', queryParams = null, customHeaders = null, body = null, timeoutMs = DEFAULT_TIMEOUT) {
|
|
37
37
|
// 处理查询参数
|
|
@@ -71,7 +71,7 @@ export async function makeHttpRequest(url, method = 'GET', queryParams = null, c
|
|
|
71
71
|
signal: controller.signal,
|
|
72
72
|
});
|
|
73
73
|
clearTimeout(timeoutId);
|
|
74
|
-
return resp;
|
|
74
|
+
return ExtendedResponse.create(resp);
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
77
|
* 发起一个SSE请求,返回一个异步生成器,每次迭代返回一个字符串(流式响应)
|
|
@@ -85,16 +85,18 @@ export async function makeHttpRequest(url, method = 'GET', queryParams = null, c
|
|
|
85
85
|
*/
|
|
86
86
|
export async function* makeSSERequest(url, method = 'GET', queryParams = null, customHeaders = null, body = null, connectTimeoutMs = 30000, messageTimeoutMs = 30000) {
|
|
87
87
|
var _a;
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return false;
|
|
91
|
-
}
|
|
92
|
-
const reader = (_a = resp.body) === null || _a === void 0 ? void 0 : _a.getReader();
|
|
93
|
-
if (reader == null) {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
let buffer = new Uint8Array(0);
|
|
88
|
+
// 最后需要释放它的锁,所以需要写出来
|
|
89
|
+
let reader = void 0;
|
|
97
90
|
try {
|
|
91
|
+
const resp = await makeHttpRequest(url, method, queryParams, customHeaders, body, connectTimeoutMs);
|
|
92
|
+
if (!resp.ok) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
reader = (_a = resp.body) === null || _a === void 0 ? void 0 : _a.getReader();
|
|
96
|
+
if (reader == null) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
let buffer = new Uint8Array(0);
|
|
98
100
|
while (true) {
|
|
99
101
|
const raceResult = await Promise.race([
|
|
100
102
|
sleep(messageTimeoutMs),
|
|
@@ -157,36 +159,9 @@ export async function* makeSSERequest(url, method = 'GET', queryParams = null, c
|
|
|
157
159
|
return false;
|
|
158
160
|
}
|
|
159
161
|
finally {
|
|
160
|
-
reader
|
|
161
|
-
|
|
162
|
-
return true;
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* 解析SSE响应中的一行数据
|
|
166
|
-
* @param buffer 需要处理的数组(只读,该函数不会修改它)
|
|
167
|
-
* @returns 返回解析到的一行数据的结束索引,如果没有完整的一行数据,返回null。这个值指向最后一个\n的位置,slice的时候注意坐标运算
|
|
168
|
-
*/
|
|
169
|
-
function extractSSELine(buffer) {
|
|
170
|
-
if (buffer.length < 8) {
|
|
171
|
-
// 每行数据至少都有'data: \n\n',不够8个字节一定是不完整的
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
// 判断开头的6个字节是否是'data: '
|
|
175
|
-
if (buffer[0] !== 0x64 ||
|
|
176
|
-
buffer[1] !== 0x61 ||
|
|
177
|
-
buffer[2] !== 0x74 ||
|
|
178
|
-
buffer[3] !== 0x61 ||
|
|
179
|
-
buffer[4] !== 0x3A ||
|
|
180
|
-
buffer[5] !== 0x20) {
|
|
181
|
-
// 不是的话就有问题了
|
|
182
|
-
throw new Error('SSE data is not valid');
|
|
183
|
-
}
|
|
184
|
-
// 查找\n\n的位置
|
|
185
|
-
for (let i = 0; i < buffer.length - 1; ++i) {
|
|
186
|
-
if (buffer[i] === 0x0A && buffer[i + 1] === 0x0A) {
|
|
187
|
-
return i + 1;
|
|
162
|
+
if (reader != null) {
|
|
163
|
+
reader.releaseLock();
|
|
188
164
|
}
|
|
189
165
|
}
|
|
190
|
-
|
|
191
|
-
return null;
|
|
166
|
+
return true;
|
|
192
167
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/parser.d.ts
ADDED
package/dist/parser.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 解析SSE响应中的一行数据
|
|
3
|
+
* @param buffer 需要处理的数组(只读,该函数不会修改它)
|
|
4
|
+
* @returns 返回解析到的一行数据的结束索引,如果没有完整的一行数据,返回null。这个值指向最后一个\n的位置,slice的时候注意坐标运算
|
|
5
|
+
*/
|
|
6
|
+
export function extractSSELine(buffer) {
|
|
7
|
+
if (buffer.length < 8) {
|
|
8
|
+
// 每行数据至少都有'data: \n\n',不够8个字节一定是不完整的
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
// 判断开头的6个字节是否是'data: '
|
|
12
|
+
if (buffer[0] !== 0x64 ||
|
|
13
|
+
buffer[1] !== 0x61 ||
|
|
14
|
+
buffer[2] !== 0x74 ||
|
|
15
|
+
buffer[3] !== 0x61 ||
|
|
16
|
+
buffer[4] !== 0x3A ||
|
|
17
|
+
buffer[5] !== 0x20) {
|
|
18
|
+
// 不是的话就有问题了
|
|
19
|
+
throw new Error('SSE data is not valid');
|
|
20
|
+
}
|
|
21
|
+
// 查找\n\n的位置
|
|
22
|
+
for (let i = 0; i < buffer.length - 1; ++i) {
|
|
23
|
+
if (buffer[i] === 0x0A && buffer[i + 1] === 0x0A) {
|
|
24
|
+
return i + 1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// 没有找到\n\n,返回null
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 扩展的Response类,使用Proxy自动代理所有Response属性和方法
|
|
3
|
+
* 用户可以像普通的Response对象一样使用,也可以调用额外的方法
|
|
4
|
+
*/
|
|
5
|
+
export declare class ExtendedResponse implements Response {
|
|
6
|
+
private _response;
|
|
7
|
+
private constructor();
|
|
8
|
+
get [Symbol.toStringTag](): string;
|
|
9
|
+
static create(response: Response): ExtendedResponse;
|
|
10
|
+
clone(): ExtendedResponse;
|
|
11
|
+
/**
|
|
12
|
+
* 从响应体中读取JSON数据,支持超时设置
|
|
13
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
14
|
+
* @returns 解析后的JSON数据
|
|
15
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
16
|
+
*/
|
|
17
|
+
jsonWithTimeout<T>(readTimeoutMs?: number): Promise<T>;
|
|
18
|
+
/**
|
|
19
|
+
* 从响应体中读取文本数据,支持超时设置
|
|
20
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
21
|
+
* @returns 解析后的文本数据
|
|
22
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
23
|
+
*/
|
|
24
|
+
textWithTimeout(readTimeoutMs?: number): Promise<string>;
|
|
25
|
+
/**
|
|
26
|
+
* 从响应体中读取数组缓冲区数据,支持超时设置
|
|
27
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
28
|
+
* @returns 解析后的数组缓冲区数据
|
|
29
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
30
|
+
*/
|
|
31
|
+
arrayBufferWithTimeout(readTimeoutMs?: number): Promise<ArrayBuffer>;
|
|
32
|
+
/**
|
|
33
|
+
* 从响应体中读取Blob数据,支持超时设置
|
|
34
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
35
|
+
* @returns 解析后的Blob数据
|
|
36
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
37
|
+
*/
|
|
38
|
+
blobWithTimeout(readTimeoutMs?: number): Promise<Blob>;
|
|
39
|
+
/**
|
|
40
|
+
* 从响应体中读取字节数据,支持超时设置
|
|
41
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
42
|
+
* @returns 解析后的字节数据
|
|
43
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
44
|
+
*/
|
|
45
|
+
bytesWithTimeout(readTimeoutMs?: number): Promise<Uint8Array<ArrayBuffer>>;
|
|
46
|
+
readonly body: ReadableStream<Uint8Array<ArrayBuffer>> | null;
|
|
47
|
+
readonly bodyUsed: boolean;
|
|
48
|
+
readonly headers: Headers;
|
|
49
|
+
readonly ok: boolean;
|
|
50
|
+
readonly redirected: boolean;
|
|
51
|
+
readonly status: number;
|
|
52
|
+
readonly statusText: string;
|
|
53
|
+
readonly type: ResponseType;
|
|
54
|
+
readonly url: string;
|
|
55
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
56
|
+
blob(): Promise<Blob>;
|
|
57
|
+
bytes(): Promise<Uint8Array<ArrayBuffer>>;
|
|
58
|
+
formData(): Promise<FormData>;
|
|
59
|
+
json(): Promise<any>;
|
|
60
|
+
text(): Promise<string>;
|
|
61
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { sleep, TIMEOUT_MARKER, DEFAULT_TIMEOUT } from "./timer";
|
|
2
|
+
/**
|
|
3
|
+
* 扩展的Response类,使用Proxy自动代理所有Response属性和方法
|
|
4
|
+
* 用户可以像普通的Response对象一样使用,也可以调用额外的方法
|
|
5
|
+
*/
|
|
6
|
+
export class ExtendedResponse {
|
|
7
|
+
// 私有构造函数,通过静态方法创建实例
|
|
8
|
+
constructor(response) {
|
|
9
|
+
this._response = response;
|
|
10
|
+
}
|
|
11
|
+
// Symbol.toStringTag 用于正确的类型识别
|
|
12
|
+
get [Symbol.toStringTag]() {
|
|
13
|
+
return 'ExtendedResponse';
|
|
14
|
+
}
|
|
15
|
+
// 静态工厂方法,创建代理后的ExtendedResponse实例
|
|
16
|
+
static create(response) {
|
|
17
|
+
const instance = new ExtendedResponse(response);
|
|
18
|
+
// 使用Proxy自动代理所有属性访问
|
|
19
|
+
return new Proxy(instance, {
|
|
20
|
+
get(target, prop) {
|
|
21
|
+
// 如果是ExtendedResponse自定义的属性或方法,优先从target获取
|
|
22
|
+
switch (prop) {
|
|
23
|
+
case '_response':
|
|
24
|
+
case 'create':
|
|
25
|
+
case 'clone':
|
|
26
|
+
case 'jsonWithTimeout':
|
|
27
|
+
case 'textWithTimeout':
|
|
28
|
+
case 'arrayBufferWithTimeout':
|
|
29
|
+
case 'blobWithTimeout':
|
|
30
|
+
case 'bytesWithTimeout':
|
|
31
|
+
return target[prop];
|
|
32
|
+
default:
|
|
33
|
+
// 否则从原始Response获取
|
|
34
|
+
const value = target._response[prop];
|
|
35
|
+
// 如果是函数,需要绑定this
|
|
36
|
+
return typeof value === 'function' ? value.bind(target._response) : value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// clone方法要特殊处理
|
|
42
|
+
clone() {
|
|
43
|
+
return ExtendedResponse.create(this._response.clone());
|
|
44
|
+
}
|
|
45
|
+
// 以下是一些额外的方法
|
|
46
|
+
/**
|
|
47
|
+
* 从响应体中读取JSON数据,支持超时设置
|
|
48
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
49
|
+
* @returns 解析后的JSON数据
|
|
50
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
51
|
+
*/
|
|
52
|
+
async jsonWithTimeout(readTimeoutMs = DEFAULT_TIMEOUT) {
|
|
53
|
+
const raceResult = await Promise.race([
|
|
54
|
+
sleep(readTimeoutMs),
|
|
55
|
+
this._response.json(),
|
|
56
|
+
]);
|
|
57
|
+
// 如果timeout先完成,说明超时了
|
|
58
|
+
if (raceResult === TIMEOUT_MARKER) {
|
|
59
|
+
throw new Error(`JSON read timeout after ${readTimeoutMs}ms`);
|
|
60
|
+
}
|
|
61
|
+
return raceResult;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 从响应体中读取文本数据,支持超时设置
|
|
65
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
66
|
+
* @returns 解析后的文本数据
|
|
67
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
68
|
+
*/
|
|
69
|
+
async textWithTimeout(readTimeoutMs = DEFAULT_TIMEOUT) {
|
|
70
|
+
const raceResult = await Promise.race([
|
|
71
|
+
sleep(readTimeoutMs),
|
|
72
|
+
this._response.text(),
|
|
73
|
+
]);
|
|
74
|
+
// 如果timeout先完成,说明超时了
|
|
75
|
+
if (raceResult === TIMEOUT_MARKER) {
|
|
76
|
+
throw new Error(`Text read timeout after ${readTimeoutMs}ms`);
|
|
77
|
+
}
|
|
78
|
+
return raceResult;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 从响应体中读取数组缓冲区数据,支持超时设置
|
|
82
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
83
|
+
* @returns 解析后的数组缓冲区数据
|
|
84
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
85
|
+
*/
|
|
86
|
+
async arrayBufferWithTimeout(readTimeoutMs = DEFAULT_TIMEOUT) {
|
|
87
|
+
const raceResult = await Promise.race([
|
|
88
|
+
sleep(readTimeoutMs),
|
|
89
|
+
this._response.arrayBuffer(),
|
|
90
|
+
]);
|
|
91
|
+
// 如果timeout先完成,说明超时了
|
|
92
|
+
if (raceResult === TIMEOUT_MARKER) {
|
|
93
|
+
throw new Error(`ArrayBuffer read timeout after ${readTimeoutMs}ms`);
|
|
94
|
+
}
|
|
95
|
+
return raceResult;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* 从响应体中读取Blob数据,支持超时设置
|
|
99
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
100
|
+
* @returns 解析后的Blob数据
|
|
101
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
102
|
+
*/
|
|
103
|
+
async blobWithTimeout(readTimeoutMs = DEFAULT_TIMEOUT) {
|
|
104
|
+
const raceResult = await Promise.race([
|
|
105
|
+
sleep(readTimeoutMs),
|
|
106
|
+
this._response.blob(),
|
|
107
|
+
]);
|
|
108
|
+
// 如果timeout先完成,说明超时了
|
|
109
|
+
if (raceResult === TIMEOUT_MARKER) {
|
|
110
|
+
throw new Error(`Blob read timeout after ${readTimeoutMs}ms`);
|
|
111
|
+
}
|
|
112
|
+
return raceResult;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 从响应体中读取字节数据,支持超时设置
|
|
116
|
+
* @param readTimeoutMs 读取请求体的超时时间,单位毫秒
|
|
117
|
+
* @returns 解析后的字节数据
|
|
118
|
+
* @throws 如果读取超时或解析失败,会抛出错误
|
|
119
|
+
*/
|
|
120
|
+
async bytesWithTimeout(readTimeoutMs = DEFAULT_TIMEOUT) {
|
|
121
|
+
const raceResult = await Promise.race([
|
|
122
|
+
sleep(readTimeoutMs),
|
|
123
|
+
this._response.bytes(),
|
|
124
|
+
]);
|
|
125
|
+
// 如果timeout先完成,说明超时了
|
|
126
|
+
if (raceResult === TIMEOUT_MARKER) {
|
|
127
|
+
throw new Error(`Bytes read timeout after ${readTimeoutMs}ms`);
|
|
128
|
+
}
|
|
129
|
+
return raceResult;
|
|
130
|
+
}
|
|
131
|
+
// Response接口的方法(实际通过Proxy代理到原始Response,这里的方法都也是骗编译器的)
|
|
132
|
+
arrayBuffer() {
|
|
133
|
+
throw new Error('Method not implemented.');
|
|
134
|
+
}
|
|
135
|
+
blob() {
|
|
136
|
+
throw new Error('Method not implemented.');
|
|
137
|
+
}
|
|
138
|
+
bytes() {
|
|
139
|
+
throw new Error('Method not implemented.');
|
|
140
|
+
}
|
|
141
|
+
formData() {
|
|
142
|
+
throw new Error('Method not implemented.');
|
|
143
|
+
}
|
|
144
|
+
json() {
|
|
145
|
+
throw new Error('Method not implemented.');
|
|
146
|
+
}
|
|
147
|
+
text() {
|
|
148
|
+
throw new Error('Method not implemented.');
|
|
149
|
+
}
|
|
150
|
+
}
|
package/dist/timer.d.ts
CHANGED
package/dist/timer.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@czf0613/http_client",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2",
|
|
5
5
|
"description": "Simple http client with fetch, supporting modified SSE protocol",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"http client",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "tsc",
|
|
23
23
|
"prepack": "npm run build",
|
|
24
|
-
"
|
|
24
|
+
"pretest": "npm run build",
|
|
25
|
+
"test": "npx tsx tests/run_all.ts"
|
|
25
26
|
},
|
|
26
27
|
"repository": {
|
|
27
28
|
"type": "git",
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
"license": "MIT",
|
|
33
34
|
"homepage": "https://github.com/czf0613/http_client_ts#readme",
|
|
34
35
|
"devDependencies": {
|
|
36
|
+
"@types/node": "^25.3.3",
|
|
35
37
|
"typescript": "^5.9.3"
|
|
36
38
|
}
|
|
37
39
|
}
|