veryfront 0.0.72 → 0.0.74

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.
@@ -392,7 +392,7 @@ function toError(veryfrontError) {
392
392
  }
393
393
 
394
394
  // src/platform/compat/runtime.ts
395
- var isDeno = typeof Deno !== "undefined";
395
+ var isDeno = typeof Deno !== "undefined" && typeof Deno.version === "object";
396
396
  var isNode = typeof globalThis.process !== "undefined" && globalThis.process?.versions?.node !== void 0;
397
397
  var isBun = typeof globalThis.Bun !== "undefined";
398
398
  var isCloudflare = typeof globalThis !== "undefined" && "caches" in globalThis && "WebSocketPair" in globalThis;
@@ -581,19 +581,69 @@ function getEnvironmentVariable(name) {
581
581
  }
582
582
  return void 0;
583
583
  }
584
+ function isProductionEnvironment() {
585
+ return getEnvironmentVariable("NODE_ENV") === "production";
586
+ }
584
587
 
585
588
  // src/core/utils/logger/logger.ts
586
589
  var cachedLogLevel;
590
+ var cachedLogFormat;
587
591
  function resolveLogLevel(force = false) {
588
592
  if (force || cachedLogLevel === void 0) {
589
593
  cachedLogLevel = getDefaultLevel();
590
594
  }
591
595
  return cachedLogLevel;
592
596
  }
