void 1.2.1 → 1.4.0

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 (200) hide show
  1. package/dist/{account-cmd-CdUll_35.mjs → account-cmd-HP8bixYo.mjs} +3 -3
  2. package/dist/{application-analysis-fEqoSeMp.mjs → application-analysis-DDOQwiXk.mjs} +18 -12
  3. package/dist/{application-routing-DAhNP4u3.mjs → application-routing-4719u_kf.mjs} +3 -1
  4. package/dist/auth-g2TQbDLD.d.mts +70 -0
  5. package/dist/{auth-link-C87vGXM-.mjs → auth-link-CedYpDCy.mjs} +3 -3
  6. package/dist/{auth-router-6I81r-ix.mjs → auth-router-vafo7x8w.mjs} +11 -6
  7. package/dist/{better-auth-shared-D-6z0q0f.mjs → better-auth-shared-7FM3Uk2N.mjs} +48 -2
  8. package/dist/{better-auth-shared-DVk2DkTW.d.mts → better-auth-shared-BHB8kiTE.d.mts} +1 -1
  9. package/dist/{build-cmd-ex1g-j_e.mjs → build-cmd-9vUAQ1_V.mjs} +1 -1
  10. package/dist/{cache-oHzrnY3z.mjs → cache-jvwLNb0h.mjs} +1 -1
  11. package/dist/{cancel-deploy-viAMD0Q_.mjs → cancel-deploy-Dw0UmWKO.mjs} +1 -1
  12. package/dist/{cf-build-output-BhVxldCw.mjs → cf-build-output-XpNR9zoh.mjs} +122 -5
  13. package/dist/cf-build-output-bd8Uwv1P.mjs +1 -0
  14. package/dist/{cf-worker-settings-BECvvTVd.mjs → cf-worker-settings-Dg27RzyT.mjs} +11 -5
  15. package/dist/cli/cli.mjs +41 -32
  16. package/dist/cli/cloudflare-operation-process.mjs +35 -8
  17. package/dist/cli/env-schema-probe.mjs +2 -2
  18. package/dist/{client-BEkdj1kx.mjs → client-B3LnB_bE.mjs} +8 -1
  19. package/dist/client-BZb9j66F.mjs +1 -0
  20. package/dist/{cloudflare-auth-Byo_0_N9.mjs → cloudflare-auth-DyD_kVFr.mjs} +1 -1
  21. package/dist/{cloudflare-cmd-DMzCv12s.mjs → cloudflare-cmd-D-b_u3T4.mjs} +2 -2
  22. package/dist/{cloudflare-config-DchHxPTo.mjs → cloudflare-config-CH1AylZT.mjs} +14 -3
  23. package/dist/{cloudflare-connect-CCRTVg-I.mjs → cloudflare-connect-zTJh8UnN.mjs} +2 -2
  24. package/dist/{cloudflare-operations-Dh9dEHJj.mjs → cloudflare-operations-Bc-7fN44.mjs} +4 -4
  25. package/dist/{cloudflare-operations-DsSaw2BT.mjs → cloudflare-operations-BtulCNOg.mjs} +1 -1
  26. package/dist/{config-BU6hMu1W.mjs → config-BV2ASlpn.mjs} +1 -1
  27. package/dist/{config-DQfHOKh9.d.mts → config-CAdZVTLN.d.mts} +4 -1
  28. package/dist/config-D-6q7EBH.mjs +87 -0
  29. package/dist/{config-DHgSEWT7.mjs → config-OFOAimzt.mjs} +5 -3
  30. package/dist/config-entry.d.mts +1 -1
  31. package/dist/{config-BIaXIkkv.mjs → config-tqMM6DwY.mjs} +1 -1
  32. package/dist/{connect-k7R2rPVA.mjs → connect-BOM6zvFX.mjs} +4 -4
  33. package/dist/{create-project-DV71ol5d.mjs → create-project-Bxd02EHN.mjs} +1 -1
  34. package/dist/{create-project-DTuy-l0z.mjs → create-project-C2K0DN2O.mjs} +1 -1
  35. package/dist/{db-DVOkLH5X.mjs → db-Cdvr3zwG.mjs} +18 -18
  36. package/dist/{db-types-NGxPwnlt.mjs → db-types-0eCiqYb1.mjs} +1 -1
  37. package/dist/{delete-BbP5eopu.mjs → delete-lETE6Tpt.mjs} +1 -1
  38. package/dist/deploy-CX94CmKa.mjs +2 -0
  39. package/dist/{deploy-7H32EnUs.mjs → deploy-CdM-LAxX.mjs} +473 -148
  40. package/dist/{deploy-managed-static-DlvcvnQT.mjs → deploy-managed-static-DjeBFG2c.mjs} +43 -20
  41. package/dist/{deploy-transport-BzGEj0Ge.mjs → deploy-transport-DIq_gFTP.mjs} +14 -13
  42. package/dist/deploy.d.mts +2 -0
  43. package/dist/deploy.mjs +8 -5
  44. package/dist/{dispatch-DoUnxTNo.mjs → dispatch-CLa2LThw.mjs} +76 -7
  45. package/dist/{domain-CA2wBgra.mjs → domain-DyhNtdYf.mjs} +2 -2
  46. package/dist/{email-DXpCc77w.mjs → email-DM2wDcBN.mjs} +59 -10
  47. package/dist/{email-cloudflare-D03g2TkS.mjs → email-cloudflare-PMbAev9Q.mjs} +124 -10
  48. package/dist/{env-DgOQ9_8S.mjs → env-78hbN-oI.mjs} +3 -3
  49. package/dist/{env-BJjULvL0.mjs → env-BaMz0j7H.mjs} +6 -1
  50. package/dist/{env-validation-Dwq3PJzK.mjs → env-validation-B3VobvXm.mjs} +2 -1
  51. package/dist/{env-validation-BmsdCZEk.mjs → env-validation-BUfFZ6bt.mjs} +1 -1
  52. package/dist/error-pages-CLIJ7aYq.mjs +37 -0
  53. package/dist/gen-BhWBT4iu.mjs +1 -0
  54. package/dist/{gen-BfmcIi2g.mjs → gen-CFmsUsW1.mjs} +3 -3
  55. package/dist/{github-cmd-CY9LUqLh.mjs → github-cmd-De-_IWbM.mjs} +1 -1
  56. package/dist/{handler-BbkSPzGF.d.mts → handler-BHktmuTL.d.mts} +4 -2
  57. package/dist/{headers-D522zd0A.mjs → headers--vaK_chu.mjs} +1 -1
  58. package/dist/{help-CxSlPpzJ.mjs → help-CKbrhZha.mjs} +16 -1
  59. package/dist/help-qOZr3wng.mjs +1 -0
  60. package/dist/index.d.mts +3 -5
  61. package/dist/index.mjs +370 -140
  62. package/dist/{info-7VDlhtyy.mjs → info-Yg0-V5Rs.mjs} +4 -4
  63. package/dist/{init-DxYyOs4-.mjs → init-CDiJQycf.mjs} +241 -206
  64. package/dist/{link-Ddmod1dZ.mjs → link-Dllip08S.mjs} +2 -2
  65. package/dist/{list-MogaugiO.mjs → list-BYRP6am0.mjs} +2 -2
  66. package/dist/{live-Bt8Y_0bg.d.mts → live-BllfDuzA.d.mts} +2 -2
  67. package/dist/{local-d1-BHBnKWRb.mjs → local-d1-CAwAs9GH.mjs} +1 -1
  68. package/dist/login-BAPmYnb7.mjs +1 -0
  69. package/dist/{login-Cttnagmm.mjs → login-PcFy5mlC.mjs} +2 -2
  70. package/dist/{logs-CAszUWZI.mjs → logs-DmJTBhed.mjs} +2 -2
  71. package/dist/{migrate-DyT2jJ8y.mjs → migrate-B8S2xTcb.mjs} +5 -66
  72. package/dist/migrate-BIYTPQE6.mjs +1 -0
  73. package/dist/nitro.mjs +4 -4
  74. package/dist/{node-WB7c2N4L.mjs → node-CDOPrGSl.mjs} +5 -5
  75. package/dist/{operator-args-UnE8Y2eK.mjs → operator-args-BsJIYXaT.mjs} +8 -1
  76. package/dist/operator-cmd-CjazwOyb.mjs +1 -0
  77. package/dist/{operator-cmd-skshjMGl.mjs → operator-cmd-DmkmS2wL.mjs} +4 -3
  78. package/dist/pages/client.d.mts +32 -4
  79. package/dist/pages/client.mjs +186 -29
  80. package/dist/pages/index.d.mts +34 -2
  81. package/dist/pages/index.mjs +96 -55
  82. package/dist/pages/islands-plugin.d.mts +1 -1
  83. package/dist/pages/islands-plugin.mjs +9 -3
  84. package/dist/pages/protocol.d.mts +2 -2
  85. package/dist/platform-args-BC8_hsDj.mjs +1 -0
  86. package/dist/{platform-args-DVbVClZl.mjs → platform-args-BMD2gjc_.mjs} +1 -1
  87. package/dist/{platform-auth-config-C-pm5Qzx.mjs → platform-auth-config-CImnfohz.mjs} +3 -3
  88. package/dist/{platform-auth-protection-SHhvim1L.mjs → platform-auth-protection-BogLhtHi.mjs} +2 -2
  89. package/dist/{platform-auth-recovery-DXVWs-wA.mjs → platform-auth-recovery-B-mEaWEi.mjs} +2 -2
  90. package/dist/{platform-cmd-PiNRkopU.mjs → platform-cmd-CV8NxIIn.mjs} +1 -1
  91. package/dist/{platform-cmd-D5I1beUX.mjs → platform-cmd-DZK2ixMl.mjs} +4 -4
  92. package/dist/{platform-domain-jpmBfZeb.mjs → platform-domain-CRvelPGE.mjs} +2 -2
  93. package/dist/{platform-lifecycle-DyKmuNY6.mjs → platform-lifecycle-CPYY7E9B.mjs} +103 -41
  94. package/dist/{platform-lifecycle-DH3Yr1qQ.mjs → platform-lifecycle-DwXq7kZt.mjs} +1 -1
  95. package/dist/{platform-management--vDwSn35.mjs → platform-management-BOnGbMSM.mjs} +1 -1
  96. package/dist/{platform-management-7m-PLaPJ.mjs → platform-management-lXdgGgAM.mjs} +1 -1
  97. package/dist/{platform-recovery-BiNpZZab.mjs → platform-recovery-noaALXnE.mjs} +2 -2
  98. package/dist/{plugin-inference-DUjCd55H.mjs → plugin-inference-CeiB6BG5.mjs} +53 -18
  99. package/dist/{prepare-BcAE5Sdj.mjs → prepare-BuOS18AM.mjs} +1 -1
  100. package/dist/{prepare-DA-17DhO.mjs → prepare-D604Yiyr.mjs} +12 -8
  101. package/dist/prepare-DU-tOpS5.mjs +1 -0
  102. package/dist/{prerender-render-Cijv7UFo.mjs → prerender-render-CaShbHJ3.mjs} +13 -1
  103. package/dist/prerender-render.d.mts +1 -0
  104. package/dist/prerender-render.mjs +1 -1
  105. package/dist/{project-cmd-DA4K5faU.mjs → project-cmd-DBYqAIbe.mjs} +15 -15
  106. package/dist/{project-team-CQBNveX3.mjs → project-team-BjCLgTRC.mjs} +1 -1
  107. package/dist/{project-token-CCalFyQf.mjs → project-token-BB6PwaA9.mjs} +1 -1
  108. package/dist/{project-tsconfig-Itv1f4WC.mjs → project-tsconfig-CVvk6Z_e.mjs} +24 -2
  109. package/dist/{project-zero-trust-DPnkiEuT.mjs → project-zero-trust-CWDh1P_t.mjs} +1 -1
  110. package/dist/{protocol-BBDpJz3G.d.mts → protocol-D5XDySYu.d.mts} +2 -2
  111. package/dist/{provision-BFLCrnUp.mjs → provision-DfxLCg-d.mjs} +1 -1
  112. package/dist/{provision-DtROpNNI.mjs → provision-DztLJ97X.mjs} +24 -8
  113. package/dist/{requests-BpH0kImL.mjs → requests-D_pbzDyz.mjs} +1 -1
  114. package/dist/{rollback-CXS1tAa-.mjs → rollback-CjfIYsJx.mjs} +2 -2
  115. package/dist/{route-types-BZi26DVS.mjs → route-types-DvEGmPif.mjs} +109 -30
  116. package/dist/routes-stub.d.mts +22 -13
  117. package/dist/runtime/auth.d.mts +2 -2
  118. package/dist/runtime/better-auth-mysql.d.mts +1 -1
  119. package/dist/runtime/better-auth-mysql.mjs +1 -1
  120. package/dist/runtime/better-auth-pg.d.mts +1 -1
  121. package/dist/runtime/better-auth-pg.mjs +1 -1
  122. package/dist/runtime/better-auth.d.mts +1 -1
  123. package/dist/runtime/better-auth.mjs +1 -1
  124. package/dist/runtime/client-react.d.mts +1 -1
  125. package/dist/runtime/client-solid.d.mts +1 -1
  126. package/dist/runtime/client-svelte.d.mts +1 -1
  127. package/dist/runtime/client-vue.d.mts +1 -1
  128. package/dist/runtime/client.d.mts +1 -1
  129. package/dist/runtime/handler.d.mts +1 -1
  130. package/dist/runtime/live-client.d.mts +1 -1
  131. package/dist/runtime/live.d.mts +1 -1
  132. package/dist/runtime/log.mjs +19 -1
  133. package/dist/runtime/routing.d.mts +5 -4
  134. package/dist/runtime/routing.mjs +14 -73
  135. package/dist/runtime/validator.d.mts +1 -1
  136. package/dist/runtime/ws-server.d.mts +3 -3
  137. package/dist/runtime/ws.d.mts +1 -1
  138. package/dist/runtime/ws.mjs +4 -2
  139. package/dist/{scan-8IzYXiU_.mjs → scan-N9WzdcAl.mjs} +302 -23
  140. package/dist/{secret-BbXHfLAt.mjs → secret-3X0PxmuX.mjs} +9 -12
  141. package/dist/static-asset-marker-Btf-6oCD.mjs +32 -0
  142. package/dist/{types-7AIz0OZu.d.mts → types-w72dljaA.d.mts} +6 -1
  143. package/dist/{version-metadata-tS39iW09.mjs → version-metadata-D2vfMQzv.mjs} +2 -1
  144. package/dist/worker-conditions-AWFRcFxR.mjs +11 -0
  145. package/dist/{wrangler-F96NnLyb.mjs → wrangler-I-scB_1t.mjs} +82 -13
  146. package/dist/wrangler-UnwdISay.mjs +1 -0
  147. package/dist/{wrapper-D3sZyh2C.mjs → wrapper-BBl1rC32.mjs} +8 -12
  148. package/dist/{ws-BH-im_tX.d.mts → ws-B3ClMVDh.d.mts} +3 -2
  149. package/docs/guide/auth.md +4 -2
  150. package/docs/guide/edge/redirects.md +22 -0
  151. package/docs/guide/edge/static-assets.md +10 -0
  152. package/docs/guide/email/domains.md +1 -1
  153. package/docs/guide/email/receiving.md +2 -0
  154. package/docs/guide/email/sending.md +1 -1
  155. package/docs/guide/pages-routing/actions-and-forms.md +43 -13
  156. package/docs/guide/pages-routing/markdown.md +2 -0
  157. package/docs/guide/pages-routing/overview.md +33 -2
  158. package/docs/guide/platform/administration/email.md +18 -0
  159. package/docs/guide/platform/installation/maintenance.md +3 -1
  160. package/docs/guide/platform/installation/prerequisites.md +9 -4
  161. package/docs/guide/platform/installation/uninstall.md +2 -0
  162. package/docs/guide/queues.md +13 -1
  163. package/docs/guide/server-routing.md +40 -2
  164. package/docs/guide/ssg.md +8 -0
  165. package/docs/guide/type-safety.md +7 -7
  166. package/docs/guide/websockets.md +4 -2
  167. package/docs/integrations/cloudflare.md +16 -7
  168. package/docs/reference/api/auth.md +6 -2
  169. package/docs/reference/api/deploy.md +9 -5
  170. package/docs/reference/api/pages.md +6 -4
  171. package/docs/reference/cli/deploy.md +10 -9
  172. package/docs/reference/cli/email.md +13 -0
  173. package/docs/reference/cli/platform-email.md +2 -2
  174. package/docs/reference/cli.md +1 -0
  175. package/docs/reference/config.md +11 -1
  176. package/docs/reference/structure.md +3 -1
  177. package/package.json +9 -7
  178. package/schema.json +4 -0
  179. package/skills/void/SKILL.md +1 -1
  180. package/templates/assets/icons.svg +49 -0
  181. package/templates/assets/react.svg +1 -0
  182. package/templates/assets/solid.svg +1 -0
  183. package/templates/assets/stack.svg +35 -0
  184. package/templates/assets/svelte.svg +1 -0
  185. package/templates/assets/void.svg +5 -0
  186. package/templates/assets/vue.svg +1 -0
  187. package/dist/auth-DkcFflXV.d.mts +0 -42
  188. package/dist/cf-build-output-BJHuSsVN.mjs +0 -1
  189. package/dist/client-BiRDZTSd.mjs +0 -1
  190. package/dist/config-CyUJzi3q.mjs +0 -32
  191. package/dist/deploy-CE0Ev0Ob.mjs +0 -2
  192. package/dist/gen-NrOlWZaG.mjs +0 -1
  193. package/dist/help-BeAFFE5C.mjs +0 -1
  194. package/dist/login-DgDwlKc_.mjs +0 -1
  195. package/dist/migrate-n49ZeS1X.mjs +0 -1
  196. package/dist/operator-cmd-d2MvJOrl.mjs +0 -1
  197. package/dist/parse-filename-DioPHiR9.mjs +0 -77
  198. package/dist/platform-args-kQrgxScY.mjs +0 -1
  199. package/dist/prepare-C4I0f--h.mjs +0 -1
  200. package/dist/wrangler-BLb3_luP.mjs +0 -1
