@voltro/cli 0.28.0 → 0.30.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 (354) hide show
  1. package/CHANGELOG.md +655 -0
  2. package/THIRD-PARTY-NOTICES.md +90 -4
  3. package/dist/{apiBuild-eUM32r1u.js → apiBuild-BceikpQf.js} +3 -3
  4. package/dist/apiBuild-CxvR1Dwt.js +2 -0
  5. package/dist/bin.js +3 -3
  6. package/dist/checkCommand-BMr-X48w.js +2427 -0
  7. package/dist/checkCommand-BOk9y264.js +2 -0
  8. package/dist/cliRuntime-Oh517vCV.js +96 -0
  9. package/dist/{commands-CSkrUI1h.js → commands-D4Y1-Nlu.js} +8575 -9366
  10. package/dist/{dbCommand-CpYgmSw4.js → dbCommand-C7hbl5pk.js} +345 -266
  11. package/dist/dbCommand-D8nhyppR.js +2 -0
  12. package/dist/{dev-DlBWWnJQ.js → dev-Cr-teYsz.js} +1 -1
  13. package/dist/{dev-CEZwJhmb.js → dev-DmnkH-wr.js} +2939 -1832
  14. package/dist/fileConventions-Cof68_BL.js +33 -0
  15. package/dist/{frameworkTableAssembly-BwHU9Euq.js → frameworkTableAssembly-BzAajuUv.js} +115 -99
  16. package/dist/frameworkTableAssembly-DU0aOxjL.js +2 -0
  17. package/dist/index.d.ts +7 -2
  18. package/dist/index.js +1 -1
  19. package/dist/{inspect-CUCCzw2I.js → inspect-BUUjt773.js} +349 -192
  20. package/dist/inspect-mmBuRXmy.js +2 -0
  21. package/dist/{inspectMetrics-BU90mvJN.js → inspectMetrics-BeJdP_3b.js} +1068 -978
  22. package/dist/{manifestBuild-BnzAxp2O.js → manifestBuild-BLrVuSlM.js} +1 -1
  23. package/dist/manifestBuild-Dj8Jjoto.js +2 -0
  24. package/dist/sdkgen-CqpOq_fy.js +582 -0
  25. package/dist/serveCommand-Ciq_wIY8.js +1577 -0
  26. package/dist/serveEntry.js +2 -2
  27. package/dist/{start-BGXIf6zT.js → start-ocqB1seX.js} +283 -270
  28. package/dist/startEntry.js +2 -2
  29. package/package.json +17 -17
  30. package/templates/AGENTS.core.md +50 -0
  31. package/templates/AGENTS.md +54 -2
  32. package/templates/agent-docs/_index.md +4 -2
  33. package/templates/agent-docs/_manifest.json +26 -5
  34. package/templates/agent-docs/ai.md +191 -0
  35. package/templates/agent-docs/cli.md +143 -6
  36. package/templates/agent-docs/configuration.md +32 -0
  37. package/templates/agent-docs/data.md +282 -0
  38. package/templates/agent-docs/database/migrations.md +47 -0
  39. package/templates/agent-docs/database/misc.md +57 -0
  40. package/templates/agent-docs/database/schema.md +3 -1
  41. package/templates/agent-docs/internationalization.md +169 -8
  42. package/templates/agent-docs/local-first-mobile.md +426 -0
  43. package/templates/agent-docs/routing.md +94 -3
  44. package/templates/agent-docs/schema-driven-ui.md +12 -0
  45. package/templates/agent-docs/security.md +17 -0
  46. package/templates/agent-docs/templates/apibackends.md +210 -1
  47. package/templates/agent-docs/templates/appshells.md +144 -1
  48. package/templates/agent-docs/templates/mobile.md +60 -0
  49. package/templates/agent-docs/templates/overview.md +17 -7
  50. package/templates/agent-docs/whats-new.md +238 -158
  51. package/templates/agent-docs/workflows.md +376 -0
  52. package/templates/apps/api-ai/package.json +7 -7
  53. package/templates/apps/api-auth/package.json +8 -8
  54. package/templates/apps/api-backend/package.json +7 -7
  55. package/templates/apps/api-backend-deactivation/package.json +7 -7
  56. package/templates/apps/api-backend-mail/package.json +8 -8
  57. package/templates/apps/api-backend-mariadb/package.json +9 -9
  58. package/templates/apps/api-backend-sqlite/.env.example +19 -0
  59. package/templates/apps/api-backend-sqlite/README.md +38 -0
  60. package/templates/apps/api-backend-sqlite/app.config.ts +35 -0
  61. package/templates/apps/api-backend-sqlite/database/schema.ts +54 -0
  62. package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.server.ts +19 -0
  63. package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.ts +37 -0
  64. package/templates/apps/api-backend-sqlite/package.json +30 -0
  65. package/templates/apps/api-backend-sqlite/queries/notes.query.server.ts +14 -0
  66. package/templates/apps/api-backend-sqlite/queries/notes.query.ts +20 -0
  67. package/templates/apps/api-backend-sqlite/template.json +6 -0
  68. package/templates/apps/api-backend-sqlite/tests/notes.create.test.ts +50 -0
  69. package/templates/apps/api-backend-sqlite/tsconfig.json +5 -0
  70. package/templates/apps/api-backend-storage/package.json +8 -8
  71. package/templates/apps/api-cms/README.md +104 -0
  72. package/templates/apps/api-cms/actions/content.get.action.server.ts +27 -0
  73. package/templates/apps/api-cms/actions/content.get.action.ts +19 -0
  74. package/templates/apps/api-cms/actions/content.types.action.server.ts +26 -0
  75. package/templates/apps/api-cms/actions/content.types.action.ts +40 -0
  76. package/templates/apps/api-cms/actions/me.action.server.ts +18 -0
  77. package/templates/apps/api-cms/actions/me.action.ts +16 -0
  78. package/templates/apps/api-cms/app.config.ts +61 -0
  79. package/templates/apps/api-cms/content/blogPost.contentType.ts +39 -0
  80. package/templates/apps/api-cms/content/index.ts +18 -0
  81. package/templates/apps/api-cms/content/page.contentType.ts +24 -0
  82. package/templates/apps/api-cms/database/schema.ts +64 -0
  83. package/templates/apps/api-cms/mutations/content.publish.mutation.server.ts +19 -0
  84. package/templates/apps/api-cms/mutations/content.publish.mutation.ts +15 -0
  85. package/templates/apps/api-cms/mutations/content.saveDraft.mutation.server.ts +36 -0
  86. package/templates/apps/api-cms/mutations/content.saveDraft.mutation.ts +32 -0
  87. package/templates/apps/api-cms/mutations/content.unpublish.mutation.server.ts +19 -0
  88. package/templates/apps/api-cms/mutations/content.unpublish.mutation.ts +11 -0
  89. package/templates/apps/api-cms/package.json +32 -0
  90. package/templates/apps/api-cms/queries/content.list.query.server.ts +0 -0
  91. package/templates/apps/api-cms/queries/content.list.query.ts +27 -0
  92. package/templates/apps/api-cms/template.json +6 -0
  93. package/templates/apps/api-cms/tests/content.descriptors.test.ts +64 -0
  94. package/templates/apps/api-cms/tests/content.write.test.ts +85 -0
  95. package/templates/apps/api-cms/tsconfig.json +5 -0
  96. package/templates/apps/api-collab/README.md +70 -0
  97. package/templates/apps/api-collab/app.config.ts +28 -0
  98. package/templates/apps/api-collab/database/schema.ts +63 -0
  99. package/templates/apps/api-collab/mutations/documents.create.mutation.server.ts +19 -0
  100. package/templates/apps/api-collab/mutations/documents.create.mutation.ts +36 -0
  101. package/templates/apps/api-collab/mutations/documents.setBody.mutation.server.ts +20 -0
  102. package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +30 -0
  103. package/templates/apps/api-collab/package.json +30 -0
  104. package/templates/apps/api-collab/queries/documents.query.server.ts +16 -0
  105. package/templates/apps/api-collab/queries/documents.query.ts +23 -0
  106. package/templates/apps/api-collab/template.json +6 -0
  107. package/templates/apps/api-collab/tests/documents.setBody.test.ts +89 -0
  108. package/templates/apps/api-collab/tsconfig.json +5 -0
  109. package/templates/apps/api-data-advanced/package.json +8 -8
  110. package/templates/apps/api-durable/package.json +8 -8
  111. package/templates/apps/api-feature-flags/package.json +9 -9
  112. package/templates/apps/api-governance/package.json +8 -8
  113. package/templates/apps/api-kv/package.json +8 -8
  114. package/templates/apps/api-moderation/package.json +8 -8
  115. package/templates/apps/api-observability/package.json +8 -8
  116. package/templates/apps/api-ratelimit/package.json +8 -8
  117. package/templates/apps/api-rbac/package.json +8 -8
  118. package/templates/apps/api-rest/package.json +7 -7
  119. package/templates/apps/api-saas/package.json +11 -11
  120. package/templates/apps/api-saas-starter/README.md +103 -0
  121. package/templates/apps/api-saas-starter/actions/me.action.server.ts +18 -0
  122. package/templates/apps/api-saas-starter/actions/me.action.ts +20 -0
  123. package/templates/apps/api-saas-starter/app.config.ts +87 -0
  124. package/templates/apps/api-saas-starter/database/schema.ts +57 -0
  125. package/templates/apps/api-saas-starter/mutations/invites.create.mutation.server.ts +26 -0
  126. package/templates/apps/api-saas-starter/mutations/invites.create.mutation.ts +18 -0
  127. package/templates/apps/api-saas-starter/mutations/projects.create.mutation.server.ts +29 -0
  128. package/templates/apps/api-saas-starter/mutations/projects.create.mutation.ts +18 -0
  129. package/templates/apps/api-saas-starter/package.json +32 -0
  130. package/templates/apps/api-saas-starter/queries/invites.list.query.server.ts +14 -0
  131. package/templates/apps/api-saas-starter/queries/invites.list.query.ts +17 -0
  132. package/templates/apps/api-saas-starter/queries/projects.list.query.server.ts +14 -0
  133. package/templates/apps/api-saas-starter/queries/projects.list.query.ts +18 -0
  134. package/templates/apps/api-saas-starter/template.json +6 -0
  135. package/templates/apps/api-saas-starter/tests/projects.create.test.ts +62 -0
  136. package/templates/apps/api-saas-starter/tests/session.test.ts +33 -0
  137. package/templates/apps/api-saas-starter/tsconfig.json +5 -0
  138. package/templates/apps/api-search/package.json +8 -8
  139. package/templates/apps/api-status/README.md +88 -0
  140. package/templates/apps/api-status/app.config.ts +36 -0
  141. package/templates/apps/api-status/authz.ts +33 -0
  142. package/templates/apps/api-status/database/schema.ts +70 -0
  143. package/templates/apps/api-status/mutations/components.create.mutation.server.ts +19 -0
  144. package/templates/apps/api-status/mutations/components.create.mutation.ts +16 -0
  145. package/templates/apps/api-status/mutations/incidents.create.mutation.server.ts +25 -0
  146. package/templates/apps/api-status/mutations/incidents.create.mutation.ts +22 -0
  147. package/templates/apps/api-status/mutations/incidents.resolve.mutation.server.ts +24 -0
  148. package/templates/apps/api-status/mutations/incidents.resolve.mutation.ts +19 -0
  149. package/templates/apps/api-status/mutations/incidents.update.mutation.server.ts +26 -0
  150. package/templates/apps/api-status/mutations/incidents.update.mutation.ts +21 -0
  151. package/templates/apps/api-status/package.json +30 -0
  152. package/templates/apps/api-status/queries/components.list.query.server.ts +14 -0
  153. package/templates/apps/api-status/queries/components.list.query.ts +13 -0
  154. package/templates/apps/api-status/queries/incidents.live.query.server.ts +14 -0
  155. package/templates/apps/api-status/queries/incidents.live.query.ts +20 -0
  156. package/templates/apps/api-status/queries/updates.list.query.server.ts +14 -0
  157. package/templates/apps/api-status/queries/updates.list.query.ts +17 -0
  158. package/templates/apps/api-status/template.json +6 -0
  159. package/templates/apps/api-status/tests/status.test.ts +70 -0
  160. package/templates/apps/api-status/tsconfig.json +5 -0
  161. package/templates/apps/api-versioning/package.json +8 -8
  162. package/templates/apps/api-webhooks/package.json +9 -9
  163. package/templates/apps/changelog/package.json +6 -6
  164. package/templates/apps/edge-functions/package.json +2 -2
  165. package/templates/apps/frontend-admin/app.config.ts +1 -1
  166. package/templates/apps/frontend-admin/package.json +8 -8
  167. package/templates/apps/frontend-admin/src/locales/index.ts +1 -1
  168. package/templates/apps/frontend-app/app.config.ts +1 -1
  169. package/templates/apps/frontend-app/package.json +8 -8
  170. package/templates/apps/frontend-app/src/locales/index.ts +1 -1
  171. package/templates/apps/frontend-app/src/pages/layout.tsx +1 -1
  172. package/templates/apps/frontend-app/src/pages/page.tsx +1 -1
  173. package/templates/apps/frontend-auth/README.md +78 -0
  174. package/templates/apps/frontend-auth/app.config.ts +34 -0
  175. package/templates/apps/frontend-auth/package.json +32 -0
  176. package/templates/apps/frontend-auth/src/components/AuthShell.tsx +35 -0
  177. package/templates/apps/frontend-auth/src/components/PasswordStrength.tsx +33 -0
  178. package/templates/apps/frontend-auth/src/config.ts +11 -0
  179. package/templates/apps/frontend-auth/src/globals.css +105 -0
  180. package/templates/apps/frontend-auth/src/globals.d.ts +6 -0
  181. package/templates/apps/frontend-auth/src/lib/auth.ts +34 -0
  182. package/templates/apps/frontend-auth/src/lib/redirect.test.ts +24 -0
  183. package/templates/apps/frontend-auth/src/lib/redirect.ts +29 -0
  184. package/templates/apps/frontend-auth/src/locales/de.ts +66 -0
  185. package/templates/apps/frontend-auth/src/locales/en.ts +76 -0
  186. package/templates/apps/frontend-auth/src/locales/index.ts +14 -0
  187. package/templates/apps/frontend-auth/src/pages/forgot/page.tsx +51 -0
  188. package/templates/apps/frontend-auth/src/pages/layout.tsx +12 -0
  189. package/templates/apps/frontend-auth/src/pages/login/page.test.tsx +53 -0
  190. package/templates/apps/frontend-auth/src/pages/login/page.tsx +67 -0
  191. package/templates/apps/frontend-auth/src/pages/logout/page.tsx +28 -0
  192. package/templates/apps/frontend-auth/src/pages/magic/page.tsx +51 -0
  193. package/templates/apps/frontend-auth/src/pages/page.tsx +26 -0
  194. package/templates/apps/frontend-auth/src/pages/reset/page.test.tsx +51 -0
  195. package/templates/apps/frontend-auth/src/pages/reset/page.tsx +63 -0
  196. package/templates/apps/frontend-auth/src/pages/signup/page.tsx +60 -0
  197. package/templates/apps/frontend-auth/src/pages/verify/page.tsx +50 -0
  198. package/templates/apps/frontend-auth/template.json +6 -0
  199. package/templates/apps/frontend-auth/tsconfig.json +5 -0
  200. package/templates/apps/frontend-blank/app.config.ts +1 -1
  201. package/templates/apps/frontend-blank/package.json +7 -7
  202. package/templates/apps/frontend-blank/src/locales/index.ts +1 -1
  203. package/templates/apps/frontend-blank/src/pages/layout.tsx +1 -1
  204. package/templates/apps/frontend-blank/src/pages/page.tsx +1 -1
  205. package/templates/apps/frontend-cms/README.md +47 -0
  206. package/templates/apps/frontend-cms/app.config.ts +37 -0
  207. package/templates/apps/frontend-cms/package.json +33 -0
  208. package/templates/apps/frontend-cms/src/config.ts +8 -0
  209. package/templates/apps/frontend-cms/src/globals.css +105 -0
  210. package/templates/apps/frontend-cms/src/globals.d.ts +6 -0
  211. package/templates/apps/frontend-cms/src/lib/api.ts +58 -0
  212. package/templates/apps/frontend-cms/src/locales/de.ts +50 -0
  213. package/templates/apps/frontend-cms/src/locales/en.ts +55 -0
  214. package/templates/apps/frontend-cms/src/locales/index.ts +14 -0
  215. package/templates/apps/frontend-cms/src/pages/(app)/error.tsx +18 -0
  216. package/templates/apps/frontend-cms/src/pages/(app)/layout.test.tsx +31 -0
  217. package/templates/apps/frontend-cms/src/pages/(app)/layout.tsx +62 -0
  218. package/templates/apps/frontend-cms/src/pages/(app)/not-found.tsx +14 -0
  219. package/templates/apps/frontend-cms/src/pages/(app)/page.test.tsx +110 -0
  220. package/templates/apps/frontend-cms/src/pages/(app)/page.tsx +159 -0
  221. package/templates/apps/frontend-cms/src/pages/layout.tsx +12 -0
  222. package/templates/apps/frontend-cms/src/pages/login/page.test.tsx +58 -0
  223. package/templates/apps/frontend-cms/src/pages/login/page.tsx +93 -0
  224. package/templates/apps/frontend-cms/template.json +6 -0
  225. package/templates/apps/frontend-cms/tsconfig.json +5 -0
  226. package/templates/apps/frontend-collab/README.md +80 -0
  227. package/templates/apps/frontend-collab/app.config.ts +38 -0
  228. package/templates/apps/frontend-collab/package.json +33 -0
  229. package/templates/apps/frontend-collab/src/locales/de.ts +29 -0
  230. package/templates/apps/frontend-collab/src/locales/en.ts +31 -0
  231. package/templates/apps/frontend-collab/src/locales/index.ts +15 -0
  232. package/templates/apps/frontend-collab/src/pages/layout.tsx +39 -0
  233. package/templates/apps/frontend-collab/src/pages/page.test.tsx +150 -0
  234. package/templates/apps/frontend-collab/src/pages/page.tsx +186 -0
  235. package/templates/apps/frontend-collab/template.json +6 -0
  236. package/templates/apps/frontend-collab/tsconfig.json +11 -0
  237. package/templates/apps/frontend-contact/package.json +7 -7
  238. package/templates/apps/frontend-dashboard/app.config.ts +1 -1
  239. package/templates/apps/frontend-dashboard/package.json +7 -7
  240. package/templates/apps/frontend-dashboard/src/locales/index.ts +1 -1
  241. package/templates/apps/frontend-docs/package.json +7 -7
  242. package/templates/apps/frontend-i18n/package.json +6 -6
  243. package/templates/apps/frontend-landing/package.json +7 -7
  244. package/templates/apps/frontend-portal/README.md +71 -0
  245. package/templates/apps/frontend-portal/app.config.ts +37 -0
  246. package/templates/apps/frontend-portal/package.json +32 -0
  247. package/templates/apps/frontend-portal/src/config.ts +8 -0
  248. package/templates/apps/frontend-portal/src/globals.css +93 -0
  249. package/templates/apps/frontend-portal/src/globals.d.ts +6 -0
  250. package/templates/apps/frontend-portal/src/lib/api.ts +62 -0
  251. package/templates/apps/frontend-portal/src/locales/de.ts +86 -0
  252. package/templates/apps/frontend-portal/src/locales/en.ts +93 -0
  253. package/templates/apps/frontend-portal/src/locales/index.ts +14 -0
  254. package/templates/apps/frontend-portal/src/pages/(portal)/api-keys/page.test.tsx +71 -0
  255. package/templates/apps/frontend-portal/src/pages/(portal)/api-keys/page.tsx +124 -0
  256. package/templates/apps/frontend-portal/src/pages/(portal)/billing/page.test.tsx +86 -0
  257. package/templates/apps/frontend-portal/src/pages/(portal)/billing/page.tsx +97 -0
  258. package/templates/apps/frontend-portal/src/pages/(portal)/error.tsx +19 -0
  259. package/templates/apps/frontend-portal/src/pages/(portal)/invoices/page.tsx +45 -0
  260. package/templates/apps/frontend-portal/src/pages/(portal)/layout.test.tsx +31 -0
  261. package/templates/apps/frontend-portal/src/pages/(portal)/layout.tsx +63 -0
  262. package/templates/apps/frontend-portal/src/pages/(portal)/not-found.tsx +15 -0
  263. package/templates/apps/frontend-portal/src/pages/(portal)/page.tsx +39 -0
  264. package/templates/apps/frontend-portal/src/pages/(portal)/profile/page.tsx +79 -0
  265. package/templates/apps/frontend-portal/src/pages/layout.tsx +12 -0
  266. package/templates/apps/frontend-portal/src/pages/login/page.test.tsx +58 -0
  267. package/templates/apps/frontend-portal/src/pages/login/page.tsx +96 -0
  268. package/templates/apps/frontend-portal/template.json +6 -0
  269. package/templates/apps/frontend-portal/tsconfig.json +5 -0
  270. package/templates/apps/frontend-saas/README.md +73 -0
  271. package/templates/apps/frontend-saas/app.config.ts +46 -0
  272. package/templates/apps/frontend-saas/package.json +32 -0
  273. package/templates/apps/frontend-saas/src/config.ts +8 -0
  274. package/templates/apps/frontend-saas/src/globals.css +85 -0
  275. package/templates/apps/frontend-saas/src/globals.d.ts +6 -0
  276. package/templates/apps/frontend-saas/src/lib/api.ts +47 -0
  277. package/templates/apps/frontend-saas/src/locales/de.ts +72 -0
  278. package/templates/apps/frontend-saas/src/locales/en.ts +81 -0
  279. package/templates/apps/frontend-saas/src/locales/index.ts +14 -0
  280. package/templates/apps/frontend-saas/src/pages/(marketing)/layout.tsx +30 -0
  281. package/templates/apps/frontend-saas/src/pages/(marketing)/login/page.test.tsx +58 -0
  282. package/templates/apps/frontend-saas/src/pages/(marketing)/login/page.tsx +96 -0
  283. package/templates/apps/frontend-saas/src/pages/(marketing)/page.tsx +27 -0
  284. package/templates/apps/frontend-saas/src/pages/dashboard/billing/page.tsx +72 -0
  285. package/templates/apps/frontend-saas/src/pages/dashboard/error.tsx +20 -0
  286. package/templates/apps/frontend-saas/src/pages/dashboard/layout.test.tsx +32 -0
  287. package/templates/apps/frontend-saas/src/pages/dashboard/layout.tsx +69 -0
  288. package/templates/apps/frontend-saas/src/pages/dashboard/not-found.tsx +17 -0
  289. package/templates/apps/frontend-saas/src/pages/dashboard/page.test.tsx +113 -0
  290. package/templates/apps/frontend-saas/src/pages/dashboard/page.tsx +76 -0
  291. package/templates/apps/frontend-saas/src/pages/dashboard/team/page.tsx +73 -0
  292. package/templates/apps/frontend-saas/src/pages/layout.tsx +12 -0
  293. package/templates/apps/frontend-saas/template.json +6 -0
  294. package/templates/apps/frontend-saas/tsconfig.json +5 -0
  295. package/templates/apps/frontend-spa/app.config.ts +1 -1
  296. package/templates/apps/frontend-spa/package.json +7 -7
  297. package/templates/apps/frontend-spa/src/locales/index.ts +1 -1
  298. package/templates/apps/frontend-spa/src/pages/layout.tsx +1 -1
  299. package/templates/apps/frontend-spa/src/pages/page.tsx +1 -1
  300. package/templates/apps/frontend-ssr/app.config.ts +1 -1
  301. package/templates/apps/frontend-ssr/package.json +7 -7
  302. package/templates/apps/frontend-ssr/src/locales/index.ts +1 -1
  303. package/templates/apps/frontend-ssr/src/pages/layout.tsx +1 -1
  304. package/templates/apps/frontend-ssr/src/pages/page.tsx +1 -1
  305. package/templates/apps/frontend-ssr-api/app.config.ts +1 -1
  306. package/templates/apps/frontend-ssr-api/package.json +8 -8
  307. package/templates/apps/frontend-ssr-api/src/locales/index.ts +1 -1
  308. package/templates/apps/frontend-ssr-api/src/pages/layout.tsx +1 -1
  309. package/templates/apps/frontend-static-blog/package.json +6 -6
  310. package/templates/apps/frontend-status/README.md +51 -0
  311. package/templates/apps/frontend-status/app.config.ts +39 -0
  312. package/templates/apps/frontend-status/package.json +32 -0
  313. package/templates/apps/frontend-status/src/config.ts +7 -0
  314. package/templates/apps/frontend-status/src/globals.css +70 -0
  315. package/templates/apps/frontend-status/src/globals.d.ts +6 -0
  316. package/templates/apps/frontend-status/src/lib/status.ts +81 -0
  317. package/templates/apps/frontend-status/src/locales/de.ts +41 -0
  318. package/templates/apps/frontend-status/src/locales/en.ts +45 -0
  319. package/templates/apps/frontend-status/src/locales/index.ts +13 -0
  320. package/templates/apps/frontend-status/src/pages/layout.tsx +27 -0
  321. package/templates/apps/frontend-status/src/pages/page.test.tsx +123 -0
  322. package/templates/apps/frontend-status/src/pages/page.tsx +136 -0
  323. package/templates/apps/frontend-status/template.json +6 -0
  324. package/templates/apps/frontend-status/tsconfig.json +11 -0
  325. package/templates/apps/mobile-app/README.md +58 -0
  326. package/templates/apps/mobile-app/app.config.ts +33 -0
  327. package/templates/apps/mobile-app/babel.config.js +5 -0
  328. package/templates/apps/mobile-app/metro.config.js +20 -0
  329. package/templates/apps/mobile-app/package.json +33 -0
  330. package/templates/apps/mobile-app/src/app/_layout.tsx +61 -0
  331. package/templates/apps/mobile-app/src/app/index.tsx +62 -0
  332. package/templates/apps/mobile-app/src/app/orders/[id].tsx +19 -0
  333. package/templates/apps/mobile-app/src/app/settings.tsx +51 -0
  334. package/templates/apps/mobile-app/src/client.ts +74 -0
  335. package/templates/apps/mobile-app/src/lib/deeplinks.ts +39 -0
  336. package/templates/apps/mobile-app/src/lib/notifications.ts +31 -0
  337. package/templates/apps/mobile-app/src/lib/sync.ts +29 -0
  338. package/templates/apps/mobile-app/src/persistence.ts +38 -0
  339. package/templates/apps/mobile-app/template.json +6 -0
  340. package/templates/apps/mobile-app/tests/deeplinks.test.ts +32 -0
  341. package/templates/apps/mobile-app/tests/notifications.test.ts +26 -0
  342. package/templates/apps/mobile-app/tests/sync.test.ts +34 -0
  343. package/templates/apps/mobile-app/tsconfig.json +10 -0
  344. package/templates/apps/mobile-app/voltro.mobile.ts +11 -0
  345. package/templates/baselines/compose/docker-compose.prod.yml +15 -0
  346. package/templates/baselines/compose-mariadb/docker-compose.prod.yml +15 -0
  347. package/templates/patches/@effect__cluster@0.60.0.patch +6 -6
  348. package/dist/apiBuild-DgBS9ayv.js +0 -2
  349. package/dist/dbCommand-DvguqlzF.js +0 -2
  350. package/dist/fileConventions-3bffWssN.js +0 -30
  351. package/dist/frameworkTableAssembly-lrjZtk0G.js +0 -2
  352. package/dist/inspect-gt8bq-Tz.js +0 -2
  353. package/dist/manifestBuild-ifczArzr.js +0 -2
  354. package/dist/serveCommand-ZTn-dPFa.js +0 -1425
