@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,79 @@
1
+ // Base catalog — the source of truth for this app's UI strings. Every key here
2
+ // MUST be mirrored in `de.ts` (and any other locale); `defineLocale<typeof en>()`
3
+ // enforces that parity at build time, so a missing translation fails typecheck
4
+ // rather than silently falling back.
5
+ //
6
+ // Messages use ICU syntax: `{name}` placeholders, `{n, plural, …}`, and rich
7
+ // tags like `<code>…</code>` (rendered by passing a matching function to
8
+ // `<T values={{ code: (c) => <code>{c}</code> }} />`).
9
+
10
+ import { defineCatalog } from '@voltro/i18n'
11
+
12
+ export default defineCatalog({
13
+ // Browser tab titles + description — locale-aware via each page's `meta({ locale })`.
14
+ 'meta.landing.title': 'Admin',
15
+ 'meta.landing.description': 'An auto-admin back-office that discovers your api’s entities and renders a live, permission-gated, undoable UI per entity.',
16
+ 'meta.login.title': 'Sign in',
17
+ 'meta.overview.title': 'Overview',
18
+ 'meta.entity.title': 'Entity',
19
+
20
+ // ---- Marketing header ((marketing)/layout.tsx) ----
21
+ 'nav.admin': 'Admin',
22
+ 'nav.signIn': 'Sign in',
23
+ 'lang.label': 'Language',
24
+
25
+ // ---- Public landing ((marketing)/index.tsx) ----
26
+ 'landing.heading': 'The back-office, done for you.',
27
+ 'landing.body': 'This admin reads your api’s <strong>capability map</strong> at runtime and renders a live, reactive <code>DataTable</code> + <code>AutoForm</code> for every entity it exposes — permission-gated with <code>useCan</code>, with a per-row provenance drawer and universal undo/redo. Point it at any Voltro api and it adapts; the page code under <code>src/pages/admin/</code> is yours to edit.',
28
+ 'landing.cta': 'Sign in to the admin →',
29
+
30
+ // ---- Public sign-in ((marketing)/login.tsx) ----
31
+ 'login.heading': 'Sign in',
32
+ 'login.body': 'No real auth here — this button sets a demo session cookie so you can see the admin gate let you through.',
33
+ 'login.cta': 'Continue as Demo Admin →',
34
+
35
+ // ---- Admin shell (admin/layout.tsx) ----
36
+ 'admin.undo': '↶ Undo',
37
+ 'admin.redo': '↷ Redo',
38
+ 'admin.nav.overview': 'Overview',
39
+ 'admin.nav.discovering': 'discovering…',
40
+ 'admin.nav.backToSite': '← Back to site',
41
+
42
+ // ---- Admin overview (admin/index.tsx) ----
43
+ 'overview.heading': 'Entities',
44
+ 'overview.intro': 'Discovered from <code>app</code>’s capability map. Each card is a live, permission-gated CRUD view — no per-entity code.',
45
+ 'overview.error': 'Couldn’t reach the api’s capability map. Is the api running with inspect enabled?',
46
+ 'overview.columns': '{count, plural, one {# column} other {# columns}}',
47
+ 'overview.reactiveSuffix': ' · reactive',
48
+ 'overview.readonly': 'read-only',
49
+ 'overview.empty': 'No user entities exposed by this api.',
50
+
51
+ // ---- Per-entity CRUD (admin/[entity].tsx) ----
52
+ 'entity.create': 'Create',
53
+ 'entity.addLabel': 'Add {table}',
54
+ 'entity.rows': 'Rows',
55
+ 'entity.noList': 'This entity exposes no list query.',
56
+ 'entity.empty': 'No {table} yet.',
57
+ 'entity.why': 'Why?',
58
+ 'entity.delete': 'Delete',
59
+ 'entity.unknown': 'No entity <code>{entity}</code> in this api’s capability map.',
60
+
61
+ // ---- Provenance drawer (admin/[entity].tsx) ----
62
+ 'prov.title': 'Provenance · <code>{table}</code> #{id}',
63
+ 'prov.close': 'close',
64
+ 'prov.loading': 'loading…',
65
+ 'prov.none': 'No provenance (is audit() on this table + inspect enabled?).',
66
+ 'prov.lastWrite': 'last write',
67
+ 'prov.byActor': 'by actor',
68
+ 'prov.attribution': 'attribution',
69
+ 'prov.via': ' (via {via})',
70
+ 'prov.derived': 'derived',
71
+ 'prov.yes': 'yes',
72
+
73
+ // ---- Scoped fallbacks (admin/error.tsx, not-found.tsx) ----
74
+ 'error.heading': 'Something went wrong',
75
+ 'error.retry': 'Try again',
76
+ 'notFound.heading': 'Not found',
77
+ 'notFound.body': 'That admin page doesn’t exist.',
78
+ 'notFound.back': '← Back to overview',
79
+ } as const)
@@ -0,0 +1,15 @@
1
+ // Catalog lookup OUTSIDE React — for a page's `meta({ locale })` locale-aware
2
+ // <title>. This app is cookie-i18n (no `/<locale>` URL prefix), so @voltro/web
3
+ // hands `meta` the active locale from the `voltro:lang` cookie. Inside React,
4
+ // use `<T>` / `useT()` via the framework's auto-wired <I18nProvider> instead.
5
+
6
+ import en from './en'
7
+ import de from './de'
8
+
9
+ // Both catalogs share `en`'s keys (defineLocale enforces the mirror), so a
10
+ // known-key lookup returns `string` — not `string | undefined` — which is what
11
+ // `PageMeta.title` requires under exactOptionalPropertyTypes.
12
+ export type Messages = Record<keyof typeof en, string>
13
+
14
+ export const getCatalog = (locale?: string): Messages =>
15
+ (locale === 'de' ? de : en) as Messages
@@ -0,0 +1,55 @@
1
+ // @vitest-environment jsdom
2
+ //
3
+ // `/` public landing — a static page with fixed content. Assert its render-mode
4
+ // + meta config and the hero copy / sign-in link.
5
+
6
+ import { afterEach, describe, expect, test } from 'vitest'
7
+ import { act, createElement, type ReactNode } from 'react'
8
+ import { createRoot, type Root } from 'react-dom/client'
9
+ import { I18nProvider } from '@voltro/i18n'
10
+ import enCatalog from '../../locales/en'
11
+
12
+ const { default: Landing, meta, renderMode } = await import('./index')
13
+
14
+ ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
15
+
16
+ let container: HTMLDivElement
17
+ let root: Root
18
+
19
+ // The page uses <T>, so it needs an <I18nProvider> (the framework auto-wires
20
+ // this in the running app). Wrap with the real English catalog.
21
+ const render = (node: ReactNode): void => {
22
+ container = document.createElement('div')
23
+ document.body.appendChild(container)
24
+ act(() => {
25
+ root = createRoot(container)
26
+ root.render(
27
+ createElement(I18nProvider, { locale: 'en', messages: enCatalog, defaultLocale: 'en', children: node }),
28
+ )
29
+ })
30
+ }
31
+
32
+ afterEach(() => {
33
+ if (root) act(() => root.unmount())
34
+ container?.remove()
35
+ document.body.innerHTML = ''
36
+ })
37
+
38
+ describe('landing — config', () => {
39
+ test('pre-renders as a static page with title + description meta', () => {
40
+ expect(renderMode).toBe('static')
41
+ expect(meta({ locale: 'en' }).title).toBe('Admin')
42
+ expect(meta({ locale: 'en' }).description).toContain('back-office')
43
+ })
44
+ })
45
+
46
+ describe('landing — render', () => {
47
+ test('renders the hero heading and a sign-in call to action', () => {
48
+ render(createElement(Landing))
49
+ expect(container.querySelector('h1')!.textContent).toContain('back-office')
50
+ const cta = Array.from(container.querySelectorAll('a')).find(
51
+ (a) => a.getAttribute('href') === '/login' && (a.textContent ?? '').includes('Sign in'),
52
+ )
53
+ expect(cta).toBeTruthy()
54
+ })
55
+ })
@@ -0,0 +1,32 @@
1
+ // Public landing — URL `/`. Pure static content; pre-renders to flat HTML.
2
+ import type { ReactNode } from 'react'
3
+ import type { PageMeta } from '@voltro/web'
4
+ import { T } from '@voltro/i18n'
5
+ import { getCatalog } from '../../locales'
6
+
7
+ export const renderMode = 'static' as const
8
+
9
+ export const meta = ({ locale }: { readonly locale: string }): PageMeta => ({
10
+ title: getCatalog(locale)['meta.landing.title'],
11
+ description: getCatalog(locale)['meta.landing.description'],
12
+ })
13
+
14
+ export default function Landing(): ReactNode {
15
+ return (
16
+ <section className="hero">
17
+ <h1><T id="landing.heading" /></h1>
18
+ <p className="muted">
19
+ <T
20
+ id="landing.body"
21
+ values={{
22
+ strong: (c: ReactNode) => <strong>{c}</strong>,
23
+ code: (c: ReactNode) => <code>{c}</code>,
24
+ }}
25
+ />
26
+ </p>
27
+ <p style={{ marginTop: '1.5rem' }}>
28
+ <a className="btn" href="/login"><T id="landing.cta" /></a>
29
+ </p>
30
+ </section>
31
+ )
32
+ }
@@ -0,0 +1,29 @@
1
+ // Layout for the SILENT route group `(marketing)/`. The parens mean the
2
+ // directory name never appears in a URL — the pages inside are `/` and
3
+ // `/login`. Gives the public pages a header distinct from the admin sidebar.
4
+ import type { ReactNode } from 'react'
5
+ import { T, useLocale, useT } from '@voltro/i18n'
6
+ import { LocaleSwitcher } from '@voltro/ui-shadcn'
7
+ import { APP_NAME } from '../../config'
8
+
9
+ const LOCALES = [
10
+ { code: 'en', label: 'English' },
11
+ { code: 'de', label: 'Deutsch' },
12
+ ]
13
+
14
+ export default function MarketingLayout({ children }: { readonly children: ReactNode }): ReactNode {
15
+ const locale = useLocale()
16
+ const langLabel = useT('lang.label')
17
+ return (
18
+ <div className="marketing">
19
+ <header>
20
+ <a className="brand" href="/">{APP_NAME}</a>
21
+ <span className="spacer" />
22
+ <a href="/admin"><T id="nav.admin" /></a>
23
+ <a href="/login"><T id="nav.signIn" /></a>
24
+ <LocaleSwitcher locales={LOCALES} current={locale} ariaLabel={langLabel} />
25
+ </header>
26
+ {children}
27
+ </div>
28
+ )
29
+ }
@@ -0,0 +1,73 @@
1
+ // @vitest-environment jsdom
2
+ //
3
+ // `/login` demo sign-in. Clicking "Continue" sets the demo session cookie and
4
+ // navigates to `?from=` (defaulting to /admin). We mock useNavigate from
5
+ // @voltro/web and drive the click.
6
+
7
+ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
8
+ import { act, createElement, type ReactNode } from 'react'
9
+ import { createRoot, type Root } from 'react-dom/client'
10
+
11
+ import { I18nProvider } from '@voltro/i18n'
12
+ import enCatalog from '../../locales/en'
13
+
14
+ const navigate = vi.fn<(to: string) => void>()
15
+ vi.mock('@voltro/web', () => ({ useNavigate: () => navigate }))
16
+
17
+ const { default: Login, meta, renderMode } = await import('./login')
18
+
19
+ ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
20
+
21
+ let container: HTMLDivElement
22
+ let root: Root
23
+
24
+ // The page uses <T>, so wrap the render in an <I18nProvider> (auto-wired in app).
25
+ const render = (node: ReactNode): void => {
26
+ container = document.createElement('div')
27
+ document.body.appendChild(container)
28
+ act(() => {
29
+ root = createRoot(container)
30
+ root.render(
31
+ createElement(I18nProvider, { locale: 'en', messages: enCatalog, defaultLocale: 'en', children: node }),
32
+ )
33
+ })
34
+ }
35
+
36
+ const clickContinue = (): void => {
37
+ const btn = container.querySelector('button')!
38
+ act(() => { btn.dispatchEvent(new MouseEvent('click', { bubbles: true })) })
39
+ }
40
+
41
+ beforeEach(() => {
42
+ navigate.mockReset()
43
+ document.cookie = 'demo_session=; path=/; max-age=0'
44
+ })
45
+
46
+ afterEach(() => {
47
+ if (root) act(() => root.unmount())
48
+ container?.remove()
49
+ document.body.innerHTML = ''
50
+ })
51
+
52
+ describe('login — config', () => {
53
+ test('is a static page titled Sign in', () => {
54
+ expect(renderMode).toBe('static')
55
+ expect(meta({ locale: 'en' }).title).toBe('Sign in')
56
+ })
57
+ })
58
+
59
+ describe('login — sign in', () => {
60
+ test('sets the session cookie and navigates to /admin by default', () => {
61
+ render(createElement(Login))
62
+ clickContinue()
63
+ expect(document.cookie).toContain('demo_session=ok')
64
+ expect(navigate).toHaveBeenCalledWith('/admin')
65
+ })
66
+
67
+ test('honours the ?from= redirect target', () => {
68
+ window.history.replaceState({}, '', '/login?from=/admin/todos')
69
+ render(createElement(Login))
70
+ clickContinue()
71
+ expect(navigate).toHaveBeenCalledWith('/admin/todos')
72
+ })
73
+ })
@@ -0,0 +1,35 @@
1
+ // Public sign-in — URL `/login`. DEMO login: it just writes the cookie the
2
+ // admin gate looks for. A real app POSTs credentials to an api that returns a
3
+ // signed, HttpOnly session cookie — see AGENTS.md › Auth.
4
+ import type { ReactNode } from 'react'
5
+ import type { PageMeta } from '@voltro/web'
6
+ import { useNavigate } from '@voltro/web'
7
+ import { T } from '@voltro/i18n'
8
+ import { getCatalog } from '../../locales'
9
+ import { SESSION_COOKIE } from '../../lib/auth'
10
+
11
+ export const renderMode = 'static' as const
12
+
13
+ export const meta = ({ locale }: { readonly locale: string }): PageMeta => ({
14
+ title: getCatalog(locale)['meta.login.title'],
15
+ })
16
+
17
+ export default function Login(): ReactNode {
18
+ const navigate = useNavigate()
19
+
20
+ const onSignIn = (): void => {
21
+ document.cookie = `${SESSION_COOKIE}=ok; path=/; max-age=86400; samesite=lax`
22
+ const from = new URLSearchParams(window.location.search).get('from') ?? '/admin'
23
+ navigate(from)
24
+ }
25
+
26
+ return (
27
+ <section className="hero">
28
+ <h1><T id="login.heading" /></h1>
29
+ <p className="muted"><T id="login.body" /></p>
30
+ <p style={{ marginTop: '1.5rem' }}>
31
+ <button type="button" className="btn" onClick={onSignIn}><T id="login.cta" /></button>
32
+ </p>
33
+ </section>
34
+ )
35
+ }
@@ -0,0 +1,121 @@
1
+ // Per-entity CRUD — URL `/admin/<entity>`. Everything here is DISCOVERED:
2
+ // useCapabilityManifest → deriveEntityAdmins gives this entity's list query +
3
+ // create/update/delete mutation tags + the columns. We bind the shipped
4
+ // schema-driven components to those tags:
5
+ // - <DataTable api="app" query={listTag}/> — live, reactive rows
6
+ // - <AutoForm api="app" mutation={createTag}/> — fields from the input schema
7
+ // Writes are gated by useCan on the conventional `<table>:create|delete` scopes;
8
+ // each row has a provenance ("why is this here?") drawer (08) + delete; the
9
+ // layout's undo bar reverts the last change (00/09). Edit this file freely — it's
10
+ // your template code, not a sealed runtime feature.
11
+ import { useState, type ReactNode } from 'react'
12
+ import type { PageMeta } from '@voltro/web'
13
+ import { useParams, AutoForm, DataTable } from '@voltro/web'
14
+ import {
15
+ useCapabilityManifest,
16
+ deriveEntityAdmins,
17
+ useCan,
18
+ useMutation,
19
+ useProvenance,
20
+ type EntityAdminSpec,
21
+ } from '@voltro/client'
22
+ import { T, useTFn } from '@voltro/i18n'
23
+ import { getCatalog } from '../../locales'
24
+
25
+ export const renderMode = 'ssr' as const
26
+
27
+ export const meta = ({ locale }: { readonly locale: string }): PageMeta => ({
28
+ title: getCatalog(locale)['meta.entity.title'],
29
+ })
30
+
31
+ type Row = Record<string, unknown>
32
+
33
+ // "Why is this value here?" — a one-shot provenance lookup (innovation/08).
34
+ // Its own component so useProvenance only runs while the drawer is open.
35
+ const ProvenanceDrawer = ({ table, id, onClose }: { readonly table: string; readonly id: string; readonly onClose: () => void }): ReactNode => {
36
+ const { data, loading, error } = useProvenance('app', table, id)
37
+ return (
38
+ <div className="drawer">
39
+ <h3>
40
+ <T id="prov.title" values={{ table, id, code: (c: ReactNode) => <code>{c}</code> }} />
41
+ {' '}
42
+ <button type="button" className="btn ghost" style={{ float: 'right' }} onClick={onClose}><T id="prov.close" /></button>
43
+ </h3>
44
+ {loading ? <p className="muted"><T id="prov.loading" /></p> : null}
45
+ {error ? <p className="muted"><T id="prov.none" /></p> : null}
46
+ {data ? (
47
+ <dl>
48
+ <dt><T id="prov.lastWrite" /></dt><dd>{data.lastWrite?.at ?? '—'}</dd>
49
+ <dt><T id="prov.byActor" /></dt><dd>{data.lastWrite?.actor?.id ?? '—'}{data.lastWrite?.actor?.via ? <T id="prov.via" values={{ via: data.lastWrite.actor.via }} /> : ''}</dd>
50
+ <dt><T id="prov.attribution" /></dt><dd>{data.attribution}</dd>
51
+ {data.derived ? <><dt><T id="prov.derived" /></dt><dd><T id="prov.yes" /></dd></> : null}
52
+ </dl>
53
+ ) : null}
54
+ </div>
55
+ )
56
+ }
57
+
58
+ // The bound CRUD surface for one entity. A child component so its hooks
59
+ // (useCan/useMutation) get the real spec, never undefined.
60
+ const EntityAdmin = ({ spec }: { readonly spec: EntityAdminSpec }): ReactNode => {
61
+ const [sel, setSel] = useState<string | null>(null)
62
+ const t = useTFn()
63
+ const canCreate = useCan(spec.createScope)
64
+ const canDelete = useCan(spec.deleteScope)
65
+ // Always-called; the tag is only used when deleteTag exists (gated below).
66
+ const del = useMutation<{ id: string }>('app', spec.deleteTag ?? '__voltro.noop')
67
+
68
+ const rowActions = (row: Row): ReactNode => {
69
+ const id = String(row['id'] ?? '')
70
+ return (
71
+ <span className="row-actions">
72
+ <button type="button" className="btn ghost" onClick={() => setSel(id)}><T id="entity.why" /></button>
73
+ {spec.deleteTag && canDelete && id ? (
74
+ <button type="button" className="btn danger" onClick={() => void del.mutate({ id })}><T id="entity.delete" /></button>
75
+ ) : null}
76
+ </span>
77
+ )
78
+ }
79
+
80
+ return (
81
+ <section>
82
+ <h1>{spec.table}</h1>
83
+
84
+ {spec.createTag && canCreate ? (
85
+ <div className="section">
86
+ <h2><T id="entity.create" /></h2>
87
+ <AutoForm api="app" mutation={spec.createTag} submitLabel={t('entity.addLabel', { table: spec.table })} />
88
+ </div>
89
+ ) : null}
90
+
91
+ <div className="section">
92
+ <h2><T id="entity.rows" /></h2>
93
+ {spec.listTag ? (
94
+ <DataTable api="app" query={spec.listTag} rowActions={rowActions} emptyText={t('entity.empty', { table: spec.table })} />
95
+ ) : (
96
+ <p className="muted"><T id="entity.noList" /></p>
97
+ )}
98
+ {sel ? <ProvenanceDrawer table={spec.table} id={sel} onClose={() => setSel(null)} /> : null}
99
+ </div>
100
+ </section>
101
+ )
102
+ }
103
+
104
+ export default function EntityPage(): ReactNode {
105
+ const { entity } = useParams<{ entity: string }>()
106
+ const { manifest, loading } = useCapabilityManifest('app')
107
+ const spec = manifest ? deriveEntityAdmins(manifest).find((e) => e.table === entity) : undefined
108
+
109
+ if (loading && !manifest) {
110
+ return <section><div className="skeleton" style={{ height: '8rem' }} /></section>
111
+ }
112
+ if (!spec) {
113
+ return (
114
+ <section>
115
+ <h1>{entity}</h1>
116
+ <p className="banner"><T id="entity.unknown" values={{ entity, code: (c: ReactNode) => <code>{c}</code> }} /></p>
117
+ </section>
118
+ )
119
+ }
120
+ return <EntityAdmin spec={spec} />
121
+ }
@@ -0,0 +1,119 @@
1
+ // @vitest-environment jsdom
2
+ //
3
+ // `/admin/<entity>` — per-entity CRUD. It resolves the entity spec from the
4
+ // capability map (useParams + useCapabilityManifest + deriveEntityAdmins) and
5
+ // renders one of three observable states: a loading skeleton, an "unknown
6
+ // entity" banner, or the bound CRUD surface (heading + AutoForm + DataTable).
7
+ // We mock @voltro/web (useParams + the AutoForm/DataTable components) and
8
+ // @voltro/client (the discovery + gating hooks).
9
+
10
+ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
11
+ import { act, createElement, type ReactNode } from 'react'
12
+ import { createRoot, type Root } from 'react-dom/client'
13
+ import { I18nProvider } from '@voltro/i18n'
14
+ import enCatalog from '../../locales/en'
15
+
16
+ const params = vi.fn<() => { entity?: string }>()
17
+
18
+ vi.mock('@voltro/web', () => ({
19
+ useParams: () => params(),
20
+ AutoForm: ({ mutation }: { mutation: string }) =>
21
+ createElement('div', { 'data-testid': 'auto-form' }, mutation),
22
+ DataTable: ({ query }: { query: string }) =>
23
+ createElement('div', { 'data-testid': 'data-table' }, query),
24
+ }))
25
+
26
+ interface ManifestState { manifest: unknown; loading: boolean }
27
+ const capabilityManifest = vi.fn<() => ManifestState>()
28
+ const entityAdmins = vi.fn<() => Array<Record<string, unknown>>>()
29
+ const can = vi.fn<() => boolean>()
30
+
31
+ vi.mock('@voltro/client', () => ({
32
+ useCapabilityManifest: () => capabilityManifest(),
33
+ deriveEntityAdmins: () => entityAdmins(),
34
+ useCan: () => can(),
35
+ useMutation: () => ({ mutate: vi.fn() }),
36
+ useProvenance: () => ({ data: undefined, loading: false, error: undefined }),
37
+ }))
38
+
39
+ const { default: EntityPage, meta, renderMode } = await import('./[entity]')
40
+
41
+ ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
42
+
43
+ let container: HTMLDivElement
44
+ let root: Root
45
+
46
+ // The page uses <T>/useTFn, so wrap renders in the framework's <I18nProvider>.
47
+ const render = (node: ReactNode): void => {
48
+ container = document.createElement('div')
49
+ document.body.appendChild(container)
50
+ act(() => {
51
+ root = createRoot(container)
52
+ root.render(
53
+ createElement(I18nProvider, { locale: 'en', messages: enCatalog, defaultLocale: 'en', children: node }),
54
+ )
55
+ })
56
+ }
57
+
58
+ const cell = (id: string): string | null =>
59
+ container.querySelector(`[data-testid="${id}"]`)?.textContent ?? null
60
+
61
+ beforeEach(() => {
62
+ params.mockReset()
63
+ capabilityManifest.mockReset()
64
+ entityAdmins.mockReset()
65
+ can.mockReset()
66
+ can.mockReturnValue(true)
67
+ })
68
+
69
+ afterEach(() => {
70
+ if (root) act(() => root.unmount())
71
+ container?.remove()
72
+ document.body.innerHTML = ''
73
+ })
74
+
75
+ describe('entity page — config', () => {
76
+ test('is an SSR page', () => {
77
+ expect(renderMode).toBe('ssr')
78
+ expect(meta({ locale: 'en' }).title).toBe('Entity')
79
+ })
80
+ })
81
+
82
+ describe('entity page — render states', () => {
83
+ test('shows a skeleton while the manifest is still loading', () => {
84
+ params.mockReturnValue({ entity: 'todos' })
85
+ capabilityManifest.mockReturnValue({ manifest: undefined, loading: true })
86
+ entityAdmins.mockReturnValue([])
87
+ render(createElement(EntityPage))
88
+ expect(container.querySelector('.skeleton')).toBeTruthy()
89
+ })
90
+
91
+ test('shows an unknown-entity banner when the entity is not in the map', () => {
92
+ params.mockReturnValue({ entity: 'ghosts' })
93
+ capabilityManifest.mockReturnValue({ manifest: {}, loading: false })
94
+ entityAdmins.mockReturnValue([{ table: 'todos' }])
95
+ render(createElement(EntityPage))
96
+ expect(container.querySelector('.banner')!.textContent).toContain('No entity')
97
+ expect(container.textContent).toContain('ghosts')
98
+ })
99
+
100
+ test('renders the bound CRUD surface (heading + AutoForm + DataTable) for a known entity', () => {
101
+ params.mockReturnValue({ entity: 'todos' })
102
+ capabilityManifest.mockReturnValue({ manifest: {}, loading: false })
103
+ entityAdmins.mockReturnValue([
104
+ {
105
+ table: 'todos',
106
+ columns: ['id', 'title'],
107
+ listTag: 'todos.list',
108
+ createTag: 'todos.create',
109
+ createScope: 'todos:create',
110
+ deleteScope: 'todos:delete',
111
+ deleteTag: 'todos.delete',
112
+ },
113
+ ])
114
+ render(createElement(EntityPage))
115
+ expect(container.querySelector('h1')!.textContent).toBe('todos')
116
+ expect(cell('auto-form')).toBe('todos.create')
117
+ expect(cell('data-table')).toBe('todos.list')
118
+ })
119
+ })
@@ -0,0 +1,20 @@
1
+ // Error boundary scoped to the /admin subtree. A render error (or a loader
2
+ // rejection that ISN'T a RedirectError / NotFoundError) bubbles to here instead
3
+ // of the router default. `reset` re-runs the failed segment.
4
+ import type { ReactNode } from 'react'
5
+ import type { ErrorBoundaryProps } from '@voltro/web'
6
+ import { T } from '@voltro/i18n'
7
+
8
+ export default function AdminError({ error, reset }: ErrorBoundaryProps): ReactNode {
9
+ return (
10
+ <div className="admin">
11
+ <main>
12
+ <h1><T id="error.heading" /></h1>
13
+ <p className="muted">{String(error)}</p>
14
+ <p style={{ marginTop: '1rem' }}>
15
+ <button type="button" className="btn" onClick={reset}><T id="error.retry" /></button>
16
+ </p>
17
+ </main>
18
+ </div>
19
+ )
20
+ }
@@ -0,0 +1,68 @@
1
+ // @vitest-environment jsdom
2
+ //
3
+ // Render smokes for the /admin subtree's scoped fallbacks: error.tsx (message +
4
+ // reset), loading.tsx (skeleton), not-found.tsx (message + back link). Pure
5
+ // presentational components, no framework hooks.
6
+
7
+ import { afterEach, describe, expect, test, vi } from 'vitest'
8
+ import { act, createElement, type ReactNode } from 'react'
9
+ import { createRoot, type Root } from 'react-dom/client'
10
+ import { I18nProvider } from '@voltro/i18n'
11
+ import enCatalog from '../../locales/en'
12
+
13
+ const { default: AdminError } = await import('./error')
14
+ const { default: AdminLoading } = await import('./loading')
15
+ const { default: AdminNotFound } = await import('./not-found')
16
+
17
+ ;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
18
+
19
+ let container: HTMLDivElement
20
+ let root: Root
21
+
22
+ // error.tsx / not-found.tsx use <T>, so wrap renders in the framework's
23
+ // <I18nProvider> (auto-wired in the running app).
24
+ const render = (node: ReactNode): void => {
25
+ container = document.createElement('div')
26
+ document.body.appendChild(container)
27
+ act(() => {
28
+ root = createRoot(container)
29
+ root.render(
30
+ createElement(I18nProvider, { locale: 'en', messages: enCatalog, defaultLocale: 'en', children: node }),
31
+ )
32
+ })
33
+ }
34
+
35
+ afterEach(() => {
36
+ if (root) act(() => root.unmount())
37
+ container?.remove()
38
+ document.body.innerHTML = ''
39
+ })
40
+
41
+ describe('admin error boundary', () => {
42
+ test('renders the error text and calls reset when Try again is clicked', () => {
43
+ const reset = vi.fn()
44
+ render(createElement(AdminError, { error: new Error('kaboom'), reset }))
45
+ expect(container.textContent).toContain('kaboom')
46
+ const btn = container.querySelector('button')!
47
+ act(() => { btn.dispatchEvent(new MouseEvent('click', { bubbles: true })) })
48
+ expect(reset).toHaveBeenCalledTimes(1)
49
+ })
50
+ })
51
+
52
+ describe('admin loading', () => {
53
+ test('renders skeleton placeholders', () => {
54
+ render(createElement(AdminLoading))
55
+ expect(container.querySelectorAll('.skeleton').length).toBeGreaterThan(0)
56
+ })
57
+ })
58
+
59
+ describe('admin not-found', () => {
60
+ test('renders the not-found message and a link back to the overview', () => {
61
+ render(createElement(AdminNotFound))
62
+ expect(container.textContent).toContain('Not found')
63
+ const back = Array.from(container.querySelectorAll('a')).find(
64
+ (a) => a.getAttribute('href') === '/admin',
65
+ )
66
+ expect(back).toBeTruthy()
67
+ })
68
+ })