@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,331 @@
|
|
|
1
|
+
# database.scaling
|
|
2
|
+
|
|
3
|
+
> Transparent read-replica routing with read-your-writes (RYW) consistency, multi-instance RYW via Redis, region-aware replica selection, and the per-dialect adapter surface.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<!-- source: en/database/read-replicas.md -->
|
|
10
|
+
## Read replicas
|
|
11
|
+
|
|
12
|
+
_Transparent read-replica routing with read-your-writes (RYW) consistency, multi-instance RYW via Redis, region-aware replica selection, and the per-dialect adapter surface._
|
|
13
|
+
|
|
14
|
+
The framework's `DataStore` can wrap a primary connection + a list
|
|
15
|
+
of read replicas behind one transparent surface. Writes go to the
|
|
16
|
+
primary. Reads query to a replica chosen by power-of-two-choices,
|
|
17
|
+
EXCEPT when the same subject just wrote — RYW (read-your-writes)
|
|
18
|
+
pins their next reads to the primary for a TTL window so they
|
|
19
|
+
never see stale data after their own changes.
|
|
20
|
+
|
|
21
|
+
This is **completely opt-in**. Without `DB_REPLICA_URLS` set, the
|
|
22
|
+
framework returns the primary store directly — no `ReplicatedDataStore`
|
|
23
|
+
wrapper, no per-call routing overhead, no cost.
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
Add replica URLs:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
DB_PRIMARY_URL=postgresql://app:app@primary.db.acme.com/app
|
|
31
|
+
DB_REPLICA_URLS=postgresql://app:app@replica.db.acme.com/app
|
|
32
|
+
voltro start
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Verify in the boot log:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
[voltro:dev] read replicas: 1 configured, ryw policy 'fallback', dialect=postgres, region=unknown
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
That's it. Reads now flow to the replica unless RYW pins them back.
|
|
42
|
+
Writes always go to primary.
|
|
43
|
+
|
|
44
|
+
## Read-your-writes (RYW)
|
|
45
|
+
|
|
46
|
+
After a mutation commits, the subject who wrote is **pinned to
|
|
47
|
+
primary** for the next 30 seconds (configurable). This guarantees
|
|
48
|
+
they see their own change on the next read, even if the replica
|
|
49
|
+
hasn't caught up yet.
|
|
50
|
+
|
|
51
|
+
How it's implemented under the hood per dialect:
|
|
52
|
+
|
|
53
|
+
| Dialect | Capture surface | Probe surface |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| Postgres | `pg_current_wal_lsn()` | `pg_last_wal_replay_lsn()` |
|
|
56
|
+
| MySQL / MariaDB | `@@global.gtid_executed` (MySQL) / `@@global.gtid_current_pos` (MariaDB) | same, on the replica |
|
|
57
|
+
| MSSQL | `end_of_log_lsn` from `sys.dm_hadr_database_replica_states` (AG only) | `last_hardened_lsn` from the same DMV |
|
|
58
|
+
| SQLite | n/a (single-process — replicas not applicable) |
|
|
59
|
+
|
|
60
|
+
The framework calls `capturePrimaryPosition()` immediately after
|
|
61
|
+
every successful write, stores it keyed by the subject, and consults
|
|
62
|
+
the cache on every subsequent read by that subject. No `position` →
|
|
63
|
+
fall through to the replica selector.
|
|
64
|
+
|
|
65
|
+
### RYW policies
|
|
66
|
+
|
|
67
|
+
Three modes via `RYW_POLICY` env or `rywPolicy:` in config:
|
|
68
|
+
|
|
69
|
+
| Policy | Behaviour |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `fallback` (default) | Pin to primary for the TTL window. Simple, low-tail-latency, no replica wait. |
|
|
72
|
+
| `wait` | Poll the replica until it catches up (max `rywMaxWaitMs`). Higher consistency at cost of latency. |
|
|
73
|
+
| `off` | No RYW — every read goes to a replica regardless of write history. For analytics workloads where staleness is OK. |
|
|
74
|
+
|
|
75
|
+
For most apps, `fallback` is correct: it prevents the "I just edited
|
|
76
|
+
this, why is it gone" UX without paying the replica-poll cost. Reach
|
|
77
|
+
for `wait` only when stronger consistency outweighs latency.
|
|
78
|
+
|
|
79
|
+
## Replica selection
|
|
80
|
+
|
|
81
|
+
By default the framework uses **power-of-two-choices** (P2C): pick
|
|
82
|
+
two replicas at random, send the read to the one with fewer
|
|
83
|
+
in-flight queries. Standard load-balancer trick — robust under skew,
|
|
84
|
+
no global LRU/LFU state needed.
|
|
85
|
+
|
|
86
|
+
### Region-aware selection (multi-region)
|
|
87
|
+
|
|
88
|
+
When replicas span regions, set `DB_REPLICA_REGIONS` to a
|
|
89
|
+
comma-separated list aligned with `DB_REPLICA_URLS`:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
DB_REPLICA_URLS=postgres://repl-use1.acme/,postgres://repl-euw1.acme/
|
|
93
|
+
DB_REPLICA_REGIONS=us-east-1,eu-west-1
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The framework auto-detects the current process's region from these
|
|
97
|
+
env vars (in priority order):
|
|
98
|
+
|
|
99
|
+
1. `VOLTRO_REGION` — explicit override
|
|
100
|
+
2. `AWS_REGION` — AWS ECS / Lambda / EC2
|
|
101
|
+
3. `FLY_REGION` — Fly.io
|
|
102
|
+
4. `RAILWAY_REGION` — Railway
|
|
103
|
+
5. `GCP_REGION` / `CLOUD_RUN_REGION` — Google Cloud
|
|
104
|
+
|
|
105
|
+
The selector then wraps P2C with a locality preference: **same-region
|
|
106
|
+
replicas are tried first**, other-region replicas only when
|
|
107
|
+
same-region is empty or unavailable. Boot log confirms:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
[voltro:dev] read replicas: 2 configured, ryw policy 'fallback', dialect=postgres, region=us-east-1, replica-locality=on
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
On a wrong-region read, you pay 80-150ms cross-region latency. On a
|
|
114
|
+
same-region read, 1-2ms. The selector does its best to keep you in
|
|
115
|
+
the second bucket.
|
|
116
|
+
|
|
117
|
+
If `DB_REPLICA_REGIONS` count mismatches `DB_REPLICA_URLS` count,
|
|
118
|
+
the framework warns + disables locality (degrades to plain P2C
|
|
119
|
+
across all replicas — correct but slower).
|
|
120
|
+
|
|
121
|
+
## Multi-instance RYW: Redis
|
|
122
|
+
|
|
123
|
+
The default RYW store is a per-process `Map`. Multi-instance
|
|
124
|
+
deployments (k8s with `replicas: 3`, multi-pod ECS, etc.) need a
|
|
125
|
+
shared store, otherwise a write on instance A doesn't pin reads on
|
|
126
|
+
instance B.
|
|
127
|
+
|
|
128
|
+
Wire Redis:
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
RYW_STORE=redis REDIS_URL=redis://host:6379
|
|
132
|
+
voltro start
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The Redis store keeps a local mirror per instance, kept warm via
|
|
136
|
+
pub/sub — reads stay sync (`Map.get`), writes propagate to peers
|
|
137
|
+
within one Redis round-trip. Sub-millisecond on localhost, 1-2ms
|
|
138
|
+
across a VPC.
|
|
139
|
+
|
|
140
|
+
What you get:
|
|
141
|
+
|
|
142
|
+
- Cross-instance coherent RYW: A's write pins B's reads for the TTL.
|
|
143
|
+
- Server-side TTL via PSETEX — entries expire automatically even
|
|
144
|
+
when no `get` triggers lazy eviction.
|
|
145
|
+
- Self-publish loopback filtering: an instance ignores its own pub/sub
|
|
146
|
+
broadcasts to avoid double-applying.
|
|
147
|
+
|
|
148
|
+
What you don't get:
|
|
149
|
+
|
|
150
|
+
- Strong consistency. Between the moment A commits + the moment B
|
|
151
|
+
receives the pub/sub broadcast, B's reads could still hit a
|
|
152
|
+
stale replica. The window is one Redis round-trip — small but
|
|
153
|
+
nonzero. For strict-consistency workloads use `RYW_POLICY=wait`
|
|
154
|
+
on top of Redis store.
|
|
155
|
+
|
|
156
|
+
## Performance
|
|
157
|
+
|
|
158
|
+
Routing overhead is sub-microsecond per call. The micro-bench in
|
|
159
|
+
`packages/runtime/src/replicatedDataStore.perf.test.ts` measures
|
|
160
|
+
~10µs per routing decision including the AsyncLocalStorage context
|
|
161
|
+
push/pop. The actual SQL query dominates wall-clock by orders of
|
|
162
|
+
magnitude.
|
|
163
|
+
|
|
164
|
+
If you see latency regressions, run that test in your fork:
|
|
165
|
+
|
|
166
|
+
```sh
|
|
167
|
+
pnpm -F @voltro/runtime test -- replicatedDataStore.perf
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
It logs the per-call cost and fails if you've accidentally turned
|
|
171
|
+
the routing path into an O(n²) walk or added an unnecessary
|
|
172
|
+
allocation per query.
|
|
173
|
+
|
|
174
|
+
## Dialect support matrix
|
|
175
|
+
|
|
176
|
+
| Dialect | Routing | RYW | Region-aware | E2E validated |
|
|
177
|
+
|---|---|---|---|---|
|
|
178
|
+
| Postgres | ✓ | streaming-replication LSN | ✓ | docker-compose (Phase 4 / C10) |
|
|
179
|
+
| MySQL 8+ | ✓ | GTID | ✓ | docker-compose (Phase 5) |
|
|
180
|
+
| MariaDB 10.6+ | ✓ | GTID (via mysql adapter) | ✓ | shares MySQL adapter |
|
|
181
|
+
| MSSQL 2019+ | ✓ AG only | AG LSN | ✓ | adapter validated, AG cluster setup is operator-managed |
|
|
182
|
+
| SQLite | n/a | n/a | n/a | single-process, no replication possible |
|
|
183
|
+
|
|
184
|
+
MSSQL routing requires Always-On Availability Group cluster setup
|
|
185
|
+
(certs + endpoints + listener + Pacemaker on Linux OR Windows
|
|
186
|
+
Failover Clustering). That's a deployment-grade infrastructure
|
|
187
|
+
setup, not a framework concern; the adapter validates against any
|
|
188
|
+
SQL Server but real replica routing needs AG. Without AG, both
|
|
189
|
+
capture + probe return the zero-LSN sentinel and the framework
|
|
190
|
+
gracefully degrades to "treat every read like a primary read"
|
|
191
|
+
(safe, just slower than it could be).
|
|
192
|
+
|
|
193
|
+
## Anti-patterns
|
|
194
|
+
|
|
195
|
+
- **Don't run `RYW_STORE=redis` without `REDIS_URL`** — the framework
|
|
196
|
+
warns + falls back to memory store. Multi-instance deployments
|
|
197
|
+
silently lose RYW coherence.
|
|
198
|
+
- **Don't set `DB_REPLICA_REGIONS` without `DB_REPLICA_URLS`** — the
|
|
199
|
+
region map is paired by order with the URL list. Without URLs the
|
|
200
|
+
region list has nothing to bind to and is ignored.
|
|
201
|
+
- **Don't use `wait` policy with high write-rate subjects** — every
|
|
202
|
+
read after a write polls the replica with a sub-second timeout.
|
|
203
|
+
The tail latency is bounded by `rywMaxWaitMs`, but for hot subjects
|
|
204
|
+
this can starve.
|
|
205
|
+
- **Don't manually pin reads to a specific replica from handler code.**
|
|
206
|
+
The framework's routing layer is the single decision point — if you
|
|
207
|
+
bypass it, you lose RYW correctness + observability counters.
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
<!-- source: en/database/multi-replica.md -->
|
|
214
|
+
## Multi-replica reactivity
|
|
215
|
+
|
|
216
|
+
_Cross-replica change fan-out behind a load balancer — native LISTEN/NOTIFY (postgres) and binlog CDC (mysql + mariadb), plus @voltro/plugin-broadcast (Redis / NATS) to close the gap for every other dialect._
|
|
217
|
+
|
|
218
|
+
Behind a load balancer, each live client WebSocket lives on exactly **one** replica. Replica A holds clients 1–100, replica B holds 101–200, and so on. A mutation runs on whichever replica received the request, and inline-emits the change to **that replica's** local subscribers. The other replicas never learn of the write — so their clients go stale until they refetch.
|
|
219
|
+
|
|
220
|
+
This page is about closing that gap: making a write on **any** replica reach the live subscriptions on **every** replica.
|
|
221
|
+
|
|
222
|
+
## The three tiers
|
|
223
|
+
|
|
224
|
+
How a change crosses replica boundaries depends on the dialect:
|
|
225
|
+
|
|
226
|
+
| Tier | Dialects | Mechanism | Cross-replica? |
|
|
227
|
+
|---|---|---|---|
|
|
228
|
+
| Native DB fan-out | postgres | `LISTEN/NOTIFY` — the database IS the bus | ✓ |
|
|
229
|
+
| Native DB fan-out | mysql · mariadb | binlog CDC (ROW format) — every replica tails the binlog | ✓ |
|
|
230
|
+
| Pub/sub bus | mssql · cockroachdb · planetscale · azure-sql | `@voltro/plugin-broadcast` (Redis / NATS) | ✓ (when wired) |
|
|
231
|
+
| Single-instance | sqlite · `memory` | in-process only | n/a (single process by design) |
|
|
232
|
+
|
|
233
|
+
Postgres (`LISTEN/NOTIFY`) and MySQL/MariaDB (ROW-format binlog CDC) have a native cross-instance path: every replica learns of a write directly from the database. For **the other SQL dialects there is no native cross-instance path** — reactivity silently degrades to single-instance. `@voltro/plugin-broadcast` closes that with a pub/sub message bus. (PlanetScale is MySQL-based but Vitess doesn't expose a raw binlog to external readers, so it stays on the bus tier.)
|
|
234
|
+
|
|
235
|
+
## @voltro/plugin-broadcast
|
|
236
|
+
|
|
237
|
+
A first-class, provider-pluggable plugin that fans out app-mutation change events to every replica over a pub/sub broker. Two backends ship: **Redis** and **NATS**.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
// app.config.ts
|
|
241
|
+
import { broadcastPlugin } from '@voltro/plugin-broadcast'
|
|
242
|
+
|
|
243
|
+
export default {
|
|
244
|
+
type: 'api' as const,
|
|
245
|
+
name: 'api',
|
|
246
|
+
plugins: [
|
|
247
|
+
broadcastPlugin(), // reads BROADCAST_URL / BROADCAST_PROVIDER / REDIS_URL from env
|
|
248
|
+
],
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
```sh
|
|
253
|
+
# Redis (RESP pub/sub)
|
|
254
|
+
BROADCAST_URL=redis://localhost:6379
|
|
255
|
+
|
|
256
|
+
# or NATS
|
|
257
|
+
BROADCAST_URL=nats://localhost:4222
|
|
258
|
+
|
|
259
|
+
# explicit provider override (otherwise inferred from the URL scheme)
|
|
260
|
+
BROADCAST_PROVIDER=redis
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
If `BROADCAST_URL` is unset the plugin falls back to an in-process **memory** provider — useful in tests, but **single-process**: a real cross-replica bus needs a broker URL. The boot banner warns when this happens.
|
|
264
|
+
|
|
265
|
+
You can also pass a pre-built provider or an explicit name:
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
import { broadcastPlugin, redisProvider } from '@voltro/plugin-broadcast'
|
|
269
|
+
|
|
270
|
+
broadcastPlugin({ provider: redisProvider({ url: process.env.REDIS_URL! }) })
|
|
271
|
+
broadcastPlugin({ provider: 'nats', url: 'nats://nats:4222' })
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
`ioredis` and `nats` are **optional dependencies** — installed only for the backend you use. The plugin dynamically imports the driver, so an app on `memory` never pulls either.
|
|
275
|
+
|
|
276
|
+
## Mechanism — additive, not a replacement
|
|
277
|
+
|
|
278
|
+
The bus is **additive** to the inline emit path. Local reactivity must survive a broker outage:
|
|
279
|
+
|
|
280
|
+
1. A local change (via `store.onChange`) publishes `{ origin: replicaId, event }` to the channel `voltro:changes`.
|
|
281
|
+
2. Every replica subscribes. On a message whose `origin` is **not** this replica, it injects the change event into the local store emitter — the same `injectExternalChange` seam the postgres `LISTEN/NOTIFY` consumer uses.
|
|
282
|
+
3. The writer's **own** broadcast is skipped (it already inline-emitted locally) — so there is no double-emit and no dedup table.
|
|
283
|
+
|
|
284
|
+
Because the inline path is never removed, a broker outage degrades **cross-replica** fan-out only — local reactivity keeps working, and the framework logs a warning. The bus reconnects when the broker returns.
|
|
285
|
+
|
|
286
|
+
## The honest caveat — app-mutation changes only
|
|
287
|
+
|
|
288
|
+
The bus carries changes that flow through **`ctx.store`** (the framework's mutation path). It does **not** capture **out-of-band DB writes** — a `psql` session, a cron job, or a second service writing the same database directly. Those changes never hit `store.onChange`, so they never reach the bus.
|
|
289
|
+
|
|
290
|
+
Only two mechanisms observe out-of-band writes:
|
|
291
|
+
|
|
292
|
+
- **postgres `LISTEN/NOTIFY`** — the DB itself fires on any committed change (the framework's triggers fan out every write, regardless of who made it).
|
|
293
|
+
- **mysql / mariadb binlog CDC** — the ROW-format binlog records every committed row change; both engines share one reader.
|
|
294
|
+
|
|
295
|
+
If your app shares its database with other writers and needs them to drive reactivity, choose postgres, mysql, or mariadb. If all writes go through the Voltro app (the common case), the broadcast bus is the right tool for non-native dialects.
|
|
296
|
+
|
|
297
|
+
## What about polling a changelog table?
|
|
298
|
+
|
|
299
|
+
**Polling is not built.** It's documented here only as the absolute last resort.
|
|
300
|
+
|
|
301
|
+
A changelog table that every replica polls (`SELECT … WHERE seq > :last`) would observe out-of-band writes on any dialect — but at a cost the pub/sub bus avoids entirely: ~150 ms latency (poll interval) vs ~1 ms, constant DB load from every replica on every tick, and a changelog table to vacuum. Pub/sub beats it on every axis, is dialect-agnostic, and Voltro already runs Redis pub/sub for read-your-writes consistency. If you genuinely need out-of-band-write reactivity on a dialect that can't do `LISTEN/NOTIFY` or binlog, that's the signal to move to postgres — not to bolt on polling.
|
|
302
|
+
|
|
303
|
+
## Boot banner
|
|
304
|
+
|
|
305
|
+
`voltro dev` / `voltro start` prints the resolved reactivity tier so `voltro logs --tail 50` answers "how do writes cross replicas here" without reading source:
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
[voltro:dev] reactivity: cross-instance via native LISTEN/NOTIFY (postgres)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
[voltro:dev] reactivity: cross-instance via @voltro/plugin-broadcast (redis) for dialect 'planetscale'
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
```
|
|
316
|
+
[voltro:dev] reactivity: cross-instance fan-out is OFF for dialect 'mssql'. A write on one replica
|
|
317
|
+
will NOT reach clients on other replicas. Add @voltro/plugin-broadcast (Redis / NATS) to close the gap…
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
When BOTH a native path and the broadcast plugin are wired (e.g. postgres + broadcast), both stay active — the own-origin skip dedups, so there's no double-emit. The native path is the primary; the bus is harmless redundancy.
|
|
321
|
+
|
|
322
|
+
## Where it lives in the codebase
|
|
323
|
+
|
|
324
|
+
- `voltro/packages/plugin-broadcast` — the plugin, the `BroadcastProvider` contract, the redis / nats / memory providers, `attachBroadcastBus`.
|
|
325
|
+
- `voltro/packages/database/src/dataStore.ts` — `DataStore.injectExternalChange` (the cross-instance seam).
|
|
326
|
+
- `voltro/packages/cli/src/dev.ts` `wireBroadcastBus()` — tier selection + boot banner.
|
|
327
|
+
|
|
328
|
+
## See also
|
|
329
|
+
|
|
330
|
+
- [SQL dialects](./dialects) — the per-dialect parity table (CDC row).
|
|
331
|
+
- [Read replicas](./read-replicas) — read routing + read-your-writes consistency (a different axis: which replica serves a *read*, not how a *write* fans out).
|