@@ -296,6 +296,63 @@ who may receive the plaintext — a private note you decrypt *for its owner* is
296
296
  valid case, so treating "encrypted" as "never to a client" would be wrong. State
297
297
  the exposure policy explicitly.
298
298
 
299
+ ## `.readableBy(...scopes)` — visible only to scoped subjects
300
+
301
+ The **graded middle of the same wire-exposure axis** as `.serverOnly()` — not a
302
+ fourth axis. A plain column is visible to everyone who can read the row;
303
+ `.serverOnly()` hides it from every client; `.readableBy(...)` sits between them —
304
+ the column reaches a subject only if it holds the scope:
305
+
306
+ ```ts
307
+ import { id, integer, text, table } from '@voltro/database'
308
+
309
+ export const invoices = table('invoices', {
310
+ id: id(),
311
+ number: text(), // visible to everyone
312
+ amountCents: integer().readableBy('billing:read'), // only billing-scoped subjects
313
+ taxNote: text().readableBy('billing:read', 'admin:pii'), // ANY of the two scopes
314
+ })
315
+ ```
316
+
317
+ **What it does:** a column marked `.readableBy(...scopes)` is stripped from the
318
+ wire OUTPUT for any subject holding NONE of the listed scopes, and present for
319
+ one holding **at least one** of them. Like `.serverOnly()` it changes nothing at
320
+ rest and nothing in the DDL — it is a wire concern only, so a server-internal
321
+ read (`ctx.store.query`) still sees the value; the strip applies only on the way
322
+ out to a client.
323
+
324
+ **Scope semantics:**
325
+
326
+ - **≥ 1 scope, OR-matched.** A subject sees the column iff it holds at least one
327
+ of the listed scopes — `.readableBy('a', 'b')` means "a OR b", not both.
328
+ - **Effective scopes.** The check is the framework's `hasEffectiveScope` — the
329
+ subject's RAW scopes **∪** its rbac role-derived scopes — so a role that grants
330
+ `billing:read` unlocks the column even when the scope is not listed directly on
331
+ the subject.
332
+ - **`admin:full` bypasses it.** A subject holding `admin:full` sees every
333
+ `.readableBy(...)` column, exactly as it satisfies every guard.
334
+ - **At least one scope is required.** `.readableBy()` with no scope would mean
335
+ "readable by nobody" — that is `.serverOnly()` — so both the empty call and a
336
+ blank scope string are rejected at declaration.
337
+
338
+ **`.serverOnly()` wins when both are present.** `.serverOnly()` is the all-hidden
339
+ end of the axis, so a column carrying both is hidden from EVERYONE — `admin:full`
340
+ included. `.readableBy()` only ever *narrows* an otherwise-visible column; it can
341
+ never re-expose a `.serverOnly()` one:
342
+
343
+ ```ts
344
+ amountCents: integer().readableBy('billing:read'), // scoped subjects see it; admin:full does too
345
+ secretKey: text().serverOnly().readableBy('billing:read'), // serverOnly wins → hidden from EVERYONE
346
+ ```
347
+
348
+ Enforcement shares the SAME chokepoint as `.serverOnly()` — the Dispatcher's read
349
+ boundary — but resolves **per subject**: it applies to a query's initial snapshot
350
+ AND every reactive subscription delta, and to the one-shot
351
+ [`publicApi`](/docs/data/rest-routes) REST GET, in both boot paths (`voltro dev`,
352
+ `voltro serve`). A table that declares no `.readableBy(...)` column pays nothing —
353
+ the strip collapses to the subject-independent `.serverOnly()` set and the
354
+ dispatcher's read/diff memo is still shared across all subscribers of a change.
355
+
299
356
  ## Worked example
