@voltro/cli 0.51.0 → 0.53.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 (233) hide show
  1. package/CHANGELOG.md +356 -0
  2. package/THIRD-PARTY-NOTICES.md +8311 -3318
  3. package/dist/{agentsMd-D6yD7IQv.js → agentsMd-0l980yhL.js} +4 -1
  4. package/dist/agentsMd-SDDSkyl4.js +2 -0
  5. package/dist/{apiBuild-CPDHXF72.js → apiBuild-CaPfoWku.js} +11 -5
  6. package/dist/apiBuild-DHtLXYx9.js +2 -0
  7. package/dist/bin.js +1 -1
  8. package/dist/build-D-OnvNMf.js +843 -0
  9. package/dist/{checkCommand-DNuPiWMc.js → checkCommand-C5elt0tW.js} +92 -46
  10. package/dist/checkCommand-D2ZduVlh.js +2 -0
  11. package/dist/{cloudCmd-F4YJeqM3.js → cloudCmd-QUXh-b5w.js} +1 -1
  12. package/dist/codegen-BWpt3VgF.js +2 -0
  13. package/dist/{codegen-CrMXs4hb.js → codegen-FEk8AZHb.js} +2 -2
  14. package/dist/{codegenCommand-BNBHcNNj.js → codegenCommand-BOiWQ5hz.js} +12 -12
  15. package/dist/{codemodRunner-BDVixlSw.js → codemodRunner-BjtB2lq6.js} +691 -545
  16. package/dist/{commands-B1OiS9bX.js → commands-DyxAmhP0.js} +36 -36
  17. package/dist/{dashboardCommand-C-vvPY1B.js → dashboardCommand-BdKTyT13.js} +3 -3
  18. package/dist/{dataCommand-C1GxXW5q.js → dataCommand-Bab9X7s8.js} +27 -27
  19. package/dist/{dbCommand-If4Y1xQ-.js → dbCommand-06O2finM.js} +277 -236
  20. package/dist/dbCommand-B1EXBC6f.js +2 -0
  21. package/dist/{dev-kdAg9Q7l.js → dev-C6LGF4iY.js} +2998 -2379
  22. package/dist/dev-GjJWAYo2.js +3 -0
  23. package/dist/doctorCommand-B0hX0tdz.js +2 -0
  24. package/dist/{doctorCommand-nKmeW78u.js → doctorCommand-etMkflRc.js} +332 -220
  25. package/dist/{dormancyCommand-CY3wa_SW.js → dormancyCommand-UwZ1AZzB.js} +1 -1
  26. package/dist/{embeddingsCommand-BDLIgje_.js → embeddingsCommand-C70zWHwo.js} +1 -1
  27. package/dist/{envCommand-C6V_xVlT.js → envCommand-dSyKvRkM.js} +15 -15
  28. package/dist/{evolveCommand-D3c4DSfN.js → evolveCommand-CG0_ebO5.js} +2 -2
  29. package/dist/fileConventions-DASGEmj-.js +35 -0
  30. package/dist/{fileTaxonomy-CJfgOllU.js → fileTaxonomy-B7uxipWS.js} +55 -55
  31. package/dist/fontPipeline-LxIHa1vo.js +2 -0
  32. package/dist/fontPipeline-Tsh8kZfA.js +152 -0
  33. package/dist/frameworkTableAssembly-C_7Z-rMs.js +2 -0
  34. package/dist/{frameworkTableAssembly-BwJVEKLr.js → frameworkTableAssembly-DKx3ba3S.js} +5 -5
  35. package/dist/imagePipeline-B_GVJgm6.js +2 -0
  36. package/dist/imagePipeline-CBZmjT4i.js +127 -0
  37. package/dist/index.js +1 -1
  38. package/dist/{infoCommand-BnRFEF1o.js → infoCommand-_53iOc_j.js} +1 -1
  39. package/dist/{inspect-CtL_xTbu.js → inspect-Bd8-9wsi.js} +1 -1
  40. package/dist/inspect-CuoDInfZ.js +2 -0
  41. package/dist/{inspectGateHint-BjnFubmH.js → inspectGateHint-4LxkNtrz.js} +1 -1
  42. package/dist/inspectMetrics-CGF94puw.js +143 -0
  43. package/dist/manifestBuild-C4-J1-m_.js +2 -0
  44. package/dist/{manifestBuild-CuU1VrSm.js → manifestBuild-Cqgsx2bM.js} +1 -1
  45. package/dist/{metaCommands-CfRLra0s.js → metaCommands-Cn2oboG4.js} +9 -3
  46. package/dist/{migrate-DehuBakM.js → migrate-Cko9rswM.js} +2 -2
  47. package/dist/{pageConvention-cEiRxdab.js → pageConvention-C938S8oC.js} +1 -1
  48. package/dist/{privacyCommand-XejDMvmu.js → privacyCommand-DWTQMC6R.js} +2 -2
  49. package/dist/{probeCommand-C9gazU0H.js → probeCommand-DkGGLknv.js} +83 -24
  50. package/dist/{projectScaffold-mIX_DpSe.js → projectScaffold-B4dmTlwT.js} +1 -1
  51. package/dist/{projectScaffold-BIl97_E6.js → projectScaffold-EzlErR4E.js} +1 -1
  52. package/dist/{renderModeScan-D7J1B7Kw.js → renderModeScan-CUbOeOAg.js} +28 -11
  53. package/dist/{renderProfile-1OWWAAtx.js → renderProfile-CskIgAfn.js} +2 -2
  54. package/dist/{runtimeTrace-ZsBU7Tkx.js → runtimeTrace-c0APJz7E.js} +1 -1
  55. package/dist/{sdkgen-O4XqWOjM.js → sdkgen-BiQCgIEr.js} +1 -1
  56. package/dist/serveCommand-CueKQgzl.js +2443 -0
  57. package/dist/serveCommand-DsnrVN3U.js +2 -0
  58. package/dist/serveEntry.js +1 -1
  59. package/dist/start-BJzZLbt8.js +3 -0
  60. package/dist/start-ekPan8BT.js +1510 -0
  61. package/dist/startEntry.js +1 -1
  62. package/dist/{staticCommand-Dr2M6tpU.js → staticCommand-xlSL-IWk.js} +1 -1
  63. package/dist/{test-rXFq4S76.js → test-BWPQcRoB.js} +1 -1
  64. package/dist/updateCommand-Bqql_rsQ.js +2 -0
  65. package/dist/{updateCommand-Bs322Q78.js → updateCommand-C_8I8Rzo.js} +139 -115
  66. package/dist/webDev-C7jWJ5dX.js +2 -0
  67. package/dist/{webDev-B-ubQEMX.js → webDev-oczpugbx.js} +1767 -913
  68. package/dist/{webhooksCommand-FLYY9IXh.js → webhooksCommand-4SVPDjKg.js} +1 -1
  69. package/package.json +72 -18
  70. package/templates/AGENTS.core.md +11 -0
  71. package/templates/AGENTS.md +19 -6
  72. package/templates/agent-docs/_index.md +8 -6
  73. package/templates/agent-docs/_manifest.json +31 -15
  74. package/templates/agent-docs/ai.md +2 -2
  75. package/templates/agent-docs/authentication.md +1 -1
  76. package/templates/agent-docs/cli.md +97 -15
  77. package/templates/agent-docs/configuration.md +17 -0
  78. package/templates/agent-docs/data.md +680 -33
  79. package/templates/agent-docs/database/advancedqueries.md +7 -7
  80. package/templates/agent-docs/database/columntypes.md +2 -2
  81. package/templates/agent-docs/database/querying.md +1 -1
  82. package/templates/agent-docs/database/schema.md +2 -2
  83. package/templates/agent-docs/database/seedsdialects.md +2 -2
  84. package/templates/agent-docs/database/transactions.md +3 -3
  85. package/templates/agent-docs/deployment.md +30 -3
  86. package/templates/agent-docs/internationalization.md +2 -2
  87. package/templates/agent-docs/introduction.md +52 -0
  88. package/templates/agent-docs/local-first-mobile.md +132 -7
  89. package/templates/agent-docs/observability.md +2 -0
  90. package/templates/agent-docs/plugins/ai-flows.md +1 -1
  91. package/templates/agent-docs/plugins/audit.md +5 -5
  92. package/templates/agent-docs/plugins/auth.md +1 -1
  93. package/templates/agent-docs/plugins/cdc-out.md +2 -2
  94. package/templates/agent-docs/plugins/comments.md +142 -0
  95. package/templates/agent-docs/plugins/notifications.md +47 -4
  96. package/templates/agent-docs/plugins/presence.md +16 -3
  97. package/templates/agent-docs/plugins/prometheus.md +1 -1
  98. package/templates/agent-docs/plugins/queue.md +129 -0
  99. package/templates/agent-docs/plugins/{versioning.md → row-history.md} +22 -22
  100. package/templates/agent-docs/plugins/storage.md +2 -2
  101. package/templates/agent-docs/plugins.md +38 -12
  102. package/templates/agent-docs/reference.md +54 -5
  103. package/templates/agent-docs/routing.md +868 -50
  104. package/templates/agent-docs/schema-driven-ui.md +292 -5
  105. package/templates/agent-docs/security.md +125 -8
  106. package/templates/agent-docs/templates/apibackends.md +14 -14
  107. package/templates/agent-docs/templates/overview.md +1 -1
  108. package/templates/agent-docs/whats-new.md +171 -54
  109. package/templates/apps/api-ai/package.json +6 -7
  110. package/templates/apps/api-ai/tests/summarize.test.ts +1 -1
  111. package/templates/apps/api-auth/package.json +8 -8
  112. package/templates/apps/api-backend/package.json +7 -7
  113. package/templates/apps/api-backend-deactivation/package.json +7 -7
  114. package/templates/apps/api-backend-mail/package.json +8 -8
  115. package/templates/apps/api-backend-mariadb/package.json +9 -9
  116. package/templates/apps/api-backend-sqlite/package.json +8 -8
  117. package/templates/apps/api-backend-storage/package.json +8 -8
  118. package/templates/apps/api-cms/package.json +9 -10
  119. package/templates/apps/api-collab/package.json +8 -8
  120. package/templates/apps/api-data-advanced/package.json +8 -8
  121. package/templates/apps/api-durable/package.json +8 -8
  122. package/templates/apps/api-feature-flags/package.json +9 -9
  123. package/templates/apps/api-governance/package.json +8 -8
  124. package/templates/apps/api-kv/package.json +8 -8
  125. package/templates/apps/api-moderation/package.json +8 -8
  126. package/templates/apps/api-observability/package.json +8 -8
  127. package/templates/apps/api-ratelimit/package.json +8 -8
  128. package/templates/apps/api-rbac/package.json +8 -8
  129. package/templates/apps/api-rest/package.json +7 -7
  130. package/templates/apps/{api-versioning → api-row-history}/README.md +3 -3
  131. package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.server.ts +1 -1
  132. package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.server.ts +1 -1
  133. package/templates/apps/{api-versioning → api-row-history}/app.config.ts +3 -3
  134. package/templates/apps/{api-versioning → api-row-history}/database/schema.ts +1 -1
  135. package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.server.ts +1 -1
  136. package/templates/apps/{api-versioning → api-row-history}/mutations/documents.create.mutation.ts +1 -1
  137. package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.ts +1 -1
  138. package/templates/apps/{api-versioning → api-row-history}/package.json +8 -8
  139. package/templates/apps/api-row-history/template.json +6 -0
  140. package/templates/apps/{api-versioning → api-row-history}/tests/documents.create.test.ts +1 -1
  141. package/templates/apps/api-saas/app.config.ts +1 -0
  142. package/templates/apps/api-saas/package.json +10 -11
  143. package/templates/apps/api-saas-starter/package.json +10 -10
  144. package/templates/apps/api-search/package.json +8 -8
  145. package/templates/apps/api-status/package.json +8 -8
  146. package/templates/apps/api-webhooks/package.json +9 -9
  147. package/templates/apps/changelog/app.config.ts +26 -2
  148. package/templates/apps/changelog/content/releases/{0.1.0.mdx → v0-1-0.mdx} +0 -1
  149. package/templates/apps/changelog/content/releases/{0.2.0.mdx → v0-2-0.mdx} +0 -1
  150. package/templates/apps/changelog/package.json +8 -8
  151. package/templates/apps/changelog/src/collections/releases.collection.ts +48 -0
  152. package/templates/apps/changelog/src/globals.d.ts +1 -1
  153. package/templates/apps/changelog/src/locales/de.ts +1 -1
  154. package/templates/apps/changelog/src/locales/en.ts +1 -1
  155. package/templates/apps/changelog/src/pages/[locale]/[slug]/page.tsx +7 -4
  156. package/templates/apps/changelog/src/pages/[locale]/mirrors.test.tsx +13 -41
  157. package/templates/apps/changelog/src/pages/[slug]/page.test.tsx +26 -51
  158. package/templates/apps/changelog/src/pages/[slug]/page.tsx +21 -19
  159. package/templates/apps/changelog/src/pages/page.test.tsx +14 -37
  160. package/templates/apps/changelog/src/pages/page.tsx +18 -12
  161. package/templates/apps/edge-functions/package.json +2 -2
  162. package/templates/apps/frontend-admin/package.json +8 -8
  163. package/templates/apps/frontend-app/package.json +9 -9
  164. package/templates/apps/frontend-auth/package.json +8 -8
  165. package/templates/apps/frontend-blank/package.json +7 -7
  166. package/templates/apps/frontend-cms/package.json +9 -9
  167. package/templates/apps/frontend-collab/package.json +10 -10
  168. package/templates/apps/frontend-collab/src/pages/page.test.tsx +10 -9
  169. package/templates/apps/frontend-collab/src/pages/page.tsx +42 -65
  170. package/templates/apps/frontend-contact/package.json +7 -7
  171. package/templates/apps/frontend-dashboard/package.json +7 -7
  172. package/templates/apps/frontend-docs/content/docs/de/guides/first-page.md +4 -0
  173. package/templates/apps/frontend-docs/content/docs/de/intro/getting-started.md +4 -0
  174. package/templates/apps/frontend-docs/content/docs/en/guides/first-page.md +4 -0
  175. package/templates/apps/frontend-docs/content/docs/en/intro/getting-started.md +4 -0
  176. package/templates/apps/frontend-docs/package.json +8 -7
  177. package/templates/apps/frontend-docs/src/collections/docs.collection.ts +21 -0
  178. package/templates/apps/frontend-docs/src/locales/de.ts +0 -5
  179. package/templates/apps/frontend-docs/src/locales/en.ts +0 -5
  180. package/templates/apps/frontend-docs/src/pages/[locale]/docs/[...slug]/page.tsx +17 -9
  181. package/templates/apps/frontend-docs/src/pages/[locale]/mirrors.test.tsx +10 -5
  182. package/templates/apps/frontend-docs/src/pages/[locale]/page.tsx +8 -0
  183. package/templates/apps/frontend-docs/src/pages/docs/[...slug]/page.test.tsx +53 -15
  184. package/templates/apps/frontend-docs/src/pages/docs/[...slug]/page.tsx +27 -33
  185. package/templates/apps/frontend-docs/src/pages/page.test.tsx +17 -3
  186. package/templates/apps/frontend-docs/src/pages/page.tsx +16 -12
  187. package/templates/apps/frontend-i18n/package.json +6 -6
  188. package/templates/apps/frontend-landing/package.json +6 -7
  189. package/templates/apps/frontend-portal/package.json +8 -8
  190. package/templates/apps/frontend-saas/package.json +8 -8
  191. package/templates/apps/frontend-spa/package.json +7 -7
  192. package/templates/apps/frontend-ssr/package.json +7 -7
  193. package/templates/apps/frontend-ssr-api/package.json +8 -8
  194. package/templates/apps/frontend-static-blog/content/posts/cms-to-ssg.md +12 -0
  195. package/templates/apps/frontend-static-blog/content/posts/hello-static.md +12 -0
  196. package/templates/apps/frontend-static-blog/content/posts/islands-not-hydration.md +14 -0
  197. package/templates/apps/frontend-static-blog/package.json +8 -6
  198. package/templates/apps/frontend-static-blog/src/collections/posts.collection.ts +27 -0
  199. package/templates/apps/frontend-static-blog/src/pages/[locale]/blog/[slug]/page.tsx +7 -4
  200. package/templates/apps/frontend-static-blog/src/pages/[locale]/mirrors.test.tsx +7 -1
  201. package/templates/apps/frontend-static-blog/src/pages/blog/[slug]/page.test.tsx +19 -9
  202. package/templates/apps/frontend-static-blog/src/pages/blog/[slug]/page.tsx +19 -21
  203. package/templates/apps/frontend-static-blog/src/pages/page.test.tsx +18 -5
  204. package/templates/apps/frontend-static-blog/src/pages/page.tsx +21 -14
  205. package/templates/apps/frontend-status/package.json +8 -8
  206. package/templates/apps/mobile-app/package.json +4 -4
  207. package/dist/agentsMd-Bu_XQgVf.js +0 -2
  208. package/dist/apiBuild-GDKuGOMV.js +0 -2
  209. package/dist/build-DETLZAFt.js +0 -752
  210. package/dist/checkCommand-CWcnDArJ.js +0 -2
  211. package/dist/codegen-DiMn2KkZ.js +0 -2
  212. package/dist/dbCommand-C27HIsGE.js +0 -2
  213. package/dist/dev-CK522MV5.js +0 -3
  214. package/dist/doctorCommand-BK4l18eG.js +0 -2
  215. package/dist/fileConventions-Cof68_BL.js +0 -33
  216. package/dist/frameworkTableAssembly-CVDB2hCq.js +0 -2
  217. package/dist/inspect-CuGDYES0.js +0 -2
  218. package/dist/inspectMetrics-CfdKLh6t.js +0 -72
  219. package/dist/manifestBuild-CPjhvM62.js +0 -2
  220. package/dist/serveCommand-BRnPCxVd.js +0 -2
  221. package/dist/serveCommand-DdiYNBBu.js +0 -2362
  222. package/dist/start-BLNmWkLa.js +0 -1154
  223. package/dist/start-Dzicuyw8.js +0 -3
  224. package/dist/updateCommand-eXB35SEv.js +0 -2
  225. package/dist/webDev-DposiF3j.js +0 -2
  226. package/templates/apps/api-versioning/template.json +0 -6
  227. package/templates/apps/changelog/scripts/generate-rss.mjs +0 -38
  228. package/templates/apps/changelog/src/lib/releases.ts +0 -21
  229. package/templates/apps/frontend-static-blog/src/content/posts.ts +0 -64
  230. /package/templates/apps/{api-versioning → api-row-history}/actions/documents.asOf.action.ts +0 -0
  231. /package/templates/apps/{api-versioning → api-row-history}/actions/documents.history.action.ts +0 -0
  232. /package/templates/apps/{api-versioning → api-row-history}/mutations/documents.update.mutation.server.ts +0 -0
  233. /package/templates/apps/{api-versioning → api-row-history}/tsconfig.json +0 -0
