@voltro/cli 0.1.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 (626) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/LICENSE +57 -0
  3. package/README.md +26 -0
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +20236 -0
  6. package/bin/voltro.mjs +43 -0
  7. package/dist/bin.d.ts +1 -0
  8. package/dist/bin.js +9 -0
  9. package/dist/commands-CfPH2Wf4.js +18061 -0
  10. package/dist/frameworkInspectState-CX2250XB.js +86 -0
  11. package/dist/index.d.ts +48 -0
  12. package/dist/index.js +5 -0
  13. package/dist/inspectState.d.ts +116 -0
  14. package/dist/inspectState.js +2 -0
  15. package/dist/startup.d.ts +26 -0
  16. package/dist/startup.js +2 -0
  17. package/dist/startupRunner-CRhuUl91.js +71 -0
  18. package/package.json +88 -0
  19. package/templates/AGENTS.core.md +258 -0
  20. package/templates/AGENTS.md +351 -0
  21. package/templates/agent-docs/_index.md +93 -0
  22. package/templates/agent-docs/_manifest.json +655 -0
  23. package/templates/agent-docs/ai.md +1845 -0
  24. package/templates/agent-docs/authentication.md +1788 -0
  25. package/templates/agent-docs/caching.md +624 -0
  26. package/templates/agent-docs/cli.md +1650 -0
  27. package/templates/agent-docs/configuration.md +295 -0
  28. package/templates/agent-docs/data.md +2172 -0
  29. package/templates/agent-docs/database/advancedqueries.md +1583 -0
  30. package/templates/agent-docs/database/columntypes.md +1200 -0
  31. package/templates/agent-docs/database/hosting.md +881 -0
  32. package/templates/agent-docs/database/migrations.md +2938 -0
  33. package/templates/agent-docs/database/misc.md +270 -0
  34. package/templates/agent-docs/database/overview.md +108 -0
  35. package/templates/agent-docs/database/querying.md +1622 -0
  36. package/templates/agent-docs/database/scaling.md +331 -0
  37. package/templates/agent-docs/database/schema.md +1458 -0
  38. package/templates/agent-docs/database/seedsdialects.md +1235 -0
  39. package/templates/agent-docs/database/transactions.md +285 -0
  40. package/templates/agent-docs/deployment.md +999 -0
  41. package/templates/agent-docs/internationalization.md +359 -0
  42. package/templates/agent-docs/introduction.md +438 -0
  43. package/templates/agent-docs/multi-tenancy.md +610 -0
  44. package/templates/agent-docs/observability.md +350 -0
  45. package/templates/agent-docs/plugins.md +1273 -0
  46. package/templates/agent-docs/reference.md +978 -0
  47. package/templates/agent-docs/routing.md +1553 -0
  48. package/templates/agent-docs/scheduling.md +666 -0
  49. package/templates/agent-docs/schema-driven-ui.md +607 -0
  50. package/templates/agent-docs/security.md +42 -0
  51. package/templates/agent-docs/templates/apibackends.md +3214 -0
  52. package/templates/agent-docs/templates/appshells.md +2062 -0
  53. package/templates/agent-docs/templates/custom.md +128 -0
  54. package/templates/agent-docs/templates/overview.md +122 -0
  55. package/templates/agent-docs/templates/serverless.md +315 -0
  56. package/templates/agent-docs/testing.md +376 -0
  57. package/templates/agent-docs/workflows.md +1351 -0
  58. package/templates/apps/api-ai/README.md +105 -0
  59. package/templates/apps/api-ai/actions/summarize.action.server.tsx +32 -0
  60. package/templates/apps/api-ai/actions/summarize.action.ts +33 -0
  61. package/templates/apps/api-ai/agents/support.agent.server.tsx +31 -0
  62. package/templates/apps/api-ai/agents/support.agent.tsx +27 -0
  63. package/templates/apps/api-ai/app.config.ts +57 -0
  64. package/templates/apps/api-ai/database/schema.ts +67 -0
  65. package/templates/apps/api-ai/package.json +27 -0
  66. package/templates/apps/api-ai/seeds/docs.seed.ts +67 -0
  67. package/templates/apps/api-ai/template.json +6 -0
  68. package/templates/apps/api-ai/tests/summarize.test.ts +50 -0
  69. package/templates/apps/api-ai/tools/searchDocs.tool.tsx +60 -0
  70. package/templates/apps/api-ai/tsconfig.json +5 -0
  71. package/templates/apps/api-auth/.env +17 -0
  72. package/templates/apps/api-auth/README.md +109 -0
  73. package/templates/apps/api-auth/actions/me.action.server.ts +20 -0
  74. package/templates/apps/api-auth/actions/me.action.ts +19 -0
  75. package/templates/apps/api-auth/app.config.ts +59 -0
  76. package/templates/apps/api-auth/database/schema.ts +26 -0
  77. package/templates/apps/api-auth/package.json +28 -0
  78. package/templates/apps/api-auth/template.json +6 -0
  79. package/templates/apps/api-auth/tests/me.test.ts +37 -0
  80. package/templates/apps/api-auth/tsconfig.json +5 -0
  81. package/templates/apps/api-backend/README.md +25 -0
  82. package/templates/apps/api-backend/app.config.ts +39 -0
  83. package/templates/apps/api-backend/database/schema.ts +57 -0
  84. package/templates/apps/api-backend/mutations/notes.create.mutation.server.ts +19 -0
  85. package/templates/apps/api-backend/mutations/notes.create.mutation.ts +37 -0
  86. package/templates/apps/api-backend/package.json +28 -0
  87. package/templates/apps/api-backend/queries/notes.query.server.ts +14 -0
  88. package/templates/apps/api-backend/queries/notes.query.ts +20 -0
  89. package/templates/apps/api-backend/template.json +6 -0
  90. package/templates/apps/api-backend/tests/notes.create.test.ts +50 -0
  91. package/templates/apps/api-backend/tsconfig.json +5 -0
  92. package/templates/apps/api-backend-deactivation/README.md +49 -0
  93. package/templates/apps/api-backend-deactivation/actions/users.get.action.server.ts +17 -0
  94. package/templates/apps/api-backend-deactivation/actions/users.get.action.ts +18 -0
  95. package/templates/apps/api-backend-deactivation/app.config.ts +20 -0
  96. package/templates/apps/api-backend-deactivation/database/schema.ts +41 -0
  97. package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.server.ts +6 -0
  98. package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.ts +16 -0
  99. package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.server.ts +10 -0
  100. package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.ts +19 -0
  101. package/templates/apps/api-backend-deactivation/package.json +28 -0
  102. package/templates/apps/api-backend-deactivation/template.json +6 -0
  103. package/templates/apps/api-backend-deactivation/tests/users.deactivate.test.ts +47 -0
  104. package/templates/apps/api-backend-deactivation/tsconfig.json +5 -0
  105. package/templates/apps/api-backend-mail/README.md +39 -0
  106. package/templates/apps/api-backend-mail/actions/sendWelcome.action.server.ts +15 -0
  107. package/templates/apps/api-backend-mail/actions/sendWelcome.action.ts +21 -0
  108. package/templates/apps/api-backend-mail/app.config.ts +34 -0
  109. package/templates/apps/api-backend-mail/database/schema.ts +57 -0
  110. package/templates/apps/api-backend-mail/emails/welcome.email.tsx +106 -0
  111. package/templates/apps/api-backend-mail/mutations/notes.create.mutation.server.ts +19 -0
  112. package/templates/apps/api-backend-mail/mutations/notes.create.mutation.ts +37 -0
  113. package/templates/apps/api-backend-mail/package.json +30 -0
  114. package/templates/apps/api-backend-mail/queries/notes.query.server.ts +14 -0
  115. package/templates/apps/api-backend-mail/queries/notes.query.ts +20 -0
  116. package/templates/apps/api-backend-mail/template.json +6 -0
  117. package/templates/apps/api-backend-mail/tests/notes.create.test.ts +50 -0
  118. package/templates/apps/api-backend-mail/tsconfig.json +5 -0
  119. package/templates/apps/api-backend-mariadb/.env.example +35 -0
  120. package/templates/apps/api-backend-mariadb/README.md +31 -0
  121. package/templates/apps/api-backend-mariadb/app.config.ts +68 -0
  122. package/templates/apps/api-backend-mariadb/database/schema.ts +57 -0
  123. package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.server.ts +19 -0
  124. package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.ts +37 -0
  125. package/templates/apps/api-backend-mariadb/package.json +30 -0
  126. package/templates/apps/api-backend-mariadb/queries/notes.query.server.ts +14 -0
  127. package/templates/apps/api-backend-mariadb/queries/notes.query.ts +20 -0
  128. package/templates/apps/api-backend-mariadb/template.json +6 -0
  129. package/templates/apps/api-backend-mariadb/tests/notes.create.test.ts +50 -0
  130. package/templates/apps/api-backend-mariadb/tsconfig.json +5 -0
  131. package/templates/apps/api-backend-storage/README.md +86 -0
  132. package/templates/apps/api-backend-storage/actions/uploadAvatar.action.server.ts +21 -0
  133. package/templates/apps/api-backend-storage/actions/uploadAvatar.action.ts +21 -0
  134. package/templates/apps/api-backend-storage/actions/uploadDocument.action.server.ts +21 -0
  135. package/templates/apps/api-backend-storage/actions/uploadDocument.action.ts +20 -0
  136. package/templates/apps/api-backend-storage/app.config.ts +38 -0
  137. package/templates/apps/api-backend-storage/database/schema.ts +57 -0
  138. package/templates/apps/api-backend-storage/mutations/notes.create.mutation.server.ts +19 -0
  139. package/templates/apps/api-backend-storage/mutations/notes.create.mutation.ts +37 -0
  140. package/templates/apps/api-backend-storage/package.json +27 -0
  141. package/templates/apps/api-backend-storage/queries/notes.query.server.ts +14 -0
  142. package/templates/apps/api-backend-storage/queries/notes.query.ts +20 -0
  143. package/templates/apps/api-backend-storage/template.json +6 -0
  144. package/templates/apps/api-backend-storage/tests/notes.create.test.ts +50 -0
  145. package/templates/apps/api-backend-storage/tsconfig.json +5 -0
  146. package/templates/apps/api-data-advanced/.env +17 -0
  147. package/templates/apps/api-data-advanced/README.md +88 -0
  148. package/templates/apps/api-data-advanced/app.config.ts +56 -0
  149. package/templates/apps/api-data-advanced/database/actors.entity.ts +14 -0
  150. package/templates/apps/api-data-advanced/database/authors.entity.ts +30 -0
  151. package/templates/apps/api-data-advanced/database/authors.relations.ts +15 -0
  152. package/templates/apps/api-data-advanced/database/books.entity.ts +59 -0
  153. package/templates/apps/api-data-advanced/database/books.relations.ts +10 -0
  154. package/templates/apps/api-data-advanced/database/index.ts +23 -0
  155. package/templates/apps/api-data-advanced/database/tenants.entity.ts +11 -0
  156. package/templates/apps/api-data-advanced/package.json +28 -0
  157. package/templates/apps/api-data-advanced/queries/authors.withBooks.query.server.ts +18 -0
  158. package/templates/apps/api-data-advanced/queries/authors.withBooks.query.ts +40 -0
  159. package/templates/apps/api-data-advanced/queries/books.search.query.server.ts +17 -0
  160. package/templates/apps/api-data-advanced/queries/books.search.query.ts +39 -0
  161. package/templates/apps/api-data-advanced/seeds/catalog.seed.ts +101 -0
  162. package/templates/apps/api-data-advanced/template.json +6 -0
  163. package/templates/apps/api-data-advanced/tests/queries.test.ts +124 -0
  164. package/templates/apps/api-data-advanced/tsconfig.json +5 -0
  165. package/templates/apps/api-durable/README.md +81 -0
  166. package/templates/apps/api-durable/aggregates/orderStats.aggregate.ts +49 -0
  167. package/templates/apps/api-durable/app.config.ts +41 -0
  168. package/templates/apps/api-durable/database/schema.ts +65 -0
  169. package/templates/apps/api-durable/mutations/orders.approve.mutation.server.ts +43 -0
  170. package/templates/apps/api-durable/mutations/orders.approve.mutation.ts +23 -0
  171. package/templates/apps/api-durable/mutations/orders.place.mutation.server.ts +45 -0
  172. package/templates/apps/api-durable/mutations/orders.place.mutation.ts +40 -0
  173. package/templates/apps/api-durable/package.json +28 -0
  174. package/templates/apps/api-durable/schedules/nightlyReport.cron.tsx +37 -0
  175. package/templates/apps/api-durable/startup/warm.startup.tsx +33 -0
  176. package/templates/apps/api-durable/subscribers/orderChanges.subscribe.ts +27 -0
  177. package/templates/apps/api-durable/template.json +6 -0
  178. package/templates/apps/api-durable/tests/orders.place.test.ts +91 -0
  179. package/templates/apps/api-durable/triggers/order.placed.trigger.tsx +21 -0
  180. package/templates/apps/api-durable/tsconfig.json +5 -0
  181. package/templates/apps/api-durable/workflows/order.fulfill.workflow.server.tsx +103 -0
  182. package/templates/apps/api-durable/workflows/order.fulfill.workflow.tsx +25 -0
  183. package/templates/apps/api-feature-flags/README.md +63 -0
  184. package/templates/apps/api-feature-flags/actions/notes.export.action.server.ts +11 -0
  185. package/templates/apps/api-feature-flags/actions/notes.export.action.ts +18 -0
  186. package/templates/apps/api-feature-flags/app.config.ts +41 -0
  187. package/templates/apps/api-feature-flags/database/schema.ts +37 -0
  188. package/templates/apps/api-feature-flags/mutations/notes.create.mutation.server.ts +29 -0
  189. package/templates/apps/api-feature-flags/mutations/notes.create.mutation.ts +28 -0
  190. package/templates/apps/api-feature-flags/package.json +29 -0
  191. package/templates/apps/api-feature-flags/template.json +6 -0
  192. package/templates/apps/api-feature-flags/tests/notes.create.test.ts +67 -0
  193. package/templates/apps/api-feature-flags/tsconfig.json +5 -0
  194. package/templates/apps/api-governance/.env +4 -0
  195. package/templates/apps/api-governance/README.md +61 -0
  196. package/templates/apps/api-governance/actions/profiles.get.action.server.ts +17 -0
  197. package/templates/apps/api-governance/actions/profiles.get.action.ts +17 -0
  198. package/templates/apps/api-governance/app.config.ts +35 -0
  199. package/templates/apps/api-governance/database/schema.ts +41 -0
  200. package/templates/apps/api-governance/mutations/profiles.create.mutation.server.ts +17 -0
  201. package/templates/apps/api-governance/mutations/profiles.create.mutation.ts +21 -0
  202. package/templates/apps/api-governance/package.json +29 -0
  203. package/templates/apps/api-governance/template.json +6 -0
  204. package/templates/apps/api-governance/tests/profiles.create.test.ts +59 -0
  205. package/templates/apps/api-governance/tsconfig.json +5 -0
  206. package/templates/apps/api-kv/README.md +100 -0
  207. package/templates/apps/api-kv/actions/sync.pull.action.server.ts +75 -0
  208. package/templates/apps/api-kv/actions/sync.pull.action.ts +25 -0
  209. package/templates/apps/api-kv/actions/sync.reset.action.server.ts +25 -0
  210. package/templates/apps/api-kv/actions/sync.reset.action.ts +22 -0
  211. package/templates/apps/api-kv/actions/sync.status.action.server.ts +28 -0
  212. package/templates/apps/api-kv/actions/sync.status.action.ts +17 -0
  213. package/templates/apps/api-kv/app.config.ts +48 -0
  214. package/templates/apps/api-kv/database/schema.ts +62 -0
  215. package/templates/apps/api-kv/package.json +28 -0
  216. package/templates/apps/api-kv/queries/events.list.query.server.ts +14 -0
  217. package/templates/apps/api-kv/queries/events.list.query.ts +23 -0
  218. package/templates/apps/api-kv/template.json +6 -0
  219. package/templates/apps/api-kv/tests/sync.test.ts +103 -0
  220. package/templates/apps/api-kv/tsconfig.json +5 -0
  221. package/templates/apps/api-moderation/README.md +46 -0
  222. package/templates/apps/api-moderation/app.config.ts +33 -0
  223. package/templates/apps/api-moderation/database/schema.ts +45 -0
  224. package/templates/apps/api-moderation/mutations/comments.create.mutation.server.ts +8 -0
  225. package/templates/apps/api-moderation/mutations/comments.create.mutation.ts +20 -0
  226. package/templates/apps/api-moderation/mutations/posts.create.mutation.server.ts +9 -0
  227. package/templates/apps/api-moderation/mutations/posts.create.mutation.ts +25 -0
  228. package/templates/apps/api-moderation/package.json +29 -0
  229. package/templates/apps/api-moderation/template.json +6 -0
  230. package/templates/apps/api-moderation/tests/posts.create.test.ts +79 -0
  231. package/templates/apps/api-moderation/tsconfig.json +5 -0
  232. package/templates/apps/api-observability/README.md +106 -0
  233. package/templates/apps/api-observability/app.config.ts +32 -0
  234. package/templates/apps/api-observability/database/schema.ts +38 -0
  235. package/templates/apps/api-observability/mutations/notes.create.mutation.server.ts +33 -0
  236. package/templates/apps/api-observability/mutations/notes.create.mutation.ts +15 -0
  237. package/templates/apps/api-observability/package.json +29 -0
  238. package/templates/apps/api-observability/queries/notes.list.query.server.ts +7 -0
  239. package/templates/apps/api-observability/queries/notes.list.query.ts +14 -0
  240. package/templates/apps/api-observability/template.json +6 -0
  241. package/templates/apps/api-observability/tests/notes.create.test.ts +36 -0
  242. package/templates/apps/api-observability/tsconfig.json +5 -0
  243. package/templates/apps/api-ratelimit/README.md +49 -0
  244. package/templates/apps/api-ratelimit/app.config.ts +42 -0
  245. package/templates/apps/api-ratelimit/database/schema.ts +37 -0
  246. package/templates/apps/api-ratelimit/mutations/notes.create.mutation.server.ts +16 -0
  247. package/templates/apps/api-ratelimit/mutations/notes.create.mutation.ts +34 -0
  248. package/templates/apps/api-ratelimit/package.json +29 -0
  249. package/templates/apps/api-ratelimit/template.json +6 -0
  250. package/templates/apps/api-ratelimit/tests/notes.create.test.ts +69 -0
  251. package/templates/apps/api-ratelimit/tsconfig.json +5 -0
  252. package/templates/apps/api-rbac/README.md +59 -0
  253. package/templates/apps/api-rbac/app.config.ts +48 -0
  254. package/templates/apps/api-rbac/database/schema.ts +37 -0
  255. package/templates/apps/api-rbac/mutations/notes.create.mutation.server.ts +22 -0
  256. package/templates/apps/api-rbac/mutations/notes.create.mutation.ts +26 -0
  257. package/templates/apps/api-rbac/package.json +29 -0
  258. package/templates/apps/api-rbac/template.json +6 -0
  259. package/templates/apps/api-rbac/tests/notes.create.test.ts +80 -0
  260. package/templates/apps/api-rbac/tsconfig.json +5 -0
  261. package/templates/apps/api-rest/README.md +85 -0
  262. package/templates/apps/api-rest/app.config.ts +70 -0
  263. package/templates/apps/api-rest/database/schema.ts +52 -0
  264. package/templates/apps/api-rest/lib/product.ts +30 -0
  265. package/templates/apps/api-rest/package.json +27 -0
  266. package/templates/apps/api-rest/routes/v1/products.create.route.tsx +45 -0
  267. package/templates/apps/api-rest/routes/v1/products.delete.route.tsx +25 -0
  268. package/templates/apps/api-rest/routes/v1/products.get.route.tsx +29 -0
  269. package/templates/apps/api-rest/routes/v1/products.list.route.tsx +43 -0
  270. package/templates/apps/api-rest/template.json +6 -0
  271. package/templates/apps/api-rest/tests/products.create.test.ts +53 -0
  272. package/templates/apps/api-rest/tsconfig.json +5 -0
  273. package/templates/apps/api-saas/README.md +106 -0
  274. package/templates/apps/api-saas/app.config.ts +51 -0
  275. package/templates/apps/api-saas/database/schema.ts +50 -0
  276. package/templates/apps/api-saas/mutations/projects.create.mutation.server.ts +52 -0
  277. package/templates/apps/api-saas/mutations/projects.create.mutation.ts +18 -0
  278. package/templates/apps/api-saas/package.json +31 -0
  279. package/templates/apps/api-saas/queries/projects.list.query.server.ts +7 -0
  280. package/templates/apps/api-saas/queries/projects.list.query.ts +18 -0
  281. package/templates/apps/api-saas/template.json +6 -0
  282. package/templates/apps/api-saas/tests/projects.create.test.ts +47 -0
  283. package/templates/apps/api-saas/tsconfig.json +5 -0
  284. package/templates/apps/api-search/README.md +68 -0
  285. package/templates/apps/api-search/app.config.ts +31 -0
  286. package/templates/apps/api-search/database/schema.ts +47 -0
  287. package/templates/apps/api-search/lib/search.ts +27 -0
  288. package/templates/apps/api-search/mutations/articles.create.mutation.server.ts +19 -0
  289. package/templates/apps/api-search/mutations/articles.create.mutation.ts +35 -0
  290. package/templates/apps/api-search/package.json +29 -0
  291. package/templates/apps/api-search/queries/articles.list.query.server.ts +14 -0
  292. package/templates/apps/api-search/queries/articles.list.query.ts +19 -0
  293. package/templates/apps/api-search/seeds/articles.seed.ts +41 -0
  294. package/templates/apps/api-search/startup/searchBackfill.startup.tsx +24 -0
  295. package/templates/apps/api-search/template.json +6 -0
  296. package/templates/apps/api-search/tests/articles.create.test.ts +69 -0
  297. package/templates/apps/api-search/tsconfig.json +5 -0
  298. package/templates/apps/api-versioning/README.md +51 -0
  299. package/templates/apps/api-versioning/actions/documents.asOf.action.server.ts +13 -0
  300. package/templates/apps/api-versioning/actions/documents.asOf.action.ts +14 -0
  301. package/templates/apps/api-versioning/actions/documents.history.action.server.ts +15 -0
  302. package/templates/apps/api-versioning/actions/documents.history.action.ts +17 -0
  303. package/templates/apps/api-versioning/app.config.ts +24 -0
  304. package/templates/apps/api-versioning/database/schema.ts +38 -0
  305. package/templates/apps/api-versioning/mutations/documents.create.mutation.server.ts +8 -0
  306. package/templates/apps/api-versioning/mutations/documents.create.mutation.ts +21 -0
  307. package/templates/apps/api-versioning/mutations/documents.update.mutation.server.ts +9 -0
  308. package/templates/apps/api-versioning/mutations/documents.update.mutation.ts +21 -0
  309. package/templates/apps/api-versioning/package.json +29 -0
  310. package/templates/apps/api-versioning/template.json +6 -0
  311. package/templates/apps/api-versioning/tests/documents.create.test.ts +37 -0
  312. package/templates/apps/api-versioning/tsconfig.json +5 -0
  313. package/templates/apps/api-webhooks/.env +6 -0
  314. package/templates/apps/api-webhooks/README.md +106 -0
  315. package/templates/apps/api-webhooks/app.config.ts +16 -0
  316. package/templates/apps/api-webhooks/database/schema.ts +49 -0
  317. package/templates/apps/api-webhooks/events/order.completed.webhook.tsx +22 -0
  318. package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.server.ts +37 -0
  319. package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.ts +15 -0
  320. package/templates/apps/api-webhooks/package.json +28 -0
  321. package/templates/apps/api-webhooks/queries/orders.list.query.server.ts +7 -0
  322. package/templates/apps/api-webhooks/queries/orders.list.query.ts +15 -0
  323. package/templates/apps/api-webhooks/template.json +6 -0
  324. package/templates/apps/api-webhooks/tests/orders.fulfill.test.ts +51 -0
  325. package/templates/apps/api-webhooks/tsconfig.json +5 -0
  326. package/templates/apps/api-webhooks/webhooks/orders.webhook.tsx +35 -0
  327. package/templates/apps/changelog/README.md +77 -0
  328. package/templates/apps/changelog/app.config.ts +27 -0
  329. package/templates/apps/changelog/content/releases/0.1.0.mdx +19 -0
  330. package/templates/apps/changelog/content/releases/0.2.0.mdx +28 -0
  331. package/templates/apps/changelog/package.json +30 -0
  332. package/templates/apps/changelog/scripts/generate-rss.mjs +38 -0
  333. package/templates/apps/changelog/src/globals.css +66 -0
  334. package/templates/apps/changelog/src/globals.d.ts +16 -0
  335. package/templates/apps/changelog/src/lib/locale.test.ts +72 -0
  336. package/templates/apps/changelog/src/lib/locale.ts +55 -0
  337. package/templates/apps/changelog/src/lib/releases.ts +21 -0
  338. package/templates/apps/changelog/src/locales/de.ts +22 -0
  339. package/templates/apps/changelog/src/locales/en.ts +30 -0
  340. package/templates/apps/changelog/src/pages/[locale]/[slug].tsx +24 -0
  341. package/templates/apps/changelog/src/pages/[locale]/index.tsx +13 -0
  342. package/templates/apps/changelog/src/pages/[locale]/mirrors.test.tsx +95 -0
  343. package/templates/apps/changelog/src/pages/[slug].test.tsx +127 -0
  344. package/templates/apps/changelog/src/pages/[slug].tsx +52 -0
  345. package/templates/apps/changelog/src/pages/index.test.tsx +112 -0
  346. package/templates/apps/changelog/src/pages/index.tsx +55 -0
  347. package/templates/apps/changelog/src/pages/layout.tsx +84 -0
  348. package/templates/apps/changelog/template.json +6 -0
  349. package/templates/apps/changelog/tsconfig.json +11 -0
  350. package/templates/apps/edge-functions/README.md +55 -0
  351. package/templates/apps/edge-functions/functions/aiComplete.serverless.ts +58 -0
  352. package/templates/apps/edge-functions/functions/currencyConvert.serverless.ts +47 -0
  353. package/templates/apps/edge-functions/functions/geoGreeting.serverless.ts +44 -0
  354. package/templates/apps/edge-functions/functions/health.serverless.ts +29 -0
  355. package/templates/apps/edge-functions/functions/resolveLink.serverless.ts +32 -0
  356. package/templates/apps/edge-functions/functions/shareLink.serverless.ts +30 -0
  357. package/templates/apps/edge-functions/functions/slackNotify.serverless.ts +40 -0
  358. package/templates/apps/edge-functions/functions/verifySignature.serverless.ts +52 -0
  359. package/templates/apps/edge-functions/package.json +21 -0
  360. package/templates/apps/edge-functions/template.json +6 -0
  361. package/templates/apps/edge-functions/tsconfig.json +10 -0
  362. package/templates/apps/frontend-admin/README.md +40 -0
  363. package/templates/apps/frontend-admin/app.config.ts +37 -0
  364. package/templates/apps/frontend-admin/package.json +30 -0
  365. package/templates/apps/frontend-admin/src/config.ts +8 -0
  366. package/templates/apps/frontend-admin/src/globals.css +76 -0
  367. package/templates/apps/frontend-admin/src/globals.d.ts +6 -0
  368. package/templates/apps/frontend-admin/src/lib/admin.ts +16 -0
  369. package/templates/apps/frontend-admin/src/lib/auth.ts +24 -0
  370. package/templates/apps/frontend-admin/src/locales/de.ts +67 -0
  371. package/templates/apps/frontend-admin/src/locales/en.ts +79 -0
  372. package/templates/apps/frontend-admin/src/locales/index.ts +15 -0
  373. package/templates/apps/frontend-admin/src/pages/(marketing)/index.test.tsx +55 -0
  374. package/templates/apps/frontend-admin/src/pages/(marketing)/index.tsx +32 -0
  375. package/templates/apps/frontend-admin/src/pages/(marketing)/layout.tsx +29 -0
  376. package/templates/apps/frontend-admin/src/pages/(marketing)/login.test.tsx +73 -0
  377. package/templates/apps/frontend-admin/src/pages/(marketing)/login.tsx +35 -0
  378. package/templates/apps/frontend-admin/src/pages/admin/[entity].tsx +121 -0
  379. package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +119 -0
  380. package/templates/apps/frontend-admin/src/pages/admin/error.tsx +20 -0
  381. package/templates/apps/frontend-admin/src/pages/admin/fallbacks.test.tsx +68 -0
  382. package/templates/apps/frontend-admin/src/pages/admin/index.test.tsx +88 -0
  383. package/templates/apps/frontend-admin/src/pages/admin/index.tsx +65 -0
  384. package/templates/apps/frontend-admin/src/pages/admin/layout.test.tsx +114 -0
  385. package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +97 -0
  386. package/templates/apps/frontend-admin/src/pages/admin/loading.tsx +15 -0
  387. package/templates/apps/frontend-admin/src/pages/admin/not-found.tsx +15 -0
  388. package/templates/apps/frontend-admin/src/pages/layout.tsx +12 -0
  389. package/templates/apps/frontend-admin/src/pages/layouts.test.tsx +54 -0
  390. package/templates/apps/frontend-admin/template.json +6 -0
  391. package/templates/apps/frontend-admin/tsconfig.json +11 -0
  392. package/templates/apps/frontend-app/README.md +98 -0
  393. package/templates/apps/frontend-app/app.config.ts +43 -0
  394. package/templates/apps/frontend-app/package.json +30 -0
  395. package/templates/apps/frontend-app/src/locales/de.ts +36 -0
  396. package/templates/apps/frontend-app/src/locales/en.ts +43 -0
  397. package/templates/apps/frontend-app/src/locales/index.ts +15 -0
  398. package/templates/apps/frontend-app/src/pages/index.test.tsx +167 -0
  399. package/templates/apps/frontend-app/src/pages/index.tsx +136 -0
  400. package/templates/apps/frontend-app/src/pages/layout.tsx +41 -0
  401. package/templates/apps/frontend-app/src/pages/schema-ui.test.tsx +99 -0
  402. package/templates/apps/frontend-app/src/pages/schema-ui.tsx +74 -0
  403. package/templates/apps/frontend-app/template.json +6 -0
  404. package/templates/apps/frontend-app/tsconfig.json +11 -0
  405. package/templates/apps/frontend-blank/README.md +18 -0
  406. package/templates/apps/frontend-blank/app.config.ts +29 -0
  407. package/templates/apps/frontend-blank/package.json +29 -0
  408. package/templates/apps/frontend-blank/src/locales/de.ts +15 -0
  409. package/templates/apps/frontend-blank/src/locales/en.ts +22 -0
  410. package/templates/apps/frontend-blank/src/locales/index.ts +15 -0
  411. package/templates/apps/frontend-blank/src/pages/index.test.tsx +55 -0
  412. package/templates/apps/frontend-blank/src/pages/index.tsx +27 -0
  413. package/templates/apps/frontend-blank/src/pages/layout.test.tsx +54 -0
  414. package/templates/apps/frontend-blank/src/pages/layout.tsx +35 -0
  415. package/templates/apps/frontend-blank/template.json +6 -0
  416. package/templates/apps/frontend-blank/tsconfig.json +11 -0
  417. package/templates/apps/frontend-contact/README.md +65 -0
  418. package/templates/apps/frontend-contact/app.config.ts +25 -0
  419. package/templates/apps/frontend-contact/functions/sendMessage.serverless.ts +69 -0
  420. package/templates/apps/frontend-contact/package.json +33 -0
  421. package/templates/apps/frontend-contact/src/components/ContactForm.island.test.tsx +142 -0
  422. package/templates/apps/frontend-contact/src/components/ContactForm.island.tsx +104 -0
  423. package/templates/apps/frontend-contact/src/config.ts +12 -0
  424. package/templates/apps/frontend-contact/src/globals.css +84 -0
  425. package/templates/apps/frontend-contact/src/lib/locale.ts +55 -0
  426. package/templates/apps/frontend-contact/src/locales/de.ts +26 -0
  427. package/templates/apps/frontend-contact/src/locales/en.ts +29 -0
  428. package/templates/apps/frontend-contact/src/pages/[locale]/index.tsx +14 -0
  429. package/templates/apps/frontend-contact/src/pages/index.test.tsx +70 -0
  430. package/templates/apps/frontend-contact/src/pages/index.tsx +63 -0
  431. package/templates/apps/frontend-contact/src/pages/layout.tsx +65 -0
  432. package/templates/apps/frontend-contact/template.json +6 -0
  433. package/templates/apps/frontend-contact/tsconfig.json +11 -0
  434. package/templates/apps/frontend-dashboard/README.md +54 -0
  435. package/templates/apps/frontend-dashboard/app.config.ts +39 -0
  436. package/templates/apps/frontend-dashboard/package.json +29 -0
  437. package/templates/apps/frontend-dashboard/src/config.ts +8 -0
  438. package/templates/apps/frontend-dashboard/src/globals.css +72 -0
  439. package/templates/apps/frontend-dashboard/src/globals.d.ts +6 -0
  440. package/templates/apps/frontend-dashboard/src/lib/auth.ts +27 -0
  441. package/templates/apps/frontend-dashboard/src/locales/de.ts +49 -0
  442. package/templates/apps/frontend-dashboard/src/locales/en.ts +60 -0
  443. package/templates/apps/frontend-dashboard/src/locales/index.ts +15 -0
  444. package/templates/apps/frontend-dashboard/src/pages/(marketing)/index.test.tsx +55 -0
  445. package/templates/apps/frontend-dashboard/src/pages/(marketing)/index.tsx +34 -0
  446. package/templates/apps/frontend-dashboard/src/pages/(marketing)/layout.tsx +32 -0
  447. package/templates/apps/frontend-dashboard/src/pages/(marketing)/login.test.tsx +74 -0
  448. package/templates/apps/frontend-dashboard/src/pages/(marketing)/login.tsx +40 -0
  449. package/templates/apps/frontend-dashboard/src/pages/dashboard/error.tsx +21 -0
  450. package/templates/apps/frontend-dashboard/src/pages/dashboard/fallbacks.test.tsx +68 -0
  451. package/templates/apps/frontend-dashboard/src/pages/dashboard/index.test.tsx +55 -0
  452. package/templates/apps/frontend-dashboard/src/pages/dashboard/index.tsx +38 -0
  453. package/templates/apps/frontend-dashboard/src/pages/dashboard/layout.test.tsx +88 -0
  454. package/templates/apps/frontend-dashboard/src/pages/dashboard/layout.tsx +69 -0
  455. package/templates/apps/frontend-dashboard/src/pages/dashboard/loading.tsx +21 -0
  456. package/templates/apps/frontend-dashboard/src/pages/dashboard/not-found.tsx +19 -0
  457. package/templates/apps/frontend-dashboard/src/pages/dashboard/settings.test.tsx +69 -0
  458. package/templates/apps/frontend-dashboard/src/pages/dashboard/settings.tsx +35 -0
  459. package/templates/apps/frontend-dashboard/src/pages/layout.tsx +12 -0
  460. package/templates/apps/frontend-dashboard/src/pages/layouts.test.tsx +54 -0
  461. package/templates/apps/frontend-dashboard/template.json +6 -0
  462. package/templates/apps/frontend-dashboard/tsconfig.json +11 -0
  463. package/templates/apps/frontend-docs/README.md +19 -0
  464. package/templates/apps/frontend-docs/app.config.ts +25 -0
  465. package/templates/apps/frontend-docs/package.json +29 -0
  466. package/templates/apps/frontend-docs/src/globals.css +32 -0
  467. package/templates/apps/frontend-docs/src/lib/locale.test.ts +72 -0
  468. package/templates/apps/frontend-docs/src/lib/locale.ts +56 -0
  469. package/templates/apps/frontend-docs/src/locales/de.ts +24 -0
  470. package/templates/apps/frontend-docs/src/locales/en.ts +28 -0
  471. package/templates/apps/frontend-docs/src/pages/[locale]/docs/[...slug].tsx +18 -0
  472. package/templates/apps/frontend-docs/src/pages/[locale]/index.tsx +13 -0
  473. package/templates/apps/frontend-docs/src/pages/[locale]/mirrors.test.tsx +67 -0
  474. package/templates/apps/frontend-docs/src/pages/docs/[...slug].test.tsx +115 -0
  475. package/templates/apps/frontend-docs/src/pages/docs/[...slug].tsx +82 -0
  476. package/templates/apps/frontend-docs/src/pages/index.test.tsx +90 -0
  477. package/templates/apps/frontend-docs/src/pages/index.tsx +45 -0
  478. package/templates/apps/frontend-docs/src/pages/layout.test.tsx +84 -0
  479. package/templates/apps/frontend-docs/src/pages/layout.tsx +66 -0
  480. package/templates/apps/frontend-docs/template.json +6 -0
  481. package/templates/apps/frontend-docs/tsconfig.json +11 -0
  482. package/templates/apps/frontend-i18n/README.md +61 -0
  483. package/templates/apps/frontend-i18n/app.config.ts +33 -0
  484. package/templates/apps/frontend-i18n/package.json +28 -0
  485. package/templates/apps/frontend-i18n/src/globals.css +46 -0
  486. package/templates/apps/frontend-i18n/src/globals.d.ts +6 -0
  487. package/templates/apps/frontend-i18n/src/lib/locale.test.ts +72 -0
  488. package/templates/apps/frontend-i18n/src/lib/locale.ts +55 -0
  489. package/templates/apps/frontend-i18n/src/locales/de.ts +24 -0
  490. package/templates/apps/frontend-i18n/src/locales/en.ts +25 -0
  491. package/templates/apps/frontend-i18n/src/pages/[locale]/about.tsx +11 -0
  492. package/templates/apps/frontend-i18n/src/pages/[locale]/index.tsx +13 -0
  493. package/templates/apps/frontend-i18n/src/pages/[locale]/mirrors.test.tsx +50 -0
  494. package/templates/apps/frontend-i18n/src/pages/about.test.tsx +75 -0
  495. package/templates/apps/frontend-i18n/src/pages/about.tsx +31 -0
  496. package/templates/apps/frontend-i18n/src/pages/index.test.tsx +102 -0
  497. package/templates/apps/frontend-i18n/src/pages/index.tsx +43 -0
  498. package/templates/apps/frontend-i18n/src/pages/layout.test.tsx +86 -0
  499. package/templates/apps/frontend-i18n/src/pages/layout.tsx +70 -0
  500. package/templates/apps/frontend-i18n/template.json +6 -0
  501. package/templates/apps/frontend-i18n/tsconfig.json +11 -0
  502. package/templates/apps/frontend-landing/README.md +17 -0
  503. package/templates/apps/frontend-landing/app.config.ts +25 -0
  504. package/templates/apps/frontend-landing/package.json +29 -0
  505. package/templates/apps/frontend-landing/src/globals.css +23 -0
  506. package/templates/apps/frontend-landing/src/lib/locale.test.ts +72 -0
  507. package/templates/apps/frontend-landing/src/lib/locale.ts +55 -0
  508. package/templates/apps/frontend-landing/src/locales/de.ts +24 -0
  509. package/templates/apps/frontend-landing/src/locales/en.ts +26 -0
  510. package/templates/apps/frontend-landing/src/pages/[locale]/index.tsx +13 -0
  511. package/templates/apps/frontend-landing/src/pages/[locale]/mirrors.test.tsx +37 -0
  512. package/templates/apps/frontend-landing/src/pages/index.test.tsx +110 -0
  513. package/templates/apps/frontend-landing/src/pages/index.tsx +75 -0
  514. package/templates/apps/frontend-landing/src/pages/layout.test.tsx +84 -0
  515. package/templates/apps/frontend-landing/src/pages/layout.tsx +66 -0
  516. package/templates/apps/frontend-landing/template.json +6 -0
  517. package/templates/apps/frontend-landing/tsconfig.json +11 -0
  518. package/templates/apps/frontend-spa/README.md +45 -0
  519. package/templates/apps/frontend-spa/app.config.ts +27 -0
  520. package/templates/apps/frontend-spa/package.json +29 -0
  521. package/templates/apps/frontend-spa/src/globals.css +84 -0
  522. package/templates/apps/frontend-spa/src/locales/de.ts +22 -0
  523. package/templates/apps/frontend-spa/src/locales/en.ts +29 -0
  524. package/templates/apps/frontend-spa/src/locales/index.ts +15 -0
  525. package/templates/apps/frontend-spa/src/pages/index.test.tsx +137 -0
  526. package/templates/apps/frontend-spa/src/pages/index.tsx +123 -0
  527. package/templates/apps/frontend-spa/src/pages/layout.tsx +27 -0
  528. package/templates/apps/frontend-spa/template.json +6 -0
  529. package/templates/apps/frontend-spa/tsconfig.json +11 -0
  530. package/templates/apps/frontend-ssr/README.md +68 -0
  531. package/templates/apps/frontend-ssr/app.config.ts +32 -0
  532. package/templates/apps/frontend-ssr/package.json +29 -0
  533. package/templates/apps/frontend-ssr/src/globals.css +67 -0
  534. package/templates/apps/frontend-ssr/src/locales/de.ts +41 -0
  535. package/templates/apps/frontend-ssr/src/locales/en.ts +54 -0
  536. package/templates/apps/frontend-ssr/src/locales/index.ts +16 -0
  537. package/templates/apps/frontend-ssr/src/pages/feed-swr.test.tsx +69 -0
  538. package/templates/apps/frontend-ssr/src/pages/feed-swr.tsx +54 -0
  539. package/templates/apps/frontend-ssr/src/pages/feed.test.tsx +73 -0
  540. package/templates/apps/frontend-ssr/src/pages/feed.tsx +64 -0
  541. package/templates/apps/frontend-ssr/src/pages/index.test.tsx +89 -0
  542. package/templates/apps/frontend-ssr/src/pages/index.tsx +72 -0
  543. package/templates/apps/frontend-ssr/src/pages/layout.tsx +37 -0
  544. package/templates/apps/frontend-ssr/template.json +6 -0
  545. package/templates/apps/frontend-ssr/tsconfig.json +11 -0
  546. package/templates/apps/frontend-ssr-api/README.md +50 -0
  547. package/templates/apps/frontend-ssr-api/app.config.ts +43 -0
  548. package/templates/apps/frontend-ssr-api/package.json +30 -0
  549. package/templates/apps/frontend-ssr-api/src/globals.css +38 -0
  550. package/templates/apps/frontend-ssr-api/src/globals.d.ts +6 -0
  551. package/templates/apps/frontend-ssr-api/src/locales/de.ts +20 -0
  552. package/templates/apps/frontend-ssr-api/src/locales/en.ts +31 -0
  553. package/templates/apps/frontend-ssr-api/src/locales/index.ts +16 -0
  554. package/templates/apps/frontend-ssr-api/src/pages/index.test.tsx +105 -0
  555. package/templates/apps/frontend-ssr-api/src/pages/index.tsx +83 -0
  556. package/templates/apps/frontend-ssr-api/src/pages/layout.tsx +28 -0
  557. package/templates/apps/frontend-ssr-api/template.json +6 -0
  558. package/templates/apps/frontend-ssr-api/tsconfig.json +11 -0
  559. package/templates/apps/frontend-static-blog/README.md +49 -0
  560. package/templates/apps/frontend-static-blog/app.config.ts +34 -0
  561. package/templates/apps/frontend-static-blog/package.json +28 -0
  562. package/templates/apps/frontend-static-blog/src/components/ReadingProgress.island.test.tsx +65 -0
  563. package/templates/apps/frontend-static-blog/src/components/ReadingProgress.island.tsx +35 -0
  564. package/templates/apps/frontend-static-blog/src/content/posts.ts +64 -0
  565. package/templates/apps/frontend-static-blog/src/globals.css +75 -0
  566. package/templates/apps/frontend-static-blog/src/lib/locale.test.ts +72 -0
  567. package/templates/apps/frontend-static-blog/src/lib/locale.ts +55 -0
  568. package/templates/apps/frontend-static-blog/src/locales/de.ts +19 -0
  569. package/templates/apps/frontend-static-blog/src/locales/en.ts +26 -0
  570. package/templates/apps/frontend-static-blog/src/pages/[locale]/blog/[slug].tsx +20 -0
  571. package/templates/apps/frontend-static-blog/src/pages/[locale]/index.tsx +13 -0
  572. package/templates/apps/frontend-static-blog/src/pages/[locale]/mirrors.test.tsx +62 -0
  573. package/templates/apps/frontend-static-blog/src/pages/blog/[slug].test.tsx +116 -0
  574. package/templates/apps/frontend-static-blog/src/pages/blog/[slug].tsx +69 -0
  575. package/templates/apps/frontend-static-blog/src/pages/index.test.tsx +100 -0
  576. package/templates/apps/frontend-static-blog/src/pages/index.tsx +58 -0
  577. package/templates/apps/frontend-static-blog/src/pages/layout.tsx +63 -0
  578. package/templates/apps/frontend-static-blog/template.json +6 -0
  579. package/templates/apps/frontend-static-blog/tsconfig.json +11 -0
  580. package/templates/baselines/bare/.env.example +37 -0
  581. package/templates/baselines/bare/README.md +47 -0
  582. package/templates/baselines/bare/baseline.json +31 -0
  583. package/templates/baselines/bare/deploy/README.md +43 -0
  584. package/templates/baselines/bare/deploy/voltro.service.example +36 -0
  585. package/templates/baselines/compose/.env.example +46 -0
  586. package/templates/baselines/compose/README.md +69 -0
  587. package/templates/baselines/compose/baseline.json +51 -0
  588. package/templates/baselines/compose/docker/.dockerignore +38 -0
  589. package/templates/baselines/compose/docker/api.Dockerfile +57 -0
  590. package/templates/baselines/compose/docker/dev.Dockerfile +35 -0
  591. package/templates/baselines/compose/docker/web.Dockerfile +59 -0
  592. package/templates/baselines/compose/docker-compose.dev.yml +89 -0
  593. package/templates/baselines/compose/docker-compose.prod.yml +87 -0
  594. package/templates/baselines/compose/docker-compose.yml +41 -0
  595. package/templates/baselines/compose-mariadb/.env.example +57 -0
  596. package/templates/baselines/compose-mariadb/README.md +78 -0
  597. package/templates/baselines/compose-mariadb/baseline.json +51 -0
  598. package/templates/baselines/compose-mariadb/docker/.dockerignore +38 -0
  599. package/templates/baselines/compose-mariadb/docker/api.Dockerfile +57 -0
  600. package/templates/baselines/compose-mariadb/docker/dev.Dockerfile +35 -0
  601. package/templates/baselines/compose-mariadb/docker/mariadb-init.sql +6 -0
  602. package/templates/baselines/compose-mariadb/docker/web.Dockerfile +59 -0
  603. package/templates/baselines/compose-mariadb/docker-compose.dev.yml +117 -0
  604. package/templates/baselines/compose-mariadb/docker-compose.prod.yml +114 -0
  605. package/templates/baselines/compose-mariadb/docker-compose.yml +79 -0
  606. package/templates/baselines/helm/.env.example +39 -0
  607. package/templates/baselines/helm/README.md +98 -0
  608. package/templates/baselines/helm/baseline.json +53 -0
  609. package/templates/baselines/helm/charts/voltro-app/.helmignore +10 -0
  610. package/templates/baselines/helm/charts/voltro-app/Chart.yaml +10 -0
  611. package/templates/baselines/helm/charts/voltro-app/templates/_helpers.tpl +36 -0
  612. package/templates/baselines/helm/charts/voltro-app/templates/configmap.yaml +13 -0
  613. package/templates/baselines/helm/charts/voltro-app/templates/deployment-api.yaml +120 -0
  614. package/templates/baselines/helm/charts/voltro-app/templates/deployment-web.yaml +45 -0
  615. package/templates/baselines/helm/charts/voltro-app/templates/ingress.yaml +37 -0
  616. package/templates/baselines/helm/charts/voltro-app/templates/postgres-service.yaml +19 -0
  617. package/templates/baselines/helm/charts/voltro-app/templates/postgres-statefulset.yaml +73 -0
  618. package/templates/baselines/helm/charts/voltro-app/templates/secret.yaml +33 -0
  619. package/templates/baselines/helm/charts/voltro-app/templates/service-api.yaml +19 -0
  620. package/templates/baselines/helm/charts/voltro-app/templates/service-web.yaml +19 -0
  621. package/templates/baselines/helm/charts/voltro-app/values-dev.yaml +21 -0
  622. package/templates/baselines/helm/charts/voltro-app/values-prod.yaml +58 -0
  623. package/templates/baselines/helm/charts/voltro-app/values-staging.yaml +25 -0
  624. package/templates/baselines/helm/charts/voltro-app/values.yaml +109 -0
  625. package/templates/baselines/helm/deploy/README.md +94 -0
  626. package/templates/patches/@effect__cluster@0.59.0.patch +262 -0