593
- var ConsoleLogger = class {
594
- constructor(prefix, level = resolveLogLevel()) {
597
+ function resolveLogFormat(force = false) {
598
+ if (force || cachedLogFormat === void 0) {
599
+ cachedLogFormat = getDefaultFormat();
600
+ }
601
+ return cachedLogFormat;
602
+ }
603
+ function getDefaultFormat() {
604
+ const envFormat = getEnvironmentVariable("LOG_FORMAT");
605
+ if (envFormat === "json" || envFormat === "text") {
606
+ return envFormat;
607
+ }
608
+ return isProductionEnvironment() ? "json" : "text";
609
+ }
610
+ function serializeError(err) {
611
+ if (err instanceof Error) {
612
+ return {
613
+ name: err.name,
614
+ message: err.message,
615
+ stack: err.stack
616
+ };
617
+ }
618
+ if (err !== void 0 && err !== null) {
619
+ return {
620
+ name: "UnknownError",
621
+ message: String(err)
622
+ };
623
+ }
624
+ return void 0;
625
+ }
626
+ function extractContext(args) {
627
+ let context;
628
+ let error;
629
+ for (const arg of args) {
630
+ if (arg instanceof Error) {
631
+ error = serializeError(arg);
632
+ } else if (typeof arg === "object" && arg !== null && !Array.isArray(arg)) {
633
+ context = { ...context, ...arg };
634
+ }
635
+ }
636
+ return { context, error };
637
+ }
638
+ var ConsoleLogger = class _ConsoleLogger {
639
+ constructor(prefix, level = resolveLogLevel(), format = resolveLogFormat(), boundContext) {
595
640
  this.prefix = prefix;
596
641
  this.level = level;
642
+ this.format = format;
643
+ this.boundContext = {};
644
+ if (boundContext) {
645
+ this.boundContext = boundContext;
646
+ }
597
647
  }
598
648
  setLevel(level) {
599
649
  this.level = level;
@@ -601,36 +651,88 @@ var ConsoleLogger = class {
601
651
  getLevel() {
602
652
  return this.level;
603
653
  }
604
- debug(message, ...args) {
605
- if (this.level <= 0 /* DEBUG */) {
606
- console.debug(`[${this.prefix}] DEBUG: ${message}`, ...args);
654
+ setFormat(format) {
655
+ this.format = format;
656
+ }
657
+ getFormat() {
658
+ return this.format;
659
+ }
660
+ /**
661
+ * Create a child logger with additional bound context.
662
+ */
663
+ child(context) {
664
+ return new _ConsoleLogger(this.prefix, this.level, this.format, {
665
+ ...this.boundContext,
666
+ ...context
667
+ });
668
+ }
669
+ formatJson(level, message, args) {
670
+ const { context, error } = extractContext(args);
671
+ const entry = {
672
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
673
+ level,
674
+ service: this.prefix.toLowerCase(),
675
+ message
676
+ };
677
+ const mergedContext = { ...this.boundContext, ...context };
678
+ if (Object.keys(mergedContext).length > 0) {
679
+ if ("requestId" in mergedContext) {
680
+ entry.requestId = String(mergedContext.requestId);
681
+ delete mergedContext.requestId;
682
+ }
683
+ if ("traceId" in mergedContext) {
684
+ entry.traceId = String(mergedContext.traceId);
685
+ delete mergedContext.traceId;
686
+ }
687
+ if ("projectSlug" in mergedContext) {
688
+ entry.projectSlug = String(mergedContext.projectSlug);
689
+ delete mergedContext.projectSlug;
690
+ }
691
+ if ("durationMs" in mergedContext) {
692
+ entry.durationMs = Number(mergedContext.durationMs);
693
+ delete mergedContext.durationMs;
694
+ }
695
+ if (Object.keys(mergedContext).length > 0) {
696
+ entry.context = mergedContext;
697
+ }
698
+ }
699
+ if (error) {
700
+ entry.error = error;
607
701
  }
702
+ return JSON.stringify(entry);
608
703
  }
609
- info(message, ...args) {
610
- if (this.level <= 1 /* INFO */) {
611
- console.log(`[${this.prefix}] ${message}`, ...args);
704
+ log(level, logLevel, consoleFn, message, args) {
705
+ if (this.level > logLevel)
706
+ return;
707
+ if (this.format === "json") {
708
+ consoleFn(this.formatJson(level, message, args));
709
+ } else {
710
+ const prefix = level === "info" ? "" : ` ${level.toUpperCase()}:`;
711
+ consoleFn(`[${this.prefix}]${prefix} ${message}`, ...args);
612
712
  }
613
713
  }
714
+ debug(message, ...args) {
715
+ this.log("debug", 0 /* DEBUG */, console.debug, message, args);
716
+ }
717
+ info(message, ...args) {
718
+ this.log("info", 1 /* INFO */, console.log, message, args);
719
+ }
614
720
  warn(message, ...args) {
615
- if (this.level <= 2 /* WARN */) {
616
- console.warn(`[${this.prefix}] WARN: ${message}`, ...args);
617
- }
721
+ this.log("warn", 2 /* WARN */, console.warn, message, args);
618
722
  }
619
723
  error(message, ...args) {
620
- if (this.level <= 3 /* ERROR */) {
621
- console.error(`[${this.prefix}] ERROR: ${message}`, ...args);
622
- }
724
+ this.log("error", 3 /* ERROR */, console.error, message, args);
623
725
  }
624
726
  async time(label, fn) {
625
727
  const start = performance.now();
626
728
  try {
627
729
  const result = await fn();
628
- const end = performance.now();
629
- this.debug(`${label} completed in ${(end - start).toFixed(2)}ms`);
730
+ const durationMs = performance.now() - start;
731
+ this.debug(`${label} completed`, { durationMs: Math.round(durationMs) });
630
732
  return result;
631
733
  } catch (error) {
632
- const end = performance.now();
633
- this.error(`${label} failed after ${(end - start).toFixed(2)}ms`, error);
734
+ const durationMs = performance.now() - start;
735
+ this.error(`${label} failed`, { durationMs: Math.round(durationMs) }, error);
634
736
  throw error;
635
737
  }
636
738
  }
@@ -673,6 +775,7 @@ var serverLogger = createLogger("SERVER");
673
775
  var rendererLogger = createLogger("RENDERER");
674
776
  var bundlerLogger = createLogger("BUNDLER");
675
777
  var agentLogger = createLogger("AGENT");
778
+ var proxyLogger = createLogger("PROXY");
676
779
  var logger = createLogger("VERYFRONT");
677
780
 
678
781
  // src/core/utils/constants/cache.ts
@@ -698,7 +801,7 @@ var LRU_DEFAULT_MAX_SIZE_BYTES = 50 * 1024 * 1024;
698
801
  // deno.json
699
802
  var deno_default = {
700
803
  name: "veryfront",
701
- version: "0.0.71",
804
+ version: "0.0.74",
702
805
  nodeModulesDir: "auto",
703
806
  exclude: [
704
807
  "npm/",
@@ -783,12 +886,12 @@ var deno_default = {
783
886
  csstype: "https://esm.sh/csstype@3.2.3",
784
887
  "@types/react": "https://esm.sh/@types/react@18.3.27?deps=csstype@3.2.3",
785
888
  "@types/react-dom": "https://esm.sh/@types/react-dom@18.3.7?deps=csstype@3.2.3",
786
- react: "https://esm.sh/react@18.3.1",
787
- "react-dom": "https://esm.sh/react-dom@18.3.1",
788
- "react-dom/server": "https://esm.sh/react-dom@18.3.1/server",
789
- "react-dom/client": "https://esm.sh/react-dom@18.3.1/client",
790
- "react/jsx-runtime": "https://esm.sh/react@18.3.1/jsx-runtime",
791
- "react/jsx-dev-runtime": "https://esm.sh/react@18.3.1/jsx-dev-runtime",
889
+ react: "npm:react@18.3.1",
890
+ "react-dom": "npm:react-dom@18.3.1",
891
+ "react-dom/server": "npm:react-dom@18.3.1/server.node",
892
+ "react-dom/client": "npm:react-dom@18.3.1/client",
893
+ "react/jsx-runtime": "npm:react@18.3.1/jsx-runtime",
894
+ "react/jsx-dev-runtime": "npm:react@18.3.1/jsx-dev-runtime",
792
895
  "@mdx-js/mdx": "npm:@mdx-js/mdx@3.0.0",
793
896
  "@mdx-js/react": "npm:@mdx-js/react@3.0.0",
794
897
  "unist-util-visit": "npm:unist-util-visit@5.0.0",
@@ -798,27 +901,36 @@ var deno_default = {
798
901
  "remark-frontmatter": "npm:remark-frontmatter@5.0.0",
799
902
  "rehype-highlight": "npm:rehype-highlight@7.0.2",
800
903
  "rehype-slug": "npm:rehype-slug@6.0.0",
801
- esbuild: "https://deno.land/x/esbuild@v0.20.1/wasm.js",
802
- "esbuild/mod.js": "https://deno.land/x/esbuild@v0.20.1/mod.js",
904
+ esbuild: "npm:esbuild@0.20.2",
905
+ "esbuild/mod.js": "npm:esbuild@0.20.2",
803
906
  "es-module-lexer": "npm:es-module-lexer@1.5.0",
804
- zod: "npm:zod@3.23.8",
907
+ zod: "npm:zod@3.25.76",
805
908
  "mime-types": "npm:mime-types@2.1.35",
806
909
  mdast: "npm:@types/mdast@4.0.3",
807
910
  hast: "npm:@types/hast@3.0.3",
808
911
  unist: "npm:@types/unist@3.0.2",
809
912
  unified: "npm:unified@11.0.5",
810
- ai: "https://esm.sh/ai@5.0.76?deps=react@18.3.1,react-dom@18.3.1",
811
- "ai/react": "https://esm.sh/@ai-sdk/react@2.0.1?deps=react@18.3.1,react-dom@18.3.1",
812
- "@ai-sdk/react": "https://esm.sh/@ai-sdk/react@2.0.1?deps=react@18.3.1,react-dom@18.3.1",
913
+ ai: "npm:ai@5.0.76",
914
+ "ai/react": "npm:@ai-sdk/react@2.0.1",
915
+ "@ai-sdk/react": "npm:@ai-sdk/react@2.0.1",
813
916
  "@ai-sdk/openai": "https://esm.sh/@ai-sdk/openai@2.0.1",
814
917
  "@ai-sdk/anthropic": "https://esm.sh/@ai-sdk/anthropic@2.0.1",
815
918
  unocss: "https://esm.sh/unocss@0.59.0",
816
919
  "@unocss/core": "https://esm.sh/@unocss/core@0.59.0",
817
920
  "@unocss/preset-wind": "https://esm.sh/@unocss/preset-wind@0.59.0",
921
+ "next-themes": "npm:next-themes@0.3.0",
818
922
  redis: "npm:redis",
819
923
  pg: "npm:pg",
820
924
  "@opentelemetry/api": "npm:@opentelemetry/api@1",
821
- "@opentelemetry/core": "npm:@opentelemetry/core@1"
925
+ "@opentelemetry/core": "npm:@opentelemetry/core@1",
926
+ "@opentelemetry/sdk-trace-base": "npm:@opentelemetry/sdk-trace-base@1",
927
+ "@opentelemetry/exporter-trace-otlp-http": "npm:@opentelemetry/exporter-trace-otlp-http@0.57",
928
+ "@opentelemetry/resources": "npm:@opentelemetry/resources@1",
929
+ "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@1",
930
+ "@babel/parser": "npm:@babel/parser@7.26.3",
931
+ "@babel/traverse": "npm:@babel/traverse@7.26.3",
932
+ "@babel/generator": "npm:@babel/generator@7.26.3",
933
+ "@babel/types": "npm:@babel/types@7.26.3"
822
934
  },
823
935
  compilerOptions: {
824
936
  jsx: "react-jsx",
@@ -826,7 +938,7 @@ var deno_default = {
826
938
  strict: true,
827
939
  noImplicitAny: true,
828
940
  noUncheckedIndexedAccess: true,
829
- types: [],
941
+ types: ["npm:@types/react@18"],
830
942
  lib: [
831
943
  "deno.window",
832
944
  "dom",
@@ -841,9 +953,9 @@ var deno_default = {
841
953
  build: "deno compile --allow-all --output ../../bin/veryfront src/cli/main.ts",
842
954
  "build:npm": "deno run -A scripts/build-npm.ts",
843
955
  release: "deno run -A scripts/release.ts",
844
- test: "DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --unstable-worker-options --unstable-net",
845
- "test:unit": "DENO_JOBS=1 deno test --parallel --allow-all --v8-flags=--max-old-space-size=8192 --ignore=tests --unstable-worker-options --unstable-net",
846
- "test:integration": "DENO_JOBS=1 deno test --parallel --fail-fast --allow-all tests --unstable-worker-options --unstable-net",
956
+ test: "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --unstable-worker-options --unstable-net",
957
+ "test:unit": "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --allow-all --v8-flags=--max-old-space-size=8192 --ignore=tests,src/ai/workflow/__tests__ --unstable-worker-options --unstable-net",
958
+ "test:integration": "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --fail-fast --allow-all tests --unstable-worker-options --unstable-net",
847
959
  "test:coverage": "rm -rf coverage && DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --coverage=coverage --unstable-worker-options --unstable-net || exit 1",
848
960
  "test:coverage:unit": "rm -rf coverage && DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --coverage=coverage --ignore=tests --unstable-worker-options --unstable-net || exit 1",
849
961
  "test:coverage:integration": "rm -rf coverage && DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --coverage=coverage tests --unstable-worker-options --unstable-net || exit 1",
@@ -918,6 +1030,7 @@ function getEnv(key) {
918
1030
 
919
1031
  // src/core/utils/version.ts
920
1032
  var VERSION = getEnv("VERYFRONT_VERSION") || (typeof deno_default.version === "string" ? deno_default.version : "0.0.0");
1033
+ var SERVER_START_TIME = Date.now();
921
1034
 
922
1035
  // src/core/utils/constants/http.ts
923
1036
  var KB_IN_BYTES = 1024;
@@ -1117,6 +1230,9 @@ var InMemoryBundleManifestStore = class {
1117
1230
  };
1118
1231
  var manifestStore = new InMemoryBundleManifestStore();
1119
1232
 
1233
+ // src/core/utils/perf-timer.ts
1234
+ var enabled = typeof process !== "undefined" ? process.env?.VERYFRONT_PERF === "1" : typeof Deno !== "undefined" ? Deno.env.get("VERYFRONT_PERF") === "1" : false;
1235
+
1120
1236
  // src/ai/workflow/blob/local-storage.ts
1121
1237
  var LocalBlobStorage = class {
1122
1238
  constructor(rootDir, baseUrl) {
@@ -3039,9 +3155,16 @@ var DAGExecutor = class {
3039
3155
  context,
3040
3156
  nodeStates
3041
3157
  );
3158
+ case "loop":
3159
+ return await this.executeLoopNode(
3160
+ node,
3161
+ config,
3162
+ context,
3163
+ nodeStates
3164
+ );
3042
3165
  default:
3043
3166
  throw new Error(
3044
- `Unknown node type "${config.type}" for node "${node.id}". Valid types are: step, parallel, map, branch, wait, subWorkflow`
3167
+ `Unknown node type "${config.type}" for node "${node.id}". Valid types are: step, parallel, map, branch, wait, subWorkflow, loop`
3045
3168
  );
3046
3169
  }
3047
3170
  }
@@ -3199,6 +3322,155 @@ var DAGExecutor = class {
3199
3322
  waiting: result.waiting
3200
3323
  };
3201
3324
  }
3325
+ /**
3326
+ * Execute a loop node
3327
+ */
3328
+ async executeLoopNode(node, config, context, nodeStates) {
3329
+ const startTime = Date.now();
3330
+ const previousResults = [];
3331
+ let iteration = 0;
3332
+ let exitReason = "condition";
3333
+ let lastError;
3334
+ const existingLoopState = context[`${node.id}_loop_state`];
3335
+ if (existingLoopState) {
3336
+ iteration = existingLoopState.iteration;
3337
+ previousResults.push(...existingLoopState.previousResults);
3338
+ }
3339
+ while (iteration < config.maxIterations) {
3340
+ const loopContext = {
3341
+ iteration,
3342
+ totalIterations: iteration,
3343
+ previousResults: [...previousResults],
3344
+ isFirstIteration: iteration === 0,
3345
+ isLastAllowedIteration: iteration === config.maxIterations - 1
3346
+ };
3347
+ const shouldContinue = await config.while(context, loopContext);
3348
+ if (!shouldContinue) {
3349
+ exitReason = "condition";
3350
+ break;
3351
+ }
3352
+ const steps = typeof config.steps === "function" ? config.steps(context, loopContext) : config.steps;
3353
+ const result = await this.execute(steps, {
3354
+ id: `${node.id}_iter_${iteration}`,
3355
+ workflowId: "",
3356
+ status: "running",
3357
+ input: context.input,
3358
+ nodeStates: {},
3359
+ currentNodes: [],
3360
+ context: { ...context, _loop: loopContext },
3361
+ checkpoints: [],
3362
+ pendingApprovals: [],
3363
+ createdAt: /* @__PURE__ */ new Date()
3364
+ });
3365
+ if (result.waiting) {
3366
+ Object.assign(nodeStates, result.nodeStates);
3367
+ const state2 = {
3368
+ nodeId: node.id,
3369
+ status: "running",
3370
+ output: {
3371
+ iteration,
3372
+ waiting: true,
3373
+ previousResults
3374
+ },
3375
+ attempt: 1,
3376
+ startedAt: new Date(startTime)
3377
+ };
3378
+ return {
3379
+ state: state2,
3380
+ contextUpdates: {
3381
+ ...result.context,
3382
+ [`${node.id}_loop_state`]: { iteration, previousResults }
3383
+ },
3384
+ waiting: true
3385
+ };
3386
+ }
3387
+ if (result.error) {
3388
+ lastError = result.error;
3389
+ exitReason = "error";
3390
+ break;
3391
+ }
3392
+ previousResults.push(result.context);
3393
+ Object.assign(context, result.context);
3394
+ Object.assign(nodeStates, result.nodeStates);
3395
+ if (config.delay && iteration < config.maxIterations - 1) {
3396
+ const delayMs = typeof config.delay === "number" ? config.delay : this.parseDuration(config.delay);
3397
+ await this.sleep(delayMs);
3398
+ }
3399
+ iteration++;
3400
+ }
3401
+ if (iteration >= config.maxIterations && exitReason !== "condition") {
3402
+ exitReason = "maxIterations";
3403
+ }
3404
+ const finalLoopContext = {
3405
+ iteration,
3406
+ totalIterations: iteration,
3407
+ previousResults,
3408
+ isFirstIteration: false,
3409
+ isLastAllowedIteration: true
3410
+ };
3411
+ let completionUpdates = {};
3412
+ if (exitReason === "maxIterations" && config.onMaxIterations) {
3413
+ completionUpdates = await config.onMaxIterations(context, finalLoopContext);
3414
+ } else if (exitReason === "condition" && config.onComplete) {
3415
+ completionUpdates = await config.onComplete(context, finalLoopContext);
3416
+ }
3417
+ const output = {
3418
+ exitReason,
3419
+ iterations: iteration,
3420
+ previousResults,
3421
+ ...completionUpdates
3422
+ };
3423
+ const state = {
3424
+ nodeId: node.id,
3425
+ status: exitReason === "error" ? "failed" : "completed",
3426
+ output,
3427
+ error: lastError,
3428
+ attempt: 1,
3429
+ startedAt: new Date(startTime),
3430
+ completedAt: /* @__PURE__ */ new Date()
3431
+ };
3432
+ this.config.onNodeComplete?.(node.id, state);
3433
+ return {
3434
+ state,
3435
+ contextUpdates: {
3436
+ [node.id]: output,
3437
+ ...completionUpdates
3438
+ },
3439
+ waiting: false
3440
+ };
3441
+ }
3442
+ /**
3443
+ * Parse duration string to milliseconds
3444
+ */
3445
+ parseDuration(duration) {
3446
+ if (typeof duration === "number")
3447
+ return duration;
3448
+ const match = duration.match(/^(\d+)(ms|s|m|h|d)$/);
3449
+ if (!match)
3450
+ return 0;
3451
+ const value = parseInt(match[1], 10);
3452
+ const unit = match[2];
3453
+ switch (unit) {
3454
+ case "ms":
3455
+ return value;
3456
+ case "s":
3457
+ return value * 1e3;
3458
+ case "m":
3459
+ return value * 60 * 1e3;
3460
+ case "h":
3461
+ return value * 60 * 60 * 1e3;
3462
+ case "d":
3463
+ return value * 24 * 60 * 60 * 1e3;
3464
+ default:
3465
+ return 0;
3466
+ }
3467
+ }
3468
+ /**
3469
+ * Sleep for specified milliseconds
3470
+ */
3471
+ sleep(ms) {
3472
+ return new Promise((resolve) => setTimeout(resolve, ms));
3473
+ }
3202
3474
  /**
3203
3475
  * Execute a step node
3204
3476
  */
@@ -3593,6 +3865,12 @@ var CheckpointManager = class {
3593
3865
  };
3594
3866
 
3595
3867
  // src/ai/workflow/executor/step-executor.ts
3868
+ var DEFAULT_RETRY = {
3869
+ maxAttempts: 1,
3870
+ backoff: "exponential",
3871
+ initialDelay: 1e3,
3872
+ maxDelay: 3e4
3873
+ };
3596
3874
  var DEFAULT_STEP_TIMEOUT_MS = 5 * 60 * 1e3;
3597
3875
  var StepExecutor = class {
3598
3876
  constructor(config = {}) {
@@ -3602,7 +3880,7 @@ var StepExecutor = class {
3602
3880
  };
3603
3881
  }
3604
3882
  /**
3605
- * Execute a step node
3883
+ * Execute a step node with retry support
3606
3884
  */
3607
3885
  async execute(node, context) {
3608
3886
  const startTime = Date.now();
@@ -3612,30 +3890,96 @@ var StepExecutor = class {
3612
3890
  `StepExecutor can only execute 'step' nodes, but node "${node.id}" has type '${config.type}'. This is likely a bug in the DAG executor routing.`
3613
3891
  );
3614
3892
  }
3615
- try {
3616
- const resolvedInput = await this.resolveInput(config.input, context);
3617
- this.config.onStepStart?.(node.id, resolvedInput);
3618
- const timeout = config.timeout ? parseDuration(config.timeout) : this.config.defaultTimeout;
3619
- const output = await this.executeWithTimeout(
3620
- () => this.executeStep(config, resolvedInput, context),
3621
- timeout,
3622
- node.id
3623
- );
3624
- this.config.onStepComplete?.(node.id, output);
3625
- return {
3626
- success: true,
3627
- output,
3628
- executionTime: Date.now() - startTime
3629
- };
3630
- } catch (error) {
3631
- const errorMessage = error instanceof Error ? error.message : String(error);
3632
- this.config.onStepError?.(node.id, error);
3633
- return {
3634
- success: false,
3635
- error: errorMessage,
3636
- executionTime: Date.now() - startTime
3637
- };
3893
+ const retryConfig = { ...DEFAULT_RETRY, ...config.retry };
3894
+ const maxAttempts = retryConfig.maxAttempts ?? 1;
3895
+ let lastError;
3896
+ let attempt = 0;
3897
+ while (attempt < maxAttempts) {
3898
+ attempt++;
3899
+ try {
3900
+ const resolvedInput = await this.resolveInput(config.input, context);
3901
+ this.config.onStepStart?.(node.id, resolvedInput);
3902
+ const timeout = config.timeout ? parseDuration(config.timeout) : this.config.defaultTimeout;
3903
+ const output = await this.executeWithTimeout(
3904
+ () => this.executeStep(config, resolvedInput, context),
3905
+ timeout,
3906
+ node.id
3907
+ );
3908
+ this.config.onStepComplete?.(node.id, output);
3909
+ return {
3910
+ success: true,
3911
+ output,
3912
+ executionTime: Date.now() - startTime
3913
+ };
3914
+ } catch (error) {
3915
+ lastError = error instanceof Error ? error : new Error(String(error));
3916
+ const shouldRetry = attempt < maxAttempts && this.isRetryableError(lastError, retryConfig);
3917
+ if (shouldRetry) {
3918
+ const delay2 = this.calculateRetryDelay(attempt, retryConfig);
3919
+ await this.sleep(delay2);
3920
+ continue;
3921
+ }
3922
+ this.config.onStepError?.(node.id, lastError);
3923
+ return {
3924
+ success: false,
3925
+ error: lastError.message,
3926
+ executionTime: Date.now() - startTime
3927
+ };
3928
+ }
3929
+ }
3930
+ return {
3931
+ success: false,
3932
+ error: lastError?.message ?? "Unknown error",
3933
+ executionTime: Date.now() - startTime
3934
+ };
3935
+ }
3936
+ /**
3937
+ * Check if error is retryable
3938
+ */
3939
+ isRetryableError(error, config) {
3940
+ if (config.retryIf) {
3941
+ return config.retryIf(error);
3942
+ }
3943
+ const retryablePatterns = [
3944
+ /timeout/i,
3945
+ /ECONNRESET/i,
3946
+ /ECONNREFUSED/i,
3947
+ /ETIMEDOUT/i,
3948
+ /rate limit/i,
3949
+ /429/,
3950
+ /503/,
3951
+ /502/
3952
+ ];
3953
+ return retryablePatterns.some((pattern) => pattern.test(error.message));
3954
+ }
3955
+ /**
3956
+ * Calculate retry delay based on backoff strategy
3957
+ */
3958
+ calculateRetryDelay(attempt, config) {
3959
+ const initialDelay = config.initialDelay ?? 1e3;
3960
+ const maxDelay = config.maxDelay ?? 3e4;
3961
+ let delay2;
3962
+ switch (config.backoff) {
3963
+ case "exponential":
3964
+ delay2 = initialDelay * Math.pow(2, attempt - 1);
3965
+ break;
3966
+ case "linear":
3967
+ delay2 = initialDelay * attempt;
3968
+ break;
3969
+ case "fixed":
3970
+ default:
3971
+ delay2 = initialDelay;
3972
+ break;
3638
3973
  }
3974
+ const jitter = delay2 * 0.1 * (Math.random() * 2 - 1);
3975
+ delay2 = Math.min(delay2 + jitter, maxDelay);
3976
+ return Math.floor(delay2);
3977
+ }
3978
+ /**
3979
+ * Sleep for specified milliseconds
3980
+ */
3981
+ sleep(ms) {
3982
+ return new Promise((resolve) => setTimeout(resolve, ms));
3639
3983
  }
3640
3984
  /**
3641
3985
  * Resolve step input from context
@@ -4621,7 +4965,35 @@ var WorkflowClient = class {
4621
4965
  this.executor = new WorkflowExecutor({
4622
4966
  backend: this.backend,
4623
4967
  debug: this.debug,
4624
- ...config.executor
4968
+ ...config.executor,
4969
+ onWaiting: async (run, nodeId) => {
4970
+ const nodeState = run.nodeStates[nodeId];
4971
+ if (!nodeState?.input) {
4972
+ if (this.debug) {
4973
+ console.log(`[WorkflowClient] No wait config found for node: ${nodeId}`);
4974
+ }
4975
+ return;
4976
+ }
4977
+ const input = nodeState.input;
4978
+ if (input.type !== "approval") {
4979
+ return;
4980
+ }
4981
+ const waitConfig = {
4982
+ type: "wait",
4983
+ waitType: "approval",
4984
+ message: input.message,
4985
+ payload: input.payload
4986
+ };
4987
+ try {
4988
+ await this.approvalManager.createApproval(run, nodeId, waitConfig, run.context);
4989
+ if (this.debug) {
4990
+ console.log(`[WorkflowClient] Created approval for node: ${nodeId}`);
4991
+ }
4992
+ } catch (error) {
4993
+ console.error(`[WorkflowClient] Failed to create approval:`, error);
4994
+ }
4995
+ config.executor?.onWaiting?.(run, nodeId);
4996
+ }
4625
4997
  });
4626
4998
  this.approvalManager = new ApprovalManager({
4627
4999
  backend: this.backend,