devflare 1.0.0-next.10 → 1.0.0-next.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/LLM.md +683 -13
  2. package/README.md +33 -5
  3. package/dist/{build-k36xrzvy.js → build-rfh8cgh3.js} +40 -11
  4. package/dist/bundler/index.d.ts +1 -0
  5. package/dist/bundler/index.d.ts.map +1 -1
  6. package/dist/bundler/worker-bundler.d.ts +14 -0
  7. package/dist/bundler/worker-bundler.d.ts.map +1 -0
  8. package/dist/cli/commands/build.d.ts.map +1 -1
  9. package/dist/cli/commands/deploy.d.ts.map +1 -1
  10. package/dist/cli/commands/dev.d.ts.map +1 -1
  11. package/dist/config/compiler.d.ts.map +1 -1
  12. package/dist/config/index.d.ts +1 -0
  13. package/dist/config/index.d.ts.map +1 -1
  14. package/dist/config/resolve.d.ts +3 -0
  15. package/dist/config/resolve.d.ts.map +1 -0
  16. package/dist/config/schema.d.ts +37 -31
  17. package/dist/config/schema.d.ts.map +1 -1
  18. package/dist/{deploy-dbvfq8vq.js → deploy-k0fcgt3d.js} +40 -11
  19. package/dist/{dev-rk8p6pse.js → dev-d4wabqyf.js} +73 -470
  20. package/dist/dev-server/server.d.ts.map +1 -1
  21. package/dist/{doctor-06y8nxd4.js → doctor-z4ffybce.js} +2 -2
  22. package/dist/{index-jht2j546.js → index-0kzg8wed.js} +26 -6
  23. package/dist/index-1xqeptt2.js +623 -0
  24. package/dist/{index-pwgyy2q9.js → index-dr6sbp8d.js} +1 -1
  25. package/dist/{index-6v3wjg1r.js → index-rfhx0yd5.js} +11 -7
  26. package/dist/{index-05fyzwne.js → index-twpgq9k9.js} +5 -5
  27. package/dist/{index-1phx14av.js → index-wyf3s77s.js} +1 -1
  28. package/dist/{index-vs49yxn4.js → index-xxwbb2nt.js} +1 -1
  29. package/dist/index-zbvmtcn2.js +795 -0
  30. package/dist/src/browser.js +1 -1
  31. package/dist/src/cli/index.js +1 -1
  32. package/dist/src/index.js +12 -13
  33. package/dist/src/sveltekit/index.js +4 -5
  34. package/dist/src/test/index.js +6 -7
  35. package/dist/src/vite/index.js +19 -399
  36. package/dist/test/simple-context.d.ts.map +1 -1
  37. package/dist/{types-x9q7t491.js → types-sffr9681.js} +7 -8
  38. package/dist/vite/config-file.d.ts +25 -0
  39. package/dist/vite/config-file.d.ts.map +1 -0
  40. package/dist/vite/index.d.ts +1 -0
  41. package/dist/vite/index.d.ts.map +1 -1
  42. package/dist/worker-entry/composed-worker.d.ts.map +1 -1
  43. package/package.json +1 -1
  44. package/dist/index-k7r18na8.js +0 -0
  45. package/dist/index-ws68xvq2.js +0 -311
@@ -1,442 +1,44 @@
1
+ import {
2
+ bundleWorkerEntry,
3
+ createDOBundler
4
+ } from "./index-1xqeptt2.js";
1
5
  import {
2
6
  detectViteProject,
3
7
  stopSpawnedProcessTree,
4
8
  waitForViteReady
5
9
  } from "./index-y1d8za14.js";
6
10
  import {
7
- prepareComposedWorkerEntrypoint
8
- } from "./index-ws68xvq2.js";
11
+ prepareComposedWorkerEntrypoint,
12
+ resolveEffectiveViteProject,
13
+ writeGeneratedViteConfig
14
+ } from "./index-zbvmtcn2.js";
9
15
  import {
10
16
  discoverRoutes,
11
17
  getRouteDirectoryCandidate
12
18
  } from "./index-1p814k7s.js";
13
- import {
14
- findDurableObjectClasses,
15
- transformDurableObject
16
- } from "./index-9wt9x09k.js";
17
- import {
18
- findFiles
19
- } from "./index-rbht7m9r.js";
19
+ import"./index-rbht7m9r.js";
20
+ import"./index-9wt9x09k.js";
20
21
  import {
21
22
  clearLocalSendEmailBindings,
22
23
  setLocalSendEmailBindings
23
24
  } from "./index-fef08w43.js";