@@ -0,0 +1,18 @@
1
+ // Create a project — the SaaS bundle in ONE handler. The billing plugin
2
+ // auto-merges its typed `EntitlementExceeded` (+ `BillingError`) into this
3
+ // procedure's wire-error union, so the client decodes the over-quota failure
4
+ // typed WITHOUT a manual `error:` declaration here.
5
+
6
+ import { defineMutation } from '@voltro/protocol'
7
+ import { Schema } from 'effect'
8
+
9
+ export const createProject = defineMutation({
10
+ name: 'projects.create',
11
+ target: { table: 'projects', op: 'insert' },
12
+ input: Schema.Struct({ name: Schema.NonEmptyString }),
13
+ output: Schema.Struct({
14
+ id: Schema.String,
15
+ name: Schema.String,
16
+ tenantId: Schema.String,
17
+ }),
18
+ })
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@{{projectName}}/{{appName}}",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "voltro dev .",
8
+ "migrate": "voltro migrate",
9
+ "test": "voltro test",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@effect/platform": "^0.96.1",
14
+ "@effect/rpc": "^0.75.1",
15
+ "@voltro/cli": "0.1.0",
16
+ "@voltro/database": "0.1.0",
17
+ "@voltro/plugin-analytics-postgres": "0.1.0",
18
+ "@voltro/plugin-billing": "0.1.0",
19
+ "@voltro/plugin-multitenancy": "0.1.0",
20
+ "@voltro/plugin-notifications": "0.1.0",
21
+ "@voltro/plugin-presence": "0.1.0",
22
+ "@voltro/protocol": "0.1.0",
23
+ "@voltro/runtime": "0.1.0",
24
+ "effect": "^3.21.2"
25
+ },
26
+ "devDependencies": {
27
+ "@voltro/testing": "0.1.0",
28
+ "typescript": "^5.7.0",
29
+ "vitest": "^3.0.0"
30
+ }
31
+ }
@@ -0,0 +1,7 @@
1
+ import type { AppContext } from '@voltro/runtime'
2
+ import { database } from '../database/schema'
3
+
4
+ const route = (_input: Record<string, never>, _ctx: AppContext) =>
5
+ database.projects.orderBy('createdAt', 'desc').limit(100)
6
+
7
+ export default route
@@ -0,0 +1,18 @@
1
+ // Reactive list of the tenant's projects. The runtime AND-merges
2
+ // `eq('tenantId', subject.tenantId)` (the table carries `tenant()`), so this
3
+ // never leaks across tenants — no manual filter needed.
4
+
5
+ import { defineQuery } from '@voltro/protocol'
6
+ import { Schema } from 'effect'
7
+
8
+ export const listProjects = defineQuery({
9
+ name: 'projects.list',
10
+ source: 'projects',
11
+ input: Schema.Struct({}),
12
+ output: Schema.Array(Schema.Struct({
13
+ id: Schema.String,
14
+ name: Schema.String,
15
+ tenantId: Schema.String,
16
+ createdAt: Schema.Date,
17
+ })),
18
+ })
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "api-saas",
3
+ "kind": "api",
4
+ "summary": "The SaaS plugin bundle in one domain: billing entitlements (quota gate), in-app + console notifications, analytics events, and live presence — wired turnkey. One projects.create mutation exercises billing + analytics + notifications together.",
5
+ "tags": ["api", "saas", "billing", "notifications", "analytics", "presence", "plugins"]
6
+ }
@@ -0,0 +1,47 @@
1
+ // Unit test with `@voltro/testing` — no database, no running server, no billing
2
+ // backend. The `projects.create` executor runs in Effect mode and `yield*`s the
3
+ // billing (requireEntitlement), analytics, and notifications plugin services —
4
+ // those live in the per-request plugin layer, which the unit harness does NOT
5
+ // wire, so we do not fake them. Instead we PIN the descriptor's wire contract
6
+ // (the shape the client codegen depends on) and prove the tenant() scoping that
7
+ // keeps one SaaS tenant's projects invisible to another — the headline feature.
8
+ // Run with `voltro test` (vitest).
9
+
10
+ import { describe, it, expect } from 'vitest'
11
+ import { Schema } from 'effect'
12
+ import { makeTestContext, mockStore } from '@voltro/testing'
13
+ import { database } from '../database/schema' // registers projects / actors / tenants
14
+ import { createProject } from '../mutations/projects.create.mutation'
15
+
16
+ describe('projects.create descriptor', () => {
17
+ it('decodes a valid input and rejects a blank name', () => {
18
+ const decode = Schema.decodeUnknownSync(createProject.input)
19
+ expect(decode({ name: 'Acme App' })).toEqual({ name: 'Acme App' })
20
+ // `name: Schema.NonEmptyString` — an empty string fails the wire decode.
21
+ expect(() => decode({ name: '' })).toThrow()
22
+ })
23
+
24
+ it('encodes the output shape the client expects', () => {
25
+ const encode = Schema.encodeUnknownSync(createProject.output)
26
+ const wire = encode({ id: 'proj_1', name: 'Acme App', tenantId: 'acme' })
27
+ expect(wire).toEqual({ id: 'proj_1', name: 'Acme App', tenantId: 'acme' })
28
+ })
29
+ })
30
+
31
+ describe('projects tenant isolation', () => {
32
+ it('t2 never sees t1 projects (REAL tenant scoping)', async () => {
33
+ const ctx = makeTestContext({
34
+ subject: { type: 'user', id: 'u1', tenantId: 't1' },
35
+ store: mockStore({ projects: [] }),
36
+ })
37
+ // Raw insert through the mixin store — tenant() auto-stamps tenantId=t1.
38
+ const row = await ctx.store.insert('projects', { name: 'secret' })
39
+ expect(row['tenantId']).toBe('t1')
40
+
41
+ const seenByT2 = await ctx.withTenant('t2', (c) => c.store.query(database.projects.descriptor))
42
+ expect(seenByT2).toHaveLength(0)
43
+ // …and t1 still sees its own row.
44
+ const seenByT1 = await ctx.store.query(database.projects.descriptor)
45
+ expect(seenByT1).toHaveLength(1)
46
+ })
47
+ })
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }
@@ -0,0 +1,68 @@
1
+ # {{projectName}} / {{appName}}
2
+
3
+ Voltro API scaffold (template: **api-search**) — full-text search that stays
4
+ in sync with your tables, powered by `@voltro/plugin-search`.
5
+
6
+ ## Boot
7
+
8
+ ```bash
9
+ pnpm install
10
+ pnpm --filter @{{projectName}}/{{appName}} dev
11
+ # → http://localhost:4000 (store: memory — zero infra)
12
+ ```
13
+
14
+ The boot log shows `search: backfilled 4 article(s) into the index` — the
15
+ demo seed's rows, indexed and ready to search.
16
+
17
+ ## What this shows
18
+
19
+ - **Index stays in sync, automatically** — `searchPlugin` taps the post-commit
20
+ ChangeEvent stream: every `articles` insert/update upserts its doc into the
21
+ index, every delete removes it. No app code — create an article and it's
22
+ searchable on the next request.
23
+ - **Tenant-scoped results** — the index spec's `tenantField: 'tenantId'` makes
24
+ the synthesized `search.query` rpc auto-filter to the caller's tenant. One
25
+ tenant can never see another's hits.
26
+ - **Backfill existing rows** — `startup/searchBackfill.startup.tsx` calls
27
+ `backfillIndex(...)` on boot to seed the index from rows already in the table
28
+ (the seed data, or every row after a restart / engine switch).
29
+ - **One config line to go to production** — `lib/search.ts` uses
30
+ `memoryBackend()` (in-process, zero infra). Swap it for `{ engine:
31
+ 'typesense' | 'meilisearch' | 'algolia', url, apiKey }` and nothing else
32
+ changes — the indexing + query code is identical.
33
+
34
+ ## Try it
35
+
36
+ The plugin synthesizes a `search.query` rpc. Hit it over the inspect surface:
37
+
38
+ ```bash
39
+ # Search the seeded articles (tenant 'acme' via the x-tenant default):
40
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
41
+ -d '{"tag":"search.query","input":{"index":"articles","q":"tenant"}}'
42
+
43
+ # Create an article — it's indexed on commit, then searchable:
44
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
45
+ -d '{"tag":"articles.create","input":{"tenantId":"acme","title":"Reactive queries","body":"Subscriptions push deltas live.","tag":"guide"}}'
46
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
47
+ -d '{"tag":"search.query","input":{"index":"articles","q":"reactive"}}'
48
+ ```
49
+
50
+ ## On the web side
51
+
52
+ A browser hook ships at `@voltro/plugin-search/web`:
53
+
54
+ ```tsx
55
+ import { useSearch } from '@voltro/plugin-search/web'
56
+ const { results, search, pending } = useSearch('articles')
57
+ // search('reactive') → results auto-scoped to the signed-in tenant
58
+ ```
59
+
60
+ ## Files
61
+
62
+ - `lib/search.ts` — the shared backend instance + the `articles` index spec.
63
+ - `app.config.ts` — wires `searchPlugin({ backend, indexes })`.
64
+ - `database/schema.ts` — the `articles` table (tenant-scoped, reactive).
65
+ - `mutations/articles.create.*` — writes that auto-index via the tap.
66
+ - `queries/articles.list.*` — a live list to sit next to the search box.
67
+ - `seeds/articles.seed.ts` — demo data.
68
+ - `startup/searchBackfill.startup.tsx` — backfills the index on boot.
@@ -0,0 +1,31 @@
1
+ // Search backend for the {{projectName}} project — full-text search over a
2
+ // table, kept in sync automatically.
3
+ //
4
+ // `searchPlugin` taps the post-commit ChangeEvent stream: every insert/update
5
+ // to a source table upserts its doc into the index, every delete removes it —
6
+ // no app code. The synthesized `search.query` rpc returns hits, auto-filtered
7
+ // to the caller's tenant. `store: 'memory'` keeps this zero-infra for dev.
8
+ import { defineEnv, envVar } from '@voltro/env'
9
+ import { searchPlugin } from '@voltro/plugin-search'
10
+ import { searchBackend, articlesIndex } from './lib/search'
11
+
12
+ export const env = defineEnv({
13
+ LOG_LEVEL: envVar.enum(['debug', 'info', 'warn', 'error'], { access: 'public', default: 'info' }),
14
+ })
15
+
16
+ export default {
17
+ type: 'api' as const,
18
+ name: '{{capProjectName}}{{capAppName}}',
19
+ store: 'memory' as const,
20
+ plugins: [
21
+ // `backend: searchBackend` is the SAME instance lib/search.ts shares with
22
+ // the boot startup's backfill. Swap it for a vendor engine in lib/search.ts
23
+ // ({ engine: 'typesense' | 'meilisearch' | 'algolia', url, apiKey }) for
24
+ // prod — the indexing + query code stays identical.
25
+ searchPlugin({
26
+ backend: searchBackend,
27
+ indexes: { articles: articlesIndex },
28
+ }),
29
+ ],
30
+ env,
31
+ }
@@ -0,0 +1,47 @@
1
+ // Schema for the {{projectName}} search backend.
2
+ //
3
+ // `articles` is the searchable domain. The search plugin mirrors every
4
+ // write to it into a search index (see app.config.ts + lib/search.ts); the
5
+ // table itself is an ordinary `tenant()`-scoped reactive table.
6
+
7
+ import {
8
+ databaseHandle,
9
+ id,
10
+ table,
11
+ text,
12
+ timestamp,
13
+ type InferRow,
14
+ } from '@voltro/database'
15
+ import { tenant } from '@voltro/plugin-multitenancy'
16
+
17
+ // ---------- Core tables (required by the audit / tenant mixins) ----------
18
+
19
+ export const actors = table('actors', {
20
+ id: id(),
21
+ kind: text().oneOf(['user', 'serviceAccount', 'apiKey', 'system']),
22
+ displayName: text().nullable(),
23
+ createdAt: timestamp().default('now'),
24
+ })
25
+
26
+ export const tenants = table('tenants', {
27
+ id: id(),
28
+ name: text(),
29
+ createdAt: timestamp().default('now'),
30
+ })
31
+
32
+ // ---------- Application table ----------
33
+
34
+ export const articles = table('articles', {
35
+ id: id({ prefix: 'art' }),
36
+ title: text(),
37
+ body: text(),
38
+ tag: text(), // a coarse category — also a search filter
39
+ })
40
+ // tenant() adds tenantId + audit columns (auto-stamped). The search
41
+ // index's `tenantField: 'tenantId'` scopes every query to the caller.
42
+ .with(tenant())
43
+ .reactive()
44
+
45
+ export type Article = InferRow<typeof articles>
46
+
47
+ export const database = databaseHandle({ actors, tenants, articles })
@@ -0,0 +1,27 @@
1
+ // The search wiring, shared so app.config (the plugin) and the boot startup
2
+ // (the backfill) use the SAME backend instance + index spec.
3
+ //
4
+ // `memoryBackend()` is an in-process index — zero infra, perfect for dev. It's
5
+ // a closure over a Map, so two `memoryBackend()` calls would be two SEPARATE
6
+ // indexes; sharing ONE instance here is what lets the startup's `backfillIndex`
7
+ // seed the very index the plugin's `search.query` reads from. Swap it for
8
+ // `{ engine: 'typesense', url, apiKey }` (or meilisearch / algolia) for prod —
9
+ // nothing else changes.
10
+ import { memoryBackend, type IndexSpec, type SearchDoc } from '@voltro/plugin-search'
11
+
12
+ export const searchBackend = memoryBackend()
13
+
14
+ // One index over the `articles` table. `map` flattens a row → a search doc
15
+ // (must include `id`); `tenantField` makes `search.query` auto-filter to the
16
+ // caller's tenant, so one tenant can never see another's hits.
17
+ export const articlesIndex: IndexSpec = {
18
+ index: 'articles',
19
+ map: (row): SearchDoc => ({
20
+ id: row['id'] as string,
21
+ title: row['title'] as string,
22
+ body: row['body'] as string,
23
+ tag: row['tag'] as string,
24
+ tenantId: row['tenantId'] as string,
25
+ }),
26
+ tenantField: 'tenantId',
27
+ }
@@ -0,0 +1,19 @@
1
+ import { assertOwnTenant } from '@voltro/plugin-multitenancy/guard'
2
+ import type { AppContext } from '@voltro/runtime'
3
+
4
+ const execute = async (
5
+ input: { tenantId: string; title: string; body: string; tag: string },
6
+ ctx: AppContext,
7
+ ) => {
8
+ assertOwnTenant(input.tenantId, ctx.request.subject)
9
+ // The framework auto-injects an `art_…` id. The insert commits, then the
10
+ // search plugin's tap indexes it — nothing to do here for search.
11
+ return ctx.store.insert('articles', {
12
+ title: input.title,
13
+ body: input.body,
14
+ tag: input.tag,
15
+ tenantId: input.tenantId,
16
+ })
17
+ }
18
+
19
+ export default execute
@@ -0,0 +1,35 @@
1
+ // Creates an article. On commit, the search plugin's ChangeEvent tap mirrors
2
+ // the new row into the index — so it's searchable immediately, no extra call.
3
+ // `assertOwnTenant` rejects cross-tenant spoofing (typed `TenantMismatch`).
4
+
5
+ import { defineMutation } from '@voltro/protocol'
6
+ import { TenantMismatch } from '@voltro/plugin-multitenancy/guard'
7
+ import { Schema } from 'effect'
8
+
9
+ export const createArticle = defineMutation({
10
+ name: 'articles.create',
11
+ target: {
12
+ table: 'articles',
13
+ op: 'insert',
14
+ shape: (input: { tenantId: string; title: string; body: string; tag: string }) => ({
15
+ title: input.title,
16
+ body: input.body,
17
+ tag: input.tag,
18
+ tenantId: input.tenantId,
19
+ }),
20
+ },
21
+ input: Schema.Struct({
22
+ tenantId: Schema.String,
23
+ title: Schema.NonEmptyString,
24
+ body: Schema.String,
25
+ tag: Schema.String,
26
+ }),
27
+ output: Schema.Struct({
28
+ id: Schema.String,
29
+ title: Schema.String,
30
+ body: Schema.String,
31
+ tag: Schema.String,
32
+ tenantId: Schema.String,
33
+ }),
34
+ error: TenantMismatch,
35
+ })
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@{{projectName}}/{{appName}}",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "voltro dev .",
8
+ "migrate": "voltro migrate",
9
+ "test": "voltro test",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@effect/platform": "^0.96.1",
14
+ "@effect/rpc": "^0.75.1",
15
+ "@voltro/cli": "0.1.0",
16
+ "@voltro/database": "0.1.0",
17
+ "@voltro/env": "0.1.0",
18
+ "@voltro/plugin-multitenancy": "0.1.0",
19
+ "@voltro/plugin-search": "0.1.0",
20
+ "@voltro/protocol": "0.1.0",
21
+ "@voltro/runtime": "0.1.0",
22
+ "effect": "^3.21.2"
23
+ },
24
+ "devDependencies": {
25
+ "@voltro/testing": "0.1.0",
26
+ "typescript": "^5.7.0",
27
+ "vitest": "^3.0.0"
28
+ }
29
+ }
@@ -0,0 +1,14 @@
1
+ import type { AppContext } from '@voltro/runtime'
2
+
3
+ const execute = (_input: Record<string, never>, _ctx: AppContext) => ({
4
+ descriptor: {
5
+ table: 'articles' as const,
6
+ predicate: undefined,
7
+ order: [{ column: 'createdAt' as const, direction: 'desc' as const }],
8
+ take: 100,
9
+ skip: undefined,
10
+ projection: undefined,
11
+ },
12
+ })
13
+
14
+ export default execute
@@ -0,0 +1,19 @@
1
+ // A live list of the tenant's articles. Useful next to the search box to show
2
+ // the full set; the search.query rpc (synthesized by searchPlugin) is what you
3
+ // hit for a query string. tenant() scopes both to the caller automatically.
4
+
5
+ import { defineQuery } from '@voltro/protocol'
6
+ import { Schema } from 'effect'
7
+
8
+ export const listArticles = defineQuery({
9
+ name: 'articles.list',
10
+ input: Schema.Struct({}),
11
+ output: Schema.Struct({
12
+ id: Schema.String,
13
+ title: Schema.String,
14
+ body: Schema.String,
15
+ tag: Schema.String,
16
+ tenantId: Schema.String,
17
+ createdAt: Schema.Date,
18
+ }),
19
+ })
@@ -0,0 +1,41 @@
1
+ // Boot-lifecycle seed — a few articles so search returns results the moment
2
+ // `voltro dev` boots. Idempotent via `upsertByUnique` (+ the fingerprint
3
+ // ledger), so re-running on every boot is safe.
4
+ //
5
+ // The seed store is the RAW store (no authenticated subject), so the `tenant()`
6
+ // auto-fill doesn't fire — we pass `tenantId` explicitly and seed the demo
7
+ // tenant row first, under `acme` (the dev AuthMiddleware's default tenant).
8
+
9
+ import { defineSeed } from '@voltro/database'
10
+
11
+ const TENANT_ID = 'acme'
12
+
13
+ export default defineSeed({
14
+ id: 'articles',
15
+ name: 'Demo articles for search',
16
+ lifecycle: 'boot',
17
+ steps: ({ step }) => [
18
+ step('tenant', async ({ upsertByUnique }) => {
19
+ await upsertByUnique(
20
+ 'tenants',
21
+ { id: TENANT_ID },
22
+ { id: TENANT_ID, name: 'Acme', createdAt: new Date() },
23
+ )
24
+ return { rowsTouched: 1 }
25
+ }),
26
+
27
+ step('articles', async ({ upsertByUnique }) => {
28
+ const now = new Date()
29
+ const rows = [
30
+ { id: 'art_voltro', title: 'Getting started with Voltro', body: 'Reactive end-to-end, multi-tenant by design — scaffold an app and ship.', tag: 'guide' },
31
+ { id: 'art_search', title: 'Full-text search that stays in sync', body: 'The search plugin mirrors every write into the index via the post-commit change tap.', tag: 'guide' },
32
+ { id: 'art_tenant', title: 'Tenant isolation', body: 'Queries auto-filter to the caller tenant — search results included, no leakage.', tag: 'concept' },
33
+ { id: 'art_effect', title: 'Effect under the hood', body: 'Handlers compose with Effect for typed errors, retries, and timeouts.', tag: 'concept' },
34
+ ]
35
+ for (const r of rows) {
36
+ await upsertByUnique('articles', { id: r.id }, { ...r, tenantId: TENANT_ID, createdAt: now })
37
+ }
38
+ return { rowsTouched: rows.length }
39
+ }),
40
+ ],
41
+ })
@@ -0,0 +1,24 @@
1
+ // Boot startup — seed the search index from rows already in the table.
2
+ //
3
+ // The live ChangeEvent tap (wired by searchPlugin) indexes every write AFTER
4
+ // boot. THIS catches what's already there: the demo seed's rows, or — with a
5
+ // persistent store or a freshly-switched vendor engine — every existing row.
6
+ // It uses the SAME `searchBackend` instance the plugin queries (shared via
7
+ // lib/search.ts), so the docs it writes are exactly what `search.query` reads.
8
+
9
+ import type { StartupContext } from '@voltro/runtime'
10
+ import { backfillIndex } from '@voltro/plugin-search'
11
+ import { database } from '../database/schema'
12
+ import { searchBackend, articlesIndex } from '../lib/search'
13
+
14
+ export default async ({ store, log }: StartupContext): Promise<void> => {
15
+ // `database.articles.descriptor` is the builder's "select all" descriptor —
16
+ // a properly-formed query (don't hand-roll a raw `{ table, order: undefined }`,
17
+ // which trips the store's order/limit handling).
18
+ const rows = (await store.query(
19
+ database.articles.descriptor,
20
+ )) as ReadonlyArray<Record<string, unknown>>
21
+
22
+ const count = await backfillIndex(searchBackend, articlesIndex, rows)
23
+ log.info(`search: backfilled ${count} article(s) into the index`)
24
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "api-search",
3
+ "kind": "api",
4
+ "summary": "Full-text search that stays in sync — @voltro/plugin-search mirrors every table write into an index via the post-commit change tap; the synthesized search.query rpc returns tenant-scoped hits. Memory backend (zero infra); swap for Typesense/Meilisearch/Algolia with one config line.",
5
+ "tags": ["api", "search", "full-text", "typesense", "meilisearch", "algolia", "plugin"]
6
+ }
@@ -0,0 +1,69 @@
1
+ // Unit test with `@voltro/testing` — no database, no running server, no search
2
+ // backend. The `articles.create` executor is a tenant-guarded insert: on commit
3
+ // the search plugin's ChangeEvent tap mirrors the row into the index, but that
4
+ // tap is dispatch-time infrastructure (Typesense/Meili/Algolia in prod, the
5
+ // in-memory backend in dev) and is NOT wired into the unit store — so we do not
6
+ // fake it. We cover the DB write + the cross-tenant write guard that gates it,
7
+ // and pin the descriptor's typed error. Run with `voltro test` (vitest).
8
+
9
+ import { describe, it, expect } from 'vitest'
10
+ import { makeTestContext, mockStore } from '@voltro/testing'
11
+ import { TenantMismatch } from '@voltro/plugin-multitenancy/guard'
12
+ import { database } from '../database/schema' // registers articles / actors / tenants
13
+ import { createArticle } from '../mutations/articles.create.mutation'
14
+ import createArticleHandler from '../mutations/articles.create.mutation.server'
15
+
16
+ describe('articles.create', () => {
17
+ it('inserts an article for the caller\'s own tenant', async () => {
18
+ const ctx = makeTestContext({
19
+ subject: { type: 'user', id: 'user_1', tenantId: 'acme' },
20
+ store: mockStore({ articles: [] }),
21
+ })
22
+ const row = await createArticleHandler(
23
+ { tenantId: 'acme', title: 'Hello', body: 'World', tag: 'news' },
24
+ ctx,
25
+ )
26
+
27
+ expect(row['title']).toBe('Hello')
28
+ expect(row['tag']).toBe('news')
29
+ expect(row['tenantId']).toBe('acme')
30
+ expect(row['id'] as string).toMatch(/^art_/)
31
+ })
32
+
33
+ it('rejects a cross-tenant write with a typed TenantMismatch (the spoof guard)', async () => {
34
+ const ctx = makeTestContext({
35
+ subject: { type: 'user', id: 'user_1', tenantId: 'acme' },
36
+ store: mockStore({ articles: [] }),
37
+ })
38
+ // Input claims a DIFFERENT tenant than the subject → assertOwnTenant throws.
39
+ // The executor is async, so the throw surfaces as a rejected promise.
40
+ await expect(
41
+ createArticleHandler(
42
+ { tenantId: 'evil', title: 'x', body: '', tag: '' },
43
+ ctx,
44
+ ),
45
+ ).rejects.toThrow(TenantMismatch)
46
+
47
+ // Nothing was written.
48
+ const rows = await ctx.store.query(database.articles.descriptor)
49
+ expect(rows).toHaveLength(0)
50
+ })
51
+
52
+ it('isolates tenants — t2 never sees t1 articles', async () => {
53
+ const ctx = makeTestContext({
54
+ subject: { type: 'user', id: 'u1', tenantId: 't1' },
55
+ store: mockStore({ articles: [] }),
56
+ })
57
+ await createArticleHandler({ tenantId: 't1', title: 'secret', body: '', tag: '' }, ctx)
58
+
59
+ const seenByT2 = await ctx.withTenant('t2', (c) => c.store.query(database.articles.descriptor))
60
+ expect(seenByT2).toHaveLength(0)
61
+ })
62
+
63
+ it('pins the descriptor error contract — articles.create declares TenantMismatch', () => {
64
+ // The descriptor is value-imported into the browser rpcGroup, so the client
65
+ // decodes this typed failure without a manual error union.
66
+ expect(createArticle.name).toBe('articles.create')
67
+ expect(createArticle.error).toBe(TenantMismatch)
68
+ })
69
+ })
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }
@@ -0,0 +1,51 @@
1
+ # {{projectName}} / {{appName}}
2
+
3
+ Voltro API scaffold (template: **api-versioning**) — full row history +
4
+ time-travel with `@voltro/plugin-versioning`.
5
+
6
+ ## Boot
7
+
8
+ ```bash
9
+ pnpm install
10
+ pnpm --filter @{{projectName}}/{{appName}} dev
11
+ # → http://localhost:4000 (store: memory — zero infra)
12
+ ```
13
+
14
+ ## What this shows
15
+
16
+ - **Every change snapshotted** — `versioningPlugin({ tables: ['documents'] })`
17
+ rides the post-commit ChangeEvent tap and records a FULL snapshot of every
18
+ insert/update/delete. No code in your handlers.
19
+ - **Read it back through handlers** —
20
+ - `documents.history` → `rowHistory(ctx.store, 'documents', id)` — every
21
+ version, oldest to newest.
22
+ - `documents.asOf` → `rowAsOf(ctx.store, 'documents', id, at)` — the value as
23
+ of a past instant (epoch ms), or `null` if it didn't exist then.
24
+ - **Distinct from audit** — `audit()` records WHO/WHEN a row changed; this
25
+ records WHAT it changed to, so you can diff or restore a prior version.
26
+
27
+ ## Try it
28
+
29
+ ```bash
30
+ # create (v1), then edit (v2):
31
+ ID=$(curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
32
+ -d '{"tag":"documents.create","input":{"title":"Spec","content":"draft v1"}}' | python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["id"])')
33
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
34
+ -d "{\"tag\":\"documents.update\",\"input\":{\"id\":\"$ID\",\"content\":\"final v2\"}}" >/dev/null
35
+
36
+ # every version:
37
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
38
+ -d "{\"tag\":\"documents.history\",\"input\":{\"id\":\"$ID\"}}"
39
+ # → [ { version:1, op:"insert", content:"draft v1", changedAt }, { version:2, op:"update", content:"final v2", … } ]
40
+
41
+ # the value as of v1's timestamp → "draft v1" (time-travel):
42
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
43
+ -d "{\"tag\":\"documents.asOf\",\"input\":{\"id\":\"$ID\",\"at\":<v1.changedAt>}}"
44
+ ```
45
+
46
+ ## Production
47
+
48
+ The memory history store is single-process. With a SQL store, history persists
49
+ in `_voltro_row_history`. List the tables you want versioned — high-churn
50
+ tables grow history fast, so version what you need to diff/restore, not
51
+ everything.