@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,104 @@
1
+ // The contact form — an ISLAND inside an otherwise-static page. The page is
2
+ // `interactive: 'islands'`, so only THIS component hydrates; the surrounding
3
+ // marketing copy stays inert HTML.
4
+ //
5
+ // `hydrate: 'visible'` defers hydration until the form scrolls into view —
6
+ // it's below the fold, so there's no reason to spend JS on it at first paint.
7
+ //
8
+ // i18n note: islands hydrate INDEPENDENTLY of the layout's URL <I18nProvider>,
9
+ // so this component must NOT import @voltro/i18n (`<T>`/useT would crash / show
10
+ // raw keys at hydration). Instead the PAGE resolves every UI string per-locale
11
+ // (inside the provider) and passes them in as `labels` — the framework
12
+ // serialises those props into the island's HTML, so it hydrates already
13
+ // translated. Dynamic runtime data (the server's error detail) stays as-is.
14
+ //
15
+ // On submit it POSTs JSON to the serverless function (CONTACT_ENDPOINT). The
16
+ // function lives on a different origin, but the framework's node runner +
17
+ // the edge hosts both send CORS headers, so the cross-origin call works.
18
+
19
+ import type { FormEvent, ReactNode } from 'react'
20
+ import { useState } from 'react'
21
+ import { island } from '@voltro/web'
22
+ import { CONTACT_ENDPOINT } from '../config'
23
+
24
+ // Every user-facing string the island shows, resolved per-locale by the page.
25
+ export interface ContactFormLabels {
26
+ readonly name: string
27
+ readonly email: string
28
+ readonly message: string
29
+ readonly send: string
30
+ readonly sending: string
31
+ readonly sentNotice: string
32
+ readonly errorPrefix: string
33
+ readonly networkError: string
34
+ }
35
+
36
+ // Object-literal type (not an interface) so it satisfies the island()
37
+ // generic's `Record<string, unknown>` constraint.
38
+ type ContactFormProps = { readonly labels: ContactFormLabels }
39
+
40
+ type Status =
41
+ | { readonly kind: 'idle' }
42
+ | { readonly kind: 'sending' }
43
+ | { readonly kind: 'sent' }
44
+ | { readonly kind: 'error'; readonly message: string }
45
+
46
+ function ContactForm({ labels }: ContactFormProps): ReactNode {
47
+ const [name, setName] = useState('')
48
+ const [email, setEmail] = useState('')
49
+ const [message, setMessage] = useState('')
50
+ const [status, setStatus] = useState<Status>({ kind: 'idle' })
51
+
52
+ const onSubmit = async (e: FormEvent): Promise<void> => {
53
+ e.preventDefault()
54
+ setStatus({ kind: 'sending' })
55
+ try {
56
+ const res = await fetch(CONTACT_ENDPOINT, {
57
+ method: 'POST',
58
+ headers: { 'content-type': 'application/json' },
59
+ body: JSON.stringify({ name, email, message }),
60
+ })
61
+ const body = (await res.json().catch(() => ({}))) as { error?: string; detail?: string }
62
+ if (!res.ok) {
63
+ setStatus({ kind: 'error', message: body.detail ?? body.error ?? `HTTP ${res.status}` })
64
+ return
65
+ }
66
+ setStatus({ kind: 'sent' })
67
+ setName('')
68
+ setEmail('')
69
+ setMessage('')
70
+ } catch (err) {
71
+ setStatus({ kind: 'error', message: err instanceof Error ? err.message : labels.networkError })
72
+ }
73
+ }
74
+
75
+ if (status.kind === 'sent') {
76
+ return <p className="notice ok">{labels.sentNotice}</p>
77
+ }
78
+
79
+ const sending = status.kind === 'sending'
80
+ return (
81
+ <form onSubmit={onSubmit} className="contact-form">
82
+ <label htmlFor="name">{labels.name}</label>
83
+ <input id="name" required value={name} onChange={(e) => setName(e.target.value)} />
84
+
85
+ <label htmlFor="email">{labels.email}</label>
86
+ <input id="email" type="email" required value={email} onChange={(e) => setEmail(e.target.value)} />
87
+
88
+ <label htmlFor="message">{labels.message}</label>
89
+ <textarea id="message" required rows={5} value={message} onChange={(e) => setMessage(e.target.value)} />
90
+
91
+ {status.kind === 'error' && (
92
+ <p className="notice err">
93
+ {labels.errorPrefix} {status.message}
94
+ </p>
95
+ )}
96
+
97
+ <button type="submit" disabled={sending}>
98
+ {sending ? labels.sending : labels.send}
99
+ </button>
100
+ </form>
101
+ )
102
+ }
103
+
104
+ export default island(ContactForm, { name: 'ContactForm', hydrate: 'visible' })
@@ -0,0 +1,12 @@
1
+ // Where the contact form POSTs.
2
+ //
3
+ // Default = the local `voltro serverless dev` port (8910), so the form works
4
+ // the moment you run `pnpm fn:dev` in another terminal. For production, set
5
+ // this to the URL `voltro serverless deploy` printed for your function (the
6
+ // Cloudflare / Scaleway endpoint, or your self-hosted node sidecar).
7
+ //
8
+ // A plain constant — not `publicEnv` — so the template typechecks BEFORE
9
+ // `voltro dev` generates the env types. To switch to typed env: declare
10
+ // `VOLTRO_PUBLIC_CONTACT_FN_URL` in app.config.ts and, after one `voltro dev`,
11
+ // read `publicEnv.VOLTRO_PUBLIC_CONTACT_FN_URL` from '@voltro/env/public'.
12
+ export const CONTACT_ENDPOINT = 'http://localhost:8910'
@@ -0,0 +1,84 @@
1
+ /* Self-contained styles — plain CSS, no Tailwind / kit dependency. */
2
+
3
+ :root {
4
+ --bg: #ffffff;
5
+ --fg: #18181b;
6
+ --muted: #71717a;
7
+ --accent: #2563eb;
8
+ --ok: #16a34a;
9
+ --err: #dc2626;
10
+ --border: #e4e4e7;
11
+ --field: #f9fafb;
12
+ }
13
+
14
+ :root.dark {
15
+ --bg: #0a0a0a;
16
+ --fg: #fafafa;
17
+ --muted: #a1a1aa;
18
+ --accent: #60a5fa;
19
+ --ok: #4ade80;
20
+ --err: #f87171;
21
+ --border: #27272a;
22
+ --field: #18181b;
23
+ }
24
+
25
+ * { box-sizing: border-box; }
26
+ html, body { margin: 0; min-height: 100vh; }
27
+
28
+ body {
29
+ background: var(--bg);
30
+ color: var(--fg);
31
+ font: 16px/1.6 ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
32
+ }
33
+
34
+ main { max-width: 38rem; margin: 0 auto; padding: 4rem 1.25rem 6rem; }
35
+
36
+ /* Language switcher (Strategy B: each locale is its own URL). */
37
+ .langswitch {
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: flex-end;
41
+ gap: 0.5rem;
42
+ max-width: 38rem;
43
+ margin: 0 auto;
44
+ padding: 1rem 1.25rem 0;
45
+ font-size: 0.85rem;
46
+ }
47
+ .langswitch .label { color: var(--muted); }
48
+ .langswitch a { color: var(--muted); text-decoration: none; }
49
+ .langswitch a[aria-current='true'] { color: var(--accent); font-weight: 600; }
50
+
51
+ .hero h1 { font-size: 2.25rem; margin: 0 0 0.5rem; }
52
+ .lead { color: var(--muted); font-size: 1.05rem; margin: 0; }
53
+
54
+ .contact { margin-top: 3rem; padding-top: 2rem; border-top: 1px solid var(--border); }
55
+ .contact h2 { font-size: 1.25rem; margin: 0 0 1.25rem; }
56
+
57
+ .contact-form { display: flex; flex-direction: column; gap: 0.4rem; }
58
+ .contact-form label { font-size: 0.85rem; color: var(--muted); margin-top: 0.75rem; }
59
+ .contact-form input,
60
+ .contact-form textarea {
61
+ font: inherit;
62
+ color: var(--fg);
63
+ background: var(--field);
64
+ border: 1px solid var(--border);
65
+ border-radius: 8px;
66
+ padding: 0.6rem 0.75rem;
67
+ resize: vertical;
68
+ }
69
+ .contact-form button {
70
+ margin-top: 1.25rem;
71
+ padding: 0.7rem 1rem;
72
+ font: inherit;
73
+ font-weight: 600;
74
+ color: #fff;
75
+ background: var(--accent);
76
+ border: none;
77
+ border-radius: 8px;
78
+ cursor: pointer;
79
+ }
80
+ .contact-form button:disabled { opacity: 0.6; cursor: progress; }
81
+
82
+ .notice { margin: 1rem 0 0; padding: 0.6rem 0.75rem; border-radius: 8px; font-size: 0.95rem; }
83
+ .notice.ok { color: var(--ok); border: 1px solid var(--ok); }
84
+ .notice.err { color: var(--err); border: 1px solid var(--err); }
@@ -0,0 +1,55 @@
1
+ // The single source of truth for this app's locales + the URL-prefix
2
+ // helpers. `app.config.ts` lists the same codes for the build pipeline;
3
+ // the [locale] mirror page + the layout's <I18nProvider> read THIS file.
4
+ //
5
+ // Keep SUPPORTED_LOCALES / DEFAULT_LOCALE in sync with `locales` /
6
+ // `defaultLocale` in app.config.ts.
7
+
8
+ import { useLocation } from '@voltro/web'
9
+ import en from '../locales/en'
10
+ import de from '../locales/de'
11
+
12
+ export const SUPPORTED_LOCALES = ['en', 'de'] as const
13
+ export const DEFAULT_LOCALE = 'en'
14
+
15
+ export type Locale = (typeof SUPPORTED_LOCALES)[number]
16
+
17
+ // The catalogs, keyed by code. The layout picks one to feed the inner
18
+ // <I18nProvider>; `meta` (build-time, per locale) reads one via getCatalog.
19
+ export const CATALOGS = { en, de } as const
20
+
21
+ // Every catalog mirrors `en`'s keys (defineLocale enforces it), so a
22
+ // plain string-record over en's keys is the common type both satisfy —
23
+ // what `meta` reads at build time (`c['meta.home.title']` → string).
24
+ export type Messages = Record<keyof typeof en, string>
25
+
26
+ const isSupported = (v: string): v is Locale =>
27
+ (SUPPORTED_LOCALES as ReadonlyArray<string>).includes(v)
28
+
29
+ export const getCatalog = (locale: string): Messages =>
30
+ isSupported(locale) ? CATALOGS[locale] : CATALOGS[DEFAULT_LOCALE]
31
+
32
+ // `/de` → 'de'; `/` → 'en' (default lives at the bare path).
33
+ export const localeFromPathname = (pathname: string): Locale => {
34
+ const match = /^\/([a-z]{2})(?:\/|$)/.exec(pathname)
35
+ return match && isSupported(match[1]!) ? (match[1] as Locale) : DEFAULT_LOCALE
36
+ }
37
+
38
+ // Reactive: useLocation re-renders on client-side nav, so the layout's
39
+ // provider swaps catalogs when you move between / and /de without a full
40
+ // reload.
41
+ export const useUrlLocale = (): Locale => localeFromPathname(useLocation())
42
+
43
+ // '/' + 'de' → '/de'; the default locale stays bare ('/').
44
+ export const withLocalePrefix = (path: string, locale: string): string => {
45
+ if (locale === DEFAULT_LOCALE) return path
46
+ const p = path.startsWith('/') ? path : `/${path}`
47
+ return p === '/' ? `/${locale}` : `/${locale}${p}`
48
+ }
49
+
50
+ // '/de' → '/'; '/' → '/'. The inverse of the above, used to keep the
51
+ // active path when switching language.
52
+ export const stripLocalePrefix = (pathname: string): string => {
53
+ const match = /^\/([a-z]{2})(\/.*|$)/.exec(pathname)
54
+ return match && isSupported(match[1]!) ? match[2] || '/' : pathname
55
+ }
@@ -0,0 +1,26 @@
1
+ // German catalog. MUST mirror en.ts key-for-key — `defineLocale<typeof en>()`
2
+ // returns a curried function that fails `tsc --noEmit` on a missing or extra
3
+ // key, so translations can never silently drift out of sync.
4
+ import { defineLocale } from '@voltro/i18n'
5
+ import en from './en'
6
+
7
+ export default defineLocale<typeof en>()({
8
+ 'nav.language': 'Sprache',
9
+
10
+ 'hero.lead':
11
+ 'Diese Seite wird vorgerendert und von einem CDN ausgeliefert. Das Formular unten spricht mit einer Serverless-Funktion, die auf null skaliert — kein ständig laufender Server in Sicht.',
12
+ 'contact.heading': 'Schreib uns eine Nachricht',
13
+
14
+ // Kontaktformular-Island-Beschriftungen (als `labels` durchgereicht).
15
+ 'form.name': 'Name',
16
+ 'form.email': 'E-Mail',
17
+ 'form.message': 'Nachricht',
18
+ 'form.send': 'Nachricht senden',
19
+ 'form.sending': 'Wird gesendet…',
20
+ 'form.sent': 'Danke — deine Nachricht ist unterwegs. ✓',
21
+ 'form.errorPrefix': 'Senden fehlgeschlagen:',
22
+ 'form.networkError': 'Netzwerkfehler',
23
+
24
+ 'meta.home.title': '{{capProjectName}} — Kontakt',
25
+ 'meta.home.description': 'Statische Seite, Serverless-Kontaktformular.',
26
+ })
@@ -0,0 +1,29 @@
1
+ // Base catalog — the source of truth. Every other locale (de.ts) mirrors
2
+ // these keys EXACTLY; `defineLocale<typeof en>()` enforces that at `tsc`.
3
+ //
4
+ // Keep ICU placeholders identical across locales — react-intl validates
5
+ // them at render time. Note: the island's copy lives here too (form.*) —
6
+ // it is resolved on the page (inside the URL provider) and passed to the
7
+ // island as a `labels` prop, because islands hydrate WITHOUT the provider.
8
+ import { defineCatalog } from '@voltro/i18n'
9
+
10
+ export default defineCatalog({
11
+ 'nav.language': 'Language',
12
+
13
+ 'hero.lead':
14
+ 'This page is pre-rendered and ships from a CDN. The form below talks to a serverless function that scales to zero — no always-on server in sight.',
15
+ 'contact.heading': 'Send us a message',
16
+
17
+ // Contact-form island labels (passed down as `labels`, not read via <T>).
18
+ 'form.name': 'Name',
19
+ 'form.email': 'Email',
20
+ 'form.message': 'Message',
21
+ 'form.send': 'Send message',
22
+ 'form.sending': 'Sending…',
23
+ 'form.sent': 'Thanks — your message is on its way. ✓',
24
+ 'form.errorPrefix': 'Could not send:',
25
+ 'form.networkError': 'network error',
26
+
27
+ 'meta.home.title': '{{capProjectName}} — Get in touch',
28
+ 'meta.home.description': 'Static page, serverless contact form.',
29
+ } as const)
@@ -0,0 +1,14 @@
1
+ // Locale mirror of `/` → renders `/de`. Re-exports the bare page verbatim
2
+ // (the component reads its locale from the URL via the layout's provider,
3
+ // and resolves the island's `labels` per-locale there); `getStaticPaths`
4
+ // tells `voltro build` which locale-prefixed paths to emit. The default
5
+ // locale is NOT listed here — it lives at the bare `/`.
6
+ import { SUPPORTED_LOCALES, DEFAULT_LOCALE } from '../../lib/locale'
7
+
8
+ export { default } from '../index'
9
+ export { renderMode, interactive, meta } from '../index'
10
+
11
+ export const getStaticPaths = async (): Promise<Array<{ params: { locale: string } }>> =>
12
+ SUPPORTED_LOCALES
13
+ .filter((l) => l !== DEFAULT_LOCALE)
14
+ .map((locale) => ({ params: { locale } }))
@@ -0,0 +1,70 @@
1
+ // @vitest-environment jsdom
2
+ //
3
+ // `/` — the static, bilingual contact page. `renderMode='static'` +
4
+ // `interactive='islands'` (only the <ContactForm> island hydrates). We assert
5
+ // those config exports, the LOCALISED meta export (via getCatalog), and that
6
+ // the hero + contact sections render their translated copy. The page resolves
7
+ // its copy through @voltro/i18n, so we render it inside a real <I18nProvider>
8
+ // with the English catalog. The <ContactForm> island is mocked to a sentinel
9
+ // so this test covers the page shell in isolation; the island's own behavior
10
+ // (and its `labels` prop) is tested in ContactForm.island.test.tsx.
11
+
12
+ import { afterEach, describe, expect, test, vi } from 'vitest'
13
+ import { act, createElement, type ReactNode } from 'react'
14
+ import { createRoot, type Root } from 'react-dom/client'
15
+ import { I18nProvider } from '@voltro/i18n'
16
+ import en from '../locales/en'
17
+
18
+ vi.mock('../components/ContactForm.island', () => ({
19
+ default: () => createElement('div', { 'data-testid': 'contact-form' }, 'form'),
20
+ }))
21
+
22
+ const { default: Index, meta, renderMode, interactive } = await import('./index')
23
+
24
+ ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
25
+
26
+ let container: HTMLDivElement
27
+ let root: Root
28
+
29
+ const render = (node: ReactNode): void => {
30
+ container = document.createElement('div')
31
+ document.body.appendChild(container)
32
+ act(() => {
33
+ root = createRoot(container)
34
+ root.render(
35
+ createElement(I18nProvider, { locale: 'en', messages: en, defaultLocale: 'en', children: node }),
36
+ )
37
+ })
38
+ }
39
+
40
+ afterEach(() => {
41
+ if (root) act(() => root.unmount())
42
+ container?.remove()
43
+ document.body.innerHTML = ''
44
+ })
45
+
46
+ describe('index — config + meta exports', () => {
47
+ test('is a static, islands-hydrated page', () => {
48
+ expect(renderMode).toBe('static')
49
+ expect(interactive).toBe('islands')
50
+ })
51
+
52
+ test('declares a localised document title + description', () => {
53
+ expect(meta({ locale: 'en' }).title).toContain('Get in touch')
54
+ expect(meta({ locale: 'en' }).description).toBe('Static page, serverless contact form.')
55
+ expect(meta({ locale: 'de' }).title).toContain('Kontakt')
56
+ expect(meta({ locale: 'de' }).description).toBe('Statische Seite, Serverless-Kontaktformular.')
57
+ })
58
+ })
59
+
60
+ describe('index — render', () => {
61
+ test('renders the hero heading + copy and the contact section with the form island', () => {
62
+ render(createElement(Index))
63
+ expect(container.querySelector('.hero h1')).toBeTruthy()
64
+ expect(container.textContent).toContain('pre-rendered')
65
+ const contact = container.querySelector('.contact')
66
+ expect(contact).toBeTruthy()
67
+ expect(contact!.querySelector('h2')!.textContent).toBe('Send us a message')
68
+ expect(container.querySelector('[data-testid="contact-form"]')).toBeTruthy()
69
+ })
70
+ })
@@ -0,0 +1,63 @@
1
+ // Static marketing page with a serverless contact form.
2
+ //
3
+ // `renderMode = 'static'` → pre-rendered HTML, servable from a CDN.
4
+ // `interactive = 'islands'` → only the <ContactForm> island hydrates; the
5
+ // headline + copy ship as plain HTML with no React lifecycle.
6
+ //
7
+ // Bilingual (Strategy B): mounted at `/` (default locale) and mirrored at
8
+ // `/de` by src/pages/[locale]/index.tsx. `meta` is a FUNCTION of `{ locale }`
9
+ // so each pre-rendered variant gets a localised <title>/<description>.
10
+ //
11
+ // The island can't read <T>/useT (it hydrates WITHOUT the layout's URL
12
+ // provider). This page IS inside that provider, so it resolves the island's
13
+ // strings here (useTFn) and hands them down as a fully-translated `labels`
14
+ // prop — the framework serialises them into the island's HTML.
15
+
16
+ import type { ReactNode } from 'react'
17
+ import type { PageMeta } from '@voltro/web'
18
+ import { T, useTFn } from '@voltro/i18n'
19
+ import { getCatalog } from '../lib/locale'
20
+ import ContactForm from '../components/ContactForm.island'
21
+
22
+ export const renderMode = 'static' as const
23
+ export const interactive = 'islands' as const
24
+
25
+ export const meta = ({ locale }: { locale: string }): PageMeta => {
26
+ const c = getCatalog(locale)
27
+ return {
28
+ title: c['meta.home.title'],
29
+ description: c['meta.home.description'],
30
+ }
31
+ }
32
+
33
+ export default function Index(): ReactNode {
34
+ const t = useTFn()
35
+ return (
36
+ <main>
37
+ <section className="hero">
38
+ <h1>{{capProjectName}}</h1>
39
+ <p className="lead">
40
+ <T id="hero.lead" />
41
+ </p>
42
+ </section>
43
+
44
+ <section className="contact">
45
+ <h2>
46
+ <T id="contact.heading" />
47
+ </h2>
48
+ <ContactForm
49
+ labels={{
50
+ name: t('form.name'),
51
+ email: t('form.email'),
52
+ message: t('form.message'),
53
+ send: t('form.send'),
54
+ sending: t('form.sending'),
55
+ sentNotice: t('form.sent'),
56
+ errorPrefix: t('form.errorPrefix'),
57
+ networkError: t('form.networkError'),
58
+ }}
59
+ />
60
+ </section>
61
+ </main>
62
+ )
63
+ }
@@ -0,0 +1,65 @@
1
+ // Root layout. The framework's auto-wired <I18nProvider> is COOKIE-driven
2
+ // (Strategy A). For URL-prefix routing (Strategy B) we add an INNER provider
3
+ // that reads the locale FROM THE URL, so client-side navigation between / and
4
+ // /de swaps the catalog without a full reload. At build time it resolves to
5
+ // the same locale the SSG pipeline pre-rendered.
6
+ //
7
+ // NOTE: the <ContactForm> island hydrates INDEPENDENTLY of this provider, so
8
+ // it can't read <T>/useT. The page resolves its strings here (inside this
9
+ // provider) and passes them down as a `labels` prop — see src/pages/index.tsx.
10
+ //
11
+ // `<html>`/`<head>`/`<body>` are owned by the framework shell — don't render
12
+ // them here. Page <title> + meta come from each page's `meta` export.
13
+
14
+ import type { ReactNode } from 'react'
15
+ import { I18nProvider, T } from '@voltro/i18n'
16
+ import { useLocation } from '@voltro/web'
17
+ import {
18
+ CATALOGS,
19
+ DEFAULT_LOCALE,
20
+ SUPPORTED_LOCALES,
21
+ stripLocalePrefix,
22
+ useUrlLocale,
23
+ withLocalePrefix,
24
+ } from '../lib/locale'
25
+ import '../globals.css'
26
+
27
+ function LangSwitcher(): ReactNode {
28
+ const locale = useUrlLocale()
29
+ // The path WITHOUT its locale prefix — so the language switch keeps you
30
+ // on the same page (e.g. /de → /, then re-prefix per locale).
31
+ const barePath = stripLocalePrefix(useLocation())
32
+
33
+ // Plain <a> (not <Link>): these hrefs are COMPUTED per locale, and
34
+ // <Link to> wants a statically-known `VoltroUrl` literal. The Router's
35
+ // global click interceptor SPA-navigates internal <a> anyway, so the
36
+ // inner provider still swaps catalogs without a full reload.
37
+ return (
38
+ <nav className="langswitch">
39
+ <span className="label">
40
+ <T id="nav.language" />:
41
+ </span>
42
+ {SUPPORTED_LOCALES.map((code) => (
43
+ <a
44
+ key={code}
45
+ href={withLocalePrefix(barePath, code)}
46
+ aria-current={code === locale ? 'true' : undefined}
47
+ >
48
+ {code.toUpperCase()}
49
+ </a>
50
+ ))}
51
+ </nav>
52
+ )
53
+ }
54
+
55
+ export default function Layout({ children }: { readonly children: ReactNode }): ReactNode {
56
+ const locale = useUrlLocale()
57
+ return (
58
+ <I18nProvider locale={locale} messages={CATALOGS[locale]} defaultLocale={DEFAULT_LOCALE}>
59
+ <div className="shell">
60
+ <LangSwitcher />
61
+ {children}
62
+ </div>
63
+ </I18nProvider>
64
+ )
65
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "id": "frontend-contact",
3
+ "kind": "web",
4
+ "summary": "Static site + a serverless email contact form — the headline 'static frontend, serverless backend' combo. A static page hosts an island form that POSTs to functions/sendMessage.serverless.ts, which sends mail via Resend over HttpClient. The function deploys SEPARATELY with `voltro serverless` (scales to zero) while the page goes to any CDN.",
5
+ "tags": ["web", "frontend", "static", "serverless", "contact", "email", "form", "islands", "resend"]
6
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "jsx": "react-jsx",
5
+ "lib": ["ES2024", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "moduleResolution": "Bundler"
8
+ },
9
+ "include": ["src/**/*", "functions/**/*"],
10
+ "exclude": ["node_modules", "dist", ".framework"]
11
+ }
@@ -0,0 +1,54 @@
1
+ # {{projectName}} / {{appName}}
2
+
3
+ Voltro web app scaffold (template: **frontend-dashboard**) — an
4
+ authenticated dashboard shell with a public marketing area.
5
+
6
+ ## Boot
7
+
8
+ ```bash
9
+ pnpm install
10
+ pnpm --filter @{{projectName}}/{{appName}} dev
11
+ # → http://localhost:{{port}}
12
+ ```
13
+
14
+ Open `/dashboard` while signed out → you're redirected to `/login`. Click
15
+ **Continue as Demo User** → the gate lets you in.
16
+
17
+ ## What this shows
18
+
19
+ - **Route groups** — `src/pages/(marketing)/` is a *silent* route group:
20
+ the parens organise files + scope a layout WITHOUT adding a URL segment,
21
+ so its pages are `/` and `/login`. The `/dashboard` area is a real dir
22
+ (real prefix) by contrast.
23
+ - **A nested layout loader as an auth gate** — `dashboard/layout.tsx`
24
+ exports a `loader` that runs once for every page beneath it, server-side,
25
+ before render. It checks the session and `throw`s `RedirectError('/login')`
26
+ when there's none. On SSR that's a `303`; on client navigation it's a
27
+ `replace` redirect.
28
+ - **`renderMode: 'ssr'`** on the dashboard pages — required so the gate runs
29
+ per request. A `static` page can't redirect per-visitor, and the build
30
+ rejects a `RedirectError` there.
31
+ - **Scoped special files** — `dashboard/error.tsx` (error boundary),
32
+ `dashboard/loading.tsx` (opt-in skeleton), `dashboard/not-found.tsx`
33
+ (scoped 404). Each applies to every page under `/dashboard`.
34
+
35
+ ## The demo gate
36
+
37
+ `src/lib/auth.ts` is a cookie-only stand-in so the template boots with **no
38
+ backend**: `login` writes a `demo_session` cookie, the dashboard loader
39
+ reads it (from the request `Cookie` header on SSR, or `document.cookie` on
40
+ client nav). Swap `isSignedIn` for a real check — verify a signed session,
41
+ or call an api — when you wire auth for real. See AGENTS.md › *Auth*.
42
+
43
+ ## Wire a backend
44
+
45
+ Add an api and an `apis:` entry in `app.config.ts`, then read live data in
46
+ the dashboard pages with `useSubscription`. The gate stays the same — only
47
+ its session check moves from a demo cookie to your real session.
48
+
49
+ ## Build + serve
50
+
51
+ ```bash
52
+ voltro build . # static marketing pages + an SSR bundle for /dashboard
53
+ voltro start . # serve: / + /login are static; /dashboard renders per request
54
+ ```
@@ -0,0 +1,39 @@
1
+ // Web app for the {{projectName}} project — an authenticated DASHBOARD shell.
2
+ //
3
+ // Two areas, two shapes:
4
+ // - Public pages live in a SILENT route group `src/pages/(marketing)/`
5
+ // (the parens organise files + scope a layout WITHOUT adding a URL
6
+ // segment — `/` and `/login`, not `/(marketing)/...`).
7
+ // - The authed area lives under `src/pages/dashboard/` (a real `/dashboard`
8
+ // prefix). Its `layout.tsx` exports a `loader` that gates the session and
9
+ // throws RedirectError to `/login` when there's none. Those pages are
10
+ // `renderMode: 'ssr'` so the gate runs per request (a `static` page can't
11
+ // redirect per-visitor — the build would reject it).
12
+ //
13
+ // No api wired — the auth gate reads a demo cookie, so this is a pure
14
+ // frontend you can `voltro build` + `voltro start` as-is. Swap the cookie
15
+ // check for a real session (add an api + `apis:`) when you have one.
16
+ import { defineEnv, envVar } from '@voltro/env'
17
+
18
+ // Public, build-time env (baked into the bundle; read via `publicEnv`).
19
+ // Never a secret here. A secret belongs on the api, not the browser bundle.
20
+ export const env = defineEnv({
21
+ VOLTRO_PUBLIC_APP_NAME: envVar.string({ access: 'public', default: '{{capProjectName}}' }),
22
+ })
23
+
24
+ export default {
25
+ type: 'web' as const,
26
+ name: '{{capProjectName}}{{capAppName}}',
27
+ port: {{port}},
28
+ // Follow the visitor's OS theme, set before first paint (no flash).
29
+ theme: 'system' as const,
30
+ env,
31
+ // Bilingual by default. The framework auto-wires a cookie-driven
32
+ // <I18nProvider> from these codes and loads the matching
33
+ // `src/locales/<code>.ts` catalog — so `<T id="…" />` / `useT()` /
34
+ // `useLocale()` work app-wide with no manual setup. The <LocaleSwitcher>
35
+ // in the layout writes the `voltro:lang` cookie + reloads. Grow the app to
36
+ // more languages by adding a code here + a `src/locales/<code>.ts`.
37
+ locales: ['en', 'de'] as const,
38
+ defaultLocale: 'en' as const,
39
+ }