@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,100 @@
1
+ # {{projectName}} / {{appName}}
2
+
3
+ Voltro backend scaffold (template: **api-kv**).
4
+
5
+ The **durable key-value** template. It demonstrates `ctx.kv` — the framework's
6
+ storage primitive for state you **can't recompute** — on a small
7
+ **external-event-sync** domain: a sync **cursor** (a watermark) and TTL-bounded
8
+ **idempotency markers** that dedupe redelivered events. It boots with **zero
9
+ infrastructure** (`store: 'memory'`).
10
+
11
+ ## The one idea
12
+
13
+ Three storage primitives, three contracts — pick by what a loss costs you:
14
+
15
+ | Primitive | Holds | If you lose it | Backend default |
16
+ |---|---|---|---|
17
+ | `ctx.cache` | recomputable derived views | free — recompute | memory (evicts for capacity) |
18
+ | **`ctx.kv`** | **the cursor + idempotency markers** | **re-process / double-process** | **database (durable, never evicted)** |
19
+ | `ctx.store` | the synced rows | re-fetchable from the source | your SQL store |
20
+
21
+ A cursor is the textbook `ctx.kv` case: it isn't derived from anything you still
22
+ have, so a cache (which evicts) is wrong and a hand-managed table row is
23
+ overkill. This template shows why.
24
+
25
+ ## Boot
26
+
27
+ ```bash
28
+ pnpm install # at the repo root
29
+ pnpm --filter @{{projectName}}/{{appName}} dev
30
+ # → http://localhost:4000
31
+ # → ws://localhost:4000/ws
32
+ ```
33
+
34
+ No env or services required. On `store: 'memory'`, `ctx.kv` runs on its
35
+ `database` backend in-process — durable within the run, reset on restart.
36
+ Switch `app.config.ts` to `store: 'postgres'` and the cursor + markers survive
37
+ restarts and are shared across replicas. To put **just the KV** on Redis (a
38
+ persistent one), set `kv: 'redis'` / `KV_BACKEND=redis` — the handler code does
39
+ not change.
40
+
41
+ ## What it demonstrates
42
+
43
+ | File | Primitive | `ctx.kv` surface |
44
+ |---|---|---|
45
+ | `app.config.ts` | app config | zero-infra boot; the optional `kv:` / `cache:` backend knobs. |
46
+ | `database/schema.ts` | schema | the `synced_events` rows (store) — deliberately NOT the cursor. |
47
+ | `actions/sync.pull.action.*` | action | **`getOrElse`** (cursor) → **`has`** / **`set(…, { ttlMs })`** (markers) → **`set`** (advance cursor). |
48
+ | `actions/sync.status.action.*` | action (Effect) | **`yield* Kv`** — `getOrElse` (cursor) + **`list`** (count live markers). |
49
+ | `actions/sync.reset.action.*` | action | **`delete`** (cursor) + **`list`** / **`delete`** (markers). |
50
+ | `queries/events.list.query.*` | query | reactive stream of the synced ROWS (store, for contrast). |
51
+
52
+ ## The end-to-end flow
53
+
54
+ 1. **`sync.pull`** reads the durable cursor with `getOrElse` (default `0` on the
55
+ first-ever call), pulls the next page from the (simulated) upstream, and for
56
+ each event: `has(marker)` → skip if seen, else `store.insert` the row +
57
+ `set(marker, { ttlMs })`. Finally it `set`s the cursor to the new watermark.
58
+ 2. **`sync.status`** reads the cursor and `list`s the live markers.
59
+ 3. **`sync.reset`** `delete`s the cursor. A following `sync.pull` re-reads from
60
+ `0` but **skips everything** — the markers survive independently, so
61
+ idempotency holds across a cursor rewind. Pass `{ markers: true }` to also
62
+ drop the markers and force a full re-ingest.
63
+
64
+ ## Try it
65
+
66
+ Over the rpc surface (a `voltro dev` web client, `POST /rpc`, or the inspect
67
+ `invoke` endpoint):
68
+
69
+ ```jsonc
70
+ { "tag": "sync.pull", "input": { "limit": 5 } } // → { pulled: 5, skipped: 0, cursor: 5 }
71
+ { "tag": "sync.pull", "input": { "limit": 5 } } // → { pulled: 5, skipped: 0, cursor: 10 }
72
+ { "tag": "sync.status", "input": {} } // → { cursor: 10, activeMarkers: 10 }
73
+ { "tag": "sync.reset", "input": {} } // → { cursorCleared: true, markersCleared: 0 }
74
+ { "tag": "sync.pull", "input": { "limit": 5 } } // → { pulled: 0, skipped: 5, cursor: 5 } ← markers survived
75
+ ```
76
+
77
+ Watch the synced rows stream in live via the `events.list` subscription (the
78
+ dashboard's data tab, or any web client).
79
+
80
+ ## TTL & tenant namespacing
81
+
82
+ - **TTL** — markers are written with `{ ttlMs }` (a 24h idempotency window).
83
+ Expiry is lazy: an expired marker reads as a miss and is dropped on the next
84
+ `has`/`get`. The cursor has **no** TTL — it's permanent until deleted.
85
+ - **Tenant namespacing** — `ctx.kv` keys are app-global; the facade never
86
+ namespaces for you. Every key here folds in `ctx.request.subject.tenantId`
87
+ (`sync:${tenantId}:…`) so two tenants' cursors/markers never collide.
88
+
89
+ ## Notes
90
+
91
+ - All primitives are **auto-discovered by file convention** — nothing is
92
+ registered in `app.config.ts`.
93
+ - `action` / `query` keep the **descriptor / executor split**: the browser-safe
94
+ `*.ts` descriptor never imports `@voltro/database`, `@voltro/runtime`, or
95
+ `node:*`; the `.server.ts` executor does the work.
96
+ - Durable KV has **two handler surfaces**, both wired in every runtime
97
+ (`voltro dev` AND `voltro serve`): the async **`ctx.kv`** facade (used by
98
+ `sync.pull` / `sync.reset`) and the Effect-native **`Kv`** service via
99
+ `yield* Kv` (used by `sync.status`). They share one resolved store, so pick by
100
+ whether your handler is async or an `Effect` — the state is identical.
@@ -0,0 +1,75 @@
1
+ // `sync.pull` — EXECUTOR (server-only, default export).
2
+ //
3
+ // The heart of the template: durable `ctx.kv` used for the two kinds of state
4
+ // you CAN'T recompute in an external sync.
5
+ //
6
+ // 1. the CURSOR — a watermark past everything already pulled. Lose it and
7
+ // you re-process (or skip) events. Read with getOrElse, advanced with set.
8
+ // 2. the MARKERS — one per external id, so a redelivered event isn't ingested
9
+ // twice. Written with a TTL (a bounded idempotency window). has() checks
10
+ // one without deserializing.
11
+ //
12
+ // The event ROWS themselves go to `ctx.store` — they're re-fetchable from the
13
+ // source, so a relational table is their home (query/join/report on them).
14
+
15
+ import type { AppContext } from '@voltro/runtime'
16
+
17
+ // Stand-in for a real upstream (a webhook backlog, a 3rd-party API page).
18
+ // Deterministic + pure so the template boots with zero infra and no network —
19
+ // event N has externalId `ext-N`. A real handler would `fetch()` here instead.
20
+ const fetchSince = (sequence: number, limit: number) =>
21
+ Array.from({ length: limit }, (_unused, i) => {
22
+ const seq = sequence + i + 1
23
+ return {
24
+ externalId: `ext-${seq}`,
25
+ kind: seq % 3 === 0 ? 'updated' : 'created',
26
+ payload: JSON.stringify({ seq }),
27
+ sequence: seq,
28
+ }
29
+ })
30
+
31
+ // Idempotency window: how long a "seen" marker lives. Long enough to dedupe
32
+ // redelivery, not forever — a bounded lifetime on an otherwise-durable store.
33
+ const MARKER_TTL_MS = 24 * 60 * 60_000 // 24h
34
+
35
+ const execute = async (input: { limit?: number }, ctx: AppContext) => {
36
+ // KV keys are app-global — fold the tenant in so two tenants never collide.
37
+ const tenantId = ctx.request.subject.tenantId ?? 'anon'
38
+ const cursorKey = `sync:${tenantId}:cursor`
39
+ const limit = Math.min(input.limit ?? 5, 50)
40
+
41
+ // Durable cursor — getOrElse writes the default (0) ONLY on a genuine
42
+ // first-run miss; on every later call it returns the stored watermark.
43
+ const cursor = await ctx.kv.getOrElse<number>(cursorKey, () => 0)
44
+
45
+ let pulled = 0
46
+ let skipped = 0
47
+ let highest = cursor
48
+
49
+ for (const evt of fetchSince(cursor, limit)) {
50
+ const markerKey = `sync:${tenantId}:seen:${evt.externalId}`
51
+ if (await ctx.kv.has(markerKey)) {
52
+ // Already ingested within the TTL window — the source redelivered it.
53
+ skipped++
54
+ } else {
55
+ await ctx.store.insert('synced_events', {
56
+ externalId: evt.externalId,
57
+ kind: evt.kind,
58
+ payload: evt.payload,
59
+ sequence: evt.sequence,
60
+ tenantId,
61
+ })
62
+ // Mark seen, with a TTL. Value carries the sequence for traceability.
63
+ await ctx.kv.set(markerKey, evt.sequence, { ttlMs: MARKER_TTL_MS })
64
+ pulled++
65
+ }
66
+ highest = Math.max(highest, evt.sequence)
67
+ }
68
+
69
+ // Advance the durable watermark past everything we just saw.
70
+ await ctx.kv.set(cursorKey, highest)
71
+
72
+ return { pulled, skipped, cursor: highest }
73
+ }
74
+
75
+ export default execute
@@ -0,0 +1,25 @@
1
+ // `sync.pull` — DESCRIPTOR (browser-safe: schema + name only; NO
2
+ // node/database/server imports — the codegen pulls this into the client
3
+ // rpcGroup bundle).
4
+ //
5
+ // Pull the next page of events from the (simulated) upstream. This is an
6
+ // ACTION, not a mutation/query: it does arbitrary imperative work and returns
7
+ // a computed summary rather than a single optimistic row or a reactive table
8
+ // scan. The durable `ctx.kv` cursor + idempotency markers live in the
9
+ // executor (`.server.ts`).
10
+
11
+ import { defineAction } from '@voltro/protocol'
12
+ import { Schema } from 'effect'
13
+
14
+ export const syncPull = defineAction({
15
+ name: 'sync.pull',
16
+ input: Schema.Struct({
17
+ // How many upstream events to pull this call (default 5, capped in the executor).
18
+ limit: Schema.optional(Schema.Int.pipe(Schema.greaterThan(0))),
19
+ }),
20
+ output: Schema.Struct({
21
+ pulled: Schema.Int, // newly ingested (marker written)
22
+ skipped: Schema.Int, // already seen within the idempotency window
23
+ cursor: Schema.Int, // the durable watermark after this call
24
+ }),
25
+ })
@@ -0,0 +1,25 @@
1
+ // `sync.reset` — EXECUTOR (server-only, default export).
2
+ //
3
+ // Shows the delete side of the KV surface: delete(key) → boolean (did it
4
+ // exist?), and list(prefix) + delete() to sweep a set of keys. `ctx.kv.clear()`
5
+ // would drop this app's ENTIRE KV namespace in one call — too broad here, since
6
+ // we only want this tenant's sync keys.
7
+
8
+ import type { AppContext } from '@voltro/runtime'
9
+
10
+ const execute = async (input: { markers?: boolean }, ctx: AppContext) => {
11
+ const tenantId = ctx.request.subject.tenantId ?? 'anon'
12
+
13
+ const cursorCleared = await ctx.kv.delete(`sync:${tenantId}:cursor`)
14
+
15
+ let markersCleared = 0
16
+ if (input.markers) {
17
+ for (const key of await ctx.kv.list(`sync:${tenantId}:seen:`)) {
18
+ if (await ctx.kv.delete(key)) markersCleared++
19
+ }
20
+ }
21
+
22
+ return { cursorCleared, markersCleared }
23
+ }
24
+
25
+ export default execute
@@ -0,0 +1,22 @@
1
+ // `sync.reset` — DESCRIPTOR (browser-safe).
2
+ //
3
+ // Rewind the sync. By default it clears ONLY the cursor — a following
4
+ // `sync.pull` then re-reads from the start but SKIPS everything, because the
5
+ // idempotency markers survive independently (proof the two KV concerns don't
6
+ // depend on each other). Pass `markers: true` to also drop the markers, which
7
+ // makes the next `sync.pull` re-ingest from scratch.
8
+
9
+ import { defineAction } from '@voltro/protocol'
10
+ import { Schema } from 'effect'
11
+
12
+ export const syncReset = defineAction({
13
+ name: 'sync.reset',
14
+ input: Schema.Struct({
15
+ // Also delete the idempotency markers (default: keep them).
16
+ markers: Schema.optional(Schema.Boolean),
17
+ }),
18
+ output: Schema.Struct({
19
+ cursorCleared: Schema.Boolean, // did a cursor exist?
20
+ markersCleared: Schema.Int, // how many markers were dropped
21
+ }),
22
+ })
@@ -0,0 +1,28 @@
1
+ // `sync.status` — EXECUTOR (server-only, default export), written in EFFECT mode.
2
+ //
3
+ // The same durable KV, reached the Effect-native way: `yield* Kv` from
4
+ // `@voltro/kv` instead of the async `ctx.kv` facade. The framework provides the
5
+ // `Kv` service to every handler runtime in BOTH boot paths (`voltro dev` and
6
+ // `voltro serve`), so this resolves identically in dev and prod. The methods
7
+ // mirror the facade but return `Effect`s (get yields an `Option`; the rest yield
8
+ // plain values). Pick whichever style fits — an async handler would use
9
+ // `ctx.kv` exactly as `sync.pull` does.
10
+
11
+ import { Effect } from 'effect'
12
+ import { Kv } from '@voltro/kv'
13
+ import type { AppContext } from '@voltro/runtime'
14
+
15
+ const execute = (_input: Record<string, never>, ctx: AppContext) =>
16
+ Effect.gen(function* () {
17
+ const kv = yield* Kv
18
+ const tenantId = ctx.request.subject.tenantId ?? 'anon'
19
+
20
+ const cursor = yield* kv.getOrElse(`sync:${tenantId}:cursor`, () => 0)
21
+ // list(prefix) → every live (non-expired) key under the prefix — here, the
22
+ // outstanding idempotency markers for this tenant.
23
+ const markers = yield* kv.list(`sync:${tenantId}:seen:`)
24
+
25
+ return { cursor, activeMarkers: markers.length }
26
+ })
27
+
28
+ export default execute
@@ -0,0 +1,17 @@
1
+ // `sync.status` — DESCRIPTOR (browser-safe).
2
+ //
3
+ // Read-only view of the durable sync state: where the cursor sits and how many
4
+ // idempotency markers are still live. The executor is written in Effect mode
5
+ // (`yield* Kv`) to show the Effect-native surface alongside the async `ctx.kv`.
6
+
7
+ import { defineAction } from '@voltro/protocol'
8
+ import { Schema } from 'effect'
9
+
10
+ export const syncStatus = defineAction({
11
+ name: 'sync.status',
12
+ input: Schema.Struct({}),
13
+ output: Schema.Struct({
14
+ cursor: Schema.Int, // the durable watermark
15
+ activeMarkers: Schema.Int, // idempotency markers not yet expired
16
+ }),
17
+ })
@@ -0,0 +1,48 @@
1
+ // Backend app for the {{projectName}} project. Read by `voltro dev`.
2
+ //
3
+ // This is the DURABLE KEY-VALUE template. It demonstrates `ctx.kv` — the
4
+ // framework's second storage primitive, for state you CAN'T recompute — on a
5
+ // small external-event-sync domain. `ctx.kv` is the opposite contract to
6
+ // `ctx.cache`: entries are NEVER evicted for capacity; they live until you
7
+ // delete them or their (optional) TTL lapses.
8
+ //
9
+ // The three storage primitives, side by side (the whole point of this template):
10
+ // • ctx.store — the relational rows you sync in (queryable, joinable). LOSS = re-fetchable.
11
+ // • ctx.kv — the sync CURSOR + idempotency MARKERS. LOSS = re-process / double-process. Durable.
12
+ // • ctx.cache — recomputable derived views. LOSS = free (recompute). Not used here; see the api-data-advanced template.
13
+ //
14
+ // What's in here (all auto-discovered by file convention — nothing is
15
+ // registered in this config):
16
+ //
17
+ // database/schema.ts — `syncedEvents` table (the rows we ingest)
18
+ // actions/sync.pull.action.* — THE STAR: getOrElse(cursor) → dedupe via
19
+ // has()/set(marker,{ttlMs}) → insert row →
20
+ // set(cursor). The full write surface.
21
+ // actions/sync.status.action.* — Effect-mode handler (`yield* Kv`): read
22
+ // the cursor + count markers via list().
23
+ // actions/sync.reset.action.* — delete(cursor) + list()/delete() the markers.
24
+ // queries/events.list.query.* — reactive list of the synced rows (store, not kv).
25
+ //
26
+ // `store: 'memory'` keeps boot zero-infra: `ctx.kv` runs on the `database`
27
+ // backend (the framework's `_voltro_kv` table) which, on the memory store, is
28
+ // in-process — durable within the run, reset on restart. Switch to `'postgres'`
29
+ // and the cursor/markers survive restarts and are shared across replicas.
30
+ import { defineEnv, envVar } from '@voltro/env'
31
+
32
+ // Typed environment — declared once, validated at boot (fail-fast).
33
+ export const env = defineEnv({
34
+ LOG_LEVEL: envVar.enum(['debug', 'info', 'warn', 'error'], { access: 'public', default: 'info' }),
35
+ })
36
+
37
+ export default {
38
+ type: 'api' as const,
39
+ name: '{{capProjectName}}{{capAppName}}',
40
+ store: 'memory' as const,
41
+ // OPTIONAL infra knobs (both default to a zero-infra backend):
42
+ // kv: 'redis' — durable ctx.kv on a PERSISTENT redis (default 'database').
43
+ // cache: 'redis' — distributed query/result cache (this template uses no cache).
44
+ // Env `KV_BACKEND` / `CACHE_BACKEND` override. See docs → Caching → Key-value backends.
45
+ // kv: 'redis' as const,
46
+ // cache: 'redis' as const,
47
+ env,
48
+ }
@@ -0,0 +1,62 @@
1
+ // Schema for the {{projectName}} backend.
2
+ //
3
+ // Tables are auto-discovered: every exported `table(...)` here (or in any
4
+ // `*.entity.ts` / `*.schema.ts` file) is registered by `voltro dev`.
5
+ // `actors` + `tenants` are the framework's core tables the `tenant()` /
6
+ // `audit()` mixins reference, so they must be declared.
7
+ //
8
+ // IMPORTANT contrast (the whole reason this template exists): the ROWS below
9
+ // live in `ctx.store` — they're re-fetchable from the external source, so a
10
+ // relational table is the right home. The sync CURSOR and the idempotency
11
+ // MARKERS do NOT live here — they're state you can't recompute, so they live
12
+ // in `ctx.kv` (durable, never evicted). Don't model a cursor as a table row
13
+ // you hand-manage; that's exactly what `ctx.kv` is for.
14
+
15
+ import {
16
+ databaseHandle,
17
+ id,
18
+ integer,
19
+ table,
20
+ text,
21
+ timestamp,
22
+ type InferRow,
23
+ } from '@voltro/database'
24
+ import { tenant } from '@voltro/plugin-multitenancy'
25
+
26
+ // ---------- Core tables (required by the tenant / audit mixins) ----------
27
+
28
+ export const actors = table('actors', {
29
+ id: id(),
30
+ kind: text().oneOf(['user', 'serviceAccount', 'apiKey', 'system']),
31
+ displayName: text().nullable(),
32
+ createdAt: timestamp().default('now'),
33
+ })
34
+
35
+ export const tenants = table('tenants', {
36
+ id: id(),
37
+ name: text(),
38
+ createdAt: timestamp().default('now'),
39
+ })
40
+
41
+ // ---------- Application table — the events we ingest from the source ----------
42
+
43
+ export const syncedEvents = table('synced_events', {
44
+ id: id({ prefix: 'evt' }),
45
+ // The external id we deduped on — carried through so you can trace a row
46
+ // back to its source record.
47
+ externalId: text(),
48
+ kind: text(),
49
+ payload: text(),
50
+ // The source sequence number this row came from (the cursor advances past it).
51
+ sequence: integer(),
52
+ })
53
+ // tenant() pulls audit() transitively → tenantId + created/updated columns,
54
+ // auto-stamped by the runtime from the caller's subject.
55
+ .with(tenant())
56
+ // Opt into the matcher engine so the `events.list` subscription wakes on
57
+ // every insert.
58
+ .reactive()
59
+
60
+ export type SyncedEvent = InferRow<typeof syncedEvents>
61
+
62
+ export const database = databaseHandle({ actors, tenants, syncedEvents })
@@ -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/kv": "0.1.0",
18
+ "@voltro/plugin-multitenancy": "0.1.0",
19
+ "@voltro/protocol": "0.1.0",
20
+ "@voltro/runtime": "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,14 @@
1
+ import type { AppContext } from '@voltro/runtime'
2
+
3
+ const execute = (_input: Record<string, never>, _ctx: AppContext) => ({
4
+ descriptor: {
5
+ table: 'synced_events' as const,
6
+ predicate: undefined,
7
+ order: [{ column: 'sequence' as const, direction: 'desc' as const }],
8
+ take: 100,
9
+ skip: undefined,
10
+ projection: undefined,
11
+ },
12
+ })
13
+
14
+ export default execute
@@ -0,0 +1,23 @@
1
+ // `events.list` — DESCRIPTOR (browser-safe).
2
+ //
3
+ // A reactive subscription over the synced ROWS (which live in `ctx.store`, not
4
+ // `ctx.kv`). This is the contrast the template is built around: the rows are
5
+ // relational data you query/stream; the cursor + markers that produced them are
6
+ // durable KV. The runtime AND-merges the caller's tenant scope via the
7
+ // `tenant()` mixin — no manual `eq('tenantId', …)` needed.
8
+
9
+ import { defineQuery } from '@voltro/protocol'
10
+ import { Schema } from 'effect'
11
+
12
+ export const listEvents = defineQuery({
13
+ name: 'events.list',
14
+ input: Schema.Struct({}),
15
+ output: Schema.Struct({
16
+ id: Schema.String,
17
+ externalId: Schema.String,
18
+ kind: Schema.String,
19
+ sequence: Schema.Number,
20
+ tenantId: Schema.String,
21
+ createdAt: Schema.Date,
22
+ }),
23
+ })
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "api-kv",
3
+ "kind": "api",
4
+ "summary": "Durable key-value state (`ctx.kv`) on an external-event-sync domain: a sync cursor you can't recompute, TTL-bounded idempotency markers to dedupe redelivered events, and the store-vs-kv-vs-cache contrast. Exercises the full ctx.kv surface (getOrElse / has / set+TTL / set / list / delete) in async AND Effect handlers.",
5
+ "tags": ["api", "kv", "key-value", "durable", "state", "idempotency", "cursor", "sync"]
6
+ }
@@ -0,0 +1,103 @@
1
+ // Unit tests with `@voltro/testing` — the headline of this template is the
2
+ // durable KV behavior, so we exercise it thoroughly against the REAL in-memory
3
+ // `ctx.kv` that `makeTestContext` provides (same contract as the production KV:
4
+ // durable, TTL-honouring, never capacity-evicted).
5
+ //
6
+ // - `sync.pull` reads/advances a durable cursor and writes per-event
7
+ // idempotency markers → asserted across TWO calls (the cursor must carry
8
+ // over so the second call continues past it, not re-pull from 0).
9
+ // - `sync.reset` clears the cursor (and optionally the markers).
10
+ //
11
+ // `sync.status` is written in Effect mode (`yield* Kv` from `@voltro/kv`), which
12
+ // needs the `Kv` service layer provided — an integration concern, not a plain
13
+ // unit run — so it is intentionally not driven here.
14
+ //
15
+ // The simulated upstream is deterministic: event N has externalId `ext-N` and
16
+ // sequence N, so pulling `limit` fresh events advances the cursor by exactly
17
+ // `limit`. Run with `voltro test` (vitest).
18
+
19
+ import { describe, it, expect } from 'vitest'
20
+ import { makeTestContext, mockStore } from '@voltro/testing'
21
+ import { database } from '../database/schema' // registers synced_events / actors / tenants
22
+ import syncPull from '../actions/sync.pull.action.server'
23
+ import syncReset from '../actions/sync.reset.action.server'
24
+
25
+ const subject = { type: 'user', id: 'user_1', tenantId: 'acme' } as const
26
+
27
+ describe('sync.pull (durable cursor + idempotency markers)', () => {
28
+ it('first pull ingests N events, inserts the rows, and advances the cursor to N', async () => {
29
+ const ctx = makeTestContext({ subject, store: mockStore({ synced_events: [] }) })
30
+
31
+ const result = await syncPull({ limit: 3 }, ctx)
32
+
33
+ expect(result.pulled).toBe(3)
34
+ expect(result.skipped).toBe(0)
35
+ expect(result.cursor).toBe(3)
36
+
37
+ // The event rows landed in the relational store, tenant-stamped.
38
+ const rows = await ctx.store.query(database.syncedEvents.descriptor)
39
+ expect(rows).toHaveLength(3)
40
+ expect(rows.every((r) => r['tenantId'] === 'acme')).toBe(true)
41
+ expect((rows.map((r) => r['externalId']) as ReadonlyArray<string>).sort()).toEqual([
42
+ 'ext-1',
43
+ 'ext-2',
44
+ 'ext-3',
45
+ ])
46
+
47
+ // The durable cursor persisted in ctx.kv.
48
+ expect(await ctx.kv.get<number>('sync:acme:cursor')).toBe(3)
49
+ })
50
+
51
+ it('a second pull continues PAST the cursor rather than re-pulling from 0', async () => {
52
+ const ctx = makeTestContext({ subject, store: mockStore({ synced_events: [] }) })
53
+
54
+ const first = await syncPull({ limit: 2 }, ctx)
55
+ expect(first.cursor).toBe(2)
56
+
57
+ const second = await syncPull({ limit: 2 }, ctx)
58
+
59
+ // Continues at 3..4 — all fresh (distinct externalIds), none skipped.
60
+ expect(second.pulled).toBe(2)
61
+ expect(second.skipped).toBe(0)
62
+ expect(second.cursor).toBe(4)
63
+
64
+ const rows = await ctx.store.query(database.syncedEvents.descriptor)
65
+ expect(rows).toHaveLength(4) // 2 + 2, no duplicates
66
+ expect((rows.map((r) => r['sequence']) as ReadonlyArray<number>).sort((a, b) => a - b)).toEqual([
67
+ 1, 2, 3, 4,
68
+ ])
69
+ })
70
+ })
71
+
72
+ describe('sync.reset', () => {
73
+ it('clears the durable cursor so the next pull restarts from 0', async () => {
74
+ const ctx = makeTestContext({ subject, store: mockStore({ synced_events: [] }) })
75
+
76
+ await syncPull({ limit: 2 }, ctx)
77
+ expect(await ctx.kv.get<number>('sync:acme:cursor')).toBe(2)
78
+
79
+ const reset = await syncReset({}, ctx)
80
+ expect(reset.cursorCleared).toBe(true) // the key existed and was removed
81
+ expect(await ctx.kv.has('sync:acme:cursor')).toBe(false)
82
+
83
+ // Cursor gone → the next pull re-reads from the getOrElse default (0).
84
+ const afterReset = await syncPull({ limit: 1 }, ctx)
85
+ expect(afterReset.cursor).toBe(1)
86
+ })
87
+
88
+ it('reports cursorCleared=false when there was no cursor to clear', async () => {
89
+ const ctx = makeTestContext({ subject, store: mockStore({ synced_events: [] }) })
90
+ const reset = await syncReset({}, ctx)
91
+ expect(reset.cursorCleared).toBe(false)
92
+ expect(reset.markersCleared).toBe(0)
93
+ })
94
+
95
+ it('sweeps the idempotency markers when { markers: true }', async () => {
96
+ const ctx = makeTestContext({ subject, store: mockStore({ synced_events: [] }) })
97
+ await syncPull({ limit: 3 }, ctx) // writes 3 seen-markers
98
+
99
+ const reset = await syncReset({ markers: true }, ctx)
100
+ expect(reset.markersCleared).toBe(3)
101
+ expect(await ctx.kv.list('sync:acme:seen:')).toHaveLength(0)
102
+ })
103
+ })
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "include": ["**/*.ts", "**/*.tsx"],
4
+ "exclude": ["node_modules", "dist"]
5
+ }
@@ -0,0 +1,46 @@
1
+ # {{projectName}} / {{appName}}
2
+
3
+ Voltro API scaffold (template: **api-moderation**) — pre-commit content
4
+ moderation with `@voltro/plugin-moderation`.
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
+ - **Two actions, two outcomes** — `moderationPlugin` checks named input fields
17
+ on the rpc interceptors, BEFORE the handler:
18
+ - **block** (`posts.create`, fields `title`/`body`): banned content fails
19
+ typed `ContentRejected` and the write **never commits**.
20
+ - **flag** (`comments.create`, field `body`): the comment IS written, but
21
+ flagged for review (the dashboard's **Moderation** panel surfaces the queue).
22
+ - **Pluggable provider** — `keywordProvider([...])` is a zero-dep deny-list;
23
+ `aiProvider()` is an optional LLM classifier (fails open). Swap one for the
24
+ other in `app.config.ts` — the rules don't change.
25
+ - **In-handler redaction** — when you need to rewrite rather than reject, the
26
+ `moderate(text)` helper returns a verdict you can act on inside a handler
27
+ (the interceptor can't rewrite input).
28
+
29
+ ## Try it
30
+
31
+ ```bash
32
+ # banned word in a post → blocked, nothing written:
33
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
34
+ -d '{"tag":"posts.create","input":{"title":"hi","body":"buy cheap spam now"}}'
35
+ # → { ok:false, error:{ _tag:"ContentRejected", reason:"matched denied term(s): spam" } }
36
+
37
+ # clean post → written:
38
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
39
+ -d '{"tag":"posts.create","input":{"title":"hi","body":"a normal post"}}'
40
+ # → { ok:true, result:{ id, … } }
41
+
42
+ # banned word in a comment → WRITTEN but flagged (flag rule doesn't block):
43
+ curl -s localhost:4000/_voltro/inspect/invoke -H 'content-type: application/json' \
44
+ -d '{"tag":"comments.create","input":{"body":"this is spam"}}'
45
+ # → { ok:true, result:{ id, … } } (and queued for review)
46
+ ```