void 0.7.13 → 0.8.1

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 (133) hide show
  1. package/dist/{agents-DqkFfc2c.mjs → agents-Dkgafwfr.mjs} +1 -1
  2. package/dist/{auth-cmd-bS88H4Lb.mjs → auth-cmd-B1-7r6ng.mjs} +3 -3
  3. package/dist/{better-auth-shared-D2JaoCs4.mjs → better-auth-shared-CbAF6sOM.mjs} +1 -1
  4. package/dist/{better-auth-shared-HXlHRw02.d.mts → better-auth-shared-Fzyk5yS1.d.mts} +2 -7
  5. package/dist/{cache-DUDCNty6.mjs → cache-CGYC-2LW.mjs} +5 -5
  6. package/dist/{cancel-deploy-DOuSbPwX.mjs → cancel-deploy-B4j31z7d.mjs} +4 -4
  7. package/dist/canonical-json-CEyQaVDa.mjs +13 -0
  8. package/dist/cli/cli.mjs +44 -31
  9. package/dist/{client-C_DYiXeu.mjs → client-C-nH6yz3.mjs} +5 -5
  10. package/dist/{collect-HqAuilH6.mjs → collect-9rO3JFNM.mjs} +1 -1
  11. package/dist/{config-DyUSwbeX.mjs → config--wXmVOoe.mjs} +10 -8
  12. package/dist/{config-CeRQg3Lz.mjs → config-BVEC0lti.mjs} +11 -2
  13. package/dist/{config-CvHtTM0q.mjs → config-hhMYPVRT.mjs} +3 -12
  14. package/dist/{create-project-hvFe6rbI.mjs → create-project-BGnQMy5N.mjs} +44 -18
  15. package/dist/{db-YEYSTfGw.mjs → db-CT5Xg6ds.mjs} +42 -37
  16. package/dist/{delete-XyM_VD9o.mjs → delete-eMGTHHB9.mjs} +5 -5
  17. package/dist/{deploy-CPAEgVQm.mjs → deploy-DbmbcKID.mjs} +209 -127
  18. package/dist/{discover-CKGQpis8.mjs → discover-C8N1I4tK.mjs} +9 -6
  19. package/dist/{domain-Dvj7u8tr.mjs → domain-UfLrp2vc.mjs} +64 -22
  20. package/dist/{drizzle-Cx7cVwrE.mjs → drizzle-DmaANP4-.mjs} +1 -1
  21. package/dist/{env-Bjl0rzuf.mjs → env-Cvdbbm_N.mjs} +7 -7
  22. package/dist/{env-helpers-Dw95CU8e.d.mts → env-helpers-Bt9yYg5I.d.mts} +1 -1
  23. package/dist/{env-types-BSQghXbx.mjs → env-types-D6qI1ThV.mjs} +8 -19
  24. package/dist/{env-validation-Buy5pe3u.mjs → env-validation-DBJsxZLz.mjs} +4 -4
  25. package/dist/{gen-DU8NIdrH.mjs → gen-Cn2hZ8RR.mjs} +37 -31
  26. package/dist/{handler-DJTuxB10.d.mts → handler-BPhL8AmU.d.mts} +3 -3
  27. package/dist/{headers-CuVHn6Kl.mjs → headers-wAPJigtE.mjs} +3 -3
  28. package/dist/index.d.mts +3 -3
  29. package/dist/index.mjs +273 -104
  30. package/dist/{init-Br5DTaG7.mjs → init-CbNCFFWx.mjs} +105 -35
  31. package/dist/{link-DdXqlUiJ.mjs → link-C5x1E-XZ.mjs} +5 -5
  32. package/dist/{list-9Bxa5Odn.mjs → list-ezL7hqDV.mjs} +5 -5
  33. package/dist/{login-C_MfDjhu.mjs → login-CkLr-AaI.mjs} +3 -3
  34. package/dist/{logs-DjqCpEER.mjs → logs-MnGILmLz.mjs} +4 -4
  35. package/dist/{mcp-vA0880_K.mjs → mcp-B84MmVAW.mjs} +3 -3
  36. package/dist/{node-gxRQXIRT.mjs → node-fpywdcbN.mjs} +6 -6
  37. package/dist/{output-D3g5otMC.mjs → output-DeiS4oEX.mjs} +1 -1
  38. package/dist/pages/client.d.mts +1 -1
  39. package/dist/pages/head-client.d.mts +1 -1
  40. package/dist/pages/head.d.mts +1 -1
  41. package/dist/pages/index.d.mts +28 -3
  42. package/dist/pages/index.mjs +6 -5
  43. package/dist/pages/islands-plugin.d.mts +1 -1
  44. package/dist/pages/islands-plugin.mjs +7 -5
  45. package/dist/pages/protocol.d.mts +2 -2
  46. package/dist/pages/protocol.mjs +1 -1
  47. package/dist/{pathe.M-eThtNZ-D-kmWkCS.mjs → pathe.M-eThtNZ-BrPhGF_K.mjs} +1 -1
  48. package/dist/plan-aD3wRDqF.mjs +271 -0
  49. package/dist/{plugin-inference-BexV6LL3.mjs → plugin-inference-Ba1uGdIy.mjs} +26 -20
  50. package/dist/{prepare-BEkH89oX.mjs → prepare-BrAISonG.mjs} +29 -19
  51. package/dist/{preset-CYQh93UF.mjs → preset-BRsCi8ZB.mjs} +10 -8
  52. package/dist/{project-DhB4A8_g.mjs → project-CH9pdo16.mjs} +1 -1
  53. package/dist/{project-cmd-CeXO06hG.mjs → project-cmd-JQUzv3qF.mjs} +10 -10
  54. package/dist/project-paths-CCMrHYQm.mjs +100 -0
  55. package/dist/{project-slug-KRvHQEQI.mjs → project-slug-De4UjtD9.mjs} +0 -1
  56. package/dist/{project-tsconfig-Bfy55qvs.mjs → project-tsconfig-HAGjfY6w.mjs} +10 -5
  57. package/dist/{protocol-D7r_deVZ.d.mts → protocol-mcSsdMij.d.mts} +3 -3
  58. package/dist/providers--_pvxpNN.d.mts +7 -0
  59. package/dist/{resolve-project-DdjLQ2tB.mjs → resolve-project-CNpUbUHd.mjs} +1 -1
  60. package/dist/{rollback-C8OSMxmq.mjs → rollback-CoAAvC18.mjs} +4 -4
  61. package/dist/{route-types-qVL-v-CB.mjs → route-types-jxRfWuCb.mjs} +1 -1
  62. package/dist/{runner-C0d3lJo1.mjs → runner-BkGBfr4e.mjs} +4 -2
  63. package/dist/{runner-pg-Bd_172vF.mjs → runner-pg-BXP0JpHW.mjs} +1 -1
  64. package/dist/runtime/ai.mjs +2 -2
  65. package/dist/runtime/auth.d.mts +1 -1
  66. package/dist/runtime/better-auth-pg.d.mts +1 -1
  67. package/dist/runtime/better-auth-pg.mjs +2 -2
  68. package/dist/runtime/better-auth.d.mts +1 -1
  69. package/dist/runtime/better-auth.mjs +2 -2
  70. package/dist/runtime/client.d.mts +2 -2
  71. package/dist/runtime/client.mjs +1 -1
  72. package/dist/runtime/env-helpers.d.mts +1 -1
  73. package/dist/runtime/env-public-client.d.mts +1 -1
  74. package/dist/runtime/env-public.d.mts +2 -2
  75. package/dist/runtime/env-public.mjs +1 -1
  76. package/dist/runtime/env.mjs +1 -1
  77. package/dist/runtime/fetch-stream.d.mts +1 -1
  78. package/dist/runtime/fetch-stream.mjs +1 -1
  79. package/dist/runtime/fetch.d.mts +1 -1
  80. package/dist/runtime/fetch.mjs +1 -1
  81. package/dist/runtime/handler.d.mts +1 -1
  82. package/dist/runtime/handler.mjs +1 -1
  83. package/dist/runtime/isr.mjs +1 -1
  84. package/dist/runtime/live.d.mts +2 -2
  85. package/dist/runtime/live.mjs +1 -1
  86. package/dist/runtime/migration-handler.d.mts +33 -1
  87. package/dist/runtime/migration-handler.mjs +332 -24
  88. package/dist/runtime/remote/index.mjs +1 -1
  89. package/dist/runtime/sandbox.d.mts +73 -3
  90. package/dist/runtime/sandbox.mjs +239 -3
  91. package/dist/runtime/validator.d.mts +1 -1
  92. package/dist/runtime/ws-server.d.mts +2 -2
  93. package/dist/runtime/ws.d.mts +3 -3
  94. package/dist/{scan-CKpylFj9.mjs → scan-2YmJkYAf.mjs} +4 -3
  95. package/dist/{scan-CkK1Hzpa.mjs → scan-DzzHqtiT.mjs} +82 -43
  96. package/dist/{secret-NDvlqAN2.mjs → secret-mz8t_CML.mjs} +5 -5
  97. package/dist/{skills-SAG0WyVn.mjs → skills-B_ynF4lA.mjs} +3 -3
  98. package/dist/{subcommand-prompt-q9T06dpg.mjs → subcommand-prompt-CPuV7tPW.mjs} +2 -2
  99. package/dist/sveltekit.mjs +1 -1
  100. package/dist/{validate-DW3ViKTA.mjs → validate-CNWm-PsL.mjs} +49 -9
  101. package/dist/{yarn-pnp-6LD6_3Ej.mjs → yarn-pnp-QgEYVC3V.mjs} +1 -1
  102. package/package.json +3 -2
  103. package/schema.json +5 -0
  104. package/skills/void/docs/guide/ai.md +1 -1
  105. package/skills/void/docs/guide/app-types.md +1 -1
  106. package/skills/void/docs/guide/database/postgresql.md +4 -3
  107. package/skills/void/docs/guide/edge/headers.md +1 -1
  108. package/skills/void/docs/guide/edge/static-assets.md +34 -0
  109. package/skills/void/docs/guide/sandboxes.md +3 -2
  110. package/skills/void/docs/guide/ssr.md +25 -2
  111. package/skills/void/docs/integrations/cloudflare.md +5 -3
  112. package/skills/void/docs/integrations/frameworks/overview.md +1 -1
  113. package/skills/void/docs/integrations/nodejs-bun-deno.md +10 -9
  114. package/skills/void/docs/reference/api.md +204 -13
  115. package/skills/void/docs/reference/cli.md +53 -7
  116. package/skills/void/docs/reference/config.md +27 -4
  117. package/skills/void/docs/reference/resource-inference.md +1 -1
  118. /package/dist/{auth-DmuALf16.d.mts → auth-SfRBhXan.d.mts} +0 -0
  119. /package/dist/{auth-migrations-BwLPwRgH.mjs → auth-migrations-BqJoGqGQ.mjs} +0 -0
  120. /package/dist/{cf-access-BW8K93Fm.mjs → cf-access-DKDsgwOU.mjs} +0 -0
  121. /package/dist/{defer-2ARBu8Et.mjs → defer-DkoEwda-.mjs} +0 -0
  122. /package/dist/{dist-q8b2Mjgb.mjs → dist-DR9sIMbM.mjs} +0 -0
  123. /package/dist/{dist-D2L3_KTK.mjs → dist-abzUneor.mjs} +0 -0
  124. /package/dist/{dotenv-HQNhRalS.mjs → dotenv-Bkoqyq9r.mjs} +0 -0
  125. /package/dist/{env-raw-DtfQ9E31.mjs → env-raw-zk5JQKZe.mjs} +0 -0
  126. /package/dist/{fetch-error-DZ868Xq4.d.mts → fetch-error-BbixZ9vW.d.mts} +0 -0
  127. /package/dist/{fetch-error-CEr0ACTl.mjs → fetch-error-rXermVU3.mjs} +0 -0
  128. /package/dist/{head-C7QW7UY1.d.mts → head-CZs8dMxG.d.mts} +0 -0
  129. /package/dist/{log-7ChR5Fbc.mjs → log-CWWZV4V1.mjs} +0 -0
  130. /package/dist/{magic-string.es-DS_lmuBe.mjs → magic-string.es-C1Fb0uxq.mjs} +0 -0
  131. /package/dist/{providers-BlFNPYen.mjs → providers-uC0PJg1c.mjs} +0 -0
  132. /package/dist/{standard-schema-D2qvCEYV.d.mts → standard-schema-WhHcCaqJ.d.mts} +0 -0
  133. /package/dist/{types-DG_Ynnyd.d.mts → types-C8wsi1hv.d.mts} +0 -0