25
+ import"./index-rfhx0yd5.js";
24
26
  import {
25
27
  loadConfig,
26
28
  resolveConfigPath
27
- } from "./index-1phx14av.js";
29
+ } from "./index-wyf3s77s.js";
30
+ import"./index-v8vvsn9x.js";
28
31
  import {
29
32
  __require
30
33
  } from "./index-37x76zdn.js";
31
34
 
32
35
  // src/cli/commands/dev.ts
33
36
  import { createConsola } from "consola";
34
- import { relative as relative2, resolve as resolve3 } from "pathe";
37
+ import { relative, resolve as resolve2 } from "pathe";
35
38
 
36
39
  // src/dev-server/server.ts
37
- import { dirname as dirname2, resolve as resolve2 } from "pathe";
38
-
39
- // src/bundler/do-bundler.ts
40
- import { resolve, dirname, relative } from "pathe";
41
- import picomatch from "picomatch";
42
- function classToBindingName(className) {
43
- return className.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").toUpperCase();
44
- }
45
- async function discoverDOs(cwd, pattern) {
46
- const fs = await import("node:fs/promises");
47
- const discovered = [];
48
- const files = await findFiles(pattern, { cwd });
49
- for (const filePath of files) {
50
- try {
51
- const code = await fs.readFile(filePath, "utf-8");
52
- const classNames = findDurableObjectClasses(code);
53
- for (const className of classNames) {
54
- discovered.push({
55
- filePath,
56
- className,
57
- bindingName: classToBindingName(className)
58
- });
59
- }
60
- } catch {}
61
- }
62
- return discovered;
63
- }
64
- function stripDecoratorSyntax(code) {
65
- let result = code;
66
- result = result.replace(/@durableObject\s*\([^)]*\)\s*\n?\s*(?=export\s+class)/g, "");
67
- result = result.replace(/import\s*\{([^}]*)\bdurableObject\b[^}]*\}\s*from\s*['"]devflare\/runtime['"]\s*;?/g, (match, imports) => {
68
- const cleanedImports = imports.split(",").map((s) => s.trim()).filter((s) => !s.startsWith("durableObject")).join(", ");
69
- if (cleanedImports.trim() === "") {
70
- return "";
71
- }
72
- return `import { ${cleanedImports} } from 'devflare/runtime'`;
73
- });
74
- return result;
75
- }
76
- function toArray(value) {
77
- return Array.isArray(value) ? value : [value];
78
- }
79
- function matchesExternalPattern(pattern, id) {
80
- if (pattern instanceof RegExp) {
81
- pattern.lastIndex = 0;
82
- return pattern.test(id);
83
- }
84
- return pattern === id;
85
- }
86
- function matchesExternalOption(option, id, parentId, isResolved) {
87
- if (!option) {
88
- return false;
89
- }
90
- if (typeof option === "function") {
91
- return option(id, parentId, isResolved) ?? false;
92
- }
93
- return toArray(option).some((pattern) => matchesExternalPattern(pattern, id));
94
- }
95
- function mergeExternalOptions(base, user) {
96
- if (!base) {
97
- return user;
98
- }
99
- if (!user) {
100
- return base;
101
- }
102
- if (typeof base !== "function" && typeof user !== "function") {
103
- return [...toArray(base), ...toArray(user)];
104
- }
105
- return (id, parentId, isResolved) => {
106
- return matchesExternalOption(base, id, parentId, isResolved) || matchesExternalOption(user, id, parentId, isResolved) || false;
107
- };
108
- }
109
- function mergePluginOptions(base, user) {
110
- if (!base) {
111
- return user;
112
- }
113
- if (!user) {
114
- return base;
115
- }
116
- return [base, user];
117
- }
118
- function mergeResolveOptions(base, user) {
119
- if (!base) {
120
- return user;
121
- }
122
- if (!user) {
123
- return base;
124
- }
125
- return {
126
- ...user,
127
- ...base,
128
- alias: {
129
- ...user.alias ?? {},
130
- ...base.alias ?? {}
131
- }
132
- };
133
- }
134
- function resolveDOBundleRolldownConfig(options) {
135
- const {
136
- output: userOutputOptions,
137
- input: _ignoredInput,
138
- cwd: _ignoredCwd,
139
- platform: _ignoredPlatform,
140
- watch: _ignoredWatch,
141
- external: userExternal,
142
- plugins: userPlugins,
143
- resolve: userResolve,
144
- tsconfig: userTsconfig,
145
- ...userInputOptions
146
- } = options.rolldownOptions ?? {};
147
- const {
148
- codeSplitting: _ignoredCodeSplitting,
149
- dir: _ignoredDir,
150
- file: _ignoredFile,
151
- format: _ignoredFormat,
152
- inlineDynamicImports: _ignoredInlineDynamicImports,
153
- ...safeUserOutputOptions
154
- } = userOutputOptions ?? {};
155
- const defaultExternalModules = [
156
- /^cloudflare:/,
157
- /^node:/,
158
- "buffer",
159
- "crypto",
160
- "events",
161
- "http",
162
- "https",
163
- "net",
164
- "os",
165
- "path",
166
- "stream",
167
- "tls",
168
- "url",
169
- "util",
170
- "zlib",
171
- "fs",
172
- "child_process",
173
- "async_hooks",
174
- "querystring",
175
- "string_decoder",
176
- "assert",
177
- "dns"
178
- ];
179
- return {
180
- inputOptions: {
181
- ...userInputOptions,
182
- input: options.inputFile,
183
- cwd: options.cwd,
184
- platform: "neutral",
185
- tsconfig: userTsconfig ?? resolve(options.cwd, "tsconfig.json"),
186
- external: mergeExternalOptions(defaultExternalModules, userExternal),
187
- plugins: mergePluginOptions(undefined, userPlugins),
188
- resolve: mergeResolveOptions({
189
- alias: {
190
- debug: options.debugShimPath
191
- }
192
- }, userResolve)
193
- },
194
- outputOptions: {
195
- ...safeUserOutputOptions,
196
- file: options.outFile,
197
- format: "esm",
198
- sourcemap: safeUserOutputOptions.sourcemap ?? options.sourcemap ?? false,
199
- minify: safeUserOutputOptions.minify ?? options.minify,
200
- codeSplitting: false
201
- }
202
- };
203
- }
204
- async function bundleDOFile(sourcePath, className, outDir, cwd, bundleOptions) {
205
- const { rolldown } = await import("rolldown");
206
- const fs = await import("node:fs/promises");
207
- await fs.mkdir(outDir, { recursive: true });
208
- const sourceCode = await fs.readFile(sourcePath, "utf-8");
209
- const transformedCode = (await transformDurableObject(sourceCode, sourcePath))?.code ?? stripDecoratorSyntax(sourceCode);
210
- const entryCode = `${transformedCode}
40
+ import { dirname, resolve } from "pathe";
211
41
 
212
- // Default export for worker (required by Miniflare)
213
- export default {
214
- async fetch(request) {
215
- return new Response('DO Worker for ${className}', { status: 200 });
216
- }
217
- };
218
- `;
219
- const tempFilePath = resolve(dirname(sourcePath), `.devflare-temp-${className}.ts`);
220
- await fs.writeFile(tempFilePath, entryCode, "utf-8");
221
- const classOutDir = resolve(outDir, className);
222
- try {
223
- await fs.rm(classOutDir, { recursive: true, force: true });
224
- } catch {}
225
- await fs.mkdir(classOutDir, { recursive: true });
226
- const debugShimCode = `
227
- // Debug module shim for local development
228
- const createDebug = (namespace) => {
229
- const logger = (...args) => {
230
- if (createDebug.enabled) console.debug(\`[\${namespace}]\`, ...args)
231
- }
232
- logger.enabled = false
233
- logger.namespace = namespace
234
- logger.extend = (sub) => createDebug(\`\${namespace}:\${sub}\`)
235
- return logger
236
- }
237
- createDebug.enabled = false
238
- createDebug.formatters = {}
239
- export default createDebug
240
- `;
241
- const debugShimPath = resolve(outDir, "_debug_shim.js");
242
- await fs.writeFile(debugShimPath, debugShimCode, "utf-8");
243
- const outFile = resolve(classOutDir, "index.js");
244
- const { inputOptions, outputOptions } = resolveDOBundleRolldownConfig({
245
- cwd,
246
- inputFile: tempFilePath,
247
- outFile,
248
- debugShimPath,
249
- rolldownOptions: bundleOptions?.rolldownOptions,
250
- sourcemap: bundleOptions?.sourcemap,
251
- minify: bundleOptions?.minify
252
- });
253
- const bundle = await rolldown(inputOptions);
254
- await bundle.write(outputOptions);
255
- await bundle.close();
256
- try {
257
- await fs.unlink(tempFilePath);
258
- } catch {}
259
- return resolve(classOutDir, "index.js");
260
- }
261
- async function bundleAllDOs(discovered, outDir, cwd, logger, bundleOptions) {
262
- const fs = await import("node:fs/promises");
263
- const bundles = new Map;
264
- const classes = new Map;
265
- const sourceFiles = new Map;
266
- const errors = [];
267
- for (const do_ of discovered) {
268
- const existing = sourceFiles.get(do_.filePath) || [];
269
- existing.push(do_.className);
270
- sourceFiles.set(do_.filePath, existing);
271
- }
272
- for (const do_ of discovered) {
273
- try {
274
- logger?.debug(`Bundling ${do_.className} from ${do_.filePath}`);
275
- const outFile = await bundleDOFile(do_.filePath, do_.className, outDir, cwd, bundleOptions);
276
- bundles.set(do_.bindingName, outFile);
277
- classes.set(do_.bindingName, do_.className);
278
- logger?.debug(` → ${outFile}`);
279
- } catch (error) {
280
- const err = error instanceof Error ? error : new Error(String(error));
281
- errors.push(err);
282
- logger?.error(`Failed to bundle ${do_.className}:`, err.message);
283
- }
284
- }
285
- return { bundles, classes, sourceFiles, errors };
286
- }
287
- function createDOBundler(options) {
288
- const { cwd, pattern, outDir, logger, onRebuild, rolldownOptions, sourcemap, minify } = options;
289
- let result = {
290
- bundles: new Map,
291
- classes: new Map,
292
- sourceFiles: new Map,
293
- errors: []
294
- };
295
- let watcher = null;
296
- let chokidarWatcher = null;
297
- async function build() {
298
- const discovered = await discoverDOs(cwd, pattern);
299
- if (discovered.length === 0) {
300
- logger?.debug("No DOs found matching pattern:", pattern);
301
- return result;
302
- }
303
- logger?.info(`Found ${discovered.length} Durable Object(s)`);
304
- for (const do_ of discovered) {
305
- logger?.info(` • ${do_.className} → ${do_.bindingName}`);
306
- }
307
- result = await bundleAllDOs(discovered, outDir, cwd, logger, {
308
- rolldownOptions,
309
- sourcemap,
310
- minify
311
- });
312
- if (result.errors.length === 0) {
313
- logger?.success(`Bundled ${result.bundles.size} DO(s) to ${outDir}`);
314
- }
315
- return result;
316
- }
317
- async function watch() {
318
- const chokidar = await import("chokidar");
319
- const files = await findFiles(pattern, { cwd });
320
- let dirsToWatch;
321
- if (files.length > 0) {
322
- dirsToWatch = [...new Set(files.map((f) => dirname(f)))];
323
- } else {
324
- const patternDir = dirname(pattern);
325
- const absolutePatternDir = resolve(cwd, patternDir === "." ? "" : patternDir) || cwd;
326
- dirsToWatch = [absolutePatternDir];
327
- logger?.debug(`No DO files yet, watching pattern directory: ${absolutePatternDir}`);
328
- }
329
- logger?.info(`Watching ${files.length} DO file(s) in ${dirsToWatch.length} director(ies)...`);
330
- const isWindows = process.platform === "win32";
331
- chokidarWatcher = chokidar.watch(dirsToWatch, {
332
- ignoreInitial: true,
333
- usePolling: isWindows,
334
- interval: isWindows ? 300 : undefined,
335
- awaitWriteFinish: {
336
- stabilityThreshold: 100,
337
- pollInterval: 50
338
- },
339
- depth: 0
340
- });
341
- const normalizePath = (p) => {
342
- let normalized = p.replace(/\\/g, "/");
343
- if (isWindows && /^[a-zA-Z]:/.test(normalized)) {
344
- normalized = normalized[0].toLowerCase() + normalized.slice(1);
345
- }
346
- return normalized;
347
- };
348
- const isMatch = picomatch(pattern, {
349
- cwd,
350
- dot: true,
351
- matchBase: false
352
- });
353
- const matchesPattern = (filePath) => {
354
- const normalizedPath = normalizePath(filePath);
355
- const relativePath = relative(normalizePath(cwd), normalizedPath);
356
- return isMatch(relativePath);
357
- };
358
- let isRebuilding = false;
359
- let pendingRebuild = null;
360
- let rebuildTimeout = null;
361
- const scheduleRebuild = (changedPath) => {
362
- if (rebuildTimeout) {
363
- clearTimeout(rebuildTimeout);
364
- }
365
- rebuildTimeout = setTimeout(() => {
366
- triggerRebuild(changedPath);
367
- }, 150);
368
- };
369
- const triggerRebuild = async (changedPath) => {
370
- if (isRebuilding) {
371
- pendingRebuild = changedPath;
372
- logger?.debug(`Rebuild already in progress, queuing: ${changedPath}`);
373
- return;
374
- }
375
- isRebuilding = true;
376
- try {
377
- logger?.info(`DO file changed: ${changedPath}`);
378
- logger?.info("Rebuilding DOs...");
379
- const startTime = Date.now();
380
- result = await build();
381
- const elapsed = Date.now() - startTime;
382
- logger?.success(`DO rebuild complete (${elapsed}ms)`);
383
- await onRebuild?.(result);
384
- } catch (error) {
385
- logger?.error("DO rebuild failed:", error);
386
- } finally {
387
- isRebuilding = false;
388
- if (pendingRebuild) {
389
- const nextPath = pendingRebuild;
390
- pendingRebuild = null;
391
- triggerRebuild(nextPath);
392
- }
393
- }
394
- };
395
- chokidarWatcher.on("change", (filePath) => {
396
- if (matchesPattern(filePath)) {
397
- logger?.debug(`File changed: ${filePath}`);
398
- scheduleRebuild(filePath);
399
- }
400
- });
401
- chokidarWatcher.on("add", (filePath) => {
402
- if (matchesPattern(filePath)) {
403
- logger?.debug(`File added: ${filePath}`);
404
- scheduleRebuild(filePath);
405
- }
406
- });
407
- chokidarWatcher.on("unlink", (filePath) => {
408
- if (matchesPattern(filePath)) {
409
- logger?.debug(`File removed: ${filePath}`);
410
- scheduleRebuild(filePath);
411
- }
412
- });
413
- chokidarWatcher.on("ready", () => {
414
- logger?.info("DO file watcher ready");
415
- });
416
- chokidarWatcher.on("error", (error) => {
417
- logger?.error("DO file watcher error:", error);
418
- });
419
- }
420
- async function close() {
421
- if (watcher) {
422
- await watcher.close();
423
- watcher = null;
424
- }
425
- if (chokidarWatcher) {
426
- await chokidarWatcher.close();
427
- chokidarWatcher = null;
428
- }
429
- }
430
- function getResult() {
431
- return result;
432
- }
433
- return {
434
- build,
435
- watch,
436
- close,
437
- getResult
438
- };
439
- }
440
42
  // src/browser-shim/server.ts
