@voltro/cli 0.33.0 → 0.35.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 (343) hide show
  1. package/CHANGELOG.md +1968 -0
  2. package/bin/nodeEnvironment.d.mts +30 -0
  3. package/bin/nodeEnvironment.mjs +158 -0
  4. package/bin/voltro.mjs +69 -5
  5. package/dist/addCommand-BNeoeSxe.js +124 -0
  6. package/dist/addCommand-aXSQveak.js +2 -0
  7. package/dist/agentsMd-BTchIZku.js +2 -0
  8. package/dist/agentsMd-mhQMF1bx.js +254 -0
  9. package/dist/apiBuild-B8aoJvuw.js +2 -0
  10. package/dist/{apiBuild-h9VHtnlw.js → apiBuild-DYD_ONLD.js} +46 -46
  11. package/dist/{appGraph-gQ_6GkQQ.js → appGraph-KGDPTuTy.js} +1 -1
  12. package/dist/appGraph-zuMGKVYX.js +2 -0
  13. package/dist/appPort-B_HpJ_ck.js +48 -0
  14. package/dist/baselineCommand-C2ClWZN3.js +2 -0
  15. package/dist/baselineCommand-DIttzO8A.js +227 -0
  16. package/dist/bin.js +71 -28
  17. package/dist/build-CD8K4XOr.js +711 -0
  18. package/dist/cacheCommand-DA4OH9xt.js +42 -0
  19. package/dist/capabilitiesCommand-nq_pz5xd.js +123 -0
  20. package/dist/checkCommand-Ct9xkTrS.js +232 -0
  21. package/dist/checkCommand-DKpDLlqu.js +2 -0
  22. package/dist/{cliArgs-qdZSElM3.js → cliArgs-D4p8n7EE.js} +12 -1
  23. package/dist/cliError-BmdYnghb.js +10 -0
  24. package/dist/cliOutput-D1tSBoRM.js +15 -0
  25. package/dist/{cliRuntime-Oh517vCV.js → cliRuntime-Dh7UDinH.js} +20 -20
  26. package/dist/cloudClient-DWL-Hw_T.js +67 -0
  27. package/dist/cloudCmd-Cvv5HGaZ.js +364 -0
  28. package/dist/clusterCmd-D5wsCmA_.js +54 -0
  29. package/dist/codegen-CYM3Zqrf.js +605 -0
  30. package/dist/codegen-ChBi_hVa.js +2 -0
  31. package/dist/codegenCommand-C4YoQIc2.js +30 -0
  32. package/dist/codemodRunner-BnFq3Fgu.js +5384 -0
  33. package/dist/commandRunner-BLAEFLjp.js +47 -0
  34. package/dist/commands-BE8E7zF3.js +816 -0
  35. package/dist/connectionConfig-UFlIEiys.js +66 -0
  36. package/dist/dashboardCommand-D7SgZGaN.js +25 -0
  37. package/dist/dataCommand-BhYwDgg-.js +537 -0
  38. package/dist/dataProfile-dW-PsfLB.js +15 -0
  39. package/dist/dbCommand-DS4b97Is.js +2 -0
  40. package/dist/{dbCommand-DTLKAfbA.js → dbCommand-O8HA63s2.js} +552 -402
  41. package/dist/{dev-C_P8FLSx.js → dev-C7sFZq3m.js} +3670 -3169
  42. package/dist/dev-D2BikO7a.js +3 -0
  43. package/dist/devActivity-Dx_3nnGv.js +100 -0
  44. package/dist/devActivity.js +1 -1
  45. package/dist/dialectDriver-CgXnDfec.js +39 -0
  46. package/dist/discover-C9XKJDco.js +25 -0
  47. package/dist/doctorCommand-CM4Ch9C7.js +2 -0
  48. package/dist/{checkCommand-xGhRFFg2.js → doctorCommand-DnimF5IM.js} +523 -1278
  49. package/dist/dormancyCommand-QewYug_s.js +69 -0
  50. package/dist/e2eCmd-BRabZww-.js +147 -0
  51. package/dist/embeddingsCommand-BfiLS_QI.js +73 -0
  52. package/dist/envCommand-CCGPRQY1.js +60 -0
  53. package/dist/evalCommand-6RUfPen4.js +118 -0
  54. package/dist/evolveCommand-CHsLCtDf.js +281 -0
  55. package/dist/fileTaxonomy-CJfgOllU.js +457 -0
  56. package/dist/frameworkTableAssembly-YyVe32Cb.js +2 -0
  57. package/dist/frameworkTableAssembly-oMPBKqlE.js +511 -0
  58. package/dist/generateCommand-DbgcUpGw.js +147 -0
  59. package/dist/index.d.ts +45 -0
  60. package/dist/index.js +4 -3
  61. package/dist/infoCommand-DwOgK1t6.js +60 -0
  62. package/dist/{inspect-BUUjt773.js → inspect-CjTYzAs_.js} +113 -41
  63. package/dist/inspect-P4pxoMaV.js +2 -0
  64. package/dist/inspectCmd-EHFZ9yYu.js +224 -0
  65. package/dist/inspectFetch-EMuhTG_9.js +151 -0
  66. package/dist/inspectMetrics-CfdKLh6t.js +72 -0
  67. package/dist/loadEnv-D9nEOClM.js +44 -0
  68. package/dist/logFileSink-C_D2wRN1.js +105 -0
  69. package/dist/logsCmd-D36xK7Zu.js +260 -0
  70. package/dist/manifestBuild-COkJoyAr.js +2 -0
  71. package/dist/{manifestBuild-BLrVuSlM.js → manifestBuild-hpPLaGxV.js} +1 -1
  72. package/dist/metaCommands-7MJfZ5cf.js +196 -0
  73. package/dist/migrate-BV7I-ZHZ.js +83 -0
  74. package/dist/mssqlClusterPatch-_4cE_nun.js +44 -0
  75. package/dist/newCommand-COWOJ1_E.js +156 -0
  76. package/dist/nodeEnvironment-cGFAj1J8.js +28 -0
  77. package/dist/packageCommand-Cug_3Ogl.js +271 -0
  78. package/dist/pageConvention-cEiRxdab.js +5 -0
  79. package/dist/privacyCommand-C-Df56U_.js +146 -0
  80. package/dist/probeCommand-CZfaaUOZ.js +122 -0
  81. package/dist/projectScaffold-DmzEKHib.js +2 -0
  82. package/dist/projectScaffold-LMMtaavR.js +814 -0
  83. package/dist/renderModeScan-D7J1B7Kw.js +105 -0
  84. package/dist/renderProfile-1OWWAAtx.js +81 -0
  85. package/dist/runtimeRegistry-DMeKfTHP.js +81 -0
  86. package/dist/runtimeTrace-CH3eUiMw.js +91 -0
  87. package/dist/scheduleCmd-DQRu6BZC.js +149 -0
  88. package/dist/scheduleManifestCmd-D2x0CTTY.js +249 -0
  89. package/dist/schemaIr-UJybUUZW.js +103 -0
  90. package/dist/{sdkgen-C81QIkiL.js → sdkgen-BLkvGRfX.js} +111 -209
  91. package/dist/seedRunner-ZmLSqNe2.js +333 -0
  92. package/dist/serveCommand-CbDHU6l-.js +2 -0
  93. package/dist/serveCommand-iwlUBNS1.js +1766 -0
  94. package/dist/serveEntry.js +5 -5
  95. package/dist/serverlessCommand-CfJZy6dS.js +482 -0
  96. package/dist/start-BgN62boB.js +3 -0
  97. package/dist/start-T4VesWiM.js +1087 -0
  98. package/dist/startEntry.js +2 -2
  99. package/dist/staticCommand-Dr2M6tpU.js +304 -0
  100. package/dist/storageCommand-Co6NfLqN.js +42 -0
  101. package/dist/templates-De8IR5-c.js +102 -0
  102. package/dist/test-CI6iDsYc.js +115 -0
  103. package/dist/tracesCmd-DStmCJPi.js +232 -0
  104. package/dist/tsconfigPaths-BWXBWgcl.js +107 -0
  105. package/dist/tsxLoader-EuXmSJ1K.js +51 -0
  106. package/dist/typecheckCommand-BlsWiCNq.js +61 -0
  107. package/dist/updateCommand-BlMZhWgO.js +2 -0
  108. package/dist/updateCommand-x0pI_x-B.js +585 -0
  109. package/dist/webDev-BcykISYQ2.js +2 -0
  110. package/dist/{inspectMetrics-1xzTKAFx.js → webDev-Dybxew86.js} +988 -1560
  111. package/dist/webhookDiscovery-CrGAfhIG.js +2 -0
  112. package/dist/webhookDiscovery-D7VaeMlz.js +51 -0
  113. package/dist/webhooksCommand-DlAgS2Iw.js +267 -0
  114. package/dist/workflowsCmd-BGF-mRZ5.js +608 -0
  115. package/package.json +209 -17
  116. package/templates/AGENTS.core.md +58 -3
  117. package/templates/AGENTS.md +64 -7
  118. package/templates/agent-docs/_index.md +6 -4
  119. package/templates/agent-docs/_manifest.json +23 -6
  120. package/templates/agent-docs/ai.md +370 -0
  121. package/templates/agent-docs/authentication.md +313 -31
  122. package/templates/agent-docs/caching.md +6 -0
  123. package/templates/agent-docs/cli.md +853 -50
  124. package/templates/agent-docs/data.md +608 -12
  125. package/templates/agent-docs/database/migrations.md +223 -25
  126. package/templates/agent-docs/database/misc.md +156 -40
  127. package/templates/agent-docs/database/querying.md +19 -1
  128. package/templates/agent-docs/database/scaling.md +60 -0
  129. package/templates/agent-docs/database/schema.md +1 -0
  130. package/templates/agent-docs/database/seedsdialects.md +208 -19
  131. package/templates/agent-docs/database/transactions.md +68 -0
  132. package/templates/agent-docs/deployment.md +284 -25
  133. package/templates/agent-docs/introduction.md +88 -17
  134. package/templates/agent-docs/local-first-mobile.md +79 -4
  135. package/templates/agent-docs/multi-tenancy.md +188 -42
  136. package/templates/agent-docs/observability.md +58 -3
  137. package/templates/agent-docs/plugins/ai-flows.md +247 -2
  138. package/templates/agent-docs/plugins/analytics-postgres.md +1 -1
  139. package/templates/agent-docs/plugins/audit.md +37 -1
  140. package/templates/agent-docs/plugins/auth-social.md +143 -0
  141. package/templates/agent-docs/plugins/auth-workos.md +4 -2
  142. package/templates/agent-docs/plugins/auth.md +131 -6
  143. package/templates/agent-docs/plugins/billing.md +132 -15
  144. package/templates/agent-docs/plugins/cdc-out.md +46 -7
  145. package/templates/agent-docs/plugins/clickhouse.md +32 -1
  146. package/templates/agent-docs/plugins/duckdb.md +1 -1
  147. package/templates/agent-docs/plugins/flags.md +132 -0
  148. package/templates/agent-docs/plugins/governance.md +105 -7
  149. package/templates/agent-docs/plugins/multitenancy.md +9 -4
  150. package/templates/agent-docs/plugins/presence.md +13 -2
  151. package/templates/agent-docs/plugins/ratelimit.md +9 -0
  152. package/templates/agent-docs/plugins/search.md +162 -8
  153. package/templates/agent-docs/plugins/sso-saml.md +47 -8
  154. package/templates/agent-docs/plugins/storage.md +11 -0
  155. package/templates/agent-docs/plugins/webhooks.md +105 -0
  156. package/templates/agent-docs/plugins.md +152 -18
  157. package/templates/agent-docs/reference.md +60 -3
  158. package/templates/agent-docs/releases.md +1117 -0
  159. package/templates/agent-docs/routing.md +43 -25
  160. package/templates/agent-docs/scheduling.md +27 -0
  161. package/templates/agent-docs/schema-driven-ui.md +92 -12
  162. package/templates/agent-docs/security.md +426 -0
  163. package/templates/agent-docs/templates/apibackends.md +87 -18
  164. package/templates/agent-docs/templates/appshells.md +32 -14
  165. package/templates/agent-docs/templates/overview.md +13 -8
  166. package/templates/agent-docs/testing.md +211 -14
  167. package/templates/agent-docs/whats-new.md +98 -136
  168. package/templates/agent-docs/workflows.md +231 -14
  169. package/templates/apps/api-ai/actions/summarize.action.ts +11 -0
  170. package/templates/apps/api-ai/package.json +8 -7
  171. package/templates/apps/api-auth/actions/me.action.ts +13 -0
  172. package/templates/apps/api-auth/package.json +9 -8
  173. package/templates/apps/api-backend/mutations/notes.create.mutation.ts +9 -0
  174. package/templates/apps/api-backend/package.json +12 -8
  175. package/templates/apps/api-backend/queries/notes.query.ts +30 -8
  176. package/templates/apps/api-backend-deactivation/actions/users.get.action.ts +14 -0
  177. package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.ts +8 -0
  178. package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.ts +10 -0
  179. package/templates/apps/api-backend-deactivation/package.json +8 -7
  180. package/templates/apps/api-backend-mail/actions/sendWelcome.action.ts +17 -0
  181. package/templates/apps/api-backend-mail/mutations/notes.create.mutation.ts +9 -0
  182. package/templates/apps/api-backend-mail/package.json +9 -8
  183. package/templates/apps/api-backend-mail/queries/notes.query.ts +30 -8
  184. package/templates/apps/api-backend-mariadb/.env.example +14 -0
  185. package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.ts +9 -0
  186. package/templates/apps/api-backend-mariadb/package.json +10 -9
  187. package/templates/apps/api-backend-mariadb/queries/notes.query.ts +30 -8
  188. package/templates/apps/api-backend-sqlite/.env.example +14 -0
  189. package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.ts +9 -0
  190. package/templates/apps/api-backend-sqlite/package.json +9 -8
  191. package/templates/apps/api-backend-sqlite/queries/notes.query.ts +30 -8
  192. package/templates/apps/api-backend-storage/actions/uploadAvatar.action.ts +13 -0
  193. package/templates/apps/api-backend-storage/actions/uploadDocument.action.ts +12 -0
  194. package/templates/apps/api-backend-storage/mutations/notes.create.mutation.ts +9 -0
  195. package/templates/apps/api-backend-storage/package.json +9 -8
  196. package/templates/apps/api-backend-storage/queries/notes.query.ts +30 -8
  197. package/templates/apps/api-cms/actions/content.get.action.ts +7 -0
  198. package/templates/apps/api-cms/actions/content.types.action.ts +6 -0
  199. package/templates/apps/api-cms/actions/me.action.ts +13 -0
  200. package/templates/apps/api-cms/app.config.ts +19 -0
  201. package/templates/apps/api-cms/authz.ts +63 -0
  202. package/templates/apps/api-cms/mutations/content.publish.mutation.ts +10 -0
  203. package/templates/apps/api-cms/mutations/content.saveDraft.mutation.ts +10 -0
  204. package/templates/apps/api-cms/mutations/content.unpublish.mutation.ts +5 -0
  205. package/templates/apps/api-cms/package.json +11 -10
  206. package/templates/apps/api-cms/queries/content.list.query.ts +23 -7
  207. package/templates/apps/api-cms/tests/accessDecisions.test.ts +121 -0
  208. package/templates/apps/api-cms/tests/content.descriptors.test.ts +8 -4
  209. package/templates/apps/api-collab/mutations/documents.create.mutation.ts +8 -0
  210. package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +13 -0
  211. package/templates/apps/api-collab/package.json +9 -8
  212. package/templates/apps/api-collab/queries/documents.query.ts +19 -7
  213. package/templates/apps/api-data-advanced/package.json +9 -8
  214. package/templates/apps/api-data-advanced/queries/authors.withBooks.query.ts +12 -0
  215. package/templates/apps/api-data-advanced/queries/books.search.query.ts +9 -0
  216. package/templates/apps/api-durable/mutations/orders.approve.mutation.ts +17 -0
  217. package/templates/apps/api-durable/mutations/orders.place.mutation.ts +9 -0
  218. package/templates/apps/api-durable/package.json +9 -8
  219. package/templates/apps/api-feature-flags/actions/notes.export.action.ts +16 -0
  220. package/templates/apps/api-feature-flags/mutations/notes.create.mutation.ts +12 -0
  221. package/templates/apps/api-feature-flags/package.json +10 -9
  222. package/templates/apps/api-governance/README.md +30 -8
  223. package/templates/apps/api-governance/actions/profiles.get.action.server.ts +17 -1
  224. package/templates/apps/api-governance/actions/profiles.get.action.ts +29 -4
  225. package/templates/apps/api-governance/database/schema.ts +16 -4
  226. package/templates/apps/api-governance/mutations/profiles.create.mutation.ts +12 -0
  227. package/templates/apps/api-governance/package.json +9 -8
  228. package/templates/apps/api-kv/actions/sync.pull.action.ts +15 -0
  229. package/templates/apps/api-kv/actions/sync.reset.action.ts +13 -0
  230. package/templates/apps/api-kv/actions/sync.status.action.ts +7 -0
  231. package/templates/apps/api-kv/package.json +9 -8
  232. package/templates/apps/api-kv/queries/events.list.query.ts +19 -8
  233. package/templates/apps/api-moderation/mutations/comments.create.mutation.ts +11 -0
  234. package/templates/apps/api-moderation/mutations/posts.create.mutation.ts +13 -0
  235. package/templates/apps/api-moderation/package.json +9 -8
  236. package/templates/apps/api-observability/mutations/notes.create.mutation.ts +8 -0
  237. package/templates/apps/api-observability/package.json +9 -8
  238. package/templates/apps/api-observability/queries/notes.list.query.ts +13 -0
  239. package/templates/apps/api-ratelimit/mutations/notes.create.mutation.ts +14 -0
  240. package/templates/apps/api-ratelimit/package.json +9 -8
  241. package/templates/apps/api-rbac/package.json +9 -8
  242. package/templates/apps/api-rest/package.json +8 -7
  243. package/templates/apps/api-saas/mutations/projects.create.mutation.ts +13 -0
  244. package/templates/apps/api-saas/package.json +12 -11
  245. package/templates/apps/api-saas/queries/projects.list.query.ts +11 -0
  246. package/templates/apps/api-saas-starter/actions/me.action.ts +13 -0
  247. package/templates/apps/api-saas-starter/app.config.ts +19 -0
  248. package/templates/apps/api-saas-starter/authz.ts +75 -0
  249. package/templates/apps/api-saas-starter/mutations/invites.create.mutation.ts +10 -0
  250. package/templates/apps/api-saas-starter/mutations/projects.create.mutation.ts +9 -0
  251. package/templates/apps/api-saas-starter/package.json +15 -11
  252. package/templates/apps/api-saas-starter/queries/invites.list.query.ts +19 -8
  253. package/templates/apps/api-saas-starter/queries/projects.list.query.ts +18 -7
  254. package/templates/apps/api-saas-starter/tests/accessDecisions.test.ts +135 -0
  255. package/templates/apps/api-search/mutations/articles.create.mutation.ts +14 -0
  256. package/templates/apps/api-search/package.json +9 -8
  257. package/templates/apps/api-search/queries/articles.list.query.ts +21 -8
  258. package/templates/apps/api-status/README.md +10 -3
  259. package/templates/apps/api-status/app.config.ts +8 -3
  260. package/templates/apps/api-status/authz.ts +5 -3
  261. package/templates/apps/api-status/package.json +9 -8
  262. package/templates/apps/api-status/queries/components.list.query.ts +14 -6
  263. package/templates/apps/api-status/queries/incidents.live.query.ts +23 -10
  264. package/templates/apps/api-status/queries/updates.list.query.ts +16 -9
  265. package/templates/apps/api-status/tests/status.test.ts +9 -1
  266. package/templates/apps/api-versioning/actions/documents.asOf.action.ts +12 -0
  267. package/templates/apps/api-versioning/actions/documents.history.action.ts +13 -0
  268. package/templates/apps/api-versioning/mutations/documents.create.mutation.ts +9 -0
  269. package/templates/apps/api-versioning/mutations/documents.update.mutation.ts +12 -0
  270. package/templates/apps/api-versioning/package.json +9 -8
  271. package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.ts +16 -0
  272. package/templates/apps/api-webhooks/package.json +10 -9
  273. package/templates/apps/api-webhooks/queries/orders.list.query.ts +10 -0
  274. package/templates/apps/changelog/package.json +8 -6
  275. package/templates/apps/edge-functions/package.json +2 -2
  276. package/templates/apps/frontend-admin/package.json +10 -8
  277. package/templates/apps/frontend-admin/src/lib/admin.ts +20 -9
  278. package/templates/apps/frontend-admin/src/locales/de.ts +11 -1
  279. package/templates/apps/frontend-admin/src/locales/en.ts +13 -1
  280. package/templates/apps/frontend-admin/src/pages/admin/[entity]/page.tsx +65 -22
  281. package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +130 -23
  282. package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +3 -2
  283. package/templates/apps/frontend-admin/src/pages/admin/page.test.tsx +19 -2
  284. package/templates/apps/frontend-admin/src/pages/admin/page.tsx +9 -4
  285. package/templates/apps/frontend-app/app.config.ts +4 -3
  286. package/templates/apps/frontend-app/package.json +11 -8
  287. package/templates/apps/frontend-app/src/lib/api.ts +25 -0
  288. package/templates/apps/frontend-app/src/pages/page.test.tsx +130 -82
  289. package/templates/apps/frontend-app/src/pages/page.tsx +14 -18
  290. package/templates/apps/frontend-auth/package.json +10 -8
  291. package/templates/apps/frontend-blank/package.json +9 -7
  292. package/templates/apps/frontend-cms/package.json +11 -9
  293. package/templates/apps/frontend-collab/package.json +12 -9
  294. package/templates/apps/frontend-collab/src/pages/page.test.tsx +122 -78
  295. package/templates/apps/frontend-contact/package.json +9 -7
  296. package/templates/apps/frontend-dashboard/package.json +9 -7
  297. package/templates/apps/frontend-docs/package.json +9 -7
  298. package/templates/apps/frontend-i18n/package.json +8 -6
  299. package/templates/apps/frontend-landing/package.json +9 -7
  300. package/templates/apps/frontend-portal/package.json +10 -8
  301. package/templates/apps/frontend-saas/app.config.ts +10 -6
  302. package/templates/apps/frontend-saas/package.json +10 -8
  303. package/templates/apps/frontend-saas/src/lib/api.ts +27 -32
  304. package/templates/apps/frontend-saas/src/pages/dashboard/billing/page.tsx +7 -8
  305. package/templates/apps/frontend-saas/src/pages/dashboard/page.test.tsx +27 -3
  306. package/templates/apps/frontend-saas/src/pages/dashboard/page.tsx +4 -4
  307. package/templates/apps/frontend-saas/src/pages/dashboard/team/page.tsx +3 -4
  308. package/templates/apps/frontend-spa/package.json +9 -7
  309. package/templates/apps/frontend-ssr/package.json +9 -7
  310. package/templates/apps/frontend-ssr-api/package.json +10 -8
  311. package/templates/apps/frontend-static-blog/package.json +8 -6
  312. package/templates/apps/frontend-status/package.json +10 -8
  313. package/templates/apps/mobile-app/README.md +1 -0
  314. package/templates/apps/mobile-app/package.json +4 -2
  315. package/templates/apps/mobile-app/src/app/index.tsx +22 -12
  316. package/templates/apps/mobile-app/src/app/orders/[id].tsx +1 -1
  317. package/templates/apps/mobile-app/src/lib/api.ts +34 -0
  318. package/templates/apps/mobile-app/voltro.mobile.ts +4 -2
  319. package/templates/baselines/bare/.env.example +14 -0
  320. package/templates/baselines/bare/baseline.json +4 -4
  321. package/templates/baselines/compose/.env.example +14 -0
  322. package/templates/baselines/compose/README.md +1 -1
  323. package/templates/baselines/compose/baseline.json +5 -5
  324. package/templates/baselines/compose-mariadb/.env.example +14 -0
  325. package/templates/baselines/compose-mariadb/README.md +1 -1
  326. package/templates/baselines/compose-mariadb/baseline.json +5 -5
  327. package/templates/baselines/helm/.env.example +14 -0
  328. package/templates/baselines/helm/baseline.json +4 -4
  329. package/dist/apiBuild-C-x9YacA.js +0 -2
  330. package/dist/appGraph-CvQCte0z.js +0 -2
  331. package/dist/checkCommand-DRovTKza.js +0 -2
  332. package/dist/commands-CJfepbm4.js +0 -11541
  333. package/dist/dbCommand-b1gum4td.js +0 -2
  334. package/dist/dev-iiMtlkfs.js +0 -3
  335. package/dist/devActivity-BhIu6ncs.js +0 -159
  336. package/dist/frameworkTableAssembly-BwIrO5nv.js +0 -638
  337. package/dist/frameworkTableAssembly-D-EebUQX.js +0 -2
  338. package/dist/inspect-mmBuRXmy.js +0 -2
  339. package/dist/manifestBuild-Dj8Jjoto.js +0 -2
  340. package/dist/seedRunner-Bqxgp7HZ.js +0 -230
  341. package/dist/serveCommand-DdaM4Hup.js +0 -1608
  342. package/dist/start-C0koT0UO.js +0 -1084
  343. /package/templates/apps/api-ai/actions/{summarize.action.server.tsx → summarize.action.server.ts} +0 -0