300
357
 
301
358
  ```ts
@@ -280,7 +280,9 @@ Two layers enforce row invariants, and they're complementary:
280
280
 
281
281
  - **`table().validate(Schema)` (below)** — app-level `effect/Schema` validation, run before the INSERT/UPDATE. Use it for the rich stuff a portable `CHECK` can't express: regex / email / structured strings, length ranges, numeric bounds, cross-field rules with custom typed errors. It's the single validation surface — don't reach for a pile of `.min()` / `.max()` / `.pattern()` column modifiers (there aren't any, by design).
282
282
 
283
- Rule of thumb: **`.check()` when the DATABASE must guarantee it; `validate(Schema)` for everything else.** There is no `.comment()` / `.index()` column modifier indexes are table-level.
283
+ - **`table().rule(name, predicate)` (cross-table)** neither of the above can read ANOTHER table. A [business rule](/docs/data/mutations#cross-table-business-rules-rule) is a predicate the runtime evaluates inside the mutation transaction, so it can compare the written row against rows in other tables (an invoice total vs. its line items) and rolls the mutation back with a typed `BusinessRuleViolation` on failure.
284
+
285
+ Rule of thumb: **`.check()` when the DATABASE must guarantee a single-row constraint; `table().rule()` for a cross-table invariant; `validate(Schema)` for everything else.** There is no `.comment()` / `.index()` column modifier — indexes are table-level.
284
286
 
285
287
  ## Default with callback
286
288
 
@@ -45,13 +45,17 @@ The framework's generated `.framework/app.tsx` wraps the Router in `<I18nProvide
45
45
 
