@ahoo-wang/fetcher-eventstream 0.1.6 → 0.2.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 +7 -9
- package/README.zh-CN.md +258 -0
- package/dist/eventStreamConverter.d.ts +1 -13
- package/dist/eventStreamConverter.d.ts.map +1 -1
- package/dist/eventStreamInterceptor.d.ts +3 -3
- package/dist/eventStreamInterceptor.d.ts.map +1 -1
- package/dist/index.es.js +55 -61
- package/dist/index.umd.js +4 -4
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-eventstream)
|
|
4
4
|
[](https://github.com/Ahoo-Wang/fetcher/actions)
|
|
5
|
-
[](https://codecov.io/gh/Ahoo-Wang/fetcher)
|
|
6
6
|
[](https://github.com/Ahoo-Wang/fetcher/blob/main/LICENSE)
|
|
7
7
|
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-eventstream)
|
|
8
8
|
|
|
@@ -15,8 +15,6 @@ Support for text/event-stream in Fetcher, enabling Server-Sent Events (SSE) func
|
|
|
15
15
|
- **SSE Parsing**: Parses Server-Sent Events according to the specification, including data, event, id, and retry fields
|
|
16
16
|
- **Streaming Support**: Handles chunked data and multi-line events correctly
|
|
17
17
|
- **Comment Handling**: Properly ignores comment lines (lines starting with `:`) as per SSE specification
|
|
18
|
-
- **Event ID Tracking**: Maintains last event ID for automatic reconnection
|
|
19
|
-
- **Retry Management**: Handles retry timeout values for connection reattempts
|
|
20
18
|
- **TypeScript Support**: Complete TypeScript type definitions
|
|
21
19
|
|
|
22
20
|
## Installation
|
|
@@ -66,11 +64,11 @@ if (response.eventStream) {
|
|
|
66
64
|
### Manual Conversion
|
|
67
65
|
|
|
68
66
|
```typescript
|
|
69
|
-
import {
|
|
67
|
+
import { toServerSentEventStream } from '@ahoo-wang/fetcher-eventstream';
|
|
70
68
|
|
|
71
69
|
// Convert a Response object manually
|
|
72
70
|
const response = await fetch('/events');
|
|
73
|
-
const eventStream =
|
|
71
|
+
const eventStream = toServerSentEventStream(response);
|
|
74
72
|
|
|
75
73
|
// Read events from the stream
|
|
76
74
|
const reader = eventStream.getReader();
|
|
@@ -122,7 +120,7 @@ if (response.eventStream) {
|
|
|
122
120
|
|
|
123
121
|
A utility class for converting `text/event-stream` responses to readable streams.
|
|
124
122
|
|
|
125
|
-
#### `
|
|
123
|
+
#### `toServerSentEventStream(response: Response): ServerEventStream`
|
|
126
124
|
|
|
127
125
|
Converts a Response object with a `text/event-stream` body to a readable stream of ServerSentEvent objects.
|
|
128
126
|
|
|
@@ -138,17 +136,17 @@ Converts a Response object with a `text/event-stream` body to a readable stream
|
|
|
138
136
|
|
|
139
137
|
A response interceptor that automatically adds an `eventStream()` method to responses with `text/event-stream` content type.
|
|
140
138
|
|
|
141
|
-
#### `intercept(
|
|
139
|
+
#### `intercept(exchange: FetchExchange): FetchExchange`
|
|
142
140
|
|
|
143
141
|
Intercepts a response and adds the `eventStream()` method if the content type is `text/event-stream`.
|
|
144
142
|
|
|
145
143
|
**Parameters:**
|
|
146
144
|
|
|
147
|
-
- `
|
|
145
|
+
- `exchange`: The fetch exchange containing the response to intercept
|
|
148
146
|
|
|
149
147
|
**Returns:**
|
|
150
148
|
|
|
151
|
-
- `
|
|
149
|
+
- `FetchExchange`: The intercepted exchange with response potentially modified to include `eventStream()` method
|
|
152
150
|
|
|
153
151
|
### ServerSentEvent
|
|
154
152
|
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# @ahoo-wang/fetcher-eventstream
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-eventstream)
|
|
4
|
+
[](https://github.com/Ahoo-Wang/fetcher/actions)
|
|
5
|
+
[](https://codecov.io/gh/Ahoo-Wang/fetcher)
|
|
6
|
+
[](https://github.com/Ahoo-Wang/fetcher/blob/main/LICENSE)
|
|
7
|
+
[](https://www.npmjs.com/package/@ahoo-wang/fetcher-eventstream)
|
|
8
|
+
|
|
9
|
+
为 Fetcher 提供 text/event-stream 支持,实现服务器发送事件(SSE)功能。
|
|
10
|
+
|
|
11
|
+
## 特性
|
|
12
|
+
|
|
13
|
+
- **事件流转换**:将 `text/event-stream` 响应转换为 `ServerSentEvent` 对象的异步生成器
|
|
14
|
+
- **拦截器集成**:自动为 `text/event-stream` 内容类型的响应添加 `eventStream()` 方法
|
|
15
|
+
- **SSE 解析**:根据规范解析服务器发送事件,包括数据、事件、ID 和重试字段
|
|
16
|
+
- **流支持**:正确处理分块数据和多行事件
|
|
17
|
+
- **注释处理**:正确忽略注释行(以 `:` 开头的行)
|
|
18
|
+
- **TypeScript 支持**:完整的 TypeScript 类型定义
|
|
19
|
+
|
|
20
|
+
## 安装
|
|
21
|
+
|
|
22
|
+
使用 pnpm:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add @ahoo-wang/fetcher-eventstream
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
使用 npm:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @ahoo-wang/fetcher-eventstream
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
使用 yarn:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
yarn add @ahoo-wang/fetcher-eventstream
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 使用
|
|
41
|
+
|
|
42
|
+
### 带拦截器的基本用法
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
46
|
+
import { EventStreamInterceptor } from '@ahoo-wang/fetcher-eventstream';
|
|
47
|
+
|
|
48
|
+
const fetcher = new Fetcher({
|
|
49
|
+
baseURL: 'https://api.example.com',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// 添加事件流拦截器
|
|
53
|
+
fetcher.interceptors.response.use(new EventStreamInterceptor());
|
|
54
|
+
|
|
55
|
+
// 在响应中使用 eventStream 方法处理 text/event-stream 内容类型
|
|
56
|
+
const response = await fetcher.get('/events');
|
|
57
|
+
if (response.eventStream) {
|
|
58
|
+
for await (const event of response.eventStream()) {
|
|
59
|
+
console.log('收到事件:', event);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 手动转换
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { toServerSentEventStream } from '@ahoo-wang/fetcher-eventstream';
|
|
68
|
+
|
|
69
|
+
// 手动转换 Response 对象
|
|
70
|
+
const response = await fetch('/events');
|
|
71
|
+
const eventStream = toServerSentEventStream(response);
|
|
72
|
+
|
|
73
|
+
// 从流中读取事件
|
|
74
|
+
const reader = eventStream.getReader();
|
|
75
|
+
try {
|
|
76
|
+
while (true) {
|
|
77
|
+
const { done, value } = await reader.read();
|
|
78
|
+
if (done) break;
|
|
79
|
+
console.log('收到事件:', value);
|
|
80
|
+
}
|
|
81
|
+
} finally {
|
|
82
|
+
reader.releaseLock();
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 异步迭代器用法
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
90
|
+
import { EventStreamInterceptor } from '@ahoo-wang/fetcher-eventstream';
|
|
91
|
+
|
|
92
|
+
const fetcher = new Fetcher({
|
|
93
|
+
baseURL: 'https://api.example.com',
|
|
94
|
+
interceptors: {
|
|
95
|
+
response: [new EventStreamInterceptor()],
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// 使用异步迭代
|
|
100
|
+
const response = await fetcher.get('/events');
|
|
101
|
+
if (response.eventStream) {
|
|
102
|
+
for await (const event of response.eventStream()) {
|
|
103
|
+
switch (event.event) {
|
|
104
|
+
case 'message':
|
|
105
|
+
console.log('消息:', event.data);
|
|
106
|
+
break;
|
|
107
|
+
case 'notification':
|
|
108
|
+
console.log('通知:', event.data);
|
|
109
|
+
break;
|
|
110
|
+
default:
|
|
111
|
+
console.log('未知事件:', event);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## API 参考
|
|
118
|
+
|
|
119
|
+
### EventStreamConverter
|
|
120
|
+
|
|
121
|
+
用于将 `text/event-stream` 响应转换为可读流的工具类。
|
|
122
|
+
|
|
123
|
+
#### `toServerSentEventStream(response: Response): ServerEventStream`
|
|
124
|
+
|
|
125
|
+
将带有 `text/event-stream` 主体的 Response 对象转换为 ServerSentEvent 对象的可读流。
|
|
126
|
+
|
|
127
|
+
**参数:**
|
|
128
|
+
|
|
129
|
+
- `response`:带有 `text/event-stream` 内容类型的 HTTP 响应
|
|
130
|
+
|
|
131
|
+
**返回:**
|
|
132
|
+
|
|
133
|
+
- `ServerEventStream`:ServerSentEvent 对象的可读流
|
|
134
|
+
|
|
135
|
+
### EventStreamInterceptor
|
|
136
|
+
|
|
137
|
+
响应拦截器,自动为 `text/event-stream` 内容类型的响应添加 `eventStream()` 方法。
|
|
138
|
+
|
|
139
|
+
#### `intercept(exchange: FetchExchange): FetchExchange`
|
|
140
|
+
|
|
141
|
+
拦截响应,如果内容类型是 `text/event-stream` 则添加 `eventStream()` 方法。
|
|
142
|
+
|
|
143
|
+
**参数:**
|
|
144
|
+
|
|
145
|
+
- `exchange`:包含要拦截响应的 fetch exchange
|
|
146
|
+
|
|
147
|
+
**返回:**
|
|
148
|
+
|
|
149
|
+
- `FetchExchange`:可能修改了响应以包含 `eventStream()` 方法的拦截 exchange
|
|
150
|
+
|
|
151
|
+
### ServerSentEvent
|
|
152
|
+
|
|
153
|
+
定义服务器发送事件结构的接口。
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
interface ServerSentEvent {
|
|
157
|
+
data: string; // 事件数据(必需)
|
|
158
|
+
event?: string; // 事件类型(可选,默认为 'message')
|
|
159
|
+
id?: string; // 事件 ID(可选)
|
|
160
|
+
retry?: number; // 以毫秒为单位的重试超时(可选)
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### ServerSentEventStream
|
|
165
|
+
|
|
166
|
+
ServerSentEvent 对象的可读流的类型别名。
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
type ServerSentEventStream = ReadableStream<ServerSentEvent>;
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## 服务器发送事件规范合规性
|
|
173
|
+
|
|
174
|
+
此包完全实现了 [服务器发送事件规范](https://html.spec.whatwg.org/multipage/server-sent-events.html):
|
|
175
|
+
|
|
176
|
+
- **数据字段**:支持多行数据字段
|
|
177
|
+
- **事件字段**:自定义事件类型
|
|
178
|
+
- **ID 字段**:最后事件 ID 跟踪
|
|
179
|
+
- **重试字段**:自动重连超时
|
|
180
|
+
- **注释行**:忽略以 `:` 开头的行
|
|
181
|
+
- **事件分发**:正确的事件分发,默认事件类型为 'message'
|
|
182
|
+
|
|
183
|
+
## 示例
|
|
184
|
+
|
|
185
|
+
### 实时通知
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
189
|
+
import { EventStreamInterceptor } from '@ahoo-wang/fetcher-eventstream';
|
|
190
|
+
|
|
191
|
+
const fetcher = new Fetcher({
|
|
192
|
+
baseURL: 'https://api.example.com',
|
|
193
|
+
});
|
|
194
|
+
fetcher.interceptors.response.use(new EventStreamInterceptor());
|
|
195
|
+
|
|
196
|
+
// 监听实时通知
|
|
197
|
+
const response = await fetcher.get('/notifications');
|
|
198
|
+
if (response.eventStream) {
|
|
199
|
+
for await (const event of response.eventStream()) {
|
|
200
|
+
switch (event.event) {
|
|
201
|
+
case 'message':
|
|
202
|
+
showNotification('消息', event.data);
|
|
203
|
+
break;
|
|
204
|
+
case 'alert':
|
|
205
|
+
showAlert('警报', event.data);
|
|
206
|
+
break;
|
|
207
|
+
case 'update':
|
|
208
|
+
handleUpdate(JSON.parse(event.data));
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### 进度更新
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
import { Fetcher } from '@ahoo-wang/fetcher';
|
|
219
|
+
import { EventStreamInterceptor } from '@ahoo-wang/fetcher-eventstream';
|
|
220
|
+
|
|
221
|
+
const fetcher = new Fetcher({
|
|
222
|
+
baseURL: 'https://api.example.com',
|
|
223
|
+
});
|
|
224
|
+
fetcher.interceptors.response.use(new EventStreamInterceptor());
|
|
225
|
+
|
|
226
|
+
// 跟踪长时间运行的任务进度
|
|
227
|
+
const response = await fetcher.get('/tasks/123/progress');
|
|
228
|
+
if (response.eventStream) {
|
|
229
|
+
for await (const event of response.eventStream()) {
|
|
230
|
+
if (event.event === 'progress') {
|
|
231
|
+
const progress = JSON.parse(event.data);
|
|
232
|
+
updateProgressBar(progress.percentage);
|
|
233
|
+
} else if (event.event === 'complete') {
|
|
234
|
+
showCompletionMessage(event.data);
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## 测试
|
|
242
|
+
|
|
243
|
+
运行此包的测试:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
pnpm test
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
测试套件包括:
|
|
250
|
+
|
|
251
|
+
- 事件流转换测试
|
|
252
|
+
- 拦截器功能测试
|
|
253
|
+
- 边界情况处理(畸形事件、分块数据等)
|
|
254
|
+
- 大事件流的性能测试
|
|
255
|
+
|
|
256
|
+
## 许可证
|
|
257
|
+
|
|
258
|
+
本项目采用 [Apache-2.0 许可证](../../LICENSE)。
|
|
@@ -3,17 +3,5 @@ import { ServerSentEvent } from './serverSentEventTransformStream';
|
|
|
3
3
|
* ServerSentEventStream is a ReadableStream of ServerSentEvent objects
|
|
4
4
|
*/
|
|
5
5
|
export type ServerSentEventStream = ReadableStream<ServerSentEvent>;
|
|
6
|
-
|
|
7
|
-
* Responsible for handling event stream responses, including converting Response to event stream (ReadableStream<ServerSentEvent>).
|
|
8
|
-
*/
|
|
9
|
-
export declare class EventStreamConverter {
|
|
10
|
-
/**
|
|
11
|
-
* Convert HTTP response to server-sent event stream
|
|
12
|
-
*
|
|
13
|
-
* @param response - HTTP response object, should contain text/event-stream formatted data
|
|
14
|
-
* @returns ServerSentEventStream - Server event stream, can be used to read events sent by the server
|
|
15
|
-
* @throws Error - Throws error when response body is null
|
|
16
|
-
*/
|
|
17
|
-
static toEventStream(response: Response): ServerSentEventStream;
|
|
18
|
-
}
|
|
6
|
+
export declare function toServerSentEventStream(response: Response): ServerSentEventStream;
|
|
19
7
|
//# sourceMappingURL=eventStreamConverter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eventStreamConverter.d.ts","sourceRoot":"","sources":["../src/eventStreamConverter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EAEhB,MAAM,kCAAkC,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,eAAe,CAAC,CAAC;AAEpE
|
|
1
|
+
{"version":3,"file":"eventStreamConverter.d.ts","sourceRoot":"","sources":["../src/eventStreamConverter.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EAEhB,MAAM,kCAAkC,CAAC;AAE1C;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,eAAe,CAAC,CAAC;AAEpE,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,GACjB,qBAAqB,CAavB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare class EventStreamInterceptor implements
|
|
3
|
-
intercept(
|
|
1
|
+
import { FetchExchange, Interceptor } from '@ahoo-wang/fetcher';
|
|
2
|
+
export declare class EventStreamInterceptor implements Interceptor {
|
|
3
|
+
intercept(exchange: FetchExchange): FetchExchange;
|
|
4
4
|
}
|
|
5
5
|
//# sourceMappingURL=eventStreamInterceptor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eventStreamInterceptor.d.ts","sourceRoot":"","sources":["../src/eventStreamInterceptor.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,
|
|
1
|
+
{"version":3,"file":"eventStreamInterceptor.d.ts","sourceRoot":"","sources":["../src/eventStreamInterceptor.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,aAAa,EACb,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAE5B,qBAAa,sBAAuB,YAAW,WAAW;IACxD,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,aAAa;CAgBlD"}
|
package/dist/index.es.js
CHANGED
|
@@ -3,50 +3,50 @@ class d {
|
|
|
3
3
|
constructor() {
|
|
4
4
|
this.buffer = "";
|
|
5
5
|
}
|
|
6
|
-
transform(
|
|
6
|
+
transform(t, r) {
|
|
7
7
|
try {
|
|
8
|
-
this.buffer +=
|
|
8
|
+
this.buffer += t;
|
|
9
9
|
const e = this.buffer.split(`
|
|
10
10
|
`);
|
|
11
11
|
this.buffer = e.pop() || "";
|
|
12
12
|
for (const n of e)
|
|
13
|
-
|
|
13
|
+
r.enqueue(n);
|
|
14
14
|
} catch (e) {
|
|
15
|
-
|
|
15
|
+
r.error(e);
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
|
-
flush(
|
|
18
|
+
flush(t) {
|
|
19
19
|
try {
|
|
20
|
-
this.buffer &&
|
|
21
|
-
} catch (
|
|
22
|
-
|
|
20
|
+
this.buffer && t.enqueue(this.buffer);
|
|
21
|
+
} catch (r) {
|
|
22
|
+
t.error(r);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
class
|
|
26
|
+
class u extends TransformStream {
|
|
27
27
|
constructor() {
|
|
28
28
|
super(new d());
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
var
|
|
32
|
-
function m(
|
|
33
|
-
switch (
|
|
31
|
+
var c = /* @__PURE__ */ ((s) => (s.ID = "id", s.RETRY = "retry", s.EVENT = "event", s.DATA = "data", s))(c || {});
|
|
32
|
+
function m(s, t, r) {
|
|
33
|
+
switch (s) {
|
|
34
34
|
case "event":
|
|
35
|
-
|
|
35
|
+
r.event = t;
|
|
36
36
|
break;
|
|
37
37
|
case "data":
|
|
38
|
-
|
|
38
|
+
r.data.push(t);
|
|
39
39
|
break;
|
|
40
40
|
case "id":
|
|
41
|
-
|
|
41
|
+
r.id = t;
|
|
42
42
|
break;
|
|
43
43
|
case "retry":
|
|
44
|
-
const e = parseInt(
|
|
45
|
-
isNaN(e) || (
|
|
44
|
+
const e = parseInt(t, 10);
|
|
45
|
+
isNaN(e) || (r.retry = e);
|
|
46
46
|
break;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
class
|
|
49
|
+
class y {
|
|
50
50
|
constructor() {
|
|
51
51
|
this.currentEvent = {
|
|
52
52
|
event: "message",
|
|
@@ -60,11 +60,11 @@ class h {
|
|
|
60
60
|
* @param chunk Input string chunk
|
|
61
61
|
* @param controller Controller for controlling the transform stream
|
|
62
62
|
*/
|
|
63
|
-
transform(
|
|
63
|
+
transform(t, r) {
|
|
64
64
|
let e = this.currentEvent;
|
|
65
65
|
try {
|
|
66
|
-
if (
|
|
67
|
-
e.data.length > 0 && (
|
|
66
|
+
if (t.trim() === "") {
|
|
67
|
+
e.data.length > 0 && (r.enqueue({
|
|
68
68
|
event: e.event || "message",
|
|
69
69
|
data: e.data.join(`
|
|
70
70
|
`),
|
|
@@ -73,13 +73,13 @@ class h {
|
|
|
73
73
|
}), e.event = "message", e.id = e.id, e.retry = e.retry, e.data = []);
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
|
-
if (
|
|
76
|
+
if (t.startsWith(":"))
|
|
77
77
|
return;
|
|
78
|
-
const n =
|
|
79
|
-
let i,
|
|
80
|
-
n === -1 ? (i =
|
|
78
|
+
const n = t.indexOf(":");
|
|
79
|
+
let i, a;
|
|
80
|
+
n === -1 ? (i = t.toLowerCase(), a = "") : (i = t.substring(0, n).toLowerCase(), a = t.substring(n + 1), a.startsWith(" ") && (a = a.substring(1))), i = i.trim(), a = a.trim(), m(i, a, e);
|
|
81
81
|
} catch (n) {
|
|
82
|
-
|
|
82
|
+
r.error(
|
|
83
83
|
n instanceof Error ? n : new Error(String(n))
|
|
84
84
|
), e.event = "message", e.id = void 0, e.retry = void 0, e.data = [];
|
|
85
85
|
}
|
|
@@ -88,56 +88,50 @@ class h {
|
|
|
88
88
|
* Called when the stream ends, used to process remaining data
|
|
89
89
|
* @param controller Controller for controlling the transform stream
|
|
90
90
|
*/
|
|
91
|
-
flush(
|
|
92
|
-
let
|
|
91
|
+
flush(t) {
|
|
92
|
+
let r = this.currentEvent;
|
|
93
93
|
try {
|
|
94
|
-
|
|
95
|
-
event:
|
|
96
|
-
data:
|
|
94
|
+
r.data.length > 0 && t.enqueue({
|
|
95
|
+
event: r.event || "message",
|
|
96
|
+
data: r.data.join(`
|
|
97
97
|
`),
|
|
98
|
-
id:
|
|
99
|
-
retry:
|
|
98
|
+
id: r.id || "",
|
|
99
|
+
retry: r.retry
|
|
100
100
|
});
|
|
101
101
|
} catch (e) {
|
|
102
|
-
|
|
102
|
+
t.error(
|
|
103
103
|
e instanceof Error ? e : new Error(String(e))
|
|
104
104
|
);
|
|
105
105
|
} finally {
|
|
106
|
-
|
|
106
|
+
r.event = "message", r.id = void 0, r.retry = void 0, r.data = [];
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
class
|
|
110
|
+
class p extends TransformStream {
|
|
111
111
|
constructor() {
|
|
112
|
-
super(new
|
|
112
|
+
super(new y());
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
* @param response - HTTP response object, should contain text/event-stream formatted data
|
|
120
|
-
* @returns ServerSentEventStream - Server event stream, can be used to read events sent by the server
|
|
121
|
-
* @throws Error - Throws error when response body is null
|
|
122
|
-
*/
|
|
123
|
-
static toEventStream(r) {
|
|
124
|
-
if (!r.body)
|
|
125
|
-
throw new Error("Response body is null");
|
|
126
|
-
return r.body.pipeThrough(new TextDecoderStream("utf-8")).pipeThrough(new c()).pipeThrough(new y());
|
|
127
|
-
}
|
|
115
|
+
function h(s) {
|
|
116
|
+
if (!s.body)
|
|
117
|
+
throw new Error("Response body is null");
|
|
118
|
+
return s.body.pipeThrough(new TextDecoderStream("utf-8")).pipeThrough(new u()).pipeThrough(new p());
|
|
128
119
|
}
|
|
129
|
-
class
|
|
130
|
-
intercept(
|
|
131
|
-
const
|
|
132
|
-
|
|
120
|
+
class v {
|
|
121
|
+
intercept(t) {
|
|
122
|
+
const r = t.response;
|
|
123
|
+
if (!r)
|
|
124
|
+
return t;
|
|
125
|
+
const e = r.headers.get(o);
|
|
126
|
+
return e && e.includes(f.TEXT_EVENT_STREAM) && (r.eventStream = () => h(r)), t;
|
|
133
127
|
}
|
|
134
128
|
}
|
|
135
129
|
export {
|
|
136
|
-
v as
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
y as
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
130
|
+
v as EventStreamInterceptor,
|
|
131
|
+
c as ServerSentEventField,
|
|
132
|
+
p as ServerSentEventTransformStream,
|
|
133
|
+
y as ServerSentEventTransformer,
|
|
134
|
+
u as TextLineTransformStream,
|
|
135
|
+
d as TextLineTransformer,
|
|
136
|
+
h as toServerSentEventStream
|
|
143
137
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
`);this.buffer=e.pop()||"";for(const
|
|
3
|
-
`),id:e.id||"",retry:e.retry}),e.event="message",e.id=e.id,e.retry=e.retry,e.data=[]);return}if(
|
|
4
|
-
`),id:
|
|
1
|
+
(function(s,o){typeof exports=="object"&&typeof module<"u"?o(exports,require("@ahoo-wang/fetcher")):typeof define=="function"&&define.amd?define(["exports","@ahoo-wang/fetcher"],o):(s=typeof globalThis<"u"?globalThis:s||self,o(s.FetcherEventStream={},s.Fetcher))})(this,(function(s,o){"use strict";class d{constructor(){this.buffer=""}transform(t,r){try{this.buffer+=t;const e=this.buffer.split(`
|
|
2
|
+
`);this.buffer=e.pop()||"";for(const a of e)r.enqueue(a)}catch(e){r.error(e)}}flush(t){try{this.buffer&&t.enqueue(this.buffer)}catch(r){t.error(r)}}}class u extends TransformStream{constructor(){super(new d)}}var c=(n=>(n.ID="id",n.RETRY="retry",n.EVENT="event",n.DATA="data",n))(c||{});function h(n,t,r){switch(n){case"event":r.event=t;break;case"data":r.data.push(t);break;case"id":r.id=t;break;case"retry":const e=parseInt(t,10);isNaN(e)||(r.retry=e);break}}class m{constructor(){this.currentEvent={event:"message",id:void 0,retry:void 0,data:[]}}transform(t,r){let e=this.currentEvent;try{if(t.trim()===""){e.data.length>0&&(r.enqueue({event:e.event||"message",data:e.data.join(`
|
|
3
|
+
`),id:e.id||"",retry:e.retry}),e.event="message",e.id=e.id,e.retry=e.retry,e.data=[]);return}if(t.startsWith(":"))return;const a=t.indexOf(":");let f,i;a===-1?(f=t.toLowerCase(),i=""):(f=t.substring(0,a).toLowerCase(),i=t.substring(a+1),i.startsWith(" ")&&(i=i.substring(1))),f=f.trim(),i=i.trim(),h(f,i,e)}catch(a){r.error(a instanceof Error?a:new Error(String(a))),e.event="message",e.id=void 0,e.retry=void 0,e.data=[]}}flush(t){let r=this.currentEvent;try{r.data.length>0&&t.enqueue({event:r.event||"message",data:r.data.join(`
|
|
4
|
+
`),id:r.id||"",retry:r.retry})}catch(e){t.error(e instanceof Error?e:new Error(String(e)))}finally{r.event="message",r.id=void 0,r.retry=void 0,r.data=[]}}}class v extends TransformStream{constructor(){super(new m)}}function T(n){if(!n.body)throw new Error("Response body is null");return n.body.pipeThrough(new TextDecoderStream("utf-8")).pipeThrough(new u).pipeThrough(new v)}class y{intercept(t){const r=t.response;if(!r)return t;const e=r.headers.get(o.ContentTypeHeader);return e&&e.includes(o.ContentTypeValues.TEXT_EVENT_STREAM)&&(r.eventStream=()=>T(r)),t}}s.EventStreamInterceptor=y,s.ServerSentEventField=c,s.ServerSentEventTransformStream=v,s.ServerSentEventTransformer=m,s.TextLineTransformStream=u,s.TextLineTransformer=d,s.toServerSentEventStream=T,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ahoo-wang/fetcher-eventstream",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Support for text/event-stream in Fetcher",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"fetch",
|
|
@@ -24,16 +24,16 @@
|
|
|
24
24
|
"types": "./dist/index.d.ts",
|
|
25
25
|
"exports": {
|
|
26
26
|
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
27
28
|
"import": "./dist/index.es.js",
|
|
28
|
-
"require": "./dist/index.umd.js"
|
|
29
|
-
"types": "./dist/index.d.ts"
|
|
29
|
+
"require": "./dist/index.umd.js"
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"files": [
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@ahoo-wang/fetcher": "0.
|
|
36
|
+
"@ahoo-wang/fetcher": "0.2.2"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@vitest/coverage-v8": "^3.2.4",
|