@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,666 @@
1
+ # Scheduling
2
+
3
+ > Deployment-agnostic scheduled jobs in Voltro — one *.cron.tsx definition that runs unchanged on a single box, a multi-instance fleet, or an external scheduler.
4
+
5
+
6
+
7
+ ---
8
+
9
+ <!-- source: en/scheduling/overview.md -->
10
+ ## Overview
11
+
12
+ _Deployment-agnostic scheduled jobs in Voltro — one *.cron.tsx definition that runs unchanged on a single box, a multi-instance fleet, or an external scheduler._
13
+
14
+ A **schedule** is a job the clock invokes — "send the digest at 09:00", "prune sessions every 15 minutes". In Voltro a schedule lives in a `*.cron.tsx` file and default-exports `defineSchedule({...})`.
15
+
16
+ The defining idea: **the job definition is identical across every hosting topology.** The cron expression, timezone, and handler never change whether you run one PM2 process, ten Kubernetes replicas, or hand the firing off to AWS EventBridge. What differs — *who* fires it and *how* exactly-once is guaranteed — is configuration, not code.
17
+
18
+ ```tsx no-check
19
+ // apps/api/schedules/digest.cron.tsx
20
+ import { defineSchedule } from '@voltro/runtime'
21
+
22
+ export default defineSchedule({
23
+ name: 'dailyDigest',
24
+ cron: '0 9 * * *', // 09:00, five-field cron
25
+ timezone: 'Europe/Berlin', // REQUIRED — never server-local
26
+ handler: async ({ app }) => {
27
+ const users = await app.store.users.where({ digestOptIn: true }).all()
28
+ for (const u of users) await sendDigest(app, u)
29
+ },
30
+ })
31
+ ```
32
+
33
+ That's the whole job. Drop the file in your api app, run `voltro dev`, and it fires at 09:00 Berlin time.
34
+
35
+ ## What's in this section
36
+
37
+ - [Defining a schedule](/docs/scheduling/definition) — `defineSchedule`, cron syntax, the timezone rule, the handler context
38
+ - [Coordination](/docs/scheduling/coordination) — `single` / `advisoryLock` / `cluster`: exactly-once across instances
39
+ - [Trigger drivers](/docs/scheduling/triggers) — `self` (in-app timer) vs `external` (platform scheduler hits an HTTP endpoint)
40
+ - [Overlap & backfill](/docs/scheduling/overlap-and-backfill) — what happens on slow runs and missed firings
41
+ - [Deployment](/docs/scheduling/deployment) — generating Kubernetes / AWS / GCP / Azure manifests with `voltro schedule-manifest`
42
+ - [Dashboard](/docs/scheduling/dashboard) — firing history, status, and "Run now"
43
+
44
+ ## Schedule vs workflow
45
+
46
+ Schedules and [workflows](/docs/workflows/overview) are both durable execution, but they answer different questions.
47
+
48
+ | You have… | Use |
49
+ |---|---|
50
+ | A job the clock starts on a recurring cadence | **Schedule** |
51
+ | A multi-step job that must survive a deploy mid-flight | [Workflow](/docs/workflows/overview) |
52
+ | "Run this daily, and each run is one quick operation" | Schedule |
53
+ | "Run this daily, and each run is a long multi-step saga" | Schedule with a direct **workflow target** or a handler that starts one |
54
+ | A user-facing request returning in <100ms | [Mutation](/docs/data/mutations) |
55
+
56
+ The wedge: **schedules answer *when*, workflows answer *how durably*.** A schedule handler is "a mutation the clock invokes" — same `app` context, same store, same plugin interceptors. If the handler's work itself needs to survive crashes, the handler kicks off a workflow and returns.
57
+
58
+ That handoff works the same in `voltro dev` and the production API server: when the app has workflow files, schedule handlers receive `app.workflows` and can start durable work without importing a client.
59
+
60
+ ```tsx
61
+ // apps/api/schedules/daily-rollup.cron.tsx
62
+ import { defineSchedule } from '@voltro/runtime'
63
+
64
+ export default defineSchedule({
65
+ name: 'dailyRollup',
66
+ cron: '0 2 * * *',
67
+ timezone: 'UTC',
68
+ handler: async ({ app, scheduledAt }) => {
69
+ await app.workflows!.start('billing.dailyRollup', {
70
+ day: scheduledAt.toISOString().slice(0, 10),
71
+ })
72
+ },
73
+ })
74
+ ```
75
+
76
+ > Earlier framework guidance suggested modelling cron with a workflow plus `Effect.sleep`. That still works for one-off durable delays, but recurring cadences belong in a `*.cron.tsx` schedule — you get coordination, backfill, overlap control, and a dashboard for free.
77
+
78
+ ## The three things you configure
79
+
80
+ 1. **The job** — `defineSchedule({ cron, timezone, handler })`. Lives in the file. Topology-independent.
81
+ 2. **The trigger** — who drives the clock: an in-app timer (`self`) or an outside scheduler (`external`). App-wide default, overridable per job. See [trigger drivers](/docs/scheduling/triggers).
82
+ 3. **The coordination** — how N instances avoid double-firing: `single`, `advisoryLock`, or `cluster`. App-wide. See [coordination](/docs/scheduling/coordination).
83
+
84
+ ```ts
85
+ // apps/api/app.config.ts
86
+ export default {
87
+ type: 'api' as const,
88
+ name: 'api',
89
+ scheduling: {
90
+ trigger: 'self', // default; 'external' to delegate the clock
91
+ coordination: 'advisoryLock', // default on every multi-instance SQL store; 'single' on memory/sqlite
92
+ },
93
+ }
94
+ ```
95
+
96
+ Sensible defaults derive from your store: **`memory` / `sqlite` → `self` / `single`**, **every multi-instance SQL store (`postgres` / `mariadb` / `mysql` / `mssql`) → `self` / `advisoryLock`** (the claim-row gate works on all of them). Most apps never set this block.
97
+
98
+ ## Guarantees and honest caveats
99
+
100
+ - **Deterministic firing instant.** `scheduledAt` is derived from the cron expression, not each replica's `Date.now()`, so every instance computes the same time-bucket — the basis for clock-skew-safe coordination.
101
+ - **Non-dying timer.** The `self` driver re-arms after every firing, even if a handler throws; a handler defect can't break the chain.
102
+ - **Exactly-once is only as strong as your coordination.** `single` does not dedupe — running two `single` instances double-fires. Use `advisoryLock` or `cluster` for multi-instance. See [coordination](/docs/scheduling/coordination).
103
+ - **Sub-minute `cluster` firings lag.** The `cluster` strategy waits for shard assignment (~10s on a cold runner) before the first firing. Fine for minute-and-up cadences; not for "every second".
104
+ - **External triggers are minute-granular.** Six-field (seconds) cron is rejected when generating external manifests — k8s/EventBridge/Cloud Scheduler can't express sub-minute. See [deployment](/docs/scheduling/deployment).
105
+
106
+
107
+
108
+ ---
109
+
110
+ <!-- source: en/scheduling/definition.md -->
111
+ ## Defining a schedule
112
+
113
+ _defineSchedule — the *.cron.tsx file convention, cron expression syntax, the mandatory timezone, and the handler context._
114
+
115
+ A schedule is a `*.cron.tsx` file that default-exports `defineSchedule({...})`. The CLI discovers it the same way it discovers queries and mutations — no registration, no manifest.
116
+
117
+ ```tsx no-check
118
+ // apps/api/schedules/cleanup.cron.tsx
119
+ import { defineSchedule } from '@voltro/runtime'
120
+
121
+ export default defineSchedule({
122
+ name: 'cleanupSessions',
123
+ cron: '*/15 * * * *',
124
+ timezone: 'UTC',
125
+ description: 'Prune expired sessions.',
126
+ handler: async ({ app, scheduledAt }) => {
127
+ await app.store.sessions.where({ expiresAt: { lt: scheduledAt } }).delete()
128
+ },
129
+ })
130
+ ```
131
+
132
+ ## The config
133
+
134
+ | Field | Required | Default | Notes |
135
+ |---|---|---|---|
136
+ | `name` | yes | — | Stable identifier. Used as the idempotency/coordination key and the dashboard label. |
137
+ | `cron` | yes | — | Standard cron expression (see below). Validated eagerly. |
138
+ | `timezone` | yes | — | IANA zone (`"UTC"`, `"Europe/Berlin"`). Never server-local. |
139
+ | `handler` | one of `handler` / `workflow` | — | `(ctx) => void \| Promise<void>`. Custom work for this firing. |
140
+ | `workflow` | one of `handler` / `workflow` | — | Direct workflow target: `{ name, payload }`. Use when the schedule only starts durable work. |
141
+ | `trigger` | no | app default | `'self'` or `'external'` — per-job override. See [triggers](/docs/scheduling/triggers). |
142
+ | `onOverlap` | no | `'skip'` | `'skip'` / `'queue'` / `'parallel'`. See [overlap](/docs/scheduling/overlap-and-backfill). |
143
+ | `backfill` | no | `'skip'` | `'skip'` / `'latest'` / `'all'`. See [backfill](/docs/scheduling/overlap-and-backfill). |
144
+ | `maxRuntimeMs` | no | `1_800_000` (30 min) | Watchdog. A run exceeding it is recorded `failed` with reason `timeout`. |
145
+ | `description` | no | — | Shown in the dashboard and generated external manifests. |
146
+
147
+ ## Direct workflow target
148
+
149
+ When the schedule's only job is to start a durable workflow, declare it directly instead of writing a one-line handler:
150
+
151
+ ```tsx
152
+ // apps/api/schedules/daily-rollup.cron.tsx
153
+ import { defineSchedule } from '@voltro/runtime'
154
+
155
+ export default defineSchedule({
156
+ name: 'dailyRollup',
157
+ cron: '0 2 * * *',
158
+ timezone: 'UTC',
159
+ workflow: {
160
+ name: 'billing.dailyRollup',
161
+ payload: ({ scheduledAt }) => ({
162
+ day: scheduledAt.toISOString().slice(0, 10),
163
+ }),
164
+ },
165
+ })
166
+ ```
167
+
168
+ Use `handler` when the firing needs custom branching, extra writes, or multiple side effects. You can still call `ctx.app.workflows.start(...)` manually from that handler.
169
+
170
+ ## Cron syntax
171
+
172
+ The expression is parsed by `effect`'s `Cron` module, which accepts **five fields** (minute precision) or **six fields** (with a leading seconds field):
173
+
174
+ ```
175
+ ┌───────────── second (0–59) ← optional 6th field
176
+ │ ┌─────────── minute (0–59)
177
+ │ │ ┌───────── hour (0–23)
178
+ │ │ │ ┌─────── day of month (1–31)
179
+ │ │ │ │ ┌───── month (1–12)
180
+ │ │ │ │ │ ┌─── day of week (0–6, Sun=0)
181
+ │ │ │ │ │ │
182
+ * * * * * *
183
+ ```
184
+
185
+ | Expression | Fires |
186
+ |---|---|
187
+ | `0 9 * * *` | 09:00 every day |
188
+ | `*/15 * * * *` | every 15 minutes |
189
+ | `0 0 1 * *` | midnight on the 1st of each month |
190
+ | `0 0 * * 1` | midnight every Monday |
191
+ | `*/30 * * * * *` | every 30 **seconds** (six-field) |
192
+
193
+ A typo throws at **definition time**, surfaced in the boot log — not silently swallowed by a scheduler loop that then never fires:
194
+
195
+ ```
196
+ defineSchedule("dailyDigest"): invalid cron expression "0 25 * * *" (tz="UTC") — …
197
+ ```
198
+
199
+ > Six-field (seconds) expressions only run under the `self` trigger. They are rejected when generating [external manifests](/docs/scheduling/deployment) because k8s/EventBridge/Cloud Scheduler are minute-granular.
200
+
201
+ ## The timezone is mandatory — on purpose
202
+
203
+ There is no default timezone. Omitting it throws. Server-local time is a bug factory: a container's TZ is usually UTC regardless of where your users are, so "09:00" silently means something different in dev, CI, and prod. Stating the zone makes the intent explicit and identical everywhere.
204
+
205
+ ```tsx
206
+ timezone: 'Europe/Berlin' // 09:00 Berlin — DST handled by effect's Cron
207
+ ```
208
+
209
+ ## The handler context
210
+
211
+ The handler receives a `ScheduleContext` — the same `app` a mutation gets, plus firing metadata:
212
+
213
+ ```tsx
214
+ handler: async (ctx) => {
215
+ ctx.scheduledAt // Date — the instant this firing was scheduled for (deterministic, cron-derived)
216
+ ctx.firedAt // Date — when the handler actually started (may lag under load/contention)
217
+ ctx.runId // string — the _voltro_schedule_runs row id for this firing
218
+ ctx.trigger // 'self' | 'external' | 'manual' — where the firing came from
219
+ ctx.app // AppContext — store, request subject, webhooks, plugin interceptors
220
+ }
221
+ ```
222
+
223
+ Use `scheduledAt`, not `Date.now()`, for any time-bucketed query (the "prune sessions older than this slot" pattern). It's stable across replicas and reflects the intended instant even if the run was delayed.
224
+
225
+ `ctx.trigger === 'manual'` distinguishes a dashboard **Run now** click from a clock firing — handy when a manual run should skip a guard (e.g. "only on weekdays") that the scheduled path enforces.
226
+
227
+ ## Discovery
228
+
229
+ `voltro dev` and `voltro build` glob `**/*.cron.{ts,tsx}` under your api app. Each discovered schedule is logged at boot:
230
+
231
+ ```
232
+ scheduler: starting total=2 self=2 external=0 coordinator=advisoryLock
233
+ ```
234
+
235
+ The framework also creates the `_voltro_schedule_runs` and (for `advisoryLock`) `_voltro_schedule_claims` tables — `voltro migrate` emits their DDL automatically, so you never hand-write a migration for them.
236
+
237
+
238
+
239
+ ---
240
+
241
+ <!-- source: en/scheduling/coordination.md -->
242
+ ## Coordination
243
+
244
+ _How N instances of your app avoid double-firing a schedule — single, advisoryLock, and cluster exactly-once strategies._
245
+
246
+ When more than one instance of your app is running, each one's `self` timer wants to fire the same schedule at the same instant. **Coordination** is the gate that decides which instance actually runs it. It's an app-wide setting:
247
+
248
+ ```ts
249
+ // apps/api/app.config.ts
250
+ scheduling: { coordination: 'advisoryLock' }
251
+ ```
252
+
253
+ The job definition never mentions coordination — you can move from one box to a fleet without touching a `*.cron.tsx` file.
254
+
255
+ ## The three strategies
256
+
257
+ | Strategy | Exactly-once across instances? | Needs | Use when |
258
+ |---|---|---|---|
259
+ | `single` | **No** — every instance fires | nothing | One process: PM2 single instance, a single container, local dev |
260
+ | `advisoryLock` | Yes — Postgres arbitrates | Postgres | Multiple instances, no orchestrator: 2–N replicas behind a load balancer |
261
+ | `cluster` | Yes — shard owner fires | Postgres + cluster runner | You already run `@effect/cluster` for workflows and want schedules on the same fabric |
262
+
263
+ Default: **`single` on a memory store, `advisoryLock` on Postgres.** You rarely set this explicitly.
264
+
265
+ ## `single`
266
+
267
+ No gate. The instance's timer fires, the handler runs. Zero coordination overhead.
268
+
269
+ This is correct **only if exactly one instance runs the schedule.** Two `single` instances = two firings. That's not a bug to work around — it's the contract. If you scale past one instance, switch to `advisoryLock`.
270
+
271
+ ## `advisoryLock`
272
+
273
+ The portable multi-instance strategy. It needs nothing but the Postgres you already have — no Redis, no orchestrator, no leader election.
274
+
275
+ **How it works.** Each firing computes a deterministic key, `<name>@<iso-second>` (e.g. `dailyDigest@2026-05-28T09:00:00`), from the cron-derived `scheduledAt` — *not* `Date.now()`, so every replica computes the identical key regardless of clock skew within the firing window. The bucket is second-precision (`YYYY-MM-DDTHH:MM:SS`, 19 chars), so a 6-field cron like `*/10 * * * * *` gets a distinct key for every firing instant within a minute. Every replica races to `INSERT` that key into `_voltro_schedule_claims`. The table's primary key makes exactly one `INSERT` win; the rest hit the conflict and stand down.
276
+
277
+ ```
278
+ replica A ─┐ ┌─ INSERT dailyDigest@…09:00 → wins, runs
279
+ replica B ─┼─ same bucket, same key ─┤
280
+ replica C ─┘ └─ PK conflict → stands down (no run row)
281
+ ```
282
+
283
+ - **Self-expiring.** The key includes the firing instant, so a crashed winner doesn't block the next firing — the next firing instant is a new key. Stale rows are pruned lazily.
284
+ - **Pool-safe.** Unlike a session-level `pg_advisory_lock` (tied to a connection a pool may reassign), a claims row is durable and connection-independent.
285
+ - **Fail-closed.** If the claims table is unreachable, the coordinator logs a warning and declines to fire rather than risk a double-fire. A missing run is recoverable via [backfill](/docs/scheduling/overlap-and-backfill); a double-fire (two charge emails) often isn't.
286
+
287
+ Losers don't write a run row — at scale that would be N−1 noise rows per firing. Only the winner's run appears in the dashboard, tagged `wonLock`.
288
+
289
+ ## `cluster`
290
+
291
+ If you already run `@effect/cluster` (for [workflows](/docs/workflows/cluster)), schedules can ride the same sharding fabric. Each schedule becomes a `ClusterCron` singleton; the cluster assigns it to exactly one shard owner, and only that runner fires. The in-app `self` timer is **not** armed in this mode — the cluster owns the clock, so arming it too would double-fire.
292
+
293
+ Runs are tagged `cluster`. Coordination is handled by the cluster's shard assignment, so there's no claims table for cluster-mode schedules.
294
+
295
+ **Caveat — sub-minute lag.** A freshly started runner waits for shard assignment before the first firing (~10s observed on a cold runner). For minute-and-up cadences this is invisible. For "every second" it isn't — use `self` + `single`/`advisoryLock` for sub-minute work, or accept the warm-up.
296
+
297
+ **Caveat — Postgres only.** `cluster` (and `advisoryLock`) need Postgres. On a memory store they fall back to `single` with a boot warning:
298
+
299
+ ```
300
+ scheduling.coordination=cluster needs store=postgres — falling back to single (dev/memory)
301
+ ```
302
+
303
+ ## Choosing
304
+
305
+ ```
306
+ Single process? ───────────────────────────────→ single
307
+ Multiple instances, no orchestrator? ──────────→ advisoryLock (← the default on Postgres)
308
+ Already running @effect/cluster for workflows? → cluster
309
+ Letting k8s/EventBridge drive the clock? ──────→ trigger: 'external' (coordination is moot — the platform fires once)
310
+ ```
311
+
312
+ When the platform scheduler drives the firing (the [`external` trigger](/docs/scheduling/triggers)), the platform guarantees once-only, so coordination doesn't apply — the run is tagged `external`.
313
+
314
+ ## What each run records
315
+
316
+ Every firing writes a `_voltro_schedule_runs` row with a `coordinationOutcome` so you can see, after the fact, *why* this instance ran it:
317
+
318
+ | `coordinationOutcome` | Meaning |
319
+ |---|---|
320
+ | `single` | No coordination — single-instance mode |
321
+ | `wonLock` | Won the `advisoryLock` race |
322
+ | `cluster` | Fired as the cluster shard owner |
323
+ | `external` | Driven by an outside scheduler hitting the fire endpoint |
324
+
325
+ (`lostLock` firings are not recorded — see above.) The dashboard surfaces this per run; see [the dashboard doc](/docs/scheduling/dashboard).
326
+
327
+
328
+
329
+ ---
330
+
331
+ <!-- source: en/scheduling/triggers.md -->
332
+ ## Trigger drivers
333
+
334
+ _self vs external — whether an in-app supervised timer drives the clock, or an outside scheduler (k8s CronJob, EventBridge, Cloud Scheduler) hits an HTTP endpoint._
335
+
336
+ The **trigger** decides *who keeps time*. Two drivers:
337
+
338
+ - **`self`** — an in-app supervised timer fires the schedule from inside your process.
339
+ - **`external`** — your app exposes an HTTP endpoint; an outside scheduler (k8s CronJob, AWS EventBridge, GCP Cloud Scheduler, …) POSTs to it on the cadence.
340
+
341
+ It's an app-wide default, overridable per job:
342
+
343
+ ```ts
344
+ // apps/api/app.config.ts
345
+ scheduling: { trigger: 'self' } // default
346
+ ```
347
+
348
+ ```tsx
349
+ // override one heavy job to be platform-driven
350
+ export default defineSchedule({
351
+ name: 'monthlyInvoice',
352
+ cron: '0 3 1 * *',
353
+ timezone: 'UTC',
354
+ trigger: 'external', // a dedicated k8s CronJob drives this one
355
+ handler: async ({ app }) => { /* … */ },
356
+ })
357
+ ```
358
+
359
+ ## `self` — the in-app timer
360
+
361
+ The default, and the right choice for most deployments. Each `self` schedule gets a self-rescheduling `setTimeout` (not `setInterval` — cron is not fixed-interval, and `setInterval` drifts and double-fires under event-loop pressure). After each firing the timer re-arms for the next cron occurrence.
362
+
363
+ Properties:
364
+
365
+ - **Non-dying.** The re-arm happens in a `finally` — a handler that throws cannot break the chain.
366
+ - **Long-wait safe.** `setTimeout` delays are 32-bit milliseconds (max ~24.8 days). A quarterly or yearly schedule is chunked into shorter sleeps and re-evaluated, so it doesn't silently clamp and fire on every tick.
367
+ - **Survives restarts.** Run state persists to `_voltro_schedule_runs`; on boot, missed firings are reconciled per the [backfill](/docs/scheduling/overlap-and-backfill) policy.
368
+ - **Coordinated.** Multiple `self` instances dedupe via the [coordination](/docs/scheduling/coordination) strategy.
369
+
370
+ This is what runs under `voltro dev`, a single PM2 process, or a fleet of replicas (with `advisoryLock`).
371
+
372
+ ## `external` — the platform drives the clock
373
+
374
+ Some environments want the orchestrator, not the app, to own scheduling — an enterprise k8s policy that all cron lives in `CronJob` objects, or a serverless deployment that scales the app to zero between firings. In `external` mode the app does **not** arm an in-app timer. Instead it exposes:
375
+
376
+ ```
377
+ POST /_voltro/schedule/<name>/fire?trigger=external
378
+ ```
379
+
380
+ The outside scheduler hits that endpoint on the cron cadence; the app runs the handler and records the run tagged `external`. Because the platform fires exactly once, [coordination](/docs/scheduling/coordination) does not apply.
381
+
382
+ You don't hand-write the scheduler config. `voltro schedule-manifest` reads your `*.cron.tsx` files and emits the matching CronJob / EventBridge / Cloud Scheduler manifest pointing at this endpoint — see [deployment](/docs/scheduling/deployment).
383
+
384
+ > The endpoint should be protected in any non-trivial deployment (network policy, an auth token, or an ingress rule) so only your scheduler can POST to it. Treat it like any other privileged internal query.
385
+
386
+ ## Mixing drivers
387
+
388
+ `trigger` is per-job, so you can split by workload:
389
+
390
+ | Job | Trigger | Why |
391
+ |---|---|---|
392
+ | `cacheWarmer` (every 5 min) | `self` | Lightweight, in-process, no infra |
393
+ | `monthlyInvoice` (1st of month) | `external` | Heavy; run it as a dedicated k8s `CronJob` pod that scales independently |
394
+
395
+ A six-field (seconds) cron forces `self` — external schedulers can't express sub-minute, so such a job is rejected by the manifest generator.
396
+
397
+ ## Picking a driver
398
+
399
+ ```
400
+ Want zero scheduling infra, app owns the clock? ──────────→ self (the default)
401
+ Enterprise policy: all cron must be k8s CronJobs? ───────→ external
402
+ App scales to zero / serverless between firings? ────────→ external
403
+ Sub-minute cadence (six-field cron)? ────────────────────→ self (external can't do sub-minute)
404
+ ```
405
+
406
+
407
+
408
+ ---
409
+
410
+ <!-- source: en/scheduling/overlap-and-backfill.md -->
411
+ ## Overlap & backfill
412
+
413
+ _What happens when a run is still in flight at the next firing (overlap), what happens to firings missed during downtime (backfill), and the per-run watchdog._
414
+
415
+ Two timing edge cases every recurring job hits eventually: a run that's still going when the next firing is due, and firings that were missed while the process was down. Voltro makes both explicit policies on the schedule.
416
+
417
+ ## Overlap — `onOverlap`
418
+
419
+ When a firing arrives and the previous run of the **same schedule** is still in flight:
420
+
421
+ | Policy | Behaviour |
422
+ |---|---|
423
+ | `skip` *(default)* | Don't start a second run. Record a `skipped` row and move on. |
424
+ | `queue` | Serialize: wait for the in-flight run to finish, then run this one. Never concurrent. |
425
+ | `parallel` | Start the new run immediately, alongside the old one. |
426
+
427
+ ```tsx
428
+ defineSchedule({
429
+ name: 'reindex',
430
+ cron: '*/5 * * * *',
431
+ timezone: 'UTC',
432
+ onOverlap: 'skip', // a slow reindex shouldn't pile up
433
+ handler: async ({ app }) => { /* … */ },
434
+ })
435
+ ```
436
+
437
+ **Choosing:**
438
+
439
+ - `skip` — idempotent or "latest state wins" jobs (reindex, cache warm). The default, and almost always right.
440
+ - `queue` — every firing's work matters and must happen in order (sequential batch processing). Runs serialize behind one another.
441
+ - `parallel` — runs are independent and you genuinely want concurrency (fan-out to per-tenant work).
442
+
443
+ > **`queue` caveat — unbounded growth.** If a `queue` job consistently takes longer than its interval, the queue grows without bound and the schedule falls further behind. `queue` assumes runs are *usually* faster than the cadence, with occasional overruns. If runs are reliably slower than the interval, your cadence is wrong, not your overlap policy.
444
+
445
+ A manual **Run now** from the dashboard always runs, regardless of `onOverlap` — operators expect the button to fire.
446
+
447
+ ## The watchdog — `maxRuntimeMs`
448
+
449
+ Every run races a watchdog (default **30 minutes**). A run that exceeds it stops being awaited and is recorded `failed` with `errorTag: 'timeout'`, so a run row never sits `running` forever.
450
+
451
+ ```tsx
452
+ defineSchedule({
453
+ name: 'nightlyExport',
454
+ cron: '0 2 * * *',
455
+ timezone: 'UTC',
456
+ maxRuntimeMs: 2 * 60 * 60_000, // 2 hours — a big export
457
+ handler: async ({ app }) => { /* … */ },
458
+ })
459
+ ```
460
+
461
+ > The watchdog stops *waiting* and records the timeout; it cannot truly abort a Promise's in-flight side effects (JavaScript has no thread-kill). Make long handlers cooperative — check a deadline, or do the heavy lifting in a [workflow](/docs/workflows/overview) with its own step-level durability.
462
+
463
+ ## Backfill — `backfill`
464
+
465
+ When the process was down across one or more firing instants, what should happen on boot? Computed from the last `_voltro_schedule_runs` row for the schedule.
466
+
467
+ | Policy | Behaviour |
468
+ |---|---|
469
+ | `skip` *(default)* | Ignore missed firings. Resume from the next future occurrence. |
470
+ | `latest` | Fire **once** to catch up to the most recent missed slot; record the older missed slots as `missed` (not silently dropped). |
471
+ | `all` | Fire **every** missed slot in order. |
472
+
473
+ ```tsx
474
+ defineSchedule({
475
+ name: 'dailyDigest',
476
+ cron: '0 9 * * *',
477
+ timezone: 'Europe/Berlin',
478
+ backfill: 'latest', // missed Tuesday's 9am after a deploy? send one catch-up, log the rest as missed
479
+ handler: async ({ app }) => { /* … */ },
480
+ })
481
+ ```
482
+
483
+ **Choosing:**
484
+
485
+ - `skip` — the firing was time-sensitive and a late run is worse than no run ("send the 9am alert" — 9am has passed, don't send it at noon).
486
+ - `latest` — you want the side effect to have happened recently, but replaying every missed slot would spam ("the digest should be reasonably current").
487
+ - `all` — every slot represents real work that must not be lost (per-period billing rollups). **Dangerous for side-effecting jobs** — a week of downtime means a week of catch-up firings. Opt in deliberately.
488
+
489
+ Backfill runs **before** the live timer is armed, so a caught-up firing never races the first scheduled one. The catch-up walk is capped (1000 slots) so a schedule that hasn't run in months doesn't enumerate forever.
490
+
491
+ Missed slots recorded under `latest` show up in the dashboard with the `missed` status — visible evidence of the gap, not a silent hole.
492
+
493
+
494
+
495
+ ---
496
+
497
+ <!-- source: en/scheduling/deployment.md -->
498
+ ## Deployment
499
+
500
+ _Run the same schedule on PM2, a Kubernetes fleet, or an external scheduler — and generate the platform manifest with voltro schedule-manifest._
501
+
502
+ The point of the `*.cron.tsx` primitive is that **the job definition doesn't change when the topology does.** You pick a trigger + coordination per environment; the cron, timezone, and handler stay put. This page maps common hosting setups to the right configuration.
503
+
504
+ ## The topology matrix
505
+
506
+ | Hosting | `trigger` | `coordination` | Notes |
507
+ |---|---|---|---|
508
+ | Local dev (`voltro dev`) | `self` | `single` | Memory store default. |
509
+ | Single VPS / one PM2 process | `self` | `single` | No coordination needed — one process. |
510
+ | PM2 cluster / multiple containers | `self` | `advisoryLock` | Postgres arbitrates. No extra infra. |
511
+ | Kubernetes, N replicas | `self` | `advisoryLock` | The pragmatic default — pods race the Postgres claim. |
512
+ | Kubernetes, already running `@effect/cluster` | `self` | `cluster` | Schedules ride the existing sharding fabric. |
513
+ | "All cron must be k8s CronJobs" (policy) | `external` | — | The cluster fires; generate a `CronJob` manifest. |
514
+ | AWS (EventBridge owns cron) | `external` | — | Generate an EventBridge rule. |
515
+ | GCP (Cloud Scheduler) / Azure (Functions timer) | `external` | — | Generate the matching job. |
516
+
517
+ The only code that changes between any two rows is the `scheduling` block in `app.config.ts` (and occasionally a per-job `trigger:` override). The schedules themselves are untouched.
518
+
519
+ ## Self-hosted, in-process (the common case)
520
+
521
+ Nothing to deploy beyond your app. Set the coordination that matches your instance count:
522
+
523
+ ```ts
524
+ // apps/api/app.config.ts
525
+ scheduling: { coordination: 'advisoryLock' } // 2+ instances on Postgres
526
+ ```
527
+
528
+ `advisoryLock` is the default on Postgres, so multi-instance "just works" — see [coordination](/docs/scheduling/coordination). This is the right answer for most fleets: no orchestrator coupling, no separate scheduler to operate.
529
+
530
+ The production API server starts the same scheduler engine as `voltro dev`. If a schedule starts a workflow, `app.workflows.start(...)` is available in the handler; with `coordination: 'cluster'`, Voltro acquires the cluster-cron layer during boot so the clock does not wait for the first incoming request.
531
+
532
+ ## Delegating to an external scheduler
533
+
534
+ When the platform must own the clock (enterprise policy, serverless scale-to-zero), set `trigger: 'external'` and generate the manifest:
535
+
536
+ ```bash
537
+ voltro schedule-manifest --provider kubernetes --base-url https://api.internal.example.com
538
+ ```
539
+
540
+ | `--provider` | Emits |
541
+ |---|---|
542
+ | `kubernetes` | A `CronJob` per schedule that `curl`s the fire endpoint |
543
+ | `aws` | EventBridge Scheduler / rule config |
544
+ | `gcp` | A `gcloud scheduler jobs create http` command |
545
+ | `azure` | An Azure Functions timer-trigger outline |
546
+ | `generic` *(default)* | A README with the `curl` shape and the cron table |
547
+
548
+ Each generated job POSTs to:
549
+
550
+ ```
551
+ POST <base-url>/_voltro/schedule/<name>/fire?trigger=external
552
+ ```
553
+
554
+ Example Kubernetes output (abridged):
555
+
556
+ ```yaml
557
+ apiVersion: batch/v1
558
+ kind: CronJob
559
+ metadata:
560
+ name: dailydigest
561
+ spec:
562
+ schedule: "0 9 * * *"
563
+ jobTemplate:
564
+ spec:
565
+ template:
566
+ spec:
567
+ containers:
568
+ - name: trigger
569
+ image: curlimages/curl:8.11.0
570
+ command:
571
+ - curl
572
+ - -fsS
573
+ - -X
574
+ - POST
575
+ - "https://api.internal.example.com/_voltro/schedule/dailyDigest/fire?trigger=external"
576
+ restartPolicy: OnFailure
577
+ ```
578
+
579
+ > **Six-field crons are rejected here.** External schedulers are minute-granular — k8s `CronJob`, EventBridge, and Cloud Scheduler can't express seconds. A schedule with a six-field expression must stay on the `self` trigger. The generator errors rather than silently dropping the seconds field.
580
+
581
+ ## What to deploy alongside
582
+
583
+ - **`voltro migrate`** creates `_voltro_schedule_runs` (and `_voltro_schedule_claims` for `advisoryLock`) automatically — run it as part of your release, the same as your other tables. You never hand-write these migrations.
584
+ - **Protect the fire endpoint.** `/_voltro/schedule/<name>/fire` runs a handler — gate it (network policy, ingress rule, or a shared token) so only your scheduler can reach it.
585
+ - See [self-hosting](/docs/deployment/self-hosting) and [Voltro Cloud](/docs/deployment/voltro-cloud) for the broader deployment story.
586
+
587
+ ## Switching topologies later
588
+
589
+ Moving from a single box to a fleet is a config change, not a rewrite:
590
+
591
+ ```diff
592
+ scheduling: {
593
+ - coordination: 'single',
594
+ + coordination: 'advisoryLock',
595
+ }
596
+ ```
597
+
598
+ Moving cron ownership to Kubernetes:
599
+
600
+ ```diff
601
+ scheduling: {
602
+ - trigger: 'self',
603
+ + trigger: 'external',
604
+ }
605
+ ```
606
+ …then `voltro schedule-manifest --provider kubernetes --base-url …` and apply the output. The `*.cron.tsx` files don't change.
607
+
608
+
609
+
610
+ ---
611
+
612
+ <!-- source: en/scheduling/dashboard.md -->
613
+ ## Dashboard
614
+
615
+ _Inspecting schedules in the DevTools and Voltro Cloud dashboards — discovered jobs, firing history, status, and "Run now"._
616
+
617
+ Every discovered schedule shows up in the **Schedules** panel of both the local DevTools dashboard (`voltro dev`) and the Voltro Cloud dashboard. Same component, two transports: DevTools polls the app's inspect endpoints directly; Cloud streams firing history live via its reactive bridge.
618
+
619
+ ## What you see
620
+
621
+ **Per schedule:**
622
+
623
+ - Name, cron expression, timezone, and a live **next-firing countdown** ("in 4h 59m").
624
+ - Trigger badge — `self` or `external`.
625
+ - The effective **coordination** strategy for the app (`single` / `advisoryLock` / `cluster`), shown once at the top.
626
+ - `overlap`, `backfill`, and `max runtime` settings.
627
+ - A **Run now** button (capability-gated) — fires the handler immediately, bypassing the clock and coordination, recorded as a `manual` run.
628
+
629
+ **Per firing (expand a schedule's run timeline):**
630
+
631
+ - Status — `succeeded`, `failed`, `skipped`, `missed`, or `running`, each colour-toned.
632
+ - When it fired (relative + absolute), how long it took, and which replica ran it.
633
+ - The `coordinationOutcome` (`single` / `wonLock` / `cluster` / `external`) — *why* this instance ran it.
634
+ - For failures: the `errorTag` and message (e.g. `SmtpError: connection refused`, or `timeout` from the [watchdog](/docs/scheduling/overlap-and-backfill)).
635
+
636
+ ## Reading the statuses
637
+
638
+ | Status | Meaning |
639
+ |---|---|
640
+ | `succeeded` | Handler completed within `maxRuntimeMs`. |
641
+ | `failed` | Handler threw, or the watchdog tripped (`errorTag: timeout`). |
642
+ | `skipped` | An overlapping firing under `onOverlap: 'skip'`. |
643
+ | `missed` | A firing skipped during downtime, recorded by `backfill: 'latest'`. Visible evidence of a gap, not a silent hole. |
644
+ | `running` | In flight right now. |
645
+
646
+ `missed` and `skipped` rows matter: they're the system telling you a firing *didn't* run and why. A wall of `missed` after a deploy means your downtime crossed firing instants — expected with `backfill: 'skip'`, a signal to consider `latest` if those runs mattered.
647
+
648
+ ## "Run now"
649
+
650
+ The **Run now** button triggers the handler out-of-band. The run is tagged `trigger: 'manual'` and always executes regardless of `onOverlap` — operators expect the button to fire. Inside the handler you can branch on `ctx.trigger === 'manual'` to skip schedule-only guards (e.g. a weekday check) during a manual test.
651
+
652
+ It's gated on the same capability as workflow run-control, so read-only dashboard viewers see the history but can't trigger firings.
653
+
654
+ ## The endpoints behind it
655
+
656
+ The dashboard is a thin client over the app's inspect API — useful if you're scripting:
657
+
658
+ ```
659
+ GET /_voltro/inspect/schedules # discovered schedules + effective coordination
660
+ GET /_voltro/inspect/schedules/runs?name= # firing history for one schedule
661
+ POST /_voltro/inspect/schedules/:name/fire # "Run now"
662
+ ```
663
+
664
+ In the Cloud dashboard, firing history is **live** — the cloud API mirrors each app's inspect stream into a reactive cache, so new runs appear without a refresh. In local DevTools the panel polls every few seconds.
665
+
666
+ See [workflow debugging](/docs/workflows/debugging) for the analogous Workflows panel.