46
46
  Server-side, the active locale is determined by, in priority order:
47
47
 
48
- 1. **`voltro:lang` cookie** — the user's explicit choice (written by `@voltro/ui-shadcn`'s ProfileMenu).
48
+ 1. **`voltro:locale` cookie** — the user's explicit choice (written by `@voltro/ui-shadcn`'s ProfileMenu).
49
49
  2. **`Accept-Language` header** — the browser/OS preference, q-weighted and sorted per RFC 4647.
50
50
  3. **`defaultLocale`** — last-resort fallback.
51
51
 
52
52
  The resolved locale is **guaranteed** to be one of the codes in `locales`. Any unsupported value (a cookie pointing at a code you no longer ship, a browser asking for `xx-YY`) falls through to the next signal. RFC 4647 lookup strips subtags one segment at a time — `de-CH-1996` → `de-CH` → `de` — so a `de` catalog serves a `de-CH` browser.
53
53
 
54
- The client side mirrors #1 (cookie) and #3 (default) for hydration safety. `Accept-Language` is **server-only** because `navigator.languages` can diverge from what the server saw, which would cause a hydration mismatch.
54
+ The client **adopts what the server resolved**, reading it from the `<html lang>` attribute the server render sets, then falling back to the cookie and the default. `Accept-Language` is never read in the browser: `navigator.languages` can diverge from what the server saw.
55
+
56
+ > Earlier versions said the client "mirrors cookie and default for hydration safety". That was the opposite of what happened. Dropping the `Accept-Language` signal is not the same as agreeing with the server about it — on a first visit, with no cookie yet, the server negotiated `Accept-Language` while the client fell through to `defaultLocale`. An English browser on a German-default app therefore hydrated `de` over an `en` tree and React discarded the entire server render, which is exactly what SSR was enabled to avoid. It stopped as soon as anything wrote the cookie, so one language switch made it un-reproducible for that developer.
57
+
58
+ `<html lang>` carries the same resolved locale — the value the `<I18nProvider>` renders with, on the same request. That matters on its own: it is what a screen reader pronounces in, what Chrome offers to translate *from*, and what hyphenation uses.
55
59
 
