@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,70 @@
1
+ // Api app config — a PUBLIC REST API. Read by `voltro dev`.
2
+ //
3
+ // What's wired here:
4
+ // • restRoutes — the four `defineRestRoute` descriptors, registered
5
+ // EXPLICITLY (REST routes are the one primitive that is NOT auto-
6
+ // discovered — they're opt-in public HTTP surface).
7
+ // • openapiPlugin — generates an OpenAPI 3.1 spec from those same
8
+ // descriptors and serves it at GET /openapi.json + a Swagger UI at /docs.
9
+ // Zero hand-maintained API docs: the descriptors ARE the spec.
10
+ // • auth.strategies — an apiKeyStrategy so `Authorization: Bearer <key>`
11
+ // resolves to a scoped Subject. The guarded routes gate on those scopes.
12
+ // • idempotency — POST/PUT/PATCH/DELETE carrying `Idempotency-Key` are
13
+ // deduplicated (replay the first response; 409 while in-flight).
14
+ //
15
+ // `store: 'memory'` keeps first-run zero-infra (no docker). Data resets on
16
+ // every restart — switch to `store: 'postgres'` (+ a local postgres) for
17
+ // anything real.
18
+
19
+ import { apiKeyStrategy } from '@voltro/protocol/apikey'
20
+ import { openapiPlugin } from '@voltro/plugin-openapi'
21
+ import listProducts from './routes/v1/products.list.route'
22
+ import getProduct from './routes/v1/products.get.route'
23
+ import createProduct from './routes/v1/products.create.route'
24
+ import deleteProduct from './routes/v1/products.delete.route'
25
+
26
+ // The descriptors, in one array — handed to BOTH `restRoutes` (to mount them)
27
+ // and `openapiPlugin` (to document them).
28
+ const routes = [listProducts, getProduct, createProduct, deleteProduct]
29
+
30
+ // ─── DEV ONLY ────────────────────────────────────────────────────────────
31
+ // A single hardcoded API key so the guarded routes work the moment you boot:
32
+ //
33
+ // curl -H 'Authorization: Bearer restdemo_devkey' ...
34
+ //
35
+ // The strategy SHA-256-hashes the full bearer token and hands the hash to
36
+ // `resolveKey`; we match it to a fixed scoped Subject. In PRODUCTION, delete
37
+ // the constant below and look the hash up against your own key store (apps
38
+ // store key HASHES, never raw tokens) — or flip on the framework's first-
39
+ // class `apiKeys: true` for admin-gated issue/list/revoke against
40
+ // `_voltro_api_keys`. This demo key is the moral equivalent of a sample
41
+ // password in a README: fine for a starter, never for a real deployment.
42
+ const DEMO_KEY_SHA256 = 'c8917896a6fbdd429ca2410e6e1958953d7421393f649d44fe8fc88c267441cd' // sha256('restdemo_devkey')
43
+
44
+ export default {
45
+ type: 'api' as const,
46
+ name: '{{capProjectName}}{{capAppName}}',
47
+ store: 'memory' as const,
48
+ auth: {
49
+ strategies: [
50
+ apiKeyStrategy({
51
+ prefix: 'restdemo_',
52
+ resolveKey: (hash) =>
53
+ hash === DEMO_KEY_SHA256
54
+ ? { id: 'svc_demo', tenantId: 'acme', scopes: ['products:read', 'products:write'] }
55
+ : null,
56
+ }),
57
+ ],
58
+ },
59
+ plugins: [
60
+ openapiPlugin({
61
+ routes,
62
+ info: { title: '{{capProjectName}} API', version: '1.0.0' },
63
+ // specPath: '/openapi.json' (default) · docsPath: '/docs' (default)
64
+ }),
65
+ ],
66
+ // Public REST endpoints — registered explicitly, NOT auto-discovered.
67
+ restRoutes: routes,
68
+ // Idempotency-Key dedup for the mutating routes (records in _voltro_idempotency).
69
+ idempotency: true,
70
+ }
@@ -0,0 +1,52 @@
1
+ // Schema for the {{projectName}} public-REST backend.
2
+ //
3
+ // `products` is a plain catalog table the REST routes read + write. It
4
+ // intentionally carries NO mixins — a public REST surface is usually a
5
+ // separate, explicitly-scoped API (the routes own their auth + filtering),
6
+ // so we keep the row shape flat and write `id` / `createdAt` by hand in the
7
+ // handler. (For the tenant()-scoped, reactive-over-WebSocket shape, see the
8
+ // `api-backend` template.)
9
+ //
10
+ // `actors` + `tenants` are the framework's core tables — declared for
11
+ // convention parity even though no mixin references them here.
12
+
13
+ import {
14
+ databaseHandle,
15
+ id,
16
+ integer,
17
+ table,
18
+ text,
19
+ timestamp,
20
+ type InferRow,
21
+ } from '@voltro/database'
22
+
23
+ // ---------- Core tables ----------
24
+
25
+ export const actors = table('actors', {
26
+ id: id(),
27
+ kind: text().oneOf(['user', 'serviceAccount', 'apiKey', 'system']),
28
+ displayName: text().nullable(),
29
+ createdAt: timestamp().default('now'),
30
+ })
31
+
32
+ export const tenants = table('tenants', {
33
+ id: id(),
34
+ name: text(),
35
+ createdAt: timestamp().default('now'),
36
+ })
37
+
38
+ // ---------- Application table ----------
39
+
40
+ export const products = table('products', {
41
+ id: id({ prefix: 'prod' }),
42
+ name: text(),
43
+ priceCents: integer(),
44
+ createdAt: timestamp().default('now'),
45
+ })
46
+ // Opt into the matcher engine so a future reactive WS query over the
47
+ // same table stays live. The REST routes don't need it, but it's free.
48
+ .reactive()
49
+
50
+ export type Product = InferRow<typeof products>
51
+
52
+ export const database = databaseHandle({ actors, tenants, products })
@@ -0,0 +1,30 @@
1
+ // Shared wire-shape for a product + a row→wire mapper.
2
+ //
3
+ // Imported by every `*.route.tsx` AND by `openapiPlugin({ routes })` (both
4
+ // server-side — REST route files are NOT pulled into the browser rpcGroup,
5
+ // so they're free to import server modules). One source of truth means the
6
+ // generated OpenAPI schema and the runtime encode can never disagree.
7
+
8
+ import { Schema } from 'effect'
9
+
10
+ export const Product = Schema.Struct({
11
+ id: Schema.String,
12
+ name: Schema.String,
13
+ priceCents: Schema.Number,
14
+ // ISO-8601 string on the wire (we stringify the Date in the mapper below).
15
+ createdAt: Schema.String,
16
+ })
17
+ export type Product = Schema.Schema.Type<typeof Product>
18
+
19
+ /**
20
+ * Coerce a raw store row into the wire shape. Two store-path quirks this
21
+ * absorbs: a `timestamp` column can come back as a `Date` OR an ISO string,
22
+ * and an `integer` column can come back as a STRING on some dialects — so we
23
+ * normalise both before the strict output Schema encodes them.
24
+ */
25
+ export const toProduct = (r: Record<string, unknown>): Product => ({
26
+ id: r['id'] as string,
27
+ name: r['name'] as string,
28
+ priceCents: Number(r['priceCents']),
29
+ createdAt: r['createdAt'] instanceof Date ? r['createdAt'].toISOString() : String(r['createdAt']),
30
+ })
@@ -0,0 +1,27 @@
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-openapi": "0.1.0",
18
+ "@voltro/protocol": "0.1.0",
19
+ "@voltro/runtime": "0.1.0",
20
+ "effect": "^3.21.2"
21
+ },
22
+ "devDependencies": {
23
+ "@voltro/testing": "0.1.0",
24
+ "typescript": "^5.7.0",
25
+ "vitest": "^3.0.0"
26
+ }
27
+ }
@@ -0,0 +1,45 @@
1
+ // POST /v1/products — create a product. GUARDED + IDEMPOTENT.
2
+ //
3
+ // - `guards: [requireScope('products:write')]` → 403 unless the resolved
4
+ // Subject carries that scope. The app's `apiKeyStrategy` (see app.config)
5
+ // maps the demo bearer token to a subject WITH the scope; an anonymous
6
+ // caller is rejected.
7
+ // - `idempotency: true` in app.config makes a POST carrying an
8
+ // `Idempotency-Key` header replay-safe: a retry with the same key replays
9
+ // the first response instead of inserting twice.
10
+ //
11
+ // The `body` is parsed from the JSON request body and decoded against the
12
+ // `body` sub-schema (a bad shape → 400 before the handler runs).
13
+
14
+ import { defineRestRoute, requireScope } from '@voltro/protocol/rest'
15
+ import { Schema } from 'effect'
16
+ import { randomUUID } from 'node:crypto'
17
+ import type { DataStore } from '@voltro/runtime'
18
+ import { Product, toProduct } from '../../lib/product'
19
+
20
+ export default defineRestRoute({
21
+ method: 'POST',
22
+ path: '/v1/products',
23
+ input: Schema.Struct({
24
+ body: Schema.Struct({
25
+ name: Schema.NonEmptyString,
26
+ priceCents: Schema.Number.pipe(Schema.int(), Schema.greaterThanOrEqualTo(0)),
27
+ }),
28
+ }),
29
+ output: Product,
30
+ summary: 'Create a product',
31
+ guards: [requireScope('products:write')],
32
+ handler: async ({ body }, ctx) => {
33
+ const store = ctx.store as DataStore
34
+ // Explicit id + createdAt — the REST store path doesn't run the mutation
35
+ // middleware that auto-injects them, so we own them here.
36
+ const row = {
37
+ id: `prod_${randomUUID().replace(/-/g, '')}`,
38
+ name: body.name,
39
+ priceCents: body.priceCents,
40
+ createdAt: new Date(),
41
+ }
42
+ await store.insert('products', row as never)
43
+ return toProduct(row)
44
+ },
45
+ })
@@ -0,0 +1,25 @@
1
+ // DELETE /v1/products/:id — GUARDED. Deletes one product or 404.
2
+ //
3
+ // Same scope guard as create. `DELETE` is one of the idempotent methods the
4
+ // `Idempotency-Key` dedup covers (app.config `idempotency: true`).
5
+
6
+ import { defineRestRoute, requireScope } from '@voltro/protocol/rest'
7
+ import { Schema } from 'effect'
8
+ import type { DataStore } from '@voltro/runtime'
9
+
10
+ export default defineRestRoute({
11
+ method: 'DELETE',
12
+ path: '/v1/products/:id',
13
+ input: Schema.Struct({
14
+ params: Schema.Struct({ id: Schema.NonEmptyString }),
15
+ }),
16
+ output: Schema.Struct({ id: Schema.String, deleted: Schema.Boolean }),
17
+ summary: 'Delete a product by id',
18
+ guards: [requireScope('products:write')],
19
+ handler: async ({ params }, ctx) => {
20
+ const store = ctx.store as DataStore
21
+ const ok = await store.delete('products', params.id)
22
+ if (!ok) throw { status: 404, message: `product '${params.id}' not found` }
23
+ return { id: params.id, deleted: true }
24
+ },
25
+ })
@@ -0,0 +1,29 @@
1
+ // GET /v1/products/:id — public, one product or 404.
2
+ //
3
+ // `:id` is a PATH param — the desugar re-derives it from the matched pattern
4
+ // into `params`. A handler `throw { status, message }` maps to that HTTP code;
5
+ // anything else → 500.
6
+
7
+ import { defineRestRoute } from '@voltro/protocol/rest'
8
+ import { Schema } from 'effect'
9
+ import { eq } from '@voltro/database'
10
+ import type { DataStore } from '@voltro/runtime'
11
+ import { database } from '../../database/schema'
12
+ import { Product, toProduct } from '../../lib/product'
13
+
14
+ export default defineRestRoute({
15
+ method: 'GET',
16
+ path: '/v1/products/:id',
17
+ input: Schema.Struct({
18
+ params: Schema.Struct({ id: Schema.NonEmptyString }),
19
+ }),
20
+ output: Product,
21
+ summary: 'Get a product by id',
22
+ handler: async ({ params }, ctx) => {
23
+ const store = ctx.store as DataStore
24
+ const rows = await store.query(database.products.where(eq('id', params.id)).limit(1).descriptor)
25
+ const row = rows[0]
26
+ if (row === undefined) throw { status: 404, message: `product '${params.id}' not found` }
27
+ return toProduct(row as Record<string, unknown>)
28
+ },
29
+ })
@@ -0,0 +1,43 @@
1
+ // GET /v1/products?limit=&cursor= — public, cursor-paginated list.
2
+ //
3
+ // The desugar parses the query string into the `query` shape and decodes it.
4
+ // `Schema.NumberFromString` turns the string query param "20" into a number;
5
+ // we clamp it in the handler. Cursor pagination keys off the sortable `prod_…`
6
+ // TypeID (no OFFSET scan): pass the last row's id back as `cursor`.
7
+
8
+ import { defineRestRoute } from '@voltro/protocol/rest'
9
+ import { Schema } from 'effect'
10
+ import { gt } from '@voltro/database'
11
+ import type { DataStore } from '@voltro/runtime'
12
+ import { database } from '../../database/schema'
13
+ import { Product, toProduct } from '../../lib/product'
14
+
15
+ export default defineRestRoute({
16
+ method: 'GET',
17
+ path: '/v1/products',
18
+ input: Schema.Struct({
19
+ query: Schema.Struct({
20
+ limit: Schema.optional(Schema.NumberFromString),
21
+ cursor: Schema.optional(Schema.String),
22
+ }),
23
+ }),
24
+ output: Schema.Struct({
25
+ data: Schema.Array(Product),
26
+ nextCursor: Schema.NullOr(Schema.String),
27
+ }),
28
+ summary: 'List products',
29
+ description: 'Cursor-paginated. Pass the response `nextCursor` back as `cursor` for the next page.',
30
+ handler: async ({ query }, ctx) => {
31
+ const store = ctx.store as DataStore
32
+ const limit = Math.min(Math.max(query.limit ?? 20, 1), 100)
33
+
34
+ const base = query.cursor
35
+ ? database.products.where(gt('id', query.cursor))
36
+ : database.products
37
+ const rows = await store.query(base.orderBy('id', 'asc').limit(limit).descriptor)
38
+
39
+ const data = rows.map((r) => toProduct(r as Record<string, unknown>))
40
+ const nextCursor = data.length === limit ? data[data.length - 1]!.id : null
41
+ return { data, nextCursor }
42
+ },
43
+ })
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "api-rest",
3
+ "kind": "api",
4
+ "summary": "Public REST API: defineRestRoute endpoints (GET/POST/DELETE, query+path+body) + scope guards + Idempotency-Key + @voltro/plugin-openapi (Swagger UI at /docs).",
5
+ "tags": ["api", "rest", "openapi", "swagger", "public-api"]
6
+ }
@@ -0,0 +1,53 @@
1
+ // Unit test with `@voltro/testing` — no database, no running HTTP server. A
2
+ // `defineRestRoute` carries its logic in a plain `handler(input, ctx)`, so the
3
+ // test calls that handler with a `RestRouteContext` built over the SAME
4
+ // mixin-wrapped `ctx.store` a route gets at runtime, and asserts the wire shape
5
+ // it returns. The REST routing + guard + idempotency layers are dispatch-time
6
+ // concerns (integration), not exercised here. Run with `voltro test` (vitest).
7
+
8
+ import { describe, it, expect } from 'vitest'
9
+ import { makeTestContext, mockStore } from '@voltro/testing'
10
+ import { anonymousSubject } from '@voltro/protocol'
11
+ import type { RestRouteContext } from '@voltro/protocol/rest'
12
+ import { database } from '../database/schema' // registers products / actors / tenants
13
+ import createProduct from '../routes/v1/products.create.route'
14
+
15
+ // A REST handler receives `{ subject, headers, store }` (not the full rpc
16
+ // AppContext). Build that shape over the real test store so `store.insert`
17
+ // runs the same mixin/validation path it does in production. `products` carries
18
+ // no tenant() mixin, so the subject identity is irrelevant to the insert.
19
+ const restCtx = (store: unknown): RestRouteContext => ({
20
+ subject: anonymousSubject(null),
21
+ headers: {},
22
+ store,
23
+ })
24
+
25
+ describe('POST /v1/products', () => {
26
+ it('inserts a product and returns the wire shape', async () => {
27
+ const ctx = makeTestContext({ store: mockStore({ products: [] }) })
28
+ const product = await createProduct.handler(
29
+ { body: { name: 'Widget', priceCents: 1999 } },
30
+ restCtx(ctx.store),
31
+ )
32
+
33
+ expect(product.name).toBe('Widget')
34
+ expect(product.priceCents).toBe(1999)
35
+ expect(product.id).toMatch(/^prod_/) // handler-minted id
36
+ // `createdAt` is normalised to an ISO-8601 string by the row→wire mapper.
37
+ expect(typeof product.createdAt).toBe('string')
38
+ expect(Number.isNaN(Date.parse(product.createdAt))).toBe(false)
39
+ })
40
+
41
+ it('persists the row to the store (readable back through the descriptor)', async () => {
42
+ const ctx = makeTestContext({ store: mockStore({ products: [] }) })
43
+ const product = await createProduct.handler(
44
+ { body: { name: 'Gadget', priceCents: 500 } },
45
+ restCtx(ctx.store),
46
+ )
47
+
48
+ const rows = await ctx.store.query(database.products.descriptor)
49
+ expect(rows).toHaveLength(1)
50
+ expect(rows[0]!['id']).toBe(product.id)
51
+ expect(rows[0]!['name']).toBe('Gadget')
52
+ })
53
+ })
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }
@@ -0,0 +1,106 @@
1
+ # {{capProjectName}} {{capAppName}} — the SaaS plugin bundle
2
+
3
+ Four cross-cutting **SaaS plugins** wired turnkey, in one cohesive domain — the
4
+ billing / notifications / analytics / presence slice of the framework end to
5
+ end. A single `projects.create` mutation pulls three of them together.
6
+
7
+ ## What it demonstrates
8
+
9
+ - **Billing** ([`@voltro/plugin-billing`](https://voltro.cloud/docs/plugins/overview)) —
10
+ subscriptions + entitlements over the in-memory `mock` provider. The `free`
11
+ plan caps `projects` at 3; `requireEntitlement(ctx, 'projects', 1)` gates the
12
+ create and fails typed **`EntitlementExceeded`** on the 4th.
13
+ - **Notifications** ([`@voltro/plugin-notifications`](https://voltro.cloud/docs/plugins/overview)) —
14
+ one `NotificationService.send(...)` across channels + a durable in-app inbox.
15
+ - **Analytics** ([`@voltro/plugin-analytics-postgres`](https://voltro.cloud/docs/plugins/overview)) —
16
+ `useAnalytics().track('project_created', …)`. Noop-safe out of the box; one
17
+ config line + a SQL store makes it durable (see below).
18
+ - **Presence** ([`@voltro/plugin-presence`](https://voltro.cloud/docs/plugins/overview)) —
19
+ ephemeral "who's online": `presence.heartbeat` / `list` / `leave` + the
20
+ `usePresence(channel)` hook.
21
+
22
+ Each plugin contributes its OWN routes + tables automatically — you wire them in
23
+ `app.config.ts` and consume their services in handlers. Boots **zero-infra**
24
+ (`store: 'memory'`).
25
+
26
+ ## The bundle, in one handler
27
+
28
+ ```ts
29
+ // mutations/projects.create.mutation.server.ts (Effect-mode)
30
+ yield* requireEntitlement(ctx, 'projects', 1) // billing quota gate
31
+ const row = yield* (yield* EffectStore).insert('projects', { name: input.name })
32
+ yield* (yield* useAnalytics()).track({ name: 'project_created', subjectId: ctx.request.subject.id })
33
+ yield* Effect.promise(() => (yield* NotificationService).send({
34
+ to: ctx.request.subject.id ?? 'system', category: 'project', title: 'Project created', body: `"${input.name}" is live.`,
35
+ }))
36
+ ```
37
+
38
+ ## Run it
39
+
40
+ ```bash
41
+ voltro dev .
42
+ ```
43
+
44
+ `projects.create` / `projects.list` and the plugin routes (`billing.*`,
45
+ `presence.*`, `notifications.*`) are rpc procedures — call them from a web app
46
+ with `useMutation` / `useSubscription`, or over HTTP via the dev inspect endpoint:
47
+
48
+ ```bash
49
+ # Create projects — the free plan allows 3
50
+ for i in 1 2 3; do
51
+ curl -s -X POST http://localhost:4000/_voltro/inspect/invoke \
52
+ -H 'content-type: application/json' \
53
+ -d "{\"tag\":\"projects.create\",\"input\":{\"name\":\"Project $i\"}}"
54
+ done
55
+ # → { "ok": true, "result": { "id": "proj_…", … } }
56
+
57
+ # The 4th trips the billing entitlement
58
+ curl -s -X POST http://localhost:4000/_voltro/inspect/invoke \
59
+ -H 'content-type: application/json' \
60
+ -d '{"tag":"projects.create","input":{"name":"Project 4"}}'
61
+ # → { "ok": false, "error": { "_tag": "EntitlementExceeded", "entitlement": "projects", "limit": 3, … } }
62
+
63
+ # Presence — announce yourself, then read the roster
64
+ curl -s -X POST http://localhost:4000/_voltro/inspect/invoke \
65
+ -H 'content-type: application/json' \
66
+ -d '{"tag":"presence.heartbeat","input":{"channel":"lobby"}}'
67
+ ```
68
+
69
+ ## Enable durable analytics
70
+
71
+ `useAnalytics().track(...)` is noop-safe without a sink. To STORE + query events
72
+ (`aggregate` / `timeseries` / `topN`), add a sink AND a SQL store — the
73
+ postgres-lite sink needs a `SqlClient`:
74
+
75
+ ```ts
76
+ // app.config.ts
77
+ import { postgresAnalytics } from '@voltro/plugin-analytics-postgres'
78
+
79
+ export default {
80
+ type: 'api' as const, name: 'AcmeApi',
81
+ store: 'postgres' as const, // or 'sqlite' (file:./.voltro/db.sqlite)
82
+ analytics: postgresAnalytics(), // events → _voltro_events
83
+ plugins: [ /* … */ ],
84
+ }
85
+ ```
86
+
87
+ ## Going to production
88
+
89
+ | Want… | Do |
90
+ |---|---|
91
+ | Real subscriptions / checkout | `billingPlugin({ provider: 'stripe', apiKey, webhookSecret, plans })` with real `priceId`s |
92
+ | Email / Slack / SMS notifications | add `emailChannel()` / `webhookChannel()` / `smsChannel()` to `channels` |
93
+ | Durable analytics + warehouse | `postgresAnalytics()` on postgres, or swap to `@voltro/plugin-clickhouse` / `@voltro/plugin-duckdb` at scale |
94
+ | Live presence roster in the UI | subscribe a reactive query over `_voltro_presence`, or use `usePresence(channel)` |
95
+
96
+ ## Anti-patterns
97
+
98
+ - **Authoring your own `notifications.*` / `billing.*` / `presence.*` procedures.**
99
+ The plugins own those tag namespaces — boot fails on a collision. Use the
100
+ plugin routes + services, don't hand-roll them.
101
+ - **Expecting analytics to persist on `memory`.** The postgres-lite sink needs
102
+ a SqlClient. `track()` is reachable + safe on memory, but events only land in
103
+ `_voltro_events` once you wire a sink + a SQL store.
104
+ - **Skipping the entitlement gate on a paid action.** A scope says "may you call
105
+ this"; an entitlement says "do you have quota left". A create-project action
106
+ needs the quota check — `requireEntitlement` is one line.
@@ -0,0 +1,51 @@
1
+ // Api app config — the SaaS plugin bundle. Read by `voltro dev`.
2
+ //
3
+ // Four cross-cutting SaaS plugins, wired turnkey. Each contributes its own
4
+ // routes + tables; the `projects.create` mutation pulls three of them together
5
+ // (billing entitlement gate → analytics event → notification).
6
+ //
7
+ // `store: 'memory'` keeps first-run zero-infra. NOTE the analytics caveat below.
8
+
9
+ import { billingPlugin } from '@voltro/plugin-billing'
10
+ import { presencePlugin } from '@voltro/plugin-presence'
11
+ import { notificationsPlugin, consoleChannel } from '@voltro/plugin-notifications'
12
+
13
+ export default {
14
+ type: 'api' as const,
15
+ name: '{{capProjectName}}{{capAppName}}',
16
+ store: 'memory' as const,
17
+ plugins: [
18
+ // ── Billing ──────────────────────────────────────────────────────────
19
+ // Subscriptions + entitlements over a pluggable provider. 'mock' is the
20
+ // in-memory provider (no Stripe key) — great for dev. `plans` is the single
21
+ // source of tier→quota truth; the `free` default caps `projects` at 3, so
22
+ // the 4th `projects.create` fails typed `EntitlementExceeded`. Swap
23
+ // provider:'stripe' (+ STRIPE_SECRET_KEY) + real priceIds for production.
24
+ billingPlugin({
25
+ provider: 'mock',
26
+ plans: {
27
+ free: { entitlements: { projects: 3 } },
28
+ pro: { priceId: 'price_demo_pro', entitlements: { projects: 'unlimited' } },
29
+ },
30
+ }),
31
+ // ── Notifications ────────────────────────────────────────────────────
32
+ // One `send` across channels + a durable in-app inbox (added automatically).
33
+ // consoleChannel() prints to stdout for dev; add email/webhook/sms/push for
34
+ // real delivery. Reach the inbox from the browser with useInbox()/useUnreadCount().
35
+ notificationsPlugin({ channels: [consoleChannel()] }),
36
+ // ── Presence ─────────────────────────────────────────────────────────
37
+ // Ephemeral "who's online" — presence.heartbeat / list / leave routes +
38
+ // the usePresence(channel) hook. Swept `_voltro_presence` table; cross-instance.
39
+ presencePlugin(),
40
+ ],
41
+
42
+ // ── Analytics ──────────────────────────────────────────────────────────
43
+ // `useAnalytics().track(...)` is called in projects.create and is NOOP-SAFE
44
+ // with no sink configured (it never crashes) — so the event API is reachable
45
+ // on the zero-infra memory store. To actually STORE + query events, add a
46
+ // sink AND a SQL store (the postgres-lite sink needs a SqlClient):
47
+ //
48
+ // import { postgresAnalytics } from '@voltro/plugin-analytics-postgres'
49
+ // store: 'postgres' as const, // or 'sqlite' (file:./.voltro/db.sqlite)
50
+ // analytics: postgresAnalytics(), // events → _voltro_events; aggregate/timeseries/topN
51
+ }
@@ -0,0 +1,50 @@
1
+ // Schema for the {{projectName}} SaaS backend.
2
+ //
3
+ // One tenant-scoped `projects` table — the SaaS plugins (billing entitlements,
4
+ // notifications, analytics, presence) layer on top via `app.config.ts`; they
5
+ // contribute their OWN tables (`_voltro_billing_*`, `notification_*`,
6
+ // `_voltro_events`, `_voltro_presence`) automatically, so you don't declare them.
7
+ //
8
+ // `actors` + `tenants` are the framework's core tables (the audit / tenant
9
+ // mixins reference them).
10
+
11
+ import {
12
+ databaseHandle,
13
+ id,
14
+ table,
15
+ text,
16
+ timestamp,
17
+ type InferRow,
18
+ } from '@voltro/database'
19
+ import { tenant } from '@voltro/plugin-multitenancy'
20
+
21
+ // ---------- Core tables ----------
22
+
23
+ export const actors = table('actors', {
24
+ id: id(),
25
+ kind: text().oneOf(['user', 'serviceAccount', 'apiKey', 'system']),
26
+ displayName: text().nullable(),
27
+ createdAt: timestamp().default('now'),
28
+ })
29
+
30
+ export const tenants = table('tenants', {
31
+ id: id(),
32
+ name: text(),
33
+ createdAt: timestamp().default('now'),
34
+ })
35
+
36
+ // ---------- Application table ----------
37
+
38
+ export const projects = table('projects', {
39
+ id: id({ prefix: 'proj' }),
40
+ name: text(),
41
+ })
42
+ // tenant() pulls audit() transitively → tenantId + createdAt / updatedAt /
43
+ // createdBy / updatedBy (auto-stamped). Creating a project is gated by the
44
+ // billing `projects` entitlement (see mutations/projects.create).
45
+ .with(tenant())
46
+ .reactive()
47
+
48
+ export type Project = InferRow<typeof projects>
49
+
50
+ export const database = databaseHandle({ actors, tenants, projects })
@@ -0,0 +1,52 @@
1
+ // One mutation, three plugins. Effect-mode so it can `yield*` the plugin
2
+ // services the framework provides in the per-request stack:
3
+ //
4
+ // 1. BILLING — requireEntitlement check-and-decrements the tenant's
5
+ // `projects` quota (free = 3). Over quota → typed
6
+ // EntitlementExceeded, the row is never written.
7
+ // 2. ANALYTICS — useAnalytics().track(...) records a `project_created`
8
+ // event. Noop-safe with no sink configured (see app.config).
9
+ // 3. NOTIFICATIONS — NotificationService.send(...) drops a note in the
10
+ // owner's in-app inbox + the console channel.
11
+
12
+ import { Effect } from 'effect'
13
+ import type { AppContext } from '@voltro/runtime'
14
+ import { EffectStore, useAnalytics } from '@voltro/runtime'
15
+ import { requireEntitlement } from '@voltro/plugin-billing'
16
+ import { NotificationService } from '@voltro/plugin-notifications'
17
+
18
+ const execute = (input: { name: string }, ctx: AppContext) =>
19
+ Effect.gen(function* () {
20
+ // 1. Billing gate — fails closed (typed EntitlementExceeded) when the
21
+ // tenant has no `projects` quota left. Runs BEFORE the write.
22
+ yield* requireEntitlement(ctx, 'projects', 1)
23
+
24
+ // 2. Write the row. `tenant()` auto-stamps tenantId + audit columns.
25
+ const store = yield* EffectStore
26
+ const row = yield* store.insert('projects', { name: input.name })
27
+
28
+ // 3. Analytics — fire-and-forget event.
29
+ const analytics = yield* useAnalytics()
30
+ yield* analytics.track({
31
+ name: 'project_created',
32
+ subjectId: ctx.request.subject.id,
33
+ properties: { name: input.name },
34
+ })
35
+
36
+ // 4. Notify the owner (in-app inbox + console).
37
+ const notify = yield* NotificationService
38
+ yield* Effect.promise(() => notify.send({
39
+ to: ctx.request.subject.id ?? 'system',
40
+ category: 'project',
41
+ title: 'Project created',
42
+ body: `"${input.name}" is live.`,
43
+ }))
44
+
45
+ return {
46
+ id: row['id'] as string,
47
+ name: row['name'] as string,
48
+ tenantId: row['tenantId'] as string,
49
+ }
50
+ })
51
+
52
+ export default execute