@bagelink/sdk 1.7.101 → 1.8.3

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.mjs CHANGED
@@ -1,5 +1,111 @@
1
1
  import axios$1 from 'axios';
2
2
 
3
+ function isSSEStream(operation) {
4
+ const responses = operation.responses || {};
5
+ for (const [statusCode, response] of Object.entries(responses)) {
6
+ if (!statusCode.startsWith("2")) continue;
7
+ if ("$ref" in response) continue;
8
+ const content = response.content || {};
9
+ const contentTypes = Object.keys(content);
10
+ if (contentTypes.length > 0) {
11
+ if ("text/event-stream" in content) {
12
+ return true;
13
+ }
14
+ }
15
+ }
16
+ const hasDefinedContentTypes = Object.entries(responses).some(([statusCode, response]) => {
17
+ if (!statusCode.startsWith("2")) return false;
18
+ if ("$ref" in response) return false;
19
+ const content = response.content || {};
20
+ return Object.keys(content).length > 0;
21
+ });
22
+ if (hasDefinedContentTypes) {
23
+ return false;
24
+ }
25
+ const description = operation.description?.toLowerCase() || "";
26
+ const summary = operation.summary?.toLowerCase() || "";
27
+ const hasExplicitSSEMention = /\bsse\b/.test(description) || /server-sent events/i.test(description) || /text\/event-stream/i.test(description) || /\bsse\b/.test(summary);
28
+ return hasExplicitSSEMention;
29
+ }
30
+ function extractSSEEventTypes(operation) {
31
+ const description = operation.description || "";
32
+ const types = /* @__PURE__ */ new Set();
33
+ const markdownMatches = description.matchAll(/^\d+\.\s+\*\*([a-z_]+)\*\*/gm);
34
+ for (const match of markdownMatches) {
35
+ types.add(match[1]);
36
+ }
37
+ const typeMatches = description.matchAll(/type:\s*"([^"]+)"/g);
38
+ for (const match of typeMatches) {
39
+ types.add(match[1]);
40
+ }
41
+ const responses = operation.responses || {};
42
+ for (const response of Object.values(responses)) {
43
+ if ("content" in response) {
44
+ const content = response.content || {};
45
+ const eventStream = content["text/event-stream"];
46
+ if (eventStream?.example) {
47
+ const exampleMatches = eventStream.example.matchAll(/^event:\s*([a-z_]+)/gm);
48
+ for (const match of exampleMatches) {
49
+ types.add(match[1]);
50
+ }
51
+ }
52
+ }
53
+ }
54
+ return types.size > 0 ? Array.from(types).sort() : void 0;
55
+ }
56
+ function extractSSEEventInfo(operation) {
57
+ const description = operation.description || "";
58
+ const eventInfos = [];
59
+ const eventSections = description.split(/(?=^\d+\.\s+\*\*)/m);
60
+ for (const section of eventSections) {
61
+ const eventMatch = section.match(/^\d+\.\s+\*\*([a-z_]+)\*\*:\s*([^\n]+)/m);
62
+ if (!eventMatch) continue;
63
+ const [, eventName, eventDescription] = eventMatch;
64
+ const fields = [];
65
+ const fieldMatches = section.matchAll(/^\s+[-*]\s+`([^`]+)`:\s*([^\n]+)/gm);
66
+ for (const fieldMatch of fieldMatches) {
67
+ const [, fieldName, fieldDescription] = fieldMatch;
68
+ fields.push({
69
+ name: fieldName,
70
+ description: fieldDescription.trim()
71
+ });
72
+ }
73
+ eventInfos.push({
74
+ name: eventName,
75
+ description: eventDescription,
76
+ fields: fields.length > 0 ? fields : void 0
77
+ });
78
+ }
79
+ if (eventInfos.length === 0) {
80
+ const responses = operation.responses || {};
81
+ for (const response of Object.values(responses)) {
82
+ if ("content" in response) {
83
+ const content = response.content || {};
84
+ const eventStream = content["text/event-stream"];
85
+ if (eventStream?.example) {
86
+ const exampleMatches = eventStream.example.matchAll(/data:\s*(\{[^}]+\})/g);
87
+ const seenEvents = /* @__PURE__ */ new Set();
88
+ for (const match of exampleMatches) {
89
+ try {
90
+ const data = JSON.parse(match[1]);
91
+ if (data.type && !seenEvents.has(data.type)) {
92
+ seenEvents.add(data.type);
93
+ const fields = Object.keys(data).filter((key) => key !== "type" && key !== "sequence").map((key) => ({ name: key }));
94
+ eventInfos.push({
95
+ name: data.type,
96
+ fields: fields.length > 0 ? fields : void 0
97
+ });
98
+ }
99
+ } catch {
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ return eventInfos.length > 0 ? eventInfos : void 0;
107
+ }
108
+
3
109
  function getPath(pathsObject, path) {
4
110
  return pathsObject ? pathsObject[path] : void 0;
5
111
  }
@@ -278,6 +384,8 @@ const primitiveTypes = [
278
384
  ];
279
385
  const functionsInventory = {};
280
386
  const pathOperations = [];
387
+ const streamEventTypes = {};
388
+ const streamEventInfo = {};
281
389
  function collectTypeForImportStatement(typeName) {
282
390
  typeName = typeName.trim().replace("[]", "");
283
391
  if (typeName.includes("|")) {
@@ -441,6 +549,77 @@ function combineAllParams(parameters, requestBodyParam) {
441
549
  }).join(", ");
442
550
  return `{ ${destructuredNames} }: { ${typeDefinition} } = {}`;
443
551
  }
552
+ function generateStreamTypeName(path) {
553
+ return `${toPascalCase(
554
+ path.split("/").filter((p) => p && !/\{|\}/.test(p)).join("_")
555
+ )}StreamEvents`;
556
+ }
557
+ function generateStreamFunction(method, path, formattedPath, allParams, requestBodyPayload, eventTypes) {
558
+ if (allParams === "undefined") {
559
+ allParams = "";
560
+ }
561
+ const bodyVar = requestBodyPayload || "{}";
562
+ const baseUrlRef = 'axios.defaults.baseURL || ""';
563
+ const streamTypeName = generateStreamTypeName(path);
564
+ if (eventTypes?.length) {
565
+ streamEventTypes[streamTypeName] = eventTypes;
566
+ }
567
+ const eventTypesComment = eventTypes?.length ? `
568
+ * Event types: ${eventTypes.map((t) => `"${t}"`).join(", ")}` : "";
569
+ const eventTypesExample = eventTypes?.length ? eventTypes.map((t) => `
570
+ * .on('${t}', (data) => console.log(data))`).join("") : "\n * .on('message', (data) => console.log(data))";
571
+ const typeAnnotation = eventTypes?.length ? `StreamController<${streamTypeName}>` : "StreamController";
572
+ const pathForUrl = formattedPath.startsWith("`") ? formattedPath.slice(1, -1) : formattedPath.slice(1, -1);
573
+ if (method === "post") {
574
+ return `{
575
+ /**
576
+ * Stream SSE events from this endpoint (returns StreamController)${eventTypesComment}
577
+ *
578
+ * @example
579
+ * const stream = api.endpoint.stream(params)${eventTypesExample}
580
+ * .on('error', (err) => console.error(err))
581
+ * .on('complete', () => console.log('Done!'))
582
+ *
583
+ * // Close stream when needed
584
+ * stream.close()
585
+ */
586
+ stream: (${allParams}, options?: SSEStreamOptions): ${typeAnnotation} => {
587
+ const url = \`\${${baseUrlRef}}${pathForUrl}\`
588
+ return createSSEStreamPost<${streamTypeName}>(url, ${bodyVar}, options)
589
+ },
590
+ /**
591
+ * Regular POST request (non-streaming)
592
+ */
593
+ post: async (${allParams}): Promise<AxiosResponse<any>> => {
594
+ return axios.post(${formattedPath}, ${bodyVar})
595
+ }
596
+ }`;
597
+ } else {
598
+ return `{
599
+ /**
600
+ * Stream SSE events from this endpoint (returns StreamController)${eventTypesComment}
601
+ *
602
+ * @example
603
+ * const stream = api.endpoint.stream(params)${eventTypesExample}
604
+ * .on('error', (err) => console.error(err))
605
+ * .on('complete', () => console.log('Done!'))
606
+ *
607
+ * // Close stream when needed
608
+ * stream.close()
609
+ */
610
+ stream: (${allParams}, options?: SSEStreamOptions): ${typeAnnotation} => {
611
+ const url = \`\${${baseUrlRef}}${pathForUrl}\`
612
+ return createSSEStream<${streamTypeName}>(url, options)
613
+ },
614
+ /**
615
+ * Regular GET request (non-streaming)
616
+ */
617
+ get: async (${allParams}): Promise<AxiosResponse<any>> => {
618
+ return axios.get(${formattedPath})
619
+ }
620
+ }`;
621
+ }
622
+ }
444
623
  function generateAxiosFunction(method, formattedPath, allParams, responseTypeStr, parameters, requestBodyPayload) {
445
624
  if (allParams === "undefined") {
446
625
  allParams = "";
@@ -498,6 +677,7 @@ function generateFunctionForOperation(method, path, operation) {
498
677
  if (!operation) {
499
678
  return "";
500
679
  }
680
+ const isStream = isSSEStream(operation);
501
681
  const methodLower = method.toLowerCase();
502
682
  if (["get", "delete"].includes(methodLower) && operation.requestBody) {
503
683
  const requestBodyDeref = dereference(operation.requestBody);
@@ -526,6 +706,22 @@ function generateFunctionForOperation(method, path, operation) {
526
706
  const responseType = generateResponseType(operation.responses);
527
707
  const responseTypeStr = responseType ? `: Promise<AxiosResponse<${responseType}>>` : "";
528
708
  const functionComment = buildJSDocComment(operation, method, path);
709
+ if (isStream) {
710
+ const eventTypes = extractSSEEventTypes(operation);
711
+ const eventInfo = extractSSEEventInfo(operation);
712
+ const streamTypeName = generateStreamTypeName(path);
713
+ if (eventInfo?.length) {
714
+ streamEventInfo[streamTypeName] = eventInfo;
715
+ }
716
+ return functionComment + generateStreamFunction(
717
+ method,
718
+ path,
719
+ formatPathWithParams(path),
720
+ allParams,
721
+ requestBodyPayload,
722
+ eventTypes
723
+ );
724
+ }
529
725
  return functionComment + generateAxiosFunction(
530
726
  method,
531
727
  formatPathWithParams(path),
@@ -621,10 +817,79 @@ export const ${parent} = ${JSON.stringify(object, void 0, 2)};
621
817
  });
622
818
  return fileTemplate(tsString, allTypes, baseUrl);
623
819
  }
820
+ function generateStreamEventTypeDefinitions() {
821
+ if (Object.keys(streamEventTypes).length === 0) {
822
+ return "";
823
+ }
824
+ let typeDefs = "\n// ============================================================================\n";
825
+ typeDefs += "// Stream Event Type Definitions (Fully Typed!)\n";
826
+ typeDefs += "// ============================================================================\n\n";
827
+ for (const [typeName, events] of Object.entries(streamEventTypes)) {
828
+ const eventInfo = streamEventInfo[typeName];
829
+ typeDefs += `/**
830
+ * Event types for ${typeName.replace("StreamEvents", "")} stream
831
+ `;
832
+ typeDefs += ` * Events: ${events.map((e) => `"${e}"`).join(", ")}
833
+ `;
834
+ if (eventInfo?.length) {
835
+ typeDefs += ` *
836
+ `;
837
+ for (const info of eventInfo) {
838
+ typeDefs += ` * - **${info.name}**: ${info.description || "Event data"}
839
+ `;
840
+ }
841
+ }
842
+ typeDefs += ` */
843
+ `;
844
+ typeDefs += `export interface ${typeName} {
845
+ `;
846
+ if (eventInfo?.length) {
847
+ for (const info of eventInfo) {
848
+ typeDefs += ` /**
849
+ * ${info.description || info.name}
850
+ `;
851
+ if (info.fields?.length) {
852
+ info.fields.forEach((field) => {
853
+ typeDefs += ` * - \`${field.name}\`: ${field.description || "Field data"}
854
+ `;
855
+ });
856
+ }
857
+ typeDefs += ` */
858
+ `;
859
+ if (info.fields?.length) {
860
+ typeDefs += ` ${info.name}: {
861
+ `;
862
+ for (const field of info.fields) {
863
+ typeDefs += ` /** ${field.description || field.name} */
864
+ `;
865
+ typeDefs += ` ${field.name}: any
866
+ `;
867
+ }
868
+ typeDefs += ` }
869
+ `;
870
+ } else {
871
+ typeDefs += ` ${info.name}: any
872
+ `;
873
+ }
874
+ }
875
+ } else {
876
+ for (const event of events) {
877
+ typeDefs += ` /** ${event} event data */
878
+ `;
879
+ typeDefs += ` ${event}: any
880
+ `;
881
+ }
882
+ }
883
+ typeDefs += "}\n\n";
884
+ }
885
+ return typeDefs;
886
+ }
624
887
  function fileTemplate(tsString, typeForImport, baseURL) {
888
+ const streamTypeDefs = generateStreamEventTypeDefinitions();
625
889
  const templateCode = `import ax from 'axios';
626
890
  import type { AxiosResponse } from 'axios';
627
891
  import type { ${typeForImport.join(", ")} } from './types.d';
892
+ import { createSSEStream, createSSEStreamPost, StreamController, type SSEStreamOptions, type SSEEvent } from './streamClient';
628
893
 
629
894
  /**
630
895
  * Options for file upload operations
@@ -638,6 +903,24 @@ export interface UploadOptions {
638
903
  tags?: string[]
639
904
  }
640
905
 
906
+ /**
907
+ * Export SSE stream utilities for direct use
908
+ *
909
+ * @example Chainable event handlers
910
+ * const stream = createSSEStream(url)
911
+ * .on('token', (data) => console.log(data))
912
+ * .on('done', () => console.log('Complete!'))
913
+ *
914
+ * @example Async iteration
915
+ * for await (const event of createSSEStream(url)) {
916
+ * console.log(event.type, event.data)
917
+ * }
918
+ *
919
+ * @example Promise-based
920
+ * const result = await createSSEStream(url).toPromise()
921
+ */
922
+ export { createSSEStream, createSSEStreamPost, StreamController, type SSEStreamOptions, type SSEEvent } from './streamClient';
923
+ ${streamTypeDefs}
641
924
  /**
642
925
  * Configured axios instance for API requests
643
926
  * @example
@@ -691,6 +974,430 @@ function generateTypes(schemas) {
691
974
  }).join("\n");
692
975
  }
693
976
 
977
+ class StreamController {
978
+ constructor(streamFn) {
979
+ this.streamFn = streamFn;
980
+ this.abortController = new AbortController();
981
+ this.start();
982
+ }
983
+ handlers = /* @__PURE__ */ new Map();
984
+ errorHandlers = /* @__PURE__ */ new Set();
985
+ completeHandlers = /* @__PURE__ */ new Set();
986
+ abortController;
987
+ _closed = false;
988
+ _promise = null;
989
+ _resolvePromise = null;
990
+ _rejectPromise = null;
991
+ start() {
992
+ const cleanup = this.streamFn(
993
+ (event) => {
994
+ this.emit(event.type, event.data);
995
+ },
996
+ (error) => {
997
+ this.emitError(error);
998
+ },
999
+ () => {
1000
+ this.emitComplete();
1001
+ }
1002
+ );
1003
+ this.abortController.signal.addEventListener("abort", () => {
1004
+ cleanup();
1005
+ this._closed = true;
1006
+ });
1007
+ }
1008
+ /**
1009
+ * Register an event handler (fully typed!)
1010
+ * @param event - Event type to listen for
1011
+ * @param handler - Handler function (data type inferred from event)
1012
+ * @returns this (for chaining)
1013
+ */
1014
+ on(event, handler) {
1015
+ if (event === "error") {
1016
+ this.errorHandlers.add(handler);
1017
+ } else if (event === "complete") {
1018
+ this.completeHandlers.add(handler);
1019
+ } else {
1020
+ if (!this.handlers.has(event)) {
1021
+ this.handlers.set(event, /* @__PURE__ */ new Set());
1022
+ }
1023
+ this.handlers.get(event).add(handler);
1024
+ }
1025
+ return this;
1026
+ }
1027
+ /**
1028
+ * Register a one-time event handler (fully typed!)
1029
+ * @param event - Event type to listen for
1030
+ * @param handler - Handler function (called once then removed, data type inferred from event)
1031
+ * @returns this (for chaining)
1032
+ */
1033
+ once(event, handler) {
1034
+ const wrappedHandler = (data) => {
1035
+ handler(data);
1036
+ this.off(event, wrappedHandler);
1037
+ };
1038
+ return this.on(event, wrappedHandler);
1039
+ }
1040
+ /**
1041
+ * Remove an event handler (fully typed!)
1042
+ * @param event - Event type
1043
+ * @param handler - Handler to remove
1044
+ * @returns this (for chaining)
1045
+ */
1046
+ off(event, handler) {
1047
+ if (event === "error") {
1048
+ this.errorHandlers.delete(handler);
1049
+ } else if (event === "complete") {
1050
+ this.completeHandlers.delete(handler);
1051
+ } else {
1052
+ this.handlers.get(event)?.delete(handler);
1053
+ }
1054
+ return this;
1055
+ }
1056
+ /**
1057
+ * Remove all handlers for an event (or all events if no event specified)
1058
+ */
1059
+ removeAllListeners(event) {
1060
+ if (event === void 0) {
1061
+ this.handlers.clear();
1062
+ this.errorHandlers.clear();
1063
+ this.completeHandlers.clear();
1064
+ } else if (event === "error") {
1065
+ this.errorHandlers.clear();
1066
+ } else if (event === "complete") {
1067
+ this.completeHandlers.clear();
1068
+ } else {
1069
+ this.handlers.delete(event);
1070
+ }
1071
+ return this;
1072
+ }
1073
+ emit(event, data) {
1074
+ const handlers = this.handlers.get(event);
1075
+ if (handlers) {
1076
+ handlers.forEach((handler) => {
1077
+ try {
1078
+ handler(data);
1079
+ } catch (error) {
1080
+ console.error(`Error in handler for event "${event}":`, error);
1081
+ }
1082
+ });
1083
+ }
1084
+ if (event === "done" && this._resolvePromise) {
1085
+ this._resolvePromise(data);
1086
+ }
1087
+ }
1088
+ emitError(error) {
1089
+ this.errorHandlers.forEach((handler) => {
1090
+ try {
1091
+ handler(error);
1092
+ } catch (err) {
1093
+ console.error("Error in error handler:", err);
1094
+ }
1095
+ });
1096
+ if (this._rejectPromise) {
1097
+ this._rejectPromise(error);
1098
+ }
1099
+ }
1100
+ emitComplete() {
1101
+ this.completeHandlers.forEach((handler) => {
1102
+ try {
1103
+ handler();
1104
+ } catch (error) {
1105
+ console.error("Error in complete handler:", error);
1106
+ }
1107
+ });
1108
+ }
1109
+ /**
1110
+ * Close the stream
1111
+ */
1112
+ close() {
1113
+ if (!this._closed) {
1114
+ this.abortController.abort();
1115
+ this._closed = true;
1116
+ }
1117
+ }
1118
+ /**
1119
+ * Check if stream is closed
1120
+ */
1121
+ get closed() {
1122
+ return this._closed;
1123
+ }
1124
+ /**
1125
+ * Convert stream to a Promise that resolves when 'done' event fires
1126
+ * @returns Promise that resolves with the 'done' event data
1127
+ */
1128
+ toPromise() {
1129
+ if (!this._promise) {
1130
+ this._promise = new Promise((resolve, reject) => {
1131
+ this._resolvePromise = resolve;
1132
+ this._rejectPromise = reject;
1133
+ });
1134
+ }
1135
+ return this._promise;
1136
+ }
1137
+ /**
1138
+ * Make the stream async iterable
1139
+ * Usage: for await (const event of stream) { ... }
1140
+ */
1141
+ async *[Symbol.asyncIterator]() {
1142
+ const events = [];
1143
+ let resolveNext = null;
1144
+ let done = false;
1145
+ const eventHandler = (type) => (data) => {
1146
+ const event = { type, data };
1147
+ if (resolveNext) {
1148
+ resolveNext(event);
1149
+ resolveNext = null;
1150
+ } else {
1151
+ events.push(event);
1152
+ }
1153
+ };
1154
+ const originalEmit = this.emit.bind(this);
1155
+ const capturedEvents = [];
1156
+ this.emit = (event, data) => {
1157
+ if (!capturedEvents.includes(event)) {
1158
+ capturedEvents.push(event);
1159
+ this.on(event, eventHandler(event));
1160
+ }
1161
+ originalEmit(event, data);
1162
+ };
1163
+ this.on("complete", () => {
1164
+ done = true;
1165
+ if (resolveNext) {
1166
+ resolveNext(null);
1167
+ resolveNext = null;
1168
+ }
1169
+ });
1170
+ try {
1171
+ while (!done) {
1172
+ if (events.length > 0) {
1173
+ const event = events.shift();
1174
+ if (event) yield event;
1175
+ } else {
1176
+ const event = await new Promise((resolve) => {
1177
+ resolveNext = resolve;
1178
+ });
1179
+ if (event === null) break;
1180
+ yield event;
1181
+ }
1182
+ }
1183
+ } finally {
1184
+ this.close();
1185
+ }
1186
+ }
1187
+ /**
1188
+ * Collect all events into an array until stream completes
1189
+ * @returns Promise<SSEEvent[]>
1190
+ */
1191
+ async toArray() {
1192
+ const events = [];
1193
+ for await (const event of this) {
1194
+ events.push(event);
1195
+ }
1196
+ return events;
1197
+ }
1198
+ /**
1199
+ * Pipe stream events through a transform function
1200
+ */
1201
+ map(transform) {
1202
+ const mappedController = new StreamController(
1203
+ (onEvent, onError, onComplete) => {
1204
+ this.on("error", onError);
1205
+ this.on("complete", onComplete);
1206
+ this.handlers.forEach((_, eventType) => {
1207
+ this.on(eventType, (data) => {
1208
+ try {
1209
+ const transformed = transform({ type: eventType, data });
1210
+ onEvent({ type: eventType, data: transformed, raw: void 0 });
1211
+ } catch (error) {
1212
+ onError(error);
1213
+ }
1214
+ });
1215
+ });
1216
+ return () => {
1217
+ this.close();
1218
+ };
1219
+ }
1220
+ );
1221
+ return mappedController;
1222
+ }
1223
+ /**
1224
+ * Filter events based on a predicate
1225
+ */
1226
+ filter(predicate) {
1227
+ const filteredController = new StreamController(
1228
+ (onEvent, onError, onComplete) => {
1229
+ this.on("error", onError);
1230
+ this.on("complete", onComplete);
1231
+ this.handlers.forEach((_, eventType) => {
1232
+ this.on(eventType, (data) => {
1233
+ const event = { type: eventType, data };
1234
+ if (predicate(event)) {
1235
+ onEvent(event);
1236
+ }
1237
+ });
1238
+ });
1239
+ return () => {
1240
+ this.close();
1241
+ };
1242
+ }
1243
+ );
1244
+ return filteredController;
1245
+ }
1246
+ }
1247
+
1248
+ function createSSEStream(url, options = {}) {
1249
+ const { headers = {}, withCredentials = true } = options;
1250
+ return new StreamController((onEvent, onError, onComplete) => {
1251
+ const controller = new AbortController();
1252
+ const { signal } = controller;
1253
+ fetch(url, {
1254
+ method: "GET",
1255
+ headers: {
1256
+ Accept: "text/event-stream",
1257
+ ...headers
1258
+ },
1259
+ credentials: withCredentials ? "include" : "same-origin",
1260
+ signal
1261
+ }).then(async (response) => {
1262
+ if (!response.ok) {
1263
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1264
+ }
1265
+ if (!response.body) {
1266
+ throw new Error("Response body is null");
1267
+ }
1268
+ const reader = response.body.getReader();
1269
+ const decoder = new TextDecoder();
1270
+ let buffer = "";
1271
+ try {
1272
+ while (true) {
1273
+ const { done, value } = await reader.read();
1274
+ if (done) {
1275
+ onComplete();
1276
+ break;
1277
+ }
1278
+ buffer += decoder.decode(value, { stream: true });
1279
+ const lines = buffer.split("\n");
1280
+ buffer = lines.pop() || "";
1281
+ let eventType = "";
1282
+ let eventData = "";
1283
+ for (const line of lines) {
1284
+ if (line.startsWith("event:")) {
1285
+ eventType = line.slice(6).trim();
1286
+ } else if (line.startsWith("data:")) {
1287
+ eventData = line.slice(5).trim();
1288
+ } else if (line === "" && eventData) {
1289
+ try {
1290
+ const parsedData = JSON.parse(eventData);
1291
+ onEvent({
1292
+ type: eventType || "message",
1293
+ data: parsedData,
1294
+ raw: eventData
1295
+ });
1296
+ } catch {
1297
+ onEvent({
1298
+ type: eventType || "message",
1299
+ data: eventData,
1300
+ raw: eventData
1301
+ });
1302
+ }
1303
+ eventType = "";
1304
+ eventData = "";
1305
+ }
1306
+ }
1307
+ }
1308
+ } catch (error) {
1309
+ if (error instanceof Error && error.name !== "AbortError") {
1310
+ onError(error);
1311
+ }
1312
+ }
1313
+ }).catch((error) => {
1314
+ if (error.name !== "AbortError") {
1315
+ onError(error);
1316
+ }
1317
+ });
1318
+ return () => {
1319
+ controller.abort();
1320
+ };
1321
+ });
1322
+ }
1323
+ function createSSEStreamPost(url, body, options = {}) {
1324
+ const { headers = {}, withCredentials = true } = options;
1325
+ return new StreamController((onEvent, onError, onComplete) => {
1326
+ const controller = new AbortController();
1327
+ const { signal } = controller;
1328
+ fetch(url, {
1329
+ method: "POST",
1330
+ headers: {
1331
+ "Accept": "text/event-stream",
1332
+ "Content-Type": "application/json",
1333
+ ...headers
1334
+ },
1335
+ credentials: withCredentials ? "include" : "same-origin",
1336
+ body: JSON.stringify(body),
1337
+ signal
1338
+ }).then(async (response) => {
1339
+ if (!response.ok) {
1340
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1341
+ }
1342
+ if (!response.body) {
1343
+ throw new Error("Response body is null");
1344
+ }
1345
+ const reader = response.body.getReader();
1346
+ const decoder = new TextDecoder();
1347
+ let buffer = "";
1348
+ try {
1349
+ while (true) {
1350
+ const { done, value } = await reader.read();
1351
+ if (done) {
1352
+ onComplete();
1353
+ break;
1354
+ }
1355
+ buffer += decoder.decode(value, { stream: true });
1356
+ const lines = buffer.split("\n");
1357
+ buffer = lines.pop() || "";
1358
+ let eventType = "";
1359
+ let eventData = "";
1360
+ for (const line of lines) {
1361
+ if (line.startsWith("event:")) {
1362
+ eventType = line.slice(6).trim();
1363
+ } else if (line.startsWith("data:")) {
1364
+ eventData = line.slice(5).trim();
1365
+ } else if (line === "" && eventData) {
1366
+ try {
1367
+ const parsedData = JSON.parse(eventData);
1368
+ onEvent({
1369
+ type: eventType || "message",
1370
+ data: parsedData,
1371
+ raw: eventData
1372
+ });
1373
+ } catch {
1374
+ onEvent({
1375
+ type: eventType || "message",
1376
+ data: eventData,
1377
+ raw: eventData
1378
+ });
1379
+ }
1380
+ eventType = "";
1381
+ eventData = "";
1382
+ }
1383
+ }
1384
+ }
1385
+ } catch (error) {
1386
+ if (error instanceof Error && error.name !== "AbortError") {
1387
+ onError(error);
1388
+ }
1389
+ }
1390
+ }).catch((error) => {
1391
+ if (error.name !== "AbortError") {
1392
+ onError(error);
1393
+ }
1394
+ });
1395
+ return () => {
1396
+ controller.abort();
1397
+ };
1398
+ });
1399
+ }
1400
+
694
1401
  const basicAuthHeader = { Authorization: "Basic YmFnZWxfdXNlcm5hbWU6Tm90U2VjdXJlQGJhZ2Vs" };
695
1402
  const index = async (openApiUrl, baseUrl) => {
696
1403
  try {
@@ -1000,4 +1707,4 @@ class Bagel {
1000
1707
  }
1001
1708
  }
1002
1709
 
1003
- export { Bagel, dereference, formatAPIErrorMessage, getPath, isReferenceObject, isSchemaObject, index as openAPI };
1710
+ export { Bagel, StreamController, createSSEStream, createSSEStreamPost, dereference, extractSSEEventInfo, extractSSEEventTypes, formatAPIErrorMessage, getPath, isReferenceObject, isSSEStream, isSchemaObject, index as openAPI };