@czf0613/http_client 0.0.1 → 0.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 CHANGED
@@ -1 +1,171 @@
1
- # http_client_ts
1
+ # @czf0613/http_client
2
+
3
+ A simple HTTP client library built with fetch API, supporting a modified SSE (Server-Sent Events) protocol for browser environments.
4
+
5
+ ## Requirements
6
+
7
+ - **Runtime**: ES2018+ compatible browsers
8
+ - **Module System**: ES Module only (not compatible with CommonJS)
9
+ - **Environment**: Browser only (Chrome, Firefox, Safari)
10
+ - **Browser Support**: Works well on all major modern browsers
11
+
12
+ ## Installation
13
+
14
+ Install the package using your preferred package manager:
15
+
16
+ ```bash
17
+ # npm
18
+ npm install @czf0613/http_client
19
+
20
+ # pnpm
21
+ pnpm add @czf0613/http_client
22
+
23
+ # yarn
24
+ yarn add @czf0613/http_client
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### `joinUrlWithParams`
30
+
31
+ Concatenates a URL with query parameters, automatically handling URL encoding.
32
+
33
+ **Parameters:**
34
+ - `url` (string): The base URL without any query parameters
35
+ - `queryParams` (Record<string, string | number | boolean>): Query parameters as key-value pairs
36
+
37
+ **Returns:** string - The complete URL with query parameters
38
+
39
+ **Example:**
40
+
41
+ ```javascript
42
+ import { joinUrlWithParams } from '@czf0613/http_client';
43
+
44
+ const url = joinUrlWithParams('https://api.example.com/users', {
45
+ page: 1,
46
+ limit: 10,
47
+ search: 'hello world'
48
+ });
49
+ // Result: 'https://api.example.com/users?page=1&limit=10&search=hello%20world'
50
+
51
+ // Automatic URL encoding for special characters
52
+ const url2 = joinUrlWithParams('https://api.example.com/search', {
53
+ keyword: '你好世界'
54
+ });
55
+ // Result: 'https://api.example.com/search?keyword=%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C'
56
+ ```
57
+
58
+ ### `makeHttpRequest`
59
+
60
+ Makes an HTTP request with default configuration using the Fetch API.
61
+
62
+ **Parameters:**
63
+ - `url` (string): The request URL (without query parameters)
64
+ - `method` (HttpMethod): HTTP method ('GET', 'POST', 'PUT', 'DELETE', 'HEAD'). Default: 'GET'
65
+ - `queryParams` (Record<string, string | number> | null): Query parameters to be appended to the URL. Default: null
66
+ - `customHeaders` (Record<string, string> | null): Custom headers (Content-Type is handled automatically). **Important: Do NOT include Content-Type in customHeaders as it will interfere with the automatic Content-Type generation.** Default: null
67
+ - `body` (any | null): Request body for POST/PUT requests. Default: null
68
+ - String or number: sent as `text/plain`
69
+ - Object: sent as `application/json`
70
+ - FormData: sent as `multipart/form-data`
71
+ - `timeoutMs` (number): Timeout in milliseconds. Default: 5000
72
+
73
+ **Returns:** Promise<Response> - Fetch API Response object
74
+
75
+ **Note:** This function does not handle exceptions by default. You should wrap it in a try-catch block.
76
+
77
+ **Example:**
78
+
79
+ ```javascript
80
+ import { makeHttpRequest } from '@czf0613/http_client';
81
+
82
+ // GET request with query parameters
83
+ const response = await makeHttpRequest(
84
+ 'https://api.example.com/users',
85
+ 'GET',
86
+ { page: 1, limit: 10 }
87
+ );
88
+ const data = await response.json();
89
+
90
+ // POST request with JSON body
91
+ const response = await makeHttpRequest(
92
+ 'https://api.example.com/users',
93
+ 'POST',
94
+ null,
95
+ null,
96
+ { name: 'John', age: 30 }
97
+ );
98
+
99
+ // POST request with FormData
100
+ const formData = new FormData();
101
+ formData.append('file', fileInput.files[0]);
102
+ const response = await makeHttpRequest(
103
+ 'https://api.example.com/upload',
104
+ 'POST',
105
+ null,
106
+ null,
107
+ formData
108
+ );
109
+
110
+ // Custom timeout
111
+ const response = await makeHttpRequest(
112
+ 'https://api.example.com/slow-endpoint',
113
+ 'GET',
114
+ null,
115
+ null,
116
+ null,
117
+ 10000 // 10 seconds timeout
118
+ );
119
+ ```
120
+
121
+ ### `makeSSERequest`
122
+
123
+ Makes an SSE (Server-Sent Events) request and returns an async generator that yields streaming responses. This is an enhanced version of the browser's EventSource with additional features, but it's not fully compatible with the standard SSE protocol.
124
+
125
+ **Parameters:**
126
+ - `url` (string): The request URL (without query parameters)
127
+ - `method` (HttpMethod): HTTP method ('GET', 'POST', 'PUT', 'DELETE', 'HEAD'). Default: 'GET'
128
+ - `queryParams` (Record<string, string | number> | null): Query parameters to be appended to the URL. Default: null
129
+ - `customHeaders` (Record<string, string> | null): Custom headers. **Important: Do NOT include Content-Type in customHeaders.** Default: null
130
+ - `body` (any | null): Request body for POST/PUT requests. Default: null
131
+
132
+ **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
+
134
+ **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
+
136
+ **Example:**
137
+
138
+ ```javascript
139
+ import { makeSSERequest } from '@czf0613/http_client';
140
+
141
+ async function streamChat() {
142
+ const generator = makeSSERequest(
143
+ 'https://api.example.com/chat',
144
+ 'POST',
145
+ null,
146
+ null,
147
+ { message: 'Hello' }
148
+ );
149
+
150
+ // Manually iterate to receive chunks
151
+ while(true) {
152
+ const { done, value } = await generator.next();
153
+
154
+ if (done) {
155
+ // Check if the stream completed successfully
156
+ if (!value) {
157
+ console.error('SSE stream failed');
158
+ }
159
+
160
+ break;
161
+ }
162
+
163
+ // Get the message value
164
+ console.log('Received:', value);
165
+ }
166
+ }
167
+ ```
168
+
169
+ ## License
170
+
171
+ MIT
@@ -14,6 +14,7 @@ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD';
14
14
  * @param queryParams 查询参数,会被自动拼接到url中(会自动进行转义)
15
15
  * @param customHeaders 自定义请求头,不需要写content-type这种会被自动处理的头
16
16
  * @param body 请求体,适用于POST/PUT请求,传入字符串、数字会被处理成text/plain,传入对象会被处理成application/json,传入FormData会被处理成multipart/form-data,目前不建议直接发送二进制对象
17
+ * @param timeoutMs 超时时间,单位毫秒,默认5秒。这个超时的时间指的是发出请求,到接收到请求头的时间,不包含读取请求体的时间。如果需要控制读取请求体的超时,请自行用Promise.race实现。
17
18
  * @returns 返回Fetch API的Response对象
18
19
  */
19
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<Response>;
@@ -23,7 +24,9 @@ export declare function makeHttpRequest(url: string, method?: HttpMethod, queryP
23
24
  * 目前这个接口只支持处理后端不停发送data: xxx\n\n这种格式的响应
24
25
  * 默认不抛出异常,会在生成器的返回值里面指明成功还是失败
25
26
  * @see makeHttpRequest 这里的参数说明
27
+ * @param connectTimeoutMs 连接超时时间,单位毫秒,默认30秒
28
+ * @param messageTimeoutMs 每条消息超时时间,单位毫秒,默认30秒
26
29
  * @returns 返回一个异步生成器,每次迭代返回一个字符串(流式响应)
27
30
  */
28
- export declare function makeSSERequest(url: string, method?: HttpMethod, queryParams?: Record<string, string | number> | null, customHeaders?: Record<string, string> | null, body?: any | null): AsyncGenerator<string, boolean, undefined>;
31
+ export declare function makeSSERequest(url: string, method?: HttpMethod, queryParams?: Record<string, string | number> | null, customHeaders?: Record<string, string> | null, body?: any | null, connectTimeoutMs?: number, messageTimeoutMs?: number): AsyncGenerator<string, boolean, undefined>;
29
32
  export {};
@@ -1,3 +1,4 @@
1
+ import { sleep, TIMEOUT_MARKER } from './timer';
1
2
  /**
2
3
  * 拼接URL和查询参数,会自动处理转义
3
4
  * @param url 基础URL,不要带任何查询参数
@@ -29,6 +30,7 @@ const DEFAULT_TIMEOUT = 5000;
29
30
  * @param queryParams 查询参数,会被自动拼接到url中(会自动进行转义)
30
31
  * @param customHeaders 自定义请求头,不需要写content-type这种会被自动处理的头
31
32
  * @param body 请求体,适用于POST/PUT请求,传入字符串、数字会被处理成text/plain,传入对象会被处理成application/json,传入FormData会被处理成multipart/form-data,目前不建议直接发送二进制对象
33
+ * @param timeoutMs 超时时间,单位毫秒,默认5秒。这个超时的时间指的是发出请求,到接收到请求头的时间,不包含读取请求体的时间。如果需要控制读取请求体的超时,请自行用Promise.race实现。
32
34
  * @returns 返回Fetch API的Response对象
33
35
  */
34
36
  export async function makeHttpRequest(url, method = 'GET', queryParams = null, customHeaders = null, body = null, timeoutMs = DEFAULT_TIMEOUT) {
@@ -44,7 +46,9 @@ export async function makeHttpRequest(url, method = 'GET', queryParams = null, c
44
46
  // 无body时,不设置Content-Type
45
47
  }
46
48
  else if (body instanceof FormData) {
47
- // 使用FormData时,浏览器会自动设置Content-Type和boundary,不要多手
49
+ // 使用FormData时,浏览器会自动设置Content-Type和boundary,不要多手,有我也得给你删了
50
+ delete customHeaders['Content-Type'];
51
+ delete customHeaders['content-type'];
48
52
  }
49
53
  else if (typeof body === 'string') {
50
54
  customHeaders['Content-Type'] = 'text/plain';
@@ -75,11 +79,13 @@ export async function makeHttpRequest(url, method = 'GET', queryParams = null, c
75
79
  * 目前这个接口只支持处理后端不停发送data: xxx\n\n这种格式的响应
76
80
  * 默认不抛出异常,会在生成器的返回值里面指明成功还是失败
77
81
  * @see makeHttpRequest 这里的参数说明
82
+ * @param connectTimeoutMs 连接超时时间,单位毫秒,默认30秒
83
+ * @param messageTimeoutMs 每条消息超时时间,单位毫秒,默认30秒
78
84
  * @returns 返回一个异步生成器,每次迭代返回一个字符串(流式响应)
79
85
  */
80
- export async function* makeSSERequest(url, method = 'GET', queryParams = null, customHeaders = null, body = null) {
86
+ export async function* makeSSERequest(url, method = 'GET', queryParams = null, customHeaders = null, body = null, connectTimeoutMs = 30000, messageTimeoutMs = 30000) {
81
87
  var _a;
82
- const resp = await makeHttpRequest(url, method, queryParams, customHeaders, body, 30000);
88
+ const resp = await makeHttpRequest(url, method, queryParams, customHeaders, body, connectTimeoutMs);
83
89
  if (!resp.ok) {
84
90
  return false;
85
91
  }
@@ -90,7 +96,15 @@ export async function* makeSSERequest(url, method = 'GET', queryParams = null, c
90
96
  let buffer = new Uint8Array(0);
91
97
  try {
92
98
  while (true) {
93
- const { done, value } = await reader.read();
99
+ const raceResult = await Promise.race([
100
+ sleep(messageTimeoutMs),
101
+ reader.read(),
102
+ ]);
103
+ // 如果timeout先完成,说明超时了
104
+ if (raceResult === TIMEOUT_MARKER) {
105
+ throw new Error('SSE message timeout');
106
+ }
107
+ const { done, value } = raceResult;
94
108
  if (done || !value) {
95
109
  break;
96
110
  }
@@ -0,0 +1,2 @@
1
+ export declare const TIMEOUT_MARKER: unique symbol;
2
+ export declare function sleep(ms: number): Promise<typeof TIMEOUT_MARKER>;
package/dist/timer.js ADDED
@@ -0,0 +1,4 @@
1
+ export const TIMEOUT_MARKER = Symbol('timeout');
2
+ export function sleep(ms) {
3
+ return new Promise(resolve => setTimeout(() => resolve(TIMEOUT_MARKER), ms));
4
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@czf0613/http_client",
3
3
  "private": false,
4
- "version": "0.0.1",
4
+ "version": "0.1.0",
5
5
  "description": "Simple http client with fetch, supporting modified SSE protocol",
6
6
  "keywords": [
7
7
  "http client",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "scripts": {
22
22
  "build": "tsc",
23
- "prepack": "rm -rf dist || true && npm run build",
23
+ "prepack": "npm run build",
24
24
  "test": "echo \"Error: no test specified\" && exit 1"
25
25
  },
26
26
  "repository": {