@voltro/cli 0.54.0 → 0.56.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 (173) hide show
  1. package/CHANGELOG.md +639 -2
  2. package/bin/voltro.mjs +24 -0
  3. package/dist/{apiBuild-DTWp0S_q.js → apiBuild-Bdaetr37.js} +119 -88
  4. package/dist/apiBuild-Vw1figjO.js +2 -0
  5. package/dist/bin.js +1 -1
  6. package/dist/{build-D4ygSbnV.js → build-CI36wL4R.js} +339 -314
  7. package/dist/buildReport-52gHKgfO.js +64 -0
  8. package/dist/{checkCommand-Dg1G7Gwd.js → checkCommand-CAwFXrxA.js} +6 -6
  9. package/dist/{checkCommand-L7DTlpIF.js → checkCommand-D0QV_zM_.js} +1 -1
  10. package/dist/{clusterCmd-DrVFCzSj.js → clusterCmd-CdLB1GkT.js} +1 -1
  11. package/dist/{codegen-DSLM8Su9.js → codegen-Bth5lUTU.js} +76 -64
  12. package/dist/codegen-DbH7NbCR.js +2 -0
  13. package/dist/{codegenCommand-CG_Vx4lc.js → codegenCommand-CidbQzbv.js} +15 -14
  14. package/dist/{codemodRunner-Cd4xkC6u.js → codemodRunner-BlQPfjzA.js} +277 -0
  15. package/dist/{commands-6Kzi92Np.js → commands-CWjfThXv.js} +35 -35
  16. package/dist/{dashboardCommand-Cq1PWvI1.js → dashboardCommand-BekcY5Ls.js} +3 -3
  17. package/dist/{dataCommand-DYzW8vkv.js → dataCommand-2pccgbIy.js} +267 -195
  18. package/dist/{dbCommand-B4NWZtGL.js → dbCommand-DpK_vQET.js} +457 -441
  19. package/dist/dbCommand-DrycGWWt.js +2 -0
  20. package/dist/{dev-cKUiZZsB.js → dev-B9Gz0k85.js} +1 -1
  21. package/dist/{dev-CmuvUKRq.js → dev-Dw263KPu.js} +2598 -2452
  22. package/dist/doctorCommand-BMWs6aVm.js +2 -0
  23. package/dist/{doctorCommand-DCiFVMtZ.js → doctorCommand-aR_bFmIi.js} +353 -251
  24. package/dist/{dormancyCommand-w1TrmgYP.js → dormancyCommand-eXTQMbHU.js} +1 -1
  25. package/dist/{embeddingsCommand-CMgPyRTr.js → embeddingsCommand-CTmiQvwa.js} +1 -1
  26. package/dist/{envCommand-Cyynmcfa.js → envCommand-BDUgV7EM.js} +12 -12
  27. package/dist/{evolveCommand-BwvQ8dVH.js → evolveCommand-YV8qW1LU.js} +2 -2
  28. package/dist/frameworkTableAssembly-CGNC0qr7.js +2 -0
  29. package/dist/{frameworkTableAssembly-D7LJuALW.js → frameworkTableAssembly-DNOFXfEQ.js} +102 -100
  30. package/dist/index.js +1 -1
  31. package/dist/{infoCommand-DlYlUPqs.js → infoCommand-BjVXpMlP.js} +1 -1
  32. package/dist/inspect-CNYvNXPU.js +1484 -0
  33. package/dist/inspect-S2rWy1Ys.js +2 -0
  34. package/dist/{inspectCmd-niF97fAq.js → inspectCmd-CP-G0sVK.js} +1 -1
  35. package/dist/{inspectFetch-EMuhTG_9.js → inspectFetch-BU1NyzxV.js} +36 -24
  36. package/dist/{inspectMetrics-CGF94puw.js → inspectMetrics-BY0Sjb2F.js} +19 -19
  37. package/dist/interruptedReplace-CwnkBb2X.js +41 -0
  38. package/dist/interruptedReplace-qzmFI020.js +2 -0
  39. package/dist/{logsCmd-B6oNsfaZ.js → logsCmd-BU8uCdys.js} +1 -1
  40. package/dist/{manifestBuild-Cqgsx2bM.js → manifestBuild-CEkjfpwc.js} +1 -1
  41. package/dist/manifestBuild-DIa_s6u0.js +2 -0
  42. package/dist/{migrate-BK_Bbx-_.js → migrate-SICulyz1.js} +2 -2
  43. package/dist/precompressAssets-YhTi1aWp.js +40 -0
  44. package/dist/{probeCommand-_C0YU207.js → probeCommand-6HxEkNDG.js} +2 -2
  45. package/dist/{runtimeTrace-CGWx1Q6l.js → runtimeTrace-DgYMc09E.js} +1 -1
  46. package/dist/{scheduleCmd-DQRu6BZC.js → scheduleCmd-DYBUfo_T.js} +1 -1
  47. package/dist/{sdkgen-CDGHQUFj.js → sdkgen-PY-umd6O.js} +1 -1
  48. package/dist/{seedRunner-Dgsiwk_e.js → seedRunner-DISBKow-.js} +16 -16
  49. package/dist/serveCommand-BITS8Hpj.js +2 -0
  50. package/dist/{serveCommand-Bje09q1v.js → serveCommand-DIJ3ma76.js} +951 -909
  51. package/dist/serveEntry.js +1 -1
  52. package/dist/{start-Clz-1BHB.js → start-B9NGB8gn.js} +627 -588
  53. package/dist/{start-B0bnJgxI.js → start-BFQQkL1i.js} +1 -1
  54. package/dist/startEntry.js +1 -1
  55. package/dist/staticCachePolicy-CIyj6DbS.js +15 -0
  56. package/dist/{test-D_kW4KMj.js → test-jipIQ5Mx.js} +1 -1
  57. package/dist/{tracesCmd-DgtgOUdi.js → tracesCmd-BWYDqMy6.js} +1 -1
  58. package/dist/{updateCommand-CRJlAOaM.js → updateCommand-C9n_Z_oG.js} +8 -2
  59. package/dist/updateCommand-DsXEAHbd.js +2 -0
  60. package/dist/webDev-C2dRz9s5.js +2 -0
  61. package/dist/{webDev-DSI9SOhs.js → webDev-C53hJdcL.js} +1265 -1288
  62. package/dist/{webhooksCommand-BvzXNHji.js → webhooksCommand-uuPu8qQX.js} +1 -1
  63. package/dist/{workflowsCmd-BGF-mRZ5.js → workflowsCmd-g-DNpaUc.js} +1 -1
  64. package/package.json +67 -19
  65. package/templates/AGENTS.core.md +20 -1
  66. package/templates/AGENTS.md +21 -2
  67. package/templates/agent-docs/_index.md +1 -1
  68. package/templates/agent-docs/_manifest.json +1 -1
  69. package/templates/agent-docs/authentication.md +49 -6
  70. package/templates/agent-docs/cli.md +216 -11
  71. package/templates/agent-docs/data.md +209 -18
  72. package/templates/agent-docs/database/scaling.md +40 -1
  73. package/templates/agent-docs/deployment.md +53 -1
  74. package/templates/agent-docs/internationalization.md +32 -0
  75. package/templates/agent-docs/local-first-mobile.md +9 -2
  76. package/templates/agent-docs/observability.md +227 -0
  77. package/templates/agent-docs/plugins/billing.md +15 -0
  78. package/templates/agent-docs/plugins/broadcast.md +2 -1
  79. package/templates/agent-docs/plugins/ratelimit.md +6 -1
  80. package/templates/agent-docs/plugins/row-history.md +11 -0
  81. package/templates/agent-docs/plugins.md +65 -8
  82. package/templates/agent-docs/reference.md +5 -3
  83. package/templates/agent-docs/routing.md +18 -0
  84. package/templates/agent-docs/schema-driven-ui.md +125 -0
  85. package/templates/agent-docs/templates/appshells.md +3 -3
  86. package/templates/agent-docs/whats-new.md +408 -106
  87. package/templates/apps/api-ai/package.json +6 -6
  88. package/templates/apps/api-auth/package.json +8 -8
  89. package/templates/apps/api-backend/package.json +7 -7
  90. package/templates/apps/api-backend-deactivation/package.json +7 -7
  91. package/templates/apps/api-backend-mail/package.json +8 -8
  92. package/templates/apps/api-backend-mariadb/package.json +9 -9
  93. package/templates/apps/api-backend-sqlite/package.json +8 -8
  94. package/templates/apps/api-backend-storage/package.json +8 -8
  95. package/templates/apps/api-cms/package.json +9 -9
  96. package/templates/apps/api-collab/package.json +8 -8
  97. package/templates/apps/api-data-advanced/package.json +8 -8
  98. package/templates/apps/api-durable/package.json +8 -8
  99. package/templates/apps/api-feature-flags/package.json +9 -9
  100. package/templates/apps/api-governance/package.json +8 -8
  101. package/templates/apps/api-kv/package.json +8 -8
  102. package/templates/apps/api-moderation/package.json +8 -8
  103. package/templates/apps/api-observability/package.json +8 -8
  104. package/templates/apps/api-ratelimit/package.json +8 -8
  105. package/templates/apps/api-rbac/package.json +8 -8
  106. package/templates/apps/api-rest/package.json +7 -7
  107. package/templates/apps/api-row-history/package.json +8 -8
  108. package/templates/apps/api-saas/package.json +11 -11
  109. package/templates/apps/api-saas-starter/package.json +10 -10
  110. package/templates/apps/api-search/package.json +8 -8
  111. package/templates/apps/api-status/package.json +8 -8
  112. package/templates/apps/api-webhooks/package.json +9 -9
  113. package/templates/apps/changelog/package.json +7 -7
  114. package/templates/apps/changelog/src/pages/[locale]/page.tsx +7 -1
  115. package/templates/apps/edge-functions/package.json +2 -2
  116. package/templates/apps/frontend-admin/package.json +7 -8
  117. package/templates/apps/frontend-admin/src/pages/(marketing)/layout.tsx +1 -2
  118. package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +1 -2
  119. package/templates/apps/frontend-app/package.json +8 -9
  120. package/templates/apps/frontend-app/src/pages/layout.tsx +1 -2
  121. package/templates/apps/frontend-auth/package.json +7 -8
  122. package/templates/apps/frontend-auth/src/components/AuthShell.tsx +1 -2
  123. package/templates/apps/frontend-blank/package.json +6 -7
  124. package/templates/apps/frontend-blank/src/pages/layout.tsx +1 -2
  125. package/templates/apps/frontend-cms/package.json +8 -9
  126. package/templates/apps/frontend-cms/src/pages/(app)/layout.tsx +1 -2
  127. package/templates/apps/frontend-collab/README.md +7 -2
  128. package/templates/apps/frontend-collab/package.json +9 -10
  129. package/templates/apps/frontend-collab/src/pages/layout.tsx +1 -2
  130. package/templates/apps/frontend-collab/src/pages/page.test.tsx +8 -4
  131. package/templates/apps/frontend-collab/src/pages/page.tsx +10 -5
  132. package/templates/apps/frontend-contact/package.json +7 -7
  133. package/templates/apps/frontend-contact/src/components/ContactForm.island.tsx +1 -1
  134. package/templates/apps/frontend-dashboard/package.json +6 -7
  135. package/templates/apps/frontend-dashboard/src/pages/(marketing)/layout.tsx +1 -2
  136. package/templates/apps/frontend-dashboard/src/pages/dashboard/layout.tsx +1 -2
  137. package/templates/apps/frontend-docs/package.json +8 -8
  138. package/templates/apps/frontend-i18n/package.json +6 -6
  139. package/templates/apps/frontend-landing/package.json +7 -7
  140. package/templates/apps/frontend-portal/package.json +7 -8
  141. package/templates/apps/frontend-portal/src/pages/(portal)/layout.tsx +1 -2
  142. package/templates/apps/frontend-saas/package.json +7 -8
  143. package/templates/apps/frontend-saas/src/pages/(marketing)/layout.tsx +1 -2
  144. package/templates/apps/frontend-saas/src/pages/dashboard/layout.tsx +1 -2
  145. package/templates/apps/frontend-spa/package.json +6 -7
  146. package/templates/apps/frontend-spa/src/pages/layout.tsx +1 -2
  147. package/templates/apps/frontend-ssr/package.json +6 -7
  148. package/templates/apps/frontend-ssr/src/pages/layout.tsx +1 -2
  149. package/templates/apps/frontend-ssr-api/package.json +7 -8
  150. package/templates/apps/frontend-ssr-api/src/pages/layout.tsx +1 -2
  151. package/templates/apps/frontend-static-blog/package.json +8 -8
  152. package/templates/apps/frontend-static-blog/src/components/ReadingProgress.island.tsx +1 -1
  153. package/templates/apps/frontend-static-blog/src/pages/[locale]/page.tsx +7 -1
  154. package/templates/apps/frontend-status/package.json +7 -8
  155. package/templates/apps/frontend-status/src/pages/layout.tsx +1 -2
  156. package/templates/apps/mobile-app/package.json +4 -4
  157. package/templates/baselines/compose/docker/api.Dockerfile +61 -5
  158. package/templates/baselines/compose/docker/web.Dockerfile +55 -10
  159. package/templates/baselines/compose-mariadb/docker/api.Dockerfile +61 -5
  160. package/templates/baselines/compose-mariadb/docker/web.Dockerfile +55 -10
  161. package/dist/apiBuild-CeUN55uk.js +0 -2
  162. package/dist/codegen-DjgxEOnD.js +0 -2
  163. package/dist/dbCommand-CSFWs9ev.js +0 -2
  164. package/dist/doctorCommand-J3qu4E0Y.js +0 -2
  165. package/dist/frameworkTableAssembly-IPD1pUnZ.js +0 -2
  166. package/dist/inspect-Bd8-9wsi.js +0 -1193
  167. package/dist/inspect-CuoDInfZ.js +0 -2
  168. package/dist/interruptedReplace-C3O3M1MM.js +0 -28
  169. package/dist/interruptedReplace-CvmiAM9K.js +0 -2
  170. package/dist/manifestBuild-C4-J1-m_.js +0 -2
  171. package/dist/serveCommand-BiPe8BJm.js +0 -2
  172. package/dist/updateCommand-CIoVDKnj.js +0 -2
  173. package/dist/webDev-DlvZO30c.js +0 -2
