@vite-hub/workflow 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +56 -6
  2. package/dist/{cloudflare-env-BVUMOgFK.d.ts → cloudflare-env-CIjdFTv6.d.ts} +2 -1
  3. package/dist/{cloudflare-env-g0Rf3ClH.js → cloudflare-env-mFp2Uj6Z.js} +7 -3
  4. package/dist/{cloudflare-vite-DcM5YVTP.js → cloudflare-vite-B0zel2Do.js} +4 -5
  5. package/dist/{config-DkgsQuXz.js → config-BDqB6I4E.js} +43 -5
  6. package/dist/index.d.ts +43 -18
  7. package/dist/index.js +99 -111
  8. package/dist/provider-operation-CWIV4k42.js +62 -0
  9. package/dist/runtime/cloudflare-runner.d.ts +2 -2
  10. package/dist/runtime/cloudflare-runner.js +15 -14
  11. package/dist/runtime/cloudflare-shared.d.ts +2 -2
  12. package/dist/runtime/cloudflare-shared.js +2 -2
  13. package/dist/runtime/cloudflare-vite.d.ts +3 -3
  14. package/dist/runtime/cloudflare-vite.js +1 -1
  15. package/dist/runtime/execute.d.ts +1 -1
  16. package/dist/runtime/openworkflow-worker.d.ts +4 -2
  17. package/dist/runtime/openworkflow-worker.js +63 -6
  18. package/dist/runtime/openworkflow.d.ts +22 -1
  19. package/dist/runtime/openworkflow.js +110 -37
  20. package/dist/runtime/state.d.ts +1 -1
  21. package/dist/runtime/vercel-vite.d.ts +58 -3
  22. package/dist/runtime/vercel-vite.js +3 -2
  23. package/dist/{types-ThDitg7Z.d.ts → types-BUlYBZwH.d.ts} +48 -6
  24. package/dist/vercel-D2_J5D_Y.js +217 -0
  25. package/dist/vite.d.ts +2 -5
  26. package/dist/vite.js +1678 -144
  27. package/package.json +45 -48
  28. package/dist/module-UYEnVfxy.js +0 -1546
  29. package/dist/nitro.d.ts +0 -19
  30. package/dist/nitro.js +0 -2
  31. package/dist/openworkflow-amN7IkGO.d.ts +0 -24
  32. package/dist/runtime/nitro-plugin.d.ts +0 -6
  33. package/dist/runtime/nitro-plugin.js +0 -14
  34. /package/dist/{_app-BVWpnNHo.d.ts → _app-DgwN4ulW.d.ts} +0 -0
  35. /package/dist/{app-BE3EYOWn.js → app-BZpq2cwC.js} +0 -0
  36. /package/dist/{cloudflare-CeHVSHoN.js → cloudflare-Cds1VIWa.js} +0 -0
  37. /package/dist/{cloudflare-shared-BIeY2kak.d.ts → cloudflare-shared-BVVgDSdq.d.ts} +0 -0