@@ -0,0 +1,21 @@
1
+ // The docs content collection — bilingual markdown under
2
+ // content/docs/{en,de}/**/*.md. The SLUG comes from the path below the
3
+ // locale segment (`en/intro/getting-started.md` → `intro/getting-started`),
4
+ // identical across locales, so the /de mirror serves the same slugs from the
5
+ // German tree. A missing translation falls back to the default locale's
6
+ // entry (marked `fallback: true`) instead of 404ing — incomplete
7
+ // translations are the normal case.
8
+
9
+ import { Schema } from 'effect'
10
+ import { defineCollection } from '@voltro/content'
11
+
12
+ export const docs = defineCollection({
13
+ name: 'docs',
14
+ directory: 'content/docs',
15
+ schema: Schema.Struct({
16
+ title: Schema.String,
17
+ }),
18
+ i18n: { locales: ['en', 'de'], defaultLocale: 'en', missing: 'fallback' },
19
+ })
20
+
21
+ export type Doc = Schema.Schema.Type<typeof docs.schema>
@@ -10,11 +10,6 @@ export default defineLocale<typeof en>()({
10
10
 
11
11
  'home.title': '{project} Dokumentation',
12
12
 
13
- 'docs.getting-started.title': 'Erste Schritte',
14
- 'docs.getting-started.body': 'Willkommen in der {{capProjectName}}-Dokumentation.',
15
- 'docs.first-page.title': 'Deine erste Seite',
16
- 'docs.first-page.body': 'Lege eine `*.tsx`-Datei in src/pages/ ab.',
17
-
18
13
  'docs.notFound.title': 'Nicht gefunden',
19
14
  'docs.notFound.body': 'Kein Dokument unter <code>{slug}</code>.',
20
15
 
@@ -14,11 +14,6 @@ export default defineCatalog({
14
14
  // `{project}` is the scaffold-substituted app name, passed as a <T> value.
15
15
  'home.title': '{project} docs',
16
16
 
17
- 'docs.getting-started.title': 'Getting started',
18
- 'docs.getting-started.body': 'Welcome to the {{capProjectName}} docs.',
19
- 'docs.first-page.title': 'Your first page',
20
- 'docs.first-page.body': 'Drop a `*.tsx` file into src/pages/.',
21
-
22
17
  'docs.notFound.title': 'Not found',
23
18
  'docs.notFound.body': 'No doc at <code>{slug}</code>.',
24
19
 
@@ -1,18 +1,26 @@
1
1
  // Locale mirror of the catch-all docs route → renders `/de/docs/<slug>`.
2
- // Re-exports the bare `[...slug]` page (the component + its per-locale meta
3
- // read locale + slug from the URL / params). This is a DYNAMIC route mirrored
4
- // under a DYNAMIC locale segment, so `getStaticPaths` must emit the
5
- // CROSS-PRODUCT of every non-default locale × every doc slug — otherwise the
6
- // German docs URLs are never enumerated for pre-render.
2
+ // Re-exports the bare `[...slug]` page's component + meta; the LOADER here
3
+ // reads the URL locale's tree of the `docs` collection (with fallback to
4
+ // the default locale for untranslated entries). This is a DYNAMIC route
5
+ // mirrored under a DYNAMIC locale segment, so `getStaticPaths` emits the
6
+ // CROSS-PRODUCT of every non-default locale × every doc slug.
7
+ import { getCollection, getEntry, type ContentEntry } from '@voltro/content'
8
+ import { docs, type Doc } from '../../../../collections/docs.collection'
7
9
  import { SUPPORTED_LOCALES, DEFAULT_LOCALE } from '../../../../lib/locale'
8
- import { DOCS } from '../../../docs/[...slug]/page'
9
10
 
10
11
  export { default } from '../../../docs/[...slug]/page'
11
12
  export { renderMode, interactive, meta } from '../../../docs/[...slug]/page'
12
13
 
14
+ export const loader = async ({ params }: {
15
+ readonly params: { readonly slug: string; readonly locale: string }
16
+ }): Promise<ContentEntry<Doc> | null> =>
17
+ await getEntry<Doc>(docs.name, params.slug, { locale: params.locale })
18
+
13
19
  export const getStaticPaths = async (): Promise<
14
20
  Array<{ params: { locale: string; slug: string } }>
15
- > =>
16
- SUPPORTED_LOCALES
21
+ > => {
22
+ const entries = await getCollection(docs.name)
23
+ return SUPPORTED_LOCALES
17
24
  .filter((l) => l !== DEFAULT_LOCALE)
18
- .flatMap((locale) => DOCS.map((d) => ({ params: { locale, slug: d.slug } })))
25
+ .flatMap((locale) => entries.map((d) => ({ params: { locale, slug: d.slug } })))
26
+ }
@@ -8,6 +8,11 @@
8
8
  // non-default locale × every doc slug.
9
9
 
10
10
  import { describe, expect, test, vi } from 'vitest'
11
+ import { dirname, join } from 'node:path'
12
+ import { fileURLToPath } from 'node:url'
13
+ import { setContentRoot } from '@voltro/content'
14
+
15
+ setContentRoot(join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..'))
11
16
 
12
17
  // The mirrors transitively import the base pages, which import @voltro/i18n
13
18
  // and @voltro/web. Stub both so importing the modules doesn't require the
@@ -50,17 +55,17 @@ describe('home mirror — getStaticPaths', () => {
50
55
  describe('docs [...slug] mirror — getStaticPaths (locale × slug cross-product)', () => {
51
56
  test('emits every non-default locale paired with every doc slug', async () => {
52
57
  const paths = await slugMirror.getStaticPaths()
58
+ // getCollection sorts by slug.
53
59
  expect(paths).toEqual([
54
- { params: { locale: 'de', slug: 'intro/getting-started' } },
55
60
  { params: { locale: 'de', slug: 'guides/first-page' } },
61
+ { params: { locale: 'de', slug: 'intro/getting-started' } },
56
62
  ])
57
63
  })
58
64
 
59
- test('re-exports the base page component + its per-locale meta function', () => {
65
+ test('re-exports the base page component + its per-locale meta function', async () => {
60
66
  expect(typeof slugMirror.default).toBe('function')
61
- expect(
62
- slugMirror.meta({ locale: 'de', params: { slug: 'guides/first-page' } }).title,
63
- ).toBe('Deine erste Seite')
67
+ const deDoc = await slugMirror.loader({ params: { slug: 'guides/first-page', locale: 'de' } })
68
+ expect(slugMirror.meta({ locale: 'de', loaderData: deDoc }).title).toBe('Deine erste Seite')
64
69
  expect(slugMirror.renderMode).toBe('static')
65
70
  expect(slugMirror.interactive).toBe('none')
66
71
  })
@@ -2,11 +2,19 @@
2
2
  // (the component reads its locale from the URL via the layout's provider);
3
3
  // `getStaticPaths` tells `voltro build` which locale-prefixed paths to emit.
4
4
  // The default locale is NOT listed here — it lives at the bare `/`.
5
+ import { getCollection, type ContentEntry } from '@voltro/content'
6
+ import { docs, type Doc } from '../../collections/docs.collection'
5
7
  import { SUPPORTED_LOCALES, DEFAULT_LOCALE } from '../../lib/locale'
6
8
 
7
9
  export { default } from '../page'
8
10
  export { renderMode, interactive, meta } from '../page'
9
11
 
12
+ // The URL locale selects the collection tree (German titles on /de).
13
+ export const loader = async ({ params }: {
14
+ readonly params: { readonly locale: string }
15
+ }): Promise<ReadonlyArray<ContentEntry<Doc>>> =>
16
+ await getCollection<Doc>(docs.name, { locale: params.locale })
17
+
10
18
  export const getStaticPaths = async (): Promise<Array<{ params: { locale: string } }>> =>
11
19
  SUPPORTED_LOCALES
12
20
  .filter((l) => l !== DEFAULT_LOCALE)
@@ -1,19 +1,30 @@
1
1
  // @vitest-environment jsdom
2
2
  //
3
3
  // `/docs/[...slug]` catch-all — its `getStaticPaths` export (enumerates the
4
- // pre-rendered doc slugs) and the localized page render for a matched vs.
5
- // unknown slug. The page reads the captured slug via `useParams` (mocked to
6
- // drive the slug) and its per-locale prose via @voltro/i18n (mocked to resolve
7
- // against the real catalogs for a controllable "current locale").
4
+ // pre-rendered doc slugs from the `docs` CONTENT COLLECTION), the loader
5
+ // (locale-tree selection + fallback), the per-doc meta, and the page render
6
+ // for a matched vs. unknown slug. The collection reads this app's REAL
7
+ // content/docs/{en,de}/**/*.md files no fixture copies to drift.
8
8
 
9
9
  import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
10
10
  import { act, createElement, type ReactNode } from 'react'
11
11
  import { createRoot, type Root } from 'react-dom/client'
12
+ import { dirname, join } from 'node:path'
13
+ import { fileURLToPath } from 'node:url'
14
+ import { setContentRoot, type ContentEntry } from '@voltro/content'
15
+ import type { Doc } from '../../../collections/docs.collection'
12
16
  import en from '../../../locales/en'
13
17
  import de from '../../../locales/de'
14
18
 
19
+ // The server half of the collection reads files under the app root.
20
+ setContentRoot(join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', '..'))
21
+
15
22
  const params = vi.fn<() => Record<string, string>>()
16
- vi.mock('@voltro/web', () => ({ useParams: () => params() }))
23
+ const loaderData = vi.fn<() => ContentEntry<Doc> | null>()
24
+ vi.mock('@voltro/web', () => ({
25
+ useParams: () => params(),
26
+ useLoaderData: () => loaderData(),
27
+ }))
17
28
 
18
29
  const catalogs: Record<string, Record<string, string>> = { en, de }
19
30
  let currentLocale = 'en'
@@ -37,7 +48,7 @@ vi.mock('@voltro/i18n', () => ({
37
48
  fill(catalogs[currentLocale]?.[id] ?? id, values),
38
49
  }))
39
50
 
40
- const { default: DocPage, getStaticPaths, meta, renderMode, interactive } = await import('./page')
51
+ const { default: DocPage, getStaticPaths, loader, meta, renderMode, interactive } = await import('./page')
41
52
 
42
53
  ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
43
54
 
@@ -53,7 +64,10 @@ const render = (node: ReactNode): void => {
53
64
  })
54
65
  }
55
66
 
56
- beforeEach(() => params.mockReset())
67
+ beforeEach(() => {
68
+ params.mockReset()
69
+ loaderData.mockReset()
70
+ })
57
71
 
58
72
  afterEach(() => {
59
73
  currentLocale = 'en'
@@ -70,36 +84,59 @@ describe('docs [...slug] — config + getStaticPaths', () => {
70
84
 
71
85
  test('enumerates every doc slug as a single `/`-joined string', async () => {
72
86
  const paths = await getStaticPaths()
87
+ // getCollection sorts by slug.
73
88
  expect(paths.map((p) => p.params.slug)).toEqual([
74
- 'intro/getting-started',
75
89
  'guides/first-page',
90
+ 'intro/getting-started',
76
91
  ])
77
92
  })
78
93
  })
79
94
 
95
+ describe('docs [...slug] — loader (collection + locale trees)', () => {
96
+ test('loads the default-locale entry with rendered html', async () => {
97
+ const doc = await loader({ params: { slug: 'guides/first-page' } })
98
+ expect(doc?.data.title).toBe('Your first page')
99
+ expect(doc?.html).toContain('<code>*.tsx</code>')
100
+ })
101
+
102
+ test('loads the German tree when a locale is passed', async () => {
103
+ const doc = await loader({ params: { slug: 'guides/first-page' }, locale: 'de' })
104
+ expect(doc?.data.title).toBe('Deine erste Seite')
105
+ expect(doc?.html).toContain('Lege eine')
106
+ })
107
+
108
+ test('returns null for an unknown slug', async () => {
109
+ expect(await loader({ params: { slug: 'does/not-exist' } })).toBeNull()
110
+ })
111
+ })
112
+
80
113
  describe('docs [...slug] — localized meta', () => {
81
- test('reads the per-doc title from the catalog, per locale', () => {
82
- expect(meta({ locale: 'en', params: { slug: 'guides/first-page' } }).title).toBe('Your first page')
83
- expect(meta({ locale: 'de', params: { slug: 'guides/first-page' } }).title).toBe('Deine erste Seite')
114
+ test('reads the per-doc title from the entry frontmatter, per locale', async () => {
115
+ const enDoc = await loader({ params: { slug: 'guides/first-page' } })
116
+ const deDoc = await loader({ params: { slug: 'guides/first-page' }, locale: 'de' })
117
+ expect(meta({ locale: 'en', loaderData: enDoc }).title).toBe('Your first page')
118
+ expect(meta({ locale: 'de', loaderData: deDoc }).title).toBe('Deine erste Seite')
84
119
  })
85
120
 
86
121
  test('falls back to the not-found title for an unknown slug', () => {
87
- expect(meta({ locale: 'en', params: { slug: 'does/not-exist' } }).title).toBe('Not found')
122
+ expect(meta({ locale: 'en', loaderData: null }).title).toBe('Not found')
88
123
  })
89
124
  })
90
125
 
91
- describe('docs [...slug] — localized render', () => {
92
- test('renders the matched doc title + body (English) for a known slug', () => {
126
+ describe('docs [...slug] — render', () => {
127
+ test('renders the matched doc title + body (English) for a known slug', async () => {
93
128
  currentLocale = 'en'
94
129
  params.mockReturnValue({ slug: 'guides/first-page' })
130
+ loaderData.mockReturnValue(await loader({ params: { slug: 'guides/first-page' } }))
95
131
  render(createElement(DocPage))
96
132
  expect(container.querySelector('h1')?.textContent).toBe('Your first page')
97
133
  expect(container.textContent).toContain('Drop a')
98
134
  })
99
135
 
100
- test('renders the matched doc title + body (German) for a known slug', () => {
136
+ test('renders the matched doc title + body (German) for a known slug', async () => {
101
137
  currentLocale = 'de'
102
138
  params.mockReturnValue({ slug: 'guides/first-page' })
139
+ loaderData.mockReturnValue(await loader({ params: { slug: 'guides/first-page' }, locale: 'de' }))
103
140
  render(createElement(DocPage))
104
141
  expect(container.querySelector('h1')?.textContent).toBe('Deine erste Seite')
105
142
  expect(container.textContent).toContain('Lege eine')
@@ -108,6 +145,7 @@ describe('docs [...slug] — localized render', () => {
108
145
  test('renders the localized not-found fallback for an unknown slug', () => {
109
146
  currentLocale = 'en'
110
147
  params.mockReturnValue({ slug: 'does/not-exist' })
148
+ loaderData.mockReturnValue(null)
111
149
  render(createElement(DocPage))
112
150
  expect(container.querySelector('h1')?.textContent).toBe('Not found')
113
151
  expect(container.textContent).toContain('does/not-exist')
@@ -2,55 +2,50 @@
2
2
  // `/docs/<path>` URL (and its `/de/docs/<path>` mirror). `getStaticPaths`
3
3
  // enumerates the doc tree at build time so every page is pre-rendered.
4
4
  //
5
- // Bilingual (Strategy B): the doc SLUG is stable across locales — only the
6
- // /de URL prefix differsso each entry carries a `titleKey` / `bodyKey`
7
- // into the message catalog rather than literal, one-language strings. The
8
- // [locale] mirror (src/pages/[locale]/docs/[...slug].tsx) re-exports this
9
- // page and emits the cross-product of non-default locales × these slugs.
5
+ // Bilingual via the `docs` CONTENT COLLECTION (content/docs/{en,de}/**):
6
+ // the SLUG is stable across locales only the /de URL prefix and the
7
+ // locale tree differ. This bare route reads the DEFAULT locale's tree; the
8
+ // [locale] mirror re-exports the component and loads the requested tree
9
+ // (with fallback to English for untranslated entries).
10
10
 
11
11
  import type { ReactNode } from 'react'
12
12
  import type { PageMeta } from '@voltro/web'
13
- import { useParams } from '@voltro/web'
14
- import { T, useTFn } from '@voltro/i18n'
15
- import { getCatalog, type Messages } from '../../../lib/locale'
13
+ import { useLoaderData, useParams } from '@voltro/web'
14
+ import { T } from '@voltro/i18n'
15
+ import { getCollection, getEntry, type ContentEntry } from '@voltro/content'
16
+ import { docs, type Doc } from '../../../collections/docs.collection'
17
+ import { getCatalog } from '../../../lib/locale'
16
18
 
17
19
  export const renderMode = 'static' as const
18
20
  export const interactive = 'none' as const
19
21
 
20
- interface DocEntry {
21
- readonly slug: string
22
- readonly titleKey: keyof Messages
23
- readonly bodyKey: keyof Messages
24
- }
25
-
26
- // The doc tree — slugs stay identical across locales; the per-locale prose
27
- // lives in the catalogs. Replace with a loader over a markdown folder / CMS
28
- // / database query; the shape stays the same.
29
- export const DOCS: ReadonlyArray<DocEntry> = [
30
- { slug: 'intro/getting-started', titleKey: 'docs.getting-started.title', bodyKey: 'docs.getting-started.body' },
31
- { slug: 'guides/first-page', titleKey: 'docs.first-page.title', bodyKey: 'docs.first-page.body' },
32
- ]
33
-
34
22
  // getStaticPaths returns `{ params: { slug: '<path>' } }` entries — the
35
23
  // framework substitutes each into `/docs/[...slug]` to enumerate the URLs
36
24
  // to pre-render. The captured slug is the raw `/`-joined path (NOT a string
37
25
  // array) so a single deep slug like 'intro/getting-started' renders one file.
38
26
  export const getStaticPaths = async (): Promise<Array<{ params: { slug: string } }>> =>
39
- DOCS.map((d) => ({ params: { slug: d.slug } }))
27
+ (await getCollection(docs.name)).map((d) => ({ params: { slug: d.slug } }))
28
+
29
+ // Loads THIS doc's entry at build time — frontmatter decoded against the
30
+ // collection schema, body rendered to HTML (code fences highlighted
31
+ // server-side). The [locale] mirror passes the URL locale instead.
32
+ export const loader = async ({ params, locale }: {
33
+ readonly params: { readonly slug: string }
34
+ readonly locale?: string
35
+ }): Promise<ContentEntry<Doc> | null> =>
36
+ await getEntry<Doc>(docs.name, params.slug, locale !== undefined ? { locale } : {})
40
37
 
41
- // Per-locale, per-doc <title>: reads the active locale + the captured slug
42
- // (both threaded in at SSG time) so each variant's head is localised.
38
+ // Per-locale, per-doc <title> from the entry's frontmatter.
43
39
  export const meta = ({
44
40
  locale,
45
- params,
41
+ loaderData,
46
42
  }: {
47
43
  locale: string
48
- params: Readonly<Record<string, string>>
44
+ loaderData: ContentEntry<Doc> | null
49
45
  }): PageMeta => {
50
46
  const c = getCatalog(locale)
51
- const doc = DOCS.find((d) => d.slug === params.slug)
52
47
  return {
53
- title: doc ? c[doc.titleKey] : c['docs.notFound.title'],
48
+ title: loaderData?.data.title ?? c['docs.notFound.title'],
54
49
  description: c['meta.docs.description'],
55
50
  }
56
51
  }
@@ -59,8 +54,7 @@ const code = (chunks: ReactNode): ReactNode => <code>{chunks}</code>
59
54
 
60
55
  export default function DocPage(): ReactNode {
61
56
  const { slug } = useParams<{ slug: string }>()
62
- const t = useTFn()
63
- const doc = DOCS.find((d) => d.slug === slug)
57
+ const doc = useLoaderData<ContentEntry<Doc> | null>()
64
58
  if (!doc) {
65
59
  return (
66
60
  <article>
@@ -75,8 +69,8 @@ export default function DocPage(): ReactNode {
75
69
  }
76
70
  return (
77
71
  <article>
78
- <h1>{t(doc.titleKey)}</h1>
79
- <p>{t(doc.bodyKey)}</p>
72
+ <h1>{doc.data.title}</h1>
73
+ <div dangerouslySetInnerHTML={{ __html: doc.html ?? '' }} />
80
74
  </article>
81
75
  )
82
76
  }
@@ -10,15 +10,25 @@
10
10
  import { afterEach, describe, expect, test, vi } from 'vitest'
11
11
  import { act, createElement, type ReactNode } from 'react'
12
12
  import { createRoot, type Root } from 'react-dom/client'
13
+ import { dirname, join } from 'node:path'
14
+ import { fileURLToPath } from 'node:url'
15
+ import { setContentRoot } from '@voltro/content'
13
16
  import en from '../locales/en'
14
17
  import de from '../locales/de'
15
18
 
19
+ // The link list comes from the docs CONTENT COLLECTION — real files.
20
+ setContentRoot(join(dirname(fileURLToPath(import.meta.url)), '..', '..'))
21
+
16
22
  const catalogs: Record<string, Record<string, string>> = { en, de }
17
23
  let currentLocale = 'en'
18
24
 
19
25
  const fill = (msg: string, values?: Record<string, unknown>): string =>
20
26
  values ? msg.replace(/\{(\w+)\}/g, (_, k: string) => String(values[k] ?? `{${k}}`)) : msg
21
27
 
28
+ vi.mock('@voltro/web', () => ({
29
+ useLoaderData: () => entriesByLocale[currentLocale] ?? entriesByLocale['en'],
30
+ }))
31
+
22
32
  vi.mock('@voltro/i18n', () => ({
23
33
  // The locale catalogs call these at module top-level; identity mirrors the
24
34
  // real (compile-time-only) implementations so importing them doesn't throw.
@@ -32,7 +42,11 @@ vi.mock('@voltro/i18n', () => ({
32
42
  fill(catalogs[currentLocale]?.[id] ?? id, values),
33
43
  }))
34
44
 
35
- const { default: Index, meta, renderMode, interactive } = await import('./page')
45
+ const { default: Index, meta, renderMode, interactive, loader } = await import('./page')
46
+ const entriesByLocale: Record<string, Awaited<ReturnType<typeof loader>>> = {
47
+ en: await loader(),
48
+ de: await loader({ locale: 'de' }),
49
+ }
36
50
 
37
51
  ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
38
52
 
@@ -75,7 +89,7 @@ describe('index — localized render', () => {
75
89
  test('links to both doc pages with bare (en) URLs and English titles', () => {
76
90
  currentLocale = 'en'
77
91
  render(createElement(Index))
78
- expect(hrefs()).toEqual(['/docs/intro/getting-started', '/docs/guides/first-page'])
92
+ expect(hrefs()).toEqual(['/docs/guides/first-page', '/docs/intro/getting-started'])
79
93
  expect(container.textContent).toContain('Getting started')
80
94
  expect(container.textContent).toContain('Your first page')
81
95
  })
@@ -83,7 +97,7 @@ describe('index — localized render', () => {
83
97
  test('prefixes the doc links with /de and shows German titles for locale de', () => {
84
98
  currentLocale = 'de'
85
99
  render(createElement(Index))
86
- expect(hrefs()).toEqual(['/de/docs/intro/getting-started', '/de/docs/guides/first-page'])
100
+ expect(hrefs()).toEqual(['/de/docs/guides/first-page', '/de/docs/intro/getting-started'])
87
101
  expect(container.textContent).toContain('Erste Schritte')
88
102
  expect(container.textContent).toContain('Deine erste Seite')
89
103
  })
@@ -2,12 +2,17 @@
2
2
  // themselves live under `src/pages/docs/[...slug].tsx`.
3
3
  //
4
4
  // Bilingual (Strategy B): mounted at `/` (default locale) and mirrored at
5
- // `/de` by src/pages/[locale]/index.tsx. The links are locale-prefixed off
6
- // the active locale so /de's landing points at the /de docs.
5
+ // `/de` by src/pages/[locale]/index.tsx. The link list comes from the `docs`
6
+ // CONTENT COLLECTION titles are the entries' frontmatter, per locale tree
7
+ // (the /de mirror passes its locale into the loader), so a new .md file
8
+ // appears here without touching this page.
7
9
 
8
10
  import type { ReactNode } from 'react'
9
11
  import type { PageMeta } from '@voltro/web'
12
+ import { useLoaderData } from '@voltro/web'
13
+ import { getCollection, type ContentEntry } from '@voltro/content'
10
14
  import { T, useLocale } from '@voltro/i18n'
15
+ import { docs, type Doc } from '../collections/docs.collection'
11
16
  import { getCatalog, withLocalePrefix } from '../lib/locale'
12
17
 
13
18
  export const renderMode = 'static' as const
@@ -21,7 +26,11 @@ export const meta = ({ locale }: { locale: string }): PageMeta => {
21
26
  }
22
27
  }
23
28
 
29
+ export const loader = async ({ locale }: { readonly locale?: string } = {}): Promise<ReadonlyArray<ContentEntry<Doc>>> =>
30
+ await getCollection<Doc>(docs.name, locale !== undefined ? { locale } : {})
31
+
24
32
  export default function Index(): ReactNode {
33
+ const entries = useLoaderData<ReadonlyArray<ContentEntry<Doc>>>()
25
34
  const locale = useLocale()
26
35
  return (
27
36
  <main>
@@ -29,16 +38,11 @@ export default function Index(): ReactNode {
29
38
  <T id="home.title" values={{ project: '{{capProjectName}}' }} />
30
39
  </h1>
31
40
  <ul>
32
- <li>
33
- <a href={withLocalePrefix('/docs/intro/getting-started', locale)}>
34
- <T id="docs.getting-started.title" />
35
- </a>
36
- </li>
37
- <li>
38
- <a href={withLocalePrefix('/docs/guides/first-page', locale)}>
39
- <T id="docs.first-page.title" />
40
- </a>
41
- </li>
41
+ {entries.map((doc) => (
42
+ <li key={doc.slug}>
43
+ <a href={withLocalePrefix(`/docs/${doc.slug}`, locale)}>{doc.data.title}</a>
44
+ </li>
45
+ ))}
42
46
  </ul>
43
47
  </main>
44
48
  )
@@ -12,16 +12,16 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/cli": "0.51.0",
16
- "@voltro/client": "0.51.0",
17
- "@voltro/env": "0.51.0",
18
- "@voltro/i18n": "0.51.0",
19
- "@voltro/web": "0.51.0",
15
+ "@voltro/cli": "0.53.0",
16
+ "@voltro/client": "0.53.0",
17
+ "@voltro/env": "0.53.0",
18
+ "@voltro/i18n": "0.53.0",
19
+ "@voltro/web": "0.53.0",
20
20
  "react": "^19.0.0",
21
21
  "react-dom": "^19.0.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@voltro/devtools": "0.51.0",
24
+ "@voltro/devtools": "0.53.0",
25
25
  "@types/react": "^19.0.0",
26
26
  "@types/react-dom": "^19.0.0",
27
27
  "jsdom": "^30.0.0",
@@ -12,17 +12,16 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/cli": "0.51.0",
16
- "@voltro/client": "0.51.0",
17
- "@voltro/env": "0.51.0",
18
- "@voltro/i18n": "0.51.0",
19
- "@voltro/ui-shadcn": "0.51.0",
20
- "@voltro/web": "0.51.0",
15
+ "@voltro/cli": "0.53.0",
16
+ "@voltro/client": "0.53.0",
17
+ "@voltro/env": "0.53.0",
18
+ "@voltro/i18n": "0.53.0",
19
+ "@voltro/web": "0.53.0",
21
20
  "react": "^19.0.0",
22
21
  "react-dom": "^19.0.0"
23
22
  },
24
23
  "devDependencies": {
25
- "@voltro/devtools": "0.51.0",
24
+ "@voltro/devtools": "0.53.0",
26
25
  "@types/react": "^19.0.0",
27
26
  "@types/react-dom": "^19.0.0",
28
27
  "jsdom": "^30.0.0",
@@ -12,18 +12,18 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@{{projectName}}/api": "0.51.0",
16
- "@voltro/cli": "0.51.0",
17
- "@voltro/client": "0.51.0",
18
- "@voltro/env": "0.51.0",
19
- "@voltro/i18n": "0.51.0",
20
- "@voltro/ui-shadcn": "0.51.0",
21
- "@voltro/web": "0.51.0",
15
+ "@{{projectName}}/api": "0.53.0",
16
+ "@voltro/cli": "0.53.0",
17
+ "@voltro/client": "0.53.0",
18
+ "@voltro/env": "0.53.0",
19
+ "@voltro/i18n": "0.53.0",
20
+ "@voltro/ui-shadcn": "0.53.0",
21
+ "@voltro/web": "0.53.0",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
24
24
  },
25
25
  "devDependencies": {
26
- "@voltro/devtools": "0.51.0",
26
+ "@voltro/devtools": "0.53.0",
27
27
  "@types/react": "^19.0.0",
28
28
  "@types/react-dom": "^19.0.0",
29
29
  "jsdom": "^30.0.0",
@@ -12,18 +12,18 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@{{projectName}}/api": "0.51.0",
16
- "@voltro/cli": "0.51.0",
17
- "@voltro/client": "0.51.0",
18
- "@voltro/env": "0.51.0",
19
- "@voltro/i18n": "0.51.0",
20
- "@voltro/ui-shadcn": "0.51.0",
21
- "@voltro/web": "0.51.0",
15
+ "@{{projectName}}/api": "0.53.0",
16
+ "@voltro/cli": "0.53.0",
17
+ "@voltro/client": "0.53.0",
18
+ "@voltro/env": "0.53.0",
19
+ "@voltro/i18n": "0.53.0",
20
+ "@voltro/ui-shadcn": "0.53.0",
21
+ "@voltro/web": "0.53.0",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
24
24
  },
25
25
  "devDependencies": {
26
- "@voltro/devtools": "0.51.0",
26
+ "@voltro/devtools": "0.53.0",
27
27
  "@types/react": "^19.0.0",
28
28
  "@types/react-dom": "^19.0.0",
29
29
  "jsdom": "^30.0.0",
@@ -12,17 +12,17 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/cli": "0.51.0",
16
- "@voltro/client": "0.51.0",
17
- "@voltro/env": "0.51.0",
18
- "@voltro/i18n": "0.51.0",
19
- "@voltro/ui-shadcn": "0.51.0",
20
- "@voltro/web": "0.51.0",
15
+ "@voltro/cli": "0.53.0",
16
+ "@voltro/client": "0.53.0",
17
+ "@voltro/env": "0.53.0",
18
+ "@voltro/i18n": "0.53.0",
19
+ "@voltro/ui-shadcn": "0.53.0",
20
+ "@voltro/web": "0.53.0",
21
21
  "react": "^19.0.0",
22
22
  "react-dom": "^19.0.0"
23
23
  },
24
24
  "devDependencies": {
25
- "@voltro/devtools": "0.51.0",
25
+ "@voltro/devtools": "0.53.0",
26
26
  "@types/react": "^19.0.0",
27
27
  "@types/react-dom": "^19.0.0",
28
28
  "jsdom": "^30.0.0",