@@ -11,7 +11,9 @@
11
11
  // `renderMode: 'ssr'` so the gate runs per request.
12
12
  //
13
13
  // The pages read the api live with `useSubscription` and write with
14
- // `useMutation` / `useAction`; the auth forms POST to the api's `/auth/*`.
14
+ // `useMutation` / `useAction` imported from `src/lib/api.ts`, which binds them
15
+ // ONCE to this api's generated procedure map so every tag and every input/output
16
+ // type is checked by the compiler. The auth forms POST to the api's `/auth/*`.
15
17
  import { defineEnv, envVar } from '@voltro/env'
16
18
 
17
19
  // Public, build-time env (baked into the bundle; read via `publicEnv`).
@@ -35,11 +37,13 @@ export default {
35
37
  // grow the app to more languages.
36
38
  locales: ['en', 'de'] as const,
37
39
  defaultLocale: 'en' as const,
38
- // The api this dashboard consumes. The NAME ('app') is the lookup key the
39
- // hooks + the loader's `query` use: `useSubscription('app', 'projects.list')`,
40
- // `useMutation('app', 'projects.create')`, `query('session.me', {})`. Its
41
- // port is auto-discovered from the api's own app.config.ts, and codegen pulls
42
- // its typed rpc surface (including the plugins' `billing.*` / `/auth`).
40
+ // The api this dashboard consumes. The NAME ('app') is the lookup key — spelled
41
+ // ONCE, in `src/lib/api.ts`'s `createHooks<AppProcedures>('app')`, and then
42
+ // never again: pages call `useSubscription('projects.list')` /
43
+ // `useMutation('projects.create')`. (A loader still names the tag itself:
44
+ // `query('session.me', {})`.) The api's port is auto-discovered from its own
45
+ // app.config.ts, and codegen pulls its typed rpc surface (including the
46
+ // plugins' `billing.*` / `/auth`) into that same map.
43
47
  apis: {
44
48
  app: { package: '@{{projectName}}/api' },
45
49
  },
@@ -12,21 +12,23 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@{{projectName}}/api": "0.33.0",
16
- "@voltro/cli": "0.33.0",
17
- "@voltro/client": "0.33.0",
18
- "@voltro/env": "0.33.0",
19
- "@voltro/i18n": "0.33.0",
20
- "@voltro/ui-shadcn": "0.33.0",
21
- "@voltro/web": "0.33.0",
15
+ "@{{projectName}}/api": "0.35.0",
16
+ "@voltro/cli": "0.35.0",
17
+ "@voltro/client": "0.35.0",
18
+ "@voltro/env": "0.35.0",
19
+ "@voltro/i18n": "0.35.0",
20
+ "@voltro/ui-shadcn": "0.35.0",
21
+ "@voltro/web": "0.35.0",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
24
24
  },