package/dist/index.mjs CHANGED
@@ -1,26 +1,27 @@
1
1
  import { r as __toESM } from "./chunk-DJd-R1mw.mjs";
2
- import { a as join, n as dirname, o as relative } from "./pathe.M-eThtNZ-D-kmWkCS.mjs";
3
- import { u as require_picocolors } from "./output-D3g5otMC.mjs";
4
- import { n as loadVoidAuthConfig, t as findVoidAuthConfig } from "./config-CvHtTM0q.mjs";
5
- import { c as getToken, u as isStagingMode } from "./client-C_DYiXeu.mjs";
6
- import { r as readProjectConfig } from "./project-DhB4A8_g.mjs";
7
- import { C as isSandboxEnabled, S as SANDBOX_MIGRATION_TAG, _ as normalizeWranglerCompatibilityFlags, a as prerenderPagesNode, b as resolveLiveBindingConfig, c as filterLoadedEnv, d as LIVE_MIGRATION_TAG, f as ensureWranglerCompat, g as nextDurableObjectMigrationTag, h as hasDurableObjectSqliteMigration, i as prerenderPages, l as resolveRemoteMode, m as ensureWranglerNodejsAlsFlag, n as resolveDistDir, o as DEFAULT_PROXY_URL, p as ensureWranglerCompatibilityDate, r as resolveWorkerDirName, s as STAGING_PROXY_URL, u as stripInternalEnvKeys, v as readExistingLiveBindingConfig, w as resolveSandboxConfig, x as syncWranglerBindings, y as readWranglerCompat } from "./deploy-CPAEgVQm.mjs";
8
- import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-CeRQg3Lz.mjs";
9
- import { i as scanJobsSync, n as scanWebSocketRoutesSync, r as scanQueuesSync, t as scanRoutes } from "./scan-CkK1Hzpa.mjs";
10
- import { a as SSR_CLIENT_ENTRY_RELATIVE_PATHS, c as validateSsrEntries, f as isIdentifier, g as walk, i as inferProjectBindings, l as validateSsrEntry, m as isMemberExpression, n as FRAMEWORK_SCAN_DIRS, o as SSR_SERVER_ENTRY_RELATIVE_PATHS, p as isLiteral, r as detectFramework, t as DEFAULT_SCAN_DIRS, u as inferBindingsFromSource } from "./plugin-inference-BexV6LL3.mjs";
11
- import { i as voidWarn, n as voidError, r as voidLog, t as setShowTimestamp } from "./log-7ChR5Fbc.mjs";
12
- import { r as parseDotenvFile } from "./dotenv-HQNhRalS.mjs";
13
- import { i as generateDbDts, n as generateQueueTypes, r as DRIZZLE_OPERATORS, t as generateRouteTypes } from "./route-types-qVL-v-CB.mjs";
14
- import { n as ensureProjectTsconfig } from "./project-tsconfig-Bfy55qvs.mjs";
15
- import { t as discoverSchema } from "./discover-CKGQpis8.mjs";
16
- import { t as MagicString } from "./magic-string.es-DS_lmuBe.mjs";
17
- import { n as generateEnvTypes, t as findEnvFile } from "./env-types-BSQghXbx.mjs";
18
- import { a as loadUserEnvFile } from "./env-validation-Buy5pe3u.mjs";
19
- import { i as resolveLayoutIds } from "./scan-CKpylFj9.mjs";
20
- import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-CuVHn6Kl.mjs";
21
- import { n as generateVoidAuthDrizzleSchema, t as generateVoidAuthDatabaseModule } from "./drizzle-Cx7cVwrE.mjs";
22
- import { t as INTERNAL_BINDING_PREFIXES } from "./env-raw-DtfQ9E31.mjs";
23
- import { n as defer } from "./defer-2ARBu8Et.mjs";
2
+ import { a as join, n as dirname, s as relative } from "./pathe.M-eThtNZ-BrPhGF_K.mjs";
3
+ import { u as require_picocolors } from "./output-DeiS4oEX.mjs";
4
+ import { C as isSandboxEnabled, E as resolveAssetRoutingPolicy, S as SANDBOX_MIGRATION_TAG, T as hasDocumentRoutes, _ as normalizeWranglerCompatibilityFlags, a as prerenderPagesNode, b as resolveLiveBindingConfig, c as filterLoadedEnv, d as LIVE_MIGRATION_TAG, f as ensureWranglerCompat, g as nextDurableObjectMigrationTag, h as hasDurableObjectSqliteMigration, i as prerenderPages, l as resolveRemoteMode, m as ensureWranglerNodejsAlsFlag, n as resolveDistDir, o as DEFAULT_PROXY_URL, p as ensureWranglerCompatibilityDate, r as resolveWorkerDirName, s as STAGING_PROXY_URL, u as stripInternalEnvKeys, v as readExistingLiveBindingConfig, w as resolveSandboxConfig, x as syncWranglerBindings, y as readWranglerCompat } from "./deploy-DbmbcKID.mjs";
5
+ import { i as resolveProjectPaths, n as readProjectPaths, t as assertNoRootSourceConventionConflicts } from "./project-paths-CCMrHYQm.mjs";
6
+ import { n as loadVoidAuthConfig, t as findVoidAuthConfig } from "./config-hhMYPVRT.mjs";
7
+ import { c as getToken, u as isStagingMode } from "./client-C-nH6yz3.mjs";
8
+ import { r as readProjectConfig } from "./project-CH9pdo16.mjs";
9
+ import { c as getDatabaseDialect, f as readConfig, l as isNodeTarget, p as resolveBindingNames } from "./config-BVEC0lti.mjs";
10
+ import { a as scanQueuesSync, i as scanWebSocketRoutesSync, n as scanRoutePatternsSync, o as scanJobsSync, r as scanRoutes, t as scanMiddlewareSync } from "./scan-DzzHqtiT.mjs";
11
+ import { a as SSR_CLIENT_ENTRY_RELATIVE_PATHS, c as validateSsrEntries, f as isIdentifier, g as walk, i as inferProjectBindings, l as validateSsrEntry, m as isMemberExpression, n as FRAMEWORK_SCAN_DIRS, o as SSR_SERVER_ENTRY_RELATIVE_PATHS, p as isLiteral, r as detectFramework, t as DEFAULT_SCAN_DIRS, u as inferBindingsFromSource } from "./plugin-inference-Ba1uGdIy.mjs";
12
+ import { i as voidWarn, n as voidError, r as voidLog, t as setShowTimestamp } from "./log-CWWZV4V1.mjs";
13
+ import { r as parseDotenvFile } from "./dotenv-Bkoqyq9r.mjs";
14
+ import { i as generateDbDts, n as generateQueueTypes, r as DRIZZLE_OPERATORS, t as generateRouteTypes } from "./route-types-jxRfWuCb.mjs";
15
+ import { n as ensureProjectTsconfig } from "./project-tsconfig-HAGjfY6w.mjs";
16
+ import { t as discoverSchema } from "./discover-C8N1I4tK.mjs";
17
+ import { t as MagicString } from "./magic-string.es-C1Fb0uxq.mjs";
18
+ import { n as findEnvFileForPaths, r as generateEnvTypes, t as findEnvFile } from "./env-types-D6qI1ThV.mjs";
19
+ import { a as loadUserEnvFile } from "./env-validation-DBJsxZLz.mjs";
20
+ import { i as resolveLayoutIds } from "./scan-2YmJkYAf.mjs";
21
+ import { n as lintDuplicateSources, r as mergeRoutingRules, t as lintDestinationSplats } from "./headers-wAPJigtE.mjs";
22
+ import { n as generateVoidAuthDrizzleSchema, t as generateVoidAuthDatabaseModule } from "./drizzle-DmaANP4-.mjs";
23
+ import { t as INTERNAL_BINDING_PREFIXES } from "./env-raw-zk5JQKZe.mjs";
24
+ import { n as defer } from "./defer-DkoEwda-.mjs";
24
25
  import { VoidAssetRewriteError, defineHandler, defineHead, defineMiddleware, defineQueue, defineRender, defineScheduled } from "./runtime/handler.mjs";
