@czf0613/http_client 0.1.1 → 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 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
@@ -70,9 +72,34 @@ Makes an HTTP request with default configuration using the Fetch API.
70
72
  - FormData: sent as `multipart/form-data`
71
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.**
72
74
 
73
- **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.
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
93
+
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
97
+
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
74
101
 
75
- **Note:** This function does not handle exceptions by default. You should wrap it in a try-catch block.
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
 
@@ -135,7 +162,7 @@ Makes an SSE (Server-Sent Events) request and returns an async generator that yi
135
162
 
136
163
  This method will **not** throw exceptions, you can get the success/failure status from the generator's return value.
137
164
 
138
- **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)
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)
139
166
 
140
167
  **Example:**
141
168
 
@@ -1,5 +1,6 @@
1
1
  import { sleep, TIMEOUT_MARKER, DEFAULT_TIMEOUT } from './timer';
2
2
  import { ExtendedResponse } from './response_ext';
3
+ import { extractSSELine } from './parser';
3
4
  /**
4
5
  * 拼接URL和查询参数,会自动处理转义
5
6
  * @param url 基础URL,不要带任何查询参数
@@ -164,32 +165,3 @@ export async function* makeSSERequest(url, method = 'GET', queryParams = null, c
164
165
  }
165
166
  return true;
166
167
  }
167
- /**
168
- * 解析SSE响应中的一行数据
169
- * @param buffer 需要处理的数组(只读,该函数不会修改它)
170
- * @returns 返回解析到的一行数据的结束索引,如果没有完整的一行数据,返回null。这个值指向最后一个\n的位置,slice的时候注意坐标运算
171
- */
172
- function extractSSELine(buffer) {
173
- if (buffer.length < 8) {
174
- // 每行数据至少都有'data: \n\n',不够8个字节一定是不完整的
175
- return null;
176
- }
177
- // 判断开头的6个字节是否是'data: '
178
- if (buffer[0] !== 0x64 ||
179
- buffer[1] !== 0x61 ||
180
- buffer[2] !== 0x74 ||
181
- buffer[3] !== 0x61 ||
182
- buffer[4] !== 0x3A ||
183
- buffer[5] !== 0x20) {
184
- // 不是的话就有问题了
185
- throw new Error('SSE data is not valid');
186
- }
187
- // 查找\n\n的位置
188
- for (let i = 0; i < buffer.length - 1; ++i) {
189
- if (buffer[i] === 0x0A && buffer[i + 1] === 0x0A) {
190
- return i + 1;
191
- }
192
- }
193
- // 没有找到\n\n,返回null
194
- return null;
195
- }
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { joinUrlWithParams, makeHttpRequest, makeSSERequest } from './http_client';
2
+ export { type ExtendedResponse } from './response_ext';
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 解析SSE响应中的一行数据
3
+ * @param buffer 需要处理的数组(只读,该函数不会修改它)
4
+ * @returns 返回解析到的一行数据的结束索引,如果没有完整的一行数据,返回null。这个值指向最后一个\n的位置,slice的时候注意坐标运算
5
+ */
6
+ export declare function extractSSELine(buffer: Uint8Array): number | null;
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
+ }
@@ -8,6 +8,41 @@ export declare class ExtendedResponse implements Response {
8
8
  get [Symbol.toStringTag](): string;
9
9
  static create(response: Response): ExtendedResponse;
10
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>>;
11
46
  readonly body: ReadableStream<Uint8Array<ArrayBuffer>> | null;
12
47
  readonly bodyUsed: boolean;
13
48
  readonly headers: Headers;
@@ -1,3 +1,4 @@
1
+ import { sleep, TIMEOUT_MARKER, DEFAULT_TIMEOUT } from "./timer";
1
2
  /**
2
3
  * 扩展的Response类,使用Proxy自动代理所有Response属性和方法
3
4
  * 用户可以像普通的Response对象一样使用,也可以调用额外的方法
@@ -22,6 +23,11 @@ export class ExtendedResponse {
22
23
  case '_response':
23
24
  case 'create':
24
25
  case 'clone':
26
+ case 'jsonWithTimeout':
27
+ case 'textWithTimeout':
28
+ case 'arrayBufferWithTimeout':
29
+ case 'blobWithTimeout':
30
+ case 'bytesWithTimeout':
25
31
  return target[prop];
26
32
  default:
27
33
  // 否则从原始Response获取
@@ -36,6 +42,92 @@ export class ExtendedResponse {
36
42
  clone() {
37
43
  return ExtendedResponse.create(this._response.clone());
38
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
+ }
39
131
  // Response接口的方法(实际通过Proxy代理到原始Response,这里的方法都也是骗编译器的)
40
132
  arrayBuffer() {
41
133
  throw new Error('Method not implemented.');
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@czf0613/http_client",
3
3
  "private": false,
4
- "version": "0.1.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",
@@ -20,8 +20,9 @@
20
20
  },
21
21
  "scripts": {
22
22
  "build": "tsc",
23
+ "prepack": "npm run build",
23
24
  "pretest": "npm run build",
24
- "test": "npx tsx tests/test_bing.ts"
25
+ "test": "npx tsx tests/run_all.ts"
25
26
  },
26
27
  "repository": {
27
28
  "type": "git",