25
25
  "devDependencies": {
26
- "@voltro/devtools": "0.33.0",
26
+ "@voltro/devtools": "0.35.0",
27
27
  "@types/react": "^19.0.0",
28
28
  "@types/react-dom": "^19.0.0",
29
+ "jsdom": "^30.0.0",
29
30
  "typescript": "^6.0.3",
31
+ "@vitest/coverage-v8": "^4.1.10",
30
32
  "vitest": "^4.1.10"
31
33
  }
32
34
  }
@@ -1,37 +1,32 @@
1
- // The row + payload shapes this dashboard reads off the api, kept LOCAL so the
2
- // pages are self-contained. They mirror the api's descriptor outputs
3
- // (`projects.list`, `invites.list`, `billing.subscription`); you can also
4
- // `import type` them from the api package a type-only cross-app import is
5
- // browser-safe.
1
+ // The TYPED hook surface for the `app` api bound ONCE, here, and imported by
2
+ // every page instead of `@voltro/client`.
3
+ //
4
+ // `AppProcedures` is emitted by codegen into the api's `rpcGroup.generated.ts`:
5
+ // a type-level map of every rpc tag (the app's own AND the plugins', e.g.
6
+ // `billing.*`) to the descriptor behind it. Feeding it to `createHooks` makes
7
+ // the tag a literal union and the input/output types INFER:
8
+ //
9
+ // const { data } = useSubscription('projects.list') // rows, typed
10
+ // const create = useMutation('projects.create') // input + output typed
11
+ //
12
+ // So the row shapes this file used to declare by hand are gone. They were a
13
+ // MIRROR of the api's descriptor outputs, kept in sync by nobody: the old
14
+ // `useSubscription<ReadonlyArray<Project>>('app', 'projects.list')` asserted a
15
+ // type nothing checked against the server, and a typo'd tag only showed up at
16
+ // runtime. Both are compile errors now.
17
+ //
18
+ // `'app'` is the key this web app gave the api in `app.config.ts` → `apis`.
19
+ // `import type` is erased, so the binding costs the browser bundle nothing.
20
+ import { createHooks } from '@voltro/client'
21
+ import type { AppProcedures } from '@{{projectName}}/api/rpcGroup'
6
22
 
