flowscale 1.2.0 → 1.3.1

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.js CHANGED
@@ -78,12 +78,9 @@ var axios_1 = __importDefault(require("axios"));
78
78
  var FlowscaleAPI = /** @class */ (function () {
79
79
  function FlowscaleAPI(config) {
80
80
  var _this = this;
81
- this.ws = null;
82
- this.wsConnected = false;
83
- this.reconnectAttempts = 0;
84
- this.reconnectTimeout = null;
85
81
  this.proxyMode = false;
86
82
  this.customHeaders = {};
83
+ this.fetchImpl = null;
87
84
  // Validate required configuration
88
85
  if (!config) {
89
86
  throw new Error('FlowscaleAPI configuration is required');
@@ -123,7 +120,7 @@ var FlowscaleAPI = /** @class */ (function () {
123
120
  this.baseUrl = config.baseUrl.trim();
124
121
  this.maxRetries = config.maxRetries || 3;
125
122
  this.retryDelay = config.retryDelay || 1000;
126
- this.WebSocketImpl = config.WebSocketImpl || (typeof WebSocket !== 'undefined' ? WebSocket : null);
123
+ this.fetchImpl = config.fetchImpl || this.resolveGlobalFetch();
127
124
  this.client = axios_1.default.create({
128
125
  baseURL: this.baseUrl,
129
126
  timeout: config.timeout || 6000000, // Default 6000s timeout instead of Axios default 0 (no timeout)
@@ -239,13 +236,13 @@ var FlowscaleAPI = /** @class */ (function () {
239
236
  */
240
237
  FlowscaleAPI.prototype.executeWorkflowAsync = function (workflowId_1, data_1, groupId_1) {
241
238
  return __awaiter(this, arguments, void 0, function (workflowId, data, groupId, pollIntervalMs, timeoutMs, returnAllOutputsOrOptions) {
242
- var _a, returnAllOutputs, onIntermediateResponse, startTime, executeResponse, runId, resolvedWorkflowId, run_status, output_names, runResponse, attempts, outputPromises, outputResults, successfulOutputs, pendingOutputs, hasErrors, _i, outputResults_1, result, deprecationWarning, legacyResponse, allOutputsResponse, error_1;
239
+ var _a, returnAllOutputs, onIntermediateResponse, startTime, executeResponse, runId, resolvedWorkflowId, run_status, output_names, runResponse, attempts, runResponse, latestOutputNames, outputPromises, outputResults, successfulOutputs, pendingOutputs, hasErrors, _i, outputResults_1, result, deprecationWarning, legacyResponse, allOutputsResponse, error_1;
243
240
  var _this = this;
244
- var _b, _c, _d, _e, _f;
241
+ var _b, _c, _d, _e, _f, _g, _h, _j;
245
242
  if (pollIntervalMs === void 0) { pollIntervalMs = 2000; }
246
243
  if (timeoutMs === void 0) { timeoutMs = 600000; }
247
- return __generator(this, function (_g) {
248
- switch (_g.label) {
244
+ return __generator(this, function (_k) {
245
+ switch (_k.label) {
249
246
  case 0:
250
247
  // Validate inputs
251
248
  if (!workflowId || typeof workflowId !== 'string' || workflowId.trim() === '') {
@@ -264,7 +261,7 @@ var FlowscaleAPI = /** @class */ (function () {
264
261
  startTime = Date.now();
265
262
  return [4 /*yield*/, this.executeWorkflow(workflowId, data, groupId)];
266
263
  case 1:
267
- executeResponse = _g.sent();
264
+ executeResponse = _k.sent();
268
265
  if (!((_b = executeResponse === null || executeResponse === void 0 ? void 0 : executeResponse.data) === null || _b === void 0 ? void 0 : _b.run_id)) {
269
266
  throw new Error('No run ID returned from workflow execution');
270
267
  }
@@ -281,10 +278,10 @@ var FlowscaleAPI = /** @class */ (function () {
281
278
  response: executeResponse,
282
279
  })];
283
280
  case 2:
284
- _g.sent();
281
+ _k.sent();
285
282
  run_status = "queued";
286
283
  output_names = [];
287
- _g.label = 3;
284
+ _k.label = 3;
288
285
  case 3:
289
286
  if (!(output_names.length === 0)) return [3 /*break*/, 8];
290
287
  if (Date.now() - startTime > timeoutMs) {
@@ -292,7 +289,7 @@ var FlowscaleAPI = /** @class */ (function () {
292
289
  }
293
290
  return [4 /*yield*/, this.getRun(runId)];
294
291
  case 4:
295
- runResponse = _g.sent();
292
+ runResponse = _k.sent();
296
293
  run_status = ((_d = runResponse === null || runResponse === void 0 ? void 0 : runResponse.data) === null || _d === void 0 ? void 0 : _d.status) || "queued";
297
294
  output_names = ((_e = runResponse === null || runResponse === void 0 ? void 0 : runResponse.data) === null || _e === void 0 ? void 0 : _e.output_names) || [];
298
295
  return [4 /*yield*/, this.emitExecuteWorkflowAsyncIntermediateResponse(onIntermediateResponse, {
@@ -306,15 +303,15 @@ var FlowscaleAPI = /** @class */ (function () {
306
303
  response: runResponse,
307
304
  })];
308
305
  case 5:
309
- _g.sent();
306
+ _k.sent();
310
307
  if (run_status === 'failed' || run_status === 'cancelled') {
311
308
  throw new Error("Run ".concat(runId, " failed with status: ").concat(run_status));
312
309
  }
313
310
  if (!(output_names.length === 0)) return [3 /*break*/, 7];
314
311
  return [4 /*yield*/, this.sleep(pollIntervalMs)];
315
312
  case 6:
316
- _g.sent();
317
- _g.label = 7;
313
+ _k.sent();
314
+ _k.label = 7;
318
315
  case 7: return [3 /*break*/, 3];
319
316
  case 8:
320
317
  if (!output_names || output_names.length === 0) {
@@ -322,15 +319,39 @@ var FlowscaleAPI = /** @class */ (function () {
322
319
  }
323
320
  this.logInfo("Found ".concat(output_names.length, " outputs to wait for: ").concat(output_names.join(', ')));
324
321
  attempts = 0;
325
- _g.label = 9;
322
+ _k.label = 9;
326
323
  case 9:
327
- if (!true) return [3 /*break*/, 21];
324
+ if (!true) return [3 /*break*/, 23];
328
325
  if (Date.now() - startTime > timeoutMs) {
329
326
  throw new Error("Workflow execution timed out after ".concat(timeoutMs, "ms"));
330
327
  }
331
- _g.label = 10;
328
+ _k.label = 10;
332
329
  case 10:
333
- _g.trys.push([10, 17, , 20]);
330
+ _k.trys.push([10, 19, , 22]);
331
+ return [4 /*yield*/, this.getRun(runId)];
332
+ case 11:
333
+ runResponse = _k.sent();
334
+ run_status = ((_g = runResponse === null || runResponse === void 0 ? void 0 : runResponse.data) === null || _g === void 0 ? void 0 : _g.status) || run_status;
335
+ latestOutputNames = ((_h = runResponse === null || runResponse === void 0 ? void 0 : runResponse.data) === null || _h === void 0 ? void 0 : _h.output_names) || [];
336
+ if (latestOutputNames.length > 0) {
337
+ output_names = latestOutputNames;
338
+ }
339
+ return [4 /*yield*/, this.emitExecuteWorkflowAsyncIntermediateResponse(onIntermediateResponse, {
340
+ type: 'run_status',
341
+ timestamp: new Date().toISOString(),
342
+ run_id: runId,
343
+ workflow_id: resolvedWorkflowId,
344
+ attempt: attempts,
345
+ run_status: run_status,
346
+ output_names: output_names,
347
+ progress: ((_j = runResponse === null || runResponse === void 0 ? void 0 : runResponse.data) === null || _j === void 0 ? void 0 : _j.progress) || undefined,
348
+ response: runResponse,
349
+ })];
350
+ case 12:
351
+ _k.sent();
352
+ if (run_status === 'failed' || run_status === 'cancelled') {
353
+ throw new Error("Run ".concat(runId, " failed with status: ").concat(run_status));
354
+ }
334
355
  outputPromises = output_names.map(function (outputName) { return __awaiter(_this, void 0, void 0, function () {
335
356
  var output, error_2;
336
357
  var _a;
@@ -375,8 +396,8 @@ var FlowscaleAPI = /** @class */ (function () {
375
396
  });
376
397
  }); });
377
398
  return [4 /*yield*/, Promise.all(outputPromises)];
378
- case 11:
379
- outputResults = _g.sent();
399
+ case 13:
400
+ outputResults = _k.sent();
380
401
  successfulOutputs = [];
381
402
  pendingOutputs = [];
382
403
  hasErrors = false;
@@ -416,9 +437,9 @@ var FlowscaleAPI = /** @class */ (function () {
416
437
  if (hasErrors) {
417
438
  throw new Error('One or more workflow outputs failed');
418
439
  }
419
- if (!(pendingOutputs.length === 0)) return [3 /*break*/, 15];
440
+ if (!(pendingOutputs.length === 0)) return [3 /*break*/, 17];
420
441
  this.logInfo("All ".concat(successfulOutputs.length, " outputs completed successfully"));
421
- if (!(returnAllOutputs === false || (returnAllOutputs === undefined && successfulOutputs.length === 1))) return [3 /*break*/, 13];
442
+ if (!(returnAllOutputs === false || (returnAllOutputs === undefined && successfulOutputs.length === 1))) return [3 /*break*/, 15];
422
443
  deprecationWarning = {
423
444
  message: successfulOutputs.length > 1
424
445
  ? 'Single output format is deprecated. This workflow returned multiple outputs but only the first is shown.'
@@ -446,10 +467,10 @@ var FlowscaleAPI = /** @class */ (function () {
446
467
  completed_outputs: successfulOutputs.map(function (output) { return output.filename; }),
447
468
  response: legacyResponse,
448
469
  })];
449
- case 12:
450
- _g.sent();
470
+ case 14:
471
+ _k.sent();
451
472
  return [2 /*return*/, legacyResponse];
452
- case 13:
473
+ case 15:
453
474
  allOutputsResponse = {
454
475
  status: 'success',
455
476
  data: successfulOutputs
@@ -463,18 +484,18 @@ var FlowscaleAPI = /** @class */ (function () {
463
484
  completed_outputs: successfulOutputs.map(function (output) { return output.filename; }),
464
485
  response: allOutputsResponse,
465
486
  })];
466
- case 14:
467
- _g.sent();
487
+ case 16:
488
+ _k.sent();
468
489
  return [2 /*return*/, allOutputsResponse];
469
- case 15:
490
+ case 17:
470
491
  // If some outputs are still pending, continue polling
471
492
  this.logDebug("".concat(pendingOutputs.length, " outputs still pending: ").concat(pendingOutputs.join(', ')));
472
493
  return [4 /*yield*/, this.sleep(pollIntervalMs)];
473
- case 16:
474
- _g.sent();
475
- return [3 /*break*/, 20];
476
- case 17:
477
- error_1 = _g.sent();
494
+ case 18:
495
+ _k.sent();
496
+ return [3 /*break*/, 22];
497
+ case 19:
498
+ error_1 = _k.sent();
478
499
  // Log the error but continue polling
479
500
  this.logWarn("Error polling for outputs (attempt ".concat(attempts, "):"), error_1);
480
501
  return [4 /*yield*/, this.emitExecuteWorkflowAsyncIntermediateResponse(onIntermediateResponse, {
@@ -486,22 +507,22 @@ var FlowscaleAPI = /** @class */ (function () {
486
507
  output_names: output_names,
487
508
  error: this.formatUnknownError(error_1),
488
509
  })];
489
- case 18:
490
- _g.sent();
510
+ case 20:
511
+ _k.sent();
491
512
  // After several failures, increase polling interval to avoid excessive requests
492
513
  if (attempts > 5) {
493
514
  pollIntervalMs = Math.min(pollIntervalMs * 1.5, 10000); // Increase interval, max 10s
494
515
  }
495
516
  // Wait before retrying
496
517
  return [4 /*yield*/, this.sleep(pollIntervalMs)];
497
- case 19:
518
+ case 21:
498
519
  // Wait before retrying
499
- _g.sent();
500
- return [3 /*break*/, 20];
501
- case 20:
520
+ _k.sent();
521
+ return [3 /*break*/, 22];
522
+ case 22:
502
523
  attempts++;
503
524
  return [3 /*break*/, 9];
504
- case 21: return [2 /*return*/];
525
+ case 23: return [2 /*return*/];
505
526
  }
506
527
  });
507
528
  });
@@ -512,7 +533,7 @@ var FlowscaleAPI = /** @class */ (function () {
512
533
  */
513
534
  FlowscaleAPI.prototype.getOutput = function (filename) {
514
535
  return __awaiter(this, void 0, void 0, function () {
515
- var error_3, axiosError;
536
+ var response, error_3, axiosError;
516
537
  return __generator(this, function (_a) {
517
538
  switch (_a.label) {
518
539
  case 0:
@@ -523,21 +544,35 @@ var FlowscaleAPI = /** @class */ (function () {
523
544
  _a.label = 1;
524
545
  case 1:
525
546
  _a.trys.push([1, 3, , 4]);
526
- return [4 /*yield*/, this.makeRequest('get', '/api/v1/runs/output', {
547
+ return [4 /*yield*/, this.client.get('/api/v1/runs/output', {
527
548
  params: { filename: filename },
549
+ // We need to inspect HTTP status directly (204 is a valid "not ready" response).
550
+ validateStatus: function () { return true; },
528
551
  })];
529
- case 2: return [2 /*return*/, _a.sent()];
530
- case 3:
531
- error_3 = _a.sent();
532
- axiosError = error_3;
533
- if (axiosError.response && axiosError.response.status === 204) {
552
+ case 2:
553
+ response = _a.sent();
554
+ if (response.status === 204) {
534
555
  return [2 /*return*/, null]; // No output found
535
556
  }
536
- else if (axiosError.response && axiosError.response.status === 504) {
557
+ else if (response.status === 504) {
537
558
  this.logWarn('Received 504 Gateway Timeout, retrying...');
538
559
  // For 504 errors specifically, we'll return null to allow the polling to continue
539
560
  return [2 /*return*/, null];
540
561
  }
562
+ else if (response.status === 408) {
563
+ throw new Error('Run Timeout');
564
+ }
565
+ if (response.status < 200 || response.status >= 300) {
566
+ throw new Error("Error: ".concat(response.status, " ").concat(response.statusText, " - ").concat(JSON.stringify(response.data)));
567
+ }
568
+ return [2 /*return*/, this.normalizeGetOutputResponse(response.data)];
569
+ case 3:
570
+ error_3 = _a.sent();
571
+ axiosError = error_3;
572
+ if (axiosError.response && axiosError.response.status === 504) {
573
+ this.logWarn('Received 504 Gateway Timeout, retrying...');
574
+ return [2 /*return*/, null];
575
+ }
541
576
  else if (axiosError.response && axiosError.response.status === 408) {
542
577
  throw new Error('Run Timeout');
543
578
  }
@@ -594,6 +629,39 @@ var FlowscaleAPI = /** @class */ (function () {
594
629
  });
595
630
  });
596
631
  };
632
+ /**
633
+ * Subscribes to run events from the SSE endpoint.
634
+ * @param runId - The run ID to stream events for.
635
+ * @param options - SSE subscription options and callbacks.
636
+ * @returns Connection handle with `close()` and `done` promise.
637
+ */
638
+ FlowscaleAPI.prototype.subscribeRunEvents = function (runId, options) {
639
+ if (options === void 0) { options = {}; }
640
+ if (!runId || typeof runId !== 'string' || runId.trim() === '') {
641
+ throw new Error('Run ID is required and must be a non-empty string');
642
+ }
643
+ if (options.fromSeq !== undefined && options.fromSeq < 0) {
644
+ throw new Error('fromSeq must be greater than or equal to 0');
645
+ }
646
+ if (!this.fetchImpl) {
647
+ throw new Error('SSE streaming requires a fetch implementation. ' +
648
+ 'In Node.js < 18, pass `fetchImpl` in FlowscaleAPI config.');
649
+ }
650
+ var abortController = new AbortController();
651
+ if (options.signal) {
652
+ if (options.signal.aborted) {
653
+ abortController.abort();
654
+ }
655
+ else {
656
+ options.signal.addEventListener('abort', function () { return abortController.abort(); }, { once: true });
657
+ }
658
+ }
659
+ var done = this.consumeRunEventsStream(runId, options, abortController.signal);
660
+ return {
661
+ close: function () { return abortController.abort(); },
662
+ done: done,
663
+ };
664
+ };
597
665
  FlowscaleAPI.prototype.resolveExecuteWorkflowAsyncOptions = function (returnAllOutputsOrOptions) {
598
666
  if (returnAllOutputsOrOptions == null ||
599
667
  typeof returnAllOutputsOrOptions === 'boolean') {
@@ -655,6 +723,400 @@ var FlowscaleAPI = /** @class */ (function () {
655
723
  });
656
724
  });
657
725
  };
726
+ FlowscaleAPI.prototype.normalizeGetOutputResponse = function (payload) {
727
+ if (!payload || typeof payload !== 'object') {
728
+ return null;
729
+ }
730
+ if (typeof payload.status === 'string') {
731
+ return payload;
732
+ }
733
+ // Handle payloads that omit top-level status but still contain useful output data.
734
+ if (payload.data && typeof payload.data === 'object') {
735
+ if (payload.data.errors) {
736
+ return {
737
+ status: 'error',
738
+ data: {
739
+ errors: payload.data.errors,
740
+ },
741
+ };
742
+ }
743
+ if (payload.data.progress) {
744
+ return {
745
+ status: 'in_progress',
746
+ data: payload.data,
747
+ };
748
+ }
749
+ if (payload.data.download_url ||
750
+ payload.data.file_content ||
751
+ payload.data.generation_status) {
752
+ return {
753
+ status: 'success',
754
+ data: payload.data,
755
+ };
756
+ }
757
+ }
758
+ // Legacy shape fallback: response data returned directly without wrapping.
759
+ if (payload.download_url || payload.file_content || payload.generation_status) {
760
+ return {
761
+ status: 'success',
762
+ data: payload,
763
+ };
764
+ }
765
+ if (payload.errors) {
766
+ return {
767
+ status: 'error',
768
+ data: {
769
+ errors: payload.errors,
770
+ },
771
+ };
772
+ }
773
+ return null;
774
+ };
775
+ FlowscaleAPI.prototype.resolveGlobalFetch = function () {
776
+ var globalScope = typeof globalThis !== 'undefined'
777
+ ? globalThis
778
+ : (typeof window !== 'undefined' ? window : undefined);
779
+ if (globalScope && typeof globalScope.fetch === 'function') {
780
+ return globalScope.fetch.bind(globalScope);
781
+ }
782
+ return null;
783
+ };
784
+ FlowscaleAPI.prototype.consumeRunEventsStream = function (runId, options, signal) {
785
+ return __awaiter(this, void 0, void 0, function () {
786
+ var url, headers_1, response, contentType, errorText, error_5, normalizedError;
787
+ var _this = this;
788
+ var _a, _b;
789
+ return __generator(this, function (_c) {
790
+ switch (_c.label) {
791
+ case 0:
792
+ _c.trys.push([0, 8, 10, 12]);
793
+ url = new URL("/api/v1/runs/".concat(encodeURIComponent(runId), "/events"), this.baseUrl);
794
+ if (options.fromSeq !== undefined) {
795
+ url.searchParams.set('from_seq', String(options.fromSeq));
796
+ }
797
+ headers_1 = {
798
+ Accept: 'text/event-stream',
799
+ };
800
+ if (!this.proxyMode && this.apiKey) {
801
+ headers_1['X-API-KEY'] = this.apiKey;
802
+ }
803
+ Object.keys(this.customHeaders).forEach(function (key) {
804
+ headers_1[key] = _this.customHeaders[key];
805
+ });
806
+ return [4 /*yield*/, this.fetchImpl(url.toString(), {
807
+ method: 'GET',
808
+ headers: headers_1,
809
+ signal: signal,
810
+ })];
811
+ case 1:
812
+ response = _c.sent();
813
+ contentType = ((_b = (_a = response.headers) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(_a, 'content-type')) || null;
814
+ return [4 /*yield*/, this.invokeRunEventsCallback(options.onOpen, {
815
+ status: response.status,
816
+ contentType: contentType,
817
+ })];
818
+ case 2:
819
+ _c.sent();
820
+ if (!!response.ok) return [3 /*break*/, 4];
821
+ return [4 /*yield*/, this.safeReadResponseText(response)];
822
+ case 3:
823
+ errorText = _c.sent();
824
+ throw new Error("Failed to stream run events: ".concat(response.status, " ").concat(response.statusText) +
825
+ (errorText ? " - ".concat(errorText) : ''));
826
+ case 4:
827
+ if (!(contentType && contentType.includes('application/json'))) return [3 /*break*/, 6];
828
+ return [4 /*yield*/, this.consumeRunEventsSnapshotResponse(response, options)];
829
+ case 5:
830
+ _c.sent();
831
+ return [2 /*return*/];
832
+ case 6:
833
+ if (!response.body || typeof response.body.getReader !== 'function') {
834
+ throw new Error('SSE response body is not readable in this environment');
835
+ }
836
+ return [4 /*yield*/, this.consumeSSEReadableStream(response.body.getReader(), options, signal)];
837
+ case 7:
838
+ _c.sent();
839
+ return [3 /*break*/, 12];
840
+ case 8:
841
+ error_5 = _c.sent();
842
+ if (signal.aborted) {
843
+ return [2 /*return*/];
844
+ }
845
+ normalizedError = error_5 instanceof Error ? error_5 : new Error(this.formatUnknownError(error_5));
846
+ return [4 /*yield*/, this.invokeRunEventsCallback(options.onError, normalizedError)];
847
+ case 9:
848
+ _c.sent();
849
+ if (!options.onError) {
850
+ throw normalizedError;
851
+ }
852
+ return [3 /*break*/, 12];
853
+ case 10: return [4 /*yield*/, this.invokeRunEventsCallback(options.onClose)];
854
+ case 11:
855
+ _c.sent();
856
+ return [7 /*endfinally*/];
857
+ case 12: return [2 /*return*/];
858
+ }
859
+ });
860
+ });
861
+ };
862
+ FlowscaleAPI.prototype.safeReadResponseText = function (response) {
863
+ return __awaiter(this, void 0, void 0, function () {
864
+ var _1;
865
+ return __generator(this, function (_a) {
866
+ switch (_a.label) {
867
+ case 0:
868
+ _a.trys.push([0, 2, , 3]);
869
+ return [4 /*yield*/, response.text()];
870
+ case 1: return [2 /*return*/, _a.sent()];
871
+ case 2:
872
+ _1 = _a.sent();
873
+ return [2 /*return*/, ''];
874
+ case 3: return [2 /*return*/];
875
+ }
876
+ });
877
+ });
878
+ };
879
+ FlowscaleAPI.prototype.consumeRunEventsSnapshotResponse = function (response, options) {
880
+ return __awaiter(this, void 0, void 0, function () {
881
+ var snapshot, _2, events, _i, events_1, event_1, payload;
882
+ return __generator(this, function (_a) {
883
+ switch (_a.label) {
884
+ case 0:
885
+ snapshot = null;
886
+ _a.label = 1;
887
+ case 1:
888
+ _a.trys.push([1, 3, , 4]);
889
+ return [4 /*yield*/, response.json()];
890
+ case 2:
891
+ snapshot = _a.sent();
892
+ return [3 /*break*/, 4];
893
+ case 3:
894
+ _2 = _a.sent();
895
+ return [2 /*return*/];
896
+ case 4:
897
+ events = snapshot === null || snapshot === void 0 ? void 0 : snapshot.events;
898
+ if (!Array.isArray(events)) {
899
+ return [2 /*return*/];
900
+ }
901
+ _i = 0, events_1 = events;
902
+ _a.label = 5;
903
+ case 5:
904
+ if (!(_i < events_1.length)) return [3 /*break*/, 8];
905
+ event_1 = events_1[_i];
906
+ payload = event_1 === null || event_1 === void 0 ? void 0 : event_1.payload;
907
+ return [4 /*yield*/, this.invokeRunEventsCallback(options.onEvent, {
908
+ id: event_1 === null || event_1 === void 0 ? void 0 : event_1.sequence,
909
+ data: payload,
910
+ raw: JSON.stringify(payload !== null && payload !== void 0 ? payload : null),
911
+ })];
912
+ case 6:
913
+ _a.sent();
914
+ _a.label = 7;
915
+ case 7:
916
+ _i++;
917
+ return [3 /*break*/, 5];
918
+ case 8: return [2 /*return*/];
919
+ }
920
+ });
921
+ });
922
+ };
923
+ FlowscaleAPI.prototype.consumeSSEReadableStream = function (reader, options, signal) {
924
+ return __awaiter(this, void 0, void 0, function () {
925
+ var decoder, buffer, eventId, eventType, dataLines, emitEvent, processLine, processBuffer, _a, done, value;
926
+ var _this = this;
927
+ return __generator(this, function (_b) {
928
+ switch (_b.label) {
929
+ case 0:
930
+ decoder = new TextDecoder();
931
+ buffer = '';
932
+ dataLines = [];
933
+ emitEvent = function () { return __awaiter(_this, void 0, void 0, function () {
934
+ var raw, parsedData, id, eventPayload;
935
+ return __generator(this, function (_a) {
936
+ switch (_a.label) {
937
+ case 0:
938
+ if (dataLines.length === 0) {
939
+ eventId = undefined;
940
+ eventType = undefined;
941
+ return [2 /*return*/];
942
+ }
943
+ raw = dataLines.join('\n');
944
+ parsedData = raw;
945
+ try {
946
+ parsedData = JSON.parse(raw);
947
+ }
948
+ catch (_) {
949
+ // Keep raw text when payload is not JSON.
950
+ }
951
+ id = this.parseSSEEventId(eventId);
952
+ eventPayload = {
953
+ id: id,
954
+ event: eventType,
955
+ data: parsedData,
956
+ raw: raw,
957
+ };
958
+ return [4 /*yield*/, this.invokeRunEventsCallback(options.onEvent, eventPayload)];
959
+ case 1:
960
+ _a.sent();
961
+ eventId = undefined;
962
+ eventType = undefined;
963
+ dataLines = [];
964
+ return [2 /*return*/];
965
+ }
966
+ });
967
+ }); };
968
+ processLine = function (line) { return __awaiter(_this, void 0, void 0, function () {
969
+ var separatorIndex, field, value;
970
+ return __generator(this, function (_a) {
971
+ switch (_a.label) {
972
+ case 0:
973
+ if (!(line === '')) return [3 /*break*/, 2];
974
+ return [4 /*yield*/, emitEvent()];
975
+ case 1:
976
+ _a.sent();
977
+ return [2 /*return*/];
978
+ case 2:
979
+ if (line.startsWith(':')) {
980
+ return [2 /*return*/];
981
+ }
982
+ separatorIndex = line.indexOf(':');
983
+ field = line;
984
+ value = '';
985
+ if (separatorIndex >= 0) {
986
+ field = line.slice(0, separatorIndex);
987
+ value = line.slice(separatorIndex + 1);
988
+ if (value.startsWith(' ')) {
989
+ value = value.slice(1);
990
+ }
991
+ }
992
+ switch (field) {
993
+ case 'id':
994
+ eventId = value;
995
+ break;
996
+ case 'event':
997
+ eventType = value;
998
+ break;
999
+ case 'data':
1000
+ dataLines.push(value);
1001
+ break;
1002
+ default:
1003
+ break;
1004
+ }
1005
+ return [2 /*return*/];
1006
+ }
1007
+ });
1008
+ }); };
1009
+ processBuffer = function () {
1010
+ var args_1 = [];
1011
+ for (var _i = 0; _i < arguments.length; _i++) {
1012
+ args_1[_i] = arguments[_i];
1013
+ }
1014
+ return __awaiter(_this, __spreadArray([], args_1, true), void 0, function (flushFinalLine) {
1015
+ var newlineIndex, line, line;
1016
+ if (flushFinalLine === void 0) { flushFinalLine = false; }
1017
+ return __generator(this, function (_a) {
1018
+ switch (_a.label) {
1019
+ case 0:
1020
+ if (!true) return [3 /*break*/, 2];
1021
+ newlineIndex = buffer.indexOf('\n');
1022
+ if (newlineIndex < 0) {
1023
+ return [3 /*break*/, 2];
1024
+ }
1025
+ line = buffer.slice(0, newlineIndex);
1026
+ buffer = buffer.slice(newlineIndex + 1);
1027
+ if (line.endsWith('\r')) {
1028
+ line = line.slice(0, -1);
1029
+ }
1030
+ return [4 /*yield*/, processLine(line)];
1031
+ case 1:
1032
+ _a.sent();
1033
+ return [3 /*break*/, 0];
1034
+ case 2:
1035
+ if (!(flushFinalLine && buffer.length > 0)) return [3 /*break*/, 4];
1036
+ line = buffer;
1037
+ buffer = '';
1038
+ if (line.endsWith('\r')) {
1039
+ line = line.slice(0, -1);
1040
+ }
1041
+ return [4 /*yield*/, processLine(line)];
1042
+ case 3:
1043
+ _a.sent();
1044
+ _a.label = 4;
1045
+ case 4: return [2 /*return*/];
1046
+ }
1047
+ });
1048
+ });
1049
+ };
1050
+ _b.label = 1;
1051
+ case 1:
1052
+ if (!!signal.aborted) return [3 /*break*/, 5];
1053
+ return [4 /*yield*/, reader.read()];
1054
+ case 2:
1055
+ _a = _b.sent(), done = _a.done, value = _a.value;
1056
+ if (done) {
1057
+ return [3 /*break*/, 5];
1058
+ }
1059
+ if (!value) return [3 /*break*/, 4];
1060
+ buffer += decoder.decode(value, { stream: true });
1061
+ return [4 /*yield*/, processBuffer()];
1062
+ case 3:
1063
+ _b.sent();
1064
+ _b.label = 4;
1065
+ case 4: return [3 /*break*/, 1];
1066
+ case 5:
1067
+ buffer += decoder.decode();
1068
+ return [4 /*yield*/, processBuffer(true)];
1069
+ case 6:
1070
+ _b.sent();
1071
+ return [4 /*yield*/, emitEvent()];
1072
+ case 7:
1073
+ _b.sent();
1074
+ return [2 /*return*/];
1075
+ }
1076
+ });
1077
+ });
1078
+ };
1079
+ FlowscaleAPI.prototype.parseSSEEventId = function (eventId) {
1080
+ if (eventId === undefined || eventId === '') {
1081
+ return undefined;
1082
+ }
1083
+ var numeric = Number(eventId);
1084
+ if (!Number.isNaN(numeric) && Number.isFinite(numeric)) {
1085
+ return numeric;
1086
+ }
1087
+ return eventId;
1088
+ };
1089
+ FlowscaleAPI.prototype.invokeRunEventsCallback = function (callback, payload) {
1090
+ return __awaiter(this, void 0, void 0, function () {
1091
+ var error_6;
1092
+ return __generator(this, function (_a) {
1093
+ switch (_a.label) {
1094
+ case 0:
1095
+ if (!callback) {
1096
+ return [2 /*return*/];
1097
+ }
1098
+ _a.label = 1;
1099
+ case 1:
1100
+ _a.trys.push([1, 6, , 7]);
1101
+ if (!(payload === undefined)) return [3 /*break*/, 3];
1102
+ return [4 /*yield*/, callback()];
1103
+ case 2:
1104
+ _a.sent();
1105
+ return [3 /*break*/, 5];
1106
+ case 3: return [4 /*yield*/, callback(payload)];
1107
+ case 4:
1108
+ _a.sent();
1109
+ _a.label = 5;
1110
+ case 5: return [3 /*break*/, 7];
1111
+ case 6:
1112
+ error_6 = _a.sent();
1113
+ this.logWarn('Run events callback failed:', error_6);
1114
+ return [3 /*break*/, 7];
1115
+ case 7: return [2 /*return*/];
1116
+ }
1117
+ });
1118
+ });
1119
+ };
658
1120
  /**
659
1121
  * Helper method to append data to FormData.
660
1122
  */
@@ -759,7 +1221,7 @@ var FlowscaleAPI = /** @class */ (function () {
759
1221
  case 0:
760
1222
  retries = 0;
761
1223
  _loop_2 = function () {
762
- var response, _b, error_5, axiosError, isTimeout, delay_1;
1224
+ var response, _b, error_7, axiosError, isTimeout, delay_1;
763
1225
  return __generator(this, function (_c) {
764
1226
  switch (_c.label) {
765
1227
  case 0:
@@ -777,13 +1239,13 @@ var FlowscaleAPI = /** @class */ (function () {
777
1239
  response = _b;
778
1240
  return [2 /*return*/, { value: response.data }];
779
1241
  case 5:
780
- error_5 = _c.sent();
781
- axiosError = error_5;
1242
+ error_7 = _c.sent();
1243
+ axiosError = error_7;
782
1244
  isTimeout = axiosError.code === 'ECONNABORTED' ||
783
1245
  (axiosError.response && axiosError.response.status === 504);
784
1246
  // Only retry on timeout errors or if we're in the browser (not Node.js)
785
1247
  if (!isTimeout && this_2.isNode) {
786
- this_2.handleError(error_5);
1248
+ this_2.handleError(error_7);
787
1249
  }
788
1250
  retries++;
789
1251
  // If we've tried max times, handle the error
@@ -793,7 +1255,7 @@ var FlowscaleAPI = /** @class */ (function () {
793
1255
  return [2 /*return*/, { value: { status: "success", data: {} } }];
794
1256
  }
795
1257
  else {
796
- this_2.handleError(error_5);
1258
+ this_2.handleError(error_7);
797
1259
  }
798
1260
  }
799
1261
  this_2.logWarn("Attempt ".concat(retries, "/").concat(this_2.maxRetries, " failed, retrying in ").concat(this_2.retryDelay * Math.pow(2, retries - 1), "ms..."));
@@ -823,127 +1285,6 @@ var FlowscaleAPI = /** @class */ (function () {
823
1285
  });
824
1286
  });
825
1287
  };
826
- /**
827
- * Connects to the WebSocket API endpoint and sets up event handlers.
828
- * @param options - Configuration options for the WebSocket connection
829
- * @returns A function that can be called to disconnect the WebSocket
830
- */
831
- FlowscaleAPI.prototype.connectWebSocket = function (options) {
832
- var _this = this;
833
- var _a, _b, _c;
834
- if (options === void 0) { options = {}; }
835
- if (!this.WebSocketImpl) {
836
- throw new Error('WebSocket functionality is not available. ' +
837
- (this.isNode ?
838
- 'For Node.js environments, install the "ws" library and pass it as WebSocketImpl in the config:\n' +
839
- 'npm install ws\n' +
840
- 'const WebSocket = require("ws");\n' +
841
- 'new FlowscaleAPI({ ..., WebSocketImpl: WebSocket })' :
842
- 'WebSocket is not supported in this environment.'));
843
- }
844
- // Default options
845
- var reconnect = (_a = options.reconnect) !== null && _a !== void 0 ? _a : true;
846
- var reconnectInterval = (_b = options.reconnectInterval) !== null && _b !== void 0 ? _b : 5000;
847
- var maxReconnectAttempts = (_c = options.maxReconnectAttempts) !== null && _c !== void 0 ? _c : 5;
848
- // Close any existing connection and clear reconnection timeout
849
- this.disconnectWebSocket();
850
- // Format the WebSocket URL
851
- var wsUrl = new URL('/api/v1/ws/subscribe', this.baseUrl);
852
- wsUrl.protocol = wsUrl.protocol.replace('https', 'wss');
853
- // Add API key only if not in proxy mode
854
- if (!this.proxyMode && this.apiKey) {
855
- wsUrl.searchParams.append('api_key', this.apiKey);
856
- }
857
- // Note: In proxy mode, authentication should be handled by the proxy server
858
- // Create a new WebSocket connection
859
- this.ws = new this.WebSocketImpl(wsUrl.toString());
860
- this.reconnectAttempts = 0;
861
- // Set up event handlers
862
- if (this.ws) {
863
- this.ws.onopen = function () {
864
- _this.logInfo('WebSocket connection established');
865
- _this.wsConnected = true;
866
- _this.reconnectAttempts = 0;
867
- if (options.onOpen)
868
- options.onOpen();
869
- };
870
- this.ws.onmessage = function (event) {
871
- try {
872
- var message = JSON.parse(event.data);
873
- if (options.onMessage)
874
- options.onMessage(message);
875
- }
876
- catch (error) {
877
- _this.logError('Failed to parse WebSocket message:', error);
878
- if (options.onError)
879
- options.onError(new ErrorEvent('error', { error: error }));
880
- }
881
- };
882
- this.ws.onclose = function (event) {
883
- _this.logInfo("WebSocket connection closed: ".concat(event.code, " ").concat(event.reason));
884
- _this.wsConnected = false;
885
- // Attempt to reconnect if enabled and not already reconnecting
886
- if (reconnect && _this.reconnectAttempts < maxReconnectAttempts && !_this.reconnectTimeout) {
887
- _this.reconnectAttempts++;
888
- _this.logInfo("Attempting to reconnect (".concat(_this.reconnectAttempts, "/").concat(maxReconnectAttempts, ")..."));
889
- _this.reconnectTimeout = setTimeout(function () {
890
- _this.reconnectTimeout = null;
891
- _this.connectWebSocket(options);
892
- }, reconnectInterval);
893
- }
894
- if (options.onClose)
895
- options.onClose(event);
896
- };
897
- this.ws.onerror = function (error) {
898
- _this.logError('WebSocket error:', error);
899
- if (options.onError)
900
- options.onError(error);
901
- };
902
- }
903
- // Return a function to disconnect
904
- return function () { return _this.disconnectWebSocket(); };
905
- };
906
- /**
907
- * Disconnects the WebSocket connection if it exists.
908
- */
909
- FlowscaleAPI.prototype.disconnectWebSocket = function () {
910
- // Clear any pending reconnection timeout
911
- if (this.reconnectTimeout) {
912
- clearTimeout(this.reconnectTimeout);
913
- this.reconnectTimeout = null;
914
- }
915
- if (this.ws) {
916
- this.ws.close();
917
- this.ws = null;
918
- this.wsConnected = false;
919
- }
920
- // Reset reconnection attempts
921
- this.reconnectAttempts = 0;
922
- };
923
- /**
924
- * Sends a message through the WebSocket connection.
925
- * @param message - The message to send
926
- * @returns Whether the message was sent successfully
927
- */
928
- FlowscaleAPI.prototype.sendWebSocketMessage = function (message) {
929
- if (message === undefined || message === null) {
930
- this.logError('Message cannot be null or undefined');
931
- return false;
932
- }
933
- if (!this.ws || !this.wsConnected) {
934
- this.logError('WebSocket is not connected');
935
- return false;
936
- }
937
- try {
938
- var messageString = typeof message === 'string' ? message : JSON.stringify(message);
939
- this.ws.send(messageString);
940
- return true;
941
- }
942
- catch (error) {
943
- this.logError('Error sending WebSocket message:', error);
944
- return false;
945
- }
946
- };
947
1288
  /**
948
1289
  * Updates custom headers for authentication (useful for refreshing JWT tokens)
949
1290
  * @param headers - Object containing header key-value pairs
@@ -964,19 +1305,6 @@ var FlowscaleAPI = /** @class */ (function () {
964
1305
  FlowscaleAPI.prototype.getCustomHeaders = function () {
965
1306
  return __assign({}, this.customHeaders);
966
1307
  };
967
- /**
968
- * Checks if the WebSocket connection is currently open.
969
- * @returns True if the WebSocket is connected, false otherwise
970
- */
971
- FlowscaleAPI.prototype.isWebSocketConnected = function () {
972
- var _a;
973
- if (!this.ws || !this.wsConnected) {
974
- return false;
975
- }
976
- // Handle both browser WebSocket and Node.js 'ws' library
977
- var OPEN_STATE = ((_a = this.WebSocketImpl) === null || _a === void 0 ? void 0 : _a.OPEN) || 1; // WebSocket.OPEN = 1
978
- return this.ws.readyState === OPEN_STATE;
979
- };
980
1308
  return FlowscaleAPI;
981
1309
  }());
982
1310
  exports.FlowscaleAPI = FlowscaleAPI;