@@ -12,18 +12,17 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@{{projectName}}/api": "0.54.0",
16
- "@voltro/cli": "0.54.0",
17
- "@voltro/client": "0.54.0",
18
- "@voltro/env": "0.54.0",
19
- "@voltro/i18n": "0.54.0",
20
- "@voltro/ui-shadcn": "0.54.0",
21
- "@voltro/web": "0.54.0",
15
+ "@{{projectName}}/api": "0.56.0",
16
+ "@voltro/cli": "0.56.0",
17
+ "@voltro/client": "0.56.0",
18
+ "@voltro/env": "0.56.0",
19
+ "@voltro/i18n": "0.56.0",
20
+ "@voltro/web": "0.56.0",
22
21
  "react": "^19.0.0",
23
22
  "react-dom": "^19.0.0"
24
23
  },
25
24
  "devDependencies": {
26
- "@voltro/devtools": "0.54.0",
25
+ "@voltro/devtools": "0.56.0",
27
26
  "@types/react": "^19.0.0",
28
27
  "@types/react-dom": "^19.0.0",
29
28
  "jsdom": "^30.0.0",
@@ -5,8 +5,7 @@
5
5
  // this runs per request.
6
6
  import type { ReactNode } from 'react'
7
7
  import { RedirectError, useLoaderData, useLocation, type LoaderFn } from '@voltro/web'
8
- import { T, useLocale, useT } from '@voltro/i18n'
9
- import { LocaleSwitcher } from '@voltro/ui-shadcn'
8
+ import { T, useLocale, useT, LocaleSwitcher } from '@voltro/i18n'
10
9
  import { APP_NAME } from '../../config'
11
10
  import type { Identity } from '../../lib/api'
12
11
 
@@ -12,18 +12,17 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@{{projectName}}/api": "0.54.0",
16
- "@voltro/cli": "0.54.0",
17
- "@voltro/client": "0.54.0",
18
- "@voltro/env": "0.54.0",
19
- "@voltro/i18n": "0.54.0",
20
- "@voltro/ui-shadcn": "0.54.0",
21
- "@voltro/web": "0.54.0",
15
+ "@{{projectName}}/api": "0.56.0",
16
+ "@voltro/cli": "0.56.0",
17
+ "@voltro/client": "0.56.0",
18
+ "@voltro/env": "0.56.0",
19
+ "@voltro/i18n": "0.56.0",
20
+ "@voltro/web": "0.56.0",
22
21
  "react": "^19.0.0",
23
22
  "react-dom": "^19.0.0"
24
23
  },