7
- export interface Project {
8
- readonly id: string
9
- readonly name: string
10
- readonly tenantId: string
11
- readonly createdAt: string | Date
12
- }
13
-
14
- export interface Invite {
15
- readonly id: string
16
- readonly email: string
17
- readonly status: string
18
- readonly tenantId: string
19
- readonly createdAt: string | Date
20
- }
21
-
22
- // `billing.subscription` streams `{ subscription: … | null }` — null before the
23
- // tenant has any subscription row (they're on the implicit free plan).
24
- export interface SubscriptionState {
25
- readonly subscription: {
26
- readonly plan: string
27
- readonly status: string
28
- readonly quantity: number
29
- readonly currentPeriodEnd: string | null
30
- readonly cancelAt: string | null
31
- } | null
32
- }
23
+ // Destructured on purpose — `react-hooks/rules-of-hooks` only treats a member
24
+ // call as a hook when the object is PascalCase, so top-level `useX` identifiers
25
+ // are what keep the React lint rules working at every call site.
26
+ export const { useSubscription, useMutation, useAction } = createHooks<AppProcedures>('app')
33
27
 
34
- // The caller's resolved identity, from `session.me`. The SSR gate reads it.
28
+ // The caller's resolved identity, from `session.me`. The SSR gate reads it, and
29
+ // a LOADER is not a hook — it calls `ctx.query` — so this shape stays hand-written.
35
30
  export interface Identity {
36
31
  readonly type: string
37
32
  readonly id: string | null
@@ -6,10 +6,9 @@
6
6
  // row yet means the tenant is on the implicit free plan.
7
7
  import type { ReactNode } from 'react'
8
8
  import type { PageMeta } from '@voltro/web'
9
- import { useSubscription, useAction } from '@voltro/client'
10
9
  import { T, useT } from '@voltro/i18n'
11
10
  import { getCatalog } from '../../../locales/index'
12
- import type { SubscriptionState } from '../../../lib/api'
11
+ import { useAction, useSubscription } from '../../../lib/api'
13
12
 
14
13
  export const renderMode = 'ssr' as const
15
14
 
@@ -17,13 +16,13 @@ export const meta = ({ locale }: { readonly locale: string }): PageMeta => ({
17
16
  title: getCatalog(locale)['meta.billing.title'],
18
17
  })
19
18
 
20
- interface CheckoutInput { plan: string; successUrl: string; cancelUrl: string }
21
- interface SeatsInput { quantity: number }
22
-
23
19
  export default function Billing(): ReactNode {
24
- const { data } = useSubscription<SubscriptionState>('app', 'billing.subscription')
25
- const checkout = useAction<CheckoutInput, { url: string }>('app', 'billing.startCheckout')
26
- const seats = useAction<SeatsInput, unknown>('app', 'billing.changeSeats')
20
+ // `billing.*` is contributed by @voltro/plugin-billing and lands in the same
21
+ // generated map as the app's own procedures, so a plugin route is typed here
22
+ // exactly like a hand-written one — no local Checkout/Seats input mirrors.
23
+ const { data } = useSubscription('billing.subscription')
24
+ const checkout = useAction('billing.startCheckout')
25
+ const seats = useAction('billing.changeSeats')
27
26
 
28
27
  const sub = data?.subscription ?? null
29
28
  const planLabel = useT('billing.plan.free')
@@ -1,7 +1,9 @@
1
1
  // @vitest-environment jsdom
2
2
  //
3
- // Projects page — the live list + the paywall branch. We mock `@voltro/client`
4
- // (useSubscription streams the rows; useMutation is the create) and drive the
3
+ // Projects page — the live list + the paywall branch. We mock the app's TYPED
4
+ // hook binding `src/lib/api.ts` (useSubscription streams the rows; useMutation
5
+ // is the create) — mocking `@voltro/client` underneath it would leave the real
6
+ // `createHooks` running over a stub, i.e. the wrong seam. We drive the
5
7
  // create form, asserting: the SSR'd rows render, the empty state shows, and a
6
8
  // `projects.create` that rejects with a typed `EntitlementExceeded` flips the
7
9
  // page into the upgrade CTA (NOT a raw error).
@@ -14,7 +16,15 @@ import enCatalog from '../../locales/en'
14
16
 
15
17
  const subData = vi.fn<() => { data: unknown }>()
16
18
  const mutate = vi.fn<(input: { name: string }) => Promise<unknown>>()
17
- vi.mock('@voltro/client', () => ({
19
+ // Only the HOOKS are faked. `isEntitlementExceeded` comes through from the real
20
+ // module — an earlier version of this file re-implemented it inside the mock,
21
+ // which meant the paywall test asserted that a copy of the predicate agreed
22
+ // with the literal the same test had just written. That passes in every world,
23
+ // including the one where the framework hands `catch` a value with no `_tag`
24
+ // at all. `import type` is erased, so importing the original pulls in nothing
25
+ // but `@voltro/client`.
26
+ vi.mock('../../lib/api', async (importOriginal) => ({
27
+ ...(await importOriginal<typeof import('../../lib/api')>()),
18
28
  useSubscription: () => subData(),
19
29
  useMutation: () => ({ mutate, pending: false, error: undefined, data: undefined }),
20
30
  }))
@@ -104,10 +114,24 @@ describe('projects — paywall', () => {
104
114
  })
105
115
 
106
116
  test('a typed EntitlementExceeded flips the page into the upgrade CTA', async () => {
117
+ // The rejection value is the TYPED ERROR ITSELF — that is the framework's
118
+ // guarantee for `mutate` / `run` / `signal` / `upload`, and the whole reason
119
+ // this page can branch on `_tag` instead of string-matching a message.
107
120
  mutate.mockRejectedValue({ _tag: 'EntitlementExceeded', entitlement: 'projects', limit: 3 })
108
121
  render(createElement(Projects))
109
122
  await submit()
110
123
  expect(container.querySelector('.paywall')).not.toBeNull()
111
124
  expect(container.textContent).toContain('hit your plan limit')
112
125
  })
126
+
127
+ test('an UNTAGGED failure does not paywall — that branch is for a declared outcome', async () => {
128
+ // The negative control, and the exact state the app was in while a rejected
129
+ // write handed back a wrapper with no `_tag`: the create fails, and the user
130
+ // is shown nothing about why. A paywall that appears for every failure is
131
+ // as wrong as one that never appears.
132
+ mutate.mockRejectedValue(new Error('socket hang up'))
133
+ render(createElement(Projects))
134
+ await submit()
135
+ expect(container.querySelector('.paywall')).toBeNull()
136
+ })
113
137
  })
@@ -7,10 +7,9 @@
7
7
  import type { ReactNode } from 'react'
8
8
  import { useState, type FormEvent } from 'react'
9
9
  import type { PageMeta } from '@voltro/web'
10
- import { useSubscription, useMutation } from '@voltro/client'
11
10
  import { T, useT } from '@voltro/i18n'
12
11
  import { getCatalog } from '../../locales'
13
- import { isEntitlementExceeded, type Project } from '../../lib/api'
12
+ import { isEntitlementExceeded, useMutation, useSubscription } from '../../lib/api'
14
13
 
15
14
  export const renderMode = 'ssr' as const
16
15
 
@@ -19,8 +18,9 @@ export const meta = ({ locale }: { readonly locale: string }): PageMeta => ({
19
18
  })
20
19
 
21
20
  export default function Projects(): ReactNode {
22
- const { data: live } = useSubscription<ReadonlyArray<Project>>('app', 'projects.list')
23
- const { mutate, pending } = useMutation<{ name: string }, Project>('app', 'projects.create')
21
+ // Tag + row type both come from the generated procedure map — no annotation.
22
+ const { data: live } = useSubscription('projects.list')
23
+ const { mutate, pending } = useMutation('projects.create')
24
24
  const projects = live ?? []
25
25
  const [name, setName] = useState('')
26
26
  const [paywalled, setPaywalled] = useState(false)
@@ -6,10 +6,9 @@
6
6
  import type { ReactNode } from 'react'
7
7
  import { useState, type FormEvent } from 'react'
8
8
  import type { PageMeta } from '@voltro/web'
9
- import { useSubscription, useMutation } from '@voltro/client'
10
9
  import { T, useT } from '@voltro/i18n'
11
10
  import { getCatalog } from '../../../locales/index'
12
- import { isEntitlementExceeded, type Invite } from '../../../lib/api'
11
+ import { isEntitlementExceeded, useMutation, useSubscription } from '../../../lib/api'
13
12
 
14
13
  export const renderMode = 'ssr' as const
15
14
 
@@ -24,8 +23,8 @@ const STATUS_KEY: Record<string, 'team.status.pending' | 'team.status.accepted'
24
23
  }
25
24
 
26
25
  export default function Team(): ReactNode {
27
- const { data: live } = useSubscription<ReadonlyArray<Invite>>('app', 'invites.list')
28
- const { mutate, pending } = useMutation<{ email: string }, Invite>('app', 'invites.create')
26
+ const { data: live } = useSubscription('invites.list')
27
+ const { mutate, pending } = useMutation('invites.create')
29
28
  const invites = live ?? []
30
29
  const [email, setEmail] = useState('')
31
30
  const [outOfSeats, setOutOfSeats] = useState(false)
@@ -12,20 +12,22 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/cli": "0.33.0",
16
- "@voltro/client": "0.33.0",
17
- "@voltro/env": "0.33.0",
18
- "@voltro/i18n": "0.33.0",
19
- "@voltro/ui-shadcn": "0.33.0",
20
- "@voltro/web": "0.33.0",
15
+ "@voltro/cli": "0.35.0",
16
+ "@voltro/client": "0.35.0",
17
+ "@voltro/env": "0.35.0",
18
+ "@voltro/i18n": "0.35.0",
19
+ "@voltro/ui-shadcn": "0.35.0",
20
+ "@voltro/web": "0.35.0",
21
21
  "react": "^19.0.0",
22
22
  "react-dom": "^19.0.0"
23
23
  },
24
24
  "devDependencies": {
25
- "@voltro/devtools": "0.33.0",
25
+ "@voltro/devtools": "0.35.0",
26
26
  "@types/react": "^19.0.0",
27
27
  "@types/react-dom": "^19.0.0",
28
+ "jsdom": "^30.0.0",
28
29
  "typescript": "^6.0.3",
30
+ "@vitest/coverage-v8": "^4.1.10",
29
31
  "vitest": "^4.1.10"
30
32
  }
31
33
  }
@@ -12,20 +12,22 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/cli": "0.33.0",
16
- "@voltro/client": "0.33.0",
17
- "@voltro/env": "0.33.0",
18
- "@voltro/i18n": "0.33.0",
19
- "@voltro/ui-shadcn": "0.33.0",
20
- "@voltro/web": "0.33.0",
15
+ "@voltro/cli": "0.35.0",
16
+ "@voltro/client": "0.35.0",
17
+ "@voltro/env": "0.35.0",
18
+ "@voltro/i18n": "0.35.0",
19
+ "@voltro/ui-shadcn": "0.35.0",
20
+ "@voltro/web": "0.35.0",
21
21
  "react": "^19.0.0",
22
22
  "react-dom": "^19.0.0"
23
23
  },
24
24
  "devDependencies": {
25
- "@voltro/devtools": "0.33.0",
25
+ "@voltro/devtools": "0.35.0",
26
26
  "@types/react": "^19.0.0",
27
27
  "@types/react-dom": "^19.0.0",
28
+ "jsdom": "^30.0.0",
28
29
  "typescript": "^6.0.3",
30
+ "@vitest/coverage-v8": "^4.1.10",
29
31
  "vitest": "^4.1.10"
30
32
  }
31
33
  }