package/dist/vite.js CHANGED
@@ -1,10 +1,11 @@
1
- import { t as normalizeWorkflowOptions } from "./config-DkgsQuXz.js";
2
- import { r as getCloudflareWorkflowClassName, t as createCloudflareWorkflowBindings } from "./cloudflare-CeHVSHoN.js";
3
- import { a as createImportPath, i as computePackageDir, n as discoverWorkflowDefinitions, o as ensureGeneratedDir, r as createRuntimeRegistryContents, s as resolveRuntimeModule$1, t as workflowNitroModule } from "./module-UYEnVfxy.js";
4
- import { cp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
5
- import { basename, resolve } from "node:path";
6
- import { existsSync } from "node:fs";
7
- import { dirname, relative as relative$1, resolve as resolve$1 } from "pathe";
1
+ import { t as normalizeWorkflowOptions } from "./config-BDqB6I4E.js";
2
+ import { r as getCloudflareWorkflowClassName, t as createCloudflareWorkflowBindings } from "./cloudflare-Cds1VIWa.js";
3
+ import { builtinModules, createRequire } from "node:module";
4
+ import { cp, mkdir, readFile, readdir, rm, rmdir, stat, symlink, writeFile } from "node:fs/promises";
5
+ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
6
+ import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
7
+ import { basename as basename$1, dirname as dirname$1, normalize, relative as relative$1, resolve as resolve$1 } from "pathe";
8
+ import { fileURLToPath } from "node:url";
8
9
  import { build } from "esbuild";
9
10
  const VITEHUB_MODES = {
10
11
  e2e: "e2e",
@@ -47,7 +48,7 @@ async function copyClientOutput(clientDir, targetDir) {
47
48
  force: true,
48
49
  recursive: true
49
50
  });
50
- await mkdir(dirname(resolvedTargetDir), { recursive: true });
51
+ await mkdir(dirname$1(resolvedTargetDir), { recursive: true });
51
52
  const targetRelativePath = relative$1(resolvedClientDir, resolvedTargetDir);
52
53
  if (targetRelativePath && !targetRelativePath.startsWith("../")) {
53
54
  const [targetRootEntry] = targetRelativePath.split("/", 1);
@@ -58,26 +59,135 @@ async function copyClientOutput(clientDir, targetDir) {
58
59
  await cp(resolvedClientDir, resolvedTargetDir, { recursive: true });
59
60
  }
60
61
  //#endregion
61
- //#region ../internal/src/build/esbuild.ts
62
- async function bundleEsmEntry(entryFile, outfile, options = {}) {
63
- const format = options.format || "esm";
64
- const platform = options.platform || "neutral";
65
- await build({
66
- alias: options.alias,
67
- banner: format === "esm" && platform === "node" ? { js: "import { createRequire as __createRequire } from \"node:module\";\nvar require = __createRequire(import.meta.url);\n" } : void 0,
68
- bundle: true,
69
- conditions: options.conditions,
70
- entryPoints: [entryFile],
71
- external: options.external,
72
- format,
73
- logLevel: "silent",
74
- outfile,
75
- platform,
76
- sourcemap: false,
77
- target: "es2022",
78
- write: true
62
+ //#region ../internal/src/build/provider-output-config.ts
63
+ function isProviderJsonArray(value, ancestors) {
64
+ if (Object.getPrototypeOf(value) !== Array.prototype || ancestors.has(value)) return false;
65
+ if (Reflect.ownKeys(value).length !== value.length + 1) return false;
66
+ ancestors.add(value);
67
+ try {
68
+ for (let index = 0; index < value.length; index++) {
69
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
70
+ if (!descriptor?.enumerable || !("value" in descriptor) || !isProviderJsonValue(descriptor.value, ancestors)) return false;
71
+ }
72
+ return true;
73
+ } finally {
74
+ ancestors.delete(value);
75
+ }
76
+ }
77
+ function isProviderJsonRecordValue(value, ancestors) {
78
+ const prototype = Object.getPrototypeOf(value);
79
+ if (prototype !== Object.prototype && prototype !== null || ancestors.has(value)) return false;
80
+ ancestors.add(value);
81
+ try {
82
+ return Reflect.ownKeys(value).every((key) => {
83
+ if (typeof key !== "string") return false;
84
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
85
+ return descriptor?.enumerable === true && "value" in descriptor && isProviderJsonValue(descriptor.value, ancestors);
86
+ });
87
+ } finally {
88
+ ancestors.delete(value);
89
+ }
90
+ }
91
+ function isProviderJsonValue(value, ancestors) {
92
+ if (value === null || typeof value === "boolean" || typeof value === "string") return true;
93
+ if (typeof value === "number") return Number.isFinite(value);
94
+ if (Array.isArray(value)) return isProviderJsonArray(value, ancestors);
95
+ return typeof value === "object" && isProviderJsonRecordValue(value, ancestors);
96
+ }
97
+ function isProviderJsonRecord(value) {
98
+ try {
99
+ return typeof value === "object" && value !== null && !Array.isArray(value) && isProviderJsonRecordValue(value, /* @__PURE__ */ new Set());
100
+ } catch {
101
+ return false;
102
+ }
103
+ }
104
+ function assertProviderJsonRecord(value) {
105
+ if (!isProviderJsonRecord(value)) throw new TypeError("[vitehub] Provider output config must be a JSON object.");
106
+ }
107
+ function isFileNotFoundError(error) {
108
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
109
+ }
110
+ async function readProviderJsonRecord(file) {
111
+ try {
112
+ const parsed = JSON.parse(await readFile(file, "utf8"));
113
+ assertProviderJsonRecord(parsed);
114
+ return parsed;
115
+ } catch (error) {
116
+ if (isFileNotFoundError(error)) return;
117
+ throw error;
118
+ }
119
+ }
120
+ function deleteOwnedFields(value, ownership) {
121
+ const next = { ...value };
122
+ for (const key of ownership.keys ?? []) delete next[key];
123
+ for (const field of Object.keys(ownership.arrays ?? {})) delete next[field];
124
+ return next;
125
+ }
126
+ function getKeyedArrayEntryValue(value, key) {
127
+ if (!isProviderJsonRecord(value)) return;
128
+ const entryValue = value[key];
129
+ return entryValue === null || typeof entryValue === "boolean" || typeof entryValue === "number" || typeof entryValue === "string" ? entryValue : void 0;
130
+ }
131
+ function preserveUnownedKeyedArrayEntries(value, ownership, replacements = []) {
132
+ const current = Array.isArray(value) ? value : [];
133
+ const replacementKeys = replacements.flatMap((entry) => {
134
+ const key = getKeyedArrayEntryValue(entry, ownership.key);
135
+ return key === void 0 ? [] : [key];
136
+ });
137
+ const ownedKeys = new Set([...ownership.values ?? [], ...replacementKeys]);
138
+ return current.filter((entry) => {
139
+ const key = getKeyedArrayEntryValue(entry, ownership.key);
140
+ return key === void 0 || !ownedKeys.has(key);
79
141
  });
80
142
  }
143
+ function mergeOwnedArrays(existing, value, arrays) {
144
+ const next = { ...value };
145
+ for (const [field, ownership] of Object.entries(arrays ?? {})) {
146
+ const incoming = Array.isArray(value[field]) ? value[field] : [];
147
+ const merged = [...preserveUnownedKeyedArrayEntries(existing[field], ownership, incoming), ...incoming];
148
+ if (merged.length) next[field] = merged;
149
+ else delete next[field];
150
+ }
151
+ return next;
152
+ }
153
+ async function persistProviderJsonRecord(file, value, removeIfEmpty) {
154
+ if (removeIfEmpty && !Object.keys(value).length) {
155
+ await rm(file, { force: true });
156
+ return;
157
+ }
158
+ await writeFile(file, `${JSON.stringify(value, null, 2)}\n`, "utf8");
159
+ }
160
+ async function writeProviderOutputConfig(file, value, ownership = {}, options = {}) {
161
+ await persistProviderJsonRecord(file, mergeProviderOutputConfig(await readProviderJsonRecord(file) ?? {}, value, ownership), options.removeIfEmpty === true);
162
+ }
163
+ function mergeProviderOutputConfig(existing, value, ownership = {}) {
164
+ assertProviderJsonRecord(existing);
165
+ assertProviderJsonRecord(value);
166
+ return {
167
+ ...deleteOwnedFields(existing, ownership),
168
+ ...mergeOwnedArrays(existing, value, ownership.arrays)
169
+ };
170
+ }
171
+ function stringifyProviderOutputConfig(value) {
172
+ assertProviderJsonRecord(value);
173
+ return JSON.stringify(value, null, 2);
174
+ }
175
+ async function cleanProviderOutputConfig(file, ownership) {
176
+ if (!ownership.keys?.length && !Object.keys(ownership.arrays ?? {}).length) return;
177
+ const existing = await readProviderJsonRecord(file);
178
+ if (!existing) return;
179
+ const next = deleteOwnedFields(existing, ownership);
180
+ let changed = (ownership.keys ?? []).some((key) => key in existing);
181
+ for (const [field, arrayOwnership] of Object.entries(ownership.arrays ?? {})) {
182
+ if (!(field in existing)) continue;
183
+ const current = existing[field];
184
+ const preserved = preserveUnownedKeyedArrayEntries(current, arrayOwnership);
185
+ changed ||= !Array.isArray(current) || preserved.length !== current.length || preserved.length === 0;
186
+ if (preserved.length) next[field] = preserved;
187
+ }
188
+ if (!changed) return;
189
+ await persistProviderJsonRecord(file, next, true);
190
+ }
81
191
  //#endregion
82
192
  //#region ../internal/src/build/user-entry.ts
83
193
  function toSafeAppName(rootDir) {
@@ -99,6 +209,343 @@ function resolveUserAppEntry(rootDir, options = {}) {
99
209
  return names.map((name) => resolve(rootDir, srcSubdir, name)).find(existsSync);
100
210
  }
101
211
  //#endregion
212
+ //#region ../internal/src/build/cloudflare.ts
213
+ const defaultCloudflareCompatibilityDate = "2026-04-20";
214
+ function createDefaultCloudflareOutputRoot(rootDir) {
215
+ return resolve(rootDir, "dist", toSafeAppName(rootDir));
216
+ }
217
+ async function writeCloudflareWranglerConfig(options) {
218
+ const outputRoot = options.outputRoot ?? createDefaultCloudflareOutputRoot(options.rootDir);
219
+ const configFile = resolve(outputRoot, "wrangler.json");
220
+ if (!options.wranglerConfig) {
221
+ await cleanProviderOutputConfig(configFile, options.wranglerConfigOwnership ?? {});
222
+ return;
223
+ }
224
+ await mkdir(outputRoot, { recursive: true });
225
+ await writeProviderOutputConfig(configFile, options.wranglerConfig, options.wranglerConfigOwnership, { removeIfEmpty: true });
226
+ }
227
+ //#endregion
228
+ //#region ../internal/src/build/vite.ts
229
+ const projectRootDirectoryMarkers = [
230
+ ["server", "agents"],
231
+ ["server", "schedules"],
232
+ ["server", "templates"],
233
+ ["server", "workspaces"]
234
+ ];
235
+ const projectRootDefinitionFileMarkers = [
236
+ ".ts",
237
+ ".mts",
238
+ ".cts",
239
+ ".js",
240
+ ".mjs",
241
+ ".cjs"
242
+ ].flatMap((extension) => [["server", `email${extension}`], [`server.email${extension}`]]);
243
+ const projectRootFileMarkers = [["package.json"]];
244
+ function createNoExternalMerger(packageName) {
245
+ return (current) => {
246
+ if (current === true) return true;
247
+ if (!current) return [packageName];
248
+ const values = Array.isArray(current) ? current : [current];
249
+ return values.includes(packageName) ? values : [...values, packageName];
250
+ };
251
+ }
252
+ function isServerEnvironment(name, config) {
253
+ return name === "ssr" || config.consumer === "server";
254
+ }
255
+ function includesNitroVitePlugin(value) {
256
+ if (Array.isArray(value)) return value.some(includesNitroVitePlugin);
257
+ if (!value || typeof value !== "object") return false;
258
+ return "name" in value && value.name === "nitro:main";
259
+ }
260
+ function hasNitroVitePlugin(config) {
261
+ return includesNitroVitePlugin(config.plugins);
262
+ }
263
+ function resolveNitroVercelFunctionName(config, product, env = process.env) {
264
+ const preset = config.nitro?.preset || env.NITRO_PRESET || env.SERVER_PRESET;
265
+ const nitro = Boolean(preset) || hasNitroVitePlugin(config);
266
+ const clientOutDir = config.environments?.client?.build?.outDir;
267
+ const vercel = preset ? preset.startsWith("vercel") : env.VITEHUB_HOSTING === "vercel" || Boolean(env.VERCEL) || Boolean(clientOutDir && /(^|[/\\])\.vercel[/\\]output([/\\]|$)/.test(clientOutDir));
268
+ return nitro && vercel ? `__${product}.func` : void 0;
269
+ }
270
+ function shouldSkipViteProviderBuild(command, mode) {
271
+ return command === "serve" || mode === "e2e";
272
+ }
273
+ function resolveViteHubProjectRoot(root, options = {}) {
274
+ const resolvedRoot = resolve(root);
275
+ if (options.projectRoot) return resolve(resolvedRoot, options.projectRoot);
276
+ if (basename(resolvedRoot) === "app") {
277
+ const parent = dirname(resolvedRoot);
278
+ if (hasProjectRootDirectoryMarker(parent) || hasProjectRootDefinitionFileMarker(parent)) return parent;
279
+ }
280
+ let current = resolvedRoot;
281
+ while (true) {
282
+ if (hasProjectRootMarker(current)) return current;
283
+ const parent = dirname(current);
284
+ if (parent === current) return resolvedRoot;
285
+ current = parent;
286
+ }
287
+ }
288
+ function hasProjectRootDirectoryMarker(root) {
289
+ for (const marker of projectRootDirectoryMarkers) try {
290
+ if (statSync(resolve(root, ...marker)).isDirectory()) return true;
291
+ } catch (error) {
292
+ if (error.code !== "ENOENT") throw error;
293
+ }
294
+ return false;
295
+ }
296
+ function hasProjectRootMarker(root) {
297
+ if (hasProjectRootDirectoryMarker(root)) return true;
298
+ if (hasProjectRootDefinitionFileMarker(root)) return true;
299
+ for (const marker of projectRootFileMarkers) try {
300
+ if (statSync(resolve(root, ...marker)).isFile()) return true;
301
+ } catch (error) {
302
+ if (error.code !== "ENOENT") throw error;
303
+ }
304
+ return false;
305
+ }
306
+ function hasProjectRootDefinitionFileMarker(root) {
307
+ for (const marker of projectRootDefinitionFileMarkers) try {
308
+ if (statSync(resolve(root, ...marker)).isFile()) return true;
309
+ } catch (error) {
310
+ if (error.code !== "ENOENT") throw error;
311
+ }
312
+ return false;
313
+ }
314
+ //#endregion
315
+ //#region ../internal/src/build/esbuild.ts
316
+ const viteRawNamespace = "vitehub-vite-raw";
317
+ const viteMarkdownTemplateNamespace = "vitehub-markdown-template";
318
+ const markdownTemplateFileSuffix = ".template.md";
319
+ const markdownTemplateModuleQuery = "markdown-template";
320
+ const markdownTemplateRegistryId = "#vitehub/templates";
321
+ const markdownTemplateRegistryPath = ".vitehub/markdown-template/templates.mjs";
322
+ const skipMarkdownTemplateResolve = "vitehubSkipMarkdownTemplateResolve";
323
+ const markdownTemplateRuntimeSpecifier = "@vite-hub/markdown-template";
324
+ function resolveEsbuildAliases(aliases) {
325
+ if (!aliases) return;
326
+ return Object.fromEntries(Object.entries(aliases).filter(([specifier]) => !specifier.endsWith("/")));
327
+ }
328
+ function stripMarkdownCode(template) {
329
+ let fence;
330
+ let inList = false;
331
+ let previousLineBlank = true;
332
+ return template.split("\n").map((line) => {
333
+ const content = line.replace(/^(?: {0,3}> ?)+/, "");
334
+ if (!fence) {
335
+ const listIndented = inList && /^ {4}(?:`{3,}|~{3,})/.test(content);
336
+ const opening = (listIndented ? content.slice(4) : content).match(/^ {0,3}(`{3,}|~{3,})/);
337
+ if (!opening) {
338
+ if (/^ {0,3}(?:[-+*]|\d+[.)])\s+/.test(content)) inList = true;
339
+ else if (content.trim() && !/^ {2,}/.test(content)) inList = false;
340
+ const indentedCode = !inList && previousLineBlank && /^(?: {4}|\t)/.test(content) || inList && previousLineBlank && /^(?: {8}| {4}\t|\t{2})/.test(content);
341
+ previousLineBlank = content.trim() === "";
342
+ return indentedCode ? "" : line;
343
+ }
344
+ fence = {
345
+ marker: opening[1][0],
346
+ length: opening[1].length,
347
+ listIndented
348
+ };
349
+ previousLineBlank = false;
350
+ return "";
351
+ }
352
+ const closing = (fence.listIndented ? content.replace(/^ {4}/, "") : content).match(/^ {0,3}(`+|~+)\s*$/)?.[1];
353
+ if (closing?.[0] === fence.marker && closing.length >= fence.length) fence = void 0;
354
+ previousLineBlank = false;
355
+ return "";
356
+ }).join("\n");
357
+ }
358
+ function extractMarkdownTemplateImportSpecifiers(template) {
359
+ const visible = stripMarkdownCode(template).replace(/(`+)[\s\S]*?\1/g, "").replace(/(!?\[[^\]]*\])\([^)]*\)/g, "$1").replace(/^ {0,3}\[[^\]]+\]:\s*\S+/gm, "").replace(/<[^>]*>/g, "");
360
+ const specifiers = /* @__PURE__ */ new Set();
361
+ for (const match of visible.matchAll(/@(\.\.?\/[^\s<>{}[\]]+)/g)) {
362
+ const token = match[1];
363
+ const trailing = token.match(/[.,;:!?)]*$/)?.[0] || "";
364
+ specifiers.add(token.slice(0, token.length - trailing.length));
365
+ }
366
+ return [...specifiers];
367
+ }
368
+ function parseMarkdownTemplateRequest(id) {
369
+ const queryIndex = id.indexOf("?");
370
+ const path = id.split(/[?#]/, 1)[0];
371
+ if (queryIndex === -1) return path.endsWith(markdownTemplateFileSuffix) ? { path } : void 0;
372
+ const query = id.slice(queryIndex + 1).split("#", 1)[0];
373
+ if (!new URLSearchParams(query).has(markdownTemplateModuleQuery)) return;
374
+ return { path };
375
+ }
376
+ function renderMarkdownTemplateModule(template, sourceId, imports = {}) {
377
+ return [
378
+ `import { renderMarkdownTemplate as vitehubRenderMarkdownTemplate } from ${JSON.stringify(markdownTemplateRuntimeSpecifier)}`,
379
+ `const vitehubMarkdownTemplate = ${JSON.stringify(template)}`,
380
+ `const vitehubMarkdownTemplateSourceId = ${JSON.stringify(sourceId)}`,
381
+ `const vitehubMarkdownTemplateImports = ${JSON.stringify(imports)}`,
382
+ "export default function render(data = {}) {",
383
+ " return vitehubRenderMarkdownTemplate(vitehubMarkdownTemplate, { data, sourceId: vitehubMarkdownTemplateSourceId, resolveImport: (specifier, importer) => vitehubMarkdownTemplateImports[`${importer}\\0${specifier}`] })",
384
+ "}",
385
+ ""
386
+ ].join("\n");
387
+ }
388
+ function hasViteRawQuery(path) {
389
+ const queryIndex = path.indexOf("?");
390
+ return queryIndex !== -1 && /(?:^|&)raw(?:&|$)/.test(path.slice(queryIndex + 1));
391
+ }
392
+ async function resolveViteRawSpecifier(path, rootDir) {
393
+ if (path.startsWith("/@fs/")) return path.slice(5);
394
+ if (!rootDir || !path.startsWith("/")) return path;
395
+ const rootRelativePath = path.slice(1);
396
+ const publicPath = resolve(rootDir, "public", rootRelativePath);
397
+ try {
398
+ await stat(publicPath);
399
+ return publicPath;
400
+ } catch (error) {
401
+ if (error.code !== "ENOENT") throw error;
402
+ return resolve(rootDir, rootRelativePath);
403
+ }
404
+ }
405
+ function createViteRawPlugin(rootDir, frameworkRuntime) {
406
+ return {
407
+ name: "vitehub-vite-raw",
408
+ setup(build) {
409
+ build.onResolve({
410
+ filter: /^@vite-hub\/markdown-template$/,
411
+ namespace: viteMarkdownTemplateNamespace
412
+ }, async (args) => {
413
+ if (!frameworkRuntime) return { path: fileURLToPath(import.meta.resolve(markdownTemplateRuntimeSpecifier)) };
414
+ return build.resolve("vite-hub/_internal/markdown-template", {
415
+ importer: args.importer,
416
+ kind: args.kind,
417
+ resolveDir: args.resolveDir
418
+ });
419
+ });
420
+ build.onResolve({ filter: /\?|\.template\.md$/ }, async (args) => {
421
+ if (args.pluginData?.[skipMarkdownTemplateResolve]) return;
422
+ const markdownTemplate = parseMarkdownTemplateRequest(args.path);
423
+ const raw = hasViteRawQuery(args.path);
424
+ if (!markdownTemplate && !raw) return;
425
+ const specifier = await resolveViteRawSpecifier(markdownTemplate?.path ?? args.path.slice(0, args.path.indexOf("?")), rootDir);
426
+ const resolved = await build.resolve(specifier, {
427
+ importer: args.importer,
428
+ kind: args.kind,
429
+ namespace: args.namespace,
430
+ pluginData: markdownTemplate ? {
431
+ ...args.pluginData && typeof args.pluginData === "object" ? args.pluginData : {},
432
+ [skipMarkdownTemplateResolve]: true
433
+ } : args.pluginData,
434
+ resolveDir: args.resolveDir,
435
+ with: args.with
436
+ });
437
+ if (resolved.errors.length) return {
438
+ errors: resolved.errors,
439
+ warnings: resolved.warnings
440
+ };
441
+ if (resolved.external) return {
442
+ external: true,
443
+ namespace: resolved.namespace,
444
+ path: resolved.path,
445
+ pluginData: resolved.pluginData,
446
+ sideEffects: resolved.sideEffects,
447
+ suffix: resolved.suffix,
448
+ warnings: resolved.warnings
449
+ };
450
+ if (resolved.namespace !== "file") return {
451
+ errors: [{ text: markdownTemplate ? `[vitehub] Markdown template fallback cannot load ${JSON.stringify(args.path)} from the ${JSON.stringify(resolved.namespace)} namespace. Handle this template import in a caller plugin.` : `[vitehub] Vite raw fallback cannot load ${JSON.stringify(args.path)} from the ${JSON.stringify(resolved.namespace)} namespace. Handle this raw import in a caller plugin.` }],
452
+ warnings: resolved.warnings
453
+ };
454
+ return {
455
+ namespace: markdownTemplate ? viteMarkdownTemplateNamespace : viteRawNamespace,
456
+ path: resolved.path,
457
+ pluginData: resolved.pluginData,
458
+ sideEffects: resolved.sideEffects,
459
+ suffix: resolved.suffix,
460
+ warnings: resolved.warnings
461
+ };
462
+ });
463
+ build.onLoad({
464
+ filter: /.*/,
465
+ namespace: viteRawNamespace
466
+ }, async (args) => ({
467
+ contents: await readFile(args.path),
468
+ loader: "text"
469
+ }));
470
+ build.onLoad({
471
+ filter: /.*/,
472
+ namespace: viteMarkdownTemplateNamespace
473
+ }, async (args) => ({
474
+ contents: await (async () => {
475
+ const template = await readFile(args.path, "utf8");
476
+ const imports = {};
477
+ const visited = new Set([args.path]);
478
+ const visit = async (source, importer) => {
479
+ for (const specifier of extractMarkdownTemplateImportSpecifiers(source)) {
480
+ const key = `${importer}\0${specifier}`;
481
+ if (imports[key]) continue;
482
+ const resolved = await build.resolve(specifier, {
483
+ importer,
484
+ kind: "import-statement",
485
+ pluginData: {
486
+ ...args.pluginData && typeof args.pluginData === "object" ? args.pluginData : {},
487
+ [skipMarkdownTemplateResolve]: true
488
+ },
489
+ resolveDir: dirname(importer)
490
+ });
491
+ if (resolved.errors.length) return Promise.reject(new Error(resolved.errors.map((error) => error.text).join("\n")));
492
+ if (resolved.external || resolved.namespace !== "file") throw new Error(`[vitehub] Could not resolve Markdown template import ${JSON.stringify(specifier)} from ${JSON.stringify(importer)} to a file.`);
493
+ const imported = {
494
+ id: resolved.path,
495
+ template: await readFile(resolved.path, "utf8")
496
+ };
497
+ imports[key] = imported;
498
+ if (!visited.has(imported.id)) {
499
+ visited.add(imported.id);
500
+ await visit(imported.template, imported.id);
501
+ }
502
+ }
503
+ };
504
+ await visit(template, args.path);
505
+ return renderMarkdownTemplateModule(template, args.path, imports);
506
+ })(),
507
+ loader: "js",
508
+ resolveDir: dirname(args.path)
509
+ }));
510
+ }
511
+ };
512
+ }
513
+ async function bundleEsmEntry(entryFile, outfile, options = {}) {
514
+ const format = options.format || "esm";
515
+ const platform = options.platform || "neutral";
516
+ const markdownTemplateRoot = options.rootDir && resolveViteHubProjectRoot(options.rootDir);
517
+ const aliases = resolveEsbuildAliases({
518
+ ...markdownTemplateRoot ? { [markdownTemplateRegistryId]: resolve(markdownTemplateRoot, markdownTemplateRegistryPath) } : {},
519
+ ...options.alias
520
+ });
521
+ const frameworkRuntime = Object.keys(aliases || {}).some((specifier) => specifier === "vite-hub" || specifier.startsWith("vite-hub/"));
522
+ await build({
523
+ alias: aliases,
524
+ banner: options.banner || format === "esm" && platform === "node" ? { js: [options.banner, ...format === "esm" && platform === "node" ? [
525
+ "import { createRequire as __createRequire } from \"node:module\";",
526
+ "import { dirname as __vitehubDirname } from \"node:path\";",
527
+ "import { fileURLToPath as __vitehubFileURLToPath } from \"node:url\";",
528
+ "globalThis.require = __createRequire(import.meta.url);",
529
+ "globalThis.__filename = __vitehubFileURLToPath(import.meta.url);",
530
+ "globalThis.__dirname = __vitehubDirname(globalThis.__filename);"
531
+ ] : []].filter(Boolean).join("\n") } : void 0,
532
+ bundle: true,
533
+ conditions: options.conditions ?? (platform === "node" ? ["node"] : void 0),
534
+ entryPoints: [entryFile],
535
+ external: options.external,
536
+ format,
537
+ logLevel: "silent",
538
+ mainFields: options.mainFields ?? (platform === "neutral" ? ["module", "main"] : void 0),
539
+ minifyIdentifiers: options.minifyIdentifiers,
540
+ outfile,
541
+ platform,
542
+ plugins: [...options.plugins ?? [], createViteRawPlugin(options.rootDir, frameworkRuntime)],
543
+ sourcemap: false,
544
+ target: "es2022",
545
+ write: true
546
+ });
547
+ }
548
+ //#endregion
102
549
  //#region ../internal/src/build/vercel-config.ts
103
550
  function createVercelConfigJson() {
104
551
  return {
@@ -121,6 +568,7 @@ function createNodeFunctionConfig(extra = {}) {
121
568
  }
122
569
  //#endregion
123
570
  //#region ../internal/src/build/deployment-output.ts
571
+ const providerDeploymentOutputWrites = /* @__PURE__ */ new Map();
124
572
  function resolveClientOutput(rootDir, clientOutDir) {
125
573
  const clientDir = resolve(rootDir, clientOutDir);
126
574
  return {
@@ -128,103 +576,118 @@ function resolveClientOutput(rootDir, clientOutDir) {
128
576
  staticIndex: hasStaticIndex(clientDir)
129
577
  };
130
578
  }
131
- function isJsonObject(value) {
132
- return typeof value === "object" && value !== null && !Array.isArray(value);
133
- }
134
- async function readJsonObject(file) {
135
- try {
136
- const parsed = JSON.parse(await readFile(file, "utf8"));
137
- return isJsonObject(parsed) ? parsed : {};
138
- } catch (error) {
139
- if (error.code === "ENOENT") return {};
140
- throw error;
141
- }
142
- }
143
- function deleteJsonObjectKeys(value, keys) {
144
- if (!keys?.length) return value;
145
- const next = { ...value };
146
- for (const key of keys) delete next[key];
147
- return next;
148
- }
149
- async function writeMergedJsonObject(file, value, ownedKeys) {
150
- const existing = await readJsonObject(file);
151
- await writeFile(file, `${JSON.stringify({
152
- ...deleteJsonObjectKeys(existing, ownedKeys),
153
- ...value
154
- }, null, 2)}\n`, "utf8");
155
- }
156
- async function deleteJsonObjectKeysFromFile(file, keys) {
157
- if (!keys?.length) return;
158
- let existing;
159
- try {
160
- const parsed = JSON.parse(await readFile(file, "utf8"));
161
- existing = isJsonObject(parsed) ? parsed : {};
162
- } catch (error) {
163
- if (error.code === "ENOENT") return;
164
- throw error;
165
- }
166
- const next = { ...existing };
167
- let changed = false;
168
- for (const key of keys) if (key in next) {
169
- delete next[key];
170
- changed = true;
171
- }
172
- if (!changed) return;
173
- await writeFile(file, `${JSON.stringify(next, null, 2)}\n`, "utf8");
174
- }
175
- function shouldSkipViteProviderBuild(command, mode) {
176
- return command === "serve" || mode === "e2e";
177
- }
178
- function createDefaultCloudflareOutputRoot(rootDir) {
179
- return resolve(rootDir, "dist", toSafeAppName(rootDir));
180
- }
181
579
  function createDefaultCloudflareStaticOutputDir(rootDir) {
182
580
  return resolve(rootDir, "dist", "client");
183
581
  }
184
582
  function createDefaultVercelOutputRoot(rootDir) {
185
583
  return resolve(rootDir, ".vercel", "output");
186
584
  }
585
+ function createDefaultNetlifyOutputRoot(rootDir) {
586
+ return resolve(rootDir, ".netlify", "v1");
587
+ }
588
+ function assertNetlifyFunctionName(functionName) {
589
+ if (!functionName || functionName.includes("\\") || functionName.split("/").some((part) => !part || part === "." || part === "..")) throw new Error(`Invalid Netlify function name: ${functionName}`);
590
+ }
591
+ function resolveNetlifyFunctionFile(functionsRoot, functionName) {
592
+ assertNetlifyFunctionName(functionName);
593
+ return resolve(functionsRoot, `${functionName}.mjs`);
594
+ }
595
+ async function appendNetlifyFunctionConfig(outfile, config) {
596
+ if (!config) return;
597
+ await writeFile(outfile, `${(await readFile(outfile, "utf8")).trimEnd()}\n\nexport const config = ${stringifyProviderOutputConfig(config)}\n`, "utf8");
598
+ }
187
599
  async function writeCloudflareDeploymentOutput(options) {
188
600
  const { clientDir, staticIndex } = resolveClientOutput(options.rootDir, options.clientOutDir);
189
601
  const outputRoot = options.outputRoot ?? createDefaultCloudflareOutputRoot(options.rootDir);
190
- const workerOutfile = resolve(outputRoot, options.bundleOutfileName ?? "index.js");
602
+ const files = Object.entries(options.files ?? {});
603
+ const workerOutfile = options.bundleEntry ? resolve(outputRoot, options.bundleOutfileName ?? "index.js") : void 0;
604
+ if (workerOutfile && files.some(([fileName]) => resolve(outputRoot, fileName) === workerOutfile)) throw new Error(`Cloudflare output file conflicts with bundle outfile: ${workerOutfile}`);
191
605
  await mkdir(outputRoot, { recursive: true });
192
- await rm(workerOutfile, {
193
- force: true,
194
- recursive: true
195
- });
196
- await Promise.all([
197
- bundleEsmEntry(options.bundleEntry, workerOutfile, options.bundleOptions),
198
- writeMergedJsonObject(resolve(outputRoot, "wrangler.json"), options.wranglerConfig, options.wranglerConfigKeys),
199
- staticIndex ? copyClientOutput(clientDir, options.staticOutputDir ?? createDefaultCloudflareStaticOutputDir(options.rootDir)) : Promise.resolve()
200
- ]);
606
+ const writes = [
607
+ writeCloudflareWranglerConfig({
608
+ outputRoot,
609
+ rootDir: options.rootDir,
610
+ wranglerConfig: options.wranglerConfig,
611
+ wranglerConfigOwnership: options.wranglerConfigOwnership ?? { keys: options.wranglerConfigKeys }
612
+ }),
613
+ options.bundleEntry && staticIndex ? copyClientOutput(clientDir, options.staticOutputDir ?? createDefaultCloudflareStaticOutputDir(options.rootDir)) : Promise.resolve(),
614
+ ...files.map(([fileName, contents]) => writeFile(resolve(outputRoot, fileName), contents, "utf8"))
615
+ ];
616
+ if (options.bundleEntry && workerOutfile) {
617
+ await rm(workerOutfile, {
618
+ force: true,
619
+ recursive: true
620
+ });
621
+ writes.push(bundleEsmEntry(options.bundleEntry, workerOutfile, {
622
+ ...options.bundleOptions,
623
+ rootDir: options.rootDir
624
+ }));
625
+ }
626
+ await Promise.all(writes);
201
627
  }
202
628
  async function writeVercelDeploymentOutput(options) {
203
629
  const { clientDir, staticIndex } = resolveClientOutput(options.rootDir, options.clientOutDir);
204
630
  const outputRoot = options.outputRoot ?? createDefaultVercelOutputRoot(options.rootDir);
205
- const serverDir = resolve(outputRoot, "functions", options.serverFunctionName ?? "__server.func");
631
+ const functionOutput = options.function ?? { kind: "root" };
632
+ const serverFunctionName = functionOutput.kind === "root" ? "__server.func" : functionOutput.name;
633
+ const config = options.config ?? (functionOutput.kind === "root" ? createVercelConfigJson() : {});
634
+ const serverDir = resolve(outputRoot, "functions", serverFunctionName);
206
635
  const serverEntry = resolve(serverDir, "index.mjs");
207
- await rm(serverDir, {
636
+ await mkdir(serverDir, { recursive: true });
637
+ const staleFiles = (await readdir(serverDir)).filter((file) => file !== "node_modules");
638
+ await Promise.all(staleFiles.map((file) => rm(resolve(serverDir, file), {
208
639
  force: true,
209
640
  recursive: true
210
- });
211
- await mkdir(serverDir, { recursive: true });
641
+ })));
212
642
  await Promise.all([
213
- bundleEsmEntry(options.bundleEntry, serverEntry, options.bundleOptions),
214
- writeFile(resolve(serverDir, ".vc-config.json"), `${JSON.stringify(options.functionConfig ?? createNodeFunctionConfig(), null, 2)}\n`, "utf8"),
215
- writeMergedJsonObject(resolve(outputRoot, "config.json"), options.config ?? createVercelConfigJson(), options.configKeys),
643
+ bundleEsmEntry(options.bundleEntry, serverEntry, {
644
+ ...options.bundleOptions,
645
+ rootDir: options.rootDir
646
+ }),
647
+ writeFile(resolve(serverDir, ".vc-config.json"), `${stringifyProviderOutputConfig(options.functionConfig ?? createNodeFunctionConfig())}\n`, "utf8"),
648
+ writeProviderOutputConfig(resolve(outputRoot, "config.json"), config, { keys: options.configKeys }),
216
649
  staticIndex ? copyClientOutput(clientDir, options.staticOutputDir ?? resolve(outputRoot, "static")) : Promise.resolve()
217
650
  ]);
218
651
  }
219
- async function cleanupCloudflareDeploymentOutput(rootDir, cleanup) {
652
+ async function writeNetlifyDeploymentOutput(options) {
653
+ const outputRoot = options.outputRoot ?? createDefaultNetlifyOutputRoot(options.rootDir);
654
+ const functionsRoot = resolve(outputRoot, "functions");
655
+ const functionWrites = (options.functions ?? []).map(async (func) => {
656
+ const outfile = resolveNetlifyFunctionFile(functionsRoot, func.functionName);
657
+ await rm(outfile, {
658
+ force: true,
659
+ recursive: true
660
+ });
661
+ await mkdir(dirname(outfile), { recursive: true });
662
+ await bundleEsmEntry(func.bundleEntry, outfile, {
663
+ ...func.bundleOptions,
664
+ minifyIdentifiers: func.config ? true : func.bundleOptions.minifyIdentifiers,
665
+ rootDir: options.rootDir
666
+ });
667
+ await appendNetlifyFunctionConfig(outfile, func.config);
668
+ });
669
+ await mkdir(outputRoot, { recursive: true });
670
+ await Promise.all([writeProviderOutputConfig(resolve(outputRoot, "config.json"), options.config ?? {}, { keys: options.configKeys }), ...functionWrites]);
671
+ }
672
+ async function cleanupCloudflareDeploymentOutput(rootDir, cleanupInput) {
673
+ const cleanup = typeof cleanupInput === "function" ? await cleanupInput() : cleanupInput;
220
674
  const outputRoot = cleanup.outputRoot ?? createDefaultCloudflareOutputRoot(rootDir);
221
- const writes = [];
222
- if (cleanup.bundleOutfileName) writes.push(rm(resolve(outputRoot, cleanup.bundleOutfileName), {
675
+ const writes = (cleanup.fileNames ?? []).map((fileName) => rm(resolve(outputRoot, fileName), {
223
676
  force: true,
224
677
  recursive: true
225
678
  }));
226
- writes.push(deleteJsonObjectKeysFromFile(resolve(outputRoot, "wrangler.json"), cleanup.wranglerConfigKeys));
679
+ writes.push(writeCloudflareWranglerConfig({
680
+ outputRoot,
681
+ rootDir,
682
+ wranglerConfigOwnership: cleanup.wranglerConfigOwnership
683
+ }));
227
684
  await Promise.all(writes);
685
+ try {
686
+ await rmdir(outputRoot);
687
+ } catch (error) {
688
+ const code = error.code;
689
+ if (code !== "ENOENT" && code !== "ENOTEMPTY") throw error;
690
+ }
228
691
  }
229
692
  async function cleanupVercelDeploymentOutput(rootDir, cleanup) {
230
693
  const outputRoot = cleanup.outputRoot ?? createDefaultVercelOutputRoot(rootDir);
@@ -233,10 +696,23 @@ async function cleanupVercelDeploymentOutput(rootDir, cleanup) {
233
696
  force: true,
234
697
  recursive: true
235
698
  }));
236
- writes.push(deleteJsonObjectKeysFromFile(resolve(outputRoot, "config.json"), cleanup.configKeys));
699
+ writes.push(cleanProviderOutputConfig(resolve(outputRoot, "config.json"), { keys: cleanup.configKeys }));
237
700
  await Promise.all(writes);
238
701
  }
239
- async function writeProviderDeploymentOutputs(options) {
702
+ async function cleanupVercelDeploymentOutputs(rootDir, cleanup) {
703
+ await Promise.all((Array.isArray(cleanup) ? cleanup : [cleanup]).map((item) => cleanupVercelDeploymentOutput(rootDir, item)));
704
+ }
705
+ async function cleanupNetlifyDeploymentOutput(rootDir, cleanup) {
706
+ const outputRoot = cleanup.outputRoot ?? createDefaultNetlifyOutputRoot(rootDir);
707
+ const functionsRoot = resolve(outputRoot, "functions");
708
+ const writes = (cleanup.functionNames ?? []).map((functionName) => rm(resolveNetlifyFunctionFile(functionsRoot, functionName), {
709
+ force: true,
710
+ recursive: true
711
+ }));
712
+ writes.push(cleanProviderOutputConfig(resolve(outputRoot, "config.json"), { keys: cleanup.configKeys }));
713
+ await Promise.all(writes);
714
+ }
715
+ async function writeProviderDeploymentOutputsNow(options) {
240
716
  const writes = [];
241
717
  if (options.cloudflare) writes.push(writeCloudflareDeploymentOutput({
242
718
  ...options.cloudflare,
@@ -244,37 +720,782 @@ async function writeProviderDeploymentOutputs(options) {
244
720
  rootDir: options.rootDir
245
721
  }));
246
722
  else if (options.cleanup?.cloudflare) writes.push(cleanupCloudflareDeploymentOutput(options.rootDir, options.cleanup.cloudflare));
723
+ if (options.netlify) writes.push(writeNetlifyDeploymentOutput({
724
+ ...options.netlify,
725
+ clientOutDir: options.clientOutDir,
726
+ rootDir: options.rootDir
727
+ }));
728
+ else if (options.cleanup?.netlify) writes.push(cleanupNetlifyDeploymentOutput(options.rootDir, options.cleanup.netlify));
247
729
  if (options.vercel) writes.push(writeVercelDeploymentOutput({
248
730
  ...options.vercel,
249
731
  clientOutDir: options.clientOutDir,
250
732
  rootDir: options.rootDir
251
733
  }));
252
- else if (options.cleanup?.vercel) writes.push(cleanupVercelDeploymentOutput(options.rootDir, options.cleanup.vercel));
734
+ else if (options.cleanup?.vercel) {
735
+ const cleanup = typeof options.cleanup.vercel === "function" ? await options.cleanup.vercel() : options.cleanup.vercel;
736
+ if (cleanup) writes.push(cleanupVercelDeploymentOutputs(options.rootDir, cleanup));
737
+ }
253
738
  await Promise.all(writes);
739
+ await options.afterWrite?.();
740
+ }
741
+ async function writeProviderDeploymentOutputs(options) {
742
+ const key = resolve(options.rootDir);
743
+ const write = (providerDeploymentOutputWrites.get(key) ?? Promise.resolve()).catch(() => void 0).then(() => writeProviderDeploymentOutputsNow(options));
744
+ providerDeploymentOutputWrites.set(key, write);
745
+ try {
746
+ await write;
747
+ } finally {
748
+ if (providerDeploymentOutputWrites.get(key) === write) providerDeploymentOutputWrites.delete(key);
749
+ }
254
750
  }
255
751
  //#endregion
256
- //#region ../internal/src/build/vite.ts
257
- function createNoExternalMerger(packageName) {
258
- return (current) => {
259
- if (current === true) return true;
260
- if (!current) return [packageName];
261
- const values = Array.isArray(current) ? current : [current];
262
- return values.includes(packageName) ? values : [...values, packageName];
752
+ //#region ../internal/src/build/paths.ts
753
+ function generatedDirSegments(productName) {
754
+ return [".vitehub", productName];
755
+ }
756
+ function ensureGeneratedDir(rootDir, productName) {
757
+ return resolve$1(rootDir, ...generatedDirSegments(productName));
758
+ }
759
+ function computePackageDir(importMetaUrl) {
760
+ const currentFileDir = dirname$1(fileURLToPath(importMetaUrl));
761
+ return resolve$1(currentFileDir, basename$1(currentFileDir) === "internal" ? "../.." : "..");
762
+ }
763
+ function resolveRuntimeModule$1(packageDir, modulePath) {
764
+ const distFile = resolve$1(packageDir, "dist", `${modulePath}.js`);
765
+ return existsSync(distFile) ? distFile : resolve$1(packageDir, "src", `${modulePath}.ts`);
766
+ }
767
+ function createImportPath(fromFile, targetFile) {
768
+ const importPath = relative$1(realpathIfExists(dirname$1(fromFile)), realpathIfExists(targetFile));
769
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
770
+ }
771
+ function realpathIfExists(path) {
772
+ try {
773
+ return realpathSync.native(path);
774
+ } catch {
775
+ return resolve$1(path);
776
+ }
777
+ }
778
+ //#endregion
779
+ //#region ../internal/src/definition-catalog.ts
780
+ const sourceFilePattern$1 = /\.(?:c|m)?[jt]sx?$/i;
781
+ const declarationFilePattern$1 = /\.d\.(?:c|m)?[jt]sx?$/i;
782
+ const ignoredDirs = new Set([
783
+ "node_modules",
784
+ "dist",
785
+ ".output",
786
+ ".nuxt",
787
+ ".vercel",
788
+ ".git",
789
+ ".vitehub"
790
+ ]);
791
+ function resolveDefinitionScanRoots(rootDir, scanDirs = []) {
792
+ return [...new Set([rootDir, ...scanDirs].filter(Boolean))];
793
+ }
794
+ function createDirectoryDefinitionSource(source, scanDirs, subdir, options = {}) {
795
+ return {
796
+ ...options,
797
+ kind: "directory",
798
+ scanDirs,
799
+ source,
800
+ subdir
263
801
  };
264
802
  }
265
- function isServerEnvironment(name, config) {
266
- return name === "ssr" || config.consumer === "server";
803
+ function createSuffixDefinitionSource(source, roots, pattern, normalizeName, options = {}) {
804
+ return {
805
+ ...options,
806
+ kind: "suffix",
807
+ normalizeName,
808
+ pattern,
809
+ roots,
810
+ source
811
+ };
812
+ }
813
+ function readDirEntries$1(root) {
814
+ try {
815
+ return readdirSync(root, { withFileTypes: true });
816
+ } catch (error) {
817
+ if (error.code === "ENOENT") return [];
818
+ throw error;
819
+ }
820
+ }
821
+ function listMatchingFiles(root, predicate, options = {}) {
822
+ const files = [];
823
+ for (const entry of readDirEntries$1(root)) {
824
+ const absolute = resolve$1(root, entry.name);
825
+ if (entry.isDirectory() && !entry.isSymbolicLink()) {
826
+ if (entry.name.startsWith(".")) continue;
827
+ if (ignoredDirs.has(entry.name)) continue;
828
+ files.push(...listMatchingFiles(absolute, predicate, options));
829
+ continue;
830
+ }
831
+ if (entry.name.startsWith(".") && !options.includeHidden) continue;
832
+ if (entry.isFile() && predicate(entry.name)) files.push(absolute);
833
+ }
834
+ return files.sort();
835
+ }
836
+ function listSourceFiles(root, options = {}) {
837
+ return listMatchingFiles(root, (name) => sourceFilePattern$1.test(name) && !declarationFilePattern$1.test(name), options);
838
+ }
839
+ function normalizePathDefinitionName(rootDir, file) {
840
+ return relative$1(rootDir, file).replace(sourceFilePattern$1, "").replace(/\/index$/i, "");
841
+ }
842
+ function normalizeSuffixDefinitionName(rootDir, file, pattern, options = {}) {
843
+ const normalized = relative$1(rootDir, file).replace(pattern, "");
844
+ if (options.stripPrefix && normalized.startsWith(options.stripPrefix)) return normalized.slice(options.stripPrefix.length);
845
+ return normalized;
846
+ }
847
+ function sortDefinitions(definitions) {
848
+ return [...definitions.values()].sort((left, right) => left.name.localeCompare(right.name));
849
+ }
850
+ function registerDefinition(definitions, definition, sourceLabel) {
851
+ const existing = definitions.get(definition.name);
852
+ if (existing) throw new Error(`Duplicate ${sourceLabel} name "${definition.name}":\n - ${existing.handler}\n - ${definition.handler}`);
853
+ definitions.set(definition.name, definition);
854
+ }
855
+ function mergeDefinitions(feature, ...sources) {
856
+ const definitions = /* @__PURE__ */ new Map();
857
+ for (const source of sources) {
858
+ if (!source) continue;
859
+ for (const definition of source) {
860
+ const existing = definitions.get(definition.name);
861
+ if (existing && existing.handler !== definition.handler) throw new Error(`Duplicate ${feature} name "${definition.name}" from multiple discovery sources:\n - ${existing.handler} (${existing.source ?? "unknown"})\n - ${definition.handler} (${definition.source ?? "unknown"})`);
862
+ if (!existing) definitions.set(definition.name, definition);
863
+ }
864
+ }
865
+ return sortDefinitions(definitions);
866
+ }
867
+ function createDefinition(source, file, name) {
868
+ if (source.createDefinition) return source.createDefinition({
869
+ file,
870
+ name
871
+ });
872
+ return {
873
+ handler: file,
874
+ name,
875
+ source: source.source
876
+ };
877
+ }
878
+ function scanSuffixDefinitions(feature, source) {
879
+ const definitions = /* @__PURE__ */ new Map();
880
+ for (const root of source.roots) for (const file of listMatchingFiles(root, (name) => source.pattern.test(name), { includeHidden: source.includeHidden })) {
881
+ const name = source.normalizeName(root, file);
882
+ if (!name) continue;
883
+ registerDefinition(definitions, createDefinition(source, file, name), feature);
884
+ }
885
+ return sortDefinitions(definitions);
886
+ }
887
+ function scanDirectoryDefinitions(feature, source) {
888
+ const definitions = /* @__PURE__ */ new Map();
889
+ for (const scanDir of source.scanDirs) {
890
+ const directory = resolve$1(scanDir, source.subdir);
891
+ for (const file of listSourceFiles(directory, { includeHidden: source.includeHidden })) {
892
+ const name = source.normalizeName ? source.normalizeName(directory, file) : normalizePathDefinitionName(directory, file);
893
+ if (!name) continue;
894
+ registerDefinition(definitions, createDefinition(source, file, name), feature);
895
+ }
896
+ }
897
+ return sortDefinitions(definitions);
898
+ }
899
+ function discoverDefinitions(feature, sources) {
900
+ return mergeDefinitions(feature, ...sources.map((source) => source.kind === "suffix" ? scanSuffixDefinitions(feature, source) : scanDirectoryDefinitions(feature, source)));
267
901
  }
268
902
  //#endregion
269
- //#region ../internal/src/build/cloudflare.ts
270
- const defaultCloudflareCompatibilityDate = "2026-04-20";
903
+ //#region ../internal/src/source-scanner.ts
904
+ function isQuote(char) {
905
+ return char === "\"" || char === "'" || char === "`";
906
+ }
907
+ function skipQuoted(source, index, controlFlowRegexes = /* @__PURE__ */ new Map()) {
908
+ const quote = source[index];
909
+ if (quote === "`") return skipTemplateLiteral(source, index, controlFlowRegexes);
910
+ index += 1;
911
+ while (index < source.length) {
912
+ if (source[index] === "\\") {
913
+ index += 2;
914
+ continue;
915
+ }
916
+ if (source[index] === quote) return index + 1;
917
+ index += 1;
918
+ }
919
+ return index;
920
+ }
921
+ function skipTemplateLiteral(source, index, controlFlowRegexes) {
922
+ index += 1;
923
+ let expressionDepth = 0;
924
+ let previousSignificant = "";
925
+ while (index < source.length) {
926
+ const char = source[index];
927
+ const next = source[index + 1];
928
+ if (char === "\\") {
929
+ index += 2;
930
+ continue;
931
+ }
932
+ if (expressionDepth === 0) {
933
+ if (char === "`") return index + 1;
934
+ if (char === "$" && next === "{") {
935
+ expressionDepth = 1;
936
+ previousSignificant = "{";
937
+ index += 2;
938
+ continue;
939
+ }
940
+ index += 1;
941
+ continue;
942
+ }
943
+ if (char === "\"" || char === "'") {
944
+ index = skipQuoted(source, index, controlFlowRegexes);
945
+ previousSignificant = "literal";
946
+ continue;
947
+ }
948
+ if (char === "`") {
949
+ index = skipTemplateLiteral(source, index, controlFlowRegexes);
950
+ previousSignificant = "literal";
951
+ continue;
952
+ }
953
+ if (char === "/" && next === "/") {
954
+ index = skipLineComment(source, index);
955
+ continue;
956
+ }
957
+ if (char === "/" && next === "*") {
958
+ index = skipBlockComment(source, index);
959
+ continue;
960
+ }
961
+ if (char === "/" && (isRegexLiteralStart(previousSignificant) || isControlFlowRegexStart(source, index, controlFlowRegexes))) {
962
+ index = skipRegexLiteral(source, index);
963
+ previousSignificant = "/";
964
+ continue;
965
+ }
966
+ if (char === "{") expressionDepth += 1;
967
+ if (char === "}") expressionDepth -= 1;
968
+ previousSignificant = trackSignificant(previousSignificant, char);
969
+ index += 1;
970
+ }
971
+ return index;
972
+ }
973
+ function skipLineComment(source, index) {
974
+ const end = source.indexOf("\n", index + 2);
975
+ return end === -1 ? source.length : end + 1;
976
+ }
977
+ function skipBlockComment(source, index) {
978
+ const end = source.indexOf("*/", index + 2);
979
+ return end === -1 ? source.length : end + 2;
980
+ }
981
+ function isRegexLiteralStart(previousSignificant) {
982
+ const token = previousSignificant.trimEnd();
983
+ if (/^\.[\w$]+$/.test(token)) return false;
984
+ return !token || /[({[=,:!&|?;>+\-*%^~]/.test(token) || /\b(?:await|case|delete|do|else|in|instanceof|return|throw|typeof|void|yield)$/.test(token);
985
+ }
986
+ function findLineCommentStart(source, start, end, controlFlowRegexes) {
987
+ for (let index = start; index <= end;) {
988
+ const char = source[index];
989
+ const next = source[index + 1];
990
+ if (isQuote(char)) {
991
+ index = skipQuoted(source, index, controlFlowRegexes);
992
+ continue;
993
+ }
994
+ if (char === "/" && next === "*") {
995
+ index = skipBlockComment(source, index);
996
+ continue;
997
+ }
998
+ if (char === "/" && next === "/") return index;
999
+ index += 1;
1000
+ }
1001
+ return -1;
1002
+ }
1003
+ function previousCodeIndex(source, index, controlFlowRegexes) {
1004
+ let current = index;
1005
+ while (current >= 0) {
1006
+ while (/\s/.test(source[current] ?? "")) current--;
1007
+ if (source[current] === "/" && source[current - 1] === "*") {
1008
+ const start = source.lastIndexOf("/*", current - 2);
1009
+ if (start === -1) return current;
1010
+ current = start - 1;
1011
+ continue;
1012
+ }
1013
+ const lineStart = source.lastIndexOf("\n", current) + 1;
1014
+ const lineComment = findLineCommentStart(source, lineStart, current, controlFlowRegexes);
1015
+ if (lineComment !== -1 && lineComment <= current) {
1016
+ current = lineStart - 1;
1017
+ continue;
1018
+ }
1019
+ return current;
1020
+ }
1021
+ return current;
1022
+ }
1023
+ function isControlFlowRegexStart(source, index, controlFlowRegexes = /* @__PURE__ */ new Map()) {
1024
+ const cached = controlFlowRegexes.get(index);
1025
+ if (cached !== void 0) return cached;
1026
+ if (controlFlowRegexes.has(index)) return true;
1027
+ controlFlowRegexes.set(index, void 0);
1028
+ try {
1029
+ const closeParen = previousCodeIndex(source, index - 1, controlFlowRegexes);
1030
+ if (source[closeParen] !== ")") {
1031
+ controlFlowRegexes.set(index, false);
1032
+ return false;
1033
+ }
1034
+ for (let current = closeParen; current >= 0; current--) {
1035
+ if (source[current] !== "(") continue;
1036
+ if (findMatching(source, current, "(", ")", controlFlowRegexes) !== closeParen) continue;
1037
+ const head = source.slice(0, current).replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, " ");
1038
+ const result = /(?:^|[^\w$])(?:catch|for|if|while|with)\s*$/.test(head);
1039
+ controlFlowRegexes.set(index, result);
1040
+ return result;
1041
+ }
1042
+ controlFlowRegexes.set(index, false);
1043
+ return false;
1044
+ } catch (error) {
1045
+ controlFlowRegexes.delete(index);
1046
+ throw error;
1047
+ }
1048
+ }
1049
+ function skipRegexLiteral(source, index) {
1050
+ index += 1;
1051
+ while (index < source.length) {
1052
+ const char = source[index];
1053
+ if (char === "\\") {
1054
+ index += 2;
1055
+ continue;
1056
+ }
1057
+ if (char === "[") {
1058
+ index += 1;
1059
+ while (index < source.length) {
1060
+ if (source[index] === "\\") {
1061
+ index += 2;
1062
+ continue;
1063
+ }
1064
+ if (source[index] === "]") break;
1065
+ index += 1;
1066
+ }
1067
+ }
1068
+ if (char === "/") {
1069
+ index += 1;
1070
+ while (/[a-z]/i.test(source[index] ?? "")) index += 1;
1071
+ return index;
1072
+ }
1073
+ index += 1;
1074
+ }
1075
+ return index;
1076
+ }
1077
+ function trackSignificant(previousSignificant, char) {
1078
+ if (/[a-z$]/i.test(char ?? "")) return /[\w$]$/.test(previousSignificant) || previousSignificant === "." ? previousSignificant + char : char ?? "";
1079
+ if (/\s/.test(char ?? "")) return /[\w$]$/.test(previousSignificant) ? `${previousSignificant} ` : previousSignificant;
1080
+ if (!/\s/.test(char ?? "")) return char ?? "";
1081
+ return previousSignificant;
1082
+ }
1083
+ function maskSourceLiterals(source) {
1084
+ const output = source.split("");
1085
+ let previousSignificant = "";
1086
+ const mask = (start, end) => {
1087
+ for (let index = start; index < end; index++) if (output[index] !== "\n" && output[index] !== "\r") output[index] = " ";
1088
+ };
1089
+ for (let index = 0; index < source.length;) {
1090
+ const char = source[index];
1091
+ const next = source[index + 1];
1092
+ let end;
1093
+ if (isQuote(char)) {
1094
+ end = skipQuoted(source, index);
1095
+ previousSignificant = "literal";
1096
+ } else if (char === "/" && next === "/") end = skipLineComment(source, index);
1097
+ else if (char === "/" && next === "*") end = skipBlockComment(source, index);
1098
+ else if (char === "/" && (isRegexLiteralStart(previousSignificant) || isControlFlowRegexStart(source, index))) {
1099
+ end = skipRegexLiteral(source, index);
1100
+ previousSignificant = "/";
1101
+ }
1102
+ if (end !== void 0) {
1103
+ mask(index, end);
1104
+ index = end;
1105
+ continue;
1106
+ }
1107
+ previousSignificant = trackSignificant(previousSignificant, char);
1108
+ index += 1;
1109
+ }
1110
+ return output.join("");
1111
+ }
1112
+ function findMatching(source, index, open, close, controlFlowRegexes = /* @__PURE__ */ new Map()) {
1113
+ let depth = 0;
1114
+ let previousSignificant = "";
1115
+ for (let current = index; current < source.length; current++) {
1116
+ const char = source[current];
1117
+ const next = source[current + 1];
1118
+ if (isQuote(char)) {
1119
+ current = skipQuoted(source, current, controlFlowRegexes) - 1;
1120
+ previousSignificant = "literal";
1121
+ continue;
1122
+ }
1123
+ if (char === "/" && next === "/") {
1124
+ current = skipLineComment(source, current) - 1;
1125
+ continue;
1126
+ }
1127
+ if (char === "/" && next === "*") {
1128
+ current = skipBlockComment(source, current) - 1;
1129
+ continue;
1130
+ }
1131
+ if (char === "/" && (isRegexLiteralStart(previousSignificant) || isControlFlowRegexStart(source, current, controlFlowRegexes))) {
1132
+ current = skipRegexLiteral(source, current) - 1;
1133
+ previousSignificant = "/";
1134
+ continue;
1135
+ }
1136
+ if (char === open) {
1137
+ depth += 1;
1138
+ previousSignificant = char;
1139
+ continue;
1140
+ }
1141
+ if (char === close && !(open === "<" && close === ">" && source[current - 1] === "=")) {
1142
+ depth -= 1;
1143
+ if (depth === 0) return current;
1144
+ previousSignificant = char;
1145
+ continue;
1146
+ }
1147
+ previousSignificant = trackSignificant(previousSignificant, char);
1148
+ }
1149
+ }
1150
+ //#endregion
1151
+ //#region src/discovery.ts
1152
+ const workflowSuffixPattern = /\.workflow\.(?:c|m)?[jt]s$/i;
1153
+ const agentSuffixPattern = /\.agent\.(?:c|m)?[jt]s$/i;
1154
+ const sourceFilePattern = /\.(?:c|m)?[jt]s$/i;
1155
+ const declarationFilePattern = /\.d\.(?:c|m)?[jt]s$/i;
1156
+ const stepFilePattern = /^\d+[.-].*\.(?:c|m)?[jt]s$/i;
1157
+ const folderAgentFilePattern = /^agent\.(?:c|m)?[jt]s$/i;
1158
+ const folderAgentIndexFilePattern = /^index\.(?:c|m)?[jt]s$/i;
1159
+ const legacyFolderAgentFilePattern = /^config\.(?:c|m)?[jt]s$/i;
1160
+ const agentEvalFilePattern = /\.eval\.(?:c|m)?[jt]s$/i;
1161
+ function normalizeSuffixWorkflowName(rootDir, file) {
1162
+ return normalizeSuffixDefinitionName(rootDir, file, workflowSuffixPattern, { stripPrefix: "src/" });
1163
+ }
1164
+ function normalizeSuffixAgentName(rootDir, file) {
1165
+ const name = normalizeSuffixDefinitionName(rootDir, file, agentSuffixPattern, { stripPrefix: "src/" });
1166
+ return name.startsWith("server/") ? void 0 : name;
1167
+ }
1168
+ function readDirEntries(root) {
1169
+ try {
1170
+ return readdirSync(root, { withFileTypes: true });
1171
+ } catch (error) {
1172
+ if (error.code === "ENOENT") return [];
1173
+ throw error;
1174
+ }
1175
+ }
1176
+ function isSourceFile(file) {
1177
+ return sourceFilePattern.test(file) && !declarationFilePattern.test(file);
1178
+ }
1179
+ function findDefineAgentObjectStart(masked, start) {
1180
+ let index = start;
1181
+ while (/\s/.test(masked[index] || "")) index++;
1182
+ if (masked[index] === "<") {
1183
+ let depth = 0;
1184
+ do {
1185
+ if (masked[index] === "<") depth++;
1186
+ else if (masked[index] === ">" && masked[index - 1] !== "=") depth--;
1187
+ index++;
1188
+ } while (index < masked.length && depth > 0);
1189
+ }
1190
+ while (/\s/.test(masked[index] || "")) index++;
1191
+ if (masked[index++] !== "(") return void 0;
1192
+ while (/\s/.test(masked[index] || "")) index++;
1193
+ if (masked[index] === "{") return index;
1194
+ const options = /^([A-Za-z_$][\w$]*)\b/.exec(masked.slice(index))?.[1];
1195
+ if (options) {
1196
+ const declaration = new RegExp(`\\b(?:const|let|var)\\s+${options}\\s*(?::(?:=>|[^=])*?)?=\\s*\\{`).exec(masked);
1197
+ if (declaration) return declaration.index + declaration[0].lastIndexOf("{");
1198
+ }
1199
+ }
1200
+ function agentFactoryNames(source) {
1201
+ const names = new Set(["defineAgent"]);
1202
+ for (const match of source.matchAll(/\bimport\s*\{([^}]+)\}\s*from\s*["']@vite-hub\/agent["']/g)) for (const specifier of match[1].split(",")) {
1203
+ const alias = /^\s*defineAgent\s+as\s+([A-Za-z_$][\w$]*)\s*$/.exec(specifier)?.[1];
1204
+ if (alias) names.add(alias);
1205
+ }
1206
+ return [...names];
1207
+ }
1208
+ function findAgentObjectStart(source, masked) {
1209
+ for (const factory of agentFactoryNames(source)) {
1210
+ const inline = new RegExp(`\\bexport\\s+default\\s+${factory}\\b`).exec(masked);
1211
+ if (inline) return findDefineAgentObjectStart(masked, inline.index + inline[0].length);
1212
+ }
1213
+ const exported = /\bexport\s+default\s+([A-Za-z_$][\w$]*)\b/.exec(masked);
1214
+ if (!exported) return void 0;
1215
+ for (const factory of agentFactoryNames(source)) {
1216
+ const declaration = new RegExp(`\\b(?:const|let|var)\\s+${exported[1]}\\s*(?::(?:=>|[^=])*?)?=\\s*${factory}\\b`).exec(masked);
1217
+ if (declaration) return findDefineAgentObjectStart(masked, declaration.index + declaration[0].length);
1218
+ }
1219
+ }
1220
+ function hasExportedDefineAgent(source, masked) {
1221
+ if (agentFactoryNames(source).some((factory) => new RegExp(`\\bexport\\s+default\\s+${factory}\\b`).test(masked))) return true;
1222
+ const exported = /\bexport\s+default\s+([A-Za-z_$][\w$]*)\b/.exec(masked);
1223
+ return Boolean(exported && agentFactoryNames(source).some((factory) => new RegExp(`\\b(?:const|let|var)\\s+${exported[1]}\\s*(?::(?:=>|[^=])*?)?=\\s*${factory}\\b`).test(masked)));
1224
+ }
1225
+ function findUnmaskedSourceMatch(source, masked, pattern, prefix) {
1226
+ return [...source.matchAll(pattern)].find((match) => prefix.test(masked.slice(match.index)));
1227
+ }
1228
+ function resolveAgentReExport(file, source, masked) {
1229
+ const direct = findUnmaskedSourceMatch(source, masked, /\bexport\s+\{\s*default\s*\}\s+from\s*(["'])([^"']+)\1/g, /^export\s+\{\s*default\s*\}\s+from\b/);
1230
+ const exported = /\bexport\s+default\s+([A-Za-z_$][\w$]*)\b/.exec(masked);
1231
+ const imported = exported && findUnmaskedSourceMatch(source, masked, new RegExp(`\\bimport\\s+${exported[1]}\\s+from\\s*(["'])([^"']+)\\1`, "g"), /^import\b/);
1232
+ const specifier = direct?.[2] || imported?.[2];
1233
+ if (!specifier?.startsWith(".")) return void 0;
1234
+ const target = resolve$1(file, "..", specifier);
1235
+ const extensions = [
1236
+ ".ts",
1237
+ ".mts",
1238
+ ".cts",
1239
+ ".js",
1240
+ ".mjs",
1241
+ ".cjs"
1242
+ ];
1243
+ const sourceTarget = target.replace(/\.(?:c|m)?js$/i, "");
1244
+ for (const candidate of [
1245
+ target,
1246
+ ...extensions.map((extension) => `${target}${extension}`),
1247
+ ...extensions.map((extension) => `${sourceTarget}${extension}`),
1248
+ ...extensions.map((extension) => resolve$1(target, `index${extension}`))
1249
+ ]) if (existsSync(candidate) && statSync(candidate).isFile()) return candidate;
1250
+ }
1251
+ function isFolderAgentImplementationTarget(file) {
1252
+ const fileName = normalize(file).split("/").pop();
1253
+ if (/^definition\.(?:c|m)?[jt]s$/i.test(fileName)) return true;
1254
+ return /^index\.(?:c|m)?[jt]s$/i.test(fileName) && normalize(resolve$1(file, "../..")).split("/").pop() === "definition";
1255
+ }
1256
+ function extractAgentRuntime(file, seen = /* @__PURE__ */ new Set()) {
1257
+ if (seen.has(file)) return void 0;
1258
+ seen.add(file);
1259
+ const source = readFileSync(file, "utf8");
1260
+ const masked = maskSourceLiterals(source);
1261
+ const start = findAgentObjectStart(source, masked);
1262
+ if (start === void 0) {
1263
+ const target = resolveAgentReExport(file, source, masked);
1264
+ if (target) return extractAgentRuntime(target, seen);
1265
+ if (hasExportedDefineAgent(source, masked)) return {};
1266
+ return /\bexport\s+default\s+[A-Za-z_$][\w$]*Agent\b/.test(masked) ? {} : void 0;
1267
+ }
1268
+ let depth = 0;
1269
+ for (let index = start; index < masked.length; index++) if (masked[index] === "{") depth++;
1270
+ else if (masked[index] === "}" && --depth === 0) return {};
1271
+ else if (depth === 1) {
1272
+ const previous = masked.slice(0, index).trimEnd().at(-1);
1273
+ if ((previous === "{" || previous === ",") && /^runtime\s*(?:,|})/.test(masked.slice(index))) return {
1274
+ masked: "runtime",
1275
+ raw: "runtime"
1276
+ };
1277
+ if (!(/^runtime\s*:/.test(masked.slice(index)) || (previous === "{" || previous === ",") && /^["']runtime["']\s*:/.test(source.slice(index)))) continue;
1278
+ const colon = source.indexOf(":", index + 7);
1279
+ let end = colon + 1;
1280
+ let nested = 0;
1281
+ for (; end < masked.length; end++) if ("([{".includes(masked[end] || "")) nested++;
1282
+ else if (")]}".includes(masked[end] || "")) {
1283
+ if (nested === 0) break;
1284
+ nested--;
1285
+ } else if (masked[end] === "," && nested === 0) break;
1286
+ return {
1287
+ masked: masked.slice(colon + 1, end).trim(),
1288
+ raw: source.slice(colon + 1, end).trim()
1289
+ };
1290
+ }
1291
+ return {};
1292
+ }
1293
+ function extractAgentWorkflowName(file, fallbackName) {
1294
+ const runtime = extractAgentRuntime(file);
1295
+ if (!runtime) return void 0;
1296
+ const workflowPattern = /^(?:(?:\/\*[\s\S]*?\*\/|\/\/[^\n]*(?:\n|$))\s*)*workflow\s*\(\s*(?:(?:\/\*[\s\S]*?\*\/|\/\/[^\n]*(?:\n|$))\s*)*(["'`])([^"'`]+)\1\s*(?:(?:\/\*[\s\S]*?\*\/|\/\/[^\n]*(?:\n|$))\s*)*\)(?:\s*(?:\/\*[\s\S]*?\*\/|\/\/[^\n]*(?:\n|$)))*\s*$/;
1297
+ const match = workflowPattern.exec(runtime.raw || "");
1298
+ if (match) return match[2] || fallbackName;
1299
+ if (/^false(?:\s+as\s+const)?$/.test(runtime.masked || "")) return void 0;
1300
+ const shorthand = /^([A-Za-z_$][\w$]*)$/.exec(runtime.masked || "")?.[1];
1301
+ if (shorthand) {
1302
+ const source = readFileSync(file, "utf8");
1303
+ const masked = maskSourceLiterals(source);
1304
+ const declaration = new RegExp(`\\b(?:const|let|var)\\s+${shorthand}\\s*(?::(?:=>|[^=])*?)?=\\s*`).exec(masked);
1305
+ if (declaration) {
1306
+ const start = declaration.index + declaration[0].length;
1307
+ let end = start;
1308
+ let depth = 0;
1309
+ for (; end < masked.length; end++) if ("([{".includes(masked[end] || "")) depth++;
1310
+ else if (")]}".includes(masked[end] || "")) depth--;
1311
+ else if (depth === 0 && (masked[end] === ";" || masked[end] === "\n")) break;
1312
+ const initializer = source.slice(start, end).trim();
1313
+ if (/^false(?:\s+as\s+const)?$/.test(masked.slice(start, end).trim())) return void 0;
1314
+ const shorthandWorkflow = workflowPattern.exec(initializer);
1315
+ if (shorthandWorkflow) return shorthandWorkflow[2] || fallbackName;
1316
+ }
1317
+ }
1318
+ return fallbackName;
1319
+ }
1320
+ function toAgentWorkflowDefinition(file, fallbackName) {
1321
+ const name = extractAgentWorkflowName(file, fallbackName);
1322
+ return name ? {
1323
+ agentIdentity: fallbackName,
1324
+ handler: file,
1325
+ name,
1326
+ source: "agent-workflow"
1327
+ } : void 0;
1328
+ }
1329
+ function isWorkflowFolder(directory) {
1330
+ return readDirEntries(directory).some((entry) => entry.isFile() && (entry.name.toLowerCase().startsWith("index.") || stepFilePattern.test(entry.name)) && isSourceFile(entry.name));
1331
+ }
1332
+ function findWorkflowFolders(workflowsDir) {
1333
+ const folders = [];
1334
+ for (const entry of readDirEntries(workflowsDir)) {
1335
+ if (!entry.isDirectory() || entry.isSymbolicLink() || entry.name.startsWith(".")) continue;
1336
+ const directory = resolve$1(workflowsDir, entry.name);
1337
+ if (isWorkflowFolder(directory)) {
1338
+ folders.push(directory);
1339
+ continue;
1340
+ }
1341
+ folders.push(...findWorkflowFolders(directory));
1342
+ }
1343
+ return folders.sort();
1344
+ }
1345
+ function hasFolderAgentDefinition(directory) {
1346
+ return readDirEntries(directory).some((entry) => entry.isFile() && (folderAgentFilePattern.test(entry.name) || folderAgentIndexFilePattern.test(entry.name)) && isSourceFile(entry.name));
1347
+ }
1348
+ function findFolderAgentFiles(agentsDir) {
1349
+ const files = [];
1350
+ for (const entry of readDirEntries(agentsDir)) {
1351
+ if (entry.name.startsWith(".")) continue;
1352
+ const absolute = resolve$1(agentsDir, entry.name);
1353
+ if (entry.isDirectory() && !entry.isSymbolicLink()) {
1354
+ if ((entry.name === "workspace" || entry.name === "skills") && hasFolderAgentDefinition(agentsDir)) continue;
1355
+ files.push(...findFolderAgentFiles(absolute));
1356
+ continue;
1357
+ }
1358
+ if (entry.isFile() && (folderAgentFilePattern.test(entry.name) || folderAgentIndexFilePattern.test(entry.name)) && !(folderAgentIndexFilePattern.test(entry.name) && readDirEntries(agentsDir).some((sibling) => sibling.isFile() && folderAgentFilePattern.test(sibling.name))) && isSourceFile(entry.name)) files.push(absolute);
1359
+ }
1360
+ return files.sort();
1361
+ }
1362
+ function discoverSuffixAgentWorkflowDefinitions(roots) {
1363
+ return discoverDefinitions("agent workflow", [createSuffixDefinitionSource("agent-workflow", roots, agentSuffixPattern, normalizeSuffixAgentName, { createDefinition: ({ file, name }) => ({
1364
+ handler: file,
1365
+ name,
1366
+ source: "agent-workflow"
1367
+ }) })]).flatMap((definition) => {
1368
+ const workflowDefinition = toAgentWorkflowDefinition(definition.handler, definition.name);
1369
+ return workflowDefinition ? [workflowDefinition] : [];
1370
+ });
1371
+ }
1372
+ function discoverFlatServerAgentWorkflowDefinitions(scanDirs) {
1373
+ const folderAgentFiles = scanDirs.flatMap((scanDir) => findFolderAgentFiles(resolve$1(scanDir, "agents")));
1374
+ const folderAgentDirs = new Set(folderAgentFiles.map((file) => normalize(resolve$1(file, ".."))));
1375
+ const folderAgentTargets = new Set(folderAgentFiles.flatMap((file) => {
1376
+ const source = readFileSync(file, "utf8");
1377
+ const target = resolveAgentReExport(file, source, maskSourceLiterals(source));
1378
+ const relativeTarget = target && normalize(relative(resolve$1(file, ".."), target));
1379
+ return target && relativeTarget && relativeTarget !== ".." && !relativeTarget.startsWith("../") && isFolderAgentImplementationTarget(target) ? [normalize(target)] : [];
1380
+ }));
1381
+ return discoverDefinitions("agent workflow", [createDirectoryDefinitionSource("agent-workflow", scanDirs, "agents", {
1382
+ normalizeName(directory, file) {
1383
+ const fileName = normalize(file).split("/").pop();
1384
+ const parent = normalize(resolve$1(file, ".."));
1385
+ const name = normalizePathDefinitionName(directory, file);
1386
+ if (folderAgentTargets.has(normalize(file))) return void 0;
1387
+ if ([...folderAgentDirs].some((agentDir) => {
1388
+ const path = normalize(relative(agentDir, file));
1389
+ return path === "workspace" || path.startsWith("workspace/") || path === "skills" || path.startsWith("skills/");
1390
+ })) return void 0;
1391
+ if (folderAgentFilePattern.test(fileName) && parent !== normalize(directory) || legacyFolderAgentFilePattern.test(fileName) || agentEvalFilePattern.test(fileName)) return;
1392
+ if (parent !== normalize(directory) && hasFolderAgentDefinition(parent) && extractAgentWorkflowName(file, "__vitehub_agent_workflow__") === void 0) return;
1393
+ if (/^index\.(?:c|m)?[jt]s$/i.test(fileName) && hasFolderAgentDefinition(resolve$1(file, ".."))) return;
1394
+ return name;
1395
+ },
1396
+ createDefinition: ({ file, name }) => ({
1397
+ handler: file,
1398
+ name,
1399
+ source: "agent-workflow"
1400
+ })
1401
+ })]).flatMap((definition) => {
1402
+ const workflowDefinition = toAgentWorkflowDefinition(definition.handler, definition.name);
1403
+ return workflowDefinition ? [workflowDefinition] : [];
1404
+ });
1405
+ }
1406
+ function discoverConfiguredServerAgentWorkflowDefinitions(scanDirs) {
1407
+ const definitions = /* @__PURE__ */ new Map();
1408
+ for (const scanDir of scanDirs) {
1409
+ const agentsDir = resolve$1(scanDir, "agents");
1410
+ const files = findFolderAgentFiles(agentsDir);
1411
+ const targets = new Set(files.flatMap((file) => {
1412
+ const source = readFileSync(file, "utf8");
1413
+ const target = resolveAgentReExport(file, source, maskSourceLiterals(source));
1414
+ const relativeTarget = target && normalize(relative(resolve$1(file, ".."), target));
1415
+ return target && relativeTarget && relativeTarget !== ".." && !relativeTarget.startsWith("../") ? [normalize(target)] : [];
1416
+ }));
1417
+ for (const file of files) {
1418
+ if (targets.has(normalize(file))) continue;
1419
+ const name = normalize(relative(agentsDir, resolve$1(file, "..")));
1420
+ if (!name || name === ".") continue;
1421
+ const definition = toAgentWorkflowDefinition(file, name);
1422
+ if (definition) registerDefinition(definitions, definition, "agent workflow");
1423
+ }
1424
+ }
1425
+ return sortDefinitions(definitions);
1426
+ }
1427
+ function discoverAgentWorkflowDefinitions(roots, serverScanDirs) {
1428
+ return mergeDefinitions("agent workflow", discoverSuffixAgentWorkflowDefinitions(roots), discoverFlatServerAgentWorkflowDefinitions(serverScanDirs), discoverConfiguredServerAgentWorkflowDefinitions(serverScanDirs));
1429
+ }
1430
+ function discoverWorkflowFolders(scanDirs, source) {
1431
+ const definitions = /* @__PURE__ */ new Map();
1432
+ for (const scanDir of scanDirs) {
1433
+ const workflowsDir = resolve$1(scanDir, "workflows");
1434
+ for (const directory of findWorkflowFolders(workflowsDir)) {
1435
+ const files = readDirEntries(directory).filter((entry) => entry.isFile() && isSourceFile(entry.name)).map((entry) => resolve$1(directory, entry.name)).sort();
1436
+ const index = files.find((file) => /\/index\.(?:c|m)?[jt]s$/i.test(normalize(file)));
1437
+ const steps = files.filter((file) => stepFilePattern.test(file.split("/").pop()));
1438
+ if (!index && steps.length === 0) continue;
1439
+ const relativeName = normalize(relative(workflowsDir, directory));
1440
+ registerDefinition(definitions, {
1441
+ handler: index || directory,
1442
+ name: relativeName,
1443
+ source,
1444
+ steps
1445
+ }, "workflow");
1446
+ }
1447
+ }
1448
+ return sortDefinitions(definitions);
1449
+ }
1450
+ function discoverFlatServerWorkflowDefinitions(scanDirs, source) {
1451
+ return discoverDefinitions("workflow", [createDirectoryDefinitionSource("server-workflows", scanDirs, "workflows", {
1452
+ normalizeName(directory, file) {
1453
+ const normalizedFile = normalize(file);
1454
+ const parent = normalize(resolve$1(file, ".."));
1455
+ if (parent !== normalize(directory) && statSync(parent).isDirectory() && isWorkflowFolder(parent)) return;
1456
+ const fileName = normalizedFile.split("/").pop();
1457
+ if (fileName.toLowerCase().startsWith("index.") || stepFilePattern.test(fileName)) return;
1458
+ return normalizePathDefinitionName(directory, file);
1459
+ },
1460
+ createDefinition: ({ file, name }) => ({
1461
+ handler: file,
1462
+ name,
1463
+ source
1464
+ })
1465
+ })]);
1466
+ }
1467
+ function discoverWorkflowDefinitions(options) {
1468
+ if (options.mode === "server-workflows") return mergeDefinitions("workflow", discoverFlatServerWorkflowDefinitions(options.scanDirs, "server-workflows"), discoverWorkflowFolders(options.scanDirs, "server-workflows"));
1469
+ const roots = resolveDefinitionScanRoots(options.rootDir, options.scanDirs);
1470
+ const serverScanDirs = roots.map((root) => resolve$1(root, "server"));
1471
+ const folderDefinitions = discoverWorkflowFolders(serverScanDirs, "server-workflows");
1472
+ return mergeDefinitions("workflow", discoverDefinitions("workflow", [createSuffixDefinitionSource("vite-suffix", roots, workflowSuffixPattern, normalizeSuffixWorkflowName, { createDefinition: ({ file, name }) => ({
1473
+ handler: file,
1474
+ name,
1475
+ source: "vite-suffix"
1476
+ }) })]), discoverFlatServerWorkflowDefinitions(serverScanDirs, "server-workflows"), folderDefinitions, discoverAgentWorkflowDefinitions(roots, serverScanDirs));
1477
+ }
271
1478
  //#endregion
272
1479
  //#region src/internal/vite-build.ts
273
1480
  const workflowPackageName = "@vite-hub/workflow";
274
1481
  const productName = "workflow";
275
1482
  const generatedRegistryFileName = "registry.mjs";
1483
+ const cloudflareWorkflowWranglerConfigKeys = [
1484
+ "compatibility_date",
1485
+ "compatibility_flags",
1486
+ "main",
1487
+ "observability",
1488
+ "workflows"
1489
+ ];
1490
+ const cloudflareWorkflowWrapperImport = `import worker, { runViteHubWorkflowDefinition } from "./worker.mjs"`;
276
1491
  const packageDir = computePackageDir(import.meta.url);
277
1492
  const resolveRuntimeModule = (modulePath) => resolveRuntimeModule$1(packageDir, modulePath);
1493
+ const nodeBuiltinExternals = [...new Set([
1494
+ "node:*",
1495
+ ...builtinModules,
1496
+ ...builtinModules.map((module) => `node:${module}`)
1497
+ ])];
1498
+ const optionalAgentRuntimeExternals = ["@vite-hub/workspace", "@vite-hub/workspace/*"];
278
1499
  const WORKFLOW_ENTRY_BASE_NAMES = [
279
1500
  "server.ts",
280
1501
  "server.mts",
@@ -291,6 +1512,119 @@ const WORKFLOW_PRIORITY_NAMES = [
291
1512
  "server-workflow.js",
292
1513
  "server-workflow.mjs"
293
1514
  ];
1515
+ const folderAgentEntryPattern = /^(?:agent|index)\.(?:c|m)?[jt]s$/i;
1516
+ async function loadVercelWorkflowBuilders() {
1517
+ try {
1518
+ const require = createRequire(import.meta.url);
1519
+ require.resolve("workflow");
1520
+ require.resolve("@workflow/builders");
1521
+ } catch (error) {
1522
+ if (error.code === "MODULE_NOT_FOUND") return void 0;
1523
+ throw error;
1524
+ }
1525
+ return await import("@workflow/builders");
1526
+ }
1527
+ async function createVercelWorkflowTransformPlugin(rootDir) {
1528
+ const builders = await loadVercelWorkflowBuilders();
1529
+ if (!builders) return void 0;
1530
+ return builders.createSwcPlugin({
1531
+ mode: "workflow",
1532
+ projectRoot: rootDir
1533
+ });
1534
+ }
1535
+ async function withVercelWorkflowPackageLink(rootDir, run) {
1536
+ const target = join(rootDir, "node_modules", "workflow");
1537
+ if (existsSync(target)) return await run();
1538
+ const source = dirname(dirname(dirname(createRequire(import.meta.url).resolve("workflow/internal/builtins"))));
1539
+ await mkdir(dirname(target), { recursive: true });
1540
+ let ownsLink = false;
1541
+ try {
1542
+ await symlink(source, target, process.platform === "win32" ? "junction" : "dir");
1543
+ ownsLink = true;
1544
+ } catch (error) {
1545
+ if (error.code !== "EEXIST") throw error;
1546
+ }
1547
+ try {
1548
+ return await run();
1549
+ } finally {
1550
+ if (ownsLink) await rm(target, { force: true });
1551
+ }
1552
+ }
1553
+ async function buildVercelNativeWorkflowOutput(rootDir, definitions, aliases = {}) {
1554
+ const builders = await loadVercelWorkflowBuilders();
1555
+ if (!definitions.length) return;
1556
+ const definitionDirs = [...new Set(definitions.map((definition) => dirname(definition.handler)))];
1557
+ let hasNativeEntry = false;
1558
+ const visited = /* @__PURE__ */ new Set();
1559
+ const visit = (file) => {
1560
+ if (visited.has(file)) return;
1561
+ visited.add(file);
1562
+ if (!existsSync(file)) return;
1563
+ if (statSync(file).isDirectory()) {
1564
+ for (const entry of readdirSync(file, { withFileTypes: true })) {
1565
+ if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
1566
+ if (entry.isDirectory() || /\.(?:c|m)?[jt]sx?$/.test(entry.name)) visit(join(file, entry.name));
1567
+ }
1568
+ return;
1569
+ }
1570
+ const source = readFileSync(file, "utf8");
1571
+ if (/^\s*["']use workflow["'];?/m.test(source)) {
1572
+ if (!definitionDirs.some((definitionDir) => {
1573
+ const path = relative(definitionDir, file);
1574
+ return !path.startsWith("..") && !isAbsolute(path);
1575
+ })) throw new Error(`Native Vercel workflow entry ${JSON.stringify(relative(rootDir, file))} must be colocated with its discovered workflow definition.`);
1576
+ hasNativeEntry = true;
1577
+ }
1578
+ for (const match of source.matchAll(/(?:from\s*|import\s*(?:\(\s*)?)["']([^"']+)["']/g)) {
1579
+ const specifier = match[1];
1580
+ const alias = Object.entries(aliases).find(([find]) => specifier === find || specifier.startsWith(`${find}/`));
1581
+ const imported = specifier.startsWith(".") ? resolve(dirname(file), specifier) : alias ? resolve(rootDir, specifier.replace(alias[0], alias[1])) : void 0;
1582
+ if (!imported) continue;
1583
+ const extensions = [
1584
+ ".ts",
1585
+ ".tsx",
1586
+ ".mts",
1587
+ ".cts",
1588
+ ".js",
1589
+ ".jsx",
1590
+ ".mjs",
1591
+ ".cjs"
1592
+ ];
1593
+ const sourceBase = imported.replace(/\.(?:m|c)?js$/, "");
1594
+ const candidate = [
1595
+ imported,
1596
+ ...extensions.map((extension) => `${sourceBase}${extension}`),
1597
+ ...extensions.map((extension) => join(imported, `index${extension}`))
1598
+ ].find((path) => existsSync(path) && statSync(path).isFile());
1599
+ if (candidate) visit(candidate);
1600
+ }
1601
+ };
1602
+ for (const definition of definitions) visit(definition.handler);
1603
+ if (!hasNativeEntry) return;
1604
+ if (!builders) throw new Error("Native Vercel workflows require the optional workflow and @workflow/builders peer dependencies.");
1605
+ const outputConfigFile = resolve(rootDir, ".vercel", "output", "config.json");
1606
+ const viteHubConfig = JSON.parse(await readFile(outputConfigFile, "utf8"));
1607
+ const builder = new builders.VercelBuildOutputAPIBuilder({
1608
+ buildTarget: "vercel-build-output-api",
1609
+ dirs: definitionDirs,
1610
+ projectRoot: rootDir,
1611
+ stepsBundlePath: "./.well-known/workflow/v1/step.js",
1612
+ webhookBundlePath: "./.well-known/workflow/v1/webhook.js",
1613
+ workflowsBundlePath: "./.well-known/workflow/v1/flow.js",
1614
+ workingDir: rootDir
1615
+ });
1616
+ await withVercelWorkflowPackageLink(rootDir, async () => await builder.build());
1617
+ const workflowConfig = JSON.parse(await readFile(outputConfigFile, "utf8"));
1618
+ await writeFile(outputConfigFile, `${JSON.stringify({
1619
+ ...workflowConfig,
1620
+ ...viteHubConfig,
1621
+ routes: [...workflowConfig.routes ?? [], ...viteHubConfig.routes ?? []]
1622
+ }, null, 2)}\n`, "utf8");
1623
+ }
1624
+ function isFolderAgentEntry(file) {
1625
+ if (!folderAgentEntryPattern.test(basename(file))) return false;
1626
+ return !(basename(file).toLowerCase().startsWith("agent.") && basename(dirname(file)) === "agents" && basename(dirname(dirname(file))) === "server");
1627
+ }
294
1628
  function resolveWorkflowUserAppEntry(rootDir) {
295
1629
  return resolveUserAppEntry(rootDir, { names: getViteMode() === VITEHUB_MODES.workflow ? [...WORKFLOW_PRIORITY_NAMES, ...WORKFLOW_ENTRY_BASE_NAMES] : [...WORKFLOW_ENTRY_BASE_NAMES] });
296
1630
  }
@@ -310,10 +1644,158 @@ const providerEntrySpecs = [{
310
1644
  function resolveWorkflowConfig(workflow, hosting) {
311
1645
  return normalizeWorkflowOptions(workflow, { hosting }) ?? false;
312
1646
  }
1647
+ function renderRegistryImport(registryFile, file) {
1648
+ return `import(${JSON.stringify(createImportPath(registryFile, file))})`;
1649
+ }
1650
+ function resolveAgentWorkspaceSourceRoot(file) {
1651
+ const workspaceDirectory = join(dirname(file), "workspace");
1652
+ return existsSync(workspaceDirectory) && statSync(workspaceDirectory).isDirectory() ? workspaceDirectory : dirname(file);
1653
+ }
1654
+ function resolveInstructionFile(file, seen) {
1655
+ if (seen.has(file)) throw new Error(`[vitehub] Circular instruction import: ${file}.`);
1656
+ seen.add(file);
1657
+ try {
1658
+ const replaceImports = (content) => content.replace(/@(\.\.?\/\S+)/g, (_token, rawSpecifier) => {
1659
+ const trailing = rawSpecifier.match(/[.,;:!?)]*$/)?.[0] || "";
1660
+ const specifier = rawSpecifier.slice(0, rawSpecifier.length - trailing.length);
1661
+ return `${resolveInstructionFile(resolve(dirname(file), specifier), seen)}${trailing}`;
1662
+ });
1663
+ let fence;
1664
+ return readFileSync(file, "utf8").split(/(?<=\n)/).map((line) => {
1665
+ const marker = line.match(/^\s*(```|~~~)/)?.[1];
1666
+ if (marker) {
1667
+ fence = fence === marker ? void 0 : fence || marker;
1668
+ return line;
1669
+ }
1670
+ if (fence) return line;
1671
+ if (/^(?: {4}|\t)/.test(line)) return line;
1672
+ return line.split(/(`+[^`]*`+)/g).map((segment, index) => index % 2 ? segment : replaceImports(segment)).join("");
1673
+ }).join("");
1674
+ } finally {
1675
+ seen.delete(file);
1676
+ }
1677
+ }
1678
+ function readAgentInstructions(file) {
1679
+ const instructions = join(dirname(file), "instructions.md");
1680
+ return existsSync(instructions) && statSync(instructions).isFile() ? resolveInstructionFile(instructions, /* @__PURE__ */ new Set()) : void 0;
1681
+ }
1682
+ function readAgentSkills(file) {
1683
+ if (!isFolderAgentEntry(file)) return void 0;
1684
+ const root = dirname(file);
1685
+ const skillsRoot = join(root, "skills");
1686
+ if (!existsSync(skillsRoot) || !statSync(skillsRoot).isDirectory()) return void 0;
1687
+ const sources = {};
1688
+ const visit = (directory) => {
1689
+ for (const entry of readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
1690
+ const target = join(directory, entry.name);
1691
+ if (entry.isDirectory()) visit(target);
1692
+ else if (entry.isFile()) {
1693
+ const workspacePath = relative(root, target).replace(/\\/g, "/");
1694
+ sources[`__vitehubAgentSkill:${workspacePath}`] = {
1695
+ content: readFileSync(target).toString("base64"),
1696
+ encoding: "base64",
1697
+ materialize: "build",
1698
+ mount: "",
1699
+ workspacePath
1700
+ };
1701
+ }
1702
+ }
1703
+ };
1704
+ visit(skillsRoot);
1705
+ return Object.keys(sources).length ? sources : void 0;
1706
+ }
1707
+ function renderAgentWorkflowRegistryEntry(registryFile, definition) {
1708
+ return [
1709
+ ` ${JSON.stringify(definition.name)}: async () => {`,
1710
+ ` const cached = registryEntryCache.get(${JSON.stringify(definition.name)})`,
1711
+ " if (cached) return cached",
1712
+ ` const loaded = await ${renderRegistryImport(registryFile, definition.handler)}`,
1713
+ ` const agent = agentWithColocatedSkills(workspaceAgentWithSourceRoot("default" in loaded ? loaded.default : loaded, ${JSON.stringify(resolveAgentWorkspaceSourceRoot(definition.handler))}, ${JSON.stringify(readAgentInstructions(definition.handler))}), ${JSON.stringify(readAgentSkills(definition.handler))})`,
1714
+ ` const entry = { handler: async (context) => await runAgentWorkflowDefinition(agent, { ...context, payload: { ...context.payload, agentIdentity: context.payload?.agentIdentity || { name: ${JSON.stringify(definition.agentIdentity || definition.name)} } } }, runAgentInline) }`,
1715
+ ` registryEntryCache.set(${JSON.stringify(definition.name)}, entry)`,
1716
+ " return entry",
1717
+ " },"
1718
+ ].join("\n");
1719
+ }
1720
+ function renderWorkflowRegistryEntry(registryFile, definition) {
1721
+ if (definition.source === "agent-workflow") return renderAgentWorkflowRegistryEntry(registryFile, definition);
1722
+ if (!definition.steps?.length) return ` ${JSON.stringify(definition.name)}: async () => ${renderRegistryImport(registryFile, definition.handler)},`;
1723
+ const workflowDirectory = /\.(?:c|m)?[jt]s$/i.test(definition.handler) ? dirname(definition.handler) : definition.handler;
1724
+ const stepImports = definition.steps.map((step) => {
1725
+ const stepName = relative(workflowDirectory, step);
1726
+ return `{ name: ${JSON.stringify(stepName)}, run: (await ${renderRegistryImport(registryFile, step)}).default }`;
1727
+ });
1728
+ const hasIndex = /\.(?:c|m)?[jt]s$/i.test(definition.handler);
1729
+ const indexImport = hasIndex ? `const index = await ${renderRegistryImport(registryFile, definition.handler)}` : "";
1730
+ const handler = hasIndex ? `index.default?.handler ? index.default : takeInlineWorkflowDefinitionForModule(${JSON.stringify(definition.name)}, index) || { handler: index.default }` : "{ handler: async (context) => { let value = context.payload; for (const step of Object.values(context.steps || {})) value = await step(value); return value } }";
1731
+ return [
1732
+ ` ${JSON.stringify(definition.name)}: async () => {`,
1733
+ ` const cached = registryEntryCache.get(${JSON.stringify(definition.name)})`,
1734
+ " if (cached) return cached",
1735
+ indexImport ? ` ${indexImport}` : "",
1736
+ ` const steps = [${stepImports.join(", ")}]`,
1737
+ ` const definition = ${handler}`,
1738
+ " const entry = {",
1739
+ " ...definition,",
1740
+ " options: { ...definition.options, rootStep: false },",
1741
+ " handler: async (context) => {",
1742
+ " const workflowSteps = createWorkflowSteps(context, steps)",
1743
+ " return await definition.handler({ ...context, steps: workflowSteps })",
1744
+ " },",
1745
+ " }",
1746
+ ` registryEntryCache.set(${JSON.stringify(definition.name)}, entry)`,
1747
+ " return entry",
1748
+ " },"
1749
+ ].filter(Boolean).join("\n");
1750
+ }
1751
+ function createWorkflowRegistryContents(registryFile, definitions, importBases = {}) {
1752
+ const agentImportBase = importBases.agent ?? "@vite-hub/agent";
1753
+ const workflowImportBase = importBases.workflow ?? "@vite-hub/workflow";
1754
+ const needsWorkflowRuntime = definitions.some((definition) => definition.steps?.length);
1755
+ const needsAgentRuntime = definitions.some((definition) => definition.source === "agent-workflow");
1756
+ const needsRegistryEntryCache = needsWorkflowRuntime || needsAgentRuntime;
1757
+ const installAgentWorkflowRuntime = needsAgentRuntime && importBases.workflow;
1758
+ const workspaceDependencyRuntimeImports = importBases.workspace ? importBases.workspaceDependencies : void 0;
1759
+ const imports = [
1760
+ ...needsAgentRuntime ? [
1761
+ `import { runAgentInline } from ${JSON.stringify(agentImportBase)}`,
1762
+ `import { agentWithColocatedSkills, runAgentWorkflowDefinition, workspaceAgentWithSourceRoot } from ${JSON.stringify(`${agentImportBase}/runtime/workflow`)}`,
1763
+ ...installAgentWorkflowRuntime ? [`import { setAgentWorkflowRuntimeLoaders } from ${JSON.stringify(`${agentImportBase}/server/internal`)}`] : []
1764
+ ] : [],
1765
+ ...needsWorkflowRuntime ? [`import { createWorkflowSteps } from ${JSON.stringify(`${workflowImportBase}/runtime/execute`)}`, `import { takeInlineWorkflowDefinitionForModule } from ${JSON.stringify(`${workflowImportBase}/runtime/state`)}`] : [],
1766
+ ...workspaceDependencyRuntimeImports ? [`import { setWorkspaceDependencyRuntimeLoaders } from ${JSON.stringify(`${importBases.workspace}/runtime`)}`] : []
1767
+ ];
1768
+ return [
1769
+ ...imports,
1770
+ imports.length ? "" : "",
1771
+ ...installAgentWorkflowRuntime ? [
1772
+ "setAgentWorkflowRuntimeLoaders({",
1773
+ ` state: () => import(${JSON.stringify(`${importBases.workflow}/runtime/state`)}),`,
1774
+ ` workflow: () => import(${JSON.stringify(importBases.workflow)}),`,
1775
+ "})",
1776
+ ""
1777
+ ] : [],
1778
+ ...workspaceDependencyRuntimeImports ? [
1779
+ "setWorkspaceDependencyRuntimeLoaders({",
1780
+ ...workspaceDependencyRuntimeImports.sandbox ? [` sandbox: () => import(${JSON.stringify(workspaceDependencyRuntimeImports.sandbox)}),`] : [],
1781
+ ...workspaceDependencyRuntimeImports.sandboxRuntimeState ? [` sandboxRuntimeState: () => import(${JSON.stringify(workspaceDependencyRuntimeImports.sandboxRuntimeState)}),`] : [],
1782
+ ...workspaceDependencyRuntimeImports.shellWorkspace ? [` shellWorkspace: () => import(${JSON.stringify(workspaceDependencyRuntimeImports.shellWorkspace)}),`] : [],
1783
+ "})",
1784
+ ""
1785
+ ] : [],
1786
+ ...needsRegistryEntryCache ? ["const registryEntryCache = new Map()", ""] : [],
1787
+ "const registry = {",
1788
+ ...definitions.map((definition) => renderWorkflowRegistryEntry(registryFile, definition)),
1789
+ "}",
1790
+ "",
1791
+ "export default registry",
1792
+ ""
1793
+ ].join("\n");
1794
+ }
313
1795
  function renderCloudflareWorkerWrapper(definitions) {
314
1796
  return [
315
1797
  definitions.length ? `import { WorkflowEntrypoint, waitUntil as viteHubWaitUntil } from "cloudflare:workers"` : `import { waitUntil as viteHubWaitUntil } from "cloudflare:workers"`,
316
- `import worker, { runViteHubWorkflowDefinition } from "./worker.mjs"`,
1798
+ cloudflareWorkflowWrapperImport,
317
1799
  "",
318
1800
  ...definitions.map((definition) => {
319
1801
  return [
@@ -336,8 +1818,13 @@ function renderCloudflareWorkerWrapper(definitions) {
336
1818
  ""
337
1819
  ].join("\n");
338
1820
  }
339
- function renderProviderEntry(spec, entryFile, userAppEntry, serializedWorkflowConfig) {
340
- const imports = [`import { ${spec.factory} } from ${JSON.stringify(createImportPath(entryFile, resolveRuntimeModule(spec.runtimeModule)))}`, `import workflowRegistry from ${JSON.stringify(`./${generatedRegistryFileName}`)}`];
1821
+ function renderProviderEntry(spec, entryFile, userAppEntry, serializedWorkflowConfig, framework) {
1822
+ const installVercelWorkflowRuntime = spec.name === "vercel" && framework;
1823
+ const imports = [
1824
+ `import { ${spec.factory}${installVercelWorkflowRuntime ? ", setVercelWorkflowRuntimeModules" : ""} } from ${JSON.stringify(createImportPath(entryFile, resolveRuntimeModule(spec.runtimeModule)))}`,
1825
+ `import workflowRegistry from ${JSON.stringify(`./${generatedRegistryFileName}`)}`,
1826
+ ...installVercelWorkflowRuntime ? [`import * as workflowApi from "workflow/api"`, `import * as workflowRuntime from "workflow/runtime"`] : []
1827
+ ];
341
1828
  if (spec.name === "cloudflare") imports.push(`import { runCloudflareWorkflow } from ${JSON.stringify(createImportPath(entryFile, resolveRuntimeModule("runtime/cloudflare-runner")))}`);
342
1829
  if (userAppEntry) imports.push(`import workflowApp from ${JSON.stringify(createImportPath(entryFile, userAppEntry))}`);
343
1830
  const cloudflareDispatcher = spec.name === "cloudflare" ? [
@@ -349,6 +1836,7 @@ function renderProviderEntry(spec, entryFile, userAppEntry, serializedWorkflowCo
349
1836
  return [
350
1837
  ...imports,
351
1838
  "",
1839
+ installVercelWorkflowRuntime ? "setVercelWorkflowRuntimeModules(workflowApi, workflowRuntime)" : "",
352
1840
  `const workflowConfig = ${serializedWorkflowConfig}`,
353
1841
  ...cloudflareDispatcher,
354
1842
  "",
@@ -360,7 +1848,7 @@ function renderProviderEntry(spec, entryFile, userAppEntry, serializedWorkflowCo
360
1848
  ""
361
1849
  ].filter(Boolean).join("\n");
362
1850
  }
363
- async function writeProviderEntries(rootDir, workflow) {
1851
+ async function writeProviderEntries(rootDir, workflow, importBases = {}) {
364
1852
  const generatedDir = ensureGeneratedDir(rootDir, productName);
365
1853
  await mkdir(generatedDir, { recursive: true });
366
1854
  const registryFile = resolve(generatedDir, generatedRegistryFileName);
@@ -368,7 +1856,7 @@ async function writeProviderEntries(rootDir, workflow) {
368
1856
  const providerDefinitions = definitions;
369
1857
  const userAppEntry = resolveWorkflowUserAppEntry(rootDir);
370
1858
  const cloudflareWorkflowConfig = resolveWorkflowConfig(workflow, "cloudflare");
371
- await writeFile(registryFile, createRuntimeRegistryContents(registryFile, definitions), "utf8");
1859
+ await writeFile(registryFile, createWorkflowRegistryContents(registryFile, definitions, importBases), "utf8");
372
1860
  const entryFiles = {
373
1861
  cloudflare: "",
374
1862
  openworkflow: "",
@@ -377,7 +1865,7 @@ async function writeProviderEntries(rootDir, workflow) {
377
1865
  await Promise.all(providerEntrySpecs.map(async (spec) => {
378
1866
  const entryFile = resolve(generatedDir, spec.entryFile);
379
1867
  const workflowConfig = spec.name === "cloudflare" ? cloudflareWorkflowConfig : resolveWorkflowConfig(workflow, spec.hosting);
380
- await writeFile(entryFile, renderProviderEntry(spec, entryFile, userAppEntry, JSON.stringify(workflowConfig, null, 2)), "utf8");
1868
+ await writeFile(entryFile, renderProviderEntry(spec, entryFile, userAppEntry, JSON.stringify(workflowConfig, null, 2), Boolean(importBases.workflow)), "utf8");
381
1869
  entryFiles[spec.name] = entryFile;
382
1870
  }));
383
1871
  return {
@@ -390,7 +1878,7 @@ async function writeProviderEntries(rootDir, workflow) {
390
1878
  vercelServerFile: entryFiles.vercel
391
1879
  };
392
1880
  }
393
- function createCloudflareOutput(rootDir, artifacts) {
1881
+ function createCloudflareOutput(rootDir, artifacts, providerImportAliases) {
394
1882
  const workflowConfig = artifacts.cloudflareWorkflowConfig && artifacts.cloudflareWorkflowConfig.provider === "cloudflare" ? artifacts.cloudflareWorkflowConfig : false;
395
1883
  const workflows = createCloudflareWorkflowBindings(workflowConfig ? artifacts.providerDefinitions : [], workflowConfig);
396
1884
  const wranglerConfig = {
@@ -403,6 +1891,7 @@ function createCloudflareOutput(rootDir, artifacts) {
403
1891
  return {
404
1892
  bundleEntry: artifacts.cloudflareWorkerFile,
405
1893
  bundleOptions: {
1894
+ alias: providerImportAliases,
406
1895
  conditions: [
407
1896
  "workerd",
408
1897
  "worker",
@@ -416,7 +1905,7 @@ function createCloudflareOutput(rootDir, artifacts) {
416
1905
  "@vercel/queue",
417
1906
  "@vercel/sandbox",
418
1907
  "cloudflare:workers",
419
- "node:async_hooks",
1908
+ ...nodeBuiltinExternals,
420
1909
  "workflow",
421
1910
  "workflow/api",
422
1911
  "workflow/runtime"
@@ -425,59 +1914,95 @@ function createCloudflareOutput(rootDir, artifacts) {
425
1914
  platform: "neutral"
426
1915
  },
427
1916
  bundleOutfileName: "worker.mjs",
1917
+ files: { "index.js": createCloudflareWorkflowWrapper(artifacts) },
428
1918
  outputRoot: createDefaultCloudflareOutputRoot(rootDir),
429
- wranglerConfigKeys: ["workflows"],
1919
+ wranglerConfigKeys: cloudflareWorkflowWranglerConfigKeys,
430
1920
  wranglerConfig
431
1921
  };
432
1922
  }
433
- async function writeCloudflareWorkflowWrapper(rootDir, artifacts) {
1923
+ function createCloudflareWorkflowWrapper(artifacts) {
1924
+ return renderCloudflareWorkerWrapper((artifacts.cloudflareWorkflowConfig && artifacts.cloudflareWorkflowConfig.provider === "cloudflare" ? artifacts.cloudflareWorkflowConfig : false) ? artifacts.providerDefinitions : []);
1925
+ }
1926
+ async function createCloudflareWorkflowCleanup(rootDir) {
434
1927
  const outputRoot = createDefaultCloudflareOutputRoot(rootDir);
435
- const workflowDefinitions = (artifacts.cloudflareWorkflowConfig && artifacts.cloudflareWorkflowConfig.provider === "cloudflare" ? artifacts.cloudflareWorkflowConfig : false) ? artifacts.providerDefinitions : [];
436
- await writeFile(resolve(outputRoot, "index.js"), renderCloudflareWorkerWrapper(workflowDefinitions), "utf8");
1928
+ let ownsWrapper = false;
1929
+ try {
1930
+ ownsWrapper = (await readFile(resolve(outputRoot, "index.js"), "utf8")).includes(cloudflareWorkflowWrapperImport);
1931
+ } catch (error) {
1932
+ if (error.code !== "ENOENT") throw error;
1933
+ }
1934
+ return {
1935
+ fileNames: ownsWrapper ? ["index.js", "worker.mjs"] : ["worker.mjs"],
1936
+ outputRoot,
1937
+ wranglerConfigOwnership: { keys: ownsWrapper ? cloudflareWorkflowWranglerConfigKeys : ["workflows"] }
1938
+ };
437
1939
  }
438
- function createVercelOutput(artifacts) {
1940
+ function createVercelOutput(artifacts, workflowTransformPlugin, frameworkImportBase, providerImportAliases, serverFunctionName) {
439
1941
  return {
440
1942
  bundleEntry: artifacts.vercelServerFile,
441
1943
  bundleOptions: {
1944
+ alias: providerImportAliases,
442
1945
  external: [
443
1946
  "@cloudflare/sandbox",
1947
+ ...frameworkImportBase ? [] : optionalAgentRuntimeExternals,
444
1948
  "cloudflare:workers",
445
- "workflow",
446
- "workflow/api",
447
- "workflow/runtime"
1949
+ ...nodeBuiltinExternals,
1950
+ ...!frameworkImportBase ? [
1951
+ "workflow",
1952
+ "workflow/api",
1953
+ "workflow/runtime"
1954
+ ] : []
448
1955
  ],
449
1956
  format: "esm",
450
- platform: "node"
451
- }
1957
+ platform: "node",
1958
+ plugins: workflowTransformPlugin ? [workflowTransformPlugin] : []
1959
+ },
1960
+ ...serverFunctionName ? { function: {
1961
+ kind: "isolated",
1962
+ name: serverFunctionName
1963
+ } } : {}
452
1964
  };
453
1965
  }
454
1966
  async function generateProviderOutputs(options) {
455
- const artifacts = await writeProviderEntries(options.rootDir, options.workflow);
1967
+ const artifacts = await writeProviderEntries(options.rootDir, options.workflow, {
1968
+ agent: options.agentImportBase,
1969
+ workflow: options.importBase,
1970
+ workspace: options.workspaceImportBase,
1971
+ workspaceDependencies: options.workspaceDependencyRuntimeImports
1972
+ });
456
1973
  const cloudflareWorkflowConfig = resolveWorkflowConfig(options.workflow, "cloudflare");
457
1974
  const vercelWorkflowConfig = resolveWorkflowConfig(options.workflow, "vercel");
458
- await writeProviderDeploymentOutputs({
459
- clientOutDir: options.clientOutDir,
460
- ...cloudflareWorkflowConfig && cloudflareWorkflowConfig.provider === "cloudflare" ? { cloudflare: createCloudflareOutput(options.rootDir, artifacts) } : {},
461
- cleanup: { cloudflare: {
462
- bundleOutfileName: "worker.mjs",
463
- outputRoot: createDefaultCloudflareOutputRoot(options.rootDir),
464
- wranglerConfigKeys: ["workflows"]
465
- } },
466
- rootDir: options.rootDir,
467
- ...vercelWorkflowConfig && vercelWorkflowConfig.provider === "vercel" ? { vercel: createVercelOutput(artifacts) } : {}
468
- });
469
- if (cloudflareWorkflowConfig && cloudflareWorkflowConfig.provider === "cloudflare") await writeCloudflareWorkflowWrapper(options.rootDir, artifacts);
1975
+ const cloudflareOutput = cloudflareWorkflowConfig && cloudflareWorkflowConfig.provider === "cloudflare" ? createCloudflareOutput(options.rootDir, artifacts, options.providerImportAliases) : void 0;
1976
+ const workflowTransformPlugin = vercelWorkflowConfig && vercelWorkflowConfig.provider === "vercel" ? await createVercelWorkflowTransformPlugin(options.rootDir) : void 0;
1977
+ const vercelOutput = vercelWorkflowConfig && vercelWorkflowConfig.provider === "vercel" ? createVercelOutput(artifacts, workflowTransformPlugin, options.importBase, options.providerImportAliases, options.serverFunctionName) : void 0;
1978
+ const writeOutputs = async () => {
1979
+ await writeProviderDeploymentOutputs({
1980
+ clientOutDir: options.clientOutDir,
1981
+ cloudflare: cloudflareOutput,
1982
+ cleanup: { cloudflare: cloudflareOutput ? void 0 : () => createCloudflareWorkflowCleanup(options.rootDir) },
1983
+ rootDir: options.rootDir,
1984
+ ...vercelOutput ? { vercel: vercelOutput } : {}
1985
+ });
1986
+ if (vercelOutput) await buildVercelNativeWorkflowOutput(options.rootDir, artifacts.providerDefinitions, options.providerImportAliases);
1987
+ };
1988
+ if (workflowTransformPlugin && options.importBase) await withVercelWorkflowPackageLink(options.rootDir, writeOutputs);
1989
+ else await writeOutputs();
470
1990
  return artifacts;
471
1991
  }
472
1992
  //#endregion
473
1993
  //#region src/vite.ts
474
1994
  const mergeNoExternal = createNoExternalMerger(workflowPackageName);
1995
+ function resolveStringAliases(config) {
1996
+ const aliases = {};
1997
+ for (const alias of config.resolve.alias) if (typeof alias.find === "string" && typeof alias.replacement === "string") aliases[alias.find] = alias.replacement;
1998
+ return aliases;
1999
+ }
475
2000
  function hubWorkflow(options) {
2001
+ const internalOptions = options;
476
2002
  let resolved;
477
2003
  let workflow = options;
478
2004
  return {
479
2005
  name: "@vite-hub/workflow/vite",
480
- nitro: workflowNitroModule,
481
2006
  config(config) {
482
2007
  workflow = config.workflow ?? workflow;
483
2008
  },
@@ -492,9 +2017,18 @@ function hubWorkflow(options) {
492
2017
  async closeBundle() {
493
2018
  if (!resolved || shouldSkipViteProviderBuild(resolved.command, getViteMode())) return;
494
2019
  await generateProviderOutputs({
2020
+ agentImportBase: internalOptions?.agentImportBase,
495
2021
  clientOutDir: resolved.build.outDir,
2022
+ importBase: internalOptions?.importBase,
2023
+ providerImportAliases: {
2024
+ ...resolveStringAliases(resolved),
2025
+ ...internalOptions?.providerImportAliases
2026
+ },
496
2027
  rootDir: resolved.root,
497
- workflow
2028
+ serverFunctionName: resolveNitroVercelFunctionName(resolved, "workflow"),
2029
+ workflow,
2030
+ workspaceDependencyRuntimeImports: internalOptions?.workspaceDependencyRuntimeImports,
2031
+ workspaceImportBase: internalOptions?.workspaceImportBase
498
2032
  });
499
2033
  }
500
2034
  };