@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.
- package/CHANGELOG.md +52 -0
- package/LICENSE +57 -0
- package/README.md +26 -0
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +20236 -0
- package/bin/voltro.mjs +43 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +9 -0
- package/dist/commands-CfPH2Wf4.js +18061 -0
- package/dist/frameworkInspectState-CX2250XB.js +86 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +5 -0
- package/dist/inspectState.d.ts +116 -0
- package/dist/inspectState.js +2 -0
- package/dist/startup.d.ts +26 -0
- package/dist/startup.js +2 -0
- package/dist/startupRunner-CRhuUl91.js +71 -0
- package/package.json +88 -0
- package/templates/AGENTS.core.md +258 -0
- package/templates/AGENTS.md +351 -0
- package/templates/agent-docs/_index.md +93 -0
- package/templates/agent-docs/_manifest.json +655 -0
- package/templates/agent-docs/ai.md +1845 -0
- package/templates/agent-docs/authentication.md +1788 -0
- package/templates/agent-docs/caching.md +624 -0
- package/templates/agent-docs/cli.md +1650 -0
- package/templates/agent-docs/configuration.md +295 -0
- package/templates/agent-docs/data.md +2172 -0
- package/templates/agent-docs/database/advancedqueries.md +1583 -0
- package/templates/agent-docs/database/columntypes.md +1200 -0
- package/templates/agent-docs/database/hosting.md +881 -0
- package/templates/agent-docs/database/migrations.md +2938 -0
- package/templates/agent-docs/database/misc.md +270 -0
- package/templates/agent-docs/database/overview.md +108 -0
- package/templates/agent-docs/database/querying.md +1622 -0
- package/templates/agent-docs/database/scaling.md +331 -0
- package/templates/agent-docs/database/schema.md +1458 -0
- package/templates/agent-docs/database/seedsdialects.md +1235 -0
- package/templates/agent-docs/database/transactions.md +285 -0
- package/templates/agent-docs/deployment.md +999 -0
- package/templates/agent-docs/internationalization.md +359 -0
- package/templates/agent-docs/introduction.md +438 -0
- package/templates/agent-docs/multi-tenancy.md +610 -0
- package/templates/agent-docs/observability.md +350 -0
- package/templates/agent-docs/plugins.md +1273 -0
- package/templates/agent-docs/reference.md +978 -0
- package/templates/agent-docs/routing.md +1553 -0
- package/templates/agent-docs/scheduling.md +666 -0
- package/templates/agent-docs/schema-driven-ui.md +607 -0
- package/templates/agent-docs/security.md +42 -0
- package/templates/agent-docs/templates/apibackends.md +3214 -0
- package/templates/agent-docs/templates/appshells.md +2062 -0
- package/templates/agent-docs/templates/custom.md +128 -0
- package/templates/agent-docs/templates/overview.md +122 -0
- package/templates/agent-docs/templates/serverless.md +315 -0
- package/templates/agent-docs/testing.md +376 -0
- package/templates/agent-docs/workflows.md +1351 -0
- package/templates/apps/api-ai/README.md +105 -0
- package/templates/apps/api-ai/actions/summarize.action.server.tsx +32 -0
- package/templates/apps/api-ai/actions/summarize.action.ts +33 -0
- package/templates/apps/api-ai/agents/support.agent.server.tsx +31 -0
- package/templates/apps/api-ai/agents/support.agent.tsx +27 -0
- package/templates/apps/api-ai/app.config.ts +57 -0
- package/templates/apps/api-ai/database/schema.ts +67 -0
- package/templates/apps/api-ai/package.json +27 -0
- package/templates/apps/api-ai/seeds/docs.seed.ts +67 -0
- package/templates/apps/api-ai/template.json +6 -0
- package/templates/apps/api-ai/tests/summarize.test.ts +50 -0
- package/templates/apps/api-ai/tools/searchDocs.tool.tsx +60 -0
- package/templates/apps/api-ai/tsconfig.json +5 -0
- package/templates/apps/api-auth/.env +17 -0
- package/templates/apps/api-auth/README.md +109 -0
- package/templates/apps/api-auth/actions/me.action.server.ts +20 -0
- package/templates/apps/api-auth/actions/me.action.ts +19 -0
- package/templates/apps/api-auth/app.config.ts +59 -0
- package/templates/apps/api-auth/database/schema.ts +26 -0
- package/templates/apps/api-auth/package.json +28 -0
- package/templates/apps/api-auth/template.json +6 -0
- package/templates/apps/api-auth/tests/me.test.ts +37 -0
- package/templates/apps/api-auth/tsconfig.json +5 -0
- package/templates/apps/api-backend/README.md +25 -0
- package/templates/apps/api-backend/app.config.ts +39 -0
- package/templates/apps/api-backend/database/schema.ts +57 -0
- package/templates/apps/api-backend/mutations/notes.create.mutation.server.ts +19 -0
- package/templates/apps/api-backend/mutations/notes.create.mutation.ts +37 -0
- package/templates/apps/api-backend/package.json +28 -0
- package/templates/apps/api-backend/queries/notes.query.server.ts +14 -0
- package/templates/apps/api-backend/queries/notes.query.ts +20 -0
- package/templates/apps/api-backend/template.json +6 -0
- package/templates/apps/api-backend/tests/notes.create.test.ts +50 -0
- package/templates/apps/api-backend/tsconfig.json +5 -0
- package/templates/apps/api-backend-deactivation/README.md +49 -0
- package/templates/apps/api-backend-deactivation/actions/users.get.action.server.ts +17 -0
- package/templates/apps/api-backend-deactivation/actions/users.get.action.ts +18 -0
- package/templates/apps/api-backend-deactivation/app.config.ts +20 -0
- package/templates/apps/api-backend-deactivation/database/schema.ts +41 -0
- package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.server.ts +6 -0
- package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.ts +16 -0
- package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.server.ts +10 -0
- package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.ts +19 -0
- package/templates/apps/api-backend-deactivation/package.json +28 -0
- package/templates/apps/api-backend-deactivation/template.json +6 -0
- package/templates/apps/api-backend-deactivation/tests/users.deactivate.test.ts +47 -0
- package/templates/apps/api-backend-deactivation/tsconfig.json +5 -0
- package/templates/apps/api-backend-mail/README.md +39 -0
- package/templates/apps/api-backend-mail/actions/sendWelcome.action.server.ts +15 -0
- package/templates/apps/api-backend-mail/actions/sendWelcome.action.ts +21 -0
- package/templates/apps/api-backend-mail/app.config.ts +34 -0
- package/templates/apps/api-backend-mail/database/schema.ts +57 -0
- package/templates/apps/api-backend-mail/emails/welcome.email.tsx +106 -0
- package/templates/apps/api-backend-mail/mutations/notes.create.mutation.server.ts +19 -0
- package/templates/apps/api-backend-mail/mutations/notes.create.mutation.ts +37 -0
- package/templates/apps/api-backend-mail/package.json +30 -0
- package/templates/apps/api-backend-mail/queries/notes.query.server.ts +14 -0
- package/templates/apps/api-backend-mail/queries/notes.query.ts +20 -0
- package/templates/apps/api-backend-mail/template.json +6 -0
- package/templates/apps/api-backend-mail/tests/notes.create.test.ts +50 -0
- package/templates/apps/api-backend-mail/tsconfig.json +5 -0
- package/templates/apps/api-backend-mariadb/.env.example +35 -0
- package/templates/apps/api-backend-mariadb/README.md +31 -0
- package/templates/apps/api-backend-mariadb/app.config.ts +68 -0
- package/templates/apps/api-backend-mariadb/database/schema.ts +57 -0
- package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.server.ts +19 -0
- package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.ts +37 -0
- package/templates/apps/api-backend-mariadb/package.json +30 -0
- package/templates/apps/api-backend-mariadb/queries/notes.query.server.ts +14 -0
- package/templates/apps/api-backend-mariadb/queries/notes.query.ts +20 -0
- package/templates/apps/api-backend-mariadb/template.json +6 -0
- package/templates/apps/api-backend-mariadb/tests/notes.create.test.ts +50 -0
- package/templates/apps/api-backend-mariadb/tsconfig.json +5 -0
- package/templates/apps/api-backend-storage/README.md +86 -0
- package/templates/apps/api-backend-storage/actions/uploadAvatar.action.server.ts +21 -0
- package/templates/apps/api-backend-storage/actions/uploadAvatar.action.ts +21 -0
- package/templates/apps/api-backend-storage/actions/uploadDocument.action.server.ts +21 -0
- package/templates/apps/api-backend-storage/actions/uploadDocument.action.ts +20 -0
- package/templates/apps/api-backend-storage/app.config.ts +38 -0
- package/templates/apps/api-backend-storage/database/schema.ts +57 -0
- package/templates/apps/api-backend-storage/mutations/notes.create.mutation.server.ts +19 -0
- package/templates/apps/api-backend-storage/mutations/notes.create.mutation.ts +37 -0
- package/templates/apps/api-backend-storage/package.json +27 -0
- package/templates/apps/api-backend-storage/queries/notes.query.server.ts +14 -0
- package/templates/apps/api-backend-storage/queries/notes.query.ts +20 -0
- package/templates/apps/api-backend-storage/template.json +6 -0
- package/templates/apps/api-backend-storage/tests/notes.create.test.ts +50 -0
- package/templates/apps/api-backend-storage/tsconfig.json +5 -0
- package/templates/apps/api-data-advanced/.env +17 -0
- package/templates/apps/api-data-advanced/README.md +88 -0
- package/templates/apps/api-data-advanced/app.config.ts +56 -0
- package/templates/apps/api-data-advanced/database/actors.entity.ts +14 -0
- package/templates/apps/api-data-advanced/database/authors.entity.ts +30 -0
- package/templates/apps/api-data-advanced/database/authors.relations.ts +15 -0
- package/templates/apps/api-data-advanced/database/books.entity.ts +59 -0
- package/templates/apps/api-data-advanced/database/books.relations.ts +10 -0
- package/templates/apps/api-data-advanced/database/index.ts +23 -0
- package/templates/apps/api-data-advanced/database/tenants.entity.ts +11 -0
- package/templates/apps/api-data-advanced/package.json +28 -0
- package/templates/apps/api-data-advanced/queries/authors.withBooks.query.server.ts +18 -0
- package/templates/apps/api-data-advanced/queries/authors.withBooks.query.ts +40 -0
- package/templates/apps/api-data-advanced/queries/books.search.query.server.ts +17 -0
- package/templates/apps/api-data-advanced/queries/books.search.query.ts +39 -0
- package/templates/apps/api-data-advanced/seeds/catalog.seed.ts +101 -0
- package/templates/apps/api-data-advanced/template.json +6 -0
- package/templates/apps/api-data-advanced/tests/queries.test.ts +124 -0
- package/templates/apps/api-data-advanced/tsconfig.json +5 -0
- package/templates/apps/api-durable/README.md +81 -0
- package/templates/apps/api-durable/aggregates/orderStats.aggregate.ts +49 -0
- package/templates/apps/api-durable/app.config.ts +41 -0
- package/templates/apps/api-durable/database/schema.ts +65 -0
- package/templates/apps/api-durable/mutations/orders.approve.mutation.server.ts +43 -0
- package/templates/apps/api-durable/mutations/orders.approve.mutation.ts +23 -0
- package/templates/apps/api-durable/mutations/orders.place.mutation.server.ts +45 -0
- package/templates/apps/api-durable/mutations/orders.place.mutation.ts +40 -0
- package/templates/apps/api-durable/package.json +28 -0
- package/templates/apps/api-durable/schedules/nightlyReport.cron.tsx +37 -0
- package/templates/apps/api-durable/startup/warm.startup.tsx +33 -0
- package/templates/apps/api-durable/subscribers/orderChanges.subscribe.ts +27 -0
- package/templates/apps/api-durable/template.json +6 -0
- package/templates/apps/api-durable/tests/orders.place.test.ts +91 -0
- package/templates/apps/api-durable/triggers/order.placed.trigger.tsx +21 -0
- package/templates/apps/api-durable/tsconfig.json +5 -0
- package/templates/apps/api-durable/workflows/order.fulfill.workflow.server.tsx +103 -0
- package/templates/apps/api-durable/workflows/order.fulfill.workflow.tsx +25 -0
- package/templates/apps/api-feature-flags/README.md +63 -0
- package/templates/apps/api-feature-flags/actions/notes.export.action.server.ts +11 -0
- package/templates/apps/api-feature-flags/actions/notes.export.action.ts +18 -0
- package/templates/apps/api-feature-flags/app.config.ts +41 -0
- package/templates/apps/api-feature-flags/database/schema.ts +37 -0
- package/templates/apps/api-feature-flags/mutations/notes.create.mutation.server.ts +29 -0
- package/templates/apps/api-feature-flags/mutations/notes.create.mutation.ts +28 -0
- package/templates/apps/api-feature-flags/package.json +29 -0
- package/templates/apps/api-feature-flags/template.json +6 -0
- package/templates/apps/api-feature-flags/tests/notes.create.test.ts +67 -0
- package/templates/apps/api-feature-flags/tsconfig.json +5 -0
- package/templates/apps/api-governance/.env +4 -0
- package/templates/apps/api-governance/README.md +61 -0
- package/templates/apps/api-governance/actions/profiles.get.action.server.ts +17 -0
- package/templates/apps/api-governance/actions/profiles.get.action.ts +17 -0
- package/templates/apps/api-governance/app.config.ts +35 -0
- package/templates/apps/api-governance/database/schema.ts +41 -0
- package/templates/apps/api-governance/mutations/profiles.create.mutation.server.ts +17 -0
- package/templates/apps/api-governance/mutations/profiles.create.mutation.ts +21 -0
- package/templates/apps/api-governance/package.json +29 -0
- package/templates/apps/api-governance/template.json +6 -0
- package/templates/apps/api-governance/tests/profiles.create.test.ts +59 -0
- package/templates/apps/api-governance/tsconfig.json +5 -0
- package/templates/apps/api-kv/README.md +100 -0
- package/templates/apps/api-kv/actions/sync.pull.action.server.ts +75 -0
- package/templates/apps/api-kv/actions/sync.pull.action.ts +25 -0
- package/templates/apps/api-kv/actions/sync.reset.action.server.ts +25 -0
- package/templates/apps/api-kv/actions/sync.reset.action.ts +22 -0
- package/templates/apps/api-kv/actions/sync.status.action.server.ts +28 -0
- package/templates/apps/api-kv/actions/sync.status.action.ts +17 -0
- package/templates/apps/api-kv/app.config.ts +48 -0
- package/templates/apps/api-kv/database/schema.ts +62 -0
- package/templates/apps/api-kv/package.json +28 -0
- package/templates/apps/api-kv/queries/events.list.query.server.ts +14 -0
- package/templates/apps/api-kv/queries/events.list.query.ts +23 -0
- package/templates/apps/api-kv/template.json +6 -0
- package/templates/apps/api-kv/tests/sync.test.ts +103 -0
- package/templates/apps/api-kv/tsconfig.json +5 -0
- package/templates/apps/api-moderation/README.md +46 -0
- package/templates/apps/api-moderation/app.config.ts +33 -0
- package/templates/apps/api-moderation/database/schema.ts +45 -0
- package/templates/apps/api-moderation/mutations/comments.create.mutation.server.ts +8 -0
- package/templates/apps/api-moderation/mutations/comments.create.mutation.ts +20 -0
- package/templates/apps/api-moderation/mutations/posts.create.mutation.server.ts +9 -0
- package/templates/apps/api-moderation/mutations/posts.create.mutation.ts +25 -0
- package/templates/apps/api-moderation/package.json +29 -0
- package/templates/apps/api-moderation/template.json +6 -0
- package/templates/apps/api-moderation/tests/posts.create.test.ts +79 -0
- package/templates/apps/api-moderation/tsconfig.json +5 -0
- package/templates/apps/api-observability/README.md +106 -0
- package/templates/apps/api-observability/app.config.ts +32 -0
- package/templates/apps/api-observability/database/schema.ts +38 -0
- package/templates/apps/api-observability/mutations/notes.create.mutation.server.ts +33 -0
- package/templates/apps/api-observability/mutations/notes.create.mutation.ts +15 -0
- package/templates/apps/api-observability/package.json +29 -0
- package/templates/apps/api-observability/queries/notes.list.query.server.ts +7 -0
- package/templates/apps/api-observability/queries/notes.list.query.ts +14 -0
- package/templates/apps/api-observability/template.json +6 -0
- package/templates/apps/api-observability/tests/notes.create.test.ts +36 -0
- package/templates/apps/api-observability/tsconfig.json +5 -0
- package/templates/apps/api-ratelimit/README.md +49 -0
- package/templates/apps/api-ratelimit/app.config.ts +42 -0
- package/templates/apps/api-ratelimit/database/schema.ts +37 -0
- package/templates/apps/api-ratelimit/mutations/notes.create.mutation.server.ts +16 -0
- package/templates/apps/api-ratelimit/mutations/notes.create.mutation.ts +34 -0
- package/templates/apps/api-ratelimit/package.json +29 -0
- package/templates/apps/api-ratelimit/template.json +6 -0
- package/templates/apps/api-ratelimit/tests/notes.create.test.ts +69 -0
- package/templates/apps/api-ratelimit/tsconfig.json +5 -0
- package/templates/apps/api-rbac/README.md +59 -0
- package/templates/apps/api-rbac/app.config.ts +48 -0
- package/templates/apps/api-rbac/database/schema.ts +37 -0
- package/templates/apps/api-rbac/mutations/notes.create.mutation.server.ts +22 -0
- package/templates/apps/api-rbac/mutations/notes.create.mutation.ts +26 -0
- package/templates/apps/api-rbac/package.json +29 -0
- package/templates/apps/api-rbac/template.json +6 -0
- package/templates/apps/api-rbac/tests/notes.create.test.ts +80 -0
- package/templates/apps/api-rbac/tsconfig.json +5 -0
- package/templates/apps/api-rest/README.md +85 -0
- package/templates/apps/api-rest/app.config.ts +70 -0
- package/templates/apps/api-rest/database/schema.ts +52 -0
- package/templates/apps/api-rest/lib/product.ts +30 -0
- package/templates/apps/api-rest/package.json +27 -0
- package/templates/apps/api-rest/routes/v1/products.create.route.tsx +45 -0
- package/templates/apps/api-rest/routes/v1/products.delete.route.tsx +25 -0
- package/templates/apps/api-rest/routes/v1/products.get.route.tsx +29 -0
- package/templates/apps/api-rest/routes/v1/products.list.route.tsx +43 -0
- package/templates/apps/api-rest/template.json +6 -0
- package/templates/apps/api-rest/tests/products.create.test.ts +53 -0
- package/templates/apps/api-rest/tsconfig.json +5 -0
- package/templates/apps/api-saas/README.md +106 -0
- package/templates/apps/api-saas/app.config.ts +51 -0
- package/templates/apps/api-saas/database/schema.ts +50 -0
- package/templates/apps/api-saas/mutations/projects.create.mutation.server.ts +52 -0
- package/templates/apps/api-saas/mutations/projects.create.mutation.ts +18 -0
- package/templates/apps/api-saas/package.json +31 -0
- package/templates/apps/api-saas/queries/projects.list.query.server.ts +7 -0
- package/templates/apps/api-saas/queries/projects.list.query.ts +18 -0
- package/templates/apps/api-saas/template.json +6 -0
- package/templates/apps/api-saas/tests/projects.create.test.ts +47 -0
- package/templates/apps/api-saas/tsconfig.json +5 -0
- package/templates/apps/api-search/README.md +68 -0
- package/templates/apps/api-search/app.config.ts +31 -0
- package/templates/apps/api-search/database/schema.ts +47 -0
- package/templates/apps/api-search/lib/search.ts +27 -0
- package/templates/apps/api-search/mutations/articles.create.mutation.server.ts +19 -0
- package/templates/apps/api-search/mutations/articles.create.mutation.ts +35 -0
- package/templates/apps/api-search/package.json +29 -0
- package/templates/apps/api-search/queries/articles.list.query.server.ts +14 -0
- package/templates/apps/api-search/queries/articles.list.query.ts +19 -0
- package/templates/apps/api-search/seeds/articles.seed.ts +41 -0
- package/templates/apps/api-search/startup/searchBackfill.startup.tsx +24 -0
- package/templates/apps/api-search/template.json +6 -0
- package/templates/apps/api-search/tests/articles.create.test.ts +69 -0
- package/templates/apps/api-search/tsconfig.json +5 -0
- package/templates/apps/api-versioning/README.md +51 -0
- package/templates/apps/api-versioning/actions/documents.asOf.action.server.ts +13 -0
- package/templates/apps/api-versioning/actions/documents.asOf.action.ts +14 -0
- package/templates/apps/api-versioning/actions/documents.history.action.server.ts +15 -0
- package/templates/apps/api-versioning/actions/documents.history.action.ts +17 -0
- package/templates/apps/api-versioning/app.config.ts +24 -0
- package/templates/apps/api-versioning/database/schema.ts +38 -0
- package/templates/apps/api-versioning/mutations/documents.create.mutation.server.ts +8 -0
- package/templates/apps/api-versioning/mutations/documents.create.mutation.ts +21 -0
- package/templates/apps/api-versioning/mutations/documents.update.mutation.server.ts +9 -0
- package/templates/apps/api-versioning/mutations/documents.update.mutation.ts +21 -0
- package/templates/apps/api-versioning/package.json +29 -0
- package/templates/apps/api-versioning/template.json +6 -0
- package/templates/apps/api-versioning/tests/documents.create.test.ts +37 -0
- package/templates/apps/api-versioning/tsconfig.json +5 -0
- package/templates/apps/api-webhooks/.env +6 -0
- package/templates/apps/api-webhooks/README.md +106 -0
- package/templates/apps/api-webhooks/app.config.ts +16 -0
- package/templates/apps/api-webhooks/database/schema.ts +49 -0
- package/templates/apps/api-webhooks/events/order.completed.webhook.tsx +22 -0
- package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.server.ts +37 -0
- package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.ts +15 -0
- package/templates/apps/api-webhooks/package.json +28 -0
- package/templates/apps/api-webhooks/queries/orders.list.query.server.ts +7 -0
- package/templates/apps/api-webhooks/queries/orders.list.query.ts +15 -0
- package/templates/apps/api-webhooks/template.json +6 -0
- package/templates/apps/api-webhooks/tests/orders.fulfill.test.ts +51 -0
- package/templates/apps/api-webhooks/tsconfig.json +5 -0
- package/templates/apps/api-webhooks/webhooks/orders.webhook.tsx +35 -0
- package/templates/apps/changelog/README.md +77 -0
- package/templates/apps/changelog/app.config.ts +27 -0
- package/templates/apps/changelog/content/releases/0.1.0.mdx +19 -0
- package/templates/apps/changelog/content/releases/0.2.0.mdx +28 -0
- package/templates/apps/changelog/package.json +30 -0
- package/templates/apps/changelog/scripts/generate-rss.mjs +38 -0
- package/templates/apps/changelog/src/globals.css +66 -0
- package/templates/apps/changelog/src/globals.d.ts +16 -0
- package/templates/apps/changelog/src/lib/locale.test.ts +72 -0
- package/templates/apps/changelog/src/lib/locale.ts +55 -0
- package/templates/apps/changelog/src/lib/releases.ts +21 -0
- package/templates/apps/changelog/src/locales/de.ts +22 -0
- package/templates/apps/changelog/src/locales/en.ts +30 -0
- package/templates/apps/changelog/src/pages/[locale]/[slug].tsx +24 -0
- package/templates/apps/changelog/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/changelog/src/pages/[locale]/mirrors.test.tsx +95 -0
- package/templates/apps/changelog/src/pages/[slug].test.tsx +127 -0
- package/templates/apps/changelog/src/pages/[slug].tsx +52 -0
- package/templates/apps/changelog/src/pages/index.test.tsx +112 -0
- package/templates/apps/changelog/src/pages/index.tsx +55 -0
- package/templates/apps/changelog/src/pages/layout.tsx +84 -0
- package/templates/apps/changelog/template.json +6 -0
- package/templates/apps/changelog/tsconfig.json +11 -0
- package/templates/apps/edge-functions/README.md +55 -0
- package/templates/apps/edge-functions/functions/aiComplete.serverless.ts +58 -0
- package/templates/apps/edge-functions/functions/currencyConvert.serverless.ts +47 -0
- package/templates/apps/edge-functions/functions/geoGreeting.serverless.ts +44 -0
- package/templates/apps/edge-functions/functions/health.serverless.ts +29 -0
- package/templates/apps/edge-functions/functions/resolveLink.serverless.ts +32 -0
- package/templates/apps/edge-functions/functions/shareLink.serverless.ts +30 -0
- package/templates/apps/edge-functions/functions/slackNotify.serverless.ts +40 -0
- package/templates/apps/edge-functions/functions/verifySignature.serverless.ts +52 -0
- package/templates/apps/edge-functions/package.json +21 -0
- package/templates/apps/edge-functions/template.json +6 -0
- package/templates/apps/edge-functions/tsconfig.json +10 -0
- package/templates/apps/frontend-admin/README.md +40 -0
- package/templates/apps/frontend-admin/app.config.ts +37 -0
- package/templates/apps/frontend-admin/package.json +30 -0
- package/templates/apps/frontend-admin/src/config.ts +8 -0
- package/templates/apps/frontend-admin/src/globals.css +76 -0
- package/templates/apps/frontend-admin/src/globals.d.ts +6 -0
- package/templates/apps/frontend-admin/src/lib/admin.ts +16 -0
- package/templates/apps/frontend-admin/src/lib/auth.ts +24 -0
- package/templates/apps/frontend-admin/src/locales/de.ts +67 -0
- package/templates/apps/frontend-admin/src/locales/en.ts +79 -0
- package/templates/apps/frontend-admin/src/locales/index.ts +15 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/index.test.tsx +55 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/index.tsx +32 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/layout.tsx +29 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/login.test.tsx +73 -0
- package/templates/apps/frontend-admin/src/pages/(marketing)/login.tsx +35 -0
- package/templates/apps/frontend-admin/src/pages/admin/[entity].tsx +121 -0
- package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +119 -0
- package/templates/apps/frontend-admin/src/pages/admin/error.tsx +20 -0
- package/templates/apps/frontend-admin/src/pages/admin/fallbacks.test.tsx +68 -0
- package/templates/apps/frontend-admin/src/pages/admin/index.test.tsx +88 -0
- package/templates/apps/frontend-admin/src/pages/admin/index.tsx +65 -0
- package/templates/apps/frontend-admin/src/pages/admin/layout.test.tsx +114 -0
- package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +97 -0
- package/templates/apps/frontend-admin/src/pages/admin/loading.tsx +15 -0
- package/templates/apps/frontend-admin/src/pages/admin/not-found.tsx +15 -0
- package/templates/apps/frontend-admin/src/pages/layout.tsx +12 -0
- package/templates/apps/frontend-admin/src/pages/layouts.test.tsx +54 -0
- package/templates/apps/frontend-admin/template.json +6 -0
- package/templates/apps/frontend-admin/tsconfig.json +11 -0
- package/templates/apps/frontend-app/README.md +98 -0
- package/templates/apps/frontend-app/app.config.ts +43 -0
- package/templates/apps/frontend-app/package.json +30 -0
- package/templates/apps/frontend-app/src/locales/de.ts +36 -0
- package/templates/apps/frontend-app/src/locales/en.ts +43 -0
- package/templates/apps/frontend-app/src/locales/index.ts +15 -0
- package/templates/apps/frontend-app/src/pages/index.test.tsx +167 -0
- package/templates/apps/frontend-app/src/pages/index.tsx +136 -0
- package/templates/apps/frontend-app/src/pages/layout.tsx +41 -0
- package/templates/apps/frontend-app/src/pages/schema-ui.test.tsx +99 -0
- package/templates/apps/frontend-app/src/pages/schema-ui.tsx +74 -0
- package/templates/apps/frontend-app/template.json +6 -0
- package/templates/apps/frontend-app/tsconfig.json +11 -0
- package/templates/apps/frontend-blank/README.md +18 -0
- package/templates/apps/frontend-blank/app.config.ts +29 -0
- package/templates/apps/frontend-blank/package.json +29 -0
- package/templates/apps/frontend-blank/src/locales/de.ts +15 -0
- package/templates/apps/frontend-blank/src/locales/en.ts +22 -0
- package/templates/apps/frontend-blank/src/locales/index.ts +15 -0
- package/templates/apps/frontend-blank/src/pages/index.test.tsx +55 -0
- package/templates/apps/frontend-blank/src/pages/index.tsx +27 -0
- package/templates/apps/frontend-blank/src/pages/layout.test.tsx +54 -0
- package/templates/apps/frontend-blank/src/pages/layout.tsx +35 -0
- package/templates/apps/frontend-blank/template.json +6 -0
- package/templates/apps/frontend-blank/tsconfig.json +11 -0
- package/templates/apps/frontend-contact/README.md +65 -0
- package/templates/apps/frontend-contact/app.config.ts +25 -0
- package/templates/apps/frontend-contact/functions/sendMessage.serverless.ts +69 -0
- package/templates/apps/frontend-contact/package.json +33 -0
- package/templates/apps/frontend-contact/src/components/ContactForm.island.test.tsx +142 -0
- package/templates/apps/frontend-contact/src/components/ContactForm.island.tsx +104 -0
- package/templates/apps/frontend-contact/src/config.ts +12 -0
- package/templates/apps/frontend-contact/src/globals.css +84 -0
- package/templates/apps/frontend-contact/src/lib/locale.ts +55 -0
- package/templates/apps/frontend-contact/src/locales/de.ts +26 -0
- package/templates/apps/frontend-contact/src/locales/en.ts +29 -0
- package/templates/apps/frontend-contact/src/pages/[locale]/index.tsx +14 -0
- package/templates/apps/frontend-contact/src/pages/index.test.tsx +70 -0
- package/templates/apps/frontend-contact/src/pages/index.tsx +63 -0
- package/templates/apps/frontend-contact/src/pages/layout.tsx +65 -0
- package/templates/apps/frontend-contact/template.json +6 -0
- package/templates/apps/frontend-contact/tsconfig.json +11 -0
- package/templates/apps/frontend-dashboard/README.md +54 -0
- package/templates/apps/frontend-dashboard/app.config.ts +39 -0
- package/templates/apps/frontend-dashboard/package.json +29 -0
- package/templates/apps/frontend-dashboard/src/config.ts +8 -0
- package/templates/apps/frontend-dashboard/src/globals.css +72 -0
- package/templates/apps/frontend-dashboard/src/globals.d.ts +6 -0
- package/templates/apps/frontend-dashboard/src/lib/auth.ts +27 -0
- package/templates/apps/frontend-dashboard/src/locales/de.ts +49 -0
- package/templates/apps/frontend-dashboard/src/locales/en.ts +60 -0
- package/templates/apps/frontend-dashboard/src/locales/index.ts +15 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/index.test.tsx +55 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/index.tsx +34 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/layout.tsx +32 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/login.test.tsx +74 -0
- package/templates/apps/frontend-dashboard/src/pages/(marketing)/login.tsx +40 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/error.tsx +21 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/fallbacks.test.tsx +68 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/index.test.tsx +55 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/index.tsx +38 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/layout.test.tsx +88 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/layout.tsx +69 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/loading.tsx +21 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/not-found.tsx +19 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/settings.test.tsx +69 -0
- package/templates/apps/frontend-dashboard/src/pages/dashboard/settings.tsx +35 -0
- package/templates/apps/frontend-dashboard/src/pages/layout.tsx +12 -0
- package/templates/apps/frontend-dashboard/src/pages/layouts.test.tsx +54 -0
- package/templates/apps/frontend-dashboard/template.json +6 -0
- package/templates/apps/frontend-dashboard/tsconfig.json +11 -0
- package/templates/apps/frontend-docs/README.md +19 -0
- package/templates/apps/frontend-docs/app.config.ts +25 -0
- package/templates/apps/frontend-docs/package.json +29 -0
- package/templates/apps/frontend-docs/src/globals.css +32 -0
- package/templates/apps/frontend-docs/src/lib/locale.test.ts +72 -0
- package/templates/apps/frontend-docs/src/lib/locale.ts +56 -0
- package/templates/apps/frontend-docs/src/locales/de.ts +24 -0
- package/templates/apps/frontend-docs/src/locales/en.ts +28 -0
- package/templates/apps/frontend-docs/src/pages/[locale]/docs/[...slug].tsx +18 -0
- package/templates/apps/frontend-docs/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/frontend-docs/src/pages/[locale]/mirrors.test.tsx +67 -0
- package/templates/apps/frontend-docs/src/pages/docs/[...slug].test.tsx +115 -0
- package/templates/apps/frontend-docs/src/pages/docs/[...slug].tsx +82 -0
- package/templates/apps/frontend-docs/src/pages/index.test.tsx +90 -0
- package/templates/apps/frontend-docs/src/pages/index.tsx +45 -0
- package/templates/apps/frontend-docs/src/pages/layout.test.tsx +84 -0
- package/templates/apps/frontend-docs/src/pages/layout.tsx +66 -0
- package/templates/apps/frontend-docs/template.json +6 -0
- package/templates/apps/frontend-docs/tsconfig.json +11 -0
- package/templates/apps/frontend-i18n/README.md +61 -0
- package/templates/apps/frontend-i18n/app.config.ts +33 -0
- package/templates/apps/frontend-i18n/package.json +28 -0
- package/templates/apps/frontend-i18n/src/globals.css +46 -0
- package/templates/apps/frontend-i18n/src/globals.d.ts +6 -0
- package/templates/apps/frontend-i18n/src/lib/locale.test.ts +72 -0
- package/templates/apps/frontend-i18n/src/lib/locale.ts +55 -0
- package/templates/apps/frontend-i18n/src/locales/de.ts +24 -0
- package/templates/apps/frontend-i18n/src/locales/en.ts +25 -0
- package/templates/apps/frontend-i18n/src/pages/[locale]/about.tsx +11 -0
- package/templates/apps/frontend-i18n/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/frontend-i18n/src/pages/[locale]/mirrors.test.tsx +50 -0
- package/templates/apps/frontend-i18n/src/pages/about.test.tsx +75 -0
- package/templates/apps/frontend-i18n/src/pages/about.tsx +31 -0
- package/templates/apps/frontend-i18n/src/pages/index.test.tsx +102 -0
- package/templates/apps/frontend-i18n/src/pages/index.tsx +43 -0
- package/templates/apps/frontend-i18n/src/pages/layout.test.tsx +86 -0
- package/templates/apps/frontend-i18n/src/pages/layout.tsx +70 -0
- package/templates/apps/frontend-i18n/template.json +6 -0
- package/templates/apps/frontend-i18n/tsconfig.json +11 -0
- package/templates/apps/frontend-landing/README.md +17 -0
- package/templates/apps/frontend-landing/app.config.ts +25 -0
- package/templates/apps/frontend-landing/package.json +29 -0
- package/templates/apps/frontend-landing/src/globals.css +23 -0
- package/templates/apps/frontend-landing/src/lib/locale.test.ts +72 -0
- package/templates/apps/frontend-landing/src/lib/locale.ts +55 -0
- package/templates/apps/frontend-landing/src/locales/de.ts +24 -0
- package/templates/apps/frontend-landing/src/locales/en.ts +26 -0
- package/templates/apps/frontend-landing/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/frontend-landing/src/pages/[locale]/mirrors.test.tsx +37 -0
- package/templates/apps/frontend-landing/src/pages/index.test.tsx +110 -0
- package/templates/apps/frontend-landing/src/pages/index.tsx +75 -0
- package/templates/apps/frontend-landing/src/pages/layout.test.tsx +84 -0
- package/templates/apps/frontend-landing/src/pages/layout.tsx +66 -0
- package/templates/apps/frontend-landing/template.json +6 -0
- package/templates/apps/frontend-landing/tsconfig.json +11 -0
- package/templates/apps/frontend-spa/README.md +45 -0
- package/templates/apps/frontend-spa/app.config.ts +27 -0
- package/templates/apps/frontend-spa/package.json +29 -0
- package/templates/apps/frontend-spa/src/globals.css +84 -0
- package/templates/apps/frontend-spa/src/locales/de.ts +22 -0
- package/templates/apps/frontend-spa/src/locales/en.ts +29 -0
- package/templates/apps/frontend-spa/src/locales/index.ts +15 -0
- package/templates/apps/frontend-spa/src/pages/index.test.tsx +137 -0
- package/templates/apps/frontend-spa/src/pages/index.tsx +123 -0
- package/templates/apps/frontend-spa/src/pages/layout.tsx +27 -0
- package/templates/apps/frontend-spa/template.json +6 -0
- package/templates/apps/frontend-spa/tsconfig.json +11 -0
- package/templates/apps/frontend-ssr/README.md +68 -0
- package/templates/apps/frontend-ssr/app.config.ts +32 -0
- package/templates/apps/frontend-ssr/package.json +29 -0
- package/templates/apps/frontend-ssr/src/globals.css +67 -0
- package/templates/apps/frontend-ssr/src/locales/de.ts +41 -0
- package/templates/apps/frontend-ssr/src/locales/en.ts +54 -0
- package/templates/apps/frontend-ssr/src/locales/index.ts +16 -0
- package/templates/apps/frontend-ssr/src/pages/feed-swr.test.tsx +69 -0
- package/templates/apps/frontend-ssr/src/pages/feed-swr.tsx +54 -0
- package/templates/apps/frontend-ssr/src/pages/feed.test.tsx +73 -0
- package/templates/apps/frontend-ssr/src/pages/feed.tsx +64 -0
- package/templates/apps/frontend-ssr/src/pages/index.test.tsx +89 -0
- package/templates/apps/frontend-ssr/src/pages/index.tsx +72 -0
- package/templates/apps/frontend-ssr/src/pages/layout.tsx +37 -0
- package/templates/apps/frontend-ssr/template.json +6 -0
- package/templates/apps/frontend-ssr/tsconfig.json +11 -0
- package/templates/apps/frontend-ssr-api/README.md +50 -0
- package/templates/apps/frontend-ssr-api/app.config.ts +43 -0
- package/templates/apps/frontend-ssr-api/package.json +30 -0
- package/templates/apps/frontend-ssr-api/src/globals.css +38 -0
- package/templates/apps/frontend-ssr-api/src/globals.d.ts +6 -0
- package/templates/apps/frontend-ssr-api/src/locales/de.ts +20 -0
- package/templates/apps/frontend-ssr-api/src/locales/en.ts +31 -0
- package/templates/apps/frontend-ssr-api/src/locales/index.ts +16 -0
- package/templates/apps/frontend-ssr-api/src/pages/index.test.tsx +105 -0
- package/templates/apps/frontend-ssr-api/src/pages/index.tsx +83 -0
- package/templates/apps/frontend-ssr-api/src/pages/layout.tsx +28 -0
- package/templates/apps/frontend-ssr-api/template.json +6 -0
- package/templates/apps/frontend-ssr-api/tsconfig.json +11 -0
- package/templates/apps/frontend-static-blog/README.md +49 -0
- package/templates/apps/frontend-static-blog/app.config.ts +34 -0
- package/templates/apps/frontend-static-blog/package.json +28 -0
- package/templates/apps/frontend-static-blog/src/components/ReadingProgress.island.test.tsx +65 -0
- package/templates/apps/frontend-static-blog/src/components/ReadingProgress.island.tsx +35 -0
- package/templates/apps/frontend-static-blog/src/content/posts.ts +64 -0
- package/templates/apps/frontend-static-blog/src/globals.css +75 -0
- package/templates/apps/frontend-static-blog/src/lib/locale.test.ts +72 -0
- package/templates/apps/frontend-static-blog/src/lib/locale.ts +55 -0
- package/templates/apps/frontend-static-blog/src/locales/de.ts +19 -0
- package/templates/apps/frontend-static-blog/src/locales/en.ts +26 -0
- package/templates/apps/frontend-static-blog/src/pages/[locale]/blog/[slug].tsx +20 -0
- package/templates/apps/frontend-static-blog/src/pages/[locale]/index.tsx +13 -0
- package/templates/apps/frontend-static-blog/src/pages/[locale]/mirrors.test.tsx +62 -0
- package/templates/apps/frontend-static-blog/src/pages/blog/[slug].test.tsx +116 -0
- package/templates/apps/frontend-static-blog/src/pages/blog/[slug].tsx +69 -0
- package/templates/apps/frontend-static-blog/src/pages/index.test.tsx +100 -0
- package/templates/apps/frontend-static-blog/src/pages/index.tsx +58 -0
- package/templates/apps/frontend-static-blog/src/pages/layout.tsx +63 -0
- package/templates/apps/frontend-static-blog/template.json +6 -0
- package/templates/apps/frontend-static-blog/tsconfig.json +11 -0
- package/templates/baselines/bare/.env.example +37 -0
- package/templates/baselines/bare/README.md +47 -0
- package/templates/baselines/bare/baseline.json +31 -0
- package/templates/baselines/bare/deploy/README.md +43 -0
- package/templates/baselines/bare/deploy/voltro.service.example +36 -0
- package/templates/baselines/compose/.env.example +46 -0
- package/templates/baselines/compose/README.md +69 -0
- package/templates/baselines/compose/baseline.json +51 -0
- package/templates/baselines/compose/docker/.dockerignore +38 -0
- package/templates/baselines/compose/docker/api.Dockerfile +57 -0
- package/templates/baselines/compose/docker/dev.Dockerfile +35 -0
- package/templates/baselines/compose/docker/web.Dockerfile +59 -0
- package/templates/baselines/compose/docker-compose.dev.yml +89 -0
- package/templates/baselines/compose/docker-compose.prod.yml +87 -0
- package/templates/baselines/compose/docker-compose.yml +41 -0
- package/templates/baselines/compose-mariadb/.env.example +57 -0
- package/templates/baselines/compose-mariadb/README.md +78 -0
- package/templates/baselines/compose-mariadb/baseline.json +51 -0
- package/templates/baselines/compose-mariadb/docker/.dockerignore +38 -0
- package/templates/baselines/compose-mariadb/docker/api.Dockerfile +57 -0
- package/templates/baselines/compose-mariadb/docker/dev.Dockerfile +35 -0
- package/templates/baselines/compose-mariadb/docker/mariadb-init.sql +6 -0
- package/templates/baselines/compose-mariadb/docker/web.Dockerfile +59 -0
- package/templates/baselines/compose-mariadb/docker-compose.dev.yml +117 -0
- package/templates/baselines/compose-mariadb/docker-compose.prod.yml +114 -0
- package/templates/baselines/compose-mariadb/docker-compose.yml +79 -0
- package/templates/baselines/helm/.env.example +39 -0
- package/templates/baselines/helm/README.md +98 -0
- package/templates/baselines/helm/baseline.json +53 -0
- package/templates/baselines/helm/charts/voltro-app/.helmignore +10 -0
- package/templates/baselines/helm/charts/voltro-app/Chart.yaml +10 -0
- package/templates/baselines/helm/charts/voltro-app/templates/_helpers.tpl +36 -0
- package/templates/baselines/helm/charts/voltro-app/templates/configmap.yaml +13 -0
- package/templates/baselines/helm/charts/voltro-app/templates/deployment-api.yaml +120 -0
- package/templates/baselines/helm/charts/voltro-app/templates/deployment-web.yaml +45 -0
- package/templates/baselines/helm/charts/voltro-app/templates/ingress.yaml +37 -0
- package/templates/baselines/helm/charts/voltro-app/templates/postgres-service.yaml +19 -0
- package/templates/baselines/helm/charts/voltro-app/templates/postgres-statefulset.yaml +73 -0
- package/templates/baselines/helm/charts/voltro-app/templates/secret.yaml +33 -0
- package/templates/baselines/helm/charts/voltro-app/templates/service-api.yaml +19 -0
- package/templates/baselines/helm/charts/voltro-app/templates/service-web.yaml +19 -0
- package/templates/baselines/helm/charts/voltro-app/values-dev.yaml +21 -0
- package/templates/baselines/helm/charts/voltro-app/values-prod.yaml +58 -0
- package/templates/baselines/helm/charts/voltro-app/values-staging.yaml +25 -0
- package/templates/baselines/helm/charts/voltro-app/values.yaml +109 -0
- package/templates/baselines/helm/deploy/README.md +94 -0
- package/templates/patches/@effect__cluster@0.59.0.patch +262 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// INCOMING webhook — a public endpoint a partner POSTs to. Mounts at
|
|
2
|
+
// `/webhooks/orders` (the `id`). The framework's incoming middleware runs
|
|
3
|
+
// BEFORE your handler:
|
|
4
|
+
//
|
|
5
|
+
// 1. verifies the `X-Webhook-Signature: t=<ts>,v1=<hmac>` HMAC over
|
|
6
|
+
// `<ts>.<rawBody>` with VOLTRO_WEBHOOK_SECRET_ORDERS → 401 on mismatch
|
|
7
|
+
// 2. rejects a `t` older than the replay window → 401
|
|
8
|
+
// 3. decodes the body against `payload` → 422 on bad shape
|
|
9
|
+
// 4. claims the `Idempotency-Key` (dedup) → 200 {duplicate:true} on replay
|
|
10
|
+
//
|
|
11
|
+
// Only a request that passes ALL of that reaches `handler`. That's the whole
|
|
12
|
+
// point: you never hand-roll signature checks, and forged traffic never runs
|
|
13
|
+
// your code.
|
|
14
|
+
|
|
15
|
+
import { defineIncomingWebhook } from '@voltro/plugin-webhooks'
|
|
16
|
+
import { genericProvider } from '@voltro/plugin-webhooks/providers'
|
|
17
|
+
import { Schema } from 'effect'
|
|
18
|
+
|
|
19
|
+
export default defineIncomingWebhook({
|
|
20
|
+
id: 'orders',
|
|
21
|
+
provider: genericProvider(), // HMAC `<ts>.<body>` → X-Webhook-Signature; idempotency via Idempotency-Key
|
|
22
|
+
payload: Schema.Struct({
|
|
23
|
+
event: Schema.String,
|
|
24
|
+
orderId: Schema.String,
|
|
25
|
+
sku: Schema.String,
|
|
26
|
+
totalCents: Schema.Number,
|
|
27
|
+
}),
|
|
28
|
+
// ctx.body is validated; ctx.idempotencyKey + ctx.headers are also available.
|
|
29
|
+
// No ctx.store here — a webhook handler is app glue; for DB writes, enqueue a
|
|
30
|
+
// workflow or call your own service. We log to prove receipt (the dev console
|
|
31
|
+
// bridge surfaces it in the terminal).
|
|
32
|
+
handler: async (ctx) => {
|
|
33
|
+
console.log(`[webhook:orders] verified ${ctx.body.event} for ${ctx.body.orderId} (idempotency=${ctx.idempotencyKey})`)
|
|
34
|
+
},
|
|
35
|
+
})
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# {{projectName}} / {{appName}}
|
|
2
|
+
|
|
3
|
+
A bilingual (en/de) changelog / release-notes site (template: **changelog**),
|
|
4
|
+
powered by [`@voltro/changelog`](https://www.npmjs.com/package/@voltro/changelog).
|
|
5
|
+
The chrome (nav, footer, empty state, page titles) is translated; release
|
|
6
|
+
entries are content and stay as authored. English lives at the bare path,
|
|
7
|
+
German under `/de` — each pre-rendered as its own crawlable URL.
|
|
8
|
+
|
|
9
|
+
## What's here
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
content/releases/*.mdx ← one file per release (frontmatter + markdown)
|
|
13
|
+
src/lib/releases.ts ← loads + sorts them via `loadReleases` (Vite raw glob)
|
|
14
|
+
src/lib/locale.ts ← URL-prefix i18n helpers (SUPPORTED_LOCALES, withLocalePrefix, …)
|
|
15
|
+
src/locales/{en,de}.ts ← typed, key-parity chrome catalogs
|
|
16
|
+
src/pages/index.tsx ← the release list
|
|
17
|
+
src/pages/[slug].tsx ← one page per release (code fences highlighted by `highlightRelease`)
|
|
18
|
+
src/pages/[locale]/** ← per-locale mirrors (getStaticPaths → /de, /de/<slug>)
|
|
19
|
+
src/pages/layout.tsx ← BlogLayout shell + URL <I18nProvider> + language switcher (headerRight)
|
|
20
|
+
scripts/generate-rss.mjs ← writes public/rss.xml via `renderReleaseRss` (build step)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm install
|
|
25
|
+
pnpm --filter @{{projectName}}/{{appName}} dev
|
|
26
|
+
# → http://localhost:{{port}} the release list
|
|
27
|
+
# → http://localhost:{{port}}/v0-2-0 a single release
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Add a release
|
|
31
|
+
|
|
32
|
+
Drop a new file in `content/releases/`. Required frontmatter is `version`
|
|
33
|
+
+ `releasedAt`; everything else is optional:
|
|
34
|
+
|
|
35
|
+
```mdx
|
|
36
|
+
---
|
|
37
|
+
version: 0.3.0
|
|
38
|
+
releasedAt: 2026-07-01
|
|
39
|
+
slug: v0-3-0 # URL segment; defaults to a slugified version
|
|
40
|
+
title: My release
|
|
41
|
+
summary: One-line summary (shown in the list + RSS).
|
|
42
|
+
tags: [feature, fix] # feature | fix | breaking | security | performance
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## What changed
|
|
46
|
+
|
|
47
|
+
Markdown body — code fences get syntax-highlighted at build time.
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Files missing `version`/`releasedAt` are skipped; the list is sorted
|
|
51
|
+
newest-first automatically.
|
|
52
|
+
|
|
53
|
+
## RSS feed
|
|
54
|
+
|
|
55
|
+
`pnpm build` runs `scripts/generate-rss.mjs` (which calls
|
|
56
|
+
`renderReleaseRss`) before the static build, emitting `public/rss.xml`
|
|
57
|
+
→ served at `/rss.xml`. Set `SITE_URL` so the feed's links are absolute:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
SITE_URL=https://changelog.example.com pnpm build && pnpm start
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## "What's new" badge in another app
|
|
64
|
+
|
|
65
|
+
The other half of `@voltro/changelog` lives on the browser-only
|
|
66
|
+
`/web` subpath: `useChangelogBadge` polls this site's `rss.xml` and tells
|
|
67
|
+
a PRODUCT app's nav when there's an unread release. Add it to that app
|
|
68
|
+
(not this one):
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { useChangelogBadge } from '@voltro/changelog/web'
|
|
72
|
+
|
|
73
|
+
const { hasUnread, latest, markRead } = useChangelogBadge({
|
|
74
|
+
url: 'https://changelog.example.com/rss.xml',
|
|
75
|
+
})
|
|
76
|
+
// render a dot when `hasUnread`; call `markRead()` when the user opens it.
|
|
77
|
+
```
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// `port` is allocated by `voltro create-project` / `voltro add-app` from
|
|
2
|
+
// the project's portRange (in project.json).
|
|
3
|
+
import { defineEnv, envVar } from '@voltro/env'
|
|
4
|
+
|
|
5
|
+
// Typed PUBLIC environment. `VOLTRO_PUBLIC_SITE_NAME` titles the changelog
|
|
6
|
+
// (header brand + RSS feed). Public vars are validated at boot and baked
|
|
7
|
+
// into the browser bundle — NEVER put a secret here. Run `voltro env` for
|
|
8
|
+
// the manifest. The RSS generator (scripts/generate-rss.mjs) reads the same
|
|
9
|
+
// value from process.env at build time.
|
|
10
|
+
export const env = defineEnv({
|
|
11
|
+
VOLTRO_PUBLIC_SITE_NAME: envVar.string({ access: 'public', default: '{{capProjectName}}' }),
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export default {
|
|
15
|
+
type: 'web' as const,
|
|
16
|
+
name: '{{capProjectName}}{{capAppName}}',
|
|
17
|
+
port: {{port}},
|
|
18
|
+
// A BILINGUAL (en/de) changelog — Strategy B (URL-prefix i18n). English
|
|
19
|
+
// lives at the bare path (`/`, `/<slug>`); German is prefixed (`/de`,
|
|
20
|
+
// `/de/<slug>`). `locales` tells `voltro build` to pre-render a per-locale
|
|
21
|
+
// HTML variant for every `[locale]/…` mirror page. Keep in sync with
|
|
22
|
+
// SUPPORTED_LOCALES / DEFAULT_LOCALE in src/lib/locale.ts; every code MUST
|
|
23
|
+
// have a matching `src/locales/<code>.ts`.
|
|
24
|
+
locales: ['en', 'de'] as const,
|
|
25
|
+
defaultLocale: 'en' as const,
|
|
26
|
+
env,
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 0.1.0
|
|
3
|
+
releasedAt: 2026-01-15
|
|
4
|
+
slug: v0-1-0
|
|
5
|
+
title: First release
|
|
6
|
+
summary: The initial cut — projects, file-based pages, and the reactive query layer.
|
|
7
|
+
tags: [feature]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Highlights
|
|
11
|
+
|
|
12
|
+
The first public release. Spin up an app and subscribe to live data — no
|
|
13
|
+
`refetch`, the subscription stays live for the component's lifetime:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const { data } = useSubscription('app', 'todos.list')
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Welcome aboard.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 0.2.0
|
|
3
|
+
releasedAt: 2026-06-21
|
|
4
|
+
slug: v0-2-0
|
|
5
|
+
title: Typed env + faster boot
|
|
6
|
+
summary: Declare your environment once; it's validated at startup with a hard public/secret boundary.
|
|
7
|
+
tags: [feature, performance]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Typed environment
|
|
11
|
+
|
|
12
|
+
`defineEnv` validates every variable at boot (fail-fast, all problems
|
|
13
|
+
listed) and hands back typed, coerced accessors instead of
|
|
14
|
+
`string | undefined`:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
export const env = defineEnv({
|
|
18
|
+
MAX_BATCH: envVar.number({ access: 'public', default: '50' }),
|
|
19
|
+
STRIPE_KEY: envVar.string({ access: 'secret' }), // never reaches the browser
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
A `secret` is unrepresentable in the browser bundle — enforced by the
|
|
24
|
+
compiler and the bundler, not by discipline.
|
|
25
|
+
|
|
26
|
+
## Faster cold boot
|
|
27
|
+
|
|
28
|
+
Roughly **30% faster** startup on a warm schema cache.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/{{appName}}",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "voltro dev .",
|
|
8
|
+
"build": "tsx scripts/generate-rss.mjs && voltro build .",
|
|
9
|
+
"start": "voltro start .",
|
|
10
|
+
"typecheck": "tsc --noEmit",
|
|
11
|
+
"test": "voltro test"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@voltro/changelog": "0.1.0",
|
|
15
|
+
"@voltro/cli": "0.1.0",
|
|
16
|
+
"@voltro/client": "0.1.0",
|
|
17
|
+
"@voltro/env": "0.1.0",
|
|
18
|
+
"@voltro/i18n": "0.1.0",
|
|
19
|
+
"@voltro/web": "0.1.0",
|
|
20
|
+
"react": "^19.0.0",
|
|
21
|
+
"react-dom": "^19.0.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/react": "^19.0.0",
|
|
25
|
+
"@types/react-dom": "^19.0.0",
|
|
26
|
+
"tsx": "^4.22.4",
|
|
27
|
+
"typescript": "^5.7.0",
|
|
28
|
+
"vitest": "^3.0.0"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Generate public/rss.xml from content/releases/*.mdx using the SAME
|
|
2
|
+
// @voltro/changelog primitives the pages use (`loadReleases` +
|
|
3
|
+
// `renderReleaseRss`). Runs BEFORE `voltro build` so Vite copies the file
|
|
4
|
+
// into the build output (publicDir = <app>/public) and `voltro start`
|
|
5
|
+
// serves it as application/xml.
|
|
6
|
+
//
|
|
7
|
+
// Why a separate Node script and not a page route: the web SSG renders
|
|
8
|
+
// HTML pages only — a feed is a non-HTML artifact, so it's emitted as a
|
|
9
|
+
// static file rather than an `rss.xml.tsx` route. Run via `tsx` so the
|
|
10
|
+
// import resolves whether @voltro/changelog points at its TS source
|
|
11
|
+
// (in-workspace dev) or its built dist (a normal install).
|
|
12
|
+
import { readdirSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
13
|
+
import { dirname, join } from 'node:path'
|
|
14
|
+
import { fileURLToPath } from 'node:url'
|
|
15
|
+
import { loadReleases, renderReleaseRss } from '@voltro/changelog'
|
|
16
|
+
|
|
17
|
+
const appRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
|
|
18
|
+
const releasesDir = join(appRoot, 'content', 'releases')
|
|
19
|
+
|
|
20
|
+
// Read each .mdx file into the { path: contents } map loadReleases expects.
|
|
21
|
+
const sources = {}
|
|
22
|
+
for (const file of readdirSync(releasesDir)) {
|
|
23
|
+
if (file.endsWith('.mdx')) sources[file] = readFileSync(join(releasesDir, file), 'utf8')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const releases = loadReleases(sources)
|
|
27
|
+
// The base URL each release links under (<siteUrl>/<slug>). Set SITE_URL in
|
|
28
|
+
// your deploy env; localhost is the dev default.
|
|
29
|
+
const siteUrl = process.env.SITE_URL ?? 'http://localhost:{{port}}'
|
|
30
|
+
const xml = renderReleaseRss(releases, siteUrl, {
|
|
31
|
+
title: '{{capProjectName}} Changelog',
|
|
32
|
+
description: 'Product updates and release notes.',
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const publicDir = join(appRoot, 'public')
|
|
36
|
+
mkdirSync(publicDir, { recursive: true })
|
|
37
|
+
writeFileSync(join(publicDir, 'rss.xml'), xml)
|
|
38
|
+
console.log(`rss.xml — ${releases.length} release(s) → public/rss.xml`)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/* Changelog template — self-contained styling, no Tailwind / kit dependency.
|
|
2
|
+
* `BlogLayout` (from @voltro/web) ships its own layout styles; this file
|
|
3
|
+
* styles the changelog LIST and the rendered markdown body (release.html).
|
|
4
|
+
* Want Tailwind + the shadcn palette instead? Swap this file for
|
|
5
|
+
* `@import "@voltro/ui-shadcn/tokens.css";` + an `@source` (see frontend-docs). */
|
|
6
|
+
|
|
7
|
+
:root {
|
|
8
|
+
--cl-fg: #18181b;
|
|
9
|
+
--cl-muted: #71717a;
|
|
10
|
+
--cl-border: rgba(127, 127, 127, 0.2);
|
|
11
|
+
--cl-accent: #4f46e5;
|
|
12
|
+
--cl-code-bg: #f4f4f5;
|
|
13
|
+
--cl-tag-bg: rgba(79, 70, 229, 0.12);
|
|
14
|
+
}
|
|
15
|
+
@media (prefers-color-scheme: dark) {
|
|
16
|
+
:root {
|
|
17
|
+
--cl-fg: #fafafa;
|
|
18
|
+
--cl-muted: #a1a1aa;
|
|
19
|
+
--cl-code-bg: #18181b;
|
|
20
|
+
--cl-accent: #818cf8;
|
|
21
|
+
--cl-tag-bg: rgba(129, 140, 248, 0.16);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
body {
|
|
26
|
+
margin: 0;
|
|
27
|
+
color: var(--cl-fg);
|
|
28
|
+
font-family: ui-sans-serif, system-ui, -apple-system, sans-serif;
|
|
29
|
+
line-height: 1.6;
|
|
30
|
+
}
|
|
31
|
+
a { color: var(--cl-accent); }
|
|
32
|
+
|
|
33
|
+
/* Language switcher pinned in BlogLayout's headerRight slot. */
|
|
34
|
+
.cl-lang { display: inline-flex; gap: 0.5rem; align-items: center; font-size: 0.85rem; }
|
|
35
|
+
.cl-lang-label { color: var(--cl-muted); }
|
|
36
|
+
.cl-lang a[aria-current='true'] { font-weight: 600; }
|
|
37
|
+
|
|
38
|
+
/* Release meta + tags (list + detail). */
|
|
39
|
+
.changelog-meta { font-size: 0.85rem; color: var(--cl-muted); margin: 0 0 0.5rem; }
|
|
40
|
+
.changelog-tag {
|
|
41
|
+
display: inline-block;
|
|
42
|
+
margin-left: 0.4rem;
|
|
43
|
+
padding: 0.05rem 0.45rem;
|
|
44
|
+
border-radius: 999px;
|
|
45
|
+
font-size: 0.72rem;
|
|
46
|
+
background: var(--cl-tag-bg);
|
|
47
|
+
color: var(--cl-accent);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* List entries. */
|
|
51
|
+
.changelog-entry { padding: 1.25rem 0; border-bottom: 1px solid var(--cl-border); }
|
|
52
|
+
.changelog-entry h2 { margin: 0.25rem 0; font-size: 1.25rem; }
|
|
53
|
+
.changelog-entry h2 a { text-decoration: none; }
|
|
54
|
+
.changelog-entry p:last-child { color: var(--cl-muted); margin: 0.25rem 0 0; }
|
|
55
|
+
|
|
56
|
+
/* Rendered markdown body (release.html from marked / shiki). */
|
|
57
|
+
.changelog-prose h1 { font-size: 1.9rem; font-weight: 700; letter-spacing: -0.02em; margin: 0 0 0.5rem; }
|
|
58
|
+
.changelog-prose h2 { font-size: 1.3rem; font-weight: 600; margin: 1.75rem 0 0.5rem; }
|
|
59
|
+
.changelog-prose h3 { font-size: 1.05rem; font-weight: 600; margin: 1.25rem 0 0.4rem; }
|
|
60
|
+
.changelog-prose p, .changelog-prose li { color: var(--cl-fg); }
|
|
61
|
+
.changelog-prose ul { padding-left: 1.4rem; }
|
|
62
|
+
.changelog-prose code { background: var(--cl-code-bg); padding: 2px 5px; border-radius: 4px; font-size: 0.85em; }
|
|
63
|
+
/* `shiki` sets its own inline background on <pre class="shiki">; this is the
|
|
64
|
+
* fallback for plain (un-highlighted) marked output. */
|
|
65
|
+
.changelog-prose pre { background: var(--cl-code-bg); padding: 1rem; border-radius: 8px; overflow-x: auto; }
|
|
66
|
+
.changelog-prose pre code { background: transparent; padding: 0; }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Ambient declarations.
|
|
2
|
+
//
|
|
3
|
+
// `declare module '*.css'`: TypeScript 6 with `moduleResolution: Bundler`
|
|
4
|
+
// rejects a bare `import './globals.css'` unless the asset module is
|
|
5
|
+
// declared. Vite resolves it at build time; the declaration is type-only.
|
|
6
|
+
declare module '*.css'
|
|
7
|
+
|
|
8
|
+
// Minimal typing for Vite's `import.meta.glob` (used by src/lib/releases.ts)
|
|
9
|
+
// so the app typechecks without depending on `vite/client` types being
|
|
10
|
+
// resolvable. Vite provides the real implementation at build time.
|
|
11
|
+
interface ImportMeta {
|
|
12
|
+
readonly glob: <T = unknown>(
|
|
13
|
+
pattern: string | ReadonlyArray<string>,
|
|
14
|
+
options?: { query?: string; import?: string; eager?: boolean },
|
|
15
|
+
) => Record<string, T>
|
|
16
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// The URL-prefix i18n helpers — the load-bearing pure functions that drive the
|
|
2
|
+
// [locale] mirror routing + the language switcher. `useUrlLocale` (the one hook
|
|
3
|
+
// in this module) reads useLocation from @voltro/web; the rest are pure string
|
|
4
|
+
// functions we can exercise directly.
|
|
5
|
+
|
|
6
|
+
import { describe, expect, test } from 'vitest'
|
|
7
|
+
import {
|
|
8
|
+
SUPPORTED_LOCALES,
|
|
9
|
+
DEFAULT_LOCALE,
|
|
10
|
+
getCatalog,
|
|
11
|
+
localeFromPathname,
|
|
12
|
+
withLocalePrefix,
|
|
13
|
+
stripLocalePrefix,
|
|
14
|
+
} from './locale'
|
|
15
|
+
|
|
16
|
+
describe('locale constants', () => {
|
|
17
|
+
test('supports en + de with en as the default', () => {
|
|
18
|
+
expect(SUPPORTED_LOCALES).toEqual(['en', 'de'])
|
|
19
|
+
expect(DEFAULT_LOCALE).toBe('en')
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('getCatalog', () => {
|
|
24
|
+
test('returns the matching catalog for a supported locale', () => {
|
|
25
|
+
expect(getCatalog('de')['nav.releases']).toBe('Versionen')
|
|
26
|
+
expect(getCatalog('en')['nav.releases']).toBe('Releases')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('falls back to the default catalog for an unsupported locale', () => {
|
|
30
|
+
expect(getCatalog('zz')['nav.releases']).toBe('Releases')
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('localeFromPathname', () => {
|
|
35
|
+
test('extracts a prefixed locale', () => {
|
|
36
|
+
expect(localeFromPathname('/de')).toBe('de')
|
|
37
|
+
expect(localeFromPathname('/de/about')).toBe('de')
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('defaults to en for the bare path or an unknown prefix', () => {
|
|
41
|
+
expect(localeFromPathname('/')).toBe('en')
|
|
42
|
+
expect(localeFromPathname('/about')).toBe('en')
|
|
43
|
+
expect(localeFromPathname('/fr/about')).toBe('en')
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('withLocalePrefix', () => {
|
|
48
|
+
test('leaves default-locale paths bare', () => {
|
|
49
|
+
expect(withLocalePrefix('/about', 'en')).toBe('/about')
|
|
50
|
+
expect(withLocalePrefix('/', 'en')).toBe('/')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('prefixes non-default locales, mapping / to /<locale>', () => {
|
|
54
|
+
expect(withLocalePrefix('/about', 'de')).toBe('/de/about')
|
|
55
|
+
expect(withLocalePrefix('/', 'de')).toBe('/de')
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe('stripLocalePrefix', () => {
|
|
60
|
+
test('removes a locale prefix, mapping /<locale> back to /', () => {
|
|
61
|
+
expect(stripLocalePrefix('/de/about')).toBe('/about')
|
|
62
|
+
expect(stripLocalePrefix('/de')).toBe('/')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('leaves an unprefixed path unchanged', () => {
|
|
66
|
+
expect(stripLocalePrefix('/about')).toBe('/about')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('round-trips with withLocalePrefix', () => {
|
|
70
|
+
expect(stripLocalePrefix(withLocalePrefix('/about', 'de'))).toBe('/about')
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -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 pages + 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/about` → 'de'; `/about` → '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 /about and /de/about
|
|
40
|
+
// without a full reload.
|
|
41
|
+
export const useUrlLocale = (): Locale => localeFromPathname(useLocation())
|
|
42
|
+
|
|
43
|
+
// '/about' + 'de' → '/de/about'; the default locale stays bare ('/about').
|
|
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/about' → '/about'; '/about' → '/about'. The inverse of the above,
|
|
51
|
+
// used to keep the 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,21 @@
|
|
|
1
|
+
// The single source of releases for every page. The MDX files under
|
|
2
|
+
// content/releases/ are read at build time via Vite's raw glob (works in
|
|
3
|
+
// dev, SSR, and the static build). `import: 'default'` yields each file's
|
|
4
|
+
// contents as a string — exactly the shape `loadReleases()` expects. The
|
|
5
|
+
// loader parses the frontmatter, renders the markdown body, sorts newest-
|
|
6
|
+
// first, and skips any file missing `version` / `releasedAt`.
|
|
7
|
+
import { loadReleases, type Release } from '@voltro/changelog'
|
|
8
|
+
|
|
9
|
+
const sources = import.meta.glob('../../content/releases/*.mdx', {
|
|
10
|
+
query: '?raw',
|
|
11
|
+
import: 'default',
|
|
12
|
+
eager: true,
|
|
13
|
+
}) as Record<string, string>
|
|
14
|
+
|
|
15
|
+
/** All releases, newest first. */
|
|
16
|
+
export const releases: ReadonlyArray<Release> = loadReleases(sources)
|
|
17
|
+
|
|
18
|
+
export const releaseBySlug = (slug: string): Release | undefined =>
|
|
19
|
+
releases.find((r) => r.slug === slug)
|
|
20
|
+
|
|
21
|
+
export type { Release }
|
|
@@ -0,0 +1,22 @@
|
|
|
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.changelog': 'Changelog',
|
|
9
|
+
'nav.releases': 'Versionen',
|
|
10
|
+
'nav.rss': 'RSS',
|
|
11
|
+
'nav.language': 'Sprache',
|
|
12
|
+
|
|
13
|
+
'footer.builtWith': 'Gebaut mit @voltro/changelog',
|
|
14
|
+
|
|
15
|
+
'empty.body':
|
|
16
|
+
'Noch keine Releases — lege eine <code>.mdx</code>-Datei unter <code>content/releases/</code> mit <code>version</code>- und <code>releasedAt</code>-Frontmatter an.',
|
|
17
|
+
|
|
18
|
+
'post.backToList': '← Alle Versionen',
|
|
19
|
+
|
|
20
|
+
'meta.home.title': '{{capProjectName}} — Changelog',
|
|
21
|
+
'meta.home.description': 'Produkt-Updates und Release-Notes.',
|
|
22
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
// CHROME ONLY: the header brand/nav, the footer, the empty state + page meta
|
|
5
|
+
// live here. Release titles / summaries / bodies / dates / tags are CONTENT
|
|
6
|
+
// (the .mdx files under content/releases/) and are NOT translated.
|
|
7
|
+
//
|
|
8
|
+
// `<code>…</code>` in `empty.body` is react-intl rich text — the render site
|
|
9
|
+
// supplies a `code` tag function. Identifiers inside it (`.mdx`,
|
|
10
|
+
// `content/releases/`, `version`, `releasedAt`) are literal, kept in both
|
|
11
|
+
// locales. `{{capProjectName}}` is a scaffold token, only read from `meta`
|
|
12
|
+
// (plain property access, never parsed as ICU) — leave it verbatim.
|
|
13
|
+
import { defineCatalog } from '@voltro/i18n'
|
|
14
|
+
|
|
15
|
+
export default defineCatalog({
|
|
16
|
+
'nav.changelog': 'Changelog',
|
|
17
|
+
'nav.releases': 'Releases',
|
|
18
|
+
'nav.rss': 'RSS',
|
|
19
|
+
'nav.language': 'Language',
|
|
20
|
+
|
|
21
|
+
'footer.builtWith': 'Built with @voltro/changelog',
|
|
22
|
+
|
|
23
|
+
'empty.body':
|
|
24
|
+
'No releases yet — add an <code>.mdx</code> file under <code>content/releases/</code> with <code>version</code> + <code>releasedAt</code> frontmatter.',
|
|
25
|
+
|
|
26
|
+
'post.backToList': '← All releases',
|
|
27
|
+
|
|
28
|
+
'meta.home.title': '{{capProjectName}} — Changelog',
|
|
29
|
+
'meta.home.description': 'Product updates and release notes.',
|
|
30
|
+
} as const)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Locale mirror of `/[slug]` → renders `/de/<slug>`. Re-exports the bare
|
|
2
|
+
// release page verbatim; only getStaticPaths differs.
|
|
3
|
+
//
|
|
4
|
+
// This is a DYNAMIC route (`[slug]`) under a DYNAMIC locale segment, so the
|
|
5
|
+
// paths to pre-render are the CROSS-PRODUCT of every non-default locale ×
|
|
6
|
+
// every release slug (the bare page enumerates just the slugs; here each slug
|
|
7
|
+
// is emitted once per non-default locale), e.g. /de/v0-2-0.
|
|
8
|
+
//
|
|
9
|
+
// NOTE: `[locale]` and the bare `[slug]` are both single-segment dynamic
|
|
10
|
+
// routes at the root. `voltro build` only emits the paths getStaticPaths
|
|
11
|
+
// enumerates, so the static output is unambiguous — the bare `[slug]` never
|
|
12
|
+
// enumerates a locale code, and only this mirror emits `/de/…`.
|
|
13
|
+
import { SUPPORTED_LOCALES, DEFAULT_LOCALE } from '../../lib/locale'
|
|
14
|
+
import { releases } from '../../lib/releases'
|
|
15
|
+
|
|
16
|
+
export { default } from '../[slug]'
|
|
17
|
+
export { renderMode, interactive, loader, meta } from '../[slug]'
|
|
18
|
+
|
|
19
|
+
export const getStaticPaths = async (): Promise<
|
|
20
|
+
Array<{ params: { locale: string; slug: string } }>
|
|
21
|
+
> =>
|
|
22
|
+
SUPPORTED_LOCALES
|
|
23
|
+
.filter((l) => l !== DEFAULT_LOCALE)
|
|
24
|
+
.flatMap((locale) => releases.map((release) => ({ params: { locale, slug: release.slug } })))
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Locale mirror of `/` → renders `/de`. Re-exports the bare index verbatim
|
|
2
|
+
// (it reads its locale from the URL via the layout's provider); getStaticPaths
|
|
3
|
+
// tells `voltro build` which locale-prefixed paths to emit. The default locale
|
|
4
|
+
// is NOT listed — it lives at the bare `/`.
|
|
5
|
+
import { SUPPORTED_LOCALES, DEFAULT_LOCALE } from '../../lib/locale'
|
|
6
|
+
|
|
7
|
+
export { default } from '../index'
|
|
8
|
+
export { renderMode, interactive, meta } from '../index'
|
|
9
|
+
|
|
10
|
+
export const getStaticPaths = async (): Promise<Array<{ params: { locale: string } }>> =>
|
|
11
|
+
SUPPORTED_LOCALES
|
|
12
|
+
.filter((l) => l !== DEFAULT_LOCALE)
|
|
13
|
+
.map((locale) => ({ params: { locale } }))
|