56
60
  See [Catalogs](/docs/i18n/catalogs) for the type-safe catalog convention and the component hooks, [Plurals & formatting](/docs/i18n/formatting) for CLDR plural selection and the `Intl`-backed date / number / relative-time hooks, and [URL strategies](/docs/i18n/url-strategies) for cookie-only vs URL-prefix routing.
57
61
 
@@ -236,7 +240,7 @@ export const getCatalog = (locale?: string) => pickCatalog({ en, de }, locale, '
236
240
  ```
237
241
 
238
242
  ```tsx
239
- // any page — meta gets the active locale (from the URL prefix, or the voltro:lang cookie)
243
+ // any page — meta gets the active locale (from the URL prefix, or the voltro:locale cookie)
240
244
  import { getCatalog } from '../locales'
241
245
 
242
246
  export const meta = ({ locale }: { readonly locale: string }): PageMeta => ({
@@ -326,14 +330,14 @@ The wrap deliberately doesn't re-export everything, to keep the framework's bles
326
330
  <!-- source: en/i18n/url-strategies.md -->
327
331
  ## URL strategies
328
332
 
329
- _Cookie-only vs URL-prefix routing (Strategy A / B), the URL-prefix integration sketch, resolveLocale on the /server subpath, the voltro:lang / voltro:theme cookie convention._
333
+ _Cookie-only vs URL-prefix routing (Strategy A / B), the URL-prefix integration sketch, resolveLocale on the /server subpath, the voltro:locale / voltro:theme cookie convention._
330
334
 
331
335
  There are two ways an i18n app can encode the active locale. Pick **one**, based on what the app's URLs are *for*.
332
336
 
333
337
  ## Strategy A — cookie-only (default for product apps)
334
338
 
335
339
  - All locales are served from the **same** URL (`/dashboard`, `/settings`).
336
- - The `voltro:lang` cookie + `Accept-Language` determine which catalog renders.
340
+ - The `voltro:locale` cookie + `Accept-Language` determine which catalog renders.
337
341
  - The ProfileMenu's language switch — or the standalone `<LocaleSwitcher>` from `@voltro/ui-shadcn` — writes the cookie and reloads.
338
342
  - Use when URLs are **functional** (`/dashboard/projects/42/deployments`) and don't need to encode the language. This is most apps.
339
343
 
@@ -507,15 +511,16 @@ The `/server` subpath is convention. Both that path and the main entry are pure
507
511
 
508
512
  ## Cookie convention
509
513
 
510
- The framework reads + writes **`voltro:lang`** for the active language choice (lowercase IETF tag: `en`, `de`, `fr-CA`). `@voltro/ui-shadcn`'s ProfileMenu and the framework's auto-wired resolver agree on this name — **don't pick a different one in app code.**
514
+ The framework reads + writes **`voltro:locale`** for the active language choice (lowercase IETF tag: `en`, `de`, `fr-CA`). `@voltro/ui-shadcn`'s ProfileMenu and the framework's auto-wired resolver agree on this name — **don't pick a different one in app code.**
511
515
 
512
- For theme the parallel cookie is **`voltro:theme`** (values `'system' | 'light' | 'dark'`). Both are managed by the kit's ProfileMenu out of the box; the helpers live in `@voltro/ui-shadcn` (`THEME_COOKIE`, `LANG_COOKIE`, `getCookie`, `setCookie`, `deleteCookie`, `parsePreferenceCookies`, `applyTheme`).
516
+ For theme the parallel cookie is **`voltro:theme`** (values `'system' | 'light' | 'dark'`). Both are managed by the kit's ProfileMenu out of the box; the helpers live in `@voltro/ui-shadcn` (`THEME_COOKIE`, `LOCALE_COOKIE`, `getCookie`, `setCookie`, `deleteCookie`, `parsePreferenceCookies`, `applyTheme`).
513
517
 
514
518
  ## Anti-patterns
515
519
 
516
520
  - **Don't mix strategies within one app.** Pick cookie-only *or* URL-prefix per app; mixing them produces ambiguous canonical URLs and broken language switching.
517
521
  - **Don't read `Accept-Language` on the client.** It's server-only — `navigator.languages` can diverge from what the server saw and cause a hydration mismatch.
518
- - **Don't invent your own cookie name.** The kit and the resolver only agree on `voltro:lang` / `voltro:theme`.
522
+ - **Don't invent your own cookie name.** The kit and the resolver only agree on `voltro:locale` / `voltro:theme`.
523
+ - **Don't hardcode the name either — import the constant.** `LOCALE_COOKIE` / `THEME_COOKIE` from `@voltro/ui-shadcn`. A cookie name the framework READS and your app WRITES is a public API, and it is the only kind where both sides can disagree with nothing failing: the resolver finds nothing, falls back to `Accept-Language`, and a user's language choice quietly stops working — but only for the users whose browser language differs from their choice, which is the least likely case anyone tests. With the constant, a rename in the framework is a compile error in your app. `voltro doctor` flags a written literal for exactly this reason.
519
524
 
520
525
 
521
526
 
@@ -697,3 +702,159 @@ For dates, numbers and relative time the hooks are the blessed path; reach for `
697
702
  - **Don't pin a global `timeZone` / locale for the whole app.** The formatters resolve the active locale from the provider; a pin makes every viewer read the app in one user's settings.
698
703
  - **Don't pass a locale-formatted string to a machine consumer.** Formatted output is presentation — send ISO strings and raw numbers to APIs, `dateTime` attributes and sort keys.
699
704
  - **Don't format inside a `.map()` by constructing `Intl` objects yourself.** The hooks memoize per locale; a fresh `new Intl.NumberFormat(...)` per row is the slow path.
705
+
706
+
707
+
708
+ ---
709
+
710
+ <!-- source: en/i18n/datetime.md -->
711
+ ## Dates & timezones
712
+
713
+ _"@voltro/datetime — UTC-instant storage, timezone-aware arithmetic/formatting on TC39 Temporal, and the request-scoped timezone-context seam."_
714
+
715
+ `@voltro/datetime` is the framework's opinion on time: **store UTC instants,
716
+ render in the viewer's timezone, never do zone math by hand.** It is built on the
717
+ [TC39 Temporal](https://tc39.es/proposal-temporal/docs/) standard (via a
718
+ polyfill, so the API is identical on the server and in the browser) and ships
719
+ pure, browser-safe helpers plus a request-scoped timezone seam.
720
+
721
+ The `.` entry is browser-safe — no `effect`, no `node:*` — so a route or
722
+ component can import it directly. The Effect seam lives in a separate subpath,
723
+ `@voltro/datetime/context`, so the pure surface carries no `effect` dependency.
724
+
725
+ > **Phase 1.** This is the storage + timezone + formatting layer. Schema-DSL
726
+ > temporal column types, `interval()`, and `rrule()` are later phases; use
727
+ > `timestamp()` columns and these helpers today.
728
+
729
+ ## The storage contract: a `Date` is a UTC instant
730
+
731
+ `timestamp()` stores `TIMESTAMPTZ`, and the driver hands your app code a plain JS
732
+ `Date`. A `Date` is a bare epoch-millisecond count with no zone of its own, so
733
+ the ONLY correct reading of it is "the UTC instant it points at". These helpers
734
+ make that reading explicit and lossless in both directions:
735
+
736
+ ```ts
737
+ import { toInstant, toUTCString, isValidTimeZone } from '@voltro/datetime'
738
+
739
+ // A `Date` from a query IS a UTC instant — read it as one, losslessly.
740
+ const when = toInstant(row.createdAt) // Temporal.Instant
741
+ toUTCString(row.createdAt) // "2026-08-06T12:00:00Z" — the wire/storage form
742
+
743
+ isValidTimeZone('Europe/Berlin') // true
744
+ isValidTimeZone('Mars/Phobos') // false
745
+ ```
746
+
747
+ `toInstant` accepts a `Temporal.Instant`, a JS `Date`, or an ISO-8601 string —
748
+ but a string MUST carry an explicit offset or `Z`. A naive `2026-01-01T12:00`
749
+ denotes no instant and throws, by design: the whole point is that there is no
750
+ silent zone-guessing. Bridge back with `toDate(instant)` for storage or interop.
751
+
752
+ ## Timezone resolution — the framework convention
753
+
754
+ Which zone should a given request render in? Resolve it in priority order, each
755
+ candidate validated as an IANA name, falling back to `UTC`:
756
+
757
+ ```ts
758
+ import { resolveTimezone } from '@voltro/datetime'
759
+
760
+ const tz = resolveTimezone({
761
+ userTimeZone: user.timezone, // 1. the viewing user's profile
762
+ tenantTimeZone: tenant.timezone, // 2. the tenant / org default
763
+ browserTimeZone: 'Europe/Berlin', // 3. the browser-reported zone
764
+ }) // → a validated IANA name, else 'UTC'
765
+ ```
766
+
767
+ This mirrors `@voltro/i18n`'s `resolveLocale` in spirit — pure, framework-agnostic,
768
+ no Effect or RPC — so it runs in any request pipeline. An invalid or absent
769
+ candidate falls through to the next signal; the result is GUARANTEED valid.
770
+
771
+ ### Carrying it through a request — the context seam
772
+
773
+ The resolved zone travels through a request via an Effect context tag in
774
+ `@voltro/datetime/context`, so any route, mutation, workflow, or agent can read it:
775
+
776
+ ```ts
777
+ import { currentTimezone, withTimezone } from '@voltro/datetime/context'
778
+ import { Effect } from 'effect'
779
+
780
+ const program = Effect.gen(function* () {
781
+ const tz = yield* currentTimezone // the request's zone, or 'UTC' when none set
782
+ return tz
783
+ })
784
+
785
+ // Provide an explicit zone for a sub-computation:
786
+ program.pipe(withTimezone('Europe/Berlin'))
787
+ ```
788
+
789
+ `currentTimezone` never fails — an absent context is the documented `UTC`
790
+ default, so call sites don't handle a missing-service error. The runtime provides
791
+ the tag per request (via `resolvedTimezoneLayer`); the web layer's `useTimezone()`
792
+ hook is a thin projection over it.
793
+
794
+ ## Arithmetic — the DST split is in the names
795
+
796
+ Every operation that is ambiguous without a zone REQUIRES an IANA `timeZone`
797
+ argument. There is no implicit "system zone", so a call site cannot silently do
798
+ the wrong thing on a differently-configured box. The DST distinction is
799
+ deliberate and encoded in the method names:
800
+
801
+ ```ts
802
+ import { addDays, addHours, startOfDay, endOfDay } from '@voltro/datetime'
803
+
804
+ // WALL-CLOCK: same local time, one calendar day later in Berlin.
805
+ // Across a DST boundary the elapsed real time is 23h or 25h. Needs a zone.
806
+ addDays(when, 1, 'Europe/Berlin')
807
+
808
+ // EXACT elapsed time: 24 × 3600 seconds, DST-oblivious. No zone.
809
+ addHours(when, 24)
810
+
811
+ // Zone-relative day boundaries.
812
+ startOfDay(when, 'Europe/Berlin')
813
+ endOfDay(when, 'Europe/Berlin')
814
+ ```
815
+
816
+ `addDays` / `addMonths` are wall-clock ("same local time, N days on"); `addHours`
817
+ is exact elapsed time. Comparisons that measure absolute instants (`isAfter`,
818
+ `isBefore`) take no zone; `isSameDay` does, because "same day" is a wall-clock
819
+ question. For a calendar date with no time and no zone — a birthday, a holiday, a
820
+ due date — use `plainDate(year, month, day)` / `parseDate('2026-08-06')`.
821
+
822
+ ## Formatting — locale- and timezone-aware
823
+
824
+ Formatting is where the viewer's timezone and locale are APPLIED. Both are
825
+ explicit arguments — this layer carries no ambient locale (the web layer resolves
826
+ `useLocale()` and passes it in):
827
+
828
+ ```ts
829
+ import { formatDate, formatDateTime, formatRelativeTime } from '@voltro/datetime'
830
+
831
+ formatDate(row.createdAt, 'Europe/Berlin', { locale: 'de' }) // "6. Aug. 2026"
832
+ formatDateTime(row.createdAt, 'America/New_York', { locale: 'en' })
833
+ formatRelativeTime(row.createdAt, { locale: 'en' }) // "3 hours ago"
834
+ ```
835
+
836
+ `FormatOptions` is `{ locale? }` plus any `Intl.DateTimeFormatOptions` override,
837
+ so `formatDate(when, tz, { locale, dateStyle: 'full' })` works. `formatRelativeTime`
838
+ picks the largest unit that fits the signed distance from `now` (default: the
839
+ current instant) and is zone-independent — it measures elapsed real time, not
840
+ wall-clock days.
841
+
842
+ > These helpers are the STORAGE/TIMEZONE layer. `@voltro/i18n`'s
843
+ > [`useFormatDate` / `useRelativeTime`](/docs/i18n/formatting) hooks are the
844
+ > React binding that read the active locale from the provider; reach for those in
845
+ > components, and for `@voltro/datetime` in server code, loaders, and tests.
846
+
847
+ ## Temporal directly
848
+
849
+ The exact standard `Temporal` types are re-exported, so you can drop to the full
850
+ API when a helper doesn't cover your case:
851
+
852
+ ```ts
853
+ import { Temporal } from '@voltro/datetime'
854
+
855
+ const noon = Temporal.PlainTime.from('12:00')
856
+ ```
857
+
858
+ Import `Temporal` from `@voltro/datetime`, never from the polyfill directly — that
859
+ keeps the eventual switch to the native global (once it is universal in browsers)
860
+ a one-line change for the whole codebase.