@bagelink/sdk 1.8.39 → 1.8.43
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/dist/index.cjs +59 -72
- package/dist/index.d.cts +2 -33
- package/dist/index.d.mts +2 -33
- package/dist/index.d.ts +2 -33
- package/dist/index.mjs +59 -72
- package/package.json +1 -1
- package/src/index.ts +0 -1
- package/src/openAPITools/StreamController.ts +4 -79
- package/src/openAPITools/streamClient.ts +72 -34
package/dist/index.cjs
CHANGED
|
@@ -1162,10 +1162,8 @@ function generateTypes(schemas) {
|
|
|
1162
1162
|
class StreamController {
|
|
1163
1163
|
constructor(cleanup) {
|
|
1164
1164
|
this.cleanup = cleanup;
|
|
1165
|
-
this.controller = new AbortController();
|
|
1166
1165
|
}
|
|
1167
1166
|
handlers = /* @__PURE__ */ new Map();
|
|
1168
|
-
controller;
|
|
1169
1167
|
_closed = false;
|
|
1170
1168
|
/**
|
|
1171
1169
|
* Register an event handler (fully typed!)
|
|
@@ -1180,40 +1178,6 @@ class StreamController {
|
|
|
1180
1178
|
this.handlers.get(event).add(handler);
|
|
1181
1179
|
return this;
|
|
1182
1180
|
}
|
|
1183
|
-
/**
|
|
1184
|
-
* Register a one-time event handler (fully typed!)
|
|
1185
|
-
* @param event - Event type to listen for
|
|
1186
|
-
* @param handler - Handler function (called once then removed)
|
|
1187
|
-
* @returns this (for chaining)
|
|
1188
|
-
*/
|
|
1189
|
-
once(event, handler) {
|
|
1190
|
-
const wrappedHandler = (data) => {
|
|
1191
|
-
handler(data);
|
|
1192
|
-
this.off(event, wrappedHandler);
|
|
1193
|
-
};
|
|
1194
|
-
return this.on(event, wrappedHandler);
|
|
1195
|
-
}
|
|
1196
|
-
/**
|
|
1197
|
-
* Remove an event handler (fully typed!)
|
|
1198
|
-
* @param event - Event type
|
|
1199
|
-
* @param handler - Handler to remove
|
|
1200
|
-
* @returns this (for chaining)
|
|
1201
|
-
*/
|
|
1202
|
-
off(event, handler) {
|
|
1203
|
-
this.handlers.get(event)?.delete(handler);
|
|
1204
|
-
return this;
|
|
1205
|
-
}
|
|
1206
|
-
/**
|
|
1207
|
-
* Remove all handlers for an event (or all events if no event specified)
|
|
1208
|
-
*/
|
|
1209
|
-
removeAllListeners(event) {
|
|
1210
|
-
if (event === void 0) {
|
|
1211
|
-
this.handlers.clear();
|
|
1212
|
-
} else {
|
|
1213
|
-
this.handlers.delete(event);
|
|
1214
|
-
}
|
|
1215
|
-
return this;
|
|
1216
|
-
}
|
|
1217
1181
|
/**
|
|
1218
1182
|
* Emit an event to all registered handlers
|
|
1219
1183
|
* @internal
|
|
@@ -1235,17 +1199,10 @@ class StreamController {
|
|
|
1235
1199
|
*/
|
|
1236
1200
|
close() {
|
|
1237
1201
|
if (!this._closed) {
|
|
1238
|
-
this.controller.abort();
|
|
1239
1202
|
this.cleanup();
|
|
1240
1203
|
this._closed = true;
|
|
1241
1204
|
}
|
|
1242
1205
|
}
|
|
1243
|
-
/**
|
|
1244
|
-
* Check if stream is closed
|
|
1245
|
-
*/
|
|
1246
|
-
get closed() {
|
|
1247
|
-
return this._closed;
|
|
1248
|
-
}
|
|
1249
1206
|
}
|
|
1250
1207
|
|
|
1251
1208
|
function createSSEStream(url, options = {}) {
|
|
@@ -1257,11 +1214,14 @@ function createSSEStream(url, options = {}) {
|
|
|
1257
1214
|
fetch(url, {
|
|
1258
1215
|
method: "GET",
|
|
1259
1216
|
headers: {
|
|
1260
|
-
Accept: "text/event-stream",
|
|
1217
|
+
"Accept": "text/event-stream",
|
|
1218
|
+
"Cache-Control": "no-cache",
|
|
1261
1219
|
...headers
|
|
1262
1220
|
},
|
|
1263
1221
|
credentials: withCredentials ? "include" : "same-origin",
|
|
1264
|
-
signal: controller.signal
|
|
1222
|
+
signal: controller.signal,
|
|
1223
|
+
// Disable keepalive for streaming
|
|
1224
|
+
keepalive: false
|
|
1265
1225
|
}).then(async (response) => {
|
|
1266
1226
|
if (!response.ok) {
|
|
1267
1227
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
@@ -1276,35 +1236,47 @@ function createSSEStream(url, options = {}) {
|
|
|
1276
1236
|
while (true) {
|
|
1277
1237
|
const { done, value } = await reader.read();
|
|
1278
1238
|
if (done) {
|
|
1239
|
+
stream.emit("done", {});
|
|
1279
1240
|
stream.emit("complete", {});
|
|
1280
1241
|
break;
|
|
1281
1242
|
}
|
|
1282
1243
|
buffer += decoder.decode(value, { stream: true });
|
|
1283
1244
|
const lines = buffer.split("\n");
|
|
1284
1245
|
buffer = lines.pop() || "";
|
|
1285
|
-
let
|
|
1246
|
+
let currentEvent = "message";
|
|
1247
|
+
let currentData = "";
|
|
1286
1248
|
for (const line of lines) {
|
|
1287
|
-
if (line.startsWith("
|
|
1288
|
-
|
|
1289
|
-
} else if (line
|
|
1249
|
+
if (line.startsWith("event:")) {
|
|
1250
|
+
currentEvent = line.slice(6).trim();
|
|
1251
|
+
} else if (line.startsWith("data:")) {
|
|
1252
|
+
currentData = line.slice(5).trim();
|
|
1253
|
+
} else if (line === "" && currentData) {
|
|
1290
1254
|
try {
|
|
1291
|
-
const
|
|
1292
|
-
stream.emit(
|
|
1293
|
-
} catch
|
|
1294
|
-
|
|
1255
|
+
const parsed = JSON.parse(currentData);
|
|
1256
|
+
stream.emit(currentEvent, parsed);
|
|
1257
|
+
} catch {
|
|
1258
|
+
stream.emit(currentEvent, currentData);
|
|
1295
1259
|
}
|
|
1296
|
-
|
|
1260
|
+
currentEvent = "message";
|
|
1261
|
+
currentData = "";
|
|
1297
1262
|
}
|
|
1298
1263
|
}
|
|
1299
1264
|
}
|
|
1300
1265
|
} catch (error) {
|
|
1301
|
-
if (error
|
|
1302
|
-
|
|
1266
|
+
if (error?.name !== "AbortError") {
|
|
1267
|
+
const errorMessage = error?.message || "Stream connection error";
|
|
1268
|
+
stream.emit("error", new Error(errorMessage));
|
|
1269
|
+
}
|
|
1270
|
+
} finally {
|
|
1271
|
+
try {
|
|
1272
|
+
reader.releaseLock();
|
|
1273
|
+
} catch {
|
|
1303
1274
|
}
|
|
1304
1275
|
}
|
|
1305
1276
|
}).catch((error) => {
|
|
1306
|
-
if (error
|
|
1307
|
-
|
|
1277
|
+
if (error?.name !== "AbortError") {
|
|
1278
|
+
const errorMessage = error?.message || "Failed to establish stream connection";
|
|
1279
|
+
stream.emit("error", new Error(errorMessage));
|
|
1308
1280
|
}
|
|
1309
1281
|
});
|
|
1310
1282
|
return stream;
|
|
@@ -1320,11 +1292,14 @@ function createSSEStreamPost(url, body, options = {}) {
|
|
|
1320
1292
|
headers: {
|
|
1321
1293
|
"Accept": "text/event-stream",
|
|
1322
1294
|
"Content-Type": "application/json",
|
|
1295
|
+
"Cache-Control": "no-cache",
|
|
1323
1296
|
...headers
|
|
1324
1297
|
},
|
|
1325
1298
|
credentials: withCredentials ? "include" : "same-origin",
|
|
1326
1299
|
body: JSON.stringify(body),
|
|
1327
|
-
signal: controller.signal
|
|
1300
|
+
signal: controller.signal,
|
|
1301
|
+
// Disable keepalive for streaming
|
|
1302
|
+
keepalive: false
|
|
1328
1303
|
}).then(async (response) => {
|
|
1329
1304
|
if (!response.ok) {
|
|
1330
1305
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
@@ -1339,35 +1314,47 @@ function createSSEStreamPost(url, body, options = {}) {
|
|
|
1339
1314
|
while (true) {
|
|
1340
1315
|
const { done, value } = await reader.read();
|
|
1341
1316
|
if (done) {
|
|
1317
|
+
stream.emit("done", {});
|
|
1342
1318
|
stream.emit("complete", {});
|
|
1343
1319
|
break;
|
|
1344
1320
|
}
|
|
1345
1321
|
buffer += decoder.decode(value, { stream: true });
|
|
1346
1322
|
const lines = buffer.split("\n");
|
|
1347
1323
|
buffer = lines.pop() || "";
|
|
1348
|
-
let
|
|
1324
|
+
let currentEvent = "message";
|
|
1325
|
+
let currentData = "";
|
|
1349
1326
|
for (const line of lines) {
|
|
1350
|
-
if (line.startsWith("
|
|
1351
|
-
|
|
1352
|
-
} else if (line
|
|
1327
|
+
if (line.startsWith("event:")) {
|
|
1328
|
+
currentEvent = line.slice(6).trim();
|
|
1329
|
+
} else if (line.startsWith("data:")) {
|
|
1330
|
+
currentData = line.slice(5).trim();
|
|
1331
|
+
} else if (line === "" && currentData) {
|
|
1353
1332
|
try {
|
|
1354
|
-
const
|
|
1355
|
-
stream.emit(
|
|
1356
|
-
} catch
|
|
1357
|
-
|
|
1333
|
+
const parsed = JSON.parse(currentData);
|
|
1334
|
+
stream.emit(currentEvent, parsed);
|
|
1335
|
+
} catch {
|
|
1336
|
+
stream.emit(currentEvent, currentData);
|
|
1358
1337
|
}
|
|
1359
|
-
|
|
1338
|
+
currentEvent = "message";
|
|
1339
|
+
currentData = "";
|
|
1360
1340
|
}
|
|
1361
1341
|
}
|
|
1362
1342
|
}
|
|
1363
1343
|
} catch (error) {
|
|
1364
|
-
if (error
|
|
1365
|
-
|
|
1344
|
+
if (error?.name !== "AbortError") {
|
|
1345
|
+
const errorMessage = error?.message || "Stream connection error";
|
|
1346
|
+
stream.emit("error", new Error(errorMessage));
|
|
1347
|
+
}
|
|
1348
|
+
} finally {
|
|
1349
|
+
try {
|
|
1350
|
+
reader.releaseLock();
|
|
1351
|
+
} catch {
|
|
1366
1352
|
}
|
|
1367
1353
|
}
|
|
1368
1354
|
}).catch((error) => {
|
|
1369
|
-
if (error
|
|
1370
|
-
|
|
1355
|
+
if (error?.name !== "AbortError") {
|
|
1356
|
+
const errorMessage = error?.message || "Failed to establish stream connection";
|
|
1357
|
+
stream.emit("error", new Error(errorMessage));
|
|
1371
1358
|
}
|
|
1372
1359
|
});
|
|
1373
1360
|
return stream;
|
package/dist/index.d.cts
CHANGED
|
@@ -5,14 +5,6 @@ import { Ref } from 'vue';
|
|
|
5
5
|
* StreamController - Simple, reliable SSE stream management
|
|
6
6
|
* Provides a type-safe API for consuming Server-Sent Events
|
|
7
7
|
*/
|
|
8
|
-
interface SSEEvent<T = any> {
|
|
9
|
-
/** Event type (e.g., "token", "tool_call", "done") */
|
|
10
|
-
type: string;
|
|
11
|
-
/** Event data payload */
|
|
12
|
-
data: T;
|
|
13
|
-
/** Raw event string */
|
|
14
|
-
raw?: string;
|
|
15
|
-
}
|
|
16
8
|
type EventHandler<T = any> = (data: T) => void;
|
|
17
9
|
/**
|
|
18
10
|
* Type-safe event map for stream events
|
|
@@ -33,7 +25,6 @@ type StreamEventMap = Record<string, any>;
|
|
|
33
25
|
declare class StreamController<TEventMap extends StreamEventMap = StreamEventMap> {
|
|
34
26
|
private cleanup;
|
|
35
27
|
private handlers;
|
|
36
|
-
private controller;
|
|
37
28
|
private _closed;
|
|
38
29
|
constructor(cleanup: () => void);
|
|
39
30
|
/**
|
|
@@ -42,25 +33,7 @@ declare class StreamController<TEventMap extends StreamEventMap = StreamEventMap
|
|
|
42
33
|
* @param handler - Handler function (data type inferred from event)
|
|
43
34
|
* @returns this (for chaining)
|
|
44
35
|
*/
|
|
45
|
-
on<K extends keyof TEventMap | 'error' | 'complete'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' ? () => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
46
|
-
/**
|
|
47
|
-
* Register a one-time event handler (fully typed!)
|
|
48
|
-
* @param event - Event type to listen for
|
|
49
|
-
* @param handler - Handler function (called once then removed)
|
|
50
|
-
* @returns this (for chaining)
|
|
51
|
-
*/
|
|
52
|
-
once<K extends keyof TEventMap | 'error' | 'complete'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' ? () => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
53
|
-
/**
|
|
54
|
-
* Remove an event handler (fully typed!)
|
|
55
|
-
* @param event - Event type
|
|
56
|
-
* @param handler - Handler to remove
|
|
57
|
-
* @returns this (for chaining)
|
|
58
|
-
*/
|
|
59
|
-
off<K extends keyof TEventMap | 'error' | 'complete'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' ? () => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
60
|
-
/**
|
|
61
|
-
* Remove all handlers for an event (or all events if no event specified)
|
|
62
|
-
*/
|
|
63
|
-
removeAllListeners(event?: keyof TEventMap | 'error' | 'complete'): this;
|
|
36
|
+
on<K extends keyof TEventMap | 'error' | 'complete' | 'done'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' | 'done' ? (data?: any) => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
64
37
|
/**
|
|
65
38
|
* Emit an event to all registered handlers
|
|
66
39
|
* @internal
|
|
@@ -70,10 +43,6 @@ declare class StreamController<TEventMap extends StreamEventMap = StreamEventMap
|
|
|
70
43
|
* Close the stream
|
|
71
44
|
*/
|
|
72
45
|
close(): void;
|
|
73
|
-
/**
|
|
74
|
-
* Check if stream is closed
|
|
75
|
-
*/
|
|
76
|
-
get closed(): boolean;
|
|
77
46
|
}
|
|
78
47
|
|
|
79
48
|
/**
|
|
@@ -799,4 +768,4 @@ declare class Bagel {
|
|
|
799
768
|
}
|
|
800
769
|
|
|
801
770
|
export { ApiResponse, Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventInfo, extractSSEEventTypes, formatAPIErrorMessage, formatFieldErrors, getPath, isReferenceObject, isSSEStream, isSchemaObject, _default as openAPI, parseApiError, unwrap, useApiRequest, useCancellableRequest, wrapApiForDirectReturn };
|
|
802
|
-
export type { ApiConfig, ApiRequestState, BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, FastAPIErrorResponse, FastAPIValidationError, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, ParsedError, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, RequestInterceptor, ResponseInterceptor, ResponseMetadata, ResponseObject, ResponsesObject,
|
|
771
|
+
export type { ApiConfig, ApiRequestState, BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, FastAPIErrorResponse, FastAPIValidationError, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, ParsedError, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, RequestInterceptor, ResponseInterceptor, ResponseMetadata, ResponseObject, ResponsesObject, SSEEventTypeInfo, SSEStreamOptions, SchemaObject, SchemaObjectType, SchemasObject, ScopesObject, SecurityRequirementObject, SecuritySchemeObject, SecuritySchemeType, ServerObject, ServerVariableObject, StreamEventMap, TableToTypeMapping, Tables, TagObject, TypeFormatT, Unwrap, Unwrapped, UploadOptions, User, XmlObject };
|
package/dist/index.d.mts
CHANGED
|
@@ -5,14 +5,6 @@ import { Ref } from 'vue';
|
|
|
5
5
|
* StreamController - Simple, reliable SSE stream management
|
|
6
6
|
* Provides a type-safe API for consuming Server-Sent Events
|
|
7
7
|
*/
|
|
8
|
-
interface SSEEvent<T = any> {
|
|
9
|
-
/** Event type (e.g., "token", "tool_call", "done") */
|
|
10
|
-
type: string;
|
|
11
|
-
/** Event data payload */
|
|
12
|
-
data: T;
|
|
13
|
-
/** Raw event string */
|
|
14
|
-
raw?: string;
|
|
15
|
-
}
|
|
16
8
|
type EventHandler<T = any> = (data: T) => void;
|
|
17
9
|
/**
|
|
18
10
|
* Type-safe event map for stream events
|
|
@@ -33,7 +25,6 @@ type StreamEventMap = Record<string, any>;
|
|
|
33
25
|
declare class StreamController<TEventMap extends StreamEventMap = StreamEventMap> {
|
|
34
26
|
private cleanup;
|
|
35
27
|
private handlers;
|
|
36
|
-
private controller;
|
|
37
28
|
private _closed;
|
|
38
29
|
constructor(cleanup: () => void);
|
|
39
30
|
/**
|
|
@@ -42,25 +33,7 @@ declare class StreamController<TEventMap extends StreamEventMap = StreamEventMap
|
|
|
42
33
|
* @param handler - Handler function (data type inferred from event)
|
|
43
34
|
* @returns this (for chaining)
|
|
44
35
|
*/
|
|
45
|
-
on<K extends keyof TEventMap | 'error' | 'complete'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' ? () => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
46
|
-
/**
|
|
47
|
-
* Register a one-time event handler (fully typed!)
|
|
48
|
-
* @param event - Event type to listen for
|
|
49
|
-
* @param handler - Handler function (called once then removed)
|
|
50
|
-
* @returns this (for chaining)
|
|
51
|
-
*/
|
|
52
|
-
once<K extends keyof TEventMap | 'error' | 'complete'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' ? () => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
53
|
-
/**
|
|
54
|
-
* Remove an event handler (fully typed!)
|
|
55
|
-
* @param event - Event type
|
|
56
|
-
* @param handler - Handler to remove
|
|
57
|
-
* @returns this (for chaining)
|
|
58
|
-
*/
|
|
59
|
-
off<K extends keyof TEventMap | 'error' | 'complete'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' ? () => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
60
|
-
/**
|
|
61
|
-
* Remove all handlers for an event (or all events if no event specified)
|
|
62
|
-
*/
|
|
63
|
-
removeAllListeners(event?: keyof TEventMap | 'error' | 'complete'): this;
|
|
36
|
+
on<K extends keyof TEventMap | 'error' | 'complete' | 'done'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' | 'done' ? (data?: any) => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
64
37
|
/**
|
|
65
38
|
* Emit an event to all registered handlers
|
|
66
39
|
* @internal
|
|
@@ -70,10 +43,6 @@ declare class StreamController<TEventMap extends StreamEventMap = StreamEventMap
|
|
|
70
43
|
* Close the stream
|
|
71
44
|
*/
|
|
72
45
|
close(): void;
|
|
73
|
-
/**
|
|
74
|
-
* Check if stream is closed
|
|
75
|
-
*/
|
|
76
|
-
get closed(): boolean;
|
|
77
46
|
}
|
|
78
47
|
|
|
79
48
|
/**
|
|
@@ -799,4 +768,4 @@ declare class Bagel {
|
|
|
799
768
|
}
|
|
800
769
|
|
|
801
770
|
export { ApiResponse, Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventInfo, extractSSEEventTypes, formatAPIErrorMessage, formatFieldErrors, getPath, isReferenceObject, isSSEStream, isSchemaObject, _default as openAPI, parseApiError, unwrap, useApiRequest, useCancellableRequest, wrapApiForDirectReturn };
|
|
802
|
-
export type { ApiConfig, ApiRequestState, BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, FastAPIErrorResponse, FastAPIValidationError, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, ParsedError, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, RequestInterceptor, ResponseInterceptor, ResponseMetadata, ResponseObject, ResponsesObject,
|
|
771
|
+
export type { ApiConfig, ApiRequestState, BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, FastAPIErrorResponse, FastAPIValidationError, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, ParsedError, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, RequestInterceptor, ResponseInterceptor, ResponseMetadata, ResponseObject, ResponsesObject, SSEEventTypeInfo, SSEStreamOptions, SchemaObject, SchemaObjectType, SchemasObject, ScopesObject, SecurityRequirementObject, SecuritySchemeObject, SecuritySchemeType, ServerObject, ServerVariableObject, StreamEventMap, TableToTypeMapping, Tables, TagObject, TypeFormatT, Unwrap, Unwrapped, UploadOptions, User, XmlObject };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,14 +5,6 @@ import { Ref } from 'vue';
|
|
|
5
5
|
* StreamController - Simple, reliable SSE stream management
|
|
6
6
|
* Provides a type-safe API for consuming Server-Sent Events
|
|
7
7
|
*/
|
|
8
|
-
interface SSEEvent<T = any> {
|
|
9
|
-
/** Event type (e.g., "token", "tool_call", "done") */
|
|
10
|
-
type: string;
|
|
11
|
-
/** Event data payload */
|
|
12
|
-
data: T;
|
|
13
|
-
/** Raw event string */
|
|
14
|
-
raw?: string;
|
|
15
|
-
}
|
|
16
8
|
type EventHandler<T = any> = (data: T) => void;
|
|
17
9
|
/**
|
|
18
10
|
* Type-safe event map for stream events
|
|
@@ -33,7 +25,6 @@ type StreamEventMap = Record<string, any>;
|
|
|
33
25
|
declare class StreamController<TEventMap extends StreamEventMap = StreamEventMap> {
|
|
34
26
|
private cleanup;
|
|
35
27
|
private handlers;
|
|
36
|
-
private controller;
|
|
37
28
|
private _closed;
|
|
38
29
|
constructor(cleanup: () => void);
|
|
39
30
|
/**
|
|
@@ -42,25 +33,7 @@ declare class StreamController<TEventMap extends StreamEventMap = StreamEventMap
|
|
|
42
33
|
* @param handler - Handler function (data type inferred from event)
|
|
43
34
|
* @returns this (for chaining)
|
|
44
35
|
*/
|
|
45
|
-
on<K extends keyof TEventMap | 'error' | 'complete'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' ? () => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
46
|
-
/**
|
|
47
|
-
* Register a one-time event handler (fully typed!)
|
|
48
|
-
* @param event - Event type to listen for
|
|
49
|
-
* @param handler - Handler function (called once then removed)
|
|
50
|
-
* @returns this (for chaining)
|
|
51
|
-
*/
|
|
52
|
-
once<K extends keyof TEventMap | 'error' | 'complete'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' ? () => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
53
|
-
/**
|
|
54
|
-
* Remove an event handler (fully typed!)
|
|
55
|
-
* @param event - Event type
|
|
56
|
-
* @param handler - Handler to remove
|
|
57
|
-
* @returns this (for chaining)
|
|
58
|
-
*/
|
|
59
|
-
off<K extends keyof TEventMap | 'error' | 'complete'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' ? () => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
60
|
-
/**
|
|
61
|
-
* Remove all handlers for an event (or all events if no event specified)
|
|
62
|
-
*/
|
|
63
|
-
removeAllListeners(event?: keyof TEventMap | 'error' | 'complete'): this;
|
|
36
|
+
on<K extends keyof TEventMap | 'error' | 'complete' | 'done'>(event: K, handler: K extends 'error' ? (error: Error) => void : K extends 'complete' | 'done' ? (data?: any) => void : K extends keyof TEventMap ? (data: TEventMap[K]) => void : EventHandler): this;
|
|
64
37
|
/**
|
|
65
38
|
* Emit an event to all registered handlers
|
|
66
39
|
* @internal
|
|
@@ -70,10 +43,6 @@ declare class StreamController<TEventMap extends StreamEventMap = StreamEventMap
|
|
|
70
43
|
* Close the stream
|
|
71
44
|
*/
|
|
72
45
|
close(): void;
|
|
73
|
-
/**
|
|
74
|
-
* Check if stream is closed
|
|
75
|
-
*/
|
|
76
|
-
get closed(): boolean;
|
|
77
46
|
}
|
|
78
47
|
|
|
79
48
|
/**
|
|
@@ -799,4 +768,4 @@ declare class Bagel {
|
|
|
799
768
|
}
|
|
800
769
|
|
|
801
770
|
export { ApiResponse, Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventInfo, extractSSEEventTypes, formatAPIErrorMessage, formatFieldErrors, getPath, isReferenceObject, isSSEStream, isSchemaObject, _default as openAPI, parseApiError, unwrap, useApiRequest, useCancellableRequest, wrapApiForDirectReturn };
|
|
802
|
-
export type { ApiConfig, ApiRequestState, BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, FastAPIErrorResponse, FastAPIValidationError, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, ParsedError, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, RequestInterceptor, ResponseInterceptor, ResponseMetadata, ResponseObject, ResponsesObject,
|
|
771
|
+
export type { ApiConfig, ApiRequestState, BaseParameterObject, CallbackObject, CallbacksObject, ComponentsObject, ContactObject, ContentObject, DiscriminatorObject, EncodingObject, EncodingPropertyObject, ExampleObject, ExamplesObject, ExternalDocumentationObject, FastAPIErrorResponse, FastAPIValidationError, HeaderObject, HeadersObject, InfoObject, LicenseObject, LinkObject, LinkParametersObject, LinksObject, MediaTypeObject, OAuthFlowObject, OAuthFlowsObject, OpenAPIObject, OperationObject, ParameterLocation, ParameterObject, ParameterStyle, ParsedError, PathItemObject, PathsObject, ReferenceObject, RequestBodyObject, RequestInterceptor, ResponseInterceptor, ResponseMetadata, ResponseObject, ResponsesObject, SSEEventTypeInfo, SSEStreamOptions, SchemaObject, SchemaObjectType, SchemasObject, ScopesObject, SecurityRequirementObject, SecuritySchemeObject, SecuritySchemeType, ServerObject, ServerVariableObject, StreamEventMap, TableToTypeMapping, Tables, TagObject, TypeFormatT, Unwrap, Unwrapped, UploadOptions, User, XmlObject };
|
package/dist/index.mjs
CHANGED
|
@@ -1156,10 +1156,8 @@ function generateTypes(schemas) {
|
|
|
1156
1156
|
class StreamController {
|
|
1157
1157
|
constructor(cleanup) {
|
|
1158
1158
|
this.cleanup = cleanup;
|
|
1159
|
-
this.controller = new AbortController();
|
|
1160
1159
|
}
|
|
1161
1160
|
handlers = /* @__PURE__ */ new Map();
|
|
1162
|
-
controller;
|
|
1163
1161
|
_closed = false;
|
|
1164
1162
|
/**
|
|
1165
1163
|
* Register an event handler (fully typed!)
|
|
@@ -1174,40 +1172,6 @@ class StreamController {
|
|
|
1174
1172
|
this.handlers.get(event).add(handler);
|
|
1175
1173
|
return this;
|
|
1176
1174
|
}
|
|
1177
|
-
/**
|
|
1178
|
-
* Register a one-time event handler (fully typed!)
|
|
1179
|
-
* @param event - Event type to listen for
|
|
1180
|
-
* @param handler - Handler function (called once then removed)
|
|
1181
|
-
* @returns this (for chaining)
|
|
1182
|
-
*/
|
|
1183
|
-
once(event, handler) {
|
|
1184
|
-
const wrappedHandler = (data) => {
|
|
1185
|
-
handler(data);
|
|
1186
|
-
this.off(event, wrappedHandler);
|
|
1187
|
-
};
|
|
1188
|
-
return this.on(event, wrappedHandler);
|
|
1189
|
-
}
|
|
1190
|
-
/**
|
|
1191
|
-
* Remove an event handler (fully typed!)
|
|
1192
|
-
* @param event - Event type
|
|
1193
|
-
* @param handler - Handler to remove
|
|
1194
|
-
* @returns this (for chaining)
|
|
1195
|
-
*/
|
|
1196
|
-
off(event, handler) {
|
|
1197
|
-
this.handlers.get(event)?.delete(handler);
|
|
1198
|
-
return this;
|
|
1199
|
-
}
|
|
1200
|
-
/**
|
|
1201
|
-
* Remove all handlers for an event (or all events if no event specified)
|
|
1202
|
-
*/
|
|
1203
|
-
removeAllListeners(event) {
|
|
1204
|
-
if (event === void 0) {
|
|
1205
|
-
this.handlers.clear();
|
|
1206
|
-
} else {
|
|
1207
|
-
this.handlers.delete(event);
|
|
1208
|
-
}
|
|
1209
|
-
return this;
|
|
1210
|
-
}
|
|
1211
1175
|
/**
|
|
1212
1176
|
* Emit an event to all registered handlers
|
|
1213
1177
|
* @internal
|
|
@@ -1229,17 +1193,10 @@ class StreamController {
|
|
|
1229
1193
|
*/
|
|
1230
1194
|
close() {
|
|
1231
1195
|
if (!this._closed) {
|
|
1232
|
-
this.controller.abort();
|
|
1233
1196
|
this.cleanup();
|
|
1234
1197
|
this._closed = true;
|
|
1235
1198
|
}
|
|
1236
1199
|
}
|
|
1237
|
-
/**
|
|
1238
|
-
* Check if stream is closed
|
|
1239
|
-
*/
|
|
1240
|
-
get closed() {
|
|
1241
|
-
return this._closed;
|
|
1242
|
-
}
|
|
1243
1200
|
}
|
|
1244
1201
|
|
|
1245
1202
|
function createSSEStream(url, options = {}) {
|
|
@@ -1251,11 +1208,14 @@ function createSSEStream(url, options = {}) {
|
|
|
1251
1208
|
fetch(url, {
|
|
1252
1209
|
method: "GET",
|
|
1253
1210
|
headers: {
|
|
1254
|
-
Accept: "text/event-stream",
|
|
1211
|
+
"Accept": "text/event-stream",
|
|
1212
|
+
"Cache-Control": "no-cache",
|
|
1255
1213
|
...headers
|
|
1256
1214
|
},
|
|
1257
1215
|
credentials: withCredentials ? "include" : "same-origin",
|
|
1258
|
-
signal: controller.signal
|
|
1216
|
+
signal: controller.signal,
|
|
1217
|
+
// Disable keepalive for streaming
|
|
1218
|
+
keepalive: false
|
|
1259
1219
|
}).then(async (response) => {
|
|
1260
1220
|
if (!response.ok) {
|
|
1261
1221
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
@@ -1270,35 +1230,47 @@ function createSSEStream(url, options = {}) {
|
|
|
1270
1230
|
while (true) {
|
|
1271
1231
|
const { done, value } = await reader.read();
|
|
1272
1232
|
if (done) {
|
|
1233
|
+
stream.emit("done", {});
|
|
1273
1234
|
stream.emit("complete", {});
|
|
1274
1235
|
break;
|
|
1275
1236
|
}
|
|
1276
1237
|
buffer += decoder.decode(value, { stream: true });
|
|
1277
1238
|
const lines = buffer.split("\n");
|
|
1278
1239
|
buffer = lines.pop() || "";
|
|
1279
|
-
let
|
|
1240
|
+
let currentEvent = "message";
|
|
1241
|
+
let currentData = "";
|
|
1280
1242
|
for (const line of lines) {
|
|
1281
|
-
if (line.startsWith("
|
|
1282
|
-
|
|
1283
|
-
} else if (line
|
|
1243
|
+
if (line.startsWith("event:")) {
|
|
1244
|
+
currentEvent = line.slice(6).trim();
|
|
1245
|
+
} else if (line.startsWith("data:")) {
|
|
1246
|
+
currentData = line.slice(5).trim();
|
|
1247
|
+
} else if (line === "" && currentData) {
|
|
1284
1248
|
try {
|
|
1285
|
-
const
|
|
1286
|
-
stream.emit(
|
|
1287
|
-
} catch
|
|
1288
|
-
|
|
1249
|
+
const parsed = JSON.parse(currentData);
|
|
1250
|
+
stream.emit(currentEvent, parsed);
|
|
1251
|
+
} catch {
|
|
1252
|
+
stream.emit(currentEvent, currentData);
|
|
1289
1253
|
}
|
|
1290
|
-
|
|
1254
|
+
currentEvent = "message";
|
|
1255
|
+
currentData = "";
|
|
1291
1256
|
}
|
|
1292
1257
|
}
|
|
1293
1258
|
}
|
|
1294
1259
|
} catch (error) {
|
|
1295
|
-
if (error
|
|
1296
|
-
|
|
1260
|
+
if (error?.name !== "AbortError") {
|
|
1261
|
+
const errorMessage = error?.message || "Stream connection error";
|
|
1262
|
+
stream.emit("error", new Error(errorMessage));
|
|
1263
|
+
}
|
|
1264
|
+
} finally {
|
|
1265
|
+
try {
|
|
1266
|
+
reader.releaseLock();
|
|
1267
|
+
} catch {
|
|
1297
1268
|
}
|
|
1298
1269
|
}
|
|
1299
1270
|
}).catch((error) => {
|
|
1300
|
-
if (error
|
|
1301
|
-
|
|
1271
|
+
if (error?.name !== "AbortError") {
|
|
1272
|
+
const errorMessage = error?.message || "Failed to establish stream connection";
|
|
1273
|
+
stream.emit("error", new Error(errorMessage));
|
|
1302
1274
|
}
|
|
1303
1275
|
});
|
|
1304
1276
|
return stream;
|
|
@@ -1314,11 +1286,14 @@ function createSSEStreamPost(url, body, options = {}) {
|
|
|
1314
1286
|
headers: {
|
|
1315
1287
|
"Accept": "text/event-stream",
|
|
1316
1288
|
"Content-Type": "application/json",
|
|
1289
|
+
"Cache-Control": "no-cache",
|
|
1317
1290
|
...headers
|
|
1318
1291
|
},
|
|
1319
1292
|
credentials: withCredentials ? "include" : "same-origin",
|
|
1320
1293
|
body: JSON.stringify(body),
|
|
1321
|
-
signal: controller.signal
|
|
1294
|
+
signal: controller.signal,
|
|
1295
|
+
// Disable keepalive for streaming
|
|
1296
|
+
keepalive: false
|
|
1322
1297
|
}).then(async (response) => {
|
|
1323
1298
|
if (!response.ok) {
|
|
1324
1299
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
@@ -1333,35 +1308,47 @@ function createSSEStreamPost(url, body, options = {}) {
|
|
|
1333
1308
|
while (true) {
|
|
1334
1309
|
const { done, value } = await reader.read();
|
|
1335
1310
|
if (done) {
|
|
1311
|
+
stream.emit("done", {});
|
|
1336
1312
|
stream.emit("complete", {});
|
|
1337
1313
|
break;
|
|
1338
1314
|
}
|
|
1339
1315
|
buffer += decoder.decode(value, { stream: true });
|
|
1340
1316
|
const lines = buffer.split("\n");
|
|
1341
1317
|
buffer = lines.pop() || "";
|
|
1342
|
-
let
|
|
1318
|
+
let currentEvent = "message";
|
|
1319
|
+
let currentData = "";
|
|
1343
1320
|
for (const line of lines) {
|
|
1344
|
-
if (line.startsWith("
|
|
1345
|
-
|
|
1346
|
-
} else if (line
|
|
1321
|
+
if (line.startsWith("event:")) {
|
|
1322
|
+
currentEvent = line.slice(6).trim();
|
|
1323
|
+
} else if (line.startsWith("data:")) {
|
|
1324
|
+
currentData = line.slice(5).trim();
|
|
1325
|
+
} else if (line === "" && currentData) {
|
|
1347
1326
|
try {
|
|
1348
|
-
const
|
|
1349
|
-
stream.emit(
|
|
1350
|
-
} catch
|
|
1351
|
-
|
|
1327
|
+
const parsed = JSON.parse(currentData);
|
|
1328
|
+
stream.emit(currentEvent, parsed);
|
|
1329
|
+
} catch {
|
|
1330
|
+
stream.emit(currentEvent, currentData);
|
|
1352
1331
|
}
|
|
1353
|
-
|
|
1332
|
+
currentEvent = "message";
|
|
1333
|
+
currentData = "";
|
|
1354
1334
|
}
|
|
1355
1335
|
}
|
|
1356
1336
|
}
|
|
1357
1337
|
} catch (error) {
|
|
1358
|
-
if (error
|
|
1359
|
-
|
|
1338
|
+
if (error?.name !== "AbortError") {
|
|
1339
|
+
const errorMessage = error?.message || "Stream connection error";
|
|
1340
|
+
stream.emit("error", new Error(errorMessage));
|
|
1341
|
+
}
|
|
1342
|
+
} finally {
|
|
1343
|
+
try {
|
|
1344
|
+
reader.releaseLock();
|
|
1345
|
+
} catch {
|
|
1360
1346
|
}
|
|
1361
1347
|
}
|
|
1362
1348
|
}).catch((error) => {
|
|
1363
|
-
if (error
|
|
1364
|
-
|
|
1349
|
+
if (error?.name !== "AbortError") {
|
|
1350
|
+
const errorMessage = error?.message || "Failed to establish stream connection";
|
|
1351
|
+
stream.emit("error", new Error(errorMessage));
|
|
1365
1352
|
}
|
|
1366
1353
|
});
|
|
1367
1354
|
return stream;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -4,15 +4,6 @@
|
|
|
4
4
|
* Provides a type-safe API for consuming Server-Sent Events
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export interface SSEEvent<T = any> {
|
|
8
|
-
/** Event type (e.g., "token", "tool_call", "done") */
|
|
9
|
-
type: string
|
|
10
|
-
/** Event data payload */
|
|
11
|
-
data: T
|
|
12
|
-
/** Raw event string */
|
|
13
|
-
raw?: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
7
|
type EventHandler<T = any> = (data: T) => void
|
|
17
8
|
|
|
18
9
|
/**
|
|
@@ -34,12 +25,9 @@ export type StreamEventMap = Record<string, any>
|
|
|
34
25
|
*/
|
|
35
26
|
export class StreamController<TEventMap extends StreamEventMap = StreamEventMap> {
|
|
36
27
|
private handlers = new Map<string, Set<EventHandler>>()
|
|
37
|
-
private controller: AbortController
|
|
38
28
|
private _closed = false
|
|
39
29
|
|
|
40
|
-
constructor(private cleanup: () => void) {
|
|
41
|
-
this.controller = new AbortController()
|
|
42
|
-
}
|
|
30
|
+
constructor(private cleanup: () => void) {}
|
|
43
31
|
|
|
44
32
|
/**
|
|
45
33
|
* Register an event handler (fully typed!)
|
|
@@ -47,12 +35,12 @@ export class StreamController<TEventMap extends StreamEventMap = StreamEventMap>
|
|
|
47
35
|
* @param handler - Handler function (data type inferred from event)
|
|
48
36
|
* @returns this (for chaining)
|
|
49
37
|
*/
|
|
50
|
-
on<K extends keyof TEventMap | 'error' | 'complete'>(
|
|
38
|
+
on<K extends keyof TEventMap | 'error' | 'complete' | 'done'>(
|
|
51
39
|
event: K,
|
|
52
40
|
handler: K extends 'error'
|
|
53
41
|
? (error: Error) => void
|
|
54
|
-
: K extends 'complete'
|
|
55
|
-
? () => void
|
|
42
|
+
: K extends 'complete' | 'done'
|
|
43
|
+
? (data?: any) => void
|
|
56
44
|
: K extends keyof TEventMap
|
|
57
45
|
? (data: TEventMap[K]) => void
|
|
58
46
|
: EventHandler
|
|
@@ -64,61 +52,6 @@ export class StreamController<TEventMap extends StreamEventMap = StreamEventMap>
|
|
|
64
52
|
return this
|
|
65
53
|
}
|
|
66
54
|
|
|
67
|
-
/**
|
|
68
|
-
* Register a one-time event handler (fully typed!)
|
|
69
|
-
* @param event - Event type to listen for
|
|
70
|
-
* @param handler - Handler function (called once then removed)
|
|
71
|
-
* @returns this (for chaining)
|
|
72
|
-
*/
|
|
73
|
-
once<K extends keyof TEventMap | 'error' | 'complete'>(
|
|
74
|
-
event: K,
|
|
75
|
-
handler: K extends 'error'
|
|
76
|
-
? (error: Error) => void
|
|
77
|
-
: K extends 'complete'
|
|
78
|
-
? () => void
|
|
79
|
-
: K extends keyof TEventMap
|
|
80
|
-
? (data: TEventMap[K]) => void
|
|
81
|
-
: EventHandler
|
|
82
|
-
): this {
|
|
83
|
-
const wrappedHandler = (data: any) => {
|
|
84
|
-
(handler as any)(data)
|
|
85
|
-
this.off(event, wrappedHandler as any)
|
|
86
|
-
}
|
|
87
|
-
return this.on(event, wrappedHandler as any)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Remove an event handler (fully typed!)
|
|
92
|
-
* @param event - Event type
|
|
93
|
-
* @param handler - Handler to remove
|
|
94
|
-
* @returns this (for chaining)
|
|
95
|
-
*/
|
|
96
|
-
off<K extends keyof TEventMap | 'error' | 'complete'>(
|
|
97
|
-
event: K,
|
|
98
|
-
handler: K extends 'error'
|
|
99
|
-
? (error: Error) => void
|
|
100
|
-
: K extends 'complete'
|
|
101
|
-
? () => void
|
|
102
|
-
: K extends keyof TEventMap
|
|
103
|
-
? (data: TEventMap[K]) => void
|
|
104
|
-
: EventHandler
|
|
105
|
-
): this {
|
|
106
|
-
this.handlers.get(event as string)?.delete(handler as EventHandler)
|
|
107
|
-
return this
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Remove all handlers for an event (or all events if no event specified)
|
|
112
|
-
*/
|
|
113
|
-
removeAllListeners(event?: keyof TEventMap | 'error' | 'complete'): this {
|
|
114
|
-
if (event === undefined) {
|
|
115
|
-
this.handlers.clear()
|
|
116
|
-
} else {
|
|
117
|
-
this.handlers.delete(event as string)
|
|
118
|
-
}
|
|
119
|
-
return this
|
|
120
|
-
}
|
|
121
|
-
|
|
122
55
|
/**
|
|
123
56
|
* Emit an event to all registered handlers
|
|
124
57
|
* @internal
|
|
@@ -141,16 +74,8 @@ export class StreamController<TEventMap extends StreamEventMap = StreamEventMap>
|
|
|
141
74
|
*/
|
|
142
75
|
close(): void {
|
|
143
76
|
if (!this._closed) {
|
|
144
|
-
this.controller.abort()
|
|
145
77
|
this.cleanup()
|
|
146
78
|
this._closed = true
|
|
147
79
|
}
|
|
148
80
|
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Check if stream is closed
|
|
152
|
-
*/
|
|
153
|
-
get closed(): boolean {
|
|
154
|
-
return this._closed
|
|
155
|
-
}
|
|
156
81
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import type { StreamEventMap } from './StreamController'
|
|
11
11
|
import { StreamController } from './StreamController'
|
|
12
12
|
|
|
13
|
-
export {
|
|
13
|
+
export { StreamController, type StreamEventMap } from './StreamController'
|
|
14
14
|
|
|
15
15
|
export interface SSEStreamOptions {
|
|
16
16
|
/** Additional headers to send with the request */
|
|
@@ -49,11 +49,14 @@ export function createSSEStream<TEventMap extends StreamEventMap = StreamEventMa
|
|
|
49
49
|
fetch(url, {
|
|
50
50
|
method: 'GET',
|
|
51
51
|
headers: {
|
|
52
|
-
Accept: 'text/event-stream',
|
|
52
|
+
'Accept': 'text/event-stream',
|
|
53
|
+
'Cache-Control': 'no-cache',
|
|
53
54
|
...headers,
|
|
54
55
|
},
|
|
55
56
|
credentials: withCredentials ? 'include' : 'same-origin',
|
|
56
57
|
signal: controller.signal,
|
|
58
|
+
// Disable keepalive for streaming
|
|
59
|
+
keepalive: false,
|
|
57
60
|
})
|
|
58
61
|
.then(async (response) => {
|
|
59
62
|
if (!response.ok) {
|
|
@@ -73,6 +76,7 @@ export function createSSEStream<TEventMap extends StreamEventMap = StreamEventMa
|
|
|
73
76
|
const { done, value } = await reader.read()
|
|
74
77
|
|
|
75
78
|
if (done) {
|
|
79
|
+
stream.emit('done', {})
|
|
76
80
|
stream.emit('complete', {})
|
|
77
81
|
break
|
|
78
82
|
}
|
|
@@ -84,33 +88,48 @@ export function createSSEStream<TEventMap extends StreamEventMap = StreamEventMa
|
|
|
84
88
|
const lines = buffer.split('\n')
|
|
85
89
|
buffer = lines.pop() || '' // Keep incomplete line in buffer
|
|
86
90
|
|
|
87
|
-
let
|
|
91
|
+
let currentEvent = 'message'
|
|
92
|
+
let currentData = ''
|
|
88
93
|
|
|
89
94
|
for (const line of lines) {
|
|
90
|
-
if (line.startsWith('
|
|
91
|
-
|
|
92
|
-
} else if (line
|
|
95
|
+
if (line.startsWith('event:')) {
|
|
96
|
+
currentEvent = line.slice(6).trim()
|
|
97
|
+
} else if (line.startsWith('data:')) {
|
|
98
|
+
currentData = line.slice(5).trim()
|
|
99
|
+
} else if (line === '' && currentData) {
|
|
93
100
|
// Empty line indicates end of event - emit immediately
|
|
94
101
|
try {
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
const parsed = JSON.parse(currentData)
|
|
103
|
+
stream.emit(currentEvent, parsed)
|
|
104
|
+
} catch {
|
|
105
|
+
// If not valid JSON, emit as string
|
|
106
|
+
stream.emit(currentEvent, currentData)
|
|
100
107
|
}
|
|
101
|
-
|
|
108
|
+
// Reset for next event
|
|
109
|
+
currentEvent = 'message'
|
|
110
|
+
currentData = ''
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
}
|
|
105
|
-
} catch (error) {
|
|
106
|
-
|
|
107
|
-
|
|
114
|
+
} catch (error: any) {
|
|
115
|
+
// Handle all stream errors (network errors, protocol errors, etc.)
|
|
116
|
+
if (error?.name !== 'AbortError') {
|
|
117
|
+
const errorMessage = error?.message || 'Stream connection error'
|
|
118
|
+
stream.emit('error', new Error(errorMessage))
|
|
119
|
+
}
|
|
120
|
+
} finally {
|
|
121
|
+
// Clean up reader
|
|
122
|
+
try {
|
|
123
|
+
reader.releaseLock()
|
|
124
|
+
} catch {
|
|
125
|
+
// Ignore cleanup errors
|
|
108
126
|
}
|
|
109
127
|
}
|
|
110
128
|
})
|
|
111
|
-
.catch((error) => {
|
|
112
|
-
if (error
|
|
113
|
-
|
|
129
|
+
.catch((error: any) => {
|
|
130
|
+
if (error?.name !== 'AbortError') {
|
|
131
|
+
const errorMessage = error?.message || 'Failed to establish stream connection'
|
|
132
|
+
stream.emit('error', new Error(errorMessage))
|
|
114
133
|
}
|
|
115
134
|
})
|
|
116
135
|
|
|
@@ -148,11 +167,14 @@ export function createSSEStreamPost<TEventMap extends StreamEventMap = StreamEve
|
|
|
148
167
|
headers: {
|
|
149
168
|
'Accept': 'text/event-stream',
|
|
150
169
|
'Content-Type': 'application/json',
|
|
170
|
+
'Cache-Control': 'no-cache',
|
|
151
171
|
...headers,
|
|
152
172
|
},
|
|
153
173
|
credentials: withCredentials ? 'include' : 'same-origin',
|
|
154
174
|
body: JSON.stringify(body),
|
|
155
175
|
signal: controller.signal,
|
|
176
|
+
// Disable keepalive for streaming
|
|
177
|
+
keepalive: false,
|
|
156
178
|
})
|
|
157
179
|
.then(async (response) => {
|
|
158
180
|
if (!response.ok) {
|
|
@@ -172,6 +194,7 @@ export function createSSEStreamPost<TEventMap extends StreamEventMap = StreamEve
|
|
|
172
194
|
const { done, value } = await reader.read()
|
|
173
195
|
|
|
174
196
|
if (done) {
|
|
197
|
+
stream.emit('done', {})
|
|
175
198
|
stream.emit('complete', {})
|
|
176
199
|
break
|
|
177
200
|
}
|
|
@@ -183,33 +206,48 @@ export function createSSEStreamPost<TEventMap extends StreamEventMap = StreamEve
|
|
|
183
206
|
const lines = buffer.split('\n')
|
|
184
207
|
buffer = lines.pop() || '' // Keep incomplete line in buffer
|
|
185
208
|
|
|
186
|
-
let
|
|
209
|
+
let currentEvent = 'message'
|
|
210
|
+
let currentData = ''
|
|
187
211
|
|
|
188
212
|
for (const line of lines) {
|
|
189
|
-
if (line.startsWith('
|
|
190
|
-
|
|
191
|
-
} else if (line
|
|
213
|
+
if (line.startsWith('event:')) {
|
|
214
|
+
currentEvent = line.slice(6).trim()
|
|
215
|
+
} else if (line.startsWith('data:')) {
|
|
216
|
+
currentData = line.slice(5).trim()
|
|
217
|
+
} else if (line === '' && currentData) {
|
|
192
218
|
// Empty line indicates end of event - emit immediately
|
|
193
219
|
try {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
220
|
+
const parsed = JSON.parse(currentData)
|
|
221
|
+
stream.emit(currentEvent, parsed)
|
|
222
|
+
} catch {
|
|
223
|
+
// If not valid JSON, emit as string
|
|
224
|
+
stream.emit(currentEvent, currentData)
|
|
199
225
|
}
|
|
200
|
-
|
|
226
|
+
// Reset for next event
|
|
227
|
+
currentEvent = 'message'
|
|
228
|
+
currentData = ''
|
|
201
229
|
}
|
|
202
230
|
}
|
|
203
231
|
}
|
|
204
|
-
} catch (error) {
|
|
205
|
-
|
|
206
|
-
|
|
232
|
+
} catch (error: any) {
|
|
233
|
+
// Handle all stream errors (network errors, protocol errors, etc.)
|
|
234
|
+
if (error?.name !== 'AbortError') {
|
|
235
|
+
const errorMessage = error?.message || 'Stream connection error'
|
|
236
|
+
stream.emit('error', new Error(errorMessage))
|
|
237
|
+
}
|
|
238
|
+
} finally {
|
|
239
|
+
// Clean up reader
|
|
240
|
+
try {
|
|
241
|
+
reader.releaseLock()
|
|
242
|
+
} catch {
|
|
243
|
+
// Ignore cleanup errors
|
|
207
244
|
}
|
|
208
245
|
}
|
|
209
246
|
})
|
|
210
|
-
.catch((error) => {
|
|
211
|
-
if (error
|
|
212
|
-
|
|
247
|
+
.catch((error: any) => {
|
|
248
|
+
if (error?.name !== 'AbortError') {
|
|
249
|
+
const errorMessage = error?.message || 'Failed to establish stream connection'
|
|
250
|
+
stream.emit('error', new Error(errorMessage))
|
|
213
251
|
}
|
|
214
252
|
})
|
|
215
253
|
|