441
43
  import { homedir } from "node:os";
442
44
  import { join } from "node:path";
@@ -658,10 +260,10 @@ function createBrowserShim(options = {}) {
658
260
  res.end("Not found");
659
261
  }
660
262
  function readBody(req) {
661
- return new Promise((resolve2, reject) => {
263
+ return new Promise((resolve, reject) => {
662
264
  const chunks = [];
663
265
  req.on("data", (chunk) => chunks.push(chunk));
664
- req.on("end", () => resolve2(Buffer.concat(chunks).toString()));
266
+ req.on("end", () => resolve(Buffer.concat(chunks).toString()));
665
267
  req.on("error", reject);
666
268
  });
667
269
  }
@@ -802,10 +404,10 @@ function createBrowserShim(options = {}) {
802
404
  });
803
405
  });
804
406
  }
805
- await new Promise((resolve2, reject) => {
407
+ await new Promise((resolve, reject) => {
806
408
  server.on("error", reject);
807
409
  server.listen(port, host, () => {
808
- resolve2();
410
+ resolve();
809
411
  });
810
412
  });
811
413
  logger?.success(`Browser shim server ready on http://${host}:${port}`);
@@ -815,8 +417,8 @@ function createBrowserShim(options = {}) {
815
417
  await closeSession(sessionId, 3, "ServerShutdown");
816
418
  }
817
419
  if (server) {
818
- await new Promise((resolve2) => {
819
- server.close(() => resolve2());
420
+ await new Promise((resolve) => {
421
+ server.close(() => resolve());
820
422
  });
821
423
  server = null;
822
424
  }
@@ -1298,6 +900,12 @@ var DEFAULT_EMAIL_ENTRY_FILES = [
1298
900
  "src/email.mts",
1299
901
  "src/email.mjs"
1300
902
  ];
903
+ var DEFAULT_TRANSPORT_ENTRY_FILES = [
904
+ "src/transport.ts",
905
+ "src/transport.js",
906
+ "src/transport.mts",
907
+ "src/transport.mjs"
908
+ ];
1301
909
  var INTERNAL_APP_SERVICE_BINDING = "__DEVFLARE_APP";
1302
910
  function formatErrorMessage(error) {
1303
911
  return error instanceof Error ? error.message : String(error);
@@ -1315,7 +923,7 @@ async function resolveWorkerHandlerPath(cwd, configuredPath, defaultEntries) {
1315
923
  candidates.add(defaultEntry);
1316
924
  }
1317
925
  for (const candidate of candidates) {
1318
- const absolutePath = resolve2(cwd, candidate);
926
+ const absolutePath = resolve(cwd, candidate);
1319
927
  try {
1320
928
  await fs.access(absolutePath);
1321
929
  return absolutePath;
@@ -1337,35 +945,29 @@ function hasWorkerSurfacePaths(surfacePaths) {
1337
945
  return Object.values(surfacePaths).some((surfacePath) => typeof surfacePath === "string" && surfacePath.length > 0);
1338
946
  }
1339
947
  function addWorkerWatchRoots(roots, cwd, configuredPath, defaultEntries) {
1340
- if (configuredPath === false) {
948
+ if (configuredPath === false || configuredPath === null) {
1341
949
  return;
1342
950
  }
1343
951
  if (typeof configuredPath === "string" && configuredPath) {
1344
- roots.add(dirname2(resolve2(cwd, configuredPath)));
952
+ roots.add(dirname(resolve(cwd, configuredPath)));
1345
953
  return;
1346
954
  }
1347
955
  for (const defaultEntry of defaultEntries) {
1348
- roots.add(dirname2(resolve2(cwd, defaultEntry)));
956
+ roots.add(dirname(resolve(cwd, defaultEntry)));
1349
957
  }
1350
958
  }
1351
959
  function collectWorkerWatchRoots(cwd, config, mainWorkerSurfacePaths) {
1352
960
  const roots = new Set;
1353
- const addFileParent = (filePath) => {
1354
- if (typeof filePath !== "string" || !filePath) {
1355
- return;
1356
- }
1357
- roots.add(dirname2(resolve2(cwd, filePath)));
1358
- };
1359
961
  for (const surfacePath of Object.values(mainWorkerSurfacePaths)) {
1360
962
  if (surfacePath) {
1361
- roots.add(dirname2(surfacePath));
963
+ roots.add(dirname(surfacePath));
1362
964
  }
1363
965
  }
1364
966
  addWorkerWatchRoots(roots, cwd, config.files?.fetch, DEFAULT_FETCH_ENTRY_FILES);
1365
967
  addWorkerWatchRoots(roots, cwd, config.files?.queue, DEFAULT_QUEUE_ENTRY_FILES);
1366
968
  addWorkerWatchRoots(roots, cwd, config.files?.scheduled, DEFAULT_SCHEDULED_ENTRY_FILES);
1367
969
  addWorkerWatchRoots(roots, cwd, config.files?.email, DEFAULT_EMAIL_ENTRY_FILES);
1368
- addFileParent(config.files?.transport);
970
+ addWorkerWatchRoots(roots, cwd, config.files?.transport, DEFAULT_TRANSPORT_ENTRY_FILES);
1369
971
  const routeDirectory = getRouteDirectoryCandidate(cwd, config);
1370
972
  if (routeDirectory) {
1371
973
  roots.add(routeDirectory.absoluteDir);
@@ -1957,36 +1559,23 @@ function createDevServer(options) {
1957
1559
  let bundledMainWorkerScriptPath = null;
1958
1560
  let currentDoResult = null;
1959
1561
  let mainWorkerRoutes = null;
1562
+ let generatedViteConfigPath = null;
1960
1563
  let reloadChain = Promise.resolve();
1961
1564
  async function bundleMainWorker() {
1962
- if (!mainWorkerScriptPath) {
1565
+ if (!mainWorkerScriptPath || !config) {
1963
1566
  bundledMainWorkerScriptPath = null;
1964
1567
  return;
1965
1568
  }
1966
- if (typeof Bun === "undefined" || typeof Bun.build !== "function") {
1967
- throw new Error("Worker-only dev mode requires the Bun runtime for main worker bundling");
1968
- }
1969
- const fs = await import("node:fs/promises");
1970
- const outDir = resolve2(cwd, ".devflare", "worker-bundles");
1971
- await fs.rm(outDir, { recursive: true, force: true });
1972
- await fs.mkdir(outDir, { recursive: true });
1973
- const result = await Bun.build({
1974
- entrypoints: [mainWorkerScriptPath],
1975
- outdir: outDir,
1976
- target: "browser",
1977
- conditions: ["browser"],
1978
- format: "esm",
1979
- minify: false,
1980
- splitting: false,
1981
- external: ["cloudflare:workers", "cloudflare:*", "node:*"]
1569
+ bundledMainWorkerScriptPath = await bundleWorkerEntry({
1570
+ cwd,
1571
+ inputFile: mainWorkerScriptPath,
1572
+ outFile: resolve(cwd, ".devflare", "worker-entrypoints", "main.js"),
1573
+ rolldownOptions: config.rolldown?.options,
1574
+ sourcemap: config.rolldown?.sourcemap,
1575
+ minify: config.rolldown?.minify,
1576
+ target: config.rolldown?.target,
1577
+ logger
1982
1578
  });
1983
- if (!result.success || result.outputs.length === 0) {
1984
- const logs = result.logs.map((log) => ("message" in log) ? log.message : String(log)).join(`
1985
- `);
1986
- throw new Error(`Failed to bundle main worker:
1987
- ${logs}`);
1988
- }
1989
- bundledMainWorkerScriptPath = result.outputs[0].path;
1990
1579
  logger?.debug(`Bundled main worker → ${bundledMainWorkerScriptPath}`);
1991
1580
  }
1992
1581
  function buildMiniflareConfig(doResult) {
@@ -1994,7 +1583,7 @@ ${logs}`);
1994
1583
  throw new Error("Config not loaded");
1995
1584
  const loadedConfig = config;
1996
1585
  const bindings = loadedConfig.bindings ?? {};
1997
- const persistPath = resolve2(cwd, ".devflare/data");
1586
+ const persistPath = resolve(cwd, ".devflare/data");
1998
1587
  const appWorkerName = loadedConfig.name;
1999
1588
  const shouldRunMainWorker = !enableVite && (hasWorkerSurfacePaths(mainWorkerSurfacePaths) || Boolean(mainWorkerRoutes?.routes.length));
2000
1589
  const queueProducers = (() => {
@@ -2253,7 +1842,7 @@ ${logs}`);
2253
1842
  }
2254
1843
  async function resolveWorkerConfigWatchPath() {
2255
1844
  if (configPath) {
2256
- const explicitPath = resolve2(cwd, configPath);
1845
+ const explicitPath = resolve(cwd, configPath);
2257
1846
  const fs = await import("node:fs/promises");
2258
1847
  try {
2259
1848
  await fs.access(explicitPath);
@@ -2271,7 +1860,7 @@ ${logs}`);
2271
1860
  const composedMainEntry = await prepareComposedWorkerEntrypoint(cwd, config, undefined, {
2272
1861
  devInternalEmail: true
2273
1862
  });
2274
- mainWorkerScriptPath = composedMainEntry ? resolve2(cwd, composedMainEntry) : null;
1863
+ mainWorkerScriptPath = composedMainEntry ? resolve(cwd, composedMainEntry) : null;
2275
1864
  if (mainWorkerScriptPath) {
2276
1865
  await bundleMainWorker();
2277
1866
  } else {
@@ -2407,7 +1996,7 @@ ${logs}`);
2407
1996
  if (!miniflare || !config?.bindings?.d1)
2408
1997
  return;
2409
1998
  const { existsSync: existsSync2, readdirSync, readFileSync } = await import("node:fs");
2410
- const migrationsDir = resolve2(cwd, "migrations");
1999
+ const migrationsDir = resolve(cwd, "migrations");
2411
2000
  if (!existsSync2(migrationsDir)) {
2412
2001
  logger?.debug("No migrations/ directory found, skipping D1 migrations");
2413
2002
  return;
@@ -2420,7 +2009,7 @@ ${logs}`);
2420
2009
  logger?.info(`Running ${files.length} D1 migration(s)...`);
2421
2010
  const allStatements = [];
2422
2011
  for (const file of files) {
2423
- const sql = readFileSync(resolve2(migrationsDir, file), "utf-8");
2012
+ const sql = readFileSync(resolve(migrationsDir, file), "utf-8");
2424
2013
  const cleanedSql = sql.split(`
2425
2014
  `).filter((line) => !line.trim().startsWith("--")).join(`
2426
2015
  `);
@@ -2459,6 +2048,9 @@ ${logs}`);
2459
2048
  async function startVite() {
2460
2049
  const { spawn } = await import("node:child_process");
2461
2050
  const args = ["vite", "dev", "--port", String(vitePort)];
2051
+ if (generatedViteConfigPath) {
2052
+ args.push("--config", generatedViteConfigPath);
2053
+ }
2462
2054
  viteProcess = spawn("bunx", args, {
2463
2055
  cwd,
2464
2056
  stdio: ["inherit", "pipe", "pipe"],
@@ -2490,6 +2082,16 @@ ${logs}`);
2490
2082
  setLocalSendEmailBindings(config.bindings?.sendEmail ?? {});
2491
2083
  resolvedWorkerConfigPath = await resolveWorkerConfigWatchPath();
2492
2084
  logger?.debug("Loaded config:", config.name);
2085
+ if (enableVite) {
2086
+ const viteProject = await detectViteProject(cwd);
2087
+ generatedViteConfigPath = await writeGeneratedViteConfig({
2088
+ cwd,
2089
+ configPath,
2090
+ localConfigPath: viteProject.viteConfigPath,
2091
+ bridgePort: miniflarePort
2092
+ });
2093
+ logger?.debug(`Generated Vite config → ${generatedViteConfigPath}`);
2094
+ }
2493
2095
  await refreshWorkerOnlySurfaceState();
2494
2096
  if (!enableVite && (hasWorkerSurfacePaths(mainWorkerSurfacePaths) || Boolean(mainWorkerRoutes?.routes.length))) {
2495
2097
  const detectedWorkerHandlers = Object.entries(mainWorkerSurfacePaths).filter(([, surfacePath]) => !!surfacePath).map(([surfaceName, surfacePath]) => `${surfaceName}=${surfacePath}`);
@@ -2537,7 +2139,7 @@ ${logs}`);
2537
2139
  const doPattern = config.files?.durableObjects;
2538
2140
  let doResult = null;
2539
2141
  if (typeof doPattern === "string" && doPattern) {
2540
- const outDir = resolve2(cwd, ".devflare/do-bundles");
2142
+ const outDir = resolve(cwd, ".devflare/do-bundles");
2541
2143
  doBundler = createDOBundler({
2542
2144
  cwd,
2543
2145
  pattern: doPattern,
@@ -2560,7 +2162,7 @@ ${logs}`);
2560
2162
  if (enableVite) {
2561
2163
  await startVite();
2562
2164
  } else {
2563
- logger?.info("Vite startup skipped (no local vite.config.* found for this package)");
2165
+ logger?.info("Vite startup skipped (no effective Vite config found for this package)");
2564
2166
  }
2565
2167
  await new Promise((r) => setTimeout(r, 1000));
2566
2168
  await runD1Migrations();
@@ -2605,11 +2207,11 @@ async function createLogWriter(cwd, options) {
2605
2207
  const fs = await import("node:fs");
2606
2208
  let logPath;
2607
2209
  if (options.logTemp) {
2608
- logPath = resolve3(cwd, ".log");
2210
+ logPath = resolve2(cwd, ".log");
2609
2211
  } else {
2610
2212
  const now = new Date;
2611
2213
  const timestamp = now.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
2612
- logPath = resolve3(cwd, `.log-${timestamp}`);
2214
+ logPath = resolve2(cwd, `.log-${timestamp}`);
2613
2215
  }
2614
2216
  const fileStream = fs.createWriteStream(logPath, { flags: "w" });
2615
2217
  const ansiRegex = /\x1b\[[0-9;]*m/g;
@@ -2640,13 +2242,14 @@ async function runDevCommand(parsed, logger, options) {
2640
2242
  const persistEnabled = parsed.options.persist === true;
2641
2243
  const debugEnabled = parsed.options.debug === true || process.env.DEVFLARE_DEBUG === "true";
2642
2244
  const verbose = parsed.options.verbose === true || debugEnabled;
2643
- const viteProject = await detectViteProject(cwd);
2245
+ const config = await loadConfig({ cwd, configFile: configPath });
2246
+ const viteProject = resolveEffectiveViteProject(await detectViteProject(cwd), config);
2644
2247
  const logWriter = await createLogWriter(cwd, {
2645
2248
  log: logEnabled,
2646
2249
  logTemp: logTempEnabled
2647
2250
  });
2648
2251
  if (logWriter) {
2649
- const logFile = relative2(cwd, logWriter.path) || ".log";
2252
+ const logFile = relative(cwd, logWriter.path) || ".log";
2650
2253
  logger.info(`\uD83D\uDCDD Logging enabled → ${logFile}`);
2651
2254
  }
2652
2255
  const devLogger = createConsola({
@@ -2673,15 +2276,15 @@ async function runDevCommand(parsed, logger, options) {
2673
2276
  logger.info("\uD83D\uDE80 Devflare Unified Dev Server");
2674
2277
  logger.info(" ├─ Vite: Full HMR for frontend");
2675
2278
  logger.info(" ├─ Miniflare: All Cloudflare bindings");
2676
- logger.info(" ├─ Rolldown: Fast DO bundling with watch");
2279
+ logger.info(" ├─ Rolldown: Worker + DO bundling with watch");
2677
2280
  logger.info(" └─ Bridge: WebSocket RPC connection");
2678
2281
  } else {
2679
2282
  logger.info("\uD83D\uDE80 Devflare Worker Dev Server");
2680
2283
  logger.info(" ├─ Miniflare: All Cloudflare bindings");
2681
- logger.info(" ├─ Rolldown: Fast DO bundling with watch");
2682
- logger.info(" └─ Vite: Disabled (no local vite.config.* found)");
2284
+ logger.info(" ├─ Rolldown: Worker + DO bundling with watch");
2285
+ logger.info(" └─ Vite: Disabled (no effective Vite config found)");
2683
2286
  if (viteProject.wantsViteIntegration) {
2684
- logger.warn("Vite-related dependencies were found, but no local vite.config.* was detected");
2287
+ logger.warn("Vite-related settings were detected, but no effective Vite config was available");
2685
2288
  logger.warn("Skipping Vite startup and running in worker-only mode");
2686
2289
  }
2687
2290
  }