@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,33 @@
1
+ // Content-moderation backend for the {{projectName}} project.
2
+ //
3
+ // `moderationPlugin` runs on the rpc interceptors BEFORE the handler: it checks
4
+ // the named input fields against a provider and, per rule, either BLOCKS the
5
+ // call (typed `ContentRejected` — the write never commits) or FLAGS it for
6
+ // review (the write commits, the item lands in the review queue). `keywordProvider`
7
+ // is a zero-dep deny-list; `aiProvider()` is an optional LLM classifier.
8
+ // Config-only, zero infra.
9
+ import { defineEnv, envVar } from '@voltro/env'
10
+ import { moderationPlugin, keywordProvider } from '@voltro/plugin-moderation'
11
+
12
+ export const env = defineEnv({
13
+ LOG_LEVEL: envVar.enum(['debug', 'info', 'warn', 'error'], { access: 'public', default: 'info' }),
14
+ })
15
+
16
+ export default {
17
+ type: 'api' as const,
18
+ name: '{{capProjectName}}{{capAppName}}',
19
+ store: 'memory' as const,
20
+ plugins: [
21
+ moderationPlugin({
22
+ provider: keywordProvider(['spam', 'scam', 'phishing', 'malware']),
23
+ rules: [
24
+ // BLOCK: a post whose title/body trips the deny-list is rejected
25
+ // (ContentRejected) before it's ever written.
26
+ { match: 'posts.create', fields: ['title', 'body'], action: 'block' },
27
+ // FLAG: a comment is still written, but flagged for review.
28
+ { match: 'comments.create', fields: ['body'], action: 'flag' },
29
+ ],
30
+ }),
31
+ ],
32
+ env,
33
+ }
@@ -0,0 +1,45 @@
1
+ // Schema for the {{projectName}} moderation backend — `posts` (block rule) and
2
+ // `comments` (flag rule). `actors` + `tenants` are the framework core tables.
3
+
4
+ import {
5
+ databaseHandle,
6
+ id,
7
+ table,
8
+ text,
9
+ timestamp,
10
+ type InferRow,
11
+ } from '@voltro/database'
12
+ import { tenant } from '@voltro/plugin-multitenancy'
13
+
14
+ export const actors = table('actors', {
15
+ id: id(),
16
+ kind: text().oneOf(['user', 'serviceAccount', 'apiKey', 'system']),
17
+ displayName: text().nullable(),
18
+ createdAt: timestamp().default('now'),
19
+ })
20
+
21
+ export const tenants = table('tenants', {
22
+ id: id(),
23
+ name: text(),
24
+ createdAt: timestamp().default('now'),
25
+ })
26
+
27
+ export const posts = table('posts', {
28
+ id: id({ prefix: 'post' }),
29
+ title: text(),
30
+ body: text(),
31
+ })
32
+ .with(tenant())
33
+ .reactive()
34
+
35
+ export const comments = table('comments', {
36
+ id: id({ prefix: 'cmt' }),
37
+ body: text(),
38
+ })
39
+ .with(tenant())
40
+ .reactive()
41
+
42
+ export type Post = InferRow<typeof posts>
43
+ export type Comment = InferRow<typeof comments>
44
+
45
+ export const database = databaseHandle({ actors, tenants, posts, comments })
@@ -0,0 +1,8 @@
1
+ import type { AppContext } from '@voltro/runtime'
2
+
3
+ // Plain insert — a `flag` rule lets the write through (and records the flag);
4
+ // only a `block` rule would have stopped us. tenant() auto-fills tenantId.
5
+ const execute = async (input: { body: string }, ctx: AppContext) =>
6
+ ctx.store.insert('comments', { body: input.body })
7
+
8
+ export default execute
@@ -0,0 +1,20 @@
1
+ // Creates a comment. The `flag`-action moderation rule (app.config.ts) checks
2
+ // the body, but unlike `block` it does NOT reject — the comment is written and
3
+ // flagged for review (the dashboard's Moderation panel surfaces the queue).
4
+ // So there's no moderation error on this procedure.
5
+
6
+ import { defineMutation } from '@voltro/protocol'
7
+ import { Schema } from 'effect'
8
+
9
+ export const createComment = defineMutation({
10
+ name: 'comments.create',
11
+ target: { table: 'comments', op: 'insert' },
12
+ input: Schema.Struct({
13
+ body: Schema.String,
14
+ }),
15
+ output: Schema.Struct({
16
+ id: Schema.String,
17
+ body: Schema.String,
18
+ tenantId: Schema.String,
19
+ }),
20
+ })
@@ -0,0 +1,9 @@
1
+ import type { AppContext } from '@voltro/runtime'
2
+
3
+ // Plain insert — the `block` moderation rule already rejected banned content
4
+ // before we got here, so this only runs on clean input. tenant() auto-fills
5
+ // tenantId from the subject.
6
+ const execute = async (input: { title: string; body: string }, ctx: AppContext) =>
7
+ ctx.store.insert('posts', { title: input.title, body: input.body })
8
+
9
+ export default execute
@@ -0,0 +1,25 @@
1
+ // Creates a post. The `block`-action moderation rule (app.config.ts) checks
2
+ // title + body BEFORE this handler runs; banned content fails typed
3
+ // `ContentRejected` and the write never commits. There's no moderation code
4
+ // in the handler. Declaring `error: ContentRejected` surfaces it to the client
5
+ // typed. `ContentRejected` comes from the browser-safe `/errors` subpath.
6
+
7
+ import { defineMutation } from '@voltro/protocol'
8
+ import { ContentRejected } from '@voltro/plugin-moderation/errors'
9
+ import { Schema } from 'effect'
10
+
11
+ export const createPost = defineMutation({
12
+ name: 'posts.create',
13
+ target: { table: 'posts', op: 'insert' },
14
+ input: Schema.Struct({
15
+ title: Schema.NonEmptyString,
16
+ body: Schema.String,
17
+ }),
18
+ output: Schema.Struct({
19
+ id: Schema.String,
20
+ title: Schema.String,
21
+ body: Schema.String,
22
+ tenantId: Schema.String,
23
+ }),
24
+ error: ContentRejected,
25
+ })
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@{{projectName}}/{{appName}}",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "voltro dev .",
8
+ "migrate": "voltro migrate",
9
+ "test": "voltro test",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@effect/platform": "^0.96.1",
14
+ "@effect/rpc": "^0.75.1",
15
+ "@voltro/cli": "0.1.0",
16
+ "@voltro/database": "0.1.0",
17
+ "@voltro/env": "0.1.0",
18
+ "@voltro/plugin-moderation": "0.1.0",
19
+ "@voltro/plugin-multitenancy": "0.1.0",
20
+ "@voltro/protocol": "0.1.0",
21
+ "@voltro/runtime": "0.1.0",
22
+ "effect": "^3.21.2"
23
+ },
24
+ "devDependencies": {
25
+ "@voltro/testing": "0.1.0",
26
+ "typescript": "^5.7.0",
27
+ "vitest": "^3.0.0"
28
+ }
29
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "api-moderation",
3
+ "kind": "api",
4
+ "summary": "Pre-commit content moderation — @voltro/plugin-moderation checks named input fields BEFORE the handler: a block rule rejects banned content with typed ContentRejected (write never commits), a flag rule writes it but queues it for review. keywordProvider (zero-dep deny-list) or aiProvider. Memory store, zero infra.",
5
+ "tags": ["api", "moderation", "content", "trust-safety", "plugin"]
6
+ }
@@ -0,0 +1,79 @@
1
+ // Unit test for `posts.create` — driven through `@voltro/testing`.
2
+ //
3
+ // Content moderation is enforced by the plugin's rpc INTERCEPTOR (a block rule
4
+ // wired in app.config.ts), which runs BEFORE this executor and rejects banned
5
+ // content with a typed `ContentRejected` — a dispatch-time middleware, NOT the
6
+ // executor. `invoke` reproduces only the input-Schema decode, not middleware
7
+ // (see its header), so we DON'T fake the interceptor: we test the executor's own
8
+ // observable behavior (the tenant-stamped insert) + pin the descriptor's
9
+ // declared `ContentRejected` error shape. Run with `voltro test` (vitest).
10
+
11
+ import { describe, it, expect } from 'vitest'
12
+ import { Schema } from 'effect'
13
+ import { makeTestContext, mockStore, invoke } from '@voltro/testing'
14
+ import { database } from '../database/schema' // registers actors / tenants / posts / comments
15
+ import { createPost } from '../mutations/posts.create.mutation'
16
+ import createPostHandler from '../mutations/posts.create.mutation.server'
17
+ import { ContentRejected } from '@voltro/plugin-moderation/errors'
18
+
19
+ const subject = { type: 'user', id: 'u1', tenantId: 'acme' } as const
20
+
21
+ describe('posts.create', () => {
22
+ it('inserts a post stamped with the caller tenant + a post_ TypeID', async () => {
23
+ const ctx = makeTestContext({ subject, store: mockStore({ posts: [] }) })
24
+
25
+ const row = (await invoke(
26
+ createPost,
27
+ createPostHandler,
28
+ { title: 'Hello', body: 'World' },
29
+ ctx,
30
+ )) as { id: string; title: string; body: string; tenantId: string }
31
+
32
+ expect(row.title).toBe('Hello')
33
+ expect(row.body).toBe('World')
34
+ expect(row.tenantId).toBe('acme') // tenant() stamped it from the subject
35
+ expect(row.id).toMatch(/^post_/)
36
+
37
+ // The row is really in the store — the live tenant-scoped query sees it.
38
+ const rows = await ctx.store.query(database.posts.descriptor)
39
+ expect(rows).toHaveLength(1)
40
+ })
41
+
42
+ it('rejects an empty title at the input-schema decode (NonEmptyString)', async () => {
43
+ const ctx = makeTestContext({ subject, store: mockStore({ posts: [] }) })
44
+
45
+ await expect(
46
+ invoke(createPost, createPostHandler, { title: '', body: '' }, ctx),
47
+ ).rejects.toThrow()
48
+ })
49
+
50
+ it('isolates tenants — a second tenant never sees the first tenant post', async () => {
51
+ const ctx = makeTestContext({
52
+ subject: { type: 'user', id: 'u1', tenantId: 't1' },
53
+ store: mockStore({ posts: [] }),
54
+ })
55
+ await invoke(createPost, createPostHandler, { title: 'secret', body: '' }, ctx)
56
+
57
+ const seenByT2 = await ctx.withTenant('t2', (c) => c.store.query(database.posts.descriptor))
58
+ expect(seenByT2).toHaveLength(0)
59
+ })
60
+
61
+ it('pins the descriptor: name, input decode + the declared ContentRejected error', async () => {
62
+ // The moderation error is a dispatch-time interceptor concern, not reachable
63
+ // from the executor here — so we pin the CONTRACT the client relies on: the
64
+ // rpc tag, that the input Schema decodes a valid post, and that the typed
65
+ // ContentRejected error the interceptor raises is declared on the procedure.
66
+ expect(createPost.name).toBe('posts.create')
67
+
68
+ const decoded = Schema.decodeUnknownSync(createPost.input)({ title: 'ok', body: 'b' })
69
+ expect(decoded).toEqual({ title: 'ok', body: 'b' })
70
+
71
+ const rejected = new ContentRejected({
72
+ tag: 'posts.create',
73
+ categories: ['profanity'],
74
+ reason: 'banned term',
75
+ })
76
+ expect(rejected).toBeInstanceOf(ContentRejected)
77
+ expect(Schema.is(ContentRejected)(rejected)).toBe(true)
78
+ })
79
+ })
@@ -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}} — observability + testing
2
+
3
+ The production-readiness slice in one backend: **Prometheus metrics**, **Sentry
4
+ error tracking**, **distributed tracing**, and a **`@voltro/testing` unit test**
5
+ — so a real service is observable AND covered from day one.
6
+
7
+ ## What it demonstrates
8
+
9
+ - **Prometheus** ([`@voltro/plugin-prometheus`](https://voltro.cloud/docs/observability/metrics)) —
10
+ `GET /metrics` in the text exposition format. The framework already records
11
+ `voltro_rpc_*` / `voltro_http_*` / subscription / cache metrics; `notes.create`
12
+ adds a **custom counter** (`app_notes_created_total`) that shows up automatically.
13
+ - **Sentry** ([`@voltro/plugin-sentry`](https://voltro.cloud/docs/observability/errors)) —
14
+ rpc / REST / workflow / render errors reported correlated to the active
15
+ `traceId`. **Inert without `SENTRY_DSN`**, so it ships wired — set the env to turn it on.
16
+ - **Tracing** — on by default in `voltro dev` (DevTools Traces panel + `traceId`
17
+ on every log line). Set `OTEL_EXPORTER_OTLP_ENDPOINT` to ship to Jaeger / Tempo / Honeycomb.
18
+ - **Testing** ([`@voltro/testing`](https://voltro.cloud/docs/testing)) — a unit
19
+ test that runs the `notes.create` handler with `makeTestContext` + `mockStore`
20
+ (no DB, no server) and asserts the **real** tenant scoping.
21
+
22
+ ## Run it
23
+
24
+ ```bash
25
+ voltro dev .
26
+ ```
27
+
28
+ ### Metrics
29
+
30
+ ```bash
31
+ # Create a note (custom counter increments)
32
+ curl -s -X POST http://localhost:4000/_voltro/inspect/invoke \
33
+ -H 'content-type: application/json' \
34
+ -d '{"tag":"notes.create","input":{"title":"Hello","body":"World"}}'
35
+
36
+ # Scrape Prometheus — your custom counter + the framework's rpc metrics
37
+ curl -s http://localhost:4000/metrics | grep -E 'app_notes_created_total|voltro_rpc_requests_total'
38
+ # app_notes_created_total 1
39
+ # voltro_rpc_requests_total{tag="notes.create",status="ok"} 1
40
+ ```
41
+
42
+ Point Prometheus / Grafana Agent at `/metrics`; gate it with
43
+ `prometheusPlugin({ token })` / `PROMETHEUS_TOKEN` for a public deploy.
44
+
45
+ ### Errors → Sentry
46
+
47
+ Set `SENTRY_DSN` (api) — then any handler throw, REST 5xx, workflow failure, or
48
+ React render/loader error is captured, tagged with the request's `traceId`. For
49
+ the full browser→backend waterfall, add the `sentry` field to a paired web app's
50
+ `app.config.ts` (see the docs).
51
+
52
+ ### A custom metric
53
+
54
+ ```ts
55
+ // mutations/notes.create.mutation.server.ts
56
+ import { Effect, Metric } from 'effect'
57
+ import { counter } from '@voltro/runtime'
58
+
59
+ const notesCreated = counter('app_notes_created_total', 'Notes created.')
60
+ Effect.runSync(Metric.increment(notesCreated)) // shows up in /metrics + the dashboard
61
+ ```
62
+
63
+ ## Test it
64
+
65
+ The template ships a unit test (`tests/notes.create.test.ts`) and the dev deps
66
+ (`@voltro/testing`, `vitest`). After `pnpm install`:
67
+
68
+ ```bash
69
+ voltro test # vitest against this app
70
+ ```
71
+
72
+ ```ts
73
+ const ctx = makeTestContext({
74
+ subject: { type: 'user', id: 'user_1', tenantId: 'acme' },
75
+ store: mockStore({ notes: [] }),
76
+ })
77
+ const row = await createNote({ title: 'Hello', body: 'World' }, ctx)
78
+ expect(row.tenantId).toBe('acme') // tenant() stamped it — the REAL store, no DB
79
+ ```
80
+
81
+ `ctx.store` is the same mixin-wrapped store production uses, so tenant
82
+ auto-scoping, soft-delete filtering, and audit auto-fill all behave identically —
83
+ in milliseconds, with no docker. `ctx.withTenant('t2', …)` re-scopes the same
84
+ data to prove cross-tenant isolation. See `@voltro/testing` for `MockClock` /
85
+ `MockEmail` / `mockAi` / `makeWorkflowRunner` / `runDialectParity`.
86
+
87
+ ## Going to production
88
+
89
+ | Want… | Do |
90
+ |---|---|
91
+ | Metrics survive a public deploy | `prometheusPlugin({ token })` + scrape over TLS |
92
+ | Errors in Sentry | set `SENTRY_DSN`; add `sentryPlugin({ traces: true })` for performance traces |
93
+ | Spans in your tracer | `OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4318` |
94
+ | Vendor-native (Datadog) | add `@voltro/plugin-datadog` — agentless metrics + correlated logs/traces |
95
+ | CI gate | run `voltro test` in CI; add `runDialectParity` for hand-written SQL |
96
+
97
+ ## Anti-patterns
98
+
99
+ - **Re-creating the counter per call.** Declare `counter(...)` at MODULE level —
100
+ a per-call `counter()` resets the series. (The runtime dedupes by name, but
101
+ module-level is the clear intent.)
102
+ - **Assuming Sentry needs code to wire each primitive.** The plugin subscribes
103
+ to the framework's server-error bus once — rpc, REST, workflows, schedules,
104
+ subscribers, webhooks, and startups are all covered. You just set the DSN.
105
+ - **Reaching for a real DB in unit tests.** `makeTestContext` + `mockStore` give
106
+ the real store behaviour in-memory. Save a live database for `voltro e2e`.
@@ -0,0 +1,32 @@
1
+ // Api app config — the observability + testing showcase. Read by `voltro dev`.
2
+ //
3
+ // Two observability plugins, both turnkey:
4
+ // • prometheusPlugin() — exposes GET /metrics in the text exposition format.
5
+ // The framework already records rpc / http / subscription / cache metrics
6
+ // into Effect's global MetricRegistry; the plugin just exports them. Your
7
+ // OWN counters (see mutations/notes.create) show up there too, automatically.
8
+ // • sentryPlugin() — reports rpc-handler + REST + workflow + render errors,
9
+ // correlated to the active trace. INERT without SENTRY_DSN, so it's safe to
10
+ // ship wired: set the env to turn it on, no code change.
11
+ //
12
+ // Tracing is on by default in `voltro dev` (a buffer-only tracer powers the
13
+ // DevTools Traces panel + traceId-in-logs); set OTEL_EXPORTER_OTLP_ENDPOINT to
14
+ // ship spans to Jaeger / Tempo / Honeycomb.
15
+ //
16
+ // `store: 'memory'` keeps first-run zero-infra.
17
+
18
+ import { prometheusPlugin } from '@voltro/plugin-prometheus'
19
+ import { sentryPlugin } from '@voltro/plugin-sentry'
20
+
21
+ export default {
22
+ type: 'api' as const,
23
+ name: '{{capProjectName}}{{capAppName}}',
24
+ store: 'memory' as const,
25
+ plugins: [
26
+ // GET /metrics → Prometheus text. Optional bearer gate: prometheusPlugin({ token })
27
+ // / PROMETHEUS_TOKEN. processMetrics defaults on (RSS + CPU).
28
+ prometheusPlugin(),
29
+ // Inert until SENTRY_DSN is set; then errors flow correlated to the traceId.
30
+ sentryPlugin(),
31
+ ],
32
+ }
@@ -0,0 +1,38 @@
1
+ // Schema for the {{projectName}} observability backend — a minimal tenant-scoped
2
+ // `notes` domain. The point of this template is what surrounds it: Prometheus
3
+ // metrics, Sentry error tracking, tracing, and a unit test (see tests/).
4
+
5
+ import {
6
+ boolean,
7
+ databaseHandle,
8
+ id,
9
+ table,
10
+ text,
11
+ type InferRow,
12
+ } from '@voltro/database'
13
+ import { tenant } from '@voltro/plugin-multitenancy'
14
+
15
+ // ---------- Core tables ----------
16
+
17
+ export const actors = table('actors', {
18
+ id: id(),
19
+ kind: text().oneOf(['user', 'serviceAccount', 'apiKey', 'system']),
20
+ displayName: text().nullable(),
21
+ })
22
+
23
+ export const tenants = table('tenants', { id: id(), name: text() })
24
+
25
+ // ---------- Application table ----------
26
+
27
+ export const notes = table('notes', {
28
+ id: id({ prefix: 'note' }),
29
+ title: text(),
30
+ body: text(),
31
+ done: boolean().default(false),
32
+ })
33
+ .with(tenant()) // tenantId + audit columns, auto-stamped
34
+ .reactive()
35
+
36
+ export type Note = InferRow<typeof notes>
37
+
38
+ export const database = databaseHandle({ actors, tenants, notes })
@@ -0,0 +1,33 @@
1
+ // Create a note + record a CUSTOM Prometheus metric.
2
+ //
3
+ // `counter(name, desc)` registers a metric in Effect's global MetricRegistry —
4
+ // the same registry GET /metrics (prometheusPlugin) and the DevTools Metrics
5
+ // panel both read, so your counter shows up in both with no extra wiring.
6
+ // We keep the handler ASYNC (so the test can `await execute(input, ctx)`) and
7
+ // bump the counter with a tiny `Effect.runSync` — Metric.increment is a
8
+ // synchronous effect.
9
+
10
+ import { Effect, Metric } from 'effect'
11
+ import type { AppContext } from '@voltro/runtime'
12
+ import { counter } from '@voltro/runtime'
13
+
14
+ // Module-level so it's the SAME metric across every call (not re-created).
15
+ const notesCreated = counter('app_notes_created_total', 'Notes created via notes.create.')
16
+
17
+ const execute = async (input: { title: string; body: string }, ctx: AppContext) => {
18
+ Effect.runSync(Metric.increment(notesCreated)) // → app_notes_created_total in /metrics
19
+ // tenant() auto-stamps tenantId (+ audit columns) from ctx.request.subject.
20
+ // `done: false` is set explicitly — a column DEFAULT only fills in on a SQL
21
+ // store; the memory store leaves an omitted column null, which would fail the
22
+ // `done: Schema.Boolean` output encode over the rpc wire.
23
+ const row = await ctx.store.insert('notes', { title: input.title, body: input.body, done: false })
24
+ return {
25
+ id: row['id'] as string,
26
+ title: row['title'] as string,
27
+ body: row['body'] as string,
28
+ done: row['done'] as boolean,
29
+ tenantId: row['tenantId'] as string,
30
+ }
31
+ }
32
+
33
+ export default execute
@@ -0,0 +1,15 @@
1
+ import { defineMutation } from '@voltro/protocol'
2
+ import { Schema } from 'effect'
3
+
4
+ export const createNote = defineMutation({
5
+ name: 'notes.create',
6
+ target: { table: 'notes', op: 'insert' },
7
+ input: Schema.Struct({ title: Schema.NonEmptyString, body: Schema.String }),
8
+ output: Schema.Struct({
9
+ id: Schema.String,
10
+ title: Schema.String,
11
+ body: Schema.String,
12
+ done: Schema.Boolean,
13
+ tenantId: Schema.String,
14
+ }),
15
+ })
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@{{projectName}}/{{appName}}",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "voltro dev .",
8
+ "migrate": "voltro migrate",
9
+ "test": "voltro test",
10
+ "typecheck": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@effect/platform": "^0.96.1",
14
+ "@effect/rpc": "^0.75.1",
15
+ "@voltro/cli": "0.1.0",
16
+ "@voltro/database": "0.1.0",
17
+ "@voltro/plugin-multitenancy": "0.1.0",
18
+ "@voltro/plugin-prometheus": "0.1.0",
19
+ "@voltro/plugin-sentry": "0.1.0",
20
+ "@voltro/protocol": "0.1.0",
21
+ "@voltro/runtime": "0.1.0",
22
+ "effect": "^3.21.2"
23
+ },
24
+ "devDependencies": {
25
+ "@voltro/testing": "0.1.0",
26
+ "typescript": "^5.7.0",
27
+ "vitest": "^3.0.0"
28
+ }
29
+ }
@@ -0,0 +1,7 @@
1
+ import type { AppContext } from '@voltro/runtime'
2
+ import { database } from '../database/schema'
3
+
4
+ const route = (_input: Record<string, never>, _ctx: AppContext) =>
5
+ database.notes.orderBy('createdAt', 'desc').limit(100)
6
+
7
+ export default route
@@ -0,0 +1,14 @@
1
+ import { defineQuery } from '@voltro/protocol'
2
+ import { Schema } from 'effect'
3
+
4
+ export const listNotes = defineQuery({
5
+ name: 'notes.list',
6
+ source: 'notes',
7
+ input: Schema.Struct({}),
8
+ output: Schema.Array(Schema.Struct({
9
+ id: Schema.String,
10
+ title: Schema.String,
11
+ body: Schema.String,
12
+ done: Schema.Boolean,
13
+ })),
14
+ })
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "api-observability",
3
+ "kind": "api",
4
+ "summary": "Production-readiness in one backend: Prometheus metrics (GET /metrics + a custom counter), Sentry error tracking (inert without a DSN), distributed tracing, AND a @voltro/testing example (makeTestContext + mockStore, run with voltro test).",
5
+ "tags": ["api", "observability", "prometheus", "metrics", "sentry", "testing", "tracing"]
6
+ }
@@ -0,0 +1,36 @@
1
+ // Unit test with `@voltro/testing` — no database, no running server, no real
2
+ // model calls. `makeTestContext` hands the handler the SAME mixin-wrapped
3
+ // `ctx.store` it gets in production, so tenant auto-scoping + audit auto-fill
4
+ // behave exactly as at runtime. Run with `voltro test` (vitest).
5
+
6
+ import { describe, it, expect } from 'vitest'
7
+ import { makeTestContext, mockStore } from '@voltro/testing'
8
+ import { database } from '../database/schema' // registers notes / actors / tenants
9
+ import createNote from '../mutations/notes.create.mutation.server'
10
+
11
+ describe('notes.create', () => {
12
+ it('inserts a note, auto-stamped to the caller tenant', async () => {
13
+ const ctx = makeTestContext({
14
+ subject: { type: 'user', id: 'user_1', tenantId: 'acme' },
15
+ store: mockStore({ notes: [] }),
16
+ })
17
+ const row = await createNote({ title: 'Hello', body: 'World' }, ctx)
18
+
19
+ expect(row.title).toBe('Hello')
20
+ expect(row.tenantId).toBe('acme') // tenant() stamped it from the subject
21
+ expect(row.id).toMatch(/^note_/) // TypeID auto-injected
22
+ })
23
+
24
+ it('isolates tenants — t2 never sees t1 rows (REAL tenant scoping)', async () => {
25
+ const ctx = makeTestContext({
26
+ subject: { type: 'user', id: 'u1', tenantId: 't1' },
27
+ store: mockStore({ notes: [] }),
28
+ })
29
+ await createNote({ title: 'secret', body: '' }, ctx)
30
+
31
+ // Re-scope the SAME store to a different tenant — `withTenant` hands the
32
+ // callback a re-scoped `c`; query THAT (the real WHERE filter hides t1's row).
33
+ const seenByT2 = await ctx.withTenant('t2', (c) => c.store.query(database.notes.descriptor))
34
+ expect(seenByT2).toHaveLength(0)
35
+ })
36
+ })
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }
@@ -0,0 +1,49 @@
1
+ # {{projectName}} / {{appName}}
2
+
3
+ Voltro API scaffold (template: **api-ratelimit**) — per-endpoint /
4
+ per-subject / per-tenant rate limiting with `@voltro/plugin-ratelimit`.
5
+
6
+ ## Boot
7
+
8
+ ```bash
9
+ pnpm install
10
+ pnpm --filter @{{projectName}}/{{appName}} dev
11
+ # → http://localhost:4000 (store: memory — zero infra)
12
+ ```
13
+
14
+ ## What this shows
15
+
16
+ - **Limits as config** — `rateLimitPlugin` runs on the rpc interceptors (the
17
+ only surface that sees every call AND the resolved subject). A `default`
18
+ fallback applies everywhere; `rules` override per endpoint:
19
+ - `match` — an exact tag or a `/regex/`; `kind` narrows to mutation/query/action.
20
+ - `algorithm` — `sliding-window` (default), `fixed-window`, or
21
+ `token-bucket` (with `burst`).
22
+ - `by` — the bucket key: `subject` (default), `tenant`, `apiKey`, `global`,
23
+ or a composite array.
24
+ - **Typed `RateLimited`** — over-limit calls fail a `RateLimited` error
25
+ (`{ tag, limit, retryAfterMs, resetAtMs }`). The plugin merges it into every
26
+ procedure's wire error union, so the client decodes it typed and can show a
27
+ "try again in N seconds".
28
+
29
+ This template caps `notes.create` at **3/min per tenant** (token-bucket,
30
+ burst 3): the first three calls land, the fourth is rejected.
31
+
32
+ ## Try it
33
+
34
+ ```bash
35
+ # Fire it four times for tenant 'acme' — the 4th trips the limit:
36
+ for i in 1 2 3 4; do
37
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
38
+ -d "{\"tag\":\"notes.create\",\"input\":{\"tenantId\":\"acme\",\"title\":\"n$i\",\"body\":\"x\"}}" \
39
+ | python3 -c 'import sys,json;d=json.load(sys.stdin);print(i, d.get("ok"), (d.get("error") or {}).get("name",""))' i=$i 2>/dev/null || true
40
+ done
41
+ # → 1 True ; 2 True ; 3 True ; 4 False RateLimited
42
+ ```
43
+
44
+ ## Multi-node
45
+
46
+ `store: 'memory'` is single-process. For a cluster, switch to `'postgres'`
47
+ (state under a row lock, reuses `DB_*`/`PG_*`) or `'redis'` (fastest — one
48
+ atomic Lua script; reads `CACHE_REDIS_URL` / `REDIS_URL`). All shared stores
49
+ fail OPEN on a backend error.