veryfront 0.0.73 → 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.
Files changed (43) hide show
  1. package/README.md +3 -0
  2. package/dist/ai/components.js +3 -3
  3. package/dist/ai/components.js.map +1 -1
  4. package/dist/ai/dev.js +130 -21
  5. package/dist/ai/dev.js.map +2 -2
  6. package/dist/ai/index.js +895 -418
  7. package/dist/ai/index.js.map +4 -4
  8. package/dist/ai/production.js +135 -38
  9. package/dist/ai/production.js.map +2 -2
  10. package/dist/ai/react.js +8 -7
  11. package/dist/ai/react.js.map +2 -2
  12. package/dist/ai/workflow.js +468 -110
  13. package/dist/ai/workflow.js.map +4 -4
  14. package/dist/components.js +8178 -1164
  15. package/dist/components.js.map +4 -4
  16. package/dist/config.js +377 -39
  17. package/dist/config.js.map +3 -3
  18. package/dist/context.d.ts +44 -0
  19. package/dist/context.js +52 -0
  20. package/dist/context.js.map +7 -0
  21. package/dist/data.js +176 -62
  22. package/dist/data.js.map +3 -3
  23. package/dist/fonts.d.ts +24 -0
  24. package/dist/fonts.js +45 -0
  25. package/dist/fonts.js.map +7 -0
  26. package/dist/head.d.ts +21 -0
  27. package/dist/head.js +34 -0
  28. package/dist/head.js.map +7 -0
  29. package/dist/index.js +8098 -1048
  30. package/dist/index.js.map +4 -4
  31. package/dist/oauth/handlers.js +6 -7
  32. package/dist/oauth/handlers.js.map +1 -1
  33. package/dist/oauth/index.js +6 -7
  34. package/dist/oauth/index.js.map +1 -1
  35. package/dist/oauth/providers.js +0 -3
  36. package/dist/oauth/providers.js.map +1 -1
  37. package/dist/oauth/token-store.js +6 -4
  38. package/dist/oauth/token-store.js.map +1 -1
  39. package/dist/router.d.ts +69 -0
  40. package/dist/router.js +61 -0
  41. package/dist/router.js.map +7 -0
  42. package/package.json +19 -2
  43. package/dist/cli.js +0 -107694
package/dist/ai/index.js CHANGED
@@ -79,19 +79,69 @@ function getEnvironmentVariable(name) {
79
79
  }
80
80
  return void 0;
81
81
  }
82
+ function isProductionEnvironment() {
83
+ return getEnvironmentVariable("NODE_ENV") === "production";
84
+ }
82
85
 
83
86
  // src/core/utils/logger/logger.ts
84
87
  var cachedLogLevel;
88
+ var cachedLogFormat;
85
89
  function resolveLogLevel(force = false) {
86
90
  if (force || cachedLogLevel === void 0) {
87
91
  cachedLogLevel = getDefaultLevel();
88
92
  }
89
93
  return cachedLogLevel;
90
94
  }