25
26
  import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readdirSync, rmSync, writeFileSync, writeSync } from "node:fs";
26
27
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -432,8 +433,9 @@ function devTriggersHintPlugin(root, options) {
432
433
  name: "void:dev-triggers-hint",
433
434
  apply: "serve",
434
435
  configureServer(server) {
435
- const jobs = scanJobsSync(root);
436
- const queues = scanQueuesSync(root);
436
+ const paths = readProjectPaths(root);
437
+ const jobs = scanJobsSync(root, paths.cronsDir);
438
+ const queues = scanQueuesSync(root, paths.queuesDir);
437
439
  if (jobs.length === 0 && queues.length === 0) return;
438
440
  const afterReady = (fn) => {
439
441
  if (server.httpServer) server.httpServer.once("listening", () => setTimeout(fn, 100));
@@ -484,9 +486,13 @@ const VIRTUAL_SCHEMA_DB_ID = "\0virtual:void-db-schema";
484
486
  */
485
487
  function drizzlePlugin(root, hasRouteTypes, dialect = "sqlite", options = {}) {
486
488
  const outDir = join(root, ".void");
487
- const schema = discoverSchema(root);
489
+ const paths = readProjectPaths(root);
490
+ const schema = discoverSchema(root, paths);
488
491
  const hasDbRuntime = schema != null || options.forceVirtualDb === true;
489
- ensureProjectTsconfig(root, hasRouteTypes, hasDbRuntime, schema != null, { redirectDbModule: options.redirectDbTypePaths });
492
+ ensureProjectTsconfig(root, hasRouteTypes, hasDbRuntime, schema != null, {
493
+ redirectDbModule: options.redirectDbTypePaths,
494
+ paths
495
+ });
490
496
  if (hasDbRuntime) generateDbDts(outDir, schema ?? null, dialect);
491
497
  let isBuild = false;
492
498
  return {
@@ -591,13 +597,14 @@ export function createDb(d1) {
591
597
  },
592
598
  async configureServer(server) {
593
599
  if (!schema) return;
594
- const migrationsDir = join(root, "db", "migrations");
600
+ const migrationsDir = paths.migrationsDir;
595
601
  if (!(existsSync(migrationsDir) && readdirSync(migrationsDir).some((f) => f.endsWith(".sql")))) {
596
602
  if (dialect === "sqlite") {
597
- const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-C0d3lJo1.mjs");
603
+ const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-BkGBfr4e.mjs");
598
604
  if ((await getApplicationTables(getMiniflareDatabasePath(options.localD1PersistRoot ?? join(root, ".void"), options.localD1DatabaseId ?? "local")).catch(() => [])).length > 0) return;
599
605
  }
600
- voidWarn("Schema found but no migrations in db/migrations/.\n `void db push` syncs the schema to your local DB without creating migration files.\n Run `void db generate` to create migration files for dev startup and deploy.");
606
+ voidWarn(`Schema found but no migrations in ${paths.sourceRelative("db/migrations")}/.\n \`void db push\` syncs the schema to your local DB without creating migration files.
607
+ Run \`void db generate\` to create migration files for dev startup and deploy.`);
601
608
  }
602
609
  const watchPath = schema.path;
603
610
  const regenerate = (path) => {
@@ -1083,11 +1090,12 @@ function frameworkTriggersDispatchPath(root) {
1083
1090
  * without depending on `configResolved` ordering.
1084
1091
  */
1085
1092
  function writeFrameworkTriggersDispatch(root, devTriggerToken) {
1086
- const jobs = scanJobsSync(root);
1087
- const queues = scanQueuesSync(root);
1093
+ const paths = readProjectPaths(root);
1094
+ const jobs = scanJobsSync(root, paths.cronsDir);
1095
+ const queues = scanQueuesSync(root, paths.queuesDir);
1088
1096
  const target = frameworkTriggersDispatchPath(root);
1089
1097
  mkdirSync(dirname(target), { recursive: true });
1090
- writeFileSync(target, generateModule(root, jobs, queues, devTriggerToken));
1098
+ writeFileSync(target, generateModule(paths.cronsDir, paths.queuesDir, jobs, queues, devTriggerToken));
1091
1099
  }
1092
1100
  /**
1093
1101
  * Writes a real file at `<root>/.void/dev-triggers.ts` exporting
@@ -1107,8 +1115,9 @@ function frameworkTriggersPlugin(root, options) {
1107
1115
  writeDispatch();
1108
1116
  },
1109
1117
  configureServer(server) {
1110
- const cronsPrefix = normalizePath(join(root, "crons"));
1111
- const queuesPrefix = normalizePath(join(root, "queues"));
1118
+ const paths = readProjectPaths(root);
1119
+ const cronsPrefix = normalizePath(paths.cronsDir);
1120
+ const queuesPrefix = normalizePath(paths.queuesDir);
1112
1121
  const isInteresting = (file) => {
1113
1122
  const normalized = normalizePath(file);
1114
1123
  return normalized.startsWith(cronsPrefix) || normalized.startsWith(queuesPrefix);
@@ -1127,9 +1136,9 @@ function frameworkTriggersPlugin(root, options) {
1127
1136
  }
1128
1137
  };
1129
1138
  }
1130
- function generateModule(root, jobs, queues, devTriggerToken) {
1131
- const jobImports = jobs.map((job, jobIndex) => `import __job${jobIndex} from ${JSON.stringify(join(root, "crons", job.filePath))};`).join("\n");
1132
- const queueImports = queues.map((queue, queueIndex) => `import __queue${queueIndex} from ${JSON.stringify(join(root, "queues", queue.filePath))};`).join("\n");
1139
+ function generateModule(cronsDir, queuesDir, jobs, queues, devTriggerToken) {
1140
+ const jobImports = jobs.map((job, jobIndex) => `import __job${jobIndex} from ${JSON.stringify(join(cronsDir, job.filePath))};`).join("\n");
1141
+ const queueImports = queues.map((queue, queueIndex) => `import __queue${queueIndex} from ${JSON.stringify(join(queuesDir, queue.filePath))};`).join("\n");
1133
1142
  const jobTable = jobs.map((job, jobIndex) => ` { crons: ${JSON.stringify(job.crons)}, handler: __job${jobIndex} }`).join(",\n");
1134
1143
  const queueTable = queues.map((queue, queueIndex) => ` { name: ${JSON.stringify(queue.name)}, handler: __queue${queueIndex} }`).join(",\n");
1135
1144
  return `// @ts-nocheck — Auto-generated by Void in dev. Do NOT edit. Regenerated on every config load.
@@ -1552,7 +1561,7 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
1552
1561
  name: "void:migrations",
1553
1562
  apply: "serve",
1554
1563
  async configureServer(server) {
1555
- const migrationsDir = join(root, "db", "migrations");
1564
+ const migrationsDir = readProjectPaths(root).migrationsDir;
1556
1565
  const hasSqlMigrations = existsSync(migrationsDir);
1557
1566
  if (!hasSqlMigrations && !auth?.enabled) return;
1558
1567
  try {
@@ -1567,9 +1576,9 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
1567
1576
  else warn();
1568
1577
  return;
1569
1578
  }
1570
- const { runPgMigrations, getPgAppliedMigrations } = await import("./runner-pg-Bd_172vF.mjs");
1571
- const { collectMigrations } = await import("./collect-HqAuilH6.mjs").then((n) => n.n);
1572
- const { validateMigrations } = await import("./validate-DW3ViKTA.mjs").then((n) => n.i);
1579
+ const { runPgMigrations, getPgAppliedMigrations } = await import("./runner-pg-BXP0JpHW.mjs");
1580
+ const { collectMigrations } = await import("./collect-9rO3JFNM.mjs").then((n) => n.n);
1581
+ const { validateMigrations } = await import("./validate-CNWm-PsL.mjs").then((n) => n.i);
1573
1582
  const allMigrations = collectMigrations(migrationsDir);
1574
1583
  const pgApplied = await getPgAppliedMigrations(url);
1575
1584
  const pgAppliedSet = new Set(pgApplied);
@@ -1582,9 +1591,9 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
1582
1591
  const applied = await runPgMigrations(migrationsDir, url);
1583
1592
  if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
1584
1593
  } else if (hasSqlMigrations) {
1585
- const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-C0d3lJo1.mjs");
1586
- const { collectMigrations } = await import("./collect-HqAuilH6.mjs").then((n) => n.n);
1587
- const { validateMigrations } = await import("./validate-DW3ViKTA.mjs").then((n) => n.i);
1594
+ const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-BkGBfr4e.mjs");
1595
+ const { collectMigrations } = await import("./collect-9rO3JFNM.mjs").then((n) => n.n);
1596
+ const { validateMigrations } = await import("./validate-CNWm-PsL.mjs").then((n) => n.i);
1588
1597
  const dbPath = getMiniflareDatabasePath(persistRoot, databaseId);
1589
1598
  const allMigrations = collectMigrations(migrationsDir);
1590
1599
  const appliedRows = await getAppliedMigrations(dbPath);
@@ -1599,7 +1608,7 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
1599
1608
  if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
1600
1609
  }
1601
1610
  if (auth?.enabled) {
1602
- const { runLocalVoidAuthMigrations } = await import("./node-gxRQXIRT.mjs");
1611
+ const { runLocalVoidAuthMigrations } = await import("./node-fpywdcbN.mjs");
1603
1612
  await runLocalVoidAuthMigrations(root, dialect, persistRoot, databaseId, auth.configPath);
1604
1613
  voidLog("Applied Better Auth migrations");
1605
1614
  }
@@ -1705,7 +1714,7 @@ function nodeTargetPlugin(target) {
1705
1714
  ]
1706
1715
  }
1707
1716
  };
1708
- if (existsSync(join(root, "pages"))) return {
1717
+ if (existsSync(readProjectPaths(root).pagesDir)) return {
1709
1718
  build: { outDir: "dist/client" },
1710
1719
  environments: { ssr: { build: ssrBuildOptions } },
1711
1720
  builder: {}
@@ -2933,6 +2942,7 @@ window.__vite_plugin_react_preamble_installed__ = true;
2933
2942
  if (options.migrationHandlerPath && (options.migrations?.length || auth)) {
2934
2943
  lines.push(`import { createMigrationHandler as __createMigrationHandler } from ${JSON.stringify(options.migrationHandlerPath)};`);
2935
2944
  lines.push(`const __migrations = ${JSON.stringify(options.migrations)};`);
2945
+ lines.push(`const __migrationPlanBundle = ${JSON.stringify(options.migrationPlanBundle)};`);
2936
2946
  lines.push(`const __migrationDialect = ${JSON.stringify(options.migrationDialect ?? "sqlite")};`);
2937
2947
  lines.push("app.post(\"/__void/migrate\", async (c) => {");
2938
2948
  lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
@@ -2944,6 +2954,49 @@ window.__vite_plugin_react_preamble_installed__ = true;
2944
2954
  lines.push(" );");
2945
2955
  lines.push(" return __voidMigrationHandler(c.req.raw);");
2946
2956
  lines.push("});");
2957
+ lines.push("app.get(\"/__void/migrate/capabilities\", async (c) => {");
2958
+ lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
2959
+ lines.push(" __getRawEnv(),");
2960
+ lines.push(" __migrations,");
2961
+ lines.push(" __migrationDialect,");
2962
+ if (auth) lines.push(` () => runVoidAuthMigrations(new Request(c.req.url), __getRawEnv(), __voidAuthOptions${auth.authSchemaPath ? ", __voidAuthMigrationSchema" : ""}),`);
2963
+ else lines.push(" undefined,");
2964
+ lines.push(" __migrationPlanBundle,");
2965
+ lines.push(" );");
2966
+ lines.push(" return __voidMigrationHandler(c.req.raw);");
2967
+ lines.push("});");
2968
+ lines.push("app.post(\"/__void/migrate/app\", async (c) => {");
2969
+ lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
2970
+ lines.push(" __getRawEnv(),");
2971
+ lines.push(" __migrations,");
2972
+ lines.push(" __migrationDialect,");
2973
+ lines.push(" undefined,");
2974
+ lines.push(" __migrationPlanBundle,");
2975
+ lines.push(" );");
2976
+ lines.push(" return __voidMigrationHandler(c.req.raw);");
2977
+ lines.push("});");
2978
+ lines.push("app.get(\"/__void/migrate/app/attempt/:attemptId\", async (c) => {");
2979
+ lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
2980
+ lines.push(" __getRawEnv(),");
2981
+ lines.push(" __migrations,");
2982
+ lines.push(" __migrationDialect,");
2983
+ lines.push(" undefined,");
2984
+ lines.push(" __migrationPlanBundle,");
2985
+ lines.push(" );");
2986
+ lines.push(" return __voidMigrationHandler(c.req.raw);");
2987
+ lines.push("});");
2988
+ if (auth) {
2989
+ lines.push("app.post(\"/__void/migrate/auth\", async (c) => {");
2990
+ lines.push(" const __voidMigrationHandler = __createMigrationHandler(");
2991
+ lines.push(" __getRawEnv(),");
2992
+ lines.push(" __migrations,");
2993
+ lines.push(" __migrationDialect,");
2994
+ lines.push(` () => runVoidAuthMigrations(new Request(c.req.url), __getRawEnv(), __voidAuthOptions${auth.authSchemaPath ? ", __voidAuthMigrationSchema" : ""}),`);
2995
+ lines.push(" __migrationPlanBundle,");
2996
+ lines.push(" );");
2997
+ lines.push(" return __voidMigrationHandler(c.req.raw);");
2998
+ lines.push("});");
2999
+ }
2947
3000
  lines.push("");
2948
3001
  }
2949
3002
  if (options.bindingHandlerPath) {
@@ -3398,7 +3451,7 @@ function compileWorkerEntry(hasQueues = false, remote, assetsDir = "assets", web
3398
3451
  const sandboxClassName = typeof sandbox === "object" ? sandbox.className ?? "Sandbox" : "Sandbox";
3399
3452
  const sandboxBindingName = typeof sandbox === "object" ? sandbox.bindingName ?? "SANDBOX" : "SANDBOX";
3400
3453
  const sandboxRuntimePath = typeof sandbox === "object" ? sandbox.runtimePath ?? "void/sandbox" : "void/sandbox";
3401
- const sandboxImport = hasSandbox ? `import { Sandbox as __VoidSandbox } from "@cloudflare/sandbox";\nimport { __setSandboxBindingName } from ${JSON.stringify(sandboxRuntimePath)};\n` : "";
3454
+ const sandboxImport = hasSandbox ? `import { VoidPlatformSandbox as __VoidSandbox, __setSandboxBindingName } from ${JSON.stringify(sandboxRuntimePath)};\n` : "";
3402
3455
  const sandboxExport = hasSandbox ? sandboxClassName === "Sandbox" ? `export { __VoidSandbox as Sandbox };\n__setSandboxBindingName(${JSON.stringify(sandboxBindingName)});\n` : `export class ${sandboxClassName} extends __VoidSandbox {}\n__setSandboxBindingName(${JSON.stringify(sandboxBindingName)});\n` : "";
3403
3456
  const hasLive = live === true || typeof live === "object";
3404
3457
  const liveBindingName = typeof live === "object" ? live.bindingName ?? "VOID_LIVE" : "VOID_LIVE";
@@ -3434,10 +3487,10 @@ function __filterInternalBindings(env) {
3434
3487
  });
3435
3488
  }
3436
3489
  `;
3437
- const spaFallbackCode = serveSpaFallback ? ` // SPA fallback: when routes/middleware make assetConfig.not_found_handling
3438
- // = 'none', the asset binding returns 404 instead of falling back to
3439
- // index.html itself. Do that fallback here, after user routes/middleware
3440
- // have run, so auth gates and OAuth callbacks see the request first.
3490
+ const spaFallbackCode = serveSpaFallback ? ` // SPA fallback: when HTML-owning features make
3491
+ // assetConfig.not_found_handling = 'none', the asset binding returns 404
3492
+ // instead of falling back to index.html itself. Do that fallback here, after
3493
+ // user routes/middleware have run, so auth gates and OAuth callbacks see the request first.
3441
3494
  // Pages and SSR apps leave this disabled so intentional HTML 404s stay 404.
3442
3495
  const accept = request.headers.get("accept") ?? "";
3443
3496
  if (accept.includes("text/html")) {
@@ -4013,20 +4066,37 @@ export async function fetch(path, options) {
4013
4066
  const VIRTUAL_ROUTES_ID = "virtual:void-routes";
4014
4067
  const VIRTUAL_CLIENT_WORKER_ID = "virtual:void-client-worker";
4015
4068
  const VIRTUAL_CLIENT_BROWSER_ID = "virtual:void-client-browser";
4069
+ const PAGE_VIRTUAL_MODULE_IDS = [
4070
+ "\0virtual:void-pages-renderer",
4071
+ "\0virtual:void-pages-client",
4072
+ "\0virtual:void-islands-renderer",
4073
+ "\0virtual:void-islands-client"
4074
+ ];
4016
4075
  const RESOLVE_ID_FILTER = /^(virtual:void-(routes|client-worker|client-browser)|void(\/client|\/queues)?)$/;
4017
4076
  const LOAD_ID_FILTER = /^virtual:void-(routes|client-worker|client-browser)$/;
4018
4077
  function routingPlugin(root, options) {
4019
4078
  let scan = null;
4020
4079
  let ssr = null;
4021
4080
  let authConfigPath = null;
4022
- const routesDir = join(root, "routes");
4023
- const middlewareDir = join(root, "middleware");
4024
- const jobsDir = join(root, "crons");
4025
- const queuesDir = join(root, "queues");
4026
- const pagesDir = join(root, "pages");
4027
- const srcDir = join(root, "src");
4028
- const authConfigDir = root;
4029
- const ssrEntryAbsPaths = new Set([...SSR_SERVER_ENTRY_RELATIVE_PATHS, ...SSR_CLIENT_ENTRY_RELATIVE_PATHS].map((path) => join(root, path)));
4081
+ const paths = readProjectPaths(root);
4082
+ const routesDir = paths.routesDir;
4083
+ const middlewareDir = paths.middlewareDir;
4084
+ const jobsDir = paths.cronsDir;
4085
+ const queuesDir = paths.queuesDir;
4086
+ const pagesDir = paths.pagesDir;
4087
+ const ssrDir = paths.ssrDir;
4088
+ const authConfigPaths = new Set(paths.authConfigCandidates.map((path) => normalizePath(path)));
4089
+ const envFilePaths = new Set(paths.envFileCandidates.map((path) => normalizePath(path)));
4090
+ const normalizedRoutesDir = normalizePath(routesDir);
4091
+ const normalizedMiddlewareDir = normalizePath(middlewareDir);
4092
+ const normalizedJobsDir = normalizePath(jobsDir);
4093
+ const normalizedQueuesDir = normalizePath(queuesDir);
4094
+ const normalizedPagesDir = normalizePath(pagesDir);
4095
+ const normalizedSsrDir = normalizePath(ssrDir);
4096
+ const ssrEntryAbsPaths = new Set([...SSR_SERVER_ENTRY_RELATIVE_PATHS, ...SSR_CLIENT_ENTRY_RELATIVE_PATHS].map((path) => normalizePath(join(ssrDir, path.split("/").pop()))));
4097
+ function isInDirectory(path, directory) {
4098
+ return path === directory || path.startsWith(directory + "/");
4099
+ }
4030
4100
  function writeRouteTypes() {
4031
4101
  if (!scan) return;
4032
4102
  const { dts } = generateRouteTypes(scan, routesDir, outDir, pagesDir);
@@ -4038,9 +4108,12 @@ function routingPlugin(root, options) {
4038
4108
  writeFileSync(join(outDir, "queues.d.ts"), dts);
4039
4109
  }
4040
4110
  async function rescan() {
4041
- scan = await scanRoutes(root, { output: options.output });
4042
- authConfigPath = findVoidAuthConfig(root);
4043
- const entries = validateSsrEntries(root);
4111
+ scan = await scanRoutes(root, {
4112
+ output: options.output,
4113
+ paths
4114
+ });
4115
+ authConfigPath = findVoidAuthConfig(root, paths);
4116
+ const entries = validateSsrEntries(root, paths);
4044
4117
  ssr = entries ? {
4045
4118
  serverEntryPath: entries.server.absolutePath,
4046
4119
  clientEntryRelativePath: entries.client.relativePath
@@ -4059,7 +4132,7 @@ function routingPlugin(root, options) {
4059
4132
  envPath: resolveFile("#self/_env")
4060
4133
  } : void 0;
4061
4134
  function getEnvSchemaModule() {
4062
- const envFile = findEnvFile(root);
4135
+ const envFile = findEnvFileForPaths(root, paths);
4063
4136
  if (!envFile) return;
4064
4137
  return {
4065
4138
  importPath: envFile,
@@ -4143,9 +4216,10 @@ function routingPlugin(root, options) {
4143
4216
  return join(runtimeDir, "auth-client.mjs");
4144
4217
  }
4145
4218
  function isPotentialAuthConfigPath(path) {
4146
- if (!path.startsWith(authConfigDir + "/")) return false;
4147
- const rel = path.slice(authConfigDir.length + 1);
4148
- return /^auth\.(c|m)?[jt]s$/.test(rel);
4219
+ return authConfigPaths.has(normalizePath(path));
4220
+ }
4221
+ function isPotentialEnvFilePath(path) {
4222
+ return envFilePaths.has(normalizePath(path));
4149
4223
  }
4150
4224
  function getPagesCompileOptions() {
4151
4225
  if (!scan || scan.pages.pages.length === 0) return null;
@@ -4164,7 +4238,28 @@ function routingPlugin(root, options) {
4164
4238
  };
4165
4239
  }
4166
4240
  function shouldServeSpaFallback() {
4167
- return !!scan && scan.pages.pages.length === 0 && !ssr && (scan.routes.length > 0 || (scan.websockets?.length ?? 0) > 0 || scan.middleware.length > 0 || compileOptions.auth !== null);
4241
+ if (!scan) return false;
4242
+ return resolveCurrentAssetRoutingPolicy().workerSpaFallback;
4243
+ }
4244
+ function resolveCurrentAssetRoutingPolicy() {
4245
+ if (!scan) return resolveAssetRoutingPolicy({
4246
+ isSsr: Boolean(ssr),
4247
+ hasPages: false,
4248
+ hasDocumentRoutes: false,
4249
+ hasMiddleware: false,
4250
+ hasWebSockets: false,
4251
+ authEnabled: compileOptions.auth !== null,
4252
+ liveEnabled: options.live !== void 0
4253
+ });
4254
+ return resolveAssetRoutingPolicy({
4255
+ isSsr: Boolean(ssr),
4256
+ hasPages: scan.pages.pages.length > 0,
4257
+ hasDocumentRoutes: hasDocumentRoutes(scan.routes),
4258
+ hasMiddleware: scan.middleware.length > 0,
4259
+ hasWebSockets: (scan.websockets?.length ?? 0) > 0,
4260
+ authEnabled: compileOptions.auth !== null,
4261
+ liveEnabled: options.live !== void 0
4262
+ });
4168
4263
  }
4169
4264
  const outDir = join(root, ".void");
4170
4265
  mkdirSync(outDir, { recursive: true });
@@ -4207,7 +4302,7 @@ function routingPlugin(root, options) {
4207
4302
  return [];
4208
4303
  }
4209
4304
  }) } };
4210
- const entries = validateSsrEntries(root);
4305
+ const entries = validateSsrEntries(root, paths);
4211
4306
  if (!entries || env.command !== "build") return;
4212
4307
  return { build: {
4213
4308
  manifest: true,
@@ -4222,13 +4317,15 @@ function routingPlugin(root, options) {
4222
4317
  writeRouteTypes();
4223
4318
  writeQueueTypes();
4224
4319
  if (!options.target) {
4225
- const migrationsDir = join(root, "db", "migrations");
4320
+ const migrationsDir = paths.migrationsDir;
4226
4321
  if (existsSync(migrationsDir) || compileOptions.auth) {
4227
- const { collectMigrations } = await import("./collect-HqAuilH6.mjs").then((n) => n.n);
4322
+ const { collectMigrations } = await import("./collect-9rO3JFNM.mjs").then((n) => n.n);
4323
+ const { buildMigrationPlanBundle } = await import("./plan-aD3wRDqF.mjs").then((n) => n.i);
4228
4324
  const migrations = existsSync(migrationsDir) ? collectMigrations(migrationsDir) : [];
4229
4325
  const dialect = getDatabaseDialect(readConfig(root));
4230
4326
  compileOptions.migrationHandlerPath = resolveFile(dialect === "postgresql" ? "#self/runtime/migration-handler-pg" : "#self/runtime/migration-handler");
4231
4327
  compileOptions.migrations = migrations;
4328
+ compileOptions.migrationPlanBundle = dialect === "sqlite" ? buildMigrationPlanBundle(migrations) : void 0;
4232
4329
  compileOptions.migrationDialect = dialect;
4233
4330
  }
4234
4331
  }
@@ -4268,10 +4365,16 @@ function routingPlugin(root, options) {
4268
4365
  configureServer(devServer) {
4269
4366
  const watcher = devServer.watcher;
4270
4367
  const handleFileChange = async (path, event) => {
4271
- if (!path.startsWith(routesDir) && !path.startsWith(middlewareDir) && !path.startsWith(jobsDir) && !path.startsWith(queuesDir) && !path.startsWith(pagesDir) && !path.startsWith(srcDir) && !isPotentialAuthConfigPath(path)) return;
4272
- if (path.startsWith(srcDir) && !ssrEntryAbsPaths.has(path)) return;
4368
+ const normalizedPath = normalizePath(path);
4369
+ const sourceConventionChanged = isInDirectory(normalizedPath, normalizedRoutesDir) || isInDirectory(normalizedPath, normalizedMiddlewareDir) || isInDirectory(normalizedPath, normalizedJobsDir) || isInDirectory(normalizedPath, normalizedQueuesDir) || isInDirectory(normalizedPath, normalizedPagesDir) || isPotentialAuthConfigPath(normalizedPath) || isPotentialEnvFilePath(normalizedPath);
4370
+ if (!sourceConventionChanged && !isInDirectory(normalizedPath, normalizedSsrDir) && !isPotentialEnvFilePath(normalizedPath)) return;
4371
+ if (!sourceConventionChanged && isInDirectory(normalizedPath, normalizedSsrDir) && !ssrEntryAbsPaths.has(normalizedPath)) return;
4273
4372
  await rescan();
4274
4373
  resolveAuthCompileOptions();
4374
+ if (event !== "change" && options.onDevAssetRoutingPolicyChange?.(resolveCurrentAssetRoutingPolicy()) === true) {
4375
+ await devServer.restart();
4376
+ return;
4377
+ }
4275
4378
  await writeAuthDatabaseFiles();
4276
4379
  writeRouteTypes();
4277
4380
  writeQueueTypes();
@@ -4284,14 +4387,19 @@ function routingPlugin(root, options) {
4284
4387
  runtimePath: sandboxRuntimePath
4285
4388
  } : false, shouldServeSpaFallback(), options.live));
4286
4389
  const structural = event !== "change";
4390
+ const pageManifestChanged = isInDirectory(normalizedPath, normalizedPagesDir) && (structural || normalizedPath.endsWith(".md"));
4287
4391
  for (const env of Object.values(devServer.environments)) {
4392
+ if (pageManifestChanged) for (const id of PAGE_VIRTUAL_MODULE_IDS) {
4393
+ const pageMod = env.moduleGraph.getModuleById(id);
4394
+ if (pageMod) env.moduleGraph.invalidateModule(pageMod);
4395
+ }
4288
4396
  const mod = env.moduleGraph.getModuleById(VIRTUAL_ROUTES_ID);
4289
4397
  if (mod) {
4290
4398
  env.moduleGraph.invalidateModule(mod);
4291
4399
  if (structural) env.hot.send({ type: "full-reload" });
4292
4400
  }
4293
4401
  }
4294
- if (/\.island\./.test(path) || path.endsWith(".md")) devServer.environments.client?.hot.send({ type: "full-reload" });
4402
+ if (/\.island\./.test(normalizedPath) || normalizedPath.endsWith(".md")) devServer.environments.client?.hot.send({ type: "full-reload" });
4295
4403
  };
4296
4404
  watcher.on("add", (path) => handleFileChange(path, "add"));
4297
4405
  watcher.on("change", (path) => handleFileChange(path, "change"));
@@ -4565,6 +4673,19 @@ function createDefaultWorkerEnvironmentPlugin() {
4565
4673
  }
4566
4674
  };
4567
4675
  }
4676
+ function createWorkerEnvironmentSanitizerPlugin(environmentNames) {
4677
+ const names = new Set(environmentNames);
4678
+ return {
4679
+ name: "void:worker-environment-sanitizer",
4680
+ enforce: "pre",
4681
+ configResolved(config) {
4682
+ for (const name of names) {
4683
+ const resolve = config.environments[name]?.resolve;
4684
+ if (resolve && "external" in resolve) resolve.external = [];
4685
+ }
4686
+ }
4687
+ };
4688
+ }
4568
4689
  function createSandboxSdkResolvePlugin() {
4569
4690
  let resolved;
4570
4691
  return {
@@ -4627,10 +4748,10 @@ const STAGING_API_URL = "https://api.staging.void.cloud";
4627
4748
  * block the server. On completion, writes project.json and restarts the server.
4628
4749
  */
4629
4750
  async function runAiSetupPrompt(root, token, staging, server) {
4630
- const { select, text, isCancel, log } = await import("./dist-D2L3_KTK.mjs").then((n) => n.p);
4631
- const { PlatformClient } = await import("./client-C_DYiXeu.mjs").then((n) => n.r);
4632
- const { writeProjectConfig } = await import("./project-DhB4A8_g.mjs").then((n) => n.n);
4633
- const { inferDefaultSlug } = await import("./resolve-project-DdjLQ2tB.mjs").then((n) => n.i);
4751
+ const { select, text, isCancel, log } = await import("./dist-abzUneor.mjs").then((n) => n.p);
4752
+ const { PlatformClient } = await import("./client-C-nH6yz3.mjs").then((n) => n.r);
4753
+ const { writeProjectConfig } = await import("./project-CH9pdo16.mjs").then((n) => n.n);
4754
+ const { inferDefaultSlug } = await import("./resolve-project-CNpUbUHd.mjs").then((n) => n.i);
4634
4755
  const client = new PlatformClient(token, staging ? STAGING_API_URL : DEFAULT_API_URL);
4635
4756
  let projects;
4636
4757
  try {
@@ -4656,24 +4777,27 @@ async function runAiSetupPrompt(root, token, staging, server) {
4656
4777
  let projectId;
4657
4778
  let slug;
4658
4779
  if (result === "__create__") {
4659
- const defaultSlug = inferDefaultSlug(root);
4660
- const { validateProjectSlug } = await import("./project-slug-KRvHQEQI.mjs").then((n) => n.t);
4661
- const slugResult = await text({
4662
- message: "Choose a slug for your new Void project:",
4663
- placeholder: "my-app",
4664
- initialValue: defaultSlug,
4665
- validate: validateProjectSlug
4666
- });
4667
- if (isCancel(slugResult)) return;
4780
+ const { validateProjectSlug } = await import("./project-slug-De4UjtD9.mjs").then((n) => n.t);
4781
+ const { createProjectWithSlugRetry } = await import("./create-project-BGnQMy5N.mjs").then((n) => n.t);
4782
+ let created;
4668
4783
  try {
4669
- const created = await client.createProject(slugResult.trim());
4670
- projectId = created.id;
4671
- slug = created.slug;
4784
+ created = await createProjectWithSlugRetry(client, inferDefaultSlug(root), async (defaultSlug) => {
4785
+ const slugResult = await text({
4786
+ message: "Choose a slug for your new Void project:",
4787
+ placeholder: "my-app",
4788
+ initialValue: defaultSlug,
4789
+ validate: validateProjectSlug
4790
+ });
4791
+ if (isCancel(slugResult)) return;
4792
+ return slugResult.trim();
4793
+ });
4672
4794
  } catch (err) {
4673
4795
  voidError(`project: Failed to create project: '${err instanceof Error ? err.message : String(err)}'.`);
4674
4796
  return;
4675
4797
  }
4676
- log.success(`Project created: ${import_picocolors.default.blue(slug)}`);
4798
+ if (!created) return;
4799
+ projectId = created.id;
4800
+ slug = created.slug;
4677
4801
  } else {
4678
4802
  const project = projects.find((p) => p.id === result);
4679
4803
  projectId = project.id;
@@ -4709,18 +4833,23 @@ function voidPlugin(options) {
4709
4833
  warnIfPackageJsonIsNotEsm(root);
4710
4834
  const devTriggerToken = isViteBuildOrPreview() ? crypto.randomUUID() : getOrCreateDevTriggerToken(root);
4711
4835
  const config = readConfig(root);
4836
+ const paths = resolveProjectPaths(root, config);
4837
+ assertNoRootSourceConventionConflicts(paths);
4712
4838
  const dialect = getDatabaseDialect(config);
4713
4839
  const detected = detectFramework(root);
4714
4840
  const framework = detected?.class === "a" ? detected.name : void 0;
4715
4841
  const scanDirs = config.inference?.scanDirs ?? (detected ? FRAMEWORK_SCAN_DIRS : void 0);
4716
4842
  const inferenceOptions = config.inference?.bindings ? { bindings: config.inference.bindings } : void 0;
4717
- const inferredBindings = inferProjectBindings(root, inferenceOptions, scanDirs);
4843
+ const inferredBindings = inferProjectBindings(root, {
4844
+ ...inferenceOptions,
4845
+ paths
4846
+ }, scanDirs);
4718
4847
  const bindings = isSandboxEnabled(config) ? {
4719
4848
  ...inferredBindings,
4720
4849
  needsSandbox: true
4721
4850
  } : inferredBindings;
4722
4851
  const sandboxConfig = bindings.needsSandbox ? resolveSandboxConfig(config, root) : void 0;
4723
- const authConfigPath = findVoidAuthConfig(root);
4852
+ const authConfigPath = findVoidAuthConfig(root, paths);
4724
4853
  const authEnabled = bindings.needsAuth || authConfigPath !== null || config.auth !== void 0;
4725
4854
  if (detected && authEnabled) throw new Error("auth: Void-managed Better Auth is only supported in Void apps. Use Better Auth's official integration for this framework instead.");
4726
4855
  if (detected && bindings.needsSandbox) throw new Error("sandbox: Cloudflare Sandboxes are currently supported in Void apps only.");
@@ -4733,7 +4862,19 @@ function voidPlugin(options) {
4733
4862
  "void/queues": "export const queues = {};",
4734
4863
  "void/storage": "export const storage = {}; export const createStorage = () => ({});",
4735
4864
  "void/isr": "export const revalidate = async () => {};",
4736
- "void/sandbox": "export const sandbox = {}; export const getSandbox = () => ({});",
4865
+ "void/sandbox": `
4866
+ export class SandboxLimitError extends Error {
4867
+ constructor(reason = 'concurrency', message) {
4868
+ super(message ?? 'Sandbox limit reached: ' + reason);
4869
+ this.name = 'SandboxLimitError';
4870
+ this.reason = reason;
4871
+ }
4872
+ }
4873
+ export async function getSandbox() {
4874
+ throw new Error('void/sandbox is server-only and cannot be imported by browser code.');
4875
+ }
4876
+ export const sandbox = { get: getSandbox };
4877
+ `,
4737
4878
  "void/live": ""
4738
4879
  };
4739
4880
  const STUB_RESOLVE_FILTER = new RegExp(`^(${Object.keys(SERVER_ONLY_STUBS).join("|").replaceAll("/", "\\/")})$`);
@@ -4757,8 +4898,8 @@ function voidPlugin(options) {
4757
4898
  }
4758
4899
  }
4759
4900
  };
4760
- const queues = framework ? [] : scanQueuesSync(root);
4761
- const websocketRoutes = framework ? [] : scanWebSocketRoutesSync(root);
4901
+ const queues = framework ? [] : scanQueuesSync(root, paths.queuesDir);
4902
+ const websocketRoutes = framework ? [] : scanWebSocketRoutesSync(root, paths);
4762
4903
  const liveConfig = !isNodeTarget(config.target) ? bindings.needsLive ? resolveLiveBindingConfig(root, {
4763
4904
  reservedBindings: websocketRoutes.map((route) => route.bindingName),
4764
4905
  reservedClasses: [...websocketRoutes.map((route) => route.className), sandboxConfig?.className]
@@ -4833,6 +4974,7 @@ function voidPlugin(options) {
4833
4974
  }
4834
4975
  const remoteMode = resolveRemoteMode(config.remote);
4835
4976
  const workerConfig = {};
4977
+ if (config.worker?.vars) workerConfig.vars = { ...config.worker.vars };
4836
4978
  let viteCommand = "build";
4837
4979
  const wranglerCompat = isNodeTarget(config.target) ? void 0 : readWranglerCompat(root, config);
4838
4980
  if (wranglerCompat) {
@@ -4846,6 +4988,10 @@ function voidPlugin(options) {
4846
4988
  mkdirSync(outDir, { recursive: true });
4847
4989
  function mergeBindings(resolved) {
4848
4990
  const result = { ...workerConfig };
4991
+ if (workerConfig.assets && resolved.assets) {
4992
+ delete resolved.assets.not_found_handling;
4993
+ delete resolved.assets.run_worker_first;
4994
+ }
4849
4995
  const d1 = [];
4850
4996
  if ((bindings.needsD1 || authEnabled && dialect === "sqlite") && !hasBinding(resolved.d1_databases, "DB")) d1.push({
4851
4997
  binding: "DB",
@@ -5085,6 +5231,7 @@ function voidPlugin(options) {
5085
5231
  } : void 0;
5086
5232
  return [
5087
5233
  createCloudflareBuiltinsExternalPlugin(),
5234
+ createWorkerEnvironmentSanitizerPlugin([viteEnvironment.name, ...viteEnvironment.childEnvironments ?? []]),
5088
5235
  createWranglerCompatPersistPlugin(root, config),
5089
5236
  restartPlugin(),
5090
5237
  bindingWatchPlugin,
@@ -5144,19 +5291,40 @@ function voidPlugin(options) {
5144
5291
  const existingFlags = workerConfig.compatibility_flags ?? [];
5145
5292
  const normalizedFlags = normalizeWranglerCompatibilityFlags(existingFlags, { needsNodejsCompat: authEnabled || dialect === "postgresql" || bindings.needsSandbox });
5146
5293
  if (normalizedFlags && normalizedFlags !== existingFlags) workerConfig.compatibility_flags = normalizedFlags;
5147
- const jobs = scanJobsSync(root);
5148
- const ssrEntry = validateSsrEntry(root);
5149
- const hasPages = existsSync(join(root, "pages"));
5294
+ const jobs = scanJobsSync(root, paths.cronsDir);
5295
+ const ssrEntry = validateSsrEntry(root, paths);
5296
+ const hasPages = existsSync(paths.pagesDir);
5297
+ const hasMiddleware = scanMiddlewareSync(root, paths).length > 0;
5298
+ const hasDocumentRoutes$1 = hasDocumentRoutes(scanRoutePatternsSync(root, paths));
5299
+ let assetRouting = resolveAssetRoutingPolicy({
5300
+ isSsr: Boolean(ssrEntry),
5301
+ hasPages,
5302
+ hasDocumentRoutes: hasDocumentRoutes$1,
5303
+ hasMiddleware,
5304
+ hasWebSockets: websocketRoutes.length > 0,
5305
+ authEnabled,
5306
+ liveEnabled: liveConfig !== void 0
5307
+ });
5150
5308
  workerConfig.main = join(root, ".void", "entry.ts");
5151
5309
  if (jobs.length > 0) workerConfig.triggers = { crons: jobs.flatMap((job) => job.crons) };
5152
- if (ssrEntry || hasPages) workerConfig.assets = {
5153
- binding: "ASSETS",
5154
- not_found_handling: "none",
5155
- run_worker_first: ["/**"]
5156
- };
5310
+ function applyAssetRoutingPolicy(policy) {
5311
+ workerConfig.assets = {
5312
+ binding: "ASSETS",
5313
+ ...policy.assetConfig
5314
+ };
5315
+ }
5316
+ function updateDevAssetRoutingPolicy(next) {
5317
+ const previous = assetRouting;
5318
+ const requiresRestart = previous.workerFirst !== next.workerFirst || previous.assetConfig.not_found_handling !== next.assetConfig.not_found_handling;
5319
+ assetRouting = next;
5320
+ if (requiresRestart) applyAssetRoutingPolicy(next);
5321
+ return requiresRestart;
5322
+ }
5323
+ applyAssetRoutingPolicy(assetRouting);
5157
5324
  return [
5158
5325
  createCloudflareBuiltinsExternalPlugin(),
5159
5326
  createDefaultWorkerEnvironmentPlugin(),
5327
+ createWorkerEnvironmentSanitizerPlugin([DEFAULT_WORKER_ENVIRONMENT_NAME]),
5160
5328
  createWranglerCompatPersistPlugin(root, config),
5161
5329
  restartPlugin(),
5162
5330
  bindingWatchPlugin,
@@ -5178,7 +5346,8 @@ function voidPlugin(options) {
5178
5346
  hasSandbox: bindings.needsSandbox,
5179
5347
  sandboxBindingName: sandboxConfig?.binding,
5180
5348
  sandboxClassName: sandboxConfig?.className,
5181
- live: liveConfig
5349
+ live: liveConfig,
5350
+ onDevAssetRoutingPolicyChange: updateDevAssetRoutingPolicy
5182
5351
  }),
5183
5352
  drizzlePlugin(root, true, dialect, {
5184
5353
  forceVirtualDb: authEnabled,