veryfront 0.0.73 → 0.0.75

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