@vite-hub/workflow 0.0.0 → 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.
- package/README.md +110 -1
- package/dist/_app-DgwN4ulW.d.ts +11 -0
- package/dist/app-BZpq2cwC.js +10 -0
- package/dist/cloudflare-Cds1VIWa.js +42 -0
- package/dist/cloudflare-env-CIjdFTv6.d.ts +22 -0
- package/dist/cloudflare-env-mFp2Uj6Z.js +53 -0
- package/dist/cloudflare-shared-BVVgDSdq.d.ts +1 -0
- package/dist/cloudflare-vite-B0zel2Do.js +39 -0
- package/dist/config-BDqB6I4E.js +130 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.js +357 -0
- package/dist/provider-operation-CWIV4k42.js +62 -0
- package/dist/runtime/cloudflare-runner.d.ts +27 -0
- package/dist/runtime/cloudflare-runner.js +24 -0
- package/dist/runtime/cloudflare-shared.d.ts +2 -0
- package/dist/runtime/cloudflare-shared.js +2 -0
- package/dist/runtime/cloudflare-vite.d.ts +17 -0
- package/dist/runtime/cloudflare-vite.js +2 -0
- package/dist/runtime/execute.d.ts +11 -0
- package/dist/runtime/execute.js +25 -0
- package/dist/runtime/openworkflow-worker.d.ts +20 -0
- package/dist/runtime/openworkflow-worker.js +112 -0
- package/dist/runtime/openworkflow.d.ts +23 -0
- package/dist/runtime/openworkflow.js +216 -0
- package/dist/runtime/state.d.ts +35 -0
- package/dist/runtime/state.js +179 -0
- package/dist/runtime/vercel-vite.d.ts +69 -0
- package/dist/runtime/vercel-vite.js +49 -0
- package/dist/types-BUlYBZwH.d.ts +163 -0
- package/dist/vercel-D2_J5D_Y.js +217 -0
- package/dist/vite.d.ts +13 -0
- package/dist/vite.js +2037 -0
- package/package.json +90 -5
package/dist/vite.js
ADDED
|
@@ -0,0 +1,2037 @@
|
|
|
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";
|
|
9
|
+
import { build } from "esbuild";
|
|
10
|
+
const VITEHUB_MODES = {
|
|
11
|
+
e2e: "e2e",
|
|
12
|
+
blob: "blob",
|
|
13
|
+
chat: "chat",
|
|
14
|
+
db: "db",
|
|
15
|
+
env: "env",
|
|
16
|
+
kv: "kv",
|
|
17
|
+
queue: "queue",
|
|
18
|
+
schedule: "schedule",
|
|
19
|
+
sandbox: "sandbox",
|
|
20
|
+
workspace: "workspace",
|
|
21
|
+
workflow: "workflow"
|
|
22
|
+
};
|
|
23
|
+
function isViteCli(argv) {
|
|
24
|
+
return argv.some((arg) => /(?:^|[/\\])vite(?:\.[cm]?js)?$/.test(arg) || arg === "vite");
|
|
25
|
+
}
|
|
26
|
+
function getViteCliMode(argv = process.argv) {
|
|
27
|
+
if (!isViteCli(argv)) return;
|
|
28
|
+
const modes = new Set(Object.values(VITEHUB_MODES));
|
|
29
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
30
|
+
const arg = argv[index];
|
|
31
|
+
const mode = arg === "--mode" || arg === "-m" ? argv[index + 1] : arg.startsWith("--mode=") ? arg.slice(7) : void 0;
|
|
32
|
+
if (mode && modes.has(mode)) return mode;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function getViteMode(env = process.env) {
|
|
36
|
+
return env["VITEHUB_VITE_MODE"] ?? getViteCliMode();
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region ../internal/src/build/client-output.ts
|
|
40
|
+
function hasStaticIndex(clientDir) {
|
|
41
|
+
return existsSync(resolve$1(clientDir, "index.html"));
|
|
42
|
+
}
|
|
43
|
+
async function copyClientOutput(clientDir, targetDir) {
|
|
44
|
+
const resolvedClientDir = resolve$1(clientDir);
|
|
45
|
+
const resolvedTargetDir = resolve$1(targetDir);
|
|
46
|
+
if (resolvedClientDir === resolvedTargetDir) return;
|
|
47
|
+
await rm(resolvedTargetDir, {
|
|
48
|
+
force: true,
|
|
49
|
+
recursive: true
|
|
50
|
+
});
|
|
51
|
+
await mkdir(dirname$1(resolvedTargetDir), { recursive: true });
|
|
52
|
+
const targetRelativePath = relative$1(resolvedClientDir, resolvedTargetDir);
|
|
53
|
+
if (targetRelativePath && !targetRelativePath.startsWith("../")) {
|
|
54
|
+
const [targetRootEntry] = targetRelativePath.split("/", 1);
|
|
55
|
+
await mkdir(resolvedTargetDir, { recursive: true });
|
|
56
|
+
await Promise.all((await readdir(resolvedClientDir)).filter((entry) => entry !== targetRootEntry).map((entry) => cp(resolve$1(resolvedClientDir, entry), resolve$1(resolvedTargetDir, entry), { recursive: true })));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await cp(resolvedClientDir, resolvedTargetDir, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
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);
|
|
141
|
+
});
|
|
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
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region ../internal/src/build/user-entry.ts
|
|
193
|
+
function toSafeAppName(rootDir) {
|
|
194
|
+
return basename(rootDir).replace(/[^a-z0-9-]+/gi, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
|
|
195
|
+
}
|
|
196
|
+
const DEFAULT_ENTRY_NAMES = [
|
|
197
|
+
"server.ts",
|
|
198
|
+
"server.mts",
|
|
199
|
+
"server.js",
|
|
200
|
+
"server.mjs",
|
|
201
|
+
"worker.ts",
|
|
202
|
+
"worker.mts",
|
|
203
|
+
"worker.js",
|
|
204
|
+
"worker.mjs"
|
|
205
|
+
];
|
|
206
|
+
function resolveUserAppEntry(rootDir, options = {}) {
|
|
207
|
+
const names = options.names ?? DEFAULT_ENTRY_NAMES;
|
|
208
|
+
const srcSubdir = options.srcSubdir ?? "src";
|
|
209
|
+
return names.map((name) => resolve(rootDir, srcSubdir, name)).find(existsSync);
|
|
210
|
+
}
|
|
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
|
|
549
|
+
//#region ../internal/src/build/vercel-config.ts
|
|
550
|
+
function createVercelConfigJson() {
|
|
551
|
+
return {
|
|
552
|
+
routes: [{ handle: "filesystem" }, {
|
|
553
|
+
src: "/(.*)",
|
|
554
|
+
dest: "/__server"
|
|
555
|
+
}],
|
|
556
|
+
version: 3
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
function createNodeFunctionConfig(extra = {}) {
|
|
560
|
+
return {
|
|
561
|
+
handler: "index.mjs",
|
|
562
|
+
launcherType: "Nodejs",
|
|
563
|
+
runtime: "nodejs22.x",
|
|
564
|
+
shouldAddHelpers: false,
|
|
565
|
+
supportsResponseStreaming: true,
|
|
566
|
+
...extra
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
//#endregion
|
|
570
|
+
//#region ../internal/src/build/deployment-output.ts
|
|
571
|
+
const providerDeploymentOutputWrites = /* @__PURE__ */ new Map();
|
|
572
|
+
function resolveClientOutput(rootDir, clientOutDir) {
|
|
573
|
+
const clientDir = resolve(rootDir, clientOutDir);
|
|
574
|
+
return {
|
|
575
|
+
clientDir,
|
|
576
|
+
staticIndex: hasStaticIndex(clientDir)
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
function createDefaultCloudflareStaticOutputDir(rootDir) {
|
|
580
|
+
return resolve(rootDir, "dist", "client");
|
|
581
|
+
}
|
|
582
|
+
function createDefaultVercelOutputRoot(rootDir) {
|
|
583
|
+
return resolve(rootDir, ".vercel", "output");
|
|
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
|
+
}
|
|
599
|
+
async function writeCloudflareDeploymentOutput(options) {
|
|
600
|
+
const { clientDir, staticIndex } = resolveClientOutput(options.rootDir, options.clientOutDir);
|
|
601
|
+
const outputRoot = options.outputRoot ?? createDefaultCloudflareOutputRoot(options.rootDir);
|
|
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}`);
|
|
605
|
+
await mkdir(outputRoot, { recursive: true });
|
|
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);
|
|
627
|
+
}
|
|
628
|
+
async function writeVercelDeploymentOutput(options) {
|
|
629
|
+
const { clientDir, staticIndex } = resolveClientOutput(options.rootDir, options.clientOutDir);
|
|
630
|
+
const outputRoot = options.outputRoot ?? createDefaultVercelOutputRoot(options.rootDir);
|
|
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);
|
|
635
|
+
const serverEntry = resolve(serverDir, "index.mjs");
|
|
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), {
|
|
639
|
+
force: true,
|
|
640
|
+
recursive: true
|
|
641
|
+
})));
|
|
642
|
+
await Promise.all([
|
|
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 }),
|
|
649
|
+
staticIndex ? copyClientOutput(clientDir, options.staticOutputDir ?? resolve(outputRoot, "static")) : Promise.resolve()
|
|
650
|
+
]);
|
|
651
|
+
}
|
|
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;
|
|
674
|
+
const outputRoot = cleanup.outputRoot ?? createDefaultCloudflareOutputRoot(rootDir);
|
|
675
|
+
const writes = (cleanup.fileNames ?? []).map((fileName) => rm(resolve(outputRoot, fileName), {
|
|
676
|
+
force: true,
|
|
677
|
+
recursive: true
|
|
678
|
+
}));
|
|
679
|
+
writes.push(writeCloudflareWranglerConfig({
|
|
680
|
+
outputRoot,
|
|
681
|
+
rootDir,
|
|
682
|
+
wranglerConfigOwnership: cleanup.wranglerConfigOwnership
|
|
683
|
+
}));
|
|
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
|
+
}
|
|
691
|
+
}
|
|
692
|
+
async function cleanupVercelDeploymentOutput(rootDir, cleanup) {
|
|
693
|
+
const outputRoot = cleanup.outputRoot ?? createDefaultVercelOutputRoot(rootDir);
|
|
694
|
+
const writes = [];
|
|
695
|
+
if (cleanup.serverFunctionName) writes.push(rm(resolve(outputRoot, "functions", cleanup.serverFunctionName), {
|
|
696
|
+
force: true,
|
|
697
|
+
recursive: true
|
|
698
|
+
}));
|
|
699
|
+
writes.push(cleanProviderOutputConfig(resolve(outputRoot, "config.json"), { keys: cleanup.configKeys }));
|
|
700
|
+
await Promise.all(writes);
|
|
701
|
+
}
|
|
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) {
|
|
716
|
+
const writes = [];
|
|
717
|
+
if (options.cloudflare) writes.push(writeCloudflareDeploymentOutput({
|
|
718
|
+
...options.cloudflare,
|
|
719
|
+
clientOutDir: options.clientOutDir,
|
|
720
|
+
rootDir: options.rootDir
|
|
721
|
+
}));
|
|
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));
|
|
729
|
+
if (options.vercel) writes.push(writeVercelDeploymentOutput({
|
|
730
|
+
...options.vercel,
|
|
731
|
+
clientOutDir: options.clientOutDir,
|
|
732
|
+
rootDir: options.rootDir
|
|
733
|
+
}));
|
|
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
|
+
}
|
|
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
|
+
}
|
|
750
|
+
}
|
|
751
|
+
//#endregion
|
|
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
|
|
801
|
+
};
|
|
802
|
+
}
|
|
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)));
|
|
901
|
+
}
|
|
902
|
+
//#endregion
|
|
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
|
+
}
|
|
1478
|
+
//#endregion
|
|
1479
|
+
//#region src/internal/vite-build.ts
|
|
1480
|
+
const workflowPackageName = "@vite-hub/workflow";
|
|
1481
|
+
const productName = "workflow";
|
|
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"`;
|
|
1491
|
+
const packageDir = computePackageDir(import.meta.url);
|
|
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/*"];
|
|
1499
|
+
const WORKFLOW_ENTRY_BASE_NAMES = [
|
|
1500
|
+
"server.ts",
|
|
1501
|
+
"server.mts",
|
|
1502
|
+
"server.js",
|
|
1503
|
+
"server.mjs",
|
|
1504
|
+
"worker.ts",
|
|
1505
|
+
"worker.mts",
|
|
1506
|
+
"worker.js",
|
|
1507
|
+
"worker.mjs"
|
|
1508
|
+
];
|
|
1509
|
+
const WORKFLOW_PRIORITY_NAMES = [
|
|
1510
|
+
"server-workflow.ts",
|
|
1511
|
+
"server-workflow.mts",
|
|
1512
|
+
"server-workflow.js",
|
|
1513
|
+
"server-workflow.mjs"
|
|
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
|
+
}
|
|
1628
|
+
function resolveWorkflowUserAppEntry(rootDir) {
|
|
1629
|
+
return resolveUserAppEntry(rootDir, { names: getViteMode() === VITEHUB_MODES.workflow ? [...WORKFLOW_PRIORITY_NAMES, ...WORKFLOW_ENTRY_BASE_NAMES] : [...WORKFLOW_ENTRY_BASE_NAMES] });
|
|
1630
|
+
}
|
|
1631
|
+
const providerEntrySpecs = [{
|
|
1632
|
+
name: "cloudflare",
|
|
1633
|
+
entryFile: "cloudflare-worker.mjs",
|
|
1634
|
+
runtimeModule: "runtime/cloudflare-vite",
|
|
1635
|
+
factory: "createWorkflowCloudflareWorker",
|
|
1636
|
+
hosting: "cloudflare"
|
|
1637
|
+
}, {
|
|
1638
|
+
name: "vercel",
|
|
1639
|
+
entryFile: "vercel-server.mjs",
|
|
1640
|
+
runtimeModule: "runtime/vercel-vite",
|
|
1641
|
+
factory: "createWorkflowVercelServer",
|
|
1642
|
+
hosting: "vercel"
|
|
1643
|
+
}];
|
|
1644
|
+
function resolveWorkflowConfig(workflow, hosting) {
|
|
1645
|
+
return normalizeWorkflowOptions(workflow, { hosting }) ?? false;
|
|
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
|
+
}
|
|
1795
|
+
function renderCloudflareWorkerWrapper(definitions) {
|
|
1796
|
+
return [
|
|
1797
|
+
definitions.length ? `import { WorkflowEntrypoint, waitUntil as viteHubWaitUntil } from "cloudflare:workers"` : `import { waitUntil as viteHubWaitUntil } from "cloudflare:workers"`,
|
|
1798
|
+
cloudflareWorkflowWrapperImport,
|
|
1799
|
+
"",
|
|
1800
|
+
...definitions.map((definition) => {
|
|
1801
|
+
return [
|
|
1802
|
+
`export class ${getCloudflareWorkflowClassName(definition.name)} extends WorkflowEntrypoint {`,
|
|
1803
|
+
" async run(event, step) {",
|
|
1804
|
+
` return await runViteHubWorkflowDefinition(${JSON.stringify(definition.name)}, this.env || {}, event, step)`,
|
|
1805
|
+
" }",
|
|
1806
|
+
"}",
|
|
1807
|
+
""
|
|
1808
|
+
].join("\n");
|
|
1809
|
+
}),
|
|
1810
|
+
"const viteHubWorker = {",
|
|
1811
|
+
" async fetch(request, env, context) {",
|
|
1812
|
+
" const waitUntil = typeof viteHubWaitUntil === \"function\" ? viteHubWaitUntil : context?.waitUntil?.bind(context)",
|
|
1813
|
+
" return await worker.fetch(request, env, waitUntil ? { ...context, waitUntil } : context)",
|
|
1814
|
+
" }",
|
|
1815
|
+
"}",
|
|
1816
|
+
"",
|
|
1817
|
+
"export default viteHubWorker",
|
|
1818
|
+
""
|
|
1819
|
+
].join("\n");
|
|
1820
|
+
}
|
|
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
|
+
];
|
|
1828
|
+
if (spec.name === "cloudflare") imports.push(`import { runCloudflareWorkflow } from ${JSON.stringify(createImportPath(entryFile, resolveRuntimeModule("runtime/cloudflare-runner")))}`);
|
|
1829
|
+
if (userAppEntry) imports.push(`import workflowApp from ${JSON.stringify(createImportPath(entryFile, userAppEntry))}`);
|
|
1830
|
+
const cloudflareDispatcher = spec.name === "cloudflare" ? [
|
|
1831
|
+
"",
|
|
1832
|
+
"export async function runViteHubWorkflowDefinition(name, env, event, step) {",
|
|
1833
|
+
" return await runCloudflareWorkflow({ config: workflowConfig, env: env || {}, event, name, registry: workflowRegistry, step })",
|
|
1834
|
+
"}"
|
|
1835
|
+
] : [];
|
|
1836
|
+
return [
|
|
1837
|
+
...imports,
|
|
1838
|
+
"",
|
|
1839
|
+
installVercelWorkflowRuntime ? "setVercelWorkflowRuntimeModules(workflowApi, workflowRuntime)" : "",
|
|
1840
|
+
`const workflowConfig = ${serializedWorkflowConfig}`,
|
|
1841
|
+
...cloudflareDispatcher,
|
|
1842
|
+
"",
|
|
1843
|
+
`export default ${spec.factory}({`,
|
|
1844
|
+
userAppEntry ? " app: workflowApp," : "",
|
|
1845
|
+
" registry: workflowRegistry,",
|
|
1846
|
+
" workflow: workflowConfig,",
|
|
1847
|
+
"})",
|
|
1848
|
+
""
|
|
1849
|
+
].filter(Boolean).join("\n");
|
|
1850
|
+
}
|
|
1851
|
+
async function writeProviderEntries(rootDir, workflow, importBases = {}) {
|
|
1852
|
+
const generatedDir = ensureGeneratedDir(rootDir, productName);
|
|
1853
|
+
await mkdir(generatedDir, { recursive: true });
|
|
1854
|
+
const registryFile = resolve(generatedDir, generatedRegistryFileName);
|
|
1855
|
+
const definitions = discoverWorkflowDefinitions({ rootDir });
|
|
1856
|
+
const providerDefinitions = definitions;
|
|
1857
|
+
const userAppEntry = resolveWorkflowUserAppEntry(rootDir);
|
|
1858
|
+
const cloudflareWorkflowConfig = resolveWorkflowConfig(workflow, "cloudflare");
|
|
1859
|
+
await writeFile(registryFile, createWorkflowRegistryContents(registryFile, definitions, importBases), "utf8");
|
|
1860
|
+
const entryFiles = {
|
|
1861
|
+
cloudflare: "",
|
|
1862
|
+
openworkflow: "",
|
|
1863
|
+
vercel: ""
|
|
1864
|
+
};
|
|
1865
|
+
await Promise.all(providerEntrySpecs.map(async (spec) => {
|
|
1866
|
+
const entryFile = resolve(generatedDir, spec.entryFile);
|
|
1867
|
+
const workflowConfig = spec.name === "cloudflare" ? cloudflareWorkflowConfig : resolveWorkflowConfig(workflow, spec.hosting);
|
|
1868
|
+
await writeFile(entryFile, renderProviderEntry(spec, entryFile, userAppEntry, JSON.stringify(workflowConfig, null, 2), Boolean(importBases.workflow)), "utf8");
|
|
1869
|
+
entryFiles[spec.name] = entryFile;
|
|
1870
|
+
}));
|
|
1871
|
+
return {
|
|
1872
|
+
cloudflareWorkerFile: entryFiles.cloudflare,
|
|
1873
|
+
cloudflareWorkflowConfig,
|
|
1874
|
+
definitions,
|
|
1875
|
+
generatedDir,
|
|
1876
|
+
providerDefinitions,
|
|
1877
|
+
registryFile,
|
|
1878
|
+
vercelServerFile: entryFiles.vercel
|
|
1879
|
+
};
|
|
1880
|
+
}
|
|
1881
|
+
function createCloudflareOutput(rootDir, artifacts, providerImportAliases) {
|
|
1882
|
+
const workflowConfig = artifacts.cloudflareWorkflowConfig && artifacts.cloudflareWorkflowConfig.provider === "cloudflare" ? artifacts.cloudflareWorkflowConfig : false;
|
|
1883
|
+
const workflows = createCloudflareWorkflowBindings(workflowConfig ? artifacts.providerDefinitions : [], workflowConfig);
|
|
1884
|
+
const wranglerConfig = {
|
|
1885
|
+
compatibility_date: defaultCloudflareCompatibilityDate,
|
|
1886
|
+
compatibility_flags: ["nodejs_compat"],
|
|
1887
|
+
main: "index.js",
|
|
1888
|
+
observability: { enabled: true },
|
|
1889
|
+
...workflows ? { workflows } : {}
|
|
1890
|
+
};
|
|
1891
|
+
return {
|
|
1892
|
+
bundleEntry: artifacts.cloudflareWorkerFile,
|
|
1893
|
+
bundleOptions: {
|
|
1894
|
+
alias: providerImportAliases,
|
|
1895
|
+
conditions: [
|
|
1896
|
+
"workerd",
|
|
1897
|
+
"worker",
|
|
1898
|
+
"browser",
|
|
1899
|
+
"default"
|
|
1900
|
+
],
|
|
1901
|
+
external: [
|
|
1902
|
+
"@cloudflare/sandbox",
|
|
1903
|
+
"@vercel/blob",
|
|
1904
|
+
"@vercel/functions",
|
|
1905
|
+
"@vercel/queue",
|
|
1906
|
+
"@vercel/sandbox",
|
|
1907
|
+
"cloudflare:workers",
|
|
1908
|
+
...nodeBuiltinExternals,
|
|
1909
|
+
"workflow",
|
|
1910
|
+
"workflow/api",
|
|
1911
|
+
"workflow/runtime"
|
|
1912
|
+
],
|
|
1913
|
+
format: "esm",
|
|
1914
|
+
platform: "neutral"
|
|
1915
|
+
},
|
|
1916
|
+
bundleOutfileName: "worker.mjs",
|
|
1917
|
+
files: { "index.js": createCloudflareWorkflowWrapper(artifacts) },
|
|
1918
|
+
outputRoot: createDefaultCloudflareOutputRoot(rootDir),
|
|
1919
|
+
wranglerConfigKeys: cloudflareWorkflowWranglerConfigKeys,
|
|
1920
|
+
wranglerConfig
|
|
1921
|
+
};
|
|
1922
|
+
}
|
|
1923
|
+
function createCloudflareWorkflowWrapper(artifacts) {
|
|
1924
|
+
return renderCloudflareWorkerWrapper((artifacts.cloudflareWorkflowConfig && artifacts.cloudflareWorkflowConfig.provider === "cloudflare" ? artifacts.cloudflareWorkflowConfig : false) ? artifacts.providerDefinitions : []);
|
|
1925
|
+
}
|
|
1926
|
+
async function createCloudflareWorkflowCleanup(rootDir) {
|
|
1927
|
+
const outputRoot = createDefaultCloudflareOutputRoot(rootDir);
|
|
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
|
+
};
|
|
1939
|
+
}
|
|
1940
|
+
function createVercelOutput(artifacts, workflowTransformPlugin, frameworkImportBase, providerImportAliases, serverFunctionName) {
|
|
1941
|
+
return {
|
|
1942
|
+
bundleEntry: artifacts.vercelServerFile,
|
|
1943
|
+
bundleOptions: {
|
|
1944
|
+
alias: providerImportAliases,
|
|
1945
|
+
external: [
|
|
1946
|
+
"@cloudflare/sandbox",
|
|
1947
|
+
...frameworkImportBase ? [] : optionalAgentRuntimeExternals,
|
|
1948
|
+
"cloudflare:workers",
|
|
1949
|
+
...nodeBuiltinExternals,
|
|
1950
|
+
...!frameworkImportBase ? [
|
|
1951
|
+
"workflow",
|
|
1952
|
+
"workflow/api",
|
|
1953
|
+
"workflow/runtime"
|
|
1954
|
+
] : []
|
|
1955
|
+
],
|
|
1956
|
+
format: "esm",
|
|
1957
|
+
platform: "node",
|
|
1958
|
+
plugins: workflowTransformPlugin ? [workflowTransformPlugin] : []
|
|
1959
|
+
},
|
|
1960
|
+
...serverFunctionName ? { function: {
|
|
1961
|
+
kind: "isolated",
|
|
1962
|
+
name: serverFunctionName
|
|
1963
|
+
} } : {}
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1966
|
+
async function generateProviderOutputs(options) {
|
|
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
|
+
});
|
|
1973
|
+
const cloudflareWorkflowConfig = resolveWorkflowConfig(options.workflow, "cloudflare");
|
|
1974
|
+
const vercelWorkflowConfig = resolveWorkflowConfig(options.workflow, "vercel");
|
|
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();
|
|
1990
|
+
return artifacts;
|
|
1991
|
+
}
|
|
1992
|
+
//#endregion
|
|
1993
|
+
//#region src/vite.ts
|
|
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
|
+
}
|
|
2000
|
+
function hubWorkflow(options) {
|
|
2001
|
+
const internalOptions = options;
|
|
2002
|
+
let resolved;
|
|
2003
|
+
let workflow = options;
|
|
2004
|
+
return {
|
|
2005
|
+
name: "@vite-hub/workflow/vite",
|
|
2006
|
+
config(config) {
|
|
2007
|
+
workflow = config.workflow ?? workflow;
|
|
2008
|
+
},
|
|
2009
|
+
configResolved(config) {
|
|
2010
|
+
resolved = config;
|
|
2011
|
+
workflow = config.workflow ?? workflow;
|
|
2012
|
+
},
|
|
2013
|
+
configEnvironment(name, config) {
|
|
2014
|
+
if (!isServerEnvironment(name, config)) return;
|
|
2015
|
+
return { resolve: { noExternal: mergeNoExternal(config.resolve?.noExternal) } };
|
|
2016
|
+
},
|
|
2017
|
+
async closeBundle() {
|
|
2018
|
+
if (!resolved || shouldSkipViteProviderBuild(resolved.command, getViteMode())) return;
|
|
2019
|
+
await generateProviderOutputs({
|
|
2020
|
+
agentImportBase: internalOptions?.agentImportBase,
|
|
2021
|
+
clientOutDir: resolved.build.outDir,
|
|
2022
|
+
importBase: internalOptions?.importBase,
|
|
2023
|
+
providerImportAliases: {
|
|
2024
|
+
...resolveStringAliases(resolved),
|
|
2025
|
+
...internalOptions?.providerImportAliases
|
|
2026
|
+
},
|
|
2027
|
+
rootDir: resolved.root,
|
|
2028
|
+
serverFunctionName: resolveNitroVercelFunctionName(resolved, "workflow"),
|
|
2029
|
+
workflow,
|
|
2030
|
+
workspaceDependencyRuntimeImports: internalOptions?.workspaceDependencyRuntimeImports,
|
|
2031
|
+
workspaceImportBase: internalOptions?.workspaceImportBase
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
//#endregion
|
|
2037
|
+
export { hubWorkflow };
|