flowscale 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -75,6 +75,7 @@ export declare class FlowscaleAPI {
75
75
  private emitExecuteWorkflowAsyncIntermediateResponse;
76
76
  private formatUnknownError;
77
77
  private sleep;
78
+ private normalizeGetOutputResponse;
78
79
  private resolveGlobalFetch;
79
80
  private consumeRunEventsStream;
80
81
  private safeReadResponseText;
package/dist/index.js CHANGED
@@ -236,13 +236,13 @@ var FlowscaleAPI = /** @class */ (function () {
236
236
  */
237
237
  FlowscaleAPI.prototype.executeWorkflowAsync = function (workflowId_1, data_1, groupId_1) {
238
238
  return __awaiter(this, arguments, void 0, function (workflowId, data, groupId, pollIntervalMs, timeoutMs, returnAllOutputsOrOptions) {
239
- 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, errorMessage, isIntentionalError;
240
240
  var _this = this;
241
- var _b, _c, _d, _e, _f;
241
+ var _b, _c, _d, _e, _f, _g, _h, _j;
242
242
  if (pollIntervalMs === void 0) { pollIntervalMs = 2000; }
243
243
  if (timeoutMs === void 0) { timeoutMs = 600000; }
244
- return __generator(this, function (_g) {
245
- switch (_g.label) {
244
+ return __generator(this, function (_k) {
245
+ switch (_k.label) {
246
246
  case 0:
247
247
  // Validate inputs
248
248
  if (!workflowId || typeof workflowId !== 'string' || workflowId.trim() === '') {
@@ -261,7 +261,7 @@ var FlowscaleAPI = /** @class */ (function () {
261
261
  startTime = Date.now();
262
262
  return [4 /*yield*/, this.executeWorkflow(workflowId, data, groupId)];
263
263
  case 1:
264
- executeResponse = _g.sent();
264
+ executeResponse = _k.sent();
265
265
  if (!((_b = executeResponse === null || executeResponse === void 0 ? void 0 : executeResponse.data) === null || _b === void 0 ? void 0 : _b.run_id)) {
266
266
  throw new Error('No run ID returned from workflow execution');
267
267
  }
@@ -278,10 +278,10 @@ var FlowscaleAPI = /** @class */ (function () {
278
278
  response: executeResponse,
279
279
  })];
280
280
  case 2:
281
- _g.sent();
281
+ _k.sent();
282
282
  run_status = "queued";
283
283
  output_names = [];
284
- _g.label = 3;
284
+ _k.label = 3;
285
285
  case 3:
286
286
  if (!(output_names.length === 0)) return [3 /*break*/, 8];
287
287
  if (Date.now() - startTime > timeoutMs) {
@@ -289,7 +289,7 @@ var FlowscaleAPI = /** @class */ (function () {
289
289
  }
290
290
  return [4 /*yield*/, this.getRun(runId)];
291
291
  case 4:
292
- runResponse = _g.sent();
292
+ runResponse = _k.sent();
293
293
  run_status = ((_d = runResponse === null || runResponse === void 0 ? void 0 : runResponse.data) === null || _d === void 0 ? void 0 : _d.status) || "queued";
294
294
  output_names = ((_e = runResponse === null || runResponse === void 0 ? void 0 : runResponse.data) === null || _e === void 0 ? void 0 : _e.output_names) || [];
295
295
  return [4 /*yield*/, this.emitExecuteWorkflowAsyncIntermediateResponse(onIntermediateResponse, {
@@ -303,15 +303,15 @@ var FlowscaleAPI = /** @class */ (function () {
303
303
  response: runResponse,
304
304
  })];
305
305
  case 5:
306
- _g.sent();
306
+ _k.sent();
307
307
  if (run_status === 'failed' || run_status === 'cancelled') {
308
308
  throw new Error("Run ".concat(runId, " failed with status: ").concat(run_status));
309
309
  }
310
310
  if (!(output_names.length === 0)) return [3 /*break*/, 7];
311
311
  return [4 /*yield*/, this.sleep(pollIntervalMs)];
312
312
  case 6:
313
- _g.sent();
314
- _g.label = 7;
313
+ _k.sent();
314
+ _k.label = 7;
315
315
  case 7: return [3 /*break*/, 3];
316
316
  case 8:
317
317
  if (!output_names || output_names.length === 0) {
@@ -319,15 +319,39 @@ var FlowscaleAPI = /** @class */ (function () {
319
319
  }
320
320
  this.logInfo("Found ".concat(output_names.length, " outputs to wait for: ").concat(output_names.join(', ')));
321
321
  attempts = 0;
322
- _g.label = 9;
322
+ _k.label = 9;
323
323
  case 9:
324
- if (!true) return [3 /*break*/, 21];
324
+ if (!true) return [3 /*break*/, 23];
325
325
  if (Date.now() - startTime > timeoutMs) {
326
326
  throw new Error("Workflow execution timed out after ".concat(timeoutMs, "ms"));
327
327
  }
328
- _g.label = 10;
328
+ _k.label = 10;
329
329
  case 10:
330
- _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
+ }
331
355
  outputPromises = output_names.map(function (outputName) { return __awaiter(_this, void 0, void 0, function () {
332
356
  var output, error_2;
333
357
  var _a;
@@ -372,8 +396,8 @@ var FlowscaleAPI = /** @class */ (function () {
372
396
  });
373
397
  }); });
374
398
  return [4 /*yield*/, Promise.all(outputPromises)];
375
- case 11:
376
- outputResults = _g.sent();
399
+ case 13:
400
+ outputResults = _k.sent();
377
401
  successfulOutputs = [];
378
402
  pendingOutputs = [];
379
403
  hasErrors = false;
@@ -413,9 +437,9 @@ var FlowscaleAPI = /** @class */ (function () {
413
437
  if (hasErrors) {
414
438
  throw new Error('One or more workflow outputs failed');
415
439
  }
416
- if (!(pendingOutputs.length === 0)) return [3 /*break*/, 15];
440
+ if (!(pendingOutputs.length === 0)) return [3 /*break*/, 17];
417
441
  this.logInfo("All ".concat(successfulOutputs.length, " outputs completed successfully"));
418
- 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];
419
443
  deprecationWarning = {
420
444
  message: successfulOutputs.length > 1
421
445
  ? 'Single output format is deprecated. This workflow returned multiple outputs but only the first is shown.'
@@ -443,10 +467,10 @@ var FlowscaleAPI = /** @class */ (function () {
443
467
  completed_outputs: successfulOutputs.map(function (output) { return output.filename; }),
444
468
  response: legacyResponse,
445
469
  })];
446
- case 12:
447
- _g.sent();
470
+ case 14:
471
+ _k.sent();
448
472
  return [2 /*return*/, legacyResponse];
449
- case 13:
473
+ case 15:
450
474
  allOutputsResponse = {
451
475
  status: 'success',
452
476
  data: successfulOutputs
@@ -460,19 +484,25 @@ var FlowscaleAPI = /** @class */ (function () {
460
484
  completed_outputs: successfulOutputs.map(function (output) { return output.filename; }),
461
485
  response: allOutputsResponse,
462
486
  })];
463
- case 14:
464
- _g.sent();
487
+ case 16:
488
+ _k.sent();
465
489
  return [2 /*return*/, allOutputsResponse];
466
- case 15:
490
+ case 17:
467
491
  // If some outputs are still pending, continue polling
468
492
  this.logDebug("".concat(pendingOutputs.length, " outputs still pending: ").concat(pendingOutputs.join(', ')));
469
493
  return [4 /*yield*/, this.sleep(pollIntervalMs)];
470
- case 16:
471
- _g.sent();
472
- return [3 /*break*/, 20];
473
- case 17:
474
- error_1 = _g.sent();
475
- // Log the error but continue polling
494
+ case 18:
495
+ _k.sent();
496
+ return [3 /*break*/, 22];
497
+ case 19:
498
+ error_1 = _k.sent();
499
+ errorMessage = error_1 instanceof Error ? error_1.message : String(error_1);
500
+ isIntentionalError = errorMessage.includes('failed with status:') ||
501
+ errorMessage.includes('One or more workflow outputs failed');
502
+ if (isIntentionalError) {
503
+ throw error_1;
504
+ }
505
+ // Transient errors: log and continue polling
476
506
  this.logWarn("Error polling for outputs (attempt ".concat(attempts, "):"), error_1);
477
507
  return [4 /*yield*/, this.emitExecuteWorkflowAsyncIntermediateResponse(onIntermediateResponse, {
478
508
  type: 'poll_retry',
@@ -483,22 +513,22 @@ var FlowscaleAPI = /** @class */ (function () {
483
513
  output_names: output_names,
484
514
  error: this.formatUnknownError(error_1),
485
515
  })];
486
- case 18:
487
- _g.sent();
516
+ case 20:
517
+ _k.sent();
488
518
  // After several failures, increase polling interval to avoid excessive requests
489
519
  if (attempts > 5) {
490
520
  pollIntervalMs = Math.min(pollIntervalMs * 1.5, 10000); // Increase interval, max 10s
491
521
  }
492
522
  // Wait before retrying
493
523
  return [4 /*yield*/, this.sleep(pollIntervalMs)];
494
- case 19:
524
+ case 21:
495
525
  // Wait before retrying
496
- _g.sent();
497
- return [3 /*break*/, 20];
498
- case 20:
526
+ _k.sent();
527
+ return [3 /*break*/, 22];
528
+ case 22:
499
529
  attempts++;
500
530
  return [3 /*break*/, 9];
501
- case 21: return [2 /*return*/];
531
+ case 23: return [2 /*return*/];
502
532
  }
503
533
  });
504
534
  });
@@ -509,7 +539,7 @@ var FlowscaleAPI = /** @class */ (function () {
509
539
  */
510
540
  FlowscaleAPI.prototype.getOutput = function (filename) {
511
541
  return __awaiter(this, void 0, void 0, function () {
512
- var error_3, axiosError;
542
+ var response, error_3, axiosError;
513
543
  return __generator(this, function (_a) {
514
544
  switch (_a.label) {
515
545
  case 0:
@@ -520,21 +550,35 @@ var FlowscaleAPI = /** @class */ (function () {
520
550
  _a.label = 1;
521
551
  case 1:
522
552
  _a.trys.push([1, 3, , 4]);
523
- return [4 /*yield*/, this.makeRequest('get', '/api/v1/runs/output', {
553
+ return [4 /*yield*/, this.client.get('/api/v1/runs/output', {
524
554
  params: { filename: filename },
555
+ // We need to inspect HTTP status directly (204 is a valid "not ready" response).
556
+ validateStatus: function () { return true; },
525
557
  })];
526
- case 2: return [2 /*return*/, _a.sent()];
527
- case 3:
528
- error_3 = _a.sent();
529
- axiosError = error_3;
530
- if (axiosError.response && axiosError.response.status === 204) {
558
+ case 2:
559
+ response = _a.sent();
560
+ if (response.status === 204) {
531
561
  return [2 /*return*/, null]; // No output found
532
562
  }
533
- else if (axiosError.response && axiosError.response.status === 504) {
563
+ else if (response.status === 504) {
534
564
  this.logWarn('Received 504 Gateway Timeout, retrying...');
535
565
  // For 504 errors specifically, we'll return null to allow the polling to continue
536
566
  return [2 /*return*/, null];
537
567
  }
568
+ else if (response.status === 408) {
569
+ throw new Error('Run Timeout');
570
+ }
571
+ if (response.status < 200 || response.status >= 300) {
572
+ throw new Error("Error: ".concat(response.status, " ").concat(response.statusText, " - ").concat(JSON.stringify(response.data)));
573
+ }
574
+ return [2 /*return*/, this.normalizeGetOutputResponse(response.data)];
575
+ case 3:
576
+ error_3 = _a.sent();
577
+ axiosError = error_3;
578
+ if (axiosError.response && axiosError.response.status === 504) {
579
+ this.logWarn('Received 504 Gateway Timeout, retrying...');
580
+ return [2 /*return*/, null];
581
+ }
538
582
  else if (axiosError.response && axiosError.response.status === 408) {
539
583
  throw new Error('Run Timeout');
540
584
  }
@@ -641,11 +685,13 @@ var FlowscaleAPI = /** @class */ (function () {
641
685
  switch (_a.label) {
642
686
  case 0:
643
687
  if (!callback) {
688
+ this.logError("No callback");
644
689
  return [2 /*return*/];
645
690
  }
646
691
  _a.label = 1;
647
692
  case 1:
648
693
  _a.trys.push([1, 3, , 4]);
694
+ this.logInfo("got INTERMEDIATe UPDATE");
649
695
  return [4 /*yield*/, callback(update)];
650
696
  case 2:
651
697
  _a.sent();
@@ -685,6 +731,55 @@ var FlowscaleAPI = /** @class */ (function () {
685
731
  });
686
732
  });
687
733
  };
734
+ FlowscaleAPI.prototype.normalizeGetOutputResponse = function (payload) {
735
+ if (!payload || typeof payload !== 'object') {
736
+ return null;
737
+ }
738
+ if (typeof payload.status === 'string') {
739
+ return payload;
740
+ }
741
+ // Handle payloads that omit top-level status but still contain useful output data.
742
+ if (payload.data && typeof payload.data === 'object') {
743
+ if (payload.data.errors) {
744
+ return {
745
+ status: 'error',
746
+ data: {
747
+ errors: payload.data.errors,
748
+ },
749
+ };
750
+ }
751
+ if (payload.data.progress) {
752
+ return {
753
+ status: 'in_progress',
754
+ data: payload.data,
755
+ };
756
+ }
757
+ if (payload.data.download_url ||
758
+ payload.data.file_content ||
759
+ payload.data.generation_status) {
760
+ return {
761
+ status: 'success',
762
+ data: payload.data,
763
+ };
764
+ }
765
+ }
766
+ // Legacy shape fallback: response data returned directly without wrapping.
767
+ if (payload.download_url || payload.file_content || payload.generation_status) {
768
+ return {
769
+ status: 'success',
770
+ data: payload,
771
+ };
772
+ }
773
+ if (payload.errors) {
774
+ return {
775
+ status: 'error',
776
+ data: {
777
+ errors: payload.errors,
778
+ },
779
+ };
780
+ }
781
+ return null;
782
+ };
688
783
  FlowscaleAPI.prototype.resolveGlobalFetch = function () {
689
784
  var globalScope = typeof globalThis !== 'undefined'
690
785
  ? globalThis
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowscale",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "An NPM library for communicating with the Flowscale APIs",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",