@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,43 @@
1
+ // `orders.approve` — EXECUTOR (server-only).
2
+ //
3
+ // Injects the `approval` signal that wakes the parked `orders.fulfill`
4
+ // run for this order. The workflow's `idempotencyKey` (`fulfill:<id>`)
5
+ // makes its executionId a deterministic function of the payload, so we
6
+ // re-derive it from `{ orderId, tenantId }` and address the signal by
7
+ // executionId — no need to track the run handle from the place mutation.
8
+ //
9
+ // `ctx.workflows.signal(target, name, payload)` writes a `signal-sent`
10
+ // row to the run's events log; the workflow's `awaitSignal('approval')`
11
+ // poll picks it up (sub-second) and parses the payload against its
12
+ // `{ approved: boolean }` schema.
13
+
14
+ import { assertOwnTenant } from '@voltro/plugin-multitenancy/guard'
15
+ import type { AppContext } from '@voltro/runtime'
16
+
17
+ const execute = async (
18
+ input: { orderId: string; tenantId: string; approved: boolean },
19
+ ctx: AppContext,
20
+ ) => {
21
+ assertOwnTenant(input.tenantId, ctx.request.subject)
22
+
23
+ if (!ctx.workflows) {
24
+ throw new Error('orders.approve: workflow runtime is not available in this context')
25
+ }
26
+
27
+ // Deterministic executionId for the running fulfillment workflow —
28
+ // same payload the `order.placed` trigger started it with.
29
+ const executionId = await ctx.workflows.executionId('orders.fulfill', {
30
+ orderId: input.orderId,
31
+ tenantId: input.tenantId,
32
+ })
33
+
34
+ const { eventId } = await ctx.workflows.signal(
35
+ { executionId },
36
+ 'approval',
37
+ { approved: input.approved },
38
+ )
39
+
40
+ return { orderId: input.orderId, eventId }
41
+ }
42
+
43
+ export default execute
@@ -0,0 +1,23 @@
1
+ // `orders.approve` — DESCRIPTOR (browser-safe).
2
+ //
3
+ // The human-approval surface: a client calls this with the order id +
4
+ // the decision; the executor injects the matching `approval` signal into
5
+ // the parked `orders.fulfill` run. This is a non-row-writing mutation
6
+ // (it pokes a workflow, not the orders table), so it declares no
7
+ // optimistic `target` and no `op` against `orders`.
8
+
9
+ import { defineMutation } from '@voltro/protocol'
10
+ import { Schema } from 'effect'
11
+
12
+ export const approveOrder = defineMutation({
13
+ name: 'orders.approve',
14
+ input: Schema.Struct({
15
+ orderId: Schema.String,
16
+ tenantId: Schema.String,
17
+ approved: Schema.Boolean,
18
+ }),
19
+ output: Schema.Struct({
20
+ orderId: Schema.String,
21
+ eventId: Schema.String,
22
+ }),
23
+ })
@@ -0,0 +1,45 @@
1
+ // `orders.place` — EXECUTOR (server-only, default export).
2
+ //
3
+ // Inserts the order in the mutation transaction, then emits a domain
4
+ // event AFTER commit. `ctx.events.emit(...)` is post-commit safe: the
5
+ // event is recorded + the `order.placed` trigger fans out only once the
6
+ // row is durably written, so a rollback never starts a fulfillment
7
+ // workflow for an order that doesn't exist.
8
+
9
+ import { assertOwnTenant } from '@voltro/plugin-multitenancy/guard'
10
+ import type { AppContext } from '@voltro/runtime'
11
+
12
+ const execute = async (
13
+ input: { tenantId: string; customerName: string; amountCents: number },
14
+ ctx: AppContext,
15
+ ) => {
16
+ // Cross-tenant write guard — reject a caller spoofing input.tenantId.
17
+ assertOwnTenant(input.tenantId, ctx.request.subject)
18
+
19
+ // Framework auto-injects an `order_…` id + the tenant/audit columns.
20
+ const row = await ctx.store.insert('orders', {
21
+ status: 'placed',
22
+ customerName: input.customerName,
23
+ amountCents: input.amountCents,
24
+ tenantId: input.tenantId,
25
+ })
26
+
27
+ // Post-commit fan-out → the `order.placed` trigger starts the
28
+ // `orders.fulfill` workflow. `ctx.events` is only present when the
29
+ // runtime wired the events facade (it always is under `voltro dev`);
30
+ // guard so a unit-test ctx without it doesn't throw.
31
+ await ctx.events?.emit('order.placed', {
32
+ orderId: row['id'] as string,
33
+ tenantId: input.tenantId,
34
+ })
35
+
36
+ return {
37
+ id: row['id'] as string,
38
+ status: row['status'] as string,
39
+ customerName: row['customerName'] as string,
40
+ amountCents: row['amountCents'] as number,
41
+ tenantId: row['tenantId'] as string,
42
+ }
43
+ }
44
+
45
+ export default execute
@@ -0,0 +1,40 @@
1
+ // `orders.place` — DESCRIPTOR (browser-safe: schema + target + typed
2
+ // error only; NO node/database/server imports).
3
+ //
4
+ // `TenantMismatch` is imported from the `/guard` subpath, NOT the
5
+ // package root — the root re-exports the schema mixin, which would drag
6
+ // `@voltro/database` into the client rpcGroup bundle. `/guard` is the
7
+ // browser-safe surface for the typed error you declare here.
8
+
9
+ import { defineMutation } from '@voltro/protocol'
10
+ import { TenantMismatch } from '@voltro/plugin-multitenancy/guard'
11
+ import { Schema } from 'effect'
12
+
13
+ export const placeOrder = defineMutation({
14
+ name: 'orders.place',
15
+ // Auto-optimistic: every query whose `source: 'orders'` matches gets a
16
+ // placeholder row prepended; the server delta replaces it on commit.
17
+ target: {
18
+ table: 'orders',
19
+ op: 'insert',
20
+ shape: (input: { tenantId: string; customerName: string; amountCents: number }) => ({
21
+ status: 'placed',
22
+ customerName: input.customerName,
23
+ amountCents: input.amountCents,
24
+ tenantId: input.tenantId,
25
+ }),
26
+ },
27
+ input: Schema.Struct({
28
+ tenantId: Schema.String,
29
+ customerName: Schema.NonEmptyString,
30
+ amountCents: Schema.Int.pipe(Schema.greaterThan(0)),
31
+ }),
32
+ output: Schema.Struct({
33
+ id: Schema.String,
34
+ status: Schema.String,
35
+ customerName: Schema.String,
36
+ amountCents: Schema.Number,
37
+ tenantId: Schema.String,
38
+ }),
39
+ error: TenantMismatch,
40
+ })
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@{{projectName}}/{{appName}}",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "voltro dev .",
8
+ "test": "voltro test",
9
+ "typecheck": "tsc --noEmit"
10
+ },
11
+ "dependencies": {
12
+ "@effect/platform": "^0.96.1",
13
+ "@effect/rpc": "^0.75.1",
14
+ "@voltro/cli": "0.1.0",
15
+ "@voltro/database": "0.1.0",
16
+ "@voltro/env": "0.1.0",
17
+ "@voltro/plugin-multitenancy": "0.1.0",
18
+ "@voltro/protocol": "0.1.0",
19
+ "@voltro/runtime": "0.1.0",
20
+ "@voltro/workflow": "0.1.0",
21
+ "effect": "^3.21.2"
22
+ },
23
+ "devDependencies": {
24
+ "@voltro/testing": "0.1.0",
25
+ "typescript": "^5.7.0",
26
+ "vitest": "^3.0.0"
27
+ }
28
+ }
@@ -0,0 +1,37 @@
1
+ // `nightlyReport` — a cron schedule (single-file `*.cron.tsx`).
2
+ //
3
+ // Runs at 02:00 UTC every day. `timezone` is MANDATORY — the cron is
4
+ // interpreted in this IANA zone, never the container's local TZ. The
5
+ // expression is validated at boot (a typo throws in the discovery log,
6
+ // it doesn't silently never-fire).
7
+ //
8
+ // The handler is "a mutation the clock invokes": `ctx.app` is the same
9
+ // AppContext shape a mutation gets (store + interceptors). For durable
10
+ // multi-step work, prefer the `workflow: { name, payload }` form
11
+ // instead of a handler.
12
+ //
13
+ // Zero-config coordination: on `store: 'memory'` this runs single-
14
+ // process; on a multi-instance SQL store the framework advisory-lock
15
+ // coordinates so exactly one replica fires each tick.
16
+
17
+ import { defineSchedule } from '@voltro/runtime'
18
+ import { count, eq } from '@voltro/database'
19
+ import { database } from '../database/schema'
20
+
21
+ export default defineSchedule({
22
+ name: 'nightlyReport',
23
+ cron: '0 2 * * *', // 02:00 every day
24
+ timezone: 'UTC',
25
+ description: 'Logs a per-status order tally each night.',
26
+ handler: async (ctx) => {
27
+ // Tally placed-but-not-yet-shipped orders as a simple health signal.
28
+ const open = await ctx.app.store.query(
29
+ database.orders.where(eq('status', 'placed')).aggregate({ total: count() }).descriptor,
30
+ )
31
+ const total = (open[0] as { total?: number } | undefined)?.total ?? 0
32
+ console.info(`[nightlyReport] fired for ${ctx.scheduledAt.toISOString()} — ${total} order(s) still 'placed'`)
33
+ },
34
+ // optional, all default-safe:
35
+ onOverlap: 'skip',
36
+ maxRuntimeMs: 5 * 60_000,
37
+ })
@@ -0,0 +1,33 @@
1
+ // `warm` — a run-once startup hook (`*.startup.tsx`).
2
+ //
3
+ // Default-exports a function (no `define*` wrapper). Runs ONCE after
4
+ // migrations + seeds, when the rpc server is listening. Use it for
5
+ // long-lived boot work: warm a cache, open a broker consumer / upstream
6
+ // websocket, start a background interval, register a process singleton —
7
+ // and register a teardown via `onShutdown`.
8
+ //
9
+ // A throw here does NOT block boot (it's logged; the rpc surface stays
10
+ // up). Distinct from `*.seed.ts` (runs once and RETURNS) and `*.cron.tsx`
11
+ // (periodic) — a startup HOLDS a resource for the process lifetime.
12
+ //
13
+ // NOTE: `StartupContext` lives on the `@voltro/cli/startup` subpath (the
14
+ // runtime package stays free of the node:fs discovery code).
15
+
16
+ import type { StartupContext } from '@voltro/cli/startup'
17
+
18
+ export default async ({ store, log, onShutdown, id }: StartupContext) => {
19
+ log.info(`startup '${id}': warming order-fulfillment caches`)
20
+
21
+ // Example long-lived resource: a heartbeat interval. Replace with a
22
+ // real warm-up (preload a dashboard cache, open a consumer, …).
23
+ const timer = setInterval(() => {
24
+ void store // `store` is the already-migrated DataStore, ready to use.
25
+ }, 60_000)
26
+
27
+ // Teardown runs on SIGTERM / SIGINT, LIFO across all startups, each
28
+ // awaited with a hard 5s timeout. Always release what you acquire.
29
+ onShutdown(() => {
30
+ clearInterval(timer)
31
+ log.info(`startup '${id}': torn down`)
32
+ })
33
+ }
@@ -0,0 +1,27 @@
1
+ // `orderChanges` — a post-commit subscriber on the `orders` table.
2
+ //
3
+ // Fires AFTER commit (the row IS written), best-effort + fire-and-forget
4
+ // (a throw logs and the next event still arrives; it can't back-pressure
5
+ // the change stream). Use it for reactions that aren't part of the
6
+ // write transaction — notify a fulfillment desk, mirror to a search
7
+ // index, bump a metric. For crash-safe multi-step reactions, have the
8
+ // subscriber start a workflow instead.
9
+ //
10
+ // `on: 'any'` matches insert / update / delete. `event.new` is present
11
+ // on insert + update (null on delete); `event.old` on update + delete
12
+ // (null on insert).
13
+
14
+ import { defineSubscriber } from '@voltro/runtime'
15
+
16
+ export default defineSubscriber({
17
+ table: 'orders',
18
+ on: 'any',
19
+ handler: async (event, ctx) => {
20
+ const row = (event.new ?? event.old) as { id?: string; status?: string } | null
21
+ ctx.log.info('order changed', {
22
+ op: event.op,
23
+ id: row?.id,
24
+ status: row?.status,
25
+ })
26
+ },
27
+ })
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "api-durable",
3
+ "kind": "api",
4
+ "summary": "A cohesive order-fulfillment domain demonstrating the whole durable surface: a durable workflow (step / sleep / awaitSignal human-approval gate), an event trigger that starts it, a signal-injecting mutation, a nightly cron schedule, a table subscriber, a materialized aggregate, and a run-once startup hook.",
5
+ "tags": ["api", "durable", "workflow", "schedule", "subscriber", "aggregate", "startup", "events"]
6
+ }
@@ -0,0 +1,91 @@
1
+ // Unit tests with `@voltro/testing` — no database, no running server. Two
2
+ // things get covered:
3
+ //
4
+ // 1. `orders.place` — the runnable primitive. `makeTestContext` hands the
5
+ // handler the SAME mixin-wrapped `ctx.store` it gets in production, so
6
+ // tenant auto-scoping + the `order_…` id injection behave exactly as at
7
+ // runtime.
8
+ // 2. `orders.fulfill` — the workflow is descriptor-pinned. A real run PARKS
9
+ // on `awaitSignal('approval')` (a human-approval gate), so a plain
10
+ // `start()` never resolves in a unit test; a full run needs a signal
11
+ // (integration). Here we assert the wire contract (name / payload /
12
+ // success decode) and that the executor factory is wired.
13
+ //
14
+ // Run with `voltro test` (vitest).
15
+
16
+ import { describe, it, expect } from 'vitest'
17
+ import { Schema } from 'effect'
18
+ import { makeTestContext, mockStore } from '@voltro/testing'
19
+ import { database } from '../database/schema' // registers orders / actors / tenants
20
+ import placeOrder from '../mutations/orders.place.mutation.server'
21
+ import { FulfillOrder } from '../workflows/order.fulfill.workflow'
22
+ import buildExecute from '../workflows/order.fulfill.workflow.server'
23
+
24
+ describe('orders.place', () => {
25
+ it('inserts an order, auto-stamped to the caller tenant', async () => {
26
+ const ctx = makeTestContext({
27
+ subject: { type: 'user', id: 'user_1', tenantId: 'acme' },
28
+ store: mockStore({ orders: [] }),
29
+ })
30
+ const row = await placeOrder(
31
+ { tenantId: 'acme', customerName: 'Ada Lovelace', amountCents: 4200 },
32
+ ctx,
33
+ )
34
+
35
+ expect(row.status).toBe('placed')
36
+ expect(row.customerName).toBe('Ada Lovelace')
37
+ expect(row.amountCents).toBe(4200)
38
+ expect(row.tenantId).toBe('acme') // tenant() stamped it from the subject
39
+ expect(row.id).toMatch(/^order_/) // TypeID auto-injected
40
+ })
41
+
42
+ it('isolates tenants — t2 never sees t1 rows (REAL tenant scoping)', async () => {
43
+ const ctx = makeTestContext({
44
+ subject: { type: 'user', id: 'u1', tenantId: 't1' },
45
+ store: mockStore({ orders: [] }),
46
+ })
47
+ await placeOrder({ tenantId: 't1', customerName: 'Grace', amountCents: 100 }, ctx)
48
+
49
+ // Re-scope the SAME store to a different tenant — the real WHERE filter
50
+ // hides t1's row from t2.
51
+ const seenByT2 = await ctx.withTenant('t2', (c) => c.store.query(database.orders.descriptor))
52
+ expect(seenByT2).toHaveLength(0)
53
+ })
54
+ })
55
+
56
+ describe('orders.fulfill (workflow descriptor)', () => {
57
+ it('declares the expected wire name', () => {
58
+ expect(FulfillOrder.name).toBe('orders.fulfill')
59
+ })
60
+
61
+ it('accepts a valid { orderId, tenantId } payload and rejects a malformed one', () => {
62
+ const decode = Schema.decodeUnknownSync(FulfillOrder.payloadSchema)
63
+ expect(decode({ orderId: 'order_1', tenantId: 'acme' })).toMatchObject({
64
+ orderId: 'order_1',
65
+ tenantId: 'acme',
66
+ })
67
+ expect(() => decode({ orderId: 'order_1' })).toThrow() // missing tenantId
68
+ })
69
+
70
+ it('declares a { orderId, outcome } success schema with a literal outcome', () => {
71
+ const decode = Schema.decodeUnknownSync(FulfillOrder.successSchema)
72
+ expect(decode({ orderId: 'order_1', outcome: 'shipped' })).toEqual({
73
+ orderId: 'order_1',
74
+ outcome: 'shipped',
75
+ })
76
+ expect(decode({ orderId: 'order_1', outcome: 'rejected' })).toEqual({
77
+ orderId: 'order_1',
78
+ outcome: 'rejected',
79
+ })
80
+ expect(() => decode({ orderId: 'order_1', outcome: 'delivered' })).toThrow() // not in the literal union
81
+ })
82
+
83
+ it('exposes an executor factory (ctx) => run — a real run needs an approval signal (integration)', () => {
84
+ const ctx = makeTestContext({
85
+ subject: { type: 'user', id: 'user_1', tenantId: 't1' },
86
+ store: mockStore({ orders: [] }),
87
+ })
88
+ expect(typeof buildExecute).toBe('function')
89
+ expect(typeof buildExecute(ctx)).toBe('function')
90
+ })
91
+ })
@@ -0,0 +1,21 @@
1
+ // Event → workflow trigger. Auto-discovered (`*.trigger.tsx`); no
2
+ // app.config wiring needed.
3
+ //
4
+ // When any handler emits `order.placed` (the `orders.place` mutation
5
+ // does, post-commit), this trigger starts the `orders.fulfill`
6
+ // workflow. The event payload IS the workflow payload here (both are
7
+ // `{ orderId, tenantId }`), so no `payload` mapper is needed — pass one
8
+ // when the shapes differ.
9
+ //
10
+ // `idempotencyKey` dedups deliveries: if the same event is re-delivered
11
+ // (crash + replay), the matching key short-circuits the second start.
12
+
13
+ import { defineEventTrigger } from '@voltro/runtime'
14
+
15
+ export default defineEventTrigger<
16
+ { orderId: string; tenantId: string }
17
+ >({
18
+ event: 'order.placed',
19
+ workflow: 'orders.fulfill',
20
+ idempotencyKey: (event) => `order.placed:${event.data.orderId}`,
21
+ })
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }
@@ -0,0 +1,103 @@
1
+ // `orders.fulfill` — EXECUTOR (server-only).
2
+ //
3
+ // Durable order-fulfillment pipeline. The default export is a factory
4
+ // `(ctx) => execute`; the CLI calls `workflow.toLayer(execute)` at boot
5
+ // so the executor closes over the live `AppContext` (store, events, …).
6
+ //
7
+ // The flow, every step durable (replayed from its journaled result on a
8
+ // crash/resume rather than re-run):
9
+ //
10
+ // 1. step 'reserve-stock' — flip the order to `reserved`.
11
+ // 2. sleep — a (short) durable delay; the wake time is
12
+ // journaled in the engine. Bump to hours/days
13
+ // for a real "hold before charging" window.
14
+ // 3. awaitSignal 'approval' — PARK until a human sends the approval
15
+ // signal (via the `orders.approve` mutation
16
+ // or the dashboard "Send signal" button).
17
+ // 4. step 'ship' / mark rejected — branch on the decision.
18
+ //
19
+ // `withCompensation` wraps the reserve step with saga-style rollback:
20
+ // the finalizer runs only if the WHOLE workflow later fails, releasing
21
+ // the reservation so a downstream failure doesn't strand held stock.
22
+
23
+ import { step, sleep, awaitSignal, withCompensation } from '@voltro/workflow'
24
+ import { Effect, Schema } from 'effect'
25
+ import type { AppContext } from '@voltro/runtime'
26
+
27
+ const Decision = Schema.Struct({ approved: Schema.Boolean })
28
+
29
+ const buildExecute = (ctx: AppContext) =>
30
+ (payload: { orderId: string; tenantId: string }, _executionId: string) =>
31
+ Effect.gen(function* () {
32
+ const { orderId } = payload
33
+
34
+ // 1. Reserve stock. `withCompensation` registers an undo that runs
35
+ // only if the workflow as a whole fails after this point.
36
+ yield* withCompensation(
37
+ step({
38
+ name: 'reserve-stock',
39
+ input: { orderId },
40
+ success: Schema.Struct({ orderId: Schema.String }),
41
+ execute: Effect.tryPromise({
42
+ try: async () => {
43
+ await ctx.store.update('orders', orderId, { status: 'reserved' })
44
+ return { orderId }
45
+ },
46
+ catch: (error) => error as never,
47
+ }),
48
+ }),
49
+ // Saga rollback: release the reservation on whole-workflow failure.
50
+ () =>
51
+ Effect.promise(() =>
52
+ ctx.store
53
+ .update('orders', orderId, { status: 'placed' })
54
+ .then(() => undefined),
55
+ ),
56
+ )
57
+
58
+ // 2. Durable delay. The wake is journaled — survives a restart.
59
+ yield* sleep({ name: 'settle-window', duration: '5 seconds' })
60
+
61
+ // 3. Human-approval gate. The run parks here until an external
62
+ // caller sends a matching `approval` signal; the parsed payload
63
+ // is journaled, so a post-signal replay returns it instantly.
64
+ const decision = yield* awaitSignal(ctx, {
65
+ name: 'approval',
66
+ schema: Decision,
67
+ // pollIntervalMs defaults to 200 (backoff → 5s); timeoutMs 24h.
68
+ })
69
+
70
+ if (!decision.approved) {
71
+ yield* step({
72
+ name: 'mark-rejected',
73
+ input: { orderId },
74
+ success: Schema.Struct({ orderId: Schema.String }),
75
+ execute: Effect.tryPromise({
76
+ try: async () => {
77
+ await ctx.store.update('orders', orderId, { status: 'rejected' })
78
+ return { orderId }
79
+ },
80
+ catch: (error) => error as never,
81
+ }),
82
+ })
83
+ return { orderId, outcome: 'rejected' as const }
84
+ }
85
+
86
+ // 4. Approved → ship.
87
+ yield* step({
88
+ name: 'ship',
89
+ input: { orderId },
90
+ success: Schema.Struct({ orderId: Schema.String }),
91
+ execute: Effect.tryPromise({
92
+ try: async () => {
93
+ await ctx.store.update('orders', orderId, { status: 'shipped' })
94
+ return { orderId }
95
+ },
96
+ catch: (error) => error as never,
97
+ }),
98
+ })
99
+
100
+ return { orderId, outcome: 'shipped' as const }
101
+ })
102
+
103
+ export default buildExecute
@@ -0,0 +1,25 @@
1
+ // `orders.fulfill` — DESCRIPTOR (browser-safe).
2
+ //
3
+ // Imports `workflow` from the `@voltro/workflow/define` subpath — the
4
+ // browser-safe contract surface. The codegen value-imports this so the
5
+ // web client can start the workflow over rpc; importing the executor's
6
+ // server graph here would drag database/cluster into the browser bundle.
7
+
8
+ import { workflow } from '@voltro/workflow/define'
9
+ import { Schema } from 'effect'
10
+
11
+ export const FulfillOrder = workflow({
12
+ name: 'orders.fulfill',
13
+ payload: {
14
+ orderId: Schema.String,
15
+ tenantId: Schema.String,
16
+ },
17
+ success: Schema.Struct({
18
+ orderId: Schema.String,
19
+ outcome: Schema.Literal('shipped', 'rejected'),
20
+ }),
21
+ // Dedup concurrent starts for the same order — the `order.placed`
22
+ // trigger could (after a crash + replay) fan out twice; the same key
23
+ // collapses them to one run.
24
+ idempotencyKey: ({ orderId }) => `fulfill:${orderId}`,
25
+ })
@@ -0,0 +1,63 @@
1
+ # {{projectName}} / {{appName}}
2
+
3
+ Voltro API scaffold (template: **api-feature-flags**) — feature flags with
4
+ `@voltro/plugin-flags`: kill-switches, % rollout, targeting, and two ways to
5
+ gate rpc calls.
6
+
7
+ ## Boot
8
+
9
+ ```bash
10
+ pnpm install
11
+ pnpm --filter @{{projectName}}/{{appName}} dev
12
+ # → http://localhost:4000 (store: memory — zero infra)
13
+ ```
14
+
15
+ ## What this shows
16
+
17
+ - **Flags as code** — declared in `app.config.ts`: a bare boolean is a
18
+ kill-switch (`newEditor: true`, `betaExport: false`); a rich definition adds
19
+ a deterministic `% rollout` (`gradualRollout: { rollout: 25 }`) and
20
+ OR-of-rules `targeting` (`proDashboard: { targeting: [{ metadata: { plan:
21
+ 'pro' } }] }`).
22
+ - **Two ways to gate** —
23
+ - **Declarative** (`gatedBy`): `notes.export` is mapped to `betaExport`. The
24
+ framework fails typed `FlagDisabled` BEFORE the handler runs when the flag
25
+ is off — no code in the handler.
26
+ - **In-handler** (`requireFlag`): `notes.create` calls
27
+ `requireFlag(ctx, 'newEditor')`, the Effect-native guard (same shape as
28
+ `requireScope` / `requireEntitlement`).
29
+ - **Typed on the client** — both paths fail `FlagDisabled`, declared on the
30
+ procedure's `error:`, so the browser decodes it typed.
31
+
32
+ ## Try it
33
+
34
+ ```bash
35
+ # newEditor is ON → requireFlag passes → note created:
36
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
37
+ -d '{"tag":"notes.create","input":{"tenantId":"acme","title":"Flagged note","body":"hi"}}'
38
+
39
+ # betaExport is OFF → gatedBy blocks before the handler → typed FlagDisabled:
40
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
41
+ -d '{"tag":"notes.export","input":{}}'
42
+ # → { ok:false, error:{ _tag:"FlagDisabled", flag:"betaExport" } }
43
+ ```
44
+
45
+ Flip `betaExport: true` in `app.config.ts` (or toggle it at runtime with
46
+ `store: 'postgres'`) and `notes.export` starts succeeding — no code change.
47
+
48
+ ## On the web side
49
+
50
+ ```tsx
51
+ import { useFlag } from '@voltro/plugin-flags/web'
52
+ const showBeta = useFlag('betaExport') // evaluated for the signed-in subject
53
+ {showBeta && <BetaExportButton />}
54
+ ```
55
+
56
+ `useFlag` reads the synthesized `flags.evaluate` query, so client UI gating
57
+ uses the SAME evaluation (targeting + rollout) as the server guards.
58
+
59
+ ## Runtime toggles
60
+
61
+ Set `store: 'postgres'` to overlay runtime-toggleable flags (in
62
+ `_voltro_feature_flags`) on the code baseline — flip a kill-switch without a
63
+ deploy. The dashboard ships a **Flags** panel for exactly this.
@@ -0,0 +1,11 @@
1
+ import type { AppContext } from '@voltro/runtime'
2
+ import { database } from '../database/schema'
3
+
4
+ // Runs ONLY when the `betaExport` gate passes (see app.config.ts gatedBy).
5
+ // The body itself is flag-agnostic — just counts the tenant's notes.
6
+ const execute = async (_input: Record<string, never>, ctx: AppContext) => {
7
+ const rows = await ctx.store.query(database.notes.descriptor)
8
+ return { exported: rows.length }
9
+ }
10
+
11
+ export default execute
@@ -0,0 +1,18 @@
1
+ // "Export all notes" — gated DECLARATIVELY. There's no flag check in the
2
+ // handler: the `gatedBy: { 'notes.export': 'betaExport' }` map in app.config.ts
3
+ // makes the framework fail `FlagDisabled` BEFORE this handler runs whenever
4
+ // `betaExport` is off. Flip the flag (config, or a postgres-store toggle) and
5
+ // the same call starts succeeding — no code change.
6
+ //
7
+ // Declaring `error: FlagDisabled` surfaces the gate's rejection typed.
8
+
9
+ import { defineAction } from '@voltro/protocol'
10
+ import { FlagDisabled } from '@voltro/plugin-flags/errors'
11
+ import { Schema } from 'effect'
12
+
13
+ export const exportNotes = defineAction({
14
+ name: 'notes.export',
15
+ input: Schema.Struct({}),
16
+ output: Schema.Struct({ exported: Schema.Number }),
17
+ error: FlagDisabled,
18
+ })