@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
@@ -23,9 +23,13 @@ Everything in it is in-memory and synchronous-friendly — no docker, no running
23
23
 
24
24
  | Export | Use |
25
25
  |---|---|
26
- | `makeTestContext(options?)` | The request `ctx` a handler/tool sees at runtime — subject-scoped `ctx.store`, `clock`, `email`, `webhooks`, optional `ai`. → [Unit testing](/docs/testing/unit-testing) |
26
+ | `makeTestContext(options?)` | The request `ctx` a handler/tool sees at runtime — subject-scoped `ctx.store`, `clock`, `webhooks`, `llm`, optional `ai`. → [Unit testing](/docs/testing/unit-testing) |
27
27
  | `mockStore(seed)` | Seed rows: `makeTestContext({ store: mockStore({ todos: [...] }) })`. |
28
- | `MockClock` / `MockEmail` / `MockLLM` | Deterministic clock, captured email, queued LLM responses. |
28
+ | `user(id, opts?)` / `apiKey` / `serviceAccount` / `anonymous` / `system` | Subject factories — the acting identity, instead of a hand-written `{ type: 'user', }` literal. |
29
+ | `MockClock` / `MockLLM` | Deterministic clock, queued LLM responses. |
30
+ | `invoke(descriptor, handler, input, ctx)` | Run a procedure the way the dispatcher does — guards, decode, transaction, plugin interceptors. → [Unit testing](/docs/testing/unit-testing) |
31
+ | `makeTestApp({ ctx, restRoutes, publicApi, strategies })` | The request-level harness — a real HTTP request through the real REST pipeline. → [Unit testing](/docs/testing/unit-testing#request-level-testing-maketestapp) |
32
+ | `fixtureRow(table, over?)` / `defineFactory(table, opts?)` | Complete a required-column row; a named factory with defaults, traits and associations. |
29
33
  | `makeVoltroTestClient(config)` | The frontend harness — render a component against mocked `useSubscription` / `useMutation`, from the `@voltro/testing/client` subpath. → [Component testing](/docs/testing/component-testing) |
30
34
  | `makeWorkflowRunner({ ctx, workflows })` | Drive a workflow in-process and assert on its steps. → [Workflows](/docs/testing/workflows) |
31
35
  | `runDialectParity(fixture)` | Run one fixture across a SQL dialect for portability — from the `@voltro/testing/dialect` subpath. → [Dialect parity](/docs/testing/dialect-parity) |
@@ -35,10 +39,11 @@ Everything in it is in-memory and synchronous-friendly — no docker, no running
35
39
  Voltro's primitives are designed so the cheapest test covers the most surface. Reach for the lowest layer that proves what you need:
36
40
 
37
41
  1. **Unit — handlers, tools, mutations, queries.** Call the executor directly with a `makeTestContext()` ctx. The store is the real mixin-wrapped store, so tenant scoping, soft-delete, and audit auto-fill all behave as in production — without a database. This is 90% of your tests. See [Unit testing](/docs/testing/unit-testing).
38
- 2. **Component — the reactive frontend.** `makeVoltroTestClient` renders a component against mocked `useSubscription` / `useMutation`, so loading, empty, stream-error and failing-write branches are all assertable without a running server. See [Component testing](/docs/testing/component-testing).
39
- 3. **Workflowdurable, multi-step logic.** `makeWorkflowRunner` drives a workflow end-to-end in-process and hands back the same step log the dashboard shows, including real retry counts. See [Workflows](/docs/testing/workflows).
40
- 4. **Parityhand-written SQL that must stay portable.** `runDialectParity` runs a fixed scenario suite against a dialect you stand up. Mostly for dialect-package authors and apps with `unsafe()` SQL. See [Dialect parity](/docs/testing/dialect-parity).
41
- 5. **End-to-endthe running stack.** `voltro e2e` boots the api + web siblings, runs your tests against the live processes, and tears them down.
42
+ 2. **Request — the transport hop.** `makeTestApp` sends a real request through the framework's own REST pipeline: the `x-tenant` header, the auth strategy chain, path-param decode, the 405/404/410 the transport produces, HTTP idempotency, and a `publicApi` annotation's scopes. Everything a handler test structurally cannot reach, and still no server, no port, no docker. See [Unit testing → request-level testing](/docs/testing/unit-testing#request-level-testing-maketestapp).
43
+ 3. **Componentthe reactive frontend.** `makeVoltroTestClient` renders a component against mocked `useSubscription` / `useMutation`, so loading, empty, stream-error and failing-write branches are all assertable without a running server. See [Component testing](/docs/testing/component-testing).
44
+ 4. **Workflowdurable, multi-step logic.** `makeWorkflowRunner` drives a workflow end-to-end in-process and hands back the same step log the dashboard shows, including real retry counts. See [Workflows](/docs/testing/workflows).
45
+ 5. **Parityhand-written SQL that must stay portable.** `runDialectParity` runs a fixed scenario suite against a dialect you stand up. Mostly for dialect-package authors and apps with `unsafe()` SQL. See [Dialect parity](/docs/testing/dialect-parity).
46
+ 6. **End-to-end — the running stack.** `voltro e2e` boots the api + web siblings, runs each `e2e/**/*.spec.ts` as a plain tsx script against the live processes (with `WEB_URL` + `API_URL` in the environment), and tears them down. No test runner and no browser ship with it — bring your own driver. See [`voltro e2e`](/docs/cli/inspect#voltro-e2e).
42
47
 
43
48
  Agents are integration tests; tools are unit tests. Don't reach for an e2e harness to assert logic a `makeTestContext` test can prove.
44
49
 
@@ -66,7 +71,7 @@ voltro e2e # boot api + web siblings, run tests, tear down
66
71
  <!-- source: en/testing/unit-testing.md -->
67
72
  ## Unit testing
68
73
 
69
- _makeTestContext — the in-memory request ctx for handler and tool tests. The real mixin-wrapped store, mockStore seeding, subject/tenant re-scoping, and the deterministic clock / email / LLM mocks._
74
+ _makeTestContext, invoke and makeTestApp — the in-memory request ctx, the handler hop and the transport hop. The real mixin-wrapped store, subject factories, row factories, plugin services, and the deterministic clock / webhook / LLM doubles._
70
75
 
71
76
  ## tsconfig `paths` aliases
72
77
 
@@ -144,8 +149,30 @@ test('searchDocs returns matching rows', async () => {
144
149
  | `env` | ambient `process.env` | Env values sealed into the boot snapshot so handler code reading `getSecret('X')` / `serverEnv.X` resolves under test. Merged over `process.env` (these win). |
145
150
  | `relations` | — | `relations()` specs to register for this context — the boot sweep's stand-in. See [Eager loads under test](#eager-loads-under-test). |
146
151
  | `rowFilter` | the registered filter | A row filter for this context only, instead of the process-global `setRowFilter(...)`. See [Row-level security under test](#row-level-security-under-test). |
152
+ | `plugins` | `[]` | Plugins whose rpc interceptors wrap every `invoke` **and** whose `services:` layer is provided to Effect-mode handlers. See [Plugin services](#plugin-services-mail-storage-your-own). |
153
+ | `layers` | `[]` | Your app's own `layers:` from `app.config.ts` — the Effect services a handler `yield*`s. Merged last, so a user layer overrides a plugin layer declaring the same Tag. |
147
154
 
148
- The returned `TestContext` carries `{ clock, email, llm, ai?, request, cache, store, withSubject, withTenant }` — read the acting subject at `ctx.request.subject` and the in-memory cache at `ctx.cache`.
155
+ The returned `TestContext` carries `{ clock, webhooks, llm, ai?, request, access, cache, kv, store, storeForTenant, outbox, load, loadMany, withSubject, withTenant }` — read the acting subject at `ctx.request.subject` and the in-memory cache at `ctx.cache`.
156
+
157
+ ### Subjects — `user()`, `apiKey()`, `anonymous()`, `system()`
158
+
159
+ Every authenticated `Subject` requires a `tenantId`, so a test that hand-writes `{ type: 'user', id: 'u1', tenantId: 't1', scopes: [] }` is restating the same four fields at every call site. Use the factories:
160
+
161
+ ```ts
162
+ import { user, apiKey, serviceAccount, anonymous, system, TEST_TENANT_ID } from '@voltro/testing'
163
+
164
+ user('u1') // tenant TEST_TENANT_ID, NO scopes → guards deny
165
+ user('u1', { scopes: ['notes:write'] }) // the caller a guard should let through
166
+ user('u2', { tenantId: 'other' }) // a different tenant — the store hides the first one's rows
167
+ apiKey('key_1', { scopes: ['orders:read'] }) // what a Bearer key resolves to
168
+ anonymous('acme') // unauthenticated, tenant from the `x-tenant` header
169
+ system('job:nightly') // the CROSS-tenant machine actor (tenantId null, admin:full)
170
+ ```
171
+
172
+ Two decisions worth knowing, because they decide what your tests mean:
173
+
174
+ - **The default tenant is shared** (`TEST_TENANT_ID`). `user('a')` and `user('b')` are two members of ONE tenant, so a cross-subject read is a row-level question. A per-call-unique tenant would make the store hide everything and every such test would pass for the wrong reason.
175
+ - **`scopes` defaults to empty, not to a bypass.** `user('b')` is refused by any `guards:` — which is the assertion most negative tests are written to make.
149
176
 
150
177
  ## `invoke` — run a handler through its guards, its input Schema, and its transaction
151
178
 
@@ -343,8 +370,96 @@ Retries are always on and carry no backoff here. Production's jitter exists to d
343
370
 
344
371
  ### What `invoke` still does NOT cover
345
372
 
346
- - **Transport concerns** — connection info, rate limiting, the tenant header. Those are properties of the HTTP hop, not of the procedure; faking them here would only assert against the fake.
373
+ - **Transport concerns** — connection info, rate limiting, the tenant header. Those are properties of the HTTP hop, not of the procedure; faking them here would only assert against the fake. Reach one level up for them: [`makeTestApp`](#request-level-testing-maketestapp) drives a real request through the real REST pipeline and runs the procedure through `invoke` underneath.
347
374
  - **Undo capture and the metrics sample** — optional injections the serve entrypoint makes. Their absence changes nothing a handler can observe.
375
+ - **The CLI-built base services** — `Cache`, `Kv`, analytics, the outbound `HttpClient`, the aggregate registry. The CLI builds those at boot from your config, and `@voltro/testing` does not depend on the CLI, so a handler that `yield*`s one gets the ordinary "Service not found". Plugin services and your own `layers:` DO resolve — see below.
376
+
377
+ ## Plugin services — mail, storage, your own
378
+
379
+ A plugin contributes an Effect **service**, not a `ctx` field: a handler writes `const mail = yield* MailService`. Register the plugin on the context and `invoke` provides its `services:` layer, exactly as the serve pipeline does — so the handler runs, and you assert on what the plugin actually recorded:
380
+
381
+ ```ts
382
+ import { invoke, makeTestContext, user } from '@voltro/testing'
383
+ import { mailPlugin, readMailBuffer, clearMailBuffer } from '@voltro/plugin-mail'
384
+ import { sendWelcome } from '../actions/welcome.action'
385
+ import sendWelcomeHandler from '../actions/welcome.action.server'
386
+
387
+ beforeEach(() => { clearMailBuffer() })
388
+
389
+ test('signing up sends the welcome email', async () => {
390
+ const ctx = makeTestContext({
391
+ subject: user('u1'),
392
+ plugins: [mailPlugin({ provider: 'memory', from: 'app@acme.com' })],
393
+ })
394
+
395
+ await invoke(sendWelcome, sendWelcomeHandler, { to: 'ada@acme.com' }, ctx)
396
+
397
+ const sent = readMailBuffer()
398
+ expect(sent).toHaveLength(1)
399
+ expect(sent[0].to).toEqual(['ada@acme.com'])
400
+ expect(sent[0].subject).toBe('Welcome')
401
+ })
402
+ ```
403
+
404
+ `provider: 'memory'` is the mail plugin's own test adapter, and `readMailBuffer()` / `clearMailBuffer()` its own assertion surface — so the whole send path runs: the default `from`, the dev allowlist, suppression, per-send idempotency, the template render. There is deliberately **no `ctx.email` mock**: a double in `@voltro/testing` would model a weaker message than `MailService` accepts, and asserting against it would be asserting against the double.
405
+
406
+ A service nobody provided still fails with **"Service not found"**, which is the honest outcome — it is what the handler would do at runtime, not something to paper over.
407
+
408
+ Your own services work the same way, through `layers:`:
409
+
410
+ ```ts
411
+ const ctx = makeTestContext({ layers: [Layer.succeed(Pricing, { quote: () => 499 })] })
412
+ ```
413
+
414
+ ## Request-level testing — `makeTestApp`
415
+
416
+ `invoke` is the handler hop. `makeTestApp` is the one above it: an actual request through the framework's own REST pipeline — no server, no port, no docker.
417
+
418
+ ```ts
419
+ import { makeTestApp, makeTestContext, anonymous, user } from '@voltro/testing'
420
+ import { listOrders } from '../queries/orders.list.query'
421
+ import listOrdersHandler from '../queries/orders.list.query.server'
422
+
423
+ const ctx = makeTestContext({ subject: anonymous(), store: mockStore({ orders: [{ id: 'o1', tenantId: 'acme' }] }) })
424
+ const app = makeTestApp({
425
+ ctx,
426
+ publicApi: [{ descriptor: listOrders, handler: listOrdersHandler }],
427
+ restRoutes: [healthRoute],
428
+ strategies: [myBearerStrategy],
429
+ })
430
+
431
+ // the transport gate: the publicApi annotation's scopes refuse an unscoped caller
432
+ expect((await app.get('/v1/orders/list')).status).toBe(403)
433
+
434
+ // acting as someone who holds the scope
435
+ const res = await app.actingAs(user('u1', { tenantId: 'acme', scopes: ['orders:read'] })).get('/v1/orders/list')
436
+ expect(res.status).toBe(200)
437
+ expect(res.body).toEqual({ ids: ['o1'] })
438
+
439
+ // how an identity is RESOLVED — headers, not an injected subject
440
+ const viaHeader = await app.withHeaders({ 'x-tenant': 'acme', authorization: 'Bearer k1' }).get('/v1/orders/list')
441
+ ```
442
+
443
+ The two ways to say who is calling are different questions, and the harness keeps them apart:
444
+
445
+ - **`actingAs(subject)`** fills the same `resolveSubject` seam the serve pipeline fills with its auth resolver. Use it to test what a given identity *may do*.
446
+ - **`withHeaders({...})`** and no `actingAs` runs the request through `composeAuthStrategies` — your real strategy chain, first-match-wins, the `x-tenant` fallback, `anonymousTenantRequired`. Use it to test how an identity *is resolved*.
447
+
448
+ Both return a derived app; neither mutates the one it came from.
449
+
450
+ | Method | |
451
+ |---|---|
452
+ | `app.get(path, opts?)` | Query string goes in the path: `app.get('/v1/orders?limit=2')`. |
453
+ | `app.post(path, body?, opts?)` | `put` / `patch` / `delete` likewise. `body` is JSON-encoded. |
454
+ | `app.request(method, path, opts?)` | The general form. |
455
+ | `app.actingAs(subject)` / `app.withHeaders(h)` | Derived views. |
456
+ | `app.paths` | Every mounted path, in mount order. |
457
+
458
+ A `TestResponse` is `{ status, headers, text, body, stream }` — `body` is the parsed JSON when the route answered with a JSON content-type.
459
+
460
+ What it runs is the framework's own code, not a re-implementation: `restRoutesToHttpRoutes` (method gate, sunset gate, `{ query, params, body }` assembly, input decode, guards, HTTP idempotency, output encode), `collectPublicApiRoutes` (the descriptor → REST projection, `scopes` → guards), `dispatchSharedPath` (the per-path dispatcher that lets a GET and a POST share one route), `composeAuthStrategies`, and `invoke` beneath a `publicApi` route — so the procedure's own guards, decode, transaction and interceptors still apply under the transport.
461
+
462
+ Two things it deliberately does not do. An auth strategy that **refuses** (`anonymousTenantRequired` with no tenant) makes the request-call REJECT rather than return a status: mapping an auth refusal onto an HTTP code is the server's job, and inventing a number here would be a number the harness made up. And there is no WebSocket / live-subscription lifecycle and no `POST /rpc` wire — those are dispatcher concerns the CLI owns; a `publicApi:` annotation is how a procedure gets an HTTP surface this harness can reach.
348
463
 
349
464
  ## Completing a fixture row — `fixtureRow`
350
465
 
@@ -372,6 +487,39 @@ What it fills and what it leaves alone:
372
487
 
373
488
  There is no flag to turn the validation off — a test store that accepts rows production rejects is a fake testing itself. `fixtureRow` is a runtime filler for the loose `store.insert(name, row)` path. For **compile-time** payload typing (a missing required column caught as a type error at the call), use [`insertRow` / `upsertRow`](/docs/data/mutations) from `@voltro/database`, which check against `InferInsertRow<T>`.
374
489
 
490
+ ### Factories — `defineFactory`
491
+
492
+ `fixtureRow` makes ONE row valid. What it cannot do is the part a fixture actually costs you: the **parent rows**. It fills a `reference()` column with a placeholder string, which satisfies the validator and points at nothing — invisible in the in-memory store, a constraint violation against a real database, and an eager `.with({ author: true })` that resolves to nothing either way.
493
+
494
+ `defineFactory` adds defaults, traits and associations on top:
495
+
496
+ ```ts
497
+ import { defineFactory, makeTestContext } from '@voltro/testing'
498
+ import { users, posts } from '../schema'
499
+
500
+ const userFactory = defineFactory(users, {
501
+ defaults: { name: 'Ada', email: (seq) => `user-${seq}@test.local` },
502
+ traits: { admin: { role: 'admin' } },
503
+ })
504
+ const postFactory = defineFactory(posts, {
505
+ defaults: { title: 'A post', views: 0 },
506
+ traits: { popular: { views: 10_000 } },
507
+ associations: { authorId: userFactory },
508
+ })
509
+
510
+ const ctx = makeTestContext({ subject })
511
+
512
+ const post = await postFactory.create(ctx.store) // an author IS created
513
+ const byAdmin = await postFactory.create(ctx.store, { authorId: (await userFactory.with('admin').create(ctx.store)).id })
514
+ const top3 = await postFactory.with('popular').createList(ctx.store, 3)
515
+ const built = postFactory.build() // pure — writes nothing
516
+ ```
517
+
518
+ - **Defaults** take a value or a `(seq) => value` function; `seq` is a process-monotonic counter shared with `fixtureRow`'s unique filler, so two sources of "unique enough" values can't collide.
519
+ - **Traits** are named override bundles, composed left to right (`with('a', 'b')` — `b` wins). An undeclared trait **throws** rather than quietly building the base row.
520
+ - **Associations** apply to `create` only. An ancestor is created only for a required `reference()` column your overrides leave unset — pass `{ authorId: existing.id }` and nothing extra is written. A column with no declared factory still gets its parent, built with a plain `fixtureRow`. A **cyclic** reference is refused, naming the path and the column to pass by hand.
521
+ - `build` / `buildList` are pure: they write nothing, so a `reference()` column gets `fixtureRow`'s placeholder. Use `create` when the relations matter.
522
+
375
523
  ## Subscribers — `makeSubscribeContext` + the change constructors
376
524
 
377
525
  A `*.subscribe.ts` handler does not receive an `AppContext`. It receives a
@@ -512,18 +660,67 @@ ctx.clock.now() // current instant in ms
512
660
  ctx.clock.date() // current instant as a Date
513
661
  ctx.clock.advance('15m') // jump forward — ms | s | m | h | d, or a number of ms
514
662
  ctx.clock.advance(900_000) // same, in milliseconds
663
+ ctx.clock.set('2026-03-01T12:00:00Z') // jump to an ABSOLUTE instant
664
+ ```
665
+
666
+ #### Freezing GLOBAL time — `withFrozenTime` / `frozenTime`
667
+
668
+ On its own a `MockClock` is a value you read: `Date.now()` and `new Date()` are
669
+ untouched. That matters more than it sounds, because almost nothing stamps a
670
+ timestamp through the clock you passed it — `MockWebhooks.emittedAt`, the mail
671
+ plugin's memory provider, every `createdAt` default and most of your own code
672
+ all call `new Date()`. Advance the mock clock, assert on one of those stamps,
673
+ and you are comparing two unrelated clocks: the assertion passes by measuring
674
+ the machine.
675
+
676
+ `withFrozenTime` takes over the realm's `Date` for the body and restores it
677
+ afterwards — the Rails `travel_to` / Laravel `Carbon::setTestNow()` move:
678
+
679
+ ```ts
680
+ import { withFrozenTime } from '@voltro/testing'
681
+
682
+ await withFrozenTime('2026-03-01T12:00:00Z', async (clock) => {
683
+ expect(new Date().toISOString()).toBe('2026-03-01T12:00:00.000Z')
684
+ await invoke(createTodo, { title: 'x' }, ctx) // its createdAt is that instant
685
+ clock.advance('1h') // global time moves with it
686
+ expect(Date.now()).toBe(Date.parse('2026-03-01T13:00:00Z'))
687
+ })
515
688
  ```
516
689
 
517
- ### `ctx.email``MockEmail`
690
+ It restores whether the body returns, throws, resolves or rejects an async
691
+ body is awaited *before* the clock goes back, so a `finally` cannot put the real
692
+ clock back underneath a still-running test.
518
693
 
519
- Captures outgoing email for assertion.
694
+ The Effect-native form freezes for a **scope** and releases on success, failure
695
+ and interruption alike:
520
696
 
521
697
  ```ts
522
- ctx.email.sent // SentEmail[] every send, in order
523
- ctx.email.lastTo('a@b.com') // the most recent email to that address, or undefined
524
- ctx.email.clear() // reset between cases
698
+ import { frozenTime } from '@voltro/testing'
699
+
700
+ Effect.scoped(Effect.gen(function* () {
701
+ const clock = yield* frozenTime('2026-03-01T12:00:00Z')
702
+ yield* somethingThatStampsNow
703
+ clock.advance('1d')
704
+ }))
525
705
  ```
526
706
 
707
+ `clock.install()` is the manual escape hatch and hands back the uninstall. Four
708
+ things worth knowing before you reach for it:
709
+
710
+ - **Only the zero-argument readings change.** `new Date(0)`, `new Date('2020-05-05')`
711
+ and `new Date(2020, 0, 2)` mean exactly what they say; `Date.parse` and
712
+ `Date.UTC` are untouched.
713
+ - **A second install throws.** Nesting two would make the inner uninstall restore
714
+ the *outer fake*, leaving the realm frozen with nothing pointing at why.
715
+ - **Timers are not faked.** `performance.now()` and `setTimeout` are unaffected —
716
+ use vitest's `vi.useFakeTimers()` for the timer wheel.
717
+ - **A module that captured `Date` into a local before the install keeps the real
718
+ one.** That is inherent to any time fake.
719
+
720
+ ### Email is not a double — see [Plugin services](#plugin-services-mail-storage-your-own)
721
+
722
+ There is no `ctx.email`. Mail is an Effect service (`yield* MailService`), so the assertion surface is the mail plugin's own memory provider — register `mailPlugin({ provider: 'memory' })` on the context and read `readMailBuffer()`.
723
+
527
724
  ### `ctx.webhooks` — `MockWebhooks`
528
725
 
529
726
  Records outgoing webhook emissions. Every context derived from this one — the transaction context `invoke` builds for a mutation, a `withSubject` / `withTenant` block — shares the same recorder, so what you assert on IS what the handler emitted.