@bagelink/sdk 1.8.14 → 1.8.16

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/bin/index.ts CHANGED
@@ -23,28 +23,7 @@ export async function loadTypes() {
23
23
  const apiPath = join(bagelinkDir, 'api.ts')
24
24
  await formatAndWriteCode(apiPath, code)
25
25
 
26
- // Copy streamClient utilities to generated directory
27
- const srcDir = join(__dirname, '..', 'src', 'openAPITools')
28
- const streamClientSource = join(srcDir, 'streamClient.ts')
29
- const streamControllerSource = join(srcDir, 'StreamController.ts')
30
-
31
- if (fs.existsSync(streamClientSource)) {
32
- const streamClientCode = fs.readFileSync(streamClientSource, 'utf-8')
33
- const streamClientDest = join(bagelinkDir, 'streamClient.ts')
34
- await formatAndWriteCode(streamClientDest, streamClientCode)
35
- console.log('✅ Generated streamClient.ts')
36
- } else {
37
- console.warn('⚠️ streamClient.ts not found at:', streamClientSource)
38
- }
39
-
40
- if (fs.existsSync(streamControllerSource)) {
41
- const streamControllerCode = fs.readFileSync(streamControllerSource, 'utf-8')
42
- const streamControllerDest = join(bagelinkDir, 'StreamController.ts')
43
- await formatAndWriteCode(streamControllerDest, streamControllerCode)
44
- console.log('✅ Generated StreamController.ts')
45
- } else {
46
- console.warn('⚠️ StreamController.ts not found at:', streamControllerSource)
47
- }
26
+ console.log('ℹ️ Stream utilities are imported from @bagelink/sdk (no need to copy)')
48
27
 
49
28
  // Optionally split into organized files
50
29
  if (shouldSplitFiles) {
package/dist/index.cjs CHANGED
@@ -899,7 +899,7 @@ function fileTemplate(tsString, typeForImport, baseURL) {
899
899
  const templateCode = `import ax from 'axios';
900
900
  import type { AxiosResponse } from 'axios';
901
901
  import type { ${typeForImport.join(", ")} } from './types.d';
902
- import { createSSEStream, createSSEStreamPost, StreamController, type SSEStreamOptions, type SSEEvent } from './streamClient';
902
+ import { createSSEStream, createSSEStreamPost, StreamController, type SSEStreamOptions, type SSEEvent } from '@bagelink/sdk';
903
903
 
904
904
  /**
905
905
  * Options for file upload operations
@@ -914,22 +914,15 @@ export interface UploadOptions {
914
914
  }
915
915
 
916
916
  /**
917
- * Export SSE stream utilities for direct use
917
+ * Stream utilities are exported from @bagelink/sdk
918
918
  *
919
919
  * @example Chainable event handlers
920
+ * import { createSSEStream } from '@bagelink/sdk'
921
+ *
920
922
  * const stream = createSSEStream(url)
921
923
  * .on('token', (data) => console.log(data))
922
924
  * .on('done', () => console.log('Complete!'))
923
- *
924
- * @example Async iteration
925
- * for await (const event of createSSEStream(url)) {
926
- * console.log(event.type, event.data)
927
- * }
928
- *
929
- * @example Promise-based
930
- * const result = await createSSEStream(url).toPromise()
931
925
  */
932
- export { createSSEStream, createSSEStreamPost, StreamController, type SSEStreamOptions, type SSEEvent } from './streamClient';
933
926
  ${streamTypeDefs}
934
927
  /**
935
928
  * Configured axios instance for API requests
@@ -985,36 +978,13 @@ function generateTypes(schemas) {
985
978
  }
986
979
 
987
980
  class StreamController {
988
- constructor(streamFn) {
989
- this.streamFn = streamFn;
990
- this.abortController = new AbortController();
991
- this.start();
981
+ constructor(cleanup) {
982
+ this.cleanup = cleanup;
983
+ this.controller = new AbortController();
992
984
  }
993
985
  handlers = /* @__PURE__ */ new Map();
994
- errorHandlers = /* @__PURE__ */ new Set();
995
- completeHandlers = /* @__PURE__ */ new Set();
996
- abortController;
986
+ controller;
997
987
  _closed = false;
998
- _promise = null;
999
- _resolvePromise = null;
1000
- _rejectPromise = null;
1001
- start() {
1002
- const cleanup = this.streamFn(
1003
- (event) => {
1004
- this.emit(event.type, event.data);
1005
- },
1006
- (error) => {
1007
- this.emitError(error);
1008
- },
1009
- () => {
1010
- this.emitComplete();
1011
- }
1012
- );
1013
- this.abortController.signal.addEventListener("abort", () => {
1014
- cleanup();
1015
- this._closed = true;
1016
- });
1017
- }
1018
988
  /**
1019
989
  * Register an event handler (fully typed!)
1020
990
  * @param event - Event type to listen for
@@ -1022,22 +992,16 @@ class StreamController {
1022
992
  * @returns this (for chaining)
1023
993
  */
1024
994
  on(event, handler) {
1025
- if (event === "error") {
1026
- this.errorHandlers.add(handler);
1027
- } else if (event === "complete") {
1028
- this.completeHandlers.add(handler);
1029
- } else {
1030
- if (!this.handlers.has(event)) {
1031
- this.handlers.set(event, /* @__PURE__ */ new Set());
1032
- }
1033
- this.handlers.get(event).add(handler);
995
+ if (!this.handlers.has(event)) {
996
+ this.handlers.set(event, /* @__PURE__ */ new Set());
1034
997
  }
998
+ this.handlers.get(event).add(handler);
1035
999
  return this;
1036
1000
  }
1037
1001
  /**
1038
1002
  * Register a one-time event handler (fully typed!)
1039
1003
  * @param event - Event type to listen for
1040
- * @param handler - Handler function (called once then removed, data type inferred from event)
1004
+ * @param handler - Handler function (called once then removed)
1041
1005
  * @returns this (for chaining)
1042
1006
  */
1043
1007
  once(event, handler) {
@@ -1054,13 +1018,7 @@ class StreamController {
1054
1018
  * @returns this (for chaining)
1055
1019
  */
1056
1020
  off(event, handler) {
1057
- if (event === "error") {
1058
- this.errorHandlers.delete(handler);
1059
- } else if (event === "complete") {
1060
- this.completeHandlers.delete(handler);
1061
- } else {
1062
- this.handlers.get(event)?.delete(handler);
1063
- }
1021
+ this.handlers.get(event)?.delete(handler);
1064
1022
  return this;
1065
1023
  }
1066
1024
  /**
@@ -1069,17 +1027,15 @@ class StreamController {
1069
1027
  removeAllListeners(event) {
1070
1028
  if (event === void 0) {
1071
1029
  this.handlers.clear();
1072
- this.errorHandlers.clear();
1073
- this.completeHandlers.clear();
1074
- } else if (event === "error") {
1075
- this.errorHandlers.clear();
1076
- } else if (event === "complete") {
1077
- this.completeHandlers.clear();
1078
1030
  } else {
1079
1031
  this.handlers.delete(event);
1080
1032
  }
1081
1033
  return this;
1082
1034
  }
1035
+ /**
1036
+ * Emit an event to all registered handlers
1037
+ * @internal
1038
+ */
1083
1039
  emit(event, data) {
1084
1040
  const handlers = this.handlers.get(event);
1085
1041
  if (handlers) {
@@ -1087,41 +1043,18 @@ class StreamController {
1087
1043
  try {
1088
1044
  handler(data);
1089
1045
  } catch (error) {
1090
- console.error(`Error in handler for event "${event}":`, error);
1046
+ console.error(`Error in ${event} handler:`, error);
1091
1047
  }
1092
1048
  });
1093
1049
  }
1094
- if (event === "done" && this._resolvePromise) {
1095
- this._resolvePromise(data);
1096
- }
1097
- }
1098
- emitError(error) {
1099
- this.errorHandlers.forEach((handler) => {
1100
- try {
1101
- handler(error);
1102
- } catch (err) {
1103
- console.error("Error in error handler:", err);
1104
- }
1105
- });
1106
- if (this._rejectPromise) {
1107
- this._rejectPromise(error);
1108
- }
1109
- }
1110
- emitComplete() {
1111
- this.completeHandlers.forEach((handler) => {
1112
- try {
1113
- handler();
1114
- } catch (error) {
1115
- console.error("Error in complete handler:", error);
1116
- }
1117
- });
1118
1050
  }
1119
1051
  /**
1120
1052
  * Close the stream
1121
1053
  */
1122
1054
  close() {
1123
1055
  if (!this._closed) {
1124
- this.abortController.abort();
1056
+ this.controller.abort();
1057
+ this.cleanup();
1125
1058
  this._closed = true;
1126
1059
  }
1127
1060
  }
@@ -1131,281 +1064,131 @@ class StreamController {
1131
1064
  get closed() {
1132
1065
  return this._closed;
1133
1066
  }
1134
- /**
1135
- * Convert stream to a Promise that resolves when 'done' event fires
1136
- * @returns Promise that resolves with the 'done' event data
1137
- */
1138
- toPromise() {
1139
- if (!this._promise) {
1140
- this._promise = new Promise((resolve, reject) => {
1141
- this._resolvePromise = resolve;
1142
- this._rejectPromise = reject;
1143
- });
1067
+ }
1068
+
1069
+ function createSSEStream(url, options = {}) {
1070
+ const { headers = {}, withCredentials = true } = options;
1071
+ const controller = new AbortController();
1072
+ const stream = new StreamController(() => {
1073
+ controller.abort();
1074
+ });
1075
+ fetch(url, {
1076
+ method: "GET",
1077
+ headers: {
1078
+ Accept: "text/event-stream",
1079
+ ...headers
1080
+ },
1081
+ credentials: withCredentials ? "include" : "same-origin",
1082
+ signal: controller.signal
1083
+ }).then(async (response) => {
1084
+ if (!response.ok) {
1085
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1144
1086
  }
1145
- return this._promise;
1146
- }
1147
- /**
1148
- * Make the stream async iterable
1149
- * Usage: for await (const event of stream) { ... }
1150
- */
1151
- async *[Symbol.asyncIterator]() {
1152
- const events = [];
1153
- let resolveNext = null;
1154
- let done = false;
1155
- const eventHandler = (type) => (data) => {
1156
- const event = { type, data };
1157
- if (resolveNext) {
1158
- resolveNext(event);
1159
- resolveNext = null;
1160
- } else {
1161
- events.push(event);
1162
- }
1163
- };
1164
- const originalEmit = this.emit.bind(this);
1165
- const capturedEvents = [];
1166
- this.emit = (event, data) => {
1167
- if (!capturedEvents.includes(event)) {
1168
- capturedEvents.push(event);
1169
- this.on(event, eventHandler(event));
1170
- }
1171
- originalEmit(event, data);
1172
- };
1173
- this.on("complete", () => {
1174
- done = true;
1175
- if (resolveNext) {
1176
- resolveNext(null);
1177
- resolveNext = null;
1178
- }
1179
- });
1087
+ if (!response.body) {
1088
+ throw new Error("Response body is null");
1089
+ }
1090
+ const reader = response.body.getReader();
1091
+ const decoder = new TextDecoder();
1092
+ let buffer = "";
1180
1093
  try {
1181
- while (!done) {
1182
- if (events.length > 0) {
1183
- const event = events.shift();
1184
- if (event) yield event;
1185
- } else {
1186
- const event = await new Promise((resolve) => {
1187
- resolveNext = resolve;
1188
- });
1189
- if (event === null) break;
1190
- yield event;
1094
+ while (true) {
1095
+ const { done, value } = await reader.read();
1096
+ if (done) {
1097
+ stream.emit("complete", {});
1098
+ break;
1191
1099
  }
1192
- }
1193
- } finally {
1194
- this.close();
1195
- }
1196
- }
1197
- /**
1198
- * Collect all events into an array until stream completes
1199
- * @returns Promise<SSEEvent[]>
1200
- */
1201
- async toArray() {
1202
- const events = [];
1203
- for await (const event of this) {
1204
- events.push(event);
1205
- }
1206
- return events;
1207
- }
1208
- /**
1209
- * Pipe stream events through a transform function
1210
- */
1211
- map(transform) {
1212
- const mappedController = new StreamController(
1213
- (onEvent, onError, onComplete) => {
1214
- this.on("error", onError);
1215
- this.on("complete", onComplete);
1216
- this.handlers.forEach((_, eventType) => {
1217
- this.on(eventType, (data) => {
1100
+ buffer += decoder.decode(value, { stream: true });
1101
+ const lines = buffer.split("\n");
1102
+ buffer = lines.pop() || "";
1103
+ let eventData = "";
1104
+ for (const line of lines) {
1105
+ if (line.startsWith("data:")) {
1106
+ eventData = line.slice(5).trim();
1107
+ } else if (line === "" && eventData) {
1218
1108
  try {
1219
- const transformed = transform({ type: eventType, data });
1220
- onEvent({ type: eventType, data: transformed, raw: void 0 });
1109
+ const data = JSON.parse(eventData);
1110
+ stream.emit(data.type || "message", data);
1221
1111
  } catch (error) {
1222
- onError(error);
1223
- }
1224
- });
1225
- });
1226
- return () => {
1227
- this.close();
1228
- };
1229
- }
1230
- );
1231
- return mappedController;
1232
- }
1233
- /**
1234
- * Filter events based on a predicate
1235
- */
1236
- filter(predicate) {
1237
- const filteredController = new StreamController(
1238
- (onEvent, onError, onComplete) => {
1239
- this.on("error", onError);
1240
- this.on("complete", onComplete);
1241
- this.handlers.forEach((_, eventType) => {
1242
- this.on(eventType, (data) => {
1243
- const event = { type: eventType, data };
1244
- if (predicate(event)) {
1245
- onEvent(event);
1246
- }
1247
- });
1248
- });
1249
- return () => {
1250
- this.close();
1251
- };
1252
- }
1253
- );
1254
- return filteredController;
1255
- }
1256
- }
1257
-
1258
- function createSSEStream(url, options = {}) {
1259
- const { headers = {}, withCredentials = true } = options;
1260
- return new StreamController((onEvent, onError, onComplete) => {
1261
- const controller = new AbortController();
1262
- const { signal } = controller;
1263
- fetch(url, {
1264
- method: "GET",
1265
- headers: {
1266
- Accept: "text/event-stream",
1267
- ...headers
1268
- },
1269
- credentials: withCredentials ? "include" : "same-origin",
1270
- signal
1271
- }).then(async (response) => {
1272
- if (!response.ok) {
1273
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1274
- }
1275
- if (!response.body) {
1276
- throw new Error("Response body is null");
1277
- }
1278
- const reader = response.body.getReader();
1279
- const decoder = new TextDecoder();
1280
- let buffer = "";
1281
- try {
1282
- while (true) {
1283
- const { done, value } = await reader.read();
1284
- if (done) {
1285
- onComplete();
1286
- break;
1287
- }
1288
- buffer += decoder.decode(value, { stream: true });
1289
- const lines = buffer.split("\n");
1290
- buffer = lines.pop() || "";
1291
- let eventType = "";
1292
- let eventData = "";
1293
- for (const line of lines) {
1294
- if (line.startsWith("event:")) {
1295
- eventType = line.slice(6).trim();
1296
- } else if (line.startsWith("data:")) {
1297
- eventData = line.slice(5).trim();
1298
- } else if (line === "" && eventData) {
1299
- try {
1300
- const parsedData = JSON.parse(eventData);
1301
- onEvent({
1302
- type: eventType || "message",
1303
- data: parsedData,
1304
- raw: eventData
1305
- });
1306
- } catch {
1307
- onEvent({
1308
- type: eventType || "message",
1309
- data: eventData,
1310
- raw: eventData
1311
- });
1312
- }
1313
- eventType = "";
1314
- eventData = "";
1112
+ console.error("Failed to parse SSE event:", error);
1315
1113
  }
1114
+ eventData = "";
1316
1115
  }
1317
1116
  }
1318
- } catch (error) {
1319
- if (error instanceof Error && error.name !== "AbortError") {
1320
- onError(error);
1321
- }
1322
1117
  }
1323
- }).catch((error) => {
1324
- if (error.name !== "AbortError") {
1325
- onError(error);
1118
+ } catch (error) {
1119
+ if (error instanceof Error && error.name !== "AbortError") {
1120
+ stream.emit("error", error);
1326
1121
  }
1327
- });
1328
- return () => {
1329
- controller.abort();
1330
- };
1122
+ }
1123
+ }).catch((error) => {
1124
+ if (error.name !== "AbortError") {
1125
+ stream.emit("error", error);
1126
+ }
1331
1127
  });
1128
+ return stream;
1332
1129
  }
1333
1130
  function createSSEStreamPost(url, body, options = {}) {
1334
1131
  const { headers = {}, withCredentials = true } = options;
1335
- return new StreamController((onEvent, onError, onComplete) => {
1336
- const controller = new AbortController();
1337
- const { signal } = controller;
1338
- fetch(url, {
1339
- method: "POST",
1340
- headers: {
1341
- "Accept": "text/event-stream",
1342
- "Content-Type": "application/json",
1343
- ...headers
1344
- },
1345
- credentials: withCredentials ? "include" : "same-origin",
1346
- body: JSON.stringify(body),
1347
- signal
1348
- }).then(async (response) => {
1349
- if (!response.ok) {
1350
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1351
- }
1352
- if (!response.body) {
1353
- throw new Error("Response body is null");
1354
- }
1355
- const reader = response.body.getReader();
1356
- const decoder = new TextDecoder();
1357
- let buffer = "";
1358
- try {
1359
- while (true) {
1360
- const { done, value } = await reader.read();
1361
- if (done) {
1362
- onComplete();
1363
- break;
1364
- }
1365
- buffer += decoder.decode(value, { stream: true });
1366
- const lines = buffer.split("\n");
1367
- buffer = lines.pop() || "";
1368
- let eventType = "";
1369
- let eventData = "";
1370
- for (const line of lines) {
1371
- if (line.startsWith("event:")) {
1372
- eventType = line.slice(6).trim();
1373
- } else if (line.startsWith("data:")) {
1374
- eventData = line.slice(5).trim();
1375
- } else if (line === "" && eventData) {
1376
- try {
1377
- const parsedData = JSON.parse(eventData);
1378
- onEvent({
1379
- type: eventType || "message",
1380
- data: parsedData,
1381
- raw: eventData
1382
- });
1383
- } catch {
1384
- onEvent({
1385
- type: eventType || "message",
1386
- data: eventData,
1387
- raw: eventData
1388
- });
1389
- }
1390
- eventType = "";
1391
- eventData = "";
1132
+ const controller = new AbortController();
1133
+ const stream = new StreamController(() => {
1134
+ controller.abort();
1135
+ });
1136
+ fetch(url, {
1137
+ method: "POST",
1138
+ headers: {
1139
+ "Accept": "text/event-stream",
1140
+ "Content-Type": "application/json",
1141
+ ...headers
1142
+ },
1143
+ credentials: withCredentials ? "include" : "same-origin",
1144
+ body: JSON.stringify(body),
1145
+ signal: controller.signal
1146
+ }).then(async (response) => {
1147
+ if (!response.ok) {
1148
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
1149
+ }
1150
+ if (!response.body) {
1151
+ throw new Error("Response body is null");
1152
+ }
1153
+ const reader = response.body.getReader();
1154
+ const decoder = new TextDecoder();
1155
+ let buffer = "";
1156
+ try {
1157
+ while (true) {
1158
+ const { done, value } = await reader.read();
1159
+ if (done) {
1160
+ stream.emit("complete", {});
1161
+ break;
1162
+ }
1163
+ buffer += decoder.decode(value, { stream: true });
1164
+ const lines = buffer.split("\n");
1165
+ buffer = lines.pop() || "";
1166
+ let eventData = "";
1167
+ for (const line of lines) {
1168
+ if (line.startsWith("data:")) {
1169
+ eventData = line.slice(5).trim();
1170
+ } else if (line === "" && eventData) {
1171
+ try {
1172
+ const data = JSON.parse(eventData);
1173
+ stream.emit(data.type || "message", data);
1174
+ } catch (error) {
1175
+ console.error("Failed to parse SSE event:", error);
1392
1176
  }
1177
+ eventData = "";
1393
1178
  }
1394
1179
  }
1395
- } catch (error) {
1396
- if (error instanceof Error && error.name !== "AbortError") {
1397
- onError(error);
1398
- }
1399
1180
  }
1400
- }).catch((error) => {
1401
- if (error.name !== "AbortError") {
1402
- onError(error);
1181
+ } catch (error) {
1182
+ if (error instanceof Error && error.name !== "AbortError") {
1183
+ stream.emit("error", error);
1403
1184
  }
1404
- });
1405
- return () => {
1406
- controller.abort();
1407
- };
1185
+ }
1186
+ }).catch((error) => {
1187
+ if (error.name !== "AbortError") {
1188
+ stream.emit("error", error);
1189
+ }
1408
1190
  });
1191
+ return stream;
1409
1192
  }
1410
1193
 
1411
1194
  const basicAuthHeader = { Authorization: "Basic YmFnZWxfdXNlcm5hbWU6Tm90U2VjdXJlQGJhZ2Vs" };