@@ -12,21 +12,23 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@{{projectName}}/api": "0.33.0",
16
- "@voltro/cli": "0.33.0",
17
- "@voltro/client": "0.33.0",
18
- "@voltro/env": "0.33.0",
19
- "@voltro/i18n": "0.33.0",
20
- "@voltro/ui-shadcn": "0.33.0",
21
- "@voltro/web": "0.33.0",
15
+ "@{{projectName}}/api": "0.35.0",
16
+ "@voltro/cli": "0.35.0",
17
+ "@voltro/client": "0.35.0",
18
+ "@voltro/env": "0.35.0",
19
+ "@voltro/i18n": "0.35.0",
20
+ "@voltro/ui-shadcn": "0.35.0",
21
+ "@voltro/web": "0.35.0",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
24
24
  },
25
25
  "devDependencies": {
26
- "@voltro/devtools": "0.33.0",
26
+ "@voltro/devtools": "0.35.0",
27
27
  "@types/react": "^19.0.0",
28
28
  "@types/react-dom": "^19.0.0",
29
+ "jsdom": "^30.0.0",
29
30
  "typescript": "^6.0.3",
31
+ "@vitest/coverage-v8": "^4.1.10",
30
32
  "vitest": "^4.1.10"
31
33
  }
32
34
  }