@@ -1,8 +1,8 @@
1
1
  import { a as join, s as relative } from "./dist-ipQi0Q8y.mjs";
2
- import { a as deriveWebSocketClassNameFromName, o as inferWebSocketResourceName } from "./plugin-inference-DUjCd55H.mjs";
3
- import { p as readConfig } from "./config-DHgSEWT7.mjs";
4
- import { d as deriveDurableResourceIdentity, f as inferDurableResourceName, t as analyzeApplicationSync } from "./application-analysis-fEqoSeMp.mjs";
5
- import { n as VOID_LOCK_FILE } from "./cloudflare-config-DchHxPTo.mjs";
2
+ import { a as deriveWebSocketClassNameFromName, o as inferWebSocketResourceName } from "./plugin-inference-CeiB6BG5.mjs";
3
+ import { p as readConfig } from "./config-OFOAimzt.mjs";
4
+ import { d as deriveDurableResourceIdentity, f as inferDurableResourceName, t as analyzeApplicationSync } from "./application-analysis-DDOQwiXk.mjs";
5
+ import { n as VOID_LOCK_FILE } from "./cloudflare-config-CH1AylZT.mjs";
6
6
  import { existsSync, readFileSync } from "node:fs";
7
7
  import { isRecord } from "@void/deploy-core";
8
8
  //#region src/cli/info.ts
@@ -1,28 +1,28 @@
1
1
  import { a as join, c as resolve, i as isAbsolute, n as dirname, s as relative, t as basename } from "./dist-ipQi0Q8y.mjs";
2
- import { r as detectFramework } from "./plugin-inference-DUjCd55H.mjs";
2
+ import { r as detectFramework } from "./plugin-inference-CeiB6BG5.mjs";
3
3
  import { n as readProjectPaths, o as TYPED_CONFIG_FILE } from "./project-paths-DGu_QZ_Q.mjs";
4
- import { c as getDatabaseCasing, l as getDatabaseDialect, p as readConfig, s as databaseConfigValue } from "./config-DHgSEWT7.mjs";
4
+ import { c as getDatabaseCasing, l as getDatabaseDialect, p as readConfig, s as databaseConfigValue } from "./config-OFOAimzt.mjs";
5
5
  import { t as MagicString } from "./dist-Cegxq8gs.mjs";
6
6
  import { c as writeDeploymentPlatform, n as readDeploymentPlatform, r as readProjectConfig, t as attachSelectedPlatform, u as writeProjectConfig } from "./project-rqDnVsJN.mjs";
7
- import { r as materializeCloudflareConfig } from "./cloudflare-config-DchHxPTo.mjs";
8
- import { M as writeProvisionedWranglerConfig, i as LATEST_KNOWN_COMPAT_DATE } from "./wrangler-F96NnLyb.mjs";
7
+ import { i as materializeCloudflareConfig } from "./cloudflare-config-CH1AylZT.mjs";
8
+ import { F as canRewriteVoidProjectConfig, L as writeVoidProjectConfig, M as writeProvisionedWranglerConfig, i as LATEST_KNOWN_COMPAT_DATE } from "./wrangler-I-scB_1t.mjs";
9
9
  import { i as readPackageJson, n as hasPackageDependency } from "./package-json-BjYqR-So.mjs";
10
10
  import { c as getToken } from "./auth-B6iOCjKI.mjs";
11
- import { n as ensureProjectTsconfig, t as createProjectTsconfig } from "./project-tsconfig-Itv1f4WC.mjs";
11
+ import { n as ensureProjectTsconfig, t as createProjectTsconfig } from "./project-tsconfig-CVvk6Z_e.mjs";
12
12
  import { t as parseDotenvFile } from "./dotenv-CNazY3-w.mjs";
13
- import { r as writeDrizzleConfig, t as PLACEHOLDER_DB_URL } from "./config-BIaXIkkv.mjs";
13
+ import { r as writeDrizzleConfig, t as PLACEHOLDER_DB_URL } from "./config-tqMM6DwY.mjs";
14
14
  import { A as confirm, K as select, L as log, O as cancel, X as text, tt as isCancel } from "./dist-Cor9XGoz.mjs";
15
15
  import { n as getOwnVersion, r as getPackageDir, t as detectAgents } from "./agents-CuNtT0D_.mjs";
16
- import { r as PlatformClient } from "./client-BEkdj1kx.mjs";
16
+ import { r as PlatformClient } from "./client-B3LnB_bE.mjs";
17
17
  import { l as formatUrl, m as printSuccess, p as note } from "./output-BpgYODYx.mjs";
18
- import { a as promptProjectSelection, r as promptAndCreateProject } from "./create-project-DV71ol5d.mjs";
18
+ import { a as promptProjectSelection, r as promptAndCreateProject } from "./create-project-Bxd02EHN.mjs";
19
19
  import { s as formatProjectCommand } from "./cloudflare-process-8jEYIeim.mjs";
20
20
  import { i as hasLocalLockfile, n as drizzleKitBin, r as findInstalledYarnPnpRuntime, t as buildDrizzleKitSpawnEnv } from "./yarn-pnp-DLbEFMT0.mjs";
21
- import { t as promptForLoginToken } from "./login-Cttnagmm.mjs";
22
- import { i as saveDiscoveredPlatform, t as discoverPlatform } from "./platform-cmd-D5I1beUX.mjs";
23
- import { t as connectCloudflare } from "./cloudflare-connect-CCRTVg-I.mjs";
21
+ import { t as promptForLoginToken } from "./login-PcFy5mlC.mjs";
22
+ import { i as saveDiscoveredPlatform, t as discoverPlatform } from "./platform-cmd-DZK2ixMl.mjs";
23
+ import { t as connectCloudflare } from "./cloudflare-connect-zTJh8UnN.mjs";
24
24
  import { n as snapshotDrizzleOutputs } from "./generate-B1m4Cm4n.mjs";
25
- import { i as writeVoidProjectConfig, o as migrateFrameworkAdapterPaths, r as canRewriteVoidProjectConfig, t as migrateLegacyVoidConfig } from "./migrate-DyT2jJ8y.mjs";
25
+ import { i as migrateFrameworkAdapterPaths, t as migrateLegacyVoidConfig } from "./migrate-B8S2xTcb.mjs";
26
26
  import { createRequire } from "node:module";
27
27
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
28
28
  import { parseSync } from "vite";
@@ -211,6 +211,9 @@ function initialCommitCommand(root) {
211
211
  return `${git} add -A && ${git} commit -m "chore: initial commit"`;
212
212
  }
213
213
  //#endregion
214
+ //#region src/cli/starter-ui.css?raw
215
+ var starter_ui_default = ":root {\n --text: rgb(22 23 29 / 70%);\n --text-h: #16171d;\n --bg: #fff;\n --border: #e5e4e7;\n --code-bg: #f2f1f4;\n --accent: #6c3bff;\n --error: #b42318;\n --social-bg: #f2f1f4;\n --radius-inner: 4px;\n --radius-element: 6px;\n --shadow: rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px;\n\n --sans: system-ui, 'Segoe UI', Roboto, sans-serif;\n --heading: system-ui, 'Segoe UI', Roboto, sans-serif;\n --mono: ui-monospace, Consolas, monospace;\n\n font: 18px/145% var(--sans);\n letter-spacing: 0.18px;\n color-scheme: light dark;\n color: var(--text);\n background: var(--bg);\n font-synthesis: none;\n text-rendering: optimizeLegibility;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n\n @media (max-width: 1024px) {\n font-size: 16px;\n }\n}\n\n@supports (corner-shape: squircle) {\n :root {\n /* Squircle curves need about twice the radius to read as rounded. */\n --radius-inner: 8px;\n --radius-element: 12px;\n }\n\n :where(.starter, .starter *),\n .starter *::before,\n .starter *::after {\n corner-shape: squircle;\n }\n}\n\n@media (prefers-color-scheme: dark) {\n :root {\n --text: rgb(255 255 255 / 70%);\n --text-h: #fff;\n --bg: #16171d;\n --border: #3b3440;\n --code-bg: #3b3440;\n --accent: #b39aff;\n --error: #fda29b;\n --social-bg: #3b3440;\n --shadow: rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px;\n }\n}\n\n.starter {\n width: 1126px;\n max-width: 100%;\n margin: 0 auto;\n text-align: center;\n border-inline: 1px solid var(--border);\n min-height: 100svh;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n}\n\nh1,\nh2 {\n font-family: var(--heading);\n font-weight: 500;\n color: var(--text-h);\n}\n\nh1 {\n font-size: 56px;\n letter-spacing: -1.68px;\n margin: 32px 0;\n @media (max-width: 1024px) {\n font-size: 36px;\n margin: 20px 0;\n }\n}\nh2 {\n font-size: 24px;\n line-height: 118%;\n letter-spacing: -0.24px;\n margin: 0 0 8px;\n @media (max-width: 1024px) {\n font-size: 20px;\n }\n}\np {\n margin: 0;\n}\n\ncode {\n font-family: var(--mono);\n display: inline-flex;\n border-radius: var(--radius-inner);\n color: var(--text-h);\n font-size: 15px;\n line-height: 135%;\n padding: 4px 8px;\n background: var(--code-bg);\n}\n\n.hero {\n position: relative;\n\n .base,\n .framework,\n .void {\n inset-inline: 0;\n margin: 0 auto;\n }\n\n .base {\n width: 170px;\n position: relative;\n z-index: 0;\n }\n\n .framework,\n .void {\n position: absolute;\n }\n\n .framework {\n z-index: 1;\n top: 34px;\n height: 28px;\n transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) scale(1.4);\n }\n\n .void {\n z-index: 0;\n top: 107px;\n height: 22px;\n width: auto;\n transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) scale(0.8);\n }\n}\n\n#center {\n display: flex;\n flex-direction: column;\n gap: 25px;\n padding-bottom: 24px;\n place-content: center;\n place-items: center;\n flex-grow: 1;\n\n @media (max-width: 1024px) {\n padding: 32px 20px 24px;\n gap: 18px;\n }\n}\n\n.starter-intro {\n width: min(720px, 100%);\n padding-inline: 20px;\n box-sizing: border-box;\n\n a {\n color: var(--accent);\n }\n\n @media (max-width: 1024px) {\n padding-inline: 0;\n }\n}\n\n.starter-description {\n display: grid;\n gap: 12px;\n\n > p:nth-child(2) {\n text-wrap: balance;\n }\n}\n\n.starter-demo {\n border-top: 1px solid var(--border);\n padding: 32px;\n text-align: left;\n\n @media (max-width: 1024px) {\n padding: 24px 20px;\n }\n}\n\n.demo-content {\n max-width: 640px;\n margin: 0 auto;\n display: grid;\n gap: 24px;\n\n h2 {\n margin: 0;\n }\n}\n\n.messages {\n list-style: none;\n margin: 0;\n padding: 0;\n display: grid;\n gap: 12px;\n\n li {\n padding: 12px 16px;\n border: 1px solid var(--border);\n border-radius: var(--radius-element);\n overflow-wrap: anywhere;\n }\n\n strong {\n display: block;\n color: var(--text-h);\n font-weight: 500;\n }\n\n small {\n font-size: 14px;\n }\n}\n\n.message-form {\n display: grid;\n gap: 12px;\n\n label {\n color: var(--text-h);\n }\n\n input,\n button {\n font: inherit;\n color: var(--text-h);\n background: var(--social-bg);\n border: 1px solid var(--border);\n border-radius: var(--radius-element);\n padding: 10px 12px;\n\n &:focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n }\n }\n\n input {\n flex: 1;\n min-width: 0;\n }\n\n button {\n cursor: pointer;\n\n &:hover {\n border-color: var(--accent);\n }\n\n &:disabled {\n cursor: wait;\n opacity: 0.6;\n }\n }\n}\n\n.message-fields {\n display: flex;\n gap: 8px;\n\n @media (max-width: 480px) {\n flex-direction: column;\n }\n}\n\n.message-form [data-void-form-error] {\n color: var(--error);\n font-size: 14px;\n}\n\n#next-steps {\n display: flex;\n border-top: 1px solid var(--border);\n text-align: left;\n\n & > div {\n flex: 1 1 0;\n padding: 32px;\n @media (max-width: 1024px) {\n padding: 24px 20px;\n }\n }\n\n .icon {\n color: var(--accent);\n margin-bottom: 16px;\n width: 22px;\n height: 22px;\n }\n\n @media (max-width: 1024px) {\n flex-direction: column;\n text-align: center;\n }\n}\n\n#docs {\n border-right: 1px solid var(--border);\n\n @media (max-width: 1024px) {\n border-right: none;\n border-bottom: 1px solid var(--border);\n }\n}\n\n#next-steps ul {\n list-style: none;\n padding: 0;\n display: flex;\n gap: 8px;\n margin: 32px 0 0;\n\n a {\n color: var(--text-h);\n font-size: 16px;\n border-radius: var(--radius-element);\n background: var(--social-bg);\n display: flex;\n padding: 6px 12px;\n align-items: center;\n gap: 8px;\n text-decoration: none;\n transition: box-shadow 0.3s;\n\n &:hover {\n box-shadow: var(--shadow);\n }\n .button-icon {\n height: 18px;\n width: 18px;\n }\n }\n\n @media (max-width: 1024px) {\n margin-top: 20px;\n flex-wrap: wrap;\n justify-content: center;\n\n li {\n flex: 1 1 calc(50% - 8px);\n }\n\n a {\n width: 100%;\n justify-content: center;\n box-sizing: border-box;\n }\n }\n}\n\n#spacer {\n height: 88px;\n border-top: 1px solid var(--border);\n @media (max-width: 1024px) {\n height: 48px;\n }\n}\n\n.ticks {\n position: relative;\n width: 100%;\n\n &::before,\n &::after {\n content: '';\n position: absolute;\n top: -4.5px;\n border: 5px solid transparent;\n }\n\n &::before {\n left: 0;\n border-left-color: var(--border);\n }\n &::after {\n right: 0;\n border-right-color: var(--border);\n }\n}\n";
216
+ //#endregion
214
217
  //#region src/cli/template.ts