91
- var ConsoleLogger = class {
92
- constructor(prefix, level = resolveLogLevel()) {
95
+ function resolveLogFormat(force = false) {
96
+ if (force || cachedLogFormat === void 0) {
97
+ cachedLogFormat = getDefaultFormat();
98
+ }
99
+ return cachedLogFormat;
100
+ }
101
+ function getDefaultFormat() {
102
+ const envFormat = getEnvironmentVariable("LOG_FORMAT");
103
+ if (envFormat === "json" || envFormat === "text") {
104
+ return envFormat;
105
+ }
106
+ return isProductionEnvironment() ? "json" : "text";
107
+ }
108
+ function serializeError(err) {
109
+ if (err instanceof Error) {
110
+ return {
111
+ name: err.name,
112
+ message: err.message,
113
+ stack: err.stack
114
+ };
115
+ }
116
+ if (err !== void 0 && err !== null) {
117
+ return {
118
+ name: "UnknownError",
119
+ message: String(err)
120
+ };
121
+ }
122
+ return void 0;
123
+ }
124
+ function extractContext(args) {
125
+ let context;
126
+ let error;
127
+ for (const arg of args) {
128
+ if (arg instanceof Error) {
129
+ error = serializeError(arg);
130
+ } else if (typeof arg === "object" && arg !== null && !Array.isArray(arg)) {
131
+ context = { ...context, ...arg };
132
+ }
133
+ }
134
+ return { context, error };
135
+ }
136
+ var ConsoleLogger = class _ConsoleLogger {
137
+ constructor(prefix, level = resolveLogLevel(), format = resolveLogFormat(), boundContext) {
93
138
  this.prefix = prefix;
94
139
  this.level = level;
140
+ this.format = format;
141
+ this.boundContext = {};
142
+ if (boundContext) {
143
+ this.boundContext = boundContext;
144
+ }
95
145
  }
96
146
  setLevel(level) {
97
147
  this.level = level;
@@ -99,36 +149,88 @@ var ConsoleLogger = class {
99
149
  getLevel() {
100
150
  return this.level;
101
151
  }
102
- debug(message, ...args) {
103
- if (this.level <= 0 /* DEBUG */) {
104
- console.debug(`[${this.prefix}] DEBUG: ${message}`, ...args);
152
+ setFormat(format) {
153
+ this.format = format;
154
+ }
155
+ getFormat() {
156
+ return this.format;
157
+ }
158
+ /**
159
+ * Create a child logger with additional bound context.
160
+ */
161
+ child(context) {
162
+ return new _ConsoleLogger(this.prefix, this.level, this.format, {
163
+ ...this.boundContext,
164
+ ...context
165
+ });
166
+ }
167
+ formatJson(level, message, args) {
168
+ const { context, error } = extractContext(args);
169
+ const entry = {
170
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
171
+ level,
172
+ service: this.prefix.toLowerCase(),
173
+ message
174
+ };
175
+ const mergedContext = { ...this.boundContext, ...context };
176
+ if (Object.keys(mergedContext).length > 0) {
177
+ if ("requestId" in mergedContext) {
178
+ entry.requestId = String(mergedContext.requestId);
179
+ delete mergedContext.requestId;
180
+ }
181
+ if ("traceId" in mergedContext) {
182
+ entry.traceId = String(mergedContext.traceId);
183
+ delete mergedContext.traceId;
184
+ }
185
+ if ("projectSlug" in mergedContext) {
186
+ entry.projectSlug = String(mergedContext.projectSlug);
187
+ delete mergedContext.projectSlug;
188
+ }
189
+ if ("durationMs" in mergedContext) {
190
+ entry.durationMs = Number(mergedContext.durationMs);
191
+ delete mergedContext.durationMs;
192
+ }
193
+ if (Object.keys(mergedContext).length > 0) {
194
+ entry.context = mergedContext;
195
+ }
196
+ }
197
+ if (error) {
198
+ entry.error = error;
105
199
  }
200
+ return JSON.stringify(entry);
106
201
  }
107
- info(message, ...args) {
108
- if (this.level <= 1 /* INFO */) {
109
- console.log(`[${this.prefix}] ${message}`, ...args);
202
+ log(level, logLevel, consoleFn, message, args) {
203
+ if (this.level > logLevel)
204
+ return;
205
+ if (this.format === "json") {
206
+ consoleFn(this.formatJson(level, message, args));
207
+ } else {
208
+ const prefix = level === "info" ? "" : ` ${level.toUpperCase()}:`;
209
+ consoleFn(`[${this.prefix}]${prefix} ${message}`, ...args);
110
210
  }
111
211
  }
212
+ debug(message, ...args) {
213
+ this.log("debug", 0 /* DEBUG */, console.debug, message, args);
214
+ }
215
+ info(message, ...args) {
216
+ this.log("info", 1 /* INFO */, console.log, message, args);
217
+ }
112
218
  warn(message, ...args) {
113
- if (this.level <= 2 /* WARN */) {
114
- console.warn(`[${this.prefix}] WARN: ${message}`, ...args);
115
- }
219
+ this.log("warn", 2 /* WARN */, console.warn, message, args);
116
220
  }
117
221
  error(message, ...args) {
118
- if (this.level <= 3 /* ERROR */) {
119
- console.error(`[${this.prefix}] ERROR: ${message}`, ...args);
120
- }
222
+ this.log("error", 3 /* ERROR */, console.error, message, args);
121
223
  }
122
224
  async time(label, fn) {
123
225
  const start = performance.now();
124
226
  try {
125
227
  const result = await fn();
126
- const end = performance.now();
127
- this.debug(`${label} completed in ${(end - start).toFixed(2)}ms`);
228
+ const durationMs = performance.now() - start;
229
+ this.debug(`${label} completed`, { durationMs: Math.round(durationMs) });
128
230
  return result;
129
231
  } catch (error) {
130
- const end = performance.now();
131
- this.error(`${label} failed after ${(end - start).toFixed(2)}ms`, error);
232
+ const durationMs = performance.now() - start;
233
+ this.error(`${label} failed`, { durationMs: Math.round(durationMs) }, error);
132
234
  throw error;
133
235
  }
134
236
  }
@@ -171,6 +273,7 @@ var serverLogger = createLogger("SERVER");
171
273
  var rendererLogger = createLogger("RENDERER");
172
274
  var bundlerLogger = createLogger("BUNDLER");
173
275
  var agentLogger = createLogger("AGENT");
276
+ var proxyLogger = createLogger("PROXY");
174
277
  var logger = createLogger("VERYFRONT");
175
278
 
176
279
  // src/ai/providers/base.ts
@@ -191,8 +294,30 @@ var OpenAIStreamChunkSchema = z.object({
191
294
  finish_reason: z.string().nullable()
192
295
  })).min(1)
193
296
  });
297
+ var OpenAICompletionResponseSchema = z.object({
298
+ id: z.string(),
299
+ choices: z.array(z.object({
300
+ message: z.object({
301
+ role: z.string(),
302
+ content: z.string().nullable().optional(),
303
+ tool_calls: z.array(z.object({
304
+ id: z.string(),
305
+ type: z.literal("function"),
306
+ function: z.object({
307
+ name: z.string(),
308
+ arguments: z.string()
309
+ })
310
+ })).optional()
311
+ }),
312
+ finish_reason: z.string().nullable()
313
+ })).min(1),
314
+ usage: z.object({
315
+ prompt_tokens: z.number(),
316
+ completion_tokens: z.number(),
317
+ total_tokens: z.number()
318
+ }).optional()
319
+ });
194
320
  var BaseProvider = class {
195
- config;
196
321
  constructor(config) {
197
322
  this.config = config;
198
323
  this.validateConfig();
@@ -231,7 +356,17 @@ var BaseProvider = class {
231
356
  }));
232
357
  }
233
358
  const data = await response.json();
234
- return this.transformResponse(data);
359
+ const parseResult = OpenAICompletionResponseSchema.safeParse(data);
360
+ if (!parseResult.success) {
361
+ agentLogger.warn(`${this.name}: Invalid response structure`, {
362
+ errors: parseResult.error.flatten()
363
+ });
364
+ throw toError(createError({
365
+ type: "agent",
366
+ message: `${this.name}: Invalid response structure from provider`
367
+ }));
368
+ }
369
+ return this.transformResponse(parseResult.data);
235
370
  }
236
371
  /**
237
372
  * Stream a completion
@@ -407,12 +542,9 @@ var OpenAIResponseSchema = z2.object({
407
542
  }).optional()
408
543
  });
409
544
  var OpenAIProvider = class extends BaseProvider {
410
- name = "openai";
411
- apiKey;
412
- baseURL;
413
- organizationId;
414
545
  constructor(config) {
415
546
  super(config);
547
+ this.name = "openai";
416
548
  this.apiKey = config.apiKey;
417
549
  this.baseURL = config.baseURL || "https://api.openai.com/v1";
418
550
  this.organizationId = config.organizationId;
@@ -515,11 +647,9 @@ var OpenAIProvider = class extends BaseProvider {
515
647
 
516
648
  // src/ai/providers/anthropic.ts
517
649
  var AnthropicProvider = class extends BaseProvider {
518
- name = "anthropic";
519
- apiKey;
520
- baseURL;
521
650
  constructor(config) {
522
651
  super(config);
652
+ this.name = "anthropic";
523
653
  this.apiKey = config.apiKey;
524
654
  this.baseURL = config.baseURL || "https://api.anthropic.com";
525
655
  }
@@ -817,11 +947,9 @@ var GoogleResponseSchema = z3.object({
817
947
  }).optional()
818
948
  });
819
949
  var GoogleProvider = class extends BaseProvider {
820
- name = "google";
821
- apiKey;
822
- baseURL;
823
950
  constructor(config) {
824
951
  super(config);
952
+ this.name = "google";
825
953
  this.apiKey = config.apiKey;
826
954
  this.baseURL = config.baseURL || "https://generativelanguage.googleapis.com/v1beta";
827
955
  }
@@ -943,9 +1071,11 @@ function getEnv(key) {
943
1071
 
944
1072
  // src/ai/providers/factory.ts
945
1073
  var ProviderRegistry = class {
946
- providers = /* @__PURE__ */ new Map();
947
- config = {};
948
- autoInitialized = false;
1074
+ constructor() {
1075
+ this.providers = /* @__PURE__ */ new Map();
1076
+ this.config = {};
1077
+ this.autoInitialized = false;
1078
+ }
949
1079
  /**
950
1080
  * Auto-initialize providers from environment variables
951
1081
  * This is called lazily when a provider is first requested
@@ -1369,7 +1499,9 @@ function generateToolId() {
1369
1499
  return `tool_${Date.now()}_${toolIdCounter++}`;
1370
1500
  }
1371
1501
  var ToolRegistryClass = class {
1372
- tools = /* @__PURE__ */ new Map();
1502
+ constructor() {
1503
+ this.tools = /* @__PURE__ */ new Map();
1504
+ }
1373
1505
  register(id, toolInstance) {
1374
1506
  if (this.tools.has(id)) {
1375
1507
  agentLogger.debug(`Tool "${id}" is already registered. Overwriting.`);
@@ -1629,7 +1761,9 @@ function patternToId(pattern) {
1629
1761
  return pattern.replace(/^\//, "").replace(/\//g, "_").replace(/:/g, "");
1630
1762
  }
1631
1763
  var ResourceRegistryClass = class {
1632
- resources = /* @__PURE__ */ new Map();
1764
+ constructor() {
1765
+ this.resources = /* @__PURE__ */ new Map();
1766
+ }
1633
1767
  /**
1634
1768
  * Register a resource
1635
1769
  */
@@ -1725,7 +1859,9 @@ function interpolateVariables(template, variables) {
1725
1859
  });
1726
1860
  }
1727
1861
  var PromptRegistryClass = class {
1728
- prompts = /* @__PURE__ */ new Map();
1862
+ constructor() {
1863
+ this.prompts = /* @__PURE__ */ new Map();
1864
+ }
1729
1865
  /**
1730
1866
  * Register a prompt
1731
1867
  */
@@ -1856,7 +1992,9 @@ function createWorkflow(config) {
1856
1992
  };
1857
1993
  }
1858
1994
  var AgentRegistryClass = class {
1859
- agents = /* @__PURE__ */ new Map();
1995
+ constructor() {
1996
+ this.agents = /* @__PURE__ */ new Map();
1997
+ }
1860
1998
  /**
1861
1999
  * Register an agent
1862
2000
  */
@@ -1931,6 +2069,9 @@ var veryfrontConfigSchema = z5.object({
1931
2069
  }).partial().optional(),
1932
2070
  router: z5.enum(["app", "pages"]).optional(),
1933
2071
  defaultLayout: z5.string().optional(),
2072
+ layout: z5.string().optional(),
2073
+ provider: z5.string().optional(),
2074
+ app: z5.string().optional(),
1934
2075
  theme: z5.object({ colors: z5.record(z5.string()).optional() }).partial().optional(),
1935
2076
  build: z5.object({
1936
2077
  outDir: z5.string().optional(),
@@ -2026,6 +2167,8 @@ var veryfrontConfigSchema = z5.object({
2026
2167
  apiBaseUrl: z5.string().url(),
2027
2168
  apiToken: z5.string(),
2028
2169
  projectSlug: z5.string(),
2170
+ proxyMode: z5.boolean().optional(),
2171
+ productionMode: z5.boolean().optional(),
2029
2172
  cache: z5.object({
2030
2173
  enabled: z5.boolean().optional(),
2031
2174
  ttl: z5.number().int().positive().optional(),
@@ -2087,6 +2230,9 @@ function findUnknownTopLevelKeys(input) {
2087
2230
  "experimental",
2088
2231
  "router",
2089
2232
  "defaultLayout",
2233
+ "layout",
2234
+ "provider",
2235
+ "app",
2090
2236
  "theme",
2091
2237
  "build",
2092
2238
  "cache",
@@ -2104,12 +2250,12 @@ function findUnknownTopLevelKeys(input) {
2104
2250
  }
2105
2251
 
2106
2252
  // src/core/config/loader.ts
2107
- import { join } from "node:path";
2253
+ import { dirname, join } from "node:path";
2108
2254
 
2109
2255
  // deno.json
2110
2256
  var deno_default = {
2111
2257
  name: "veryfront",
2112
- version: "0.0.73",
2258
+ version: "0.0.74",
2113
2259
  nodeModulesDir: "auto",
2114
2260
  exclude: [
2115
2261
  "npm/",
@@ -2194,12 +2340,12 @@ var deno_default = {
2194
2340
  csstype: "https://esm.sh/csstype@3.2.3",
2195
2341
  "@types/react": "https://esm.sh/@types/react@18.3.27?deps=csstype@3.2.3",
2196
2342
  "@types/react-dom": "https://esm.sh/@types/react-dom@18.3.7?deps=csstype@3.2.3",
2197
- react: "https://esm.sh/react@18.3.1",
2198
- "react-dom": "https://esm.sh/react-dom@18.3.1",
2199
- "react-dom/server": "https://esm.sh/react-dom@18.3.1/server",
2200
- "react-dom/client": "https://esm.sh/react-dom@18.3.1/client",
2201
- "react/jsx-runtime": "https://esm.sh/react@18.3.1/jsx-runtime",
2202
- "react/jsx-dev-runtime": "https://esm.sh/react@18.3.1/jsx-dev-runtime",
2343
+ react: "npm:react@18.3.1",
2344
+ "react-dom": "npm:react-dom@18.3.1",
2345
+ "react-dom/server": "npm:react-dom@18.3.1/server.node",
2346
+ "react-dom/client": "npm:react-dom@18.3.1/client",
2347
+ "react/jsx-runtime": "npm:react@18.3.1/jsx-runtime",
2348
+ "react/jsx-dev-runtime": "npm:react@18.3.1/jsx-dev-runtime",
2203
2349
  "@mdx-js/mdx": "npm:@mdx-js/mdx@3.0.0",
2204
2350
  "@mdx-js/react": "npm:@mdx-js/react@3.0.0",
2205
2351
  "unist-util-visit": "npm:unist-util-visit@5.0.0",
@@ -2209,27 +2355,36 @@ var deno_default = {
2209
2355
  "remark-frontmatter": "npm:remark-frontmatter@5.0.0",
2210
2356
  "rehype-highlight": "npm:rehype-highlight@7.0.2",
2211
2357
  "rehype-slug": "npm:rehype-slug@6.0.0",
2212
- esbuild: "https://deno.land/x/esbuild@v0.20.1/wasm.js",
2213
- "esbuild/mod.js": "https://deno.land/x/esbuild@v0.20.1/mod.js",
2358
+ esbuild: "npm:esbuild@0.20.2",
2359
+ "esbuild/mod.js": "npm:esbuild@0.20.2",
2214
2360
  "es-module-lexer": "npm:es-module-lexer@1.5.0",
2215
- zod: "npm:zod@3.23.8",
2361
+ zod: "npm:zod@3.25.76",
2216
2362
  "mime-types": "npm:mime-types@2.1.35",
2217
2363
  mdast: "npm:@types/mdast@4.0.3",
2218
2364
  hast: "npm:@types/hast@3.0.3",
2219
2365
  unist: "npm:@types/unist@3.0.2",
2220
2366
  unified: "npm:unified@11.0.5",
2221
- ai: "https://esm.sh/ai@5.0.76?deps=react@18.3.1,react-dom@18.3.1",
2222
- "ai/react": "https://esm.sh/@ai-sdk/react@2.0.1?deps=react@18.3.1,react-dom@18.3.1",
2223
- "@ai-sdk/react": "https://esm.sh/@ai-sdk/react@2.0.1?deps=react@18.3.1,react-dom@18.3.1",
2367
+ ai: "npm:ai@5.0.76",
2368
+ "ai/react": "npm:@ai-sdk/react@2.0.1",
2369
+ "@ai-sdk/react": "npm:@ai-sdk/react@2.0.1",
2224
2370
  "@ai-sdk/openai": "https://esm.sh/@ai-sdk/openai@2.0.1",
2225
2371
  "@ai-sdk/anthropic": "https://esm.sh/@ai-sdk/anthropic@2.0.1",
2226
2372
  unocss: "https://esm.sh/unocss@0.59.0",
2227
2373
  "@unocss/core": "https://esm.sh/@unocss/core@0.59.0",
2228
2374
  "@unocss/preset-wind": "https://esm.sh/@unocss/preset-wind@0.59.0",
2375
+ "next-themes": "npm:next-themes@0.3.0",
2229
2376
  redis: "npm:redis",
2230
2377
  pg: "npm:pg",
2231
2378
  "@opentelemetry/api": "npm:@opentelemetry/api@1",
2232
- "@opentelemetry/core": "npm:@opentelemetry/core@1"
2379
+ "@opentelemetry/core": "npm:@opentelemetry/core@1",
2380
+ "@opentelemetry/sdk-trace-base": "npm:@opentelemetry/sdk-trace-base@1",
2381
+ "@opentelemetry/exporter-trace-otlp-http": "npm:@opentelemetry/exporter-trace-otlp-http@0.57",
2382
+ "@opentelemetry/resources": "npm:@opentelemetry/resources@1",
2383
+ "@opentelemetry/semantic-conventions": "npm:@opentelemetry/semantic-conventions@1",
2384
+ "@babel/parser": "npm:@babel/parser@7.26.3",
2385
+ "@babel/traverse": "npm:@babel/traverse@7.26.3",
2386
+ "@babel/generator": "npm:@babel/generator@7.26.3",
2387
+ "@babel/types": "npm:@babel/types@7.26.3"
2233
2388
  },
2234
2389
  compilerOptions: {
2235
2390
  jsx: "react-jsx",
@@ -2237,7 +2392,7 @@ var deno_default = {
2237
2392
  strict: true,
2238
2393
  noImplicitAny: true,
2239
2394
  noUncheckedIndexedAccess: true,
2240
- types: [],
2395
+ types: ["npm:@types/react@18"],
2241
2396
  lib: [
2242
2397
  "deno.window",
2243
2398
  "dom",
@@ -2252,9 +2407,9 @@ var deno_default = {
2252
2407
  build: "deno compile --allow-all --output ../../bin/veryfront src/cli/main.ts",
2253
2408
  "build:npm": "deno run -A scripts/build-npm.ts",
2254
2409
  release: "deno run -A scripts/release.ts",
2255
- test: "DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --unstable-worker-options --unstable-net",
2256
- "test:unit": "DENO_JOBS=1 deno test --parallel --allow-all --v8-flags=--max-old-space-size=8192 --ignore=tests --unstable-worker-options --unstable-net",
2257
- "test:integration": "DENO_JOBS=1 deno test --parallel --fail-fast --allow-all tests --unstable-worker-options --unstable-net",
2410
+ test: "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --unstable-worker-options --unstable-net",
2411
+ "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",
2412
+ "test:integration": "VF_DISABLE_LRU_INTERVAL=1 DENO_JOBS=1 deno test --parallel --fail-fast --allow-all tests --unstable-worker-options --unstable-net",
2258
2413
  "test:coverage": "rm -rf coverage && DENO_JOBS=1 deno test --parallel --fail-fast --allow-all --coverage=coverage --unstable-worker-options --unstable-net || exit 1",
2259
2414
  "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",
2260
2415
  "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",
@@ -2316,6 +2471,7 @@ var deno_default = {
2316
2471
 
2317
2472
  // src/core/utils/version.ts
2318
2473
  var VERSION = getEnv("VERYFRONT_VERSION") || (typeof deno_default.version === "string" ? deno_default.version : "0.0.0");
2474
+ var SERVER_START_TIME = Date.now();
2319
2475
 
2320
2476
  // src/core/utils/constants/cdn.ts
2321
2477
  var ESM_CDN_BASE = "https://esm.sh";
@@ -2431,6 +2587,166 @@ var DEV_SERVER_ENDPOINTS = {
2431
2587
  ERROR_OVERLAY: INTERNAL_ENDPOINTS.ERROR_OVERLAY
2432
2588
  };
2433
2589
 
2590
+ // src/platform/compat/fs.ts
2591
+ var NodeFileSystem = class {
2592
+ constructor() {
2593
+ this.fs = null;
2594
+ this.os = null;
2595
+ this.path = null;
2596
+ this.initialized = false;
2597
+ }
2598
+ async ensureInitialized() {
2599
+ if (this.initialized)
2600
+ return;
2601
+ if (!isNode) {
2602
+ throw toError(createError({
2603
+ type: "not_supported",
2604
+ message: "Node.js fs modules not available",
2605
+ feature: "Node.js"
2606
+ }));
2607
+ }
2608
+ const [fsModule, osModule, pathModule] = await Promise.all([
2609
+ import("node:fs/promises"),
2610
+ import("node:os"),
2611
+ import("node:path")
2612
+ ]);
2613
+ this.fs = fsModule;
2614
+ this.os = osModule;
2615
+ this.path = pathModule;
2616
+ this.initialized = true;
2617
+ }
2618
+ async readTextFile(path) {
2619
+ await this.ensureInitialized();
2620
+ return await this.fs.readFile(path, { encoding: "utf8" });
2621
+ }
2622
+ async readFile(path) {
2623
+ await this.ensureInitialized();
2624
+ return await this.fs.readFile(path);
2625
+ }
2626
+ async writeTextFile(path, data) {
2627
+ await this.ensureInitialized();
2628
+ await this.fs.writeFile(path, data, { encoding: "utf8" });
2629
+ }
2630
+ async writeFile(path, data) {
2631
+ await this.ensureInitialized();
2632
+ await this.fs.writeFile(path, data);
2633
+ }
2634
+ async exists(path) {
2635
+ await this.ensureInitialized();
2636
+ try {
2637
+ await this.fs.access(path);
2638
+ return true;
2639
+ } catch (error) {
2640
+ if (error.code === "ENOENT") {
2641
+ return false;
2642
+ }
2643
+ throw error;
2644
+ }
2645
+ }
2646
+ async stat(path) {
2647
+ await this.ensureInitialized();
2648
+ const stat = await this.fs.stat(path);
2649
+ return {
2650
+ isFile: stat.isFile(),
2651
+ isDirectory: stat.isDirectory(),
2652
+ isSymlink: stat.isSymbolicLink(),
2653
+ size: stat.size,
2654
+ mtime: stat.mtime
2655
+ };
2656
+ }
2657
+ async mkdir(path, options) {
2658
+ await this.ensureInitialized();
2659
+ await this.fs.mkdir(path, { recursive: options?.recursive ?? false });
2660
+ }
2661
+ async *readDir(path) {
2662
+ await this.ensureInitialized();
2663
+ const entries = await this.fs.readdir(path, { withFileTypes: true });
2664
+ for (const entry of entries) {
2665
+ yield {
2666
+ name: entry.name,
2667
+ isFile: entry.isFile(),
2668
+ isDirectory: entry.isDirectory()
2669
+ };
2670
+ }
2671
+ }
2672
+ async remove(path, options) {
2673
+ await this.ensureInitialized();
2674
+ await this.fs.rm(path, {
2675
+ recursive: options?.recursive ?? false,
2676
+ force: options?.recursive ?? false
2677
+ });
2678
+ }
2679
+ async makeTempDir(options) {
2680
+ await this.ensureInitialized();
2681
+ const tempDir = this.path.join(
2682
+ this.os.tmpdir(),
2683
+ `${options?.prefix ?? "tmp-"}${Math.random().toString(36).substring(2, 8)}`
2684
+ );
2685
+ await this.fs.mkdir(tempDir, { recursive: true });
2686
+ return tempDir;
2687
+ }
2688
+ };
2689
+ var DenoFileSystem = class {
2690
+ async readTextFile(path) {
2691
+ return await Deno.readTextFile(path);
2692
+ }
2693
+ async readFile(path) {
2694
+ return await Deno.readFile(path);
2695
+ }
2696
+ async writeTextFile(path, data) {
2697
+ await Deno.writeTextFile(path, data);
2698
+ }
2699
+ async writeFile(path, data) {
2700
+ await Deno.writeFile(path, data);
2701
+ }
2702
+ async exists(path) {
2703
+ try {
2704
+ await Deno.stat(path);
2705
+ return true;
2706
+ } catch (error) {
2707
+ if (error instanceof Deno.errors.NotFound) {
2708
+ return false;
2709
+ }
2710
+ throw error;
2711
+ }
2712
+ }
2713
+ async stat(path) {
2714
+ const stat = await Deno.stat(path);
2715
+ return {
2716
+ isFile: stat.isFile,
2717
+ isDirectory: stat.isDirectory,
2718
+ isSymlink: stat.isSymlink,
2719
+ size: stat.size,
2720
+ mtime: stat.mtime
2721
+ };
2722
+ }
2723
+ async mkdir(path, options) {
2724
+ await Deno.mkdir(path, { recursive: options?.recursive ?? false });
2725
+ }
2726
+ async *readDir(path) {
2727
+ for await (const entry of Deno.readDir(path)) {
2728
+ yield {
2729
+ name: entry.name,
2730
+ isFile: entry.isFile,
2731
+ isDirectory: entry.isDirectory
2732
+ };
2733
+ }
2734
+ }
2735
+ async remove(path, options) {
2736
+ await Deno.remove(path, { recursive: options?.recursive ?? false });
2737
+ }
2738
+ async makeTempDir(options) {
2739
+ return await Deno.makeTempDir({ prefix: options?.prefix });
2740
+ }
2741
+ };
2742
+ function createFileSystem() {
2743
+ if (isDeno) {
2744
+ return new DenoFileSystem();
2745
+ } else {
2746
+ return new NodeFileSystem();
2747
+ }
2748
+ }
2749
+
2434
2750
  // src/core/config/loader.ts
2435
2751
  function getDefaultImportMapForConfig() {
2436
2752
  return { imports: getReactImportMap(REACT_DEFAULT_VERSION) };
@@ -2567,7 +2883,62 @@ var ConfigValidationError = class extends Error {
2567
2883
  this.name = "ConfigValidationError";
2568
2884
  }
2569
2885
  };
2570
- async function loadAndMergeConfig(configPath, projectDir) {
2886
+ function isVirtualFilesystem(adapter) {
2887
+ const wrappedAdapter = adapter?.fs?.fsAdapter;
2888
+ const adapterName = wrappedAdapter?.constructor?.name;
2889
+ return adapterName === "VeryfrontFSAdapter";
2890
+ }
2891
+ async function loadConfigFromVirtualFS(configPath, projectDir, adapter) {
2892
+ const fs2 = createFileSystem();
2893
+ const content = await adapter.fs.readFile(configPath);
2894
+ const source = typeof content === "string" ? content : new TextDecoder().decode(content);
2895
+ serverLogger.debug(`[CONFIG] Loading config from virtual FS: ${configPath}`);
2896
+ const isTsx = configPath.endsWith(".tsx");
2897
+ const loader = isTsx ? "tsx" : configPath.endsWith(".ts") ? "ts" : "js";
2898
+ const { build } = await import("esbuild");
2899
+ const result = await build({
2900
+ bundle: false,
2901
+ // Config files shouldn't need bundling
2902
+ write: false,
2903
+ format: "esm",
2904
+ platform: "neutral",
2905
+ target: "es2022",
2906
+ stdin: {
2907
+ contents: source,
2908
+ loader,
2909
+ resolveDir: dirname(configPath),
2910
+ sourcefile: configPath
2911
+ }
2912
+ });
2913
+ if (result.errors && result.errors.length > 0) {
2914
+ const first = result.errors[0]?.text || "unknown error";
2915
+ throw new ConfigValidationError(`Failed to transpile config: ${first}`);
2916
+ }
2917
+ const js = result.outputFiles?.[0]?.text ?? "export default {}";
2918
+ const tempDir = await fs2.makeTempDir({ prefix: "vf-config-" });
2919
+ const tempFile = join(tempDir, "config.mjs");
2920
+ try {
2921
+ await fs2.writeTextFile(tempFile, js);
2922
+ const configModule = await import(`file://${tempFile}?v=${Date.now()}`);
2923
+ const userConfig = configModule.default || configModule;
2924
+ if (userConfig === null || typeof userConfig !== "object" || Array.isArray(userConfig)) {
2925
+ throw new ConfigValidationError(
2926
+ `Expected object, received ${userConfig === null ? "null" : typeof userConfig}`
2927
+ );
2928
+ }
2929
+ validateCorsConfig(userConfig);
2930
+ validateConfigShape(userConfig);
2931
+ const merged = mergeConfigs(userConfig);
2932
+ configCacheByProject.set(projectDir, { revision: cacheRevision, config: merged });
2933
+ return merged;
2934
+ } finally {
2935
+ await fs2.remove(tempDir, { recursive: true });
2936
+ }
2937
+ }
2938
+ async function loadAndMergeConfig(configPath, projectDir, adapter) {
2939
+ if (isVirtualFilesystem(adapter)) {
2940
+ return loadConfigFromVirtualFS(configPath, projectDir, adapter);
2941
+ }
2571
2942
  try {
2572
2943
  const configUrl = `file://${configPath}?t=${Date.now()}-${crypto.randomUUID()}`;
2573
2944
  const configModule = await import(configUrl);
@@ -2603,7 +2974,7 @@ async function getConfig(projectDir, adapter) {
2603
2974
  if (!exists)
2604
2975
  continue;
2605
2976
  try {
2606
- const merged = await loadAndMergeConfig(configPath, projectDir);
2977
+ const merged = await loadAndMergeConfig(configPath, projectDir, adapter);
2607
2978
  if (merged)
2608
2979
  return merged;
2609
2980
  } catch (error) {
@@ -2672,266 +3043,108 @@ function createMockAdapter() {
2672
3043
  throw toError(createError({
2673
3044
  type: "file",
2674
3045
  message: `File not found: ${path}`
2675
- }));
2676
- }
2677
- return Promise.resolve(new TextEncoder().encode(content));
2678
- },
2679
- writeFile: (path, content) => {
2680
- files.set(path, content);
2681
- return Promise.resolve();
2682
- },
2683
- exists: (path) => {
2684
- if (files.has(path))
2685
- return Promise.resolve(true);
2686
- if (directories.has(path))
2687
- return Promise.resolve(true);
2688
- for (const filePath of files.keys()) {
2689
- if (filePath.startsWith(path + "/"))
2690
- return Promise.resolve(true);
2691
- }
2692
- return Promise.resolve(false);
2693
- },
2694
- readDir: async function* (path) {
2695
- const entries = /* @__PURE__ */ new Map();
2696
- for (const filePath of files.keys()) {
2697
- if (filePath.startsWith(path + "/")) {
2698
- const relativePath = filePath.slice(path.length + 1);
2699
- const parts = relativePath.split("/");
2700
- const name = parts[0];
2701
- if (!entries.has(name)) {
2702
- entries.set(name, {
2703
- isFile: parts.length === 1,
2704
- isDirectory: parts.length > 1
2705
- });
2706
- }
2707
- }
2708
- }
2709
- for (const [name, meta] of entries.entries()) {
2710
- yield { name, ...meta, isSymlink: false };
2711
- }
2712
- },
2713
- stat: (path) => {
2714
- if (files.has(path)) {
2715
- const content = files.get(path);
2716
- return Promise.resolve({
2717
- size: content.length,
2718
- isFile: true,
2719
- isDirectory: false,
2720
- isSymlink: false,
2721
- mtime: /* @__PURE__ */ new Date()
2722
- });
2723
- }
2724
- if (directories.has(path)) {
2725
- return Promise.resolve({
2726
- size: 0,
2727
- isFile: false,
2728
- isDirectory: true,
2729
- isSymlink: false,
2730
- mtime: /* @__PURE__ */ new Date()
2731
- });
2732
- }
2733
- for (const filePath of files.keys()) {
2734
- if (filePath.startsWith(path + "/")) {
2735
- return Promise.resolve({
2736
- size: 0,
2737
- isFile: false,
2738
- isDirectory: true,
2739
- isSymlink: false,
2740
- mtime: /* @__PURE__ */ new Date()
2741
- });
2742
- }
2743
- }
2744
- return Promise.reject(new Error(`Path not found: ${path}`));
2745
- },
2746
- mkdir: (_path) => Promise.resolve(),
2747
- remove: (_path) => Promise.resolve(),
2748
- makeTempDir: (prefix) => Promise.resolve(`/tmp/${prefix}-${Math.random().toString(36).slice(2)}`),
2749
- watch: () => ({
2750
- async *[Symbol.asyncIterator]() {
2751
- yield { kind: "any", paths: [] };
2752
- },
2753
- close: () => {
2754
- }
2755
- })
2756
- },
2757
- env: {
2758
- get: (key) => envVars.get(key),
2759
- set: (key, value) => envVars.set(key, value),
2760
- toObject: () => Object.fromEntries(envVars)
2761
- },
2762
- server: {
2763
- upgradeWebSocket: (_request) => {
2764
- throw new Error(
2765
- "WebSocket upgrade not available in mock adapter. The mock adapter is designed for unit testing filesystem and environment operations. For WebSocket testing, use integration tests with the actual Deno/Node/Bun adapter, or mock the WebSocket behavior at a higher level in your tests."
2766
- );
2767
- }
2768
- },
2769
- features: {
2770
- websocket: false,
2771
- http2: false,
2772
- workers: false,
2773
- jsx: false,
2774
- typescript: false
2775
- }
2776
- };
2777
- }
2778
-
2779
- // src/platform/compat/fs.ts
2780
- var NodeFileSystem = class {
2781
- fs = null;
2782
- os = null;
2783
- path = null;
2784
- initialized = false;
2785
- async ensureInitialized() {
2786
- if (this.initialized)
2787
- return;
2788
- if (!isNode) {
2789
- throw toError(createError({
2790
- type: "not_supported",
2791
- message: "Node.js fs modules not available",
2792
- feature: "Node.js"
2793
- }));
2794
- }
2795
- const [fsModule, osModule, pathModule] = await Promise.all([
2796
- import("node:fs/promises"),
2797
- import("node:os"),
2798
- import("node:path")
2799
- ]);
2800
- this.fs = fsModule;
2801
- this.os = osModule;
2802
- this.path = pathModule;
2803
- this.initialized = true;
2804
- }
2805
- async readTextFile(path) {
2806
- await this.ensureInitialized();
2807
- return await this.fs.readFile(path, { encoding: "utf8" });
2808
- }
2809
- async readFile(path) {
2810
- await this.ensureInitialized();
2811
- return await this.fs.readFile(path);
2812
- }
2813
- async writeTextFile(path, data) {
2814
- await this.ensureInitialized();
2815
- await this.fs.writeFile(path, data, { encoding: "utf8" });
2816
- }
2817
- async writeFile(path, data) {
2818
- await this.ensureInitialized();
2819
- await this.fs.writeFile(path, data);
2820
- }
2821
- async exists(path) {
2822
- await this.ensureInitialized();
2823
- try {
2824
- await this.fs.access(path);
2825
- return true;
2826
- } catch (error) {
2827
- if (error.code === "ENOENT") {
2828
- return false;
2829
- }
2830
- throw error;
2831
- }
2832
- }
2833
- async stat(path) {
2834
- await this.ensureInitialized();
2835
- const stat = await this.fs.stat(path);
2836
- return {
2837
- isFile: stat.isFile(),
2838
- isDirectory: stat.isDirectory(),
2839
- isSymlink: stat.isSymbolicLink(),
2840
- size: stat.size,
2841
- mtime: stat.mtime
2842
- };
2843
- }
2844
- async mkdir(path, options) {
2845
- await this.ensureInitialized();
2846
- await this.fs.mkdir(path, { recursive: options?.recursive ?? false });
2847
- }
2848
- async *readDir(path) {
2849
- await this.ensureInitialized();
2850
- const entries = await this.fs.readdir(path, { withFileTypes: true });
2851
- for (const entry of entries) {
2852
- yield {
2853
- name: entry.name,
2854
- isFile: entry.isFile(),
2855
- isDirectory: entry.isDirectory()
2856
- };
2857
- }
2858
- }
2859
- async remove(path, options) {
2860
- await this.ensureInitialized();
2861
- await this.fs.rm(path, {
2862
- recursive: options?.recursive ?? false,
2863
- force: options?.recursive ?? false
2864
- });
2865
- }
2866
- async makeTempDir(options) {
2867
- await this.ensureInitialized();
2868
- const tempDir = this.path.join(
2869
- this.os.tmpdir(),
2870
- `${options?.prefix ?? "tmp-"}${Math.random().toString(36).substring(2, 8)}`
2871
- );
2872
- await this.fs.mkdir(tempDir, { recursive: true });
2873
- return tempDir;
2874
- }
2875
- };
2876
- var DenoFileSystem = class {
2877
- async readTextFile(path) {
2878
- return await Deno.readTextFile(path);
2879
- }
2880
- async readFile(path) {
2881
- return await Deno.readFile(path);
2882
- }
2883
- async writeTextFile(path, data) {
2884
- await Deno.writeTextFile(path, data);
2885
- }
2886
- async writeFile(path, data) {
2887
- await Deno.writeFile(path, data);
2888
- }
2889
- async exists(path) {
2890
- try {
2891
- await Deno.stat(path);
2892
- return true;
2893
- } catch (error) {
2894
- if (error instanceof Deno.errors.NotFound) {
2895
- return false;
3046
+ }));
3047
+ }
3048
+ return Promise.resolve(new TextEncoder().encode(content));
3049
+ },
3050
+ writeFile: (path, content) => {
3051
+ files.set(path, content);
3052
+ return Promise.resolve();
3053
+ },
3054
+ exists: (path) => {
3055
+ if (files.has(path))
3056
+ return Promise.resolve(true);
3057
+ if (directories.has(path))
3058
+ return Promise.resolve(true);
3059
+ for (const filePath of files.keys()) {
3060
+ if (filePath.startsWith(path + "/"))
3061
+ return Promise.resolve(true);
3062
+ }
3063
+ return Promise.resolve(false);
3064
+ },
3065
+ readDir: async function* (path) {
3066
+ const entries = /* @__PURE__ */ new Map();
3067
+ for (const filePath of files.keys()) {
3068
+ if (filePath.startsWith(path + "/")) {
3069
+ const relativePath = filePath.slice(path.length + 1);
3070
+ const parts = relativePath.split("/");
3071
+ const name = parts[0];
3072
+ if (!entries.has(name)) {
3073
+ entries.set(name, {
3074
+ isFile: parts.length === 1,
3075
+ isDirectory: parts.length > 1
3076
+ });
3077
+ }
3078
+ }
3079
+ }
3080
+ for (const [name, meta] of entries.entries()) {
3081
+ yield { name, ...meta, isSymlink: false };
3082
+ }
3083
+ },
3084
+ stat: (path) => {
3085
+ if (files.has(path)) {
3086
+ const content = files.get(path);
3087
+ return Promise.resolve({
3088
+ size: content.length,
3089
+ isFile: true,
3090
+ isDirectory: false,
3091
+ isSymlink: false,
3092
+ mtime: /* @__PURE__ */ new Date()
3093
+ });
3094
+ }
3095
+ if (directories.has(path)) {
3096
+ return Promise.resolve({
3097
+ size: 0,
3098
+ isFile: false,
3099
+ isDirectory: true,
3100
+ isSymlink: false,
3101
+ mtime: /* @__PURE__ */ new Date()
3102
+ });
3103
+ }
3104
+ for (const filePath of files.keys()) {
3105
+ if (filePath.startsWith(path + "/")) {
3106
+ return Promise.resolve({
3107
+ size: 0,
3108
+ isFile: false,
3109
+ isDirectory: true,
3110
+ isSymlink: false,
3111
+ mtime: /* @__PURE__ */ new Date()
3112
+ });
3113
+ }
3114
+ }
3115
+ return Promise.reject(new Error(`Path not found: ${path}`));
3116
+ },
3117
+ mkdir: (_path) => Promise.resolve(),
3118
+ remove: (_path) => Promise.resolve(),
3119
+ makeTempDir: (prefix) => Promise.resolve(`/tmp/${prefix}-${Math.random().toString(36).slice(2)}`),
3120
+ watch: () => ({
3121
+ async *[Symbol.asyncIterator]() {
3122
+ yield { kind: "any", paths: [] };
3123
+ },
3124
+ close: () => {
3125
+ }
3126
+ })
3127
+ },
3128
+ env: {
3129
+ get: (key) => envVars.get(key),
3130
+ set: (key, value) => envVars.set(key, value),
3131
+ toObject: () => Object.fromEntries(envVars)
3132
+ },
3133
+ server: {
3134
+ upgradeWebSocket: (_request) => {
3135
+ throw new Error(
3136
+ "WebSocket upgrade not available in mock adapter. The mock adapter is designed for unit testing filesystem and environment operations. For WebSocket testing, use integration tests with the actual Deno/Node/Bun adapter, or mock the WebSocket behavior at a higher level in your tests."
3137
+ );
2896
3138
  }
2897
- throw error;
2898
- }
2899
- }
2900
- async stat(path) {
2901
- const stat = await Deno.stat(path);
2902
- return {
2903
- isFile: stat.isFile,
2904
- isDirectory: stat.isDirectory,
2905
- isSymlink: stat.isSymlink,
2906
- size: stat.size,
2907
- mtime: stat.mtime
2908
- };
2909
- }
2910
- async mkdir(path, options) {
2911
- await Deno.mkdir(path, { recursive: options?.recursive ?? false });
2912
- }
2913
- async *readDir(path) {
2914
- for await (const entry of Deno.readDir(path)) {
2915
- yield {
2916
- name: entry.name,
2917
- isFile: entry.isFile,
2918
- isDirectory: entry.isDirectory
2919
- };
3139
+ },
3140
+ features: {
3141
+ websocket: false,
3142
+ http2: false,
3143
+ workers: false,
3144
+ jsx: false,
3145
+ typescript: false
2920
3146
  }
2921
- }
2922
- async remove(path, options) {
2923
- await Deno.remove(path, { recursive: options?.recursive ?? false });
2924
- }
2925
- async makeTempDir(options) {
2926
- return await Deno.makeTempDir({ prefix: options?.prefix });
2927
- }
2928
- };
2929
- function createFileSystem() {
2930
- if (isDeno) {
2931
- return new DenoFileSystem();
2932
- } else {
2933
- return new NodeFileSystem();
2934
- }
3147
+ };
2935
3148
  }
2936
3149
 
2937
3150
  // src/platform/compat/path-helper.ts
@@ -2949,7 +3162,7 @@ function getPathMod() {
2949
3162
  return pathMod;
2950
3163
  return nodePath;
2951
3164
  }
2952
- var dirname = (path) => getPathMod().dirname(path);
3165
+ var dirname2 = (path) => getPathMod().dirname(path);
2953
3166
  var join2 = (...paths) => getPathMod().join(...paths);
2954
3167
  var resolve = (...paths) => getPathMod().resolve(...paths);
2955
3168
  var extname = (path) => getPathMod().extname(path);
@@ -2996,7 +3209,7 @@ function createFsAdapterPlugin(fsAdapter) {
2996
3209
  build.onResolve(
2997
3210
  { filter: /^\.\.?\// },
2998
3211
  async (args) => {
2999
- const importerDir = args.importer ? dirname(args.importer) : args.resolveDir;
3212
+ const importerDir = args.importer ? dirname2(args.importer) : args.resolveDir;
3000
3213
  const basePath = resolve(importerDir, args.path);
3001
3214
  const resolvedPath = await resolveWithExtensions(basePath);
3002
3215
  if (resolvedPath) {
@@ -3023,7 +3236,7 @@ function createFsAdapterPlugin(fsAdapter) {
3023
3236
  contents: content,
3024
3237
  loader,
3025
3238
  // Set resolveDir for nested imports from this file
3026
- resolveDir: dirname(args.path)
3239
+ resolveDir: dirname2(args.path)
3027
3240
  };
3028
3241
  } catch (error) {
3029
3242
  return {
@@ -3058,7 +3271,7 @@ async function importModule(file, context) {
3058
3271
  const isJsx = filePath.endsWith(".jsx");
3059
3272
  const loader = isTsx ? "tsx" : isJsx ? "jsx" : filePath.endsWith(".ts") ? "ts" : "js";
3060
3273
  const { build } = await import("esbuild");
3061
- const fileDir = dirname(filePath);
3274
+ const fileDir = dirname2(filePath);
3062
3275
  const relativeImports = [];
3063
3276
  if (isDeno) {
3064
3277
  const relativeImportPattern = /from\s+["'](\.\.[^"']+)["']/g;
@@ -3522,9 +3735,8 @@ function generateId(prefix) {
3522
3735
 
3523
3736
  // src/ai/agent/memory.ts
3524
3737
  var ConversationMemory = class {
3525
- messages = [];
3526
- config;
3527
3738
  constructor(config) {
3739
+ this.messages = [];
3528
3740
  this.config = config;
3529
3741
  }
3530
3742
  async add(message) {
@@ -3569,10 +3781,8 @@ var ConversationMemory = class {
3569
3781
  }
3570
3782
  };
3571
3783
  var BufferMemory = class {
3572
- messages = [];
3573
- config;
3574
- bufferSize;
3575
3784
  constructor(config) {
3785
+ this.messages = [];
3576
3786
  this.config = config;
3577
3787
  this.bufferSize = config.maxMessages || 10;
3578
3788
  }
@@ -3606,11 +3816,9 @@ var BufferMemory = class {
3606
3816
  }
3607
3817
  };
3608
3818
  var SummaryMemory = class {
3609
- messages = [];
3610
- summary = "";
3611
- config;
3612
- summaryThreshold;
3613
3819
  constructor(config) {
3820
+ this.messages = [];
3821
+ this.summary = "";
3614
3822
  this.config = config;
3615
3823
  this.summaryThreshold = config.maxMessages || 20;
3616
3824
  }
@@ -3719,9 +3927,11 @@ var VERYFRONT_PATHS = {
3719
3927
 
3720
3928
  // src/core/utils/bundle-manifest.ts
3721
3929
  var InMemoryBundleManifestStore = class {
3722
- metadata = /* @__PURE__ */ new Map();
3723
- code = /* @__PURE__ */ new Map();
3724
- sourceIndex = /* @__PURE__ */ new Map();
3930
+ constructor() {
3931
+ this.metadata = /* @__PURE__ */ new Map();
3932
+ this.code = /* @__PURE__ */ new Map();
3933
+ this.sourceIndex = /* @__PURE__ */ new Map();
3934
+ }
3725
3935
  getBundleMetadata(key) {
3726
3936
  const entry = this.metadata.get(key);
3727
3937
  if (!entry)
@@ -3812,6 +4022,9 @@ var InMemoryBundleManifestStore = class {
3812
4022
  };
3813
4023
  var manifestStore = new InMemoryBundleManifestStore();
3814
4024
 
4025
+ // src/core/utils/perf-timer.ts
4026
+ var enabled = typeof process !== "undefined" ? process.env?.VERYFRONT_PERF === "1" : typeof Deno !== "undefined" ? Deno.env.get("VERYFRONT_PERF") === "1" : false;
4027
+
3815
4028
  // src/observability/tracing/config.ts
3816
4029
  var DEFAULT_CONFIG2 = {
3817
4030
  enabled: false,
@@ -4013,14 +4226,16 @@ var ContextPropagation = class {
4013
4226
 
4014
4227
  // src/observability/tracing/manager.ts
4015
4228
  var TracingManager = class {
4016
- state = {
4017
- initialized: false,
4018
- tracer: null,
4019
- api: null,
4020
- propagator: null
4021
- };
4022
- spanOps = null;
4023
- contextProp = null;
4229
+ constructor() {
4230
+ this.state = {
4231
+ initialized: false,
4232
+ tracer: null,
4233
+ api: null,
4234
+ propagator: null
4235
+ };
4236
+ this.spanOps = null;
4237
+ this.contextProp = null;
4238
+ }
4024
4239
  async initialize(config = {}, adapter) {
4025
4240
  if (this.state.initialized) {
4026
4241
  serverLogger.debug("[tracing] Already initialized");
@@ -4106,7 +4321,27 @@ async function withSpan(name, fn, options = {}) {
4106
4321
  );
4107
4322
  }
4108
4323
 
4109
- // src/ai/agent/runtime.ts
4324
+ // src/ai/config/defaults.ts
4325
+ var AGENT_DEFAULTS = {
4326
+ /** Maximum tokens for completion */
4327
+ maxTokens: 4096,
4328
+ /** Default temperature for generation */
4329
+ temperature: 0.7,
4330
+ /** Maximum agent loop steps */
4331
+ maxSteps: 20,
4332
+ /** Default memory type */
4333
+ memoryType: "conversation",
4334
+ /** Default memory max tokens */
4335
+ memoryMaxTokens: 4e3
4336
+ };
4337
+ var STREAMING_DEFAULTS = {
4338
+ /** Maximum buffer size for streaming (1MB) */
4339
+ maxBufferSize: 1024 * 1024,
4340
+ /** Chunk size for stream processing */
4341
+ chunkSize: 16384
4342
+ };
4343
+
4344
+ // src/ai/agent/streaming/stream-events.ts
4110
4345
  import { z as z6 } from "zod";
4111
4346
  var AgentStreamEventSchema = z6.discriminatedUnion("type", [
4112
4347
  z6.object({
@@ -4146,8 +4381,8 @@ var AgentStreamEventSchema = z6.discriminatedUnion("type", [
4146
4381
  })
4147
4382
  })
4148
4383
  ]);
4149
- var DEFAULT_MAX_TOKENS = 4096;
4150
- var DEFAULT_TEMPERATURE = 0.7;
4384
+
4385
+ // src/ai/agent/message-converter.ts
4151
4386
  function convertMessageToProvider(msg) {
4152
4387
  const content = getTextFromParts(msg.parts);
4153
4388
  const providerMsg = {
@@ -4177,12 +4412,14 @@ function convertMessageToProvider(msg) {
4177
4412
  }
4178
4413
  return providerMsg;
4179
4414
  }
4415
+
4416
+ // src/ai/agent/runtime.ts
4417
+ var DEFAULT_MAX_TOKENS = AGENT_DEFAULTS.maxTokens;
4418
+ var DEFAULT_TEMPERATURE = AGENT_DEFAULTS.temperature;
4419
+ var MAX_STREAM_BUFFER_SIZE = STREAMING_DEFAULTS.maxBufferSize;
4180
4420
  var AgentRuntime = class {
4181
- id;
4182
- config;
4183
- memory;
4184
- status = "idle";
4185
4421
  constructor(id, config) {
4422
+ this.status = "idle";
4186
4423
  this.id = id;
4187
4424
  this.config = config;
4188
4425
  const memoryConfig = config.memory || { type: "conversation", maxTokens: 4e3 };
@@ -4610,6 +4847,10 @@ var AgentRuntime = class {
4610
4847
  if (done)
4611
4848
  break;
4612
4849
  partial += decoder.decode(value, { stream: true });
4850
+ if (partial.length > MAX_STREAM_BUFFER_SIZE) {
4851
+ serverLogger.warn("[AGENT] Stream buffer exceeded max size, truncating");
4852
+ partial = partial.slice(-MAX_STREAM_BUFFER_SIZE / 2);
4853
+ }
4613
4854
  const segments = partial.split("\n");
4614
4855
  partial = segments.pop() ?? "";
4615
4856
  const lines = segments.filter((line) => line.trim());
@@ -5166,7 +5407,6 @@ async function setupAI(options = {}) {
5166
5407
 
5167
5408
  // src/ai/mcp/server.ts
5168
5409
  var MCPServer = class {
5169
- config;
5170
5410
  constructor(config) {
5171
5411
  this.config = config;
5172
5412
  }
@@ -5477,9 +5717,8 @@ import { anthropic } from "@ai-sdk/anthropic";
5477
5717
 
5478
5718
  // src/ai/production/rate-limit/limiter.ts
5479
5719
  var FixedWindowLimiter = class {
5480
- requests = /* @__PURE__ */ new Map();
5481
- config;
5482
5720
  constructor(config) {
5721
+ this.requests = /* @__PURE__ */ new Map();
5483
5722
  this.config = config;
5484
5723
  }
5485
5724
  check(identifier) {
@@ -5520,10 +5759,8 @@ var FixedWindowLimiter = class {
5520
5759
  }
5521
5760
  };
5522
5761
  var TokenBucketLimiter = class {
5523
- buckets = /* @__PURE__ */ new Map();
5524
- config;
5525
- refillRate;
5526
5762
  constructor(config) {
5763
+ this.buckets = /* @__PURE__ */ new Map();
5527
5764
  this.config = config;
5528
5765
  this.refillRate = config.maxRequests / config.windowMs;
5529
5766
  }
@@ -5626,7 +5863,9 @@ function rateLimitMiddleware(config) {
5626
5863
 
5627
5864
  // src/ai/production/cache/cache.ts
5628
5865
  var MemoryCache = class {
5629
- cache = /* @__PURE__ */ new Map();
5866
+ constructor() {
5867
+ this.cache = /* @__PURE__ */ new Map();
5868
+ }
5630
5869
  set(key, response) {
5631
5870
  this.cache.set(key, {
5632
5871
  response,
@@ -5657,9 +5896,8 @@ var MemoryCache = class {
5657
5896
  }
5658
5897
  };
5659
5898
  var LRUCache = class {
5660
- cache = /* @__PURE__ */ new Map();
5661
- maxSize;
5662
5899
  constructor(maxSize = 100) {
5900
+ this.cache = /* @__PURE__ */ new Map();
5663
5901
  this.maxSize = maxSize;
5664
5902
  }
5665
5903
  set(key, response) {
@@ -5703,10 +5941,9 @@ var LRUCache = class {
5703
5941
  }
5704
5942
  };
5705
5943
  var TTLCache = class {
5706
- cache = /* @__PURE__ */ new Map();
5707
- ttl;
5708
- cleanupInterval = null;
5709
5944
  constructor(ttl = 3e5) {
5945
+ this.cache = /* @__PURE__ */ new Map();
5946
+ this.cleanupInterval = null;
5710
5947
  this.ttl = ttl;
5711
5948
  this.startCleanup();
5712
5949
  }
@@ -5860,14 +6097,13 @@ function cacheMiddleware(config) {
5860
6097
 
5861
6098
  // src/ai/production/cost-tracking/tracker.ts
5862
6099
  var CostTracker = class {
5863
- records = [];
5864
- config;
5865
- dailyTotal = 0;
5866
- monthlyTotal = 0;
5867
- lastDayReset = Date.now();
5868
- lastMonthReset = Date.now();
5869
- resetInterval = null;
5870
6100
  constructor(config) {
6101
+ this.records = [];
6102
+ this.dailyTotal = 0;
6103
+ this.monthlyTotal = 0;
6104
+ this.lastDayReset = Date.now();
6105
+ this.lastMonthReset = Date.now();
6106
+ this.resetInterval = null;
5871
6107
  this.config = config;
5872
6108
  this.startPeriodicReset();
5873
6109
  }
@@ -6130,7 +6366,6 @@ var PII_PATTERNS = {
6130
6366
  creditCard: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g
6131
6367
  };
6132
6368
  var InputValidator = class {
6133
- config;
6134
6369
  constructor(config) {
6135
6370
  this.config = config || {};
6136
6371
  }
@@ -6190,7 +6425,6 @@ var InputValidator = class {
6190
6425
  }
6191
6426
  };
6192
6427
  var OutputFilter = class {
6193
- config;
6194
6428
  constructor(config) {
6195
6429
  this.config = config || {};
6196
6430
  }
@@ -6464,13 +6698,12 @@ function hasLockSupport(backend) {
6464
6698
  // src/ai/workflow/backends/memory.ts
6465
6699
  var DEFAULT_MAX_QUEUE_SIZE = 1e4;
6466
6700
  var MemoryBackend = class {
6467
- runs = /* @__PURE__ */ new Map();
6468
- checkpoints = /* @__PURE__ */ new Map();
6469
- approvals = /* @__PURE__ */ new Map();
6470
- queue = [];
6471
- locks = /* @__PURE__ */ new Map();
6472
- config;
6473
6701
  constructor(config = {}) {
6702
+ this.runs = /* @__PURE__ */ new Map();
6703
+ this.checkpoints = /* @__PURE__ */ new Map();
6704
+ this.approvals = /* @__PURE__ */ new Map();
6705
+ this.queue = [];
6706
+ this.locks = /* @__PURE__ */ new Map();
6474
6707
  this.config = {
6475
6708
  prefix: "wf:",
6476
6709
  debug: false,
@@ -6801,7 +7034,6 @@ var MemoryBackend = class {
6801
7034
 
6802
7035
  // src/ai/workflow/executor/dag-executor.ts
6803
7036
  var DAGExecutor = class {
6804
- config;
6805
7037
  constructor(config) {
6806
7038
  this.config = {
6807
7039
  maxConcurrency: 10,
@@ -6944,9 +7176,16 @@ var DAGExecutor = class {
6944
7176
  context,
6945
7177
  nodeStates
6946
7178
  );
7179
+ case "loop":
7180
+ return await this.executeLoopNode(
7181
+ node,
7182
+ config,
7183
+ context,
7184
+ nodeStates
7185
+ );
6947
7186
  default:
6948
7187
  throw new Error(
6949
- `Unknown node type "${config.type}" for node "${node.id}". Valid types are: step, parallel, map, branch, wait, subWorkflow`
7188
+ `Unknown node type "${config.type}" for node "${node.id}". Valid types are: step, parallel, map, branch, wait, subWorkflow, loop`
6950
7189
  );
6951
7190
  }
6952
7191
  }
@@ -7104,6 +7343,155 @@ var DAGExecutor = class {
7104
7343
  waiting: result.waiting
7105
7344
  };
7106
7345
  }
7346
+ /**
7347
+ * Execute a loop node
7348
+ */
7349
+ async executeLoopNode(node, config, context, nodeStates) {
7350
+ const startTime = Date.now();
7351
+ const previousResults = [];
7352
+ let iteration = 0;
7353
+ let exitReason = "condition";
7354
+ let lastError;
7355
+ const existingLoopState = context[`${node.id}_loop_state`];
7356
+ if (existingLoopState) {
7357
+ iteration = existingLoopState.iteration;
7358
+ previousResults.push(...existingLoopState.previousResults);
7359
+ }
7360
+ while (iteration < config.maxIterations) {
7361
+ const loopContext = {
7362
+ iteration,
7363
+ totalIterations: iteration,
7364
+ previousResults: [...previousResults],
7365
+ isFirstIteration: iteration === 0,
7366
+ isLastAllowedIteration: iteration === config.maxIterations - 1
7367
+ };
7368
+ const shouldContinue = await config.while(context, loopContext);
7369
+ if (!shouldContinue) {
7370
+ exitReason = "condition";
7371
+ break;
7372
+ }
7373
+ const steps = typeof config.steps === "function" ? config.steps(context, loopContext) : config.steps;
7374
+ const result = await this.execute(steps, {
7375
+ id: `${node.id}_iter_${iteration}`,
7376
+ workflowId: "",
7377
+ status: "running",
7378
+ input: context.input,
7379
+ nodeStates: {},
7380
+ currentNodes: [],
7381
+ context: { ...context, _loop: loopContext },
7382
+ checkpoints: [],
7383
+ pendingApprovals: [],
7384
+ createdAt: /* @__PURE__ */ new Date()
7385
+ });
7386
+ if (result.waiting) {
7387
+ Object.assign(nodeStates, result.nodeStates);
7388
+ const state2 = {
7389
+ nodeId: node.id,
7390
+ status: "running",
7391
+ output: {
7392
+ iteration,
7393
+ waiting: true,
7394
+ previousResults
7395
+ },
7396
+ attempt: 1,
7397
+ startedAt: new Date(startTime)
7398
+ };
7399
+ return {
7400
+ state: state2,
7401
+ contextUpdates: {
7402
+ ...result.context,
7403
+ [`${node.id}_loop_state`]: { iteration, previousResults }
7404
+ },
7405
+ waiting: true
7406
+ };
7407
+ }
7408
+ if (result.error) {
7409
+ lastError = result.error;
7410
+ exitReason = "error";
7411
+ break;
7412
+ }
7413
+ previousResults.push(result.context);
7414
+ Object.assign(context, result.context);
7415
+ Object.assign(nodeStates, result.nodeStates);
7416
+ if (config.delay && iteration < config.maxIterations - 1) {
7417
+ const delayMs = typeof config.delay === "number" ? config.delay : this.parseDuration(config.delay);
7418
+ await this.sleep(delayMs);
7419
+ }
7420
+ iteration++;
7421
+ }
7422
+ if (iteration >= config.maxIterations && exitReason !== "condition") {
7423
+ exitReason = "maxIterations";
7424
+ }
7425
+ const finalLoopContext = {
7426
+ iteration,
7427
+ totalIterations: iteration,
7428
+ previousResults,
7429
+ isFirstIteration: false,
7430
+ isLastAllowedIteration: true
7431
+ };
7432
+ let completionUpdates = {};
7433
+ if (exitReason === "maxIterations" && config.onMaxIterations) {
7434
+ completionUpdates = await config.onMaxIterations(context, finalLoopContext);
7435
+ } else if (exitReason === "condition" && config.onComplete) {
7436
+ completionUpdates = await config.onComplete(context, finalLoopContext);
7437
+ }
7438
+ const output = {
7439
+ exitReason,
7440
+ iterations: iteration,
7441
+ previousResults,
7442
+ ...completionUpdates
7443
+ };
7444
+ const state = {
7445
+ nodeId: node.id,
7446
+ status: exitReason === "error" ? "failed" : "completed",
7447
+ output,
7448
+ error: lastError,
7449
+ attempt: 1,
7450
+ startedAt: new Date(startTime),
7451
+ completedAt: /* @__PURE__ */ new Date()
7452
+ };
7453
+ this.config.onNodeComplete?.(node.id, state);
7454
+ return {
7455
+ state,
7456
+ contextUpdates: {
7457
+ [node.id]: output,
7458
+ ...completionUpdates
7459
+ },
7460
+ waiting: false
7461
+ };
7462
+ }
7463
+ /**
7464
+ * Parse duration string to milliseconds
7465
+ */
7466
+ parseDuration(duration) {
7467
+ if (typeof duration === "number")
7468
+ return duration;
7469
+ const match = duration.match(/^(\d+)(ms|s|m|h|d)$/);
7470
+ if (!match)
7471
+ return 0;
7472
+ const value = parseInt(match[1], 10);
7473
+ const unit = match[2];
7474
+ switch (unit) {
7475
+ case "ms":
7476
+ return value;
7477
+ case "s":
7478
+ return value * 1e3;
7479
+ case "m":
7480
+ return value * 60 * 1e3;
7481
+ case "h":
7482
+ return value * 60 * 60 * 1e3;
7483
+ case "d":
7484
+ return value * 24 * 60 * 60 * 1e3;
7485
+ default:
7486
+ return 0;
7487
+ }
7488
+ }
7489
+ /**
7490
+ * Sleep for specified milliseconds
7491
+ */
7492
+ sleep(ms) {
7493
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
7494
+ }
7107
7495
  /**
7108
7496
  * Execute a step node
7109
7497
  */
@@ -7343,7 +7731,6 @@ var DAGExecutor = class {
7343
7731
 
7344
7732
  // src/ai/workflow/executor/checkpoint-manager.ts
7345
7733
  var CheckpointManager = class {
7346
- config;
7347
7734
  constructor(config) {
7348
7735
  this.config = {
7349
7736
  debug: false,
@@ -7499,9 +7886,14 @@ var CheckpointManager = class {
7499
7886
  };
7500
7887
 
7501
7888
  // src/ai/workflow/executor/step-executor.ts
7889
+ var DEFAULT_RETRY = {
7890
+ maxAttempts: 1,
7891
+ backoff: "exponential",
7892
+ initialDelay: 1e3,
7893
+ maxDelay: 3e4
7894
+ };
7502
7895
  var DEFAULT_STEP_TIMEOUT_MS = 5 * 60 * 1e3;
7503
7896
  var StepExecutor = class {
7504
- config;
7505
7897
  constructor(config = {}) {
7506
7898
  this.config = {
7507
7899
  defaultTimeout: DEFAULT_STEP_TIMEOUT_MS,
@@ -7509,7 +7901,7 @@ var StepExecutor = class {
7509
7901
  };
7510
7902
  }
7511
7903
  /**
7512
- * Execute a step node
7904
+ * Execute a step node with retry support
7513
7905
  */
7514
7906
  async execute(node, context) {
7515
7907
  const startTime = Date.now();
@@ -7519,30 +7911,96 @@ var StepExecutor = class {
7519
7911
  `StepExecutor can only execute 'step' nodes, but node "${node.id}" has type '${config.type}'. This is likely a bug in the DAG executor routing.`
7520
7912
  );
7521
7913
  }
7522
- try {
7523
- const resolvedInput = await this.resolveInput(config.input, context);
7524
- this.config.onStepStart?.(node.id, resolvedInput);
7525
- const timeout = config.timeout ? parseDuration(config.timeout) : this.config.defaultTimeout;
7526
- const output = await this.executeWithTimeout(
7527
- () => this.executeStep(config, resolvedInput, context),
7528
- timeout,
7529
- node.id
7530
- );
7531
- this.config.onStepComplete?.(node.id, output);
7532
- return {
7533
- success: true,
7534
- output,
7535
- executionTime: Date.now() - startTime
7536
- };
7537
- } catch (error) {
7538
- const errorMessage = error instanceof Error ? error.message : String(error);
7539
- this.config.onStepError?.(node.id, error);
7540
- return {
7541
- success: false,
7542
- error: errorMessage,
7543
- executionTime: Date.now() - startTime
7544
- };
7914
+ const retryConfig = { ...DEFAULT_RETRY, ...config.retry };
7915
+ const maxAttempts = retryConfig.maxAttempts ?? 1;
7916
+ let lastError;
7917
+ let attempt = 0;
7918
+ while (attempt < maxAttempts) {
7919
+ attempt++;
7920
+ try {
7921
+ const resolvedInput = await this.resolveInput(config.input, context);
7922
+ this.config.onStepStart?.(node.id, resolvedInput);
7923
+ const timeout = config.timeout ? parseDuration(config.timeout) : this.config.defaultTimeout;
7924
+ const output = await this.executeWithTimeout(
7925
+ () => this.executeStep(config, resolvedInput, context),
7926
+ timeout,
7927
+ node.id
7928
+ );
7929
+ this.config.onStepComplete?.(node.id, output);
7930
+ return {
7931
+ success: true,
7932
+ output,
7933
+ executionTime: Date.now() - startTime
7934
+ };
7935
+ } catch (error) {
7936
+ lastError = error instanceof Error ? error : new Error(String(error));
7937
+ const shouldRetry = attempt < maxAttempts && this.isRetryableError(lastError, retryConfig);
7938
+ if (shouldRetry) {
7939
+ const delay2 = this.calculateRetryDelay(attempt, retryConfig);
7940
+ await this.sleep(delay2);
7941
+ continue;
7942
+ }
7943
+ this.config.onStepError?.(node.id, lastError);
7944
+ return {
7945
+ success: false,
7946
+ error: lastError.message,
7947
+ executionTime: Date.now() - startTime
7948
+ };
7949
+ }
7950
+ }
7951
+ return {
7952
+ success: false,
7953
+ error: lastError?.message ?? "Unknown error",
7954
+ executionTime: Date.now() - startTime
7955
+ };
7956
+ }
7957
+ /**
7958
+ * Check if error is retryable
7959
+ */
7960
+ isRetryableError(error, config) {
7961
+ if (config.retryIf) {
7962
+ return config.retryIf(error);
7545
7963
  }
7964
+ const retryablePatterns = [
7965
+ /timeout/i,
7966
+ /ECONNRESET/i,
7967
+ /ECONNREFUSED/i,
7968
+ /ETIMEDOUT/i,
7969
+ /rate limit/i,
7970
+ /429/,
7971
+ /503/,
7972
+ /502/
7973
+ ];
7974
+ return retryablePatterns.some((pattern) => pattern.test(error.message));
7975
+ }
7976
+ /**
7977
+ * Calculate retry delay based on backoff strategy
7978
+ */
7979
+ calculateRetryDelay(attempt, config) {
7980
+ const initialDelay = config.initialDelay ?? 1e3;
7981
+ const maxDelay = config.maxDelay ?? 3e4;
7982
+ let delay2;
7983
+ switch (config.backoff) {
7984
+ case "exponential":
7985
+ delay2 = initialDelay * Math.pow(2, attempt - 1);
7986
+ break;
7987
+ case "linear":
7988
+ delay2 = initialDelay * attempt;
7989
+ break;
7990
+ case "fixed":
7991
+ default:
7992
+ delay2 = initialDelay;
7993
+ break;
7994
+ }
7995
+ const jitter = delay2 * 0.1 * (Math.random() * 2 - 1);
7996
+ delay2 = Math.min(delay2 + jitter, maxDelay);
7997
+ return Math.floor(delay2);
7998
+ }
7999
+ /**
8000
+ * Sleep for specified milliseconds
8001
+ */
8002
+ sleep(ms) {
8003
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
7546
8004
  }
7547
8005
  /**
7548
8006
  * Resolve step input from context
@@ -7723,15 +8181,8 @@ var StepExecutor = class {
7723
8181
 
7724
8182
  // src/ai/workflow/executor/workflow-executor.ts
7725
8183
  var WorkflowExecutor = class _WorkflowExecutor {
7726
- config;
7727
- stepExecutor;
7728
- checkpointManager;
7729
- dagExecutor;
7730
- workflows = /* @__PURE__ */ new Map();
7731
- blobResolver;
7732
- /** Default lock duration: 30 seconds */
7733
- static DEFAULT_LOCK_DURATION = 3e4;
7734
8184
  constructor(config) {
8185
+ this.workflows = /* @__PURE__ */ new Map();
7735
8186
  this.config = {
7736
8187
  maxConcurrency: 10,
7737
8188
  debug: false,
@@ -7767,6 +8218,10 @@ var WorkflowExecutor = class _WorkflowExecutor {
7767
8218
  };
7768
8219
  }
7769
8220
  }
8221
+ static {
8222
+ /** Default lock duration: 30 seconds */
8223
+ this.DEFAULT_LOCK_DURATION = 3e4;
8224
+ }
7770
8225
  /**
7771
8226
  * Register a workflow definition
7772
8227
  */
@@ -8121,10 +8576,8 @@ var WorkflowExecutor = class _WorkflowExecutor {
8121
8576
 
8122
8577
  // src/ai/workflow/runtime/approval-manager.ts
8123
8578
  var ApprovalManager = class {
8124
- config;
8125
- expirationTimer;
8126
- destroyed = false;
8127
8579
  constructor(config) {
8580
+ this.destroyed = false;
8128
8581
  this.config = {
8129
8582
  expirationCheckInterval: 6e4,
8130
8583
  // Check every minute
@@ -8351,17 +8804,41 @@ var ApprovalManager = class {
8351
8804
 
8352
8805
  // src/ai/workflow/api/workflow-client.ts
8353
8806
  var WorkflowClient = class {
8354
- backend;
8355
- executor;
8356
- approvalManager;
8357
- debug;
8358
8807
  constructor(config = {}) {
8359
8808
  this.debug = config.debug ?? false;
8360
8809
  this.backend = config.backend ?? new MemoryBackend({ debug: this.debug });
8361
8810
  this.executor = new WorkflowExecutor({
8362
8811
  backend: this.backend,
8363
8812
  debug: this.debug,
8364
- ...config.executor
8813
+ ...config.executor,
8814
+ onWaiting: async (run, nodeId) => {
8815
+ const nodeState = run.nodeStates[nodeId];
8816
+ if (!nodeState?.input) {
8817
+ if (this.debug) {
8818
+ console.log(`[WorkflowClient] No wait config found for node: ${nodeId}`);
8819
+ }
8820
+ return;
8821
+ }
8822
+ const input = nodeState.input;
8823
+ if (input.type !== "approval") {
8824
+ return;
8825
+ }
8826
+ const waitConfig = {
8827
+ type: "wait",
8828
+ waitType: "approval",
8829
+ message: input.message,
8830
+ payload: input.payload
8831
+ };
8832
+ try {
8833
+ await this.approvalManager.createApproval(run, nodeId, waitConfig, run.context);
8834
+ if (this.debug) {
8835
+ console.log(`[WorkflowClient] Created approval for node: ${nodeId}`);
8836
+ }
8837
+ } catch (error) {
8838
+ console.error(`[WorkflowClient] Failed to create approval:`, error);
8839
+ }
8840
+ config.executor?.onWaiting?.(run, nodeId);
8841
+ }
8365
8842
  });
8366
8843
  this.approvalManager = new ApprovalManager({
8367
8844
  backend: this.backend,