@@ -12,19 +12,21 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/cli": "0.33.0",
16
- "@voltro/client": "0.33.0",
17
- "@voltro/env": "0.33.0",
18
- "@voltro/i18n": "0.33.0",
19
- "@voltro/web": "0.33.0",
15
+ "@voltro/cli": "0.35.0",
16
+ "@voltro/client": "0.35.0",
17
+ "@voltro/env": "0.35.0",
18
+ "@voltro/i18n": "0.35.0",
19
+ "@voltro/web": "0.35.0",
20
20
  "react": "^19.0.0",
21
21
  "react-dom": "^19.0.0"
22
22
  },
23
23
  "devDependencies": {
24
- "@voltro/devtools": "0.33.0",
24
+ "@voltro/devtools": "0.35.0",
25
25
  "@types/react": "^19.0.0",
26
26
  "@types/react-dom": "^19.0.0",
27
+ "jsdom": "^30.0.0",
27
28
  "typescript": "^6.0.3",
29
+ "@vitest/coverage-v8": "^4.1.10",
28
30
  "vitest": "^4.1.10"
29
31
  }
30
32
  }
@@ -12,21 +12,23 @@
12
12
  "test": "voltro test"
13
13
  },
14
14
  "dependencies": {
15
- "@{{projectName}}/api": "0.33.0",
16
- "@voltro/cli": "0.33.0",
17
- "@voltro/client": "0.33.0",
18
- "@voltro/env": "0.33.0",
19
- "@voltro/i18n": "0.33.0",
20
- "@voltro/ui-shadcn": "0.33.0",
21
- "@voltro/web": "0.33.0",
15
+ "@{{projectName}}/api": "0.35.0",
16
+ "@voltro/cli": "0.35.0",
17
+ "@voltro/client": "0.35.0",
18
+ "@voltro/env": "0.35.0",
19
+ "@voltro/i18n": "0.35.0",
20
+ "@voltro/ui-shadcn": "0.35.0",
21
+ "@voltro/web": "0.35.0",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
24
24
  },