25
24
  "devDependencies": {
26
- "@voltro/devtools": "0.54.0",
25
+ "@voltro/devtools": "0.56.0",
27
26
  "@types/react": "^19.0.0",
28
27
  "@types/react-dom": "^19.0.0",
29
28
  "jsdom": "^30.0.0",
@@ -3,8 +3,7 @@
3
3
  // `/login`. It gives the public pages a marketing header distinct from the
4
4
  // dashboard's sidebar shell.
5
5
  import type { ReactNode } from 'react'
6
- import { T, useLocale, useT } from '@voltro/i18n'
7
- import { LocaleSwitcher } from '@voltro/ui-shadcn'
6
+ import { T, useLocale, useT, LocaleSwitcher } from '@voltro/i18n'
8
7
  import { APP_NAME } from '../../config'
9
8
 
10
9
  const LOCALES = [
@@ -6,8 +6,7 @@
6
6
  // per-visitor, and the build rejects a RedirectError there.
7
7
  import type { ReactNode } from 'react'
8
8
  import { RedirectError, useLoaderData, useLocation, type LoaderFn } from '@voltro/web'
9
- import { T, useLocale, useT } from '@voltro/i18n'
10
- import { LocaleSwitcher } from '@voltro/ui-shadcn'
9
+ import { T, useLocale, useT, LocaleSwitcher } from '@voltro/i18n'
11
10
  import { APP_NAME } from '../../config'
12
11
  import type { Identity } from '../../lib/api'
13
12
 
@@ -12,17 +12,16 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/cli": "0.54.0",
16
- "@voltro/client": "0.54.0",
17
- "@voltro/env": "0.54.0",
18
- "@voltro/i18n": "0.54.0",
19
- "@voltro/ui-shadcn": "0.54.0",
20
- "@voltro/web": "0.54.0",
15
+ "@voltro/cli": "0.56.0",
16
+ "@voltro/client": "0.56.0",
17
+ "@voltro/env": "0.56.0",
18
+ "@voltro/i18n": "0.56.0",
19
+ "@voltro/web": "0.56.0",
21
20
  "react": "^19.0.0",
22
21
  "react-dom": "^19.0.0"
23
22
  },
24
23
  "devDependencies": {
25
- "@voltro/devtools": "0.54.0",
24
+ "@voltro/devtools": "0.56.0",
26
25
  "@types/react": "^19.0.0",
27
26
  "@types/react-dom": "^19.0.0",
28
27
  "jsdom": "^30.0.0",
@@ -6,8 +6,7 @@
6
6
  // chosen language.
7
7
 
8
8
  import type { ReactNode } from 'react'
9
- import { useLocale, useT } from '@voltro/i18n'
10
- import { LocaleSwitcher } from '@voltro/ui-shadcn'
9
+ import { useLocale, useT, LocaleSwitcher } from '@voltro/i18n'
11
10
  import '../globals.css'
12
11
 
13
12
  const LOCALES = [
@@ -12,17 +12,16 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/cli": "0.54.0",
16
- "@voltro/client": "0.54.0",
17
- "@voltro/env": "0.54.0",
18
- "@voltro/i18n": "0.54.0",
19
- "@voltro/ui-shadcn": "0.54.0",
20
- "@voltro/web": "0.54.0",
15
+ "@voltro/cli": "0.56.0",
16
+ "@voltro/client": "0.56.0",
17
+ "@voltro/env": "0.56.0",
18
+ "@voltro/i18n": "0.56.0",
19
+ "@voltro/web": "0.56.0",
21
20
  "react": "^19.0.0",
22
21
  "react-dom": "^19.0.0"
23
22
  },
24
23
  "devDependencies": {
25
- "@voltro/devtools": "0.54.0",
24
+ "@voltro/devtools": "0.56.0",
26
25
  "@types/react": "^19.0.0",
27
26
  "@types/react-dom": "^19.0.0",
28
27
  "jsdom": "^30.0.0",
@@ -7,8 +7,7 @@
7
7
  // + reloads so the framework re-renders in the chosen language.
8
8
 
9
9
  import type { ReactNode } from 'react'
10
- import { T, useLocale, useT } from '@voltro/i18n'
11
- import { LocaleSwitcher } from '@voltro/ui-shadcn'
10
+ import { T, useLocale, useT, LocaleSwitcher } from '@voltro/i18n'
12
11
  import '../globals.css'
13
12
 
14
13
  const LOCALES = [
@@ -12,18 +12,17 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@{{projectName}}/api": "0.54.0",
16
- "@voltro/cli": "0.54.0",
17
- "@voltro/client": "0.54.0",
18
- "@voltro/env": "0.54.0",
19
- "@voltro/i18n": "0.54.0",
20
- "@voltro/ui-shadcn": "0.54.0",
21
- "@voltro/web": "0.54.0",
15
+ "@{{projectName}}/api": "0.56.0",
16
+ "@voltro/cli": "0.56.0",
17
+ "@voltro/client": "0.56.0",
18
+ "@voltro/env": "0.56.0",
19
+ "@voltro/i18n": "0.56.0",
20
+ "@voltro/web": "0.56.0",
22
21
  "react": "^19.0.0",
23
22
  "react-dom": "^19.0.0"
24
23
  },
25
24
  "devDependencies": {
26
- "@voltro/devtools": "0.54.0",
25
+ "@voltro/devtools": "0.56.0",
27
26
  "@types/react": "^19.0.0",
28
27
  "@types/react-dom": "^19.0.0",
29
28
  "jsdom": "^30.0.0",
@@ -5,8 +5,7 @@
5
5
  // writes the `voltro:locale` cookie + reloads so the framework re-renders in the
6
6
  // chosen language.
7
7
  import type { ReactNode } from 'react'
8
- import { useLocale, useT } from '@voltro/i18n'
9
- import { LocaleSwitcher } from '@voltro/ui-shadcn'
8
+ import { useLocale, useT, LocaleSwitcher } from '@voltro/i18n'
10
9
  import '../globals.css'
11
10
 
12
11
  const LOCALES = [
@@ -12,19 +12,19 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/cli": "0.54.0",
16
- "@voltro/client": "0.54.0",
17
- "@voltro/content": "0.54.0",
18
- "@voltro/env": "0.54.0",
19
- "@voltro/i18n": "0.54.0",
20
- "@voltro/ui-shadcn": "0.54.0",
21
- "@voltro/web": "0.54.0",
15
+ "@voltro/cli": "0.56.0",
16
+ "@voltro/client": "0.56.0",
17
+ "@voltro/content": "0.56.0",
18
+ "@voltro/env": "0.56.0",
19
+ "@voltro/i18n": "0.56.0",
20
+ "@voltro/ui-shadcn": "0.56.0",
21
+ "@voltro/web": "0.56.0",
22
22
  "effect": "^3.22.0",
23
23
  "react": "^19.0.0",
24
24
  "react-dom": "^19.0.0"
25
25
  },
26
26
  "devDependencies": {
27
- "@voltro/devtools": "0.54.0",
27
+ "@voltro/devtools": "0.56.0",
28
28
  "@types/react": "^19.0.0",
29
29
  "@types/react-dom": "^19.0.0",
30
30
  "jsdom": "^30.0.0",
@@ -9,7 +9,7 @@
9
9
 
10
10
  import type { ReactNode } from 'react'
11
11
  import { useEffect, useState } from 'react'
12
- import { island } from '@voltro/web'
12
+ import { island } from '@voltro/web/islands'
13
13
 
14
14
  function ReadingProgress(): ReactNode {
15
15
  const [pct, setPct] = useState(0)
@@ -5,7 +5,13 @@
5
5
  import { SUPPORTED_LOCALES, DEFAULT_LOCALE } from '../../lib/locale'
6
6
 
7
7
  export { default } from '../page'
8
- export { renderMode, interactive, meta } from '../page'
8
+ // `loader` belongs in this list: the re-exported component calls
9
+ // `useLoaderData()`, and a mirror that forwards everything EXCEPT the loader
10
+ // renders it at a level that declares none — which is a prerender failure, not
11
+ // a missing-data warning. It was absent here while the `[slug]` mirror next
12
+ // door forwarded it correctly, and no test could see the difference: a mirror
13
+ // test asserts what IS re-exported, so an omission reads as a shorter list.
14
+ export { renderMode, interactive, loader, meta } from '../page'
9
15
 
10
16
  export const getStaticPaths = async (): Promise<Array<{ params: { locale: string } }>> =>
11
17
  SUPPORTED_LOCALES
@@ -12,18 +12,17 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@{{projectName}}/api": "0.54.0",
16
- "@voltro/cli": "0.54.0",
17
- "@voltro/client": "0.54.0",
18
- "@voltro/env": "0.54.0",
19
- "@voltro/i18n": "0.54.0",
20
- "@voltro/ui-shadcn": "0.54.0",
21
- "@voltro/web": "0.54.0",
15
+ "@{{projectName}}/api": "0.56.0",
16
+ "@voltro/cli": "0.56.0",
17
+ "@voltro/client": "0.56.0",
18
+ "@voltro/env": "0.56.0",
19
+ "@voltro/i18n": "0.56.0",
20
+ "@voltro/web": "0.56.0",
22
21
  "react": "^19.0.0",
23
22
  "react-dom": "^19.0.0"
24
23
  },
25
24
  "devDependencies": {
26
- "@voltro/devtools": "0.54.0",
25
+ "@voltro/devtools": "0.56.0",
27
26
  "@types/react": "^19.0.0",
28
27
  "@types/react-dom": "^19.0.0",
29
28
  "jsdom": "^30.0.0",
@@ -1,8 +1,7 @@
1
1
  // Root layout — the status-page header (brand + language) and the stylesheet.
2
2
  // Don't render <html>/<head>/<body> here — the framework shell owns them.
3
3
  import type { ReactNode } from 'react'
4
- import { T, useLocale, useT } from '@voltro/i18n'
5
- import { LocaleSwitcher } from '@voltro/ui-shadcn'
4
+ import { T, useLocale, useT, LocaleSwitcher } from '@voltro/i18n'
6
5
  import { APP_NAME } from '../config'
7
6
  import '../globals.css'
8
7
 
@@ -16,9 +16,9 @@
16
16
  "dependencies": {
17
17
  "@react-native-async-storage/async-storage": "1.23.1",
18
18
  "@react-native-community/netinfo": "11.4.1",
19
- "@voltro/client": "0.54.0",
20
- "@voltro/react-native": "0.54.0",
21
- "@{{projectName}}/api": "0.54.0",
19
+ "@voltro/client": "0.56.0",
20
+ "@voltro/react-native": "0.56.0",
21
+ "@{{projectName}}/api": "0.56.0",
22
22
  "expo": "^52.0.0",
23
23
  "expo-constants": "~17.0.0",
24
24
  "expo-linking": "~7.0.0",
@@ -33,7 +33,7 @@
33
33
  "@types/node": "^22.0.0",
34
34
  "@types/react": "^19.0.0",
35
35
  "@vitest/coverage-v8": "^4.1.10",
36
- "@voltro/cli": "0.54.0",
36
+ "@voltro/cli": "0.56.0",
37
37
  "typescript": "^6.0.3",
38
38
  "vitest": "^4.1.10"
39
39
  }
@@ -58,15 +58,71 @@ RUN APP_NAME=$(node -p "require('/workspace/${APP_PATH}/package.json').name") \
58
58
  && pnpm --filter="${APP_NAME}" --prod --no-optional --legacy deploy /deploy \
59
59
  ; cp -r /workspace/${APP_PATH}/.framework /deploy/.framework 2>/dev/null || echo "no .framework (serve loads from source)"
60
60
 
61
+
62
+ # SLIM the runtime tree + BAKE the V8 code cache, and prove the image boots.
63
+ #
64
+ # The entrypoint below is `node <serveBundle>`, so the container never resolves
65
+ # the @voltro/cli bin — `voltro prune-runtime` @vercel/nft-traces the reachable
66
+ # set FROM that bundle and drops the rest (the used native SQL driver is traced
67
+ # and kept automatically; an unused one is dropped). It runs in THIS stage,
68
+ # where @vercel/nft is still installed.
69
+ #
70
+ # The boot smoke is the gate: it starts the PRUNED tree with the EXACT prod
71
+ # entrypoint. `NODE_COMPILE_CACHE` makes that same run write node's compiled
72
+ # bytecode into the image — a scale-from-zero container starts with an empty
73
+ # temp dir, so baking it here is the only way it is warm when it matters.
74
+ # Measured on the reference fixture: `voltro serve` boot 287 ms → 222 ms.
75
+ #
76
+ # The prune's `|| echo` covers an older framework without the command.
77
+ #
78
+ # Then TWO steps, and their being two instructions is the point.
79
+ #
80
+ # The LOAD is a hard gate. An api legitimately refuses to boot in this stage —
81
+ # it has no database and no secrets — so demanding `serve: ready` here would
82
+ # fail every honest build. But "cannot boot without a database" is not the same
83
+ # sentence as "cannot be checked": every failure this step exists to catch — a
84
+ # module `prune-runtime` traced away, a truncated artefact, a wrong entry path,
85
+ # a missing export — happens at IMPORT, before anything connects. So the import
86
+ # must succeed and `runServe` must be callable, or the build stops here.
87
+ #
88
+ # The SMOKE is not a gate. It reports how far the boot got, and both steps write
89
+ # the V8 code cache into the image.
90
+ #
91
+ # They are separate RUN instructions on purpose. Chained into one shell command,
92
+ # the smoke's trailing `|| echo` becomes the instruction's exit status, and a
93
+ # failed load gate would ship a green build — the same shape as a pipe
94
+ # swallowing the exit code of the check in front of it.
95
+ RUN cd /deploy \
96
+ && (pnpm --filter="$(node -p "require('/deploy/package.json').name")" exec voltro prune-runtime /deploy \
97
+ || echo "prune-runtime unavailable — shipping the un-pruned tree") \
98
+ && NODE_COMPILE_CACHE=/deploy/.framework/.compile-cache node --input-type=module -e "\
99
+ const m = await import('/deploy/.framework/dist-api/serveBundle/serveEntry.js'); \
100
+ if (typeof m.runServe !== 'function') { \
101
+ console.error('load gate: the serve bundle exports no callable runServe'); \
102
+ process.exit(1); \
103
+ } \
104
+ console.log('load gate: the serve bundle imports, and runServe is callable');"
105
+
106
+ RUN cd /deploy \
107
+ ; NODE_ENV=production NODE_COMPILE_CACHE=/deploy/.framework/.compile-cache \
108
+ timeout 20 node .framework/dist-api/serveBundle/serveEntry.js > /tmp/smoke.log 2>&1 \
109
+ ; grep -q 'serve: ready' /tmp/smoke.log \
110
+ && echo "boot smoke: ready (compile cache baked)" \
111
+ || echo "boot smoke: no DB/secrets in this stage — the load gate above is the one that had to pass"
112
+
61
113
  # ---------- prod runner ----------
62
114
  FROM node:${NODE_VERSION} AS prod
63
115
  ARG PNPM_VERSION
64
- RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate
65
116
  WORKDIR /app
66
117
  COPY --from=builder /deploy /app
67
118
  EXPOSE 4000
68
119
  ENV NODE_ENV=production
69
- # `voltro serve` is the ONE production entrypoint it auto-detects the app kind
70
- # and runs the API server. `voltro start` is web-only and aborts an API app at
71
- # boot with "no web app found at this path".
72
- CMD ["pnpm", "voltro", "serve", "."]
120
+ # Reuse the cache the builder baked; node reads it before any user code runs.
121
+ ENV NODE_COMPILE_CACHE=/app/.framework/.compile-cache
122
+ # Direct-node entrypoint boots the precompiled serve bundle IN THIS PROCESS.
123
+ # No pnpm process, no @voltro/cli bin to resolve, which is what lets
124
+ # `prune-runtime` drop the CLI from the runtime tree entirely. The bundle's
125
+ # generated entry loads `.env` itself and chdir's to the app root, so it is
126
+ # self-contained. (`pnpm voltro serve .` still works and does the same thing —
127
+ # it just pays a second node startup and needs the CLI present.)
128
+ CMD ["node", ".framework/dist-api/serveBundle/serveEntry.js"]
@@ -10,7 +10,8 @@
10
10
  # Must match the app's app.config.ts `port` field.
11
11
  #
12
12
  # Targets:
13
- # prod — production image (`voltro build .` then `voltro start .`)
13
+ # prod — production image: `voltro build` `pnpm deploy` `prune-runtime`
14
+ # → boot smoke (which also bakes the V8 code cache) → `node startEntry.js`
14
15
  #
15
16
  # Build:
16
17
  # docker build \
@@ -41,19 +42,63 @@ RUN pnpm install --frozen-lockfile
41
42
  FROM deps AS builder
42
43
  ARG APP_PATH
43
44
  WORKDIR /workspace/${APP_PATH}
45
+ # Builds the client assets, the SSR bundle and the start bundle. NOT
46
+ # best-effort: an SSR-bundle failure is fatal by design, so an image whose
47
+ # `voltro start` would fall back to compiling TSX per request never ships.
44
48
  RUN pnpm voltro build .
45
49
 
46
- # ---------- prod runner ----------
47
- FROM node:${NODE_VERSION} AS prod
48
- ARG PNPM_VERSION
49
- ARG APP_PATH
50
- ARG APP_PORT
51
- RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate
50
+ # Isolate a prod-only, self-contained tree for THIS app, then carry the build
51
+ # OUTPUT into it — `pnpm deploy` copies package files, not generated output.
52
+ #
53
+ # `--no-optional` drops @voltro/cli's BUILD toolchain (tsx/esbuild/vite/tailwind
54
+ # and their native tree), declared as optionalDependencies precisely so a
55
+ # serve-only install sheds them. Nothing on the production start path is
56
+ # TypeScript: `voltro build` precompiled `app.config.ts` next to the SSR bundle,
57
+ # and the CMD below runs the start bundle directly.
52
58
  WORKDIR /workspace
59
+ RUN APP_NAME=$(node -p "require('/workspace/${APP_PATH}/package.json').name") \
60
+ && pnpm --filter="${APP_NAME}" --prod --no-optional --legacy deploy /deploy \
61
+ && cp -r /workspace/${APP_PATH}/.framework /deploy/.framework \
62
+ && rm -f /deploy/app.config.ts \
63
+ && (pnpm --filter="${APP_NAME}" exec voltro prune-runtime /deploy \
64
+ || echo "prune-runtime unavailable — shipping the un-pruned tree")
65
+ # The prune runs in THIS stage, where @vercel/nft (an @voltro/cli
66
+ # optionalDependency) is still installed, and in the same RUN as the deploy so
67
+ # `APP_NAME` is still in scope. The entrypoint is `node <startBundle>` (the CMD
68
+ # below), so the container never resolves the @voltro/cli bin — nft traces the
69
+ # reachable set FROM that bundle and drops everything else. A used native leaf
70
+ # (a SQL driver an ISR/config path touches) is traced + kept automatically.
53
71
 
54
- COPY --from=builder /workspace /workspace
72
+ # BAKE THE V8 CODE CACHE, and prove the image boots, in one step.
73
+ #
74
+ # The boot smoke is the gate: it starts the PRUNED tree with the EXACT prod
75
+ # entrypoint and REQUIRES the framework's `start: ready` line, so a pruned-away
76
+ # module or a broken artefact fails the BUILD instead of rolling out red. No
77
+ # `|| true` — no ready line MUST fail the build.
78
+ #
79
+ # `NODE_COMPILE_CACHE` makes that same run write node's compiled-bytecode cache
80
+ # into the image. A scale-from-zero container starts with an empty temp dir, so
81
+ # without baking it here the cache would never be warm when it matters. Measured
82
+ # on the reference fixture: `voltro start` boot 166 ms → 83 ms.
83
+ RUN cd /deploy \
84
+ && NODE_ENV=production PORT=8099 VOLTRO_BOOT_TIMING=1 \
85
+ NODE_COMPILE_CACHE=/deploy/.framework/.compile-cache \
86
+ timeout 25 node .framework/dist-web/startBundle/startEntry.js 2>&1 \
87
+ | grep -m1 'start: ready'
55
88
 
56
- WORKDIR /workspace/${APP_PATH}
89
+ # ---------- prod runner ----------
90
+ FROM node:${NODE_VERSION} AS prod
91
+ ARG APP_PORT
92
+ # No pnpm/corepack here: the entrypoint is a direct `node` invocation of the
93
+ # self-contained start bundle, so the runtime image needs only the node base
94
+ # plus the pruned /deploy tree.
95
+ WORKDIR /app
96
+ COPY --from=builder /deploy /app
57
97
  EXPOSE ${APP_PORT}
58
98
  ENV NODE_ENV=production
59
- CMD ["pnpm", "voltro", "start", "."]
99
+ # Reuse the cache the builder baked. Node reads it before any user code runs.
100
+ ENV NODE_COMPILE_CACHE=/app/.framework/.compile-cache
101
+ # Direct-node entrypoint — boots the precompiled start bundle IN THIS PROCESS,
102
+ # no `pnpm voltro start` wrapper (no pnpm process, no @voltro/cli bin to
103
+ # resolve). This is what lets `prune-runtime` drop the CLI from the runtime tree.
104
+ CMD ["node", ".framework/dist-web/startBundle/startEntry.js"]
@@ -58,15 +58,71 @@ RUN APP_NAME=$(node -p "require('/workspace/${APP_PATH}/package.json').name") \
58
58
  && pnpm --filter="${APP_NAME}" --prod --no-optional --legacy deploy /deploy \
59
59
  ; cp -r /workspace/${APP_PATH}/.framework /deploy/.framework 2>/dev/null || echo "no .framework (serve loads from source)"
60
60
 
61
+
62
+ # SLIM the runtime tree + BAKE the V8 code cache, and prove the image boots.
63
+ #
64
+ # The entrypoint below is `node <serveBundle>`, so the container never resolves
65
+ # the @voltro/cli bin — `voltro prune-runtime` @vercel/nft-traces the reachable
66
+ # set FROM that bundle and drops the rest (the used native SQL driver is traced
67
+ # and kept automatically; an unused one is dropped). It runs in THIS stage,
68
+ # where @vercel/nft is still installed.
69
+ #
70
+ # The boot smoke is the gate: it starts the PRUNED tree with the EXACT prod
71
+ # entrypoint. `NODE_COMPILE_CACHE` makes that same run write node's compiled
72
+ # bytecode into the image — a scale-from-zero container starts with an empty
73
+ # temp dir, so baking it here is the only way it is warm when it matters.
74
+ # Measured on the reference fixture: `voltro serve` boot 287 ms → 222 ms.
75
+ #
76
+ # The prune's `|| echo` covers an older framework without the command.
77
+ #
78
+ # Then TWO steps, and their being two instructions is the point.
79
+ #
80
+ # The LOAD is a hard gate. An api legitimately refuses to boot in this stage —
81
+ # it has no database and no secrets — so demanding `serve: ready` here would
82
+ # fail every honest build. But "cannot boot without a database" is not the same
83
+ # sentence as "cannot be checked": every failure this step exists to catch — a
84
+ # module `prune-runtime` traced away, a truncated artefact, a wrong entry path,
85
+ # a missing export — happens at IMPORT, before anything connects. So the import
86
+ # must succeed and `runServe` must be callable, or the build stops here.
87
+ #
88
+ # The SMOKE is not a gate. It reports how far the boot got, and both steps write
89
+ # the V8 code cache into the image.
90
+ #
91
+ # They are separate RUN instructions on purpose. Chained into one shell command,
92
+ # the smoke's trailing `|| echo` becomes the instruction's exit status, and a
93
+ # failed load gate would ship a green build — the same shape as a pipe
94
+ # swallowing the exit code of the check in front of it.
95
+ RUN cd /deploy \
96
+ && (pnpm --filter="$(node -p "require('/deploy/package.json').name")" exec voltro prune-runtime /deploy \
97
+ || echo "prune-runtime unavailable — shipping the un-pruned tree") \
98
+ && NODE_COMPILE_CACHE=/deploy/.framework/.compile-cache node --input-type=module -e "\
99
+ const m = await import('/deploy/.framework/dist-api/serveBundle/serveEntry.js'); \
100
+ if (typeof m.runServe !== 'function') { \
101
+ console.error('load gate: the serve bundle exports no callable runServe'); \
102
+ process.exit(1); \
103
+ } \
104
+ console.log('load gate: the serve bundle imports, and runServe is callable');"
105
+
106
+ RUN cd /deploy \
107
+ ; NODE_ENV=production NODE_COMPILE_CACHE=/deploy/.framework/.compile-cache \
108
+ timeout 20 node .framework/dist-api/serveBundle/serveEntry.js > /tmp/smoke.log 2>&1 \
109
+ ; grep -q 'serve: ready' /tmp/smoke.log \
110
+ && echo "boot smoke: ready (compile cache baked)" \
111
+ || echo "boot smoke: no DB/secrets in this stage — the load gate above is the one that had to pass"
112
+
61
113
  # ---------- prod runner ----------
62
114
  FROM node:${NODE_VERSION} AS prod
63
115
  ARG PNPM_VERSION
64
- RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate
65
116
  WORKDIR /app
66
117
  COPY --from=builder /deploy /app
67
118
  EXPOSE 4000
68
119
  ENV NODE_ENV=production
69
- # `voltro serve` is the ONE production entrypoint it auto-detects the app kind
70
- # and runs the API server. `voltro start` is web-only and aborts an API app at
71
- # boot with "no web app found at this path".
72
- CMD ["pnpm", "voltro", "serve", "."]
120
+ # Reuse the cache the builder baked; node reads it before any user code runs.
121
+ ENV NODE_COMPILE_CACHE=/app/.framework/.compile-cache
122
+ # Direct-node entrypoint boots the precompiled serve bundle IN THIS PROCESS.
123
+ # No pnpm process, no @voltro/cli bin to resolve, which is what lets
124
+ # `prune-runtime` drop the CLI from the runtime tree entirely. The bundle's
125
+ # generated entry loads `.env` itself and chdir's to the app root, so it is
126
+ # self-contained. (`pnpm voltro serve .` still works and does the same thing —
127
+ # it just pays a second node startup and needs the CLI present.)
128
+ CMD ["node", ".framework/dist-api/serveBundle/serveEntry.js"]
@@ -10,7 +10,8 @@
10
10
  # Must match the app's app.config.ts `port` field.
11
11
  #
12
12
  # Targets:
13
- # prod — production image (`voltro build .` then `voltro start .`)
13
+ # prod — production image: `voltro build` `pnpm deploy` `prune-runtime`
14
+ # → boot smoke (which also bakes the V8 code cache) → `node startEntry.js`
14
15
  #
15
16
  # Build:
16
17
  # docker build \
@@ -41,19 +42,63 @@ RUN pnpm install --frozen-lockfile
41
42
  FROM deps AS builder
42
43
  ARG APP_PATH
43
44
  WORKDIR /workspace/${APP_PATH}
45
+ # Builds the client assets, the SSR bundle and the start bundle. NOT
46
+ # best-effort: an SSR-bundle failure is fatal by design, so an image whose
47
+ # `voltro start` would fall back to compiling TSX per request never ships.
44
48
  RUN pnpm voltro build .
45
49
 
46
- # ---------- prod runner ----------
47
- FROM node:${NODE_VERSION} AS prod
48
- ARG PNPM_VERSION
49
- ARG APP_PATH
50
- ARG APP_PORT
51
- RUN corepack enable && corepack prepare pnpm@${PNPM_VERSION} --activate
50
+ # Isolate a prod-only, self-contained tree for THIS app, then carry the build
51
+ # OUTPUT into it — `pnpm deploy` copies package files, not generated output.
52
+ #
53
+ # `--no-optional` drops @voltro/cli's BUILD toolchain (tsx/esbuild/vite/tailwind
54
+ # and their native tree), declared as optionalDependencies precisely so a
55
+ # serve-only install sheds them. Nothing on the production start path is
56
+ # TypeScript: `voltro build` precompiled `app.config.ts` next to the SSR bundle,
57
+ # and the CMD below runs the start bundle directly.
52
58
  WORKDIR /workspace
59
+ RUN APP_NAME=$(node -p "require('/workspace/${APP_PATH}/package.json').name") \
60
+ && pnpm --filter="${APP_NAME}" --prod --no-optional --legacy deploy /deploy \
61
+ && cp -r /workspace/${APP_PATH}/.framework /deploy/.framework \
62
+ && rm -f /deploy/app.config.ts \
63
+ && (pnpm --filter="${APP_NAME}" exec voltro prune-runtime /deploy \
64
+ || echo "prune-runtime unavailable — shipping the un-pruned tree")
65
+ # The prune runs in THIS stage, where @vercel/nft (an @voltro/cli
66
+ # optionalDependency) is still installed, and in the same RUN as the deploy so
67
+ # `APP_NAME` is still in scope. The entrypoint is `node <startBundle>` (the CMD
68
+ # below), so the container never resolves the @voltro/cli bin — nft traces the
69
+ # reachable set FROM that bundle and drops everything else. A used native leaf
70
+ # (a SQL driver an ISR/config path touches) is traced + kept automatically.
53
71
 
54
- COPY --from=builder /workspace /workspace
72
+ # BAKE THE V8 CODE CACHE, and prove the image boots, in one step.
73
+ #
74
+ # The boot smoke is the gate: it starts the PRUNED tree with the EXACT prod
75
+ # entrypoint and REQUIRES the framework's `start: ready` line, so a pruned-away
76
+ # module or a broken artefact fails the BUILD instead of rolling out red. No
77
+ # `|| true` — no ready line MUST fail the build.
78
+ #
79
+ # `NODE_COMPILE_CACHE` makes that same run write node's compiled-bytecode cache
80
+ # into the image. A scale-from-zero container starts with an empty temp dir, so
81
+ # without baking it here the cache would never be warm when it matters. Measured
82
+ # on the reference fixture: `voltro start` boot 166 ms → 83 ms.
83
+ RUN cd /deploy \
84
+ && NODE_ENV=production PORT=8099 VOLTRO_BOOT_TIMING=1 \
85
+ NODE_COMPILE_CACHE=/deploy/.framework/.compile-cache \
86
+ timeout 25 node .framework/dist-web/startBundle/startEntry.js 2>&1 \
87
+ | grep -m1 'start: ready'
55
88
 
56
- WORKDIR /workspace/${APP_PATH}
89
+ # ---------- prod runner ----------
90
+ FROM node:${NODE_VERSION} AS prod
91
+ ARG APP_PORT
92
+ # No pnpm/corepack here: the entrypoint is a direct `node` invocation of the
93
+ # self-contained start bundle, so the runtime image needs only the node base
94
+ # plus the pruned /deploy tree.
95
+ WORKDIR /app
96
+ COPY --from=builder /deploy /app
57
97
  EXPOSE ${APP_PORT}
58
98
  ENV NODE_ENV=production
59
- CMD ["pnpm", "voltro", "start", "."]
99
+ # Reuse the cache the builder baked. Node reads it before any user code runs.
100
+ ENV NODE_COMPILE_CACHE=/app/.framework/.compile-cache
101
+ # Direct-node entrypoint — boots the precompiled start bundle IN THIS PROCESS,
102
+ # no `pnpm voltro start` wrapper (no pnpm process, no @voltro/cli bin to
103
+ # resolve). This is what lets `prune-runtime` drop the CLI from the runtime tree.
104
+ CMD ["node", ".framework/dist-web/startBundle/startEntry.js"]