215
218
  const SCAFFOLD_GITIGNORE_LINES = [
216
219
  "node_modules",
@@ -242,7 +245,7 @@ const SCAFFOLD_TOOLCHAIN_OPTIONS = [{
242
245
  label: "Vite",
243
246
  hint: "Uses plain Vite commands without Vite+."
244
247
  }];
245
- const REACT_SCAFFOLD_COMPILER_DEV_DEPENDENCIES = { "oxc-transform-react": "^0.149.0" };
248
+ const REACT_SCAFFOLD_COMPILER_DEV_DEPENDENCIES = { "oxc-transform-react": "^0.152.0" };
246
249
  const SCAFFOLD_FRAMEWORK_OPTIONS = [
247
250
  {
248
251
  value: "react",
@@ -268,6 +271,7 @@ const SCAFFOLD_FRAMEWORK_OPTIONS = [
268
271
  const SCAFFOLD_FRAMEWORKS = {
269
272
  react: {
270
273
  label: "React",
274
+ docsUrl: "https://react.dev/",
271
275
  adapterPackage: "@void/react",
272
276
  adapterImport: "voidReact",
273
277
  adapterPlugin: "@void/react/plugin",
@@ -285,6 +289,7 @@ const SCAFFOLD_FRAMEWORKS = {
285
289
  },
286
290
  vue: {
287
291
  label: "Vue",
292
+ docsUrl: "https://vuejs.org/",
288
293
  adapterPackage: "@void/vue",
289
294
  adapterImport: "voidVue",
290
295
  adapterPlugin: "@void/vue/plugin",
@@ -295,6 +300,7 @@ const SCAFFOLD_FRAMEWORKS = {
295
300
  },
296
301
  svelte: {
297
302
  label: "Svelte",
303
+ docsUrl: "https://svelte.dev/",
298
304
  adapterPackage: "@void/svelte",
299
305
  adapterImport: "voidSvelte",
300
306
  adapterPlugin: "@void/svelte/plugin",
@@ -305,6 +311,7 @@ const SCAFFOLD_FRAMEWORKS = {
305
311
  },
306
312
  solid: {
307
313
  label: "Solid",
314
+ docsUrl: "https://www.solidjs.com/",
308
315
  adapterPackage: "@void/solid",
309
316
  adapterImport: "voidSolid",
310
317
  adapterPlugin: "@void/solid/plugin",
@@ -326,9 +333,16 @@ function getScaffoldTsconfigCompilerOptions(framework) {
326
333
  function createPagesStarterTemplate(options) {
327
334
  const { framework, starterMode, toolchain = "vite", voidPackageSpec, adapterPackageSpec } = options;
328
335
  const config = SCAFFOLD_FRAMEWORKS[framework];
336
+ const assets = join(getPackageDir(), "templates", "assets");
329
337
  const files = {
330
338
  "vite.config.ts": getViteConfig(framework, config),
331
- [`pages/index.${config.pageExtension}`]: getIndexPage(framework, starterMode)
339
+ [`pages/index.${config.pageExtension}`]: getIndexPage(framework, starterMode),
340
+ "pages/style.css": starter_ui_default,
341
+ "pages/vite-env.d.ts": "/// <reference types=\"vite/client\" />\n",
342
+ "public/stack.svg": readFileSync(join(assets, "stack.svg"), "utf8"),
343
+ "public/void.svg": readFileSync(join(assets, "void.svg"), "utf8"),
344
+ [`public/${framework}.svg`]: readFileSync(join(assets, `${framework}.svg`), "utf8"),
345
+ "public/icons.svg": readFileSync(join(assets, "icons.svg"), "utf8")
332
346
  };
333
347
  if (starterMode !== "static") {
334
348
  files["pages/index.server.ts"] = getIndexServer();
@@ -452,210 +466,216 @@ export default defineSeed<typeof import("./schema")>(async ({ db, schema }) => {
452
466
  `;
453
467
  }
454
468
  function getIndexPage(framework, starterMode) {
455
- if (starterMode === "static") switch (framework) {
456
- case "react": return `export default function HomePage() {
457
- return (
458
- <main>
459
- <h1>Welcome to Void</h1>
460
- <p>This starter is running in Pages mode with no database or server code yet.</p>
461
- <p>
462
- Add <code>pages/*.server.ts</code>, <code>routes/</code>, or <code>void/db</code> when
463
- you want to grow into full-stack features.
464
- </p>
465
- </main>
466
- );
467
- }
468
- `;
469
- case "vue": return `<template>
470
- <main>
471
- <h1>Welcome to Void</h1>
472
- <p>This starter is running in Pages mode with no database or server code yet.</p>
473
- <p>
474
- Add <code>pages/*.server.ts</code>, <code>routes/</code>, or <code>void/db</code> when you
475
- want to grow into full-stack features.
476
- </p>
477
- </main>
478
- </template>
479
- `;
480
- case "svelte": return `<main>
481
- <h1>Welcome to Void</h1>
482
- <p>This starter is running in Pages mode with no database or server code yet.</p>
483
- <p>
484
- Add <code>pages/*.server.ts</code>, <code>routes/</code>, or <code>void/db</code> when you
485
- want to grow into full-stack features.
486
- </p>
487
- </main>
488
- `;
489
- case "solid": return `export default function HomePage() {
490
- return (
491
- <main>
492
- <h1>Welcome to Void</h1>
493
- <p>This starter is running in Pages mode with no database or server code yet.</p>
494
- <p>
495
- Add <code>pages/*.server.ts</code>, <code>routes/</code>, or <code>void/db</code> when
496
- you want to grow into full-stack features.
497
- </p>
498
- </main>
499
- );
500
- }
501
- `;
502
- }
469
+ const { label, docsUrl } = SCAFFOLD_FRAMEWORKS[framework];
470
+ const hasDatabase = starterMode !== "static";
471
+ const body = `<main class="starter">
472
+ <section id="center" aria-labelledby="welcome-title">
473
+ <div class="hero">
474
+ <img class="base" src="/stack.svg" width="170" height="179" alt="" />
475
+ <img class="framework" src="/${framework}.svg" alt="${label} logo" />
476
+ <img class="void" src="/void.svg" alt="Void logo" />
477
+ </div>
478
+ <div class="starter-intro">
479
+ <h1 id="welcome-title">Welcome to Void</h1>
480
+ <div class="starter-description">
481
+ ${indent(starterMode === "static" ? `<p>This starter is running in Pages mode with no database or server code yet.</p>
482
+ <p>
483
+ Add <code>pages/*.server.ts</code>, <code>routes/</code>, or <code>void/db</code> when
484
+ you want to grow into full-stack features.
485
+ </p>` : `<p>
486
+ This starter is running in Pages mode. The list below is loaded from your database in <code>pages/index.server.ts</code>.
487
+ </p>
488
+ <p>API example: <a href="/api/hello">/api/hello</a></p>`, 8)}
489
+ </div>
490
+ </div>
491
+ </section>${hasDatabase ? `\n <div class="ticks" aria-hidden="true"></div>\n${indent(getMessagesDemo(framework), 2)}` : ""}
492
+ <div class="ticks" aria-hidden="true"></div>
493
+ <section id="next-steps" aria-label="Resources">
494
+ <div id="docs">
495
+ <svg class="icon" role="presentation" aria-hidden="true">
496
+ <use href="/icons.svg#documentation-icon"></use>
497
+ </svg>
498
+ <h2>Documentation</h2>
499
+ <p>Your questions, answered</p>
500
+ <ul>
501
+ <li>
502
+ <a href="https://void.cloud/guide/quickstart" target="_blank" rel="noreferrer">
503
+ Quickstart
504
+ </a>
505
+ </li>
506
+ <li>
507
+ <a href="${docsUrl}" target="_blank" rel="noreferrer">
508
+ <img class="button-icon" src="/${framework}.svg" alt="" />
509
+ Learn more
510
+ </a>
511
+ </li>
512
+ </ul>
513
+ </div>
514
+ <div id="social">
515
+ <svg class="icon" role="presentation" aria-hidden="true">
516
+ <use href="/icons.svg#social-icon"></use>
517
+ </svg>
518
+ <h2>Connect with us</h2>
519
+ <p>Join the Void community</p>
520
+ <ul>
521
+ <li>
522
+ <a href="https://github.com/voidzero-dev/void" target="_blank" rel="noreferrer">
523
+ <svg class="button-icon" role="presentation" aria-hidden="true">
524
+ <use href="/icons.svg#github-icon"></use>
525
+ </svg>
526
+ GitHub
527
+ </a>
528
+ </li>
529
+ <li>
530
+ <a href="https://discord.gg/cC6TEVFKSx" target="_blank" rel="noreferrer">
531
+ <svg class="button-icon" role="presentation" aria-hidden="true">
532
+ <use href="/icons.svg#discord-icon"></use>
533
+ </svg>
534
+ Discord
535
+ </a>
536
+ </li>
537
+ <li>
538
+ <a href="https://x.com/deploytovoid" target="_blank" rel="noreferrer">
539
+ <svg class="button-icon" role="presentation" aria-hidden="true">
540
+ <use href="/icons.svg#x-icon"></use>
541
+ </svg>
542
+ X.com
543
+ </a>
544
+ </li>
545
+ <li>
546
+ <a href="https://bsky.app/profile/voidzero.dev" target="_blank" rel="noreferrer">
547
+ <svg class="button-icon" role="presentation" aria-hidden="true">
548
+ <use href="/icons.svg#bluesky-icon"></use>
549
+ </svg>
550
+ Bluesky
551
+ </a>
552
+ </li>
553
+ </ul>
554
+ </div>
555
+ </section>
556
+ <div class="ticks" aria-hidden="true"></div>
557
+ <div id="spacer" aria-hidden="true"></div>
558
+ </main>`;
503
559
  switch (framework) {
504
- case "react": return `import { Form, useForm } from "${SCAFFOLD_FRAMEWORKS[framework].adapterPackage}";
560
+ case "react":
561
+ case "solid": {
562
+ const jsx = framework === "react" ? body.replaceAll("class=", "className=") : body;
563
+ return `${hasDatabase ? `${framework === "solid" ? "import { For, Show } from \"solid-js\";\n" : ""}import { Form, useForm } from "${SCAFFOLD_FRAMEWORKS[framework].adapterPackage}";
505
564
  import type { Props } from "./index.server";
565
+ ` : ""}import "./style.css";
506
566
 
507
- export default function HomePage({ messages }: Props) {
508
- const form = useForm("/", { text: "" });
509
- return (
510
- <main>
511
- <h1>Welcome to Void</h1>
512
- <p>
513
- This starter is running in Pages mode. The list below is loaded from your database in
514
- <code> pages/index.server.ts</code>.
515
- </p>
516
- <p>
517
- API example: <a href="/api/hello">/api/hello</a>
518
- </p>
519
- {messages.length === 0 ? (
520
- <p>
521
- No messages yet. Run <code>void db seed</code> to load the sample row from
522
- <code> db/seed.ts</code>.
523
- </p>
524
- ) : (
525
- <ul>
526
- {messages.map((message) => (
527
- <li key={message.id}>
528
- <strong>{message.text}</strong>
529
- <br />
530
- <small>{String(message.createdAt)}</small>
531
- </li>
532
- ))}
533
- </ul>
534
- )}
535
- <Form form={form}>
536
- <label>Message <input value={form.data.text} onChange={event => form.setData("text", event.target.value)} /></label>
537
- <button disabled={form.pending}>Add message</button>
538
- </Form>
539
- </main>
567
+ export default function HomePage(${hasDatabase ? framework === "react" ? "{ messages }: Props" : "props: Props" : ""}) {
568
+ ${hasDatabase ? " const form = useForm(\"/\", { text: \"\" });\n\n" : ""} return (
569
+ ${indent(jsx, 4)}
540
570
  );
541
571
  }
542
572
  `;
573
+ }
543
574
  case "vue": return `<script setup lang="ts">
544
- import type { Props } from "./index.server";
545
- import { Form, useForm } from "@void/vue";
546
-
547
- defineProps<Props>();
548
- const form = useForm("/", { text: "" });
549
- <\/script>
575
+ ${hasDatabase ? "import { Form, useForm } from \"@void/vue\";\nimport type { Props } from \"./index.server\";\n" : ""}import "./style.css";
576
+ ${hasDatabase ? "\ndefineProps<Props>();\nconst form = useForm(\"/\", { text: \"\" });\n" : ""}<\/script>
550
577
 
551
578
  <template>
552
- <main>
553
- <h1>Welcome to Void</h1>
554
- <p>
555
- This starter is running in Pages mode. The list below is loaded from your database in
556
- <code>pages/index.server.ts</code>.
557
- </p>
558
- <p>
559
- API example: <a href="/api/hello">/api/hello</a>
560
- </p>
561
- <p v-if="messages.length === 0">
562
- No messages yet. Run <code>void db seed</code> to load the sample row from
563
- <code>db/seed.ts</code>.
564
- </p>
565
- <ul v-else>
566
- <li v-for="message in messages" :key="message.id">
567
- <strong>{{ message.text }}</strong>
568
- <br />
569
- <small>{{ String(message.createdAt) }}</small>
570
- </li>
571
- </ul>
572
- <Form :form="form">
573
- <label>Message <input v-model="form.data.text" /></label>
574
- <button :disabled="form.pending">Add message</button>
575
- </Form>
576
- </main>
579
+ ${indent(body, 2)}
577
580
  </template>
578
581
  `;
579
582
  case "svelte": return `<script lang="ts">
580
- import type { Props } from "./index.server";
581
- import { Form, useForm } from "@void/svelte";
582
- const form = useForm("/", { text: "" });
583
-
584
- let { messages }: Props = $props();
585
- <\/script>
586
-
587
- <main>
588
- <h1>Welcome to Void</h1>
589
- <p>
590
- This starter is running in Pages mode. The list below is loaded from your database in
591
- <code>pages/index.server.ts</code>.
592
- </p>
593
- <p>
594
- API example: <a href="/api/hello">/api/hello</a>
595
- </p>
596
- {#if messages.length === 0}
597
- <p>
598
- No messages yet. Run <code>void db seed</code> to load the sample row from
599
- <code>db/seed.ts</code>.
600
- </p>
601
- {:else}
602
- <ul>
603
- {#each messages as message (message.id)}
604
- <li>
605
- <strong>{message.text}</strong>
606
- <br />
607
- <small>{String(message.createdAt)}</small>
608
- </li>
609
- {/each}
610
- </ul>
611
- {/if}
612
- <Form {form}>
613
- <label>Message <input bind:value={form.data.text} /></label>
614
- <button disabled={form.pending}>Add message</button>
615
- </Form>
616
- </main>
617
- `;
618
- case "solid": return `import { Form, useForm } from "${SCAFFOLD_FRAMEWORKS[framework].adapterPackage}";
619
- import type { Props } from "./index.server";
583
+ ${hasDatabase ? " import { Form, useForm } from \"@void/svelte\";\n import type { Props } from \"./index.server\";\n" : ""} import "./style.css";
584
+ ${hasDatabase ? "\n let { messages }: Props = $props();\n const form = useForm(\"/\", { text: \"\" });\n" : ""}<\/script>
620
585
 
621
- export default function HomePage(props: Props) {
622
- const form = useForm("/", { text: "" });
623
- return (
624
- <main>
625
- <h1>Welcome to Void</h1>
626
- <p>
627
- This starter is running in Pages mode. The list below is loaded from your database in
628
- <code> pages/index.server.ts</code>.
629
- </p>
630
- <p>
631
- API example: <a href="/api/hello">/api/hello</a>
632
- </p>
633
- {props.messages.length === 0 ? (
634
- <p>
635
- No messages yet. Run <code>void db seed</code> to load the sample row from
636
- <code> db/seed.ts</code>.
637
- </p>
638
- ) : (
639
- <ul>
640
- {props.messages.map((message) => (
641
- <li>
642
- <strong>{message.text}</strong>
643
- <br />
644
- <small>{String(message.createdAt)}</small>
645
- </li>
646
- ))}
647
- </ul>
648
- )}
649
- <Form form={form}>
650
- <label>Message <input value={form.data.text} onInput={event => form.setData("text", event.target.value)} /></label>
651
- <button disabled={form.pending}>Add message</button>
652
- </Form>
653
- </main>
654
- );
655
- }
586
+ ${body}
656
587
  `;
657
588
  }
658
589
  }
590
+ function getMessagesDemo(framework) {
591
+ const empty = `<p class="empty-state">
592
+ No messages yet. Run <code>void db seed</code> to load the sample row from <code>db/seed.ts</code>.
593
+ </p>`;
594
+ const content = {
595
+ react: `{messages.length === 0 ? (
596
+ ${indent(empty, 2)}
597
+ ) : (
598
+ <ul class="messages">
599
+ {messages.map((message) => (
600
+ <li key={message.id}>
601
+ <strong>{message.text}</strong>
602
+ <small>{String(message.createdAt)}</small>
603
+ </li>
604
+ ))}
605
+ </ul>
606
+ )}
607
+ <Form form={form} className="message-form">
608
+ <label htmlFor="message-text">Message</label>
609
+ <div class="message-fields">
610
+ <input id="message-text" name="text" value={form.data.text} onChange={(event) => form.setData("text", event.target.value)} required maxLength={1000} />
611
+ <button type="submit" disabled={form.pending}>Add message</button>
612
+ </div>
613
+ </Form>`,
614
+ vue: `<template v-if="messages.length === 0">
615
+ ${indent(empty, 2)}
616
+ </template>
617
+ <ul v-else class="messages">
618
+ <li v-for="message in messages" :key="message.id">
619
+ <strong>{{ message.text }}</strong>
620
+ <small>{{ String(message.createdAt) }}</small>
621
+ </li>
622
+ </ul>
623
+ <Form :form="form" class="message-form">
624
+ <label for="message-text">Message</label>
625
+ <div class="message-fields">
626
+ <input id="message-text" name="text" v-model="form.data.text" required maxlength="1000" />
627
+ <button type="submit" :disabled="form.pending">Add message</button>
628
+ </div>
629
+ </Form>`,
630
+ svelte: `{#if messages.length === 0}
631
+ ${indent(empty, 2)}
632
+ {:else}
633
+ <ul class="messages">
634
+ {#each messages as message (message.id)}
635
+ <li>
636
+ <strong>{message.text}</strong>
637
+ <small>{String(message.createdAt)}</small>
638
+ </li>
639
+ {/each}
640
+ </ul>
641
+ {/if}
642
+ <Form {form} class="message-form">
643
+ <label for="message-text">Message</label>
644
+ <div class="message-fields">
645
+ <input id="message-text" name="text" bind:value={form.data.text} required maxlength={1000} />
646
+ <button type="submit" disabled={form.pending}>Add message</button>
647
+ </div>
648
+ </Form>`,
649
+ solid: `<Show when={props.messages.length > 0} fallback={
650
+ ${indent(empty, 2)}
651
+ }>
652
+ <ul class="messages">
653
+ <For each={props.messages}>{(message) => (
654
+ <li>
655
+ <strong>{message.text}</strong>
656
+ <small>{String(message.createdAt)}</small>
657
+ </li>
658
+ )}</For>
659
+ </ul>
660
+ </Show>
661
+ <Form form={form} class="message-form">
662
+ <label for="message-text">Message</label>
663
+ <div class="message-fields">
664
+ <input id="message-text" name="text" value={form.data.text} onInput={(event) => form.setData("text", event.currentTarget.value)} required maxLength={1000} />
665
+ <button type="submit" disabled={form.pending}>Add message</button>
666
+ </div>
667
+ </Form>`
668
+ }[framework];
669
+ return `<section class="starter-demo" aria-labelledby="messages-title">
670
+ <div class="demo-content">
671
+ <h2 id="messages-title">Messages</h2>
672
+ ${indent(content, 4)}
673
+ </div>
674
+ </section>`;
675
+ }
676
+ function indent(source, spaces) {
677
+ return source.split("\n").map((line) => " ".repeat(spaces) + line).join("\n");
678
+ }
659
679
  //#endregion
660
680
  //#region src/cli/init-vite-plus.ts
661
681
  /** Keep every Vite consumer on the core used by the selected Vite+ toolchain. */
@@ -2408,18 +2428,28 @@ export const posts = sqliteTable("posts", {
2408
2428
  });
2409
2429
  `;
2410
2430
  }
2431
+ function demoFiles(root) {
2432
+ const paths = readProjectPaths(root);
2433
+ return {
2434
+ schema: paths.schemaFile,
2435
+ route: join(paths.routesDir, "api", "posts.ts"),
2436
+ client: join(paths.ssrDir, "demo.ts")
2437
+ };
2438
+ }
2411
2439
  /**
2412
2440
  * Scaffolds the "SQL → types → handler → fetch" demo into an existing project.
2413
2441
  * `setupExistingProject` has already declared `void` in `package.json`.
2442
+ * The caller skips the demo when any of its files exists, so these writes never replace user code.
2414
2443
  */
2415
2444
  function scaffoldDemo(root, dialect) {
2416
2445
  const devCommand = getDevCommand(root);
2417
2446
  const paths = readProjectPaths(root);
2447
+ const files = demoFiles(root);
2418
2448
  mkdirSync(paths.dbDir, { recursive: true });
2419
- writeFileSync(paths.schemaFile, demoSchemaSource(dialect));
2449
+ writeFileSync(files.schema, demoSchemaSource(dialect));
2420
2450
  log.step("Created db/schema.ts");
2421
2451
  mkdirSync(join(paths.routesDir, "api"), { recursive: true });
2422
- writeFileSync(join(paths.routesDir, "api", "posts.ts"), `import { defineHandler } from "void";
2452
+ writeFileSync(files.route, `import { defineHandler } from "void";
2423
2453
  import { db } from "void/db";
2424
2454
  import { posts } from "@schema";
2425
2455
 
@@ -2431,7 +2461,7 @@ export const GET = defineHandler(async () => {
2431
2461
  log.step("Created routes/api/posts.ts");
2432
2462
  ensureProjectTsconfig(root, void 0, true, true, { paths });
2433
2463
  mkdirSync(paths.ssrDir, { recursive: true });
2434
- writeFileSync(join(paths.ssrDir, "demo.ts"), `import { fetch } from "void/client";
2464
+ writeFileSync(files.client, `import { fetch } from "void/client";
2435
2465
 
2436
2466
  // Fully typed: response type is inferred from the GET handler's return type,
2437
2467
  // which is derived from the migration schema. No manual types needed.
@@ -2569,8 +2599,13 @@ async function runInitCommand(root, initArgs) {
2569
2599
  linkSkillsForSpecificAgents(root, agents);
2570
2600
  } else log.info("Bundled Void docs: node_modules/void/docs/");
2571
2601
  if (!targeted && !framework) {
2602
+ const existingDemoFiles = Object.values(demoFiles(root)).filter((file) => existsSync(file));
2572
2603
  if (didScaffoldStarter || state.hasRoutes || state.hasMigrations || state.hasPages) log.info("Project already has pages, routes, or migrations, skipping demo");
2573
- else {
2604
+ else if (hasDrizzleSchema(root)) log.info("Project already has a database schema, skipping demo");
2605
+ else if (existingDemoFiles.length > 0) {
2606
+ const names = existingDemoFiles.map((file) => relative(root, file)).join(", ");
2607
+ log.info(`Project already has ${names}, skipping demo`);
2608
+ } else {
2574
2609
  const scaffold = await confirm({ message: "Scaffold demo code? (migration + API route + frontend fetch)" });
2575
2610
  if (isCancel(scaffold)) {
2576
2611
  cancel("Setup cancelled.");
@@ -1,9 +1,9 @@
1
1
  import { r as readProjectConfig, t as attachSelectedPlatform, u as writeProjectConfig } from "./project-rqDnVsJN.mjs";
2
2
  import { c as getToken, s as getSiteDomain } from "./auth-B6iOCjKI.mjs";
3
3
  import { H as outro, L as log, O as cancel } from "./dist-Cor9XGoz.mjs";
4
- import { r as PlatformClient } from "./client-BEkdj1kx.mjs";
4
+ import { r as PlatformClient } from "./client-B3LnB_bE.mjs";
5
5
  import { f as import_picocolors, m as printSuccess } from "./output-BpgYODYx.mjs";
6
- import { a as promptProjectSelection, r as promptAndCreateProject } from "./create-project-DV71ol5d.mjs";
6
+ import { a as promptProjectSelection, r as promptAndCreateProject } from "./create-project-Bxd02EHN.mjs";
7
7
  import { a as resolveProjectBySlug } from "./resolve-project-Xoi6eTWY.mjs";
8
8
  //#region src/cli/link.ts
9
9
  function projectLabel(slug, root, url) {
@@ -1,7 +1,7 @@
1
1
  import { n as readDeploymentPlatform, r as readProjectConfig } from "./project-rqDnVsJN.mjs";
2
2
  import { c as getToken } from "./auth-B6iOCjKI.mjs";
3
3
  import { H as outro, L as log } from "./dist-Cor9XGoz.mjs";
4
- import { r as PlatformClient } from "./client-BEkdj1kx.mjs";
4
+ import { r as PlatformClient } from "./client-B3LnB_bE.mjs";
5
5
  import { _ as tablePrefix, c as formatRelativeTime, f as import_picocolors, h as sanitizeTerminalText, i as dim, o as formatCount } from "./output-BpgYODYx.mjs";
6
6
  import { a as resolveProjectBySlug } from "./resolve-project-Xoi6eTWY.mjs";
7
7
  //#region src/cli/list.ts
@@ -42,7 +42,7 @@ async function runListCommand(root, slug) {
42
42
  }
43
43
  async function showCloudflareDeployments(root, slug) {
44
44
  if (slug) throw new Error("project: A project name selects a Void platform project and is not used for direct Cloudflare apps.");
45
- const { readCloudflareWorkerStatus, resolveCloudflareOperationContext } = await import("./cloudflare-operations-DsSaw2BT.mjs");
45
+ const { readCloudflareWorkerStatus, resolveCloudflareOperationContext } = await import("./cloudflare-operations-BtulCNOg.mjs");
46
46
  const status = await readCloudflareWorkerStatus(resolveCloudflareOperationContext(root));
47
47
  if (status.versions.length === 0) {
48
48
  outro(`No deployments for ${status.workerName}.`);
@@ -1,5 +1,5 @@
1
- import { r as AuthUser } from "./auth-DkcFflXV.mjs";
2
- import { a as CloudEnv, r as CloudContext } from "./handler-BbkSPzGF.mjs";
1
+ import { r as AuthUser } from "./auth-g2TQbDLD.mjs";
2
+ import { a as CloudEnv, r as CloudContext } from "./handler-BHktmuTL.mjs";
3
3
  //#region src/runtime/live.d.ts
4
4
  type MaybePromise<T> = T | Promise<T>;
5
5
  type LiveControlRequest = {
@@ -1,5 +1,5 @@
1
1
  import { a as join, c as resolve, i as isAbsolute } from "./dist-ipQi0Q8y.mjs";
2
- import { b as readRootWranglerConfig } from "./wrangler-F96NnLyb.mjs";
2
+ import { b as readRootWranglerConfig } from "./wrangler-I-scB_1t.mjs";
3
3
  import { r as getMiniflareDatabasePath } from "./runner-3uGC-oLF.mjs";
4
4
  import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
5
5
  import { createHash } from "node:crypto";
@@ -0,0 +1 @@
1
+ export { r as selectPlatformLoginMethod } from "./login-PcFy5mlC.mjs";