25
25
  "devDependencies": {
26
- "@voltro/devtools": "0.33.0",
26
+ "@voltro/devtools": "0.35.0",
27
27
  "@types/react": "^19.0.0",
28
28
  "@types/react-dom": "^19.0.0",
29
+ "jsdom": "^30.0.0",
29
30
  "typescript": "^6.0.3",
31
+ "@vitest/coverage-v8": "^4.1.10",
30
32
  "vitest": "^4.1.10"
31
33
  }
32
34
  }
@@ -27,6 +27,7 @@ pnpm ios # or: pnpm android · pnpm start (Expo Go)
27
27
 
28
28
  | Path | What |
29
29
  |---|---|
30
+ | `src/lib/api.ts` | the **typed hook binding** — `createHooks<AppProcedures>('app')`, imported by every screen |
30
31
  | `src/lib/deeplinks.ts` | the typed deep-link table + pure resolver (**tested**) |
31
32
  | `src/lib/sync.ts` | the background-sync decision layer (**tested**) |
32
33
  | `src/lib/notifications.ts` | build a `_voltro_devices` row from a push token (**tested**) |
@@ -12,8 +12,8 @@
12
12
  "typecheck": "tsc --noEmit"
13
13
  },
14
14
  "dependencies": {
15
- "@voltro/client": "0.33.0",
16
- "@voltro/react-native": "0.33.0",
15
+ "@voltro/client": "0.35.0",
16
+ "@voltro/react-native": "0.35.0",
17
17
  "expo": "^52.0.0",
18
18
  "expo-constants": "~17.0.0",
19
19
  "expo-linking": "~7.0.0",
@@ -26,8 +26,10 @@
26
26
  "react-native": "0.76.5"
27
27
  },
28
28
  "devDependencies": {
29
+ "@{{projectName}}/api": "0.35.0",
29
30
  "@types/react": "^19.0.0",
30
31
  "typescript": "^6.0.3",
32
+ "@vitest/coverage-v8": "^4.1.10",
31
33
  "vitest": "^4.1.10"
32
34
  }
33
35
  }
@@ -1,22 +1,29 @@
1
1
  // Home screen — the reactive end-to-end loop, on a phone.
2
2
  //
3
- // The SAME hooks as the web frontend: `useSubscription('app', 'notes.list')`
4
- // streams the server-side query; `useMutation('app', 'notes.create')` writes it.
5
- // When the mutation lands, the server pushes a delta and this list re-renders —
6
- // no refetch, no polling. (Requires the M0 client boot; see ../client.ts.)
3
+ // The SAME hooks as the web frontend: `useSubscription('notes.list')` streams
4
+ // the server-side query; `useMutation('notes.create')` writes it. When the
5
+ // mutation lands, the server pushes a delta and this list re-renders — no
6
+ // refetch, no polling. (Requires the M0 client boot; see ../client.ts.)
7
+ //
8
+ // Both come from `../lib/api`, which binds them ONCE to the api's generated
9
+ // procedure map — so the tag autocompletes, a typo is a compile error, and the
10
+ // row type is inferred rather than declared here.
7
11
  import { useState } from 'react'
8
12
  import { Link } from 'expo-router'
9
13
  import { FlatList, Pressable, Text, TextInput, View } from 'react-native'
10
- import { useMutation, useSubscription } from '@voltro/client'
11
14
 
12
- interface Note {
13
- readonly id: string
14
- readonly title: string
15
- }
15
+ import { useMutation, useSubscription } from '../lib/api'
16
+
17
+ // The dev AuthMiddleware resolves the tenant from the `x-tenant` header,
18
+ // defaulting to 'acme'. `notes.create` guards that the submitted tenantId
19
+ // matches the caller's (a typed TenantMismatch otherwise).
20
+ const TENANT = 'acme'
16
21
 
17
22
  export default function Home() {
18
- const notes = useSubscription<ReadonlyArray<Note>>('app', 'notes.list')
19
- const createNote = useMutation<{ title: string }, Note>('app', 'notes.create')
23
+ // No row `interface` and no `<T>`: the shape comes from `notes.list`'s own
24
+ // output schema, including the client's `optimistic` marker.
25
+ const notes = useSubscription('notes.list')
26
+ const createNote = useMutation('notes.create')
20
27
  const [title, setTitle] = useState('')
21
28
 
22
29
  return (
@@ -32,7 +39,10 @@ export default function Home() {
32
39
  onPress={() => {
33
40
  if (title.trim().length === 0) return
34
41
  // Auto-optimistic: the list updates instantly, reconciles on ack.
35
- createNote.mutate({ title: title.trim() })
42
+ // `tenantId` and `body` are REQUIRED by `notes.create`'s input
43
+ // schema — the old hand-written `useMutation<{ title: string }>`
44
+ // annotation hid that, so this call decoded-failed at runtime.
45
+ createNote.mutate({ tenantId: TENANT, title: title.trim(), body: '' })
36
46
  setTitle('')
37
47
  }}
38
48
  style={{ backgroundColor: '#2563eb', borderRadius: 8, paddingHorizontal: 16, justifyContent: 'center' }}
@@ -12,7 +12,7 @@ export default function OrderDetail() {
12
12
  <Text style={{ fontSize: 20, fontWeight: '700' }}>Order {id}</Text>
13
13
  <Text style={{ color: '#666' }}>
14
14
  Reached via the typed deep-link table. Fetch this order with
15
- {' '}useSubscription('app', 'orders.get', {'{'} id {'}'}).
15
+ {' '}useSubscription('orders.get', {'{'} id {'}'}) from ../../lib/api.
16
16
  </Text>
17
17
  </View>
18
18
  )
@@ -0,0 +1,34 @@
1
+ // The typed hook surface for the `app` api — bound ONCE, here, and imported by
2
+ // every screen instead of `@voltro/client`.
3
+ //
4
+ // `AppProcedures` is emitted by codegen into the sibling api's
5
+ // `rpcGroup.generated.ts`: a type-level map of every rpc tag to the descriptor
6
+ // behind it. Feeding it to `createHooks` makes the tag a literal union and the
7
+ // input/output types INFER:
8
+ //
9
+ // const notes = useSubscription('notes.list') // rows, typed
10
+ // const create = useMutation('notes.create') // input + output typed
11
+ //
12
+ // So no screen declares a row `interface` mirroring the api any more. The old
13
+ // `useSubscription<ReadonlyArray<Note>>('app', 'notes.list')` spelled the api
14
+ // name at every call site, left the tag an unchecked string (a typo was a
15
+ // runtime error), and the `<T>` was an ASSERTION nothing compared against the
16
+ // server. All three are compile-checked now.
17
+ //
18
+ // `'app'` is the key this project's api is registered under. The api is a
19
+ // type-only import — `import type` is erased, so Metro never resolves it and
20
+ // nothing server-side reaches the bundle. It is a devDependency for exactly
21
+ // that reason; scaffold the pair (`create-project --api=api-backend --mobile`),
22
+ // which is what this template's README already assumes.
23
+ //
24
+ // RUNTIME NOTE: the hooks are typed today and live once the mobile codegen
25
+ // target lands — `buildApiHandles()` in ../client.ts still returns an empty map
26
+ // (see the seam documented there), so screens render their loading state until
27
+ // then. The TYPES are real either way; they come from the api's own descriptors.
28
+ import { createHooks } from '@voltro/client'
29
+ import type { AppProcedures } from '@{{projectName}}/api/rpcGroup'
30
+
31
+ // Destructured on purpose — `react-hooks/rules-of-hooks` only treats a member
32
+ // call as a hook when the object is PascalCase, so top-level `useX` identifiers
33
+ // are what keep the React lint rules working at every call site.
34
+ export const { useSubscription, useMutation, useAction } = createHooks<AppProcedures>('app')
@@ -1,6 +1,8 @@
1
1
  // The api(s) this mobile app consumes — mirrors a web app.config.ts's `apis:`
2
- // map. The NAME ('app') is the lookup key the hooks use:
3
- // `useSubscription('app', 'notes.list')`, `useMutation('app', 'notes.create')`.
2
+ // map. The NAME ('app') is the lookup key the hooks bind to: `src/lib/api.ts`
3
+ // spells it ONCE via `createHooks<AppProcedures>('app')`, and every screen then
4
+ // calls `useSubscription('notes.list')` / `useMutation('notes.create')` with no
5
+ // api name and no hand-written result type.
4
6
  // The `package` resolves the sibling api in this project so codegen can pull its
5
7
  // typed rpc surface (end-to-end types on a phone). Scaffold the api with
6
8
  // `--api=api-backend`.
@@ -5,6 +5,20 @@
5
5
  # override .env values, so CI can set DB_URL via env without touching
6
6
  # this file.
7
7
 
8
+ # ── Environment ────────────────────────────────────────────────────────
9
+ #
10
+ # DECLARE IT. Every `voltro db …` / `voltro migrate` invocation resolves an
11
+ # UNSET `NODE_ENV` to `production` — the same way `voltro serve` and `voltro
12
+ # start` do — so a bare `pnpm migrate` with no NODE_ENV refuses rather than
13
+ # applying an un-reviewed diff to what might be a production database. It also
14
+ # decides which `_voltro_*` bookkeeping tables the command declares, and a
15
+ # migration command that resolves it differently from the serving process
16
+ # declares a DIFFERENT schema.
17
+ #
18
+ # `voltro dev` declares `development` for itself and needs nothing from here;
19
+ # this line is what makes the explicit schema commands work locally.
20
+ NODE_ENV=development
21
+
8
22
  # Postgres connection. Bring your own (host install, docker run,
9
23
  # cloud provider URL).
10
24
  DB_URL=postgres://app:app@localhost:5432/{{projectName}}
@@ -23,9 +23,9 @@
23
23
  "deploy/voltro.service.example"
24
24
  ],
25
25
  "packageJsonScripts": {
26
- "dev": "turbo run dev",
27
- "build": "turbo run build",
28
- "test": "turbo run test",
29
- "typecheck": "turbo run typecheck"
26
+ "dev": "pnpm -r --parallel dev",
27
+ "build": "pnpm -r build",
28
+ "test": "pnpm -r test",
29
+ "typecheck": "pnpm -r typecheck"
30
30
  }
31
31
  }
@@ -5,6 +5,20 @@
5
5
  # override .env values, so CI can set DB_URL via env without touching
6
6
  # this file.
7
7
 
8
+ # ── Environment ────────────────────────────────────────────────────────
9
+ #
10
+ # DECLARE IT. Every `voltro db …` / `voltro migrate` invocation resolves an
11
+ # UNSET `NODE_ENV` to `production` — the same way `voltro serve` and `voltro
12
+ # start` do — so a bare `pnpm migrate` with no NODE_ENV refuses rather than
13
+ # applying an un-reviewed diff to what might be a production database. It also
14
+ # decides which `_voltro_*` bookkeeping tables the command declares, and a
15
+ # migration command that resolves it differently from the serving process
16
+ # declares a DIFFERENT schema.
17
+ #
18
+ # `voltro dev` declares `development` for itself and needs nothing from here;
19
+ # this line is what makes the explicit schema commands work locally.
20
+ NODE_ENV=development
21
+
8
22
  # Postgres connection — points at the postgres service in
9
23
  # docker-compose.yml (port 5433 to avoid clashing with a host pg on 5432).
10
24
  # When you run inside the docker network (dev:docker / prod:up), the