@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,978 @@
|
|
|
1
|
+
# Reference
|
|
2
|
+
|
|
3
|
+
> The client-side hook surface, grouped by purpose.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
<!-- source: en/reference/hooks-overview.md -->
|
|
10
|
+
## React hooks — overview
|
|
11
|
+
|
|
12
|
+
_The client-side hook surface, grouped by purpose._
|
|
13
|
+
|
|
14
|
+
The web side of a Voltro app talks to API apps through React hooks. Data hooks are keyed by API name and RPC tag; routing hooks are provided by `@voltro/web`.
|
|
15
|
+
|
|
16
|
+
## Data Hooks
|
|
17
|
+
|
|
18
|
+
| Hook | Purpose |
|
|
19
|
+
|---|---|
|
|
20
|
+
| [`useSubscription`](/docs/reference/hooks-data) | Subscribe to a reactive query (`*.query.ts`). |
|
|
21
|
+
| [`useMutation`](/docs/reference/hooks-data) | Run an atomic write (`*.mutation.ts`). |
|
|
22
|
+
| [`useAction`](/docs/reference/hooks-data) | Run a unary non-transactional action (`*.action.ts`). |
|
|
23
|
+
| [`useWorkflow`](/docs/reference/hooks-data) | Start, cancel, resume, or signal a durable workflow (`*.workflow.tsx`). |
|
|
24
|
+
| [`useWorkflowRun`](/docs/reference/hooks-data) | Subscribe to one workflow run's reactive status row. |
|
|
25
|
+
| [`useWorkflowRuns`](/docs/reference/hooks-data) | Subscribe to a bounded/filterable workflow run list. |
|
|
26
|
+
| [`useWorkflowRunSteps`](/docs/reference/hooks-data) | Subscribe to one run's checkpointed step timeline. |
|
|
27
|
+
| [`useWorkflowRunEvents`](/docs/reference/hooks-data) | Subscribe to one run's lifecycle/timer/signal event timeline. |
|
|
28
|
+
| [`useAgentStream`](/docs/reference/hooks-data) | Consume a one-shot element stream (`*.stream.ts`). |
|
|
29
|
+
| [`useAgent`](/docs/reference/hooks-data) | Convenience wrapper for transient AI streams. |
|
|
30
|
+
|
|
31
|
+
## Routing Hooks
|
|
32
|
+
|
|
33
|
+
| Hook | Purpose |
|
|
34
|
+
|---|---|
|
|
35
|
+
| [`useLocation`](/docs/reference/hooks-routing) | Current pathname and route state. |
|
|
36
|
+
| [`useParams`](/docs/reference/hooks-routing) | URL params from `[name]` segments. |
|
|
37
|
+
| [`useNavigate`](/docs/reference/hooks-routing) | Programmatic navigation. |
|
|
38
|
+
| [`usePrefetch`](/docs/reference/hooks-routing) | Trigger loader-data prefetch. |
|
|
39
|
+
| [`useLoaderData`](/docs/reference/hooks-routing) | Page loader output. |
|
|
40
|
+
|
|
41
|
+
## Server / Context Hooks
|
|
42
|
+
|
|
43
|
+
| Hook | Purpose |
|
|
44
|
+
|---|---|
|
|
45
|
+
| [`useServerRequest`](/docs/reference/hooks-server) | Request snapshot during SSR and hydration-sensitive client code. |
|
|
46
|
+
|
|
47
|
+
## Where To Read Next
|
|
48
|
+
|
|
49
|
+
- [Data hooks](/docs/reference/hooks-data)
|
|
50
|
+
- [Routing hooks](/docs/reference/hooks-routing)
|
|
51
|
+
- [Server hooks](/docs/reference/hooks-server)
|
|
52
|
+
- [Runtime context](/docs/reference/runtime-context)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
<!-- source: en/reference/hooks-data.md -->
|
|
59
|
+
## Data hooks
|
|
60
|
+
|
|
61
|
+
_useSubscription, useMutation, useAction, useWorkflow, workflow status hooks, useAgentStream, and useAgent._
|
|
62
|
+
|
|
63
|
+
Data hooks from `@voltro/client` are keyed by **api name** plus **RPC tag**. The tags come from descriptors discovered in the API app.
|
|
64
|
+
|
|
65
|
+
## `useSubscription(apiName, rpcTag, input?, options?)`
|
|
66
|
+
|
|
67
|
+
Subscribes to a reactive `defineQuery` RPC.
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
const { data, error, revision, pendingPatches } = useSubscription(
|
|
71
|
+
'app',
|
|
72
|
+
'notes.list',
|
|
73
|
+
{ archived: false },
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
|
|
79
|
+
| Field | Meaning |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `data` | Latest typed query output, or `undefined` before the first snapshot. |
|
|
82
|
+
| `error` | Subscription setup error, if no snapshot could be delivered. |
|
|
83
|
+
| `revision` | Server revision counter. |
|
|
84
|
+
| `emittedAt` | Timestamp for the latest server delta. |
|
|
85
|
+
| `pendingPatches` | Number of active optimistic patches applied to this cache entry. |
|
|
86
|
+
|
|
87
|
+
Use `{ skip }` to defer until inputs are ready:
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
const { data } = useSubscription(
|
|
91
|
+
'app',
|
|
92
|
+
'messages.list',
|
|
93
|
+
{ channelId },
|
|
94
|
+
{ skip: channelId === undefined },
|
|
95
|
+
)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## `useMutation(apiName, rpcTag)`
|
|
99
|
+
|
|
100
|
+
Calls a `defineMutation` RPC.
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
const create = useMutation<{ title: string }, { id: string }>('app', 'notes.create')
|
|
104
|
+
|
|
105
|
+
await create.mutate({ title: 'Hello' })
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
|
|
110
|
+
| Field | Meaning |
|
|
111
|
+
|---|---|
|
|
112
|
+
| `mutate(input)` | Calls the mutation and resolves the typed output. |
|
|
113
|
+
| `pending` | `true` while a call is in flight. |
|
|
114
|
+
| `error` | Last failure, or `undefined`. |
|
|
115
|
+
| `data` | Last successful result, or `undefined`. |
|
|
116
|
+
| `withOptimistic(fn)` | Override descriptor-derived optimistic patches. |
|
|
117
|
+
| `withoutOptimistic()` | Disable optimistic patches for this mutation handle. |
|
|
118
|
+
|
|
119
|
+
Custom optimistic example:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
const create = useMutation('app', 'notes.create').withOptimistic((cache, input) => {
|
|
123
|
+
cache.forTag<ReadonlyArray<{ id: string; title: string }>>('notes.list', (rows) => [
|
|
124
|
+
{ id: `temp:${Date.now()}`, title: String(input.title) },
|
|
125
|
+
...rows,
|
|
126
|
+
])
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## `useAction(apiName, rpcTag)`
|
|
131
|
+
|
|
132
|
+
Calls a `defineAction` RPC. Actions are unary like mutations, but the client has no optimistic/cache surface for them.
|
|
133
|
+
|
|
134
|
+
```tsx
|
|
135
|
+
const invite = useAction<{ email: string }, { ok: boolean }>('app', 'invites.send')
|
|
136
|
+
|
|
137
|
+
await invite.run({ email })
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Returns `run`, `pending`, `error`, and `lastResult`.
|
|
141
|
+
|
|
142
|
+
## `useWorkflow(apiName, workflowName)`
|
|
143
|
+
|
|
144
|
+
Starts and controls a discovered `*.workflow.tsx`. Starting returns a `WorkflowRunHandle` immediately; it does not wait for the workflow's success payload.
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
const summarise = useWorkflow<{ noteId: string }>('app', 'notes.summarise')
|
|
148
|
+
|
|
149
|
+
const run = await summarise.start({ noteId })
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`voltro codegen` also emits workflow type maps from your `*.workflow.tsx`
|
|
153
|
+
definitions:
|
|
154
|
+
|
|
155
|
+
```tsx
|
|
156
|
+
import type {
|
|
157
|
+
WorkflowMessages,
|
|
158
|
+
WorkflowPayloads,
|
|
159
|
+
WorkflowResults,
|
|
160
|
+
} from '@app/api/rpcGroup.generated'
|
|
161
|
+
|
|
162
|
+
const summarise = useWorkflow<
|
|
163
|
+
WorkflowPayloads['notes.summarise'],
|
|
164
|
+
WorkflowMessages['notes.summarise']
|
|
165
|
+
>('app', 'notes.summarise')
|
|
166
|
+
|
|
167
|
+
type SummaryResult = WorkflowResults['notes.summarise']
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
|
|
172
|
+
| Field | Meaning |
|
|
173
|
+
|---|---|
|
|
174
|
+
| `start(payload)` | Starts the durable workflow and resolves a run handle. |
|
|
175
|
+
| `cancel({ workflowName, executionId })` | Cancels a running execution. |
|
|
176
|
+
| `resume({ workflowName, executionId })` | Resumes a suspended execution. |
|
|
177
|
+
| `signal({ id }, signalName, payload?)` | Sends an external signal to a run id or execution id. |
|
|
178
|
+
| `update({ id }, updateName, payload?, options?)` | Sends a tracked update and resolves with the workflow handler result. |
|
|
179
|
+
| `pending` | `true` while `start(...)` is in flight. |
|
|
180
|
+
| `error` | Last start failure, or `undefined`. |
|
|
181
|
+
| `data` | Last returned run handle, or `undefined`. |
|
|
182
|
+
|
|
183
|
+
## `useWorkflowSignal(apiName)`
|
|
184
|
+
|
|
185
|
+
Focused helper for signal buttons and approval forms that do not also start workflows:
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
const signal = useWorkflowSignal('app')
|
|
189
|
+
|
|
190
|
+
await signal.signal({ id: runId }, 'approval', { approved: true })
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
It returns `signal(...)`, `pending`, `error`, and the last `{ eventId }`.
|
|
194
|
+
|
|
195
|
+
## `useWorkflowUpdate(apiName)`
|
|
196
|
+
|
|
197
|
+
Focused helper for tracked workflow updates. Unlike signals, updates wait for the workflow's `awaitUpdate(...)` handler to validate and return a result:
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
const approve = useWorkflowUpdate('app')
|
|
201
|
+
|
|
202
|
+
const result = await approve.update(
|
|
203
|
+
{ id: runId },
|
|
204
|
+
'approve',
|
|
205
|
+
{ decision: true },
|
|
206
|
+
{ timeoutMs: 30_000 },
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
It returns `update(...)`, `pending`, `error`, and the last `{ eventId, updateId, completedEventId, result }`.
|
|
211
|
+
|
|
212
|
+
## `useWorkflowRun(apiName, id)`
|
|
213
|
+
|
|
214
|
+
Subscribes to Voltro's built-in reactive workflow-run query. `id` can be the durable `executionId` returned by `useWorkflow().start(...)` or the `_voltro_workflow_runs.id` from inspection data.
|
|
215
|
+
|
|
216
|
+
```tsx
|
|
217
|
+
const { run, data, error } = useWorkflowRun('app', runId)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
`run` is the first row from the built-in query. It updates through the normal reactivity engine as `_voltro_workflow_runs` changes, so workflow status UIs do not need polling.
|
|
221
|
+
|
|
222
|
+
## `useWorkflowRuns(apiName, filters?, options?)`
|
|
223
|
+
|
|
224
|
+
Subscribes to a bounded workflow-run list:
|
|
225
|
+
|
|
226
|
+
```tsx
|
|
227
|
+
const { runs } = useWorkflowRuns('app', {
|
|
228
|
+
tag: 'notes.summarise',
|
|
229
|
+
status: 'running',
|
|
230
|
+
limit: 25,
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Filters are optional. `limit` defaults to `100` and is capped by the server, so app-level job centers do not accidentally subscribe to the entire run history.
|
|
235
|
+
|
|
236
|
+
## `useWorkflowRunSteps(apiName, runId)`
|
|
237
|
+
|
|
238
|
+
Subscribes to the step timeline for one `_voltro_workflow_runs.id`:
|
|
239
|
+
|
|
240
|
+
```tsx
|
|
241
|
+
const { steps } = useWorkflowRunSteps('app', liveRun?.id)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Step rows update reactively as checkpointed `step({...})` activities start, succeed, or fail.
|
|
245
|
+
|
|
246
|
+
## `useWorkflowRunEvents(apiName, runId)`
|
|
247
|
+
|
|
248
|
+
Subscribes to the event timeline for one `_voltro_workflow_runs.id`:
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
const { events } = useWorkflowRunEvents('app', liveRun?.id)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Events include lifecycle changes, timers, signals, and updates recorded by the workflow runtime.
|
|
255
|
+
|
|
256
|
+
`useWorkflowEvents(apiName, runId)` is a shorter alias for the same hook.
|
|
257
|
+
|
|
258
|
+
## `useAgentStream(apiName, rpcTag)`
|
|
259
|
+
|
|
260
|
+
Consumes a `defineStream` RPC. Despite the name, this hook is not limited to AI agents; it handles any one-shot element stream.
|
|
261
|
+
|
|
262
|
+
```tsx
|
|
263
|
+
const ticker = useAgentStream<{ price: number }>('app', 'ticker.watch')
|
|
264
|
+
|
|
265
|
+
ticker.start({ symbol: 'BTC' })
|
|
266
|
+
ticker.cancel()
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
|
|
271
|
+
| Field | Meaning |
|
|
272
|
+
|---|---|
|
|
273
|
+
| `events` | Elements received so far, in order. |
|
|
274
|
+
| `status` | `'idle'`, `'streaming'`, `'done'`, or `'error'`. |
|
|
275
|
+
| `error` | Failure when `status === 'error'`. |
|
|
276
|
+
| `start(input?)` | Starts a new run and clears old events. |
|
|
277
|
+
| `cancel()` | Interrupts the in-flight run. |
|
|
278
|
+
|
|
279
|
+
## `useAgent(apiName, rpcTag)`
|
|
280
|
+
|
|
281
|
+
Ergonomic wrapper over `useAgentStream` for transient AI chat streams. It derives `tokens` from token events and folds completed turns into `history`.
|
|
282
|
+
|
|
283
|
+
```tsx
|
|
284
|
+
const support = useAgent('app', 'support.run')
|
|
285
|
+
|
|
286
|
+
support.send({ message: 'Help me', history: support.history })
|
|
287
|
+
support.cancel()
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
For durable chat generated by `defineAgent`, use the normal pair: `useSubscription('app', '<name>.messages', input)` plus `useAction('app', '<name>.send')`.
|
|
291
|
+
|
|
292
|
+
## Connection Lifecycle
|
|
293
|
+
|
|
294
|
+
All hooks share the API WebSocket. Query subscriptions resubscribe after reconnect and get fresh snapshots. In-flight unary calls reject on disconnect. In-flight streams end with an error and must be started again.
|
|
295
|
+
|
|
296
|
+
## See Also
|
|
297
|
+
|
|
298
|
+
- [Queries](/docs/data/queries)
|
|
299
|
+
- [Mutations](/docs/data/mutations)
|
|
300
|
+
- [Actions](/docs/data/actions)
|
|
301
|
+
- [Streams](/docs/data/streams)
|
|
302
|
+
- [Workflows](/docs/workflows/overview)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
<!-- source: en/reference/hooks-routing.md -->
|
|
309
|
+
## Routing hooks
|
|
310
|
+
|
|
311
|
+
_useLocation, useParams, useNavigate, usePrefetch, useLoaderData — navigating + reading URL state._
|
|
312
|
+
|
|
313
|
+
The routing hooks from `@voltro/web`. They read URL state, trigger navigation, and access loader data.
|
|
314
|
+
|
|
315
|
+
## `useLocation()`
|
|
316
|
+
|
|
317
|
+
The current pathname (just the path; query string is separate).
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { useLocation } from '@voltro/web'
|
|
321
|
+
|
|
322
|
+
const pathname = useLocation()
|
|
323
|
+
// '/docs/intro/getting-started'
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Updates on every navigation. Use for:
|
|
327
|
+
|
|
328
|
+
- Active-link styling
|
|
329
|
+
- "Did the URL change?" effect dependencies
|
|
330
|
+
- Conditional rendering based on path
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
const Nav = () => {
|
|
334
|
+
const pathname = useLocation()
|
|
335
|
+
return (
|
|
336
|
+
<ul>
|
|
337
|
+
<li><Link to="/" className={pathname === '/' ? 'active' : ''}>Home</Link></li>
|
|
338
|
+
<li><Link to="/about" className={pathname === '/about' ? 'active' : ''}>About</Link></li>
|
|
339
|
+
</ul>
|
|
340
|
+
)
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
For "active if URL starts with prefix":
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
className={pathname.startsWith('/dashboard') ? 'active' : ''}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## `useParams<T>()`
|
|
351
|
+
|
|
352
|
+
URL params from `[name]` segments. Typed via the generic.
|
|
353
|
+
|
|
354
|
+
```tsx
|
|
355
|
+
// src/pages/users/[id].tsx
|
|
356
|
+
import { useParams } from '@voltro/web'
|
|
357
|
+
|
|
358
|
+
const { id } = useParams<{ id: string }>()
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
For catch-all queries:
|
|
362
|
+
|
|
363
|
+
```tsx
|
|
364
|
+
// src/pages/docs/[...slug].tsx
|
|
365
|
+
const { slug } = useParams<{ slug: string }>()
|
|
366
|
+
// /docs/intro/getting-started → slug = "intro/getting-started"
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
For multi-segment dynamic paths:
|
|
370
|
+
|
|
371
|
+
```tsx
|
|
372
|
+
// src/pages/orgs/[orgId]/projects/[projectId].tsx
|
|
373
|
+
const { orgId, projectId } = useParams<{ orgId: string; projectId: string }>()
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
Params are always strings — convert numbers explicitly:
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
const id = Number(params.id)
|
|
380
|
+
if (Number.isNaN(id)) throw new NotFoundError()
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## `useNavigate()`
|
|
384
|
+
|
|
385
|
+
Programmatic navigation.
|
|
386
|
+
|
|
387
|
+
```tsx
|
|
388
|
+
import { useNavigate } from '@voltro/web'
|
|
389
|
+
|
|
390
|
+
const SignOutButton = () => {
|
|
391
|
+
const navigate = useNavigate()
|
|
392
|
+
const onSignOut = async () => {
|
|
393
|
+
await fetch('/auth/signout', { method: 'POST' })
|
|
394
|
+
navigate('/login')
|
|
395
|
+
}
|
|
396
|
+
return <button onClick={onSignOut}>Sign out</button>
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Returns a function `(to: string, options?) => void`.
|
|
401
|
+
|
|
402
|
+
| Option | Notes |
|
|
403
|
+
|---|---|
|
|
404
|
+
| `replace: true` | Replace the history entry (no back-button entry). |
|
|
405
|
+
| `scroll: false` | Don't scroll to top after navigation. |
|
|
406
|
+
|
|
407
|
+
`navigate` takes a path string only — there is no numeric history overload. For back / forward, reach for the browser API:
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
window.history.back() // back
|
|
411
|
+
window.history.forward() // forward
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## `useBlocker()`
|
|
415
|
+
|
|
416
|
+
Hold a pending navigation so you can prompt before the user leaves — the unsaved-changes guard.
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
import { useBlocker } from '@voltro/web'
|
|
420
|
+
|
|
421
|
+
const blocker = useBlocker(form.isDirty) // boolean or a predicate
|
|
422
|
+
// …
|
|
423
|
+
{blocker.blocked && (
|
|
424
|
+
<ConfirmDialog onConfirm={blocker.retry} onCancel={blocker.reset} />
|
|
425
|
+
)}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
Pass `true`/`false` or a predicate `({ to, opts }) => boolean` (to allow some destinations). When a navigation is held, the hook returns `{ blocked: true, to, retry, reset }`: `retry()` proceeds, `reset()` cancels. A full-page unload also triggers the browser's native prompt while any blocker is active. See [Navigation](/docs/routing/navigation) for the full example.
|
|
429
|
+
|
|
430
|
+
## `useSearchParams()` + `useSetSearchParams()`
|
|
431
|
+
|
|
432
|
+
Read the query string as a `URLSearchParams` (SSR-aware — the request URL on the server, `window.location.search` on the client):
|
|
433
|
+
|
|
434
|
+
```tsx
|
|
435
|
+
import { useSearchParams } from '@voltro/web'
|
|
436
|
+
|
|
437
|
+
const tab = useSearchParams().get('tab') ?? 'overview'
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
Write it with `useSetSearchParams()` — the setter updates the query via `navigate`, so readers re-render immediately:
|
|
441
|
+
|
|
442
|
+
```tsx
|
|
443
|
+
import { useSetSearchParams } from '@voltro/web'
|
|
444
|
+
|
|
445
|
+
const setParams = useSetSearchParams()
|
|
446
|
+
setParams({ tab: 'members' }) // set (default: replace)
|
|
447
|
+
setParams((p) => { p.set('page', '2'); return p }) // patch one param
|
|
448
|
+
setParams({ page: '2' }, { push: true }) // distinct history entry
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Writes default to a history replace; pass `{ push: true }` for a Back entry or `{ scroll: false }` to keep scroll. See [Navigation](/docs/routing/navigation#reading--writing-search-params).
|
|
452
|
+
|
|
453
|
+
## `usePrefetch()`
|
|
454
|
+
|
|
455
|
+
Trigger loader-data prefetch on hover / focus. Wired automatically by `<Link prefetch />`; export only useful for custom triggers.
|
|
456
|
+
|
|
457
|
+
```tsx
|
|
458
|
+
import { usePrefetch } from '@voltro/web'
|
|
459
|
+
|
|
460
|
+
const Card = ({ id }) => {
|
|
461
|
+
const prefetch = usePrefetch()
|
|
462
|
+
return (
|
|
463
|
+
<article onMouseEnter={() => prefetch(`/notes/${id}`)}>
|
|
464
|
+
{/* …card body, no Link inside */}
|
|
465
|
+
</article>
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Idempotent — multiple calls for the same path fire one loader. The prefetched result is held in the loader cache until it's consumed by the actual navigation (or invalidated on an error reset).
|
|
471
|
+
|
|
472
|
+
## `useLoaderData<T>()`
|
|
473
|
+
|
|
474
|
+
Page's loader output, typed.
|
|
475
|
+
|
|
476
|
+
```tsx
|
|
477
|
+
// src/pages/notes/[id].tsx
|
|
478
|
+
import { useLoaderData } from '@voltro/web'
|
|
479
|
+
|
|
480
|
+
interface Note {
|
|
481
|
+
id: string
|
|
482
|
+
title: string
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export const loader = async ({ params }): Promise<Note> => {
|
|
486
|
+
return await fetchNote(params.id)
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export default function NotePage(): ReactNode {
|
|
490
|
+
const note = useLoaderData<Note>()
|
|
491
|
+
return <article><h1>{note.title}</h1></article>
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
Available in:
|
|
496
|
+
|
|
497
|
+
- The page component itself
|
|
498
|
+
- Any layout in the page's chain
|
|
499
|
+
- Any descendant of the layout
|
|
500
|
+
|
|
501
|
+
Returns `null` on pages without a `loader`. The generic narrows the type.
|
|
502
|
+
|
|
503
|
+
See [Loaders & meta](/docs/routing/loaders-and-meta) for the server-side counterpart.
|
|
504
|
+
|
|
505
|
+
## Compositions
|
|
506
|
+
|
|
507
|
+
```tsx
|
|
508
|
+
// "go to next page after a delay"
|
|
509
|
+
const navigate = useNavigate()
|
|
510
|
+
useEffect(() => {
|
|
511
|
+
const t = setTimeout(() => navigate('/welcome'), 3000)
|
|
512
|
+
return () => clearTimeout(t)
|
|
513
|
+
}, [navigate])
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
```tsx
|
|
517
|
+
// "save the URL the user came from for after-signin redirect"
|
|
518
|
+
const pathname = useLocation()
|
|
519
|
+
const fromUrl = useMemo(() => pathname, []) // capture at mount, not every render
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
```tsx
|
|
523
|
+
// "active link with hover-prefetch"
|
|
524
|
+
const Link = ({ to, children }) => {
|
|
525
|
+
const pathname = useLocation()
|
|
526
|
+
const prefetch = usePrefetch()
|
|
527
|
+
const active = pathname === to
|
|
528
|
+
return (
|
|
529
|
+
<a
|
|
530
|
+
href={to}
|
|
531
|
+
onMouseEnter={() => prefetch(to)}
|
|
532
|
+
className={active ? 'active' : ''}
|
|
533
|
+
>
|
|
534
|
+
{children}
|
|
535
|
+
</a>
|
|
536
|
+
)
|
|
537
|
+
}
|
|
538
|
+
// (You don't usually write this — `<Link>` from `@voltro/web` does it all.)
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
## Anti-patterns
|
|
542
|
+
|
|
543
|
+
- **`window.location.href = '/foo'` for internal nav.** Full reload — defeats the SPA. Use `useNavigate`.
|
|
544
|
+
- **Reading `params` outside the page tree.** Layouts above the page CAN read params (they share the chain), but components imported as siblings can't. Pass params down explicitly.
|
|
545
|
+
- **`useEffect(() => navigate(...), [])` for default redirects.** Triggers a flash of the original page. Do redirects in the LOADER instead (`throw new RedirectError(...)`).
|
|
546
|
+
|
|
547
|
+
## See also
|
|
548
|
+
|
|
549
|
+
- [Navigation](/docs/routing/navigation) — `<Link>` + prefetch patterns
|
|
550
|
+
- [Loaders & meta](/docs/routing/loaders-and-meta) — what populates `useLoaderData`
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
---
|
|
555
|
+
|
|
556
|
+
<!-- source: en/reference/hooks-server.md -->
|
|
557
|
+
## Server hooks
|
|
558
|
+
|
|
559
|
+
_useServerRequest — the SSR-only escape hatch for reading cookies, headers, and the URL during render._
|
|
560
|
+
|
|
561
|
+
The only true "server hook" in Voltro is `useServerRequest`. It exposes the request snapshot during SSR so layouts + pages can read cookies, headers, and the URL during the render pass.
|
|
562
|
+
|
|
563
|
+
## `useServerRequest()`
|
|
564
|
+
|
|
565
|
+
```tsx
|
|
566
|
+
import { useServerRequest } from '@voltro/web'
|
|
567
|
+
|
|
568
|
+
const req = useServerRequest()
|
|
569
|
+
// req is { cookies, headers, url } on the server, null on the client
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
|
|
574
|
+
```ts
|
|
575
|
+
interface ServerRequest {
|
|
576
|
+
readonly cookies: Readonly<Record<string, string>>
|
|
577
|
+
readonly headers: Readonly<Record<string, string>>
|
|
578
|
+
readonly url: string // includes query string
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
Or `null` on the client (after hydration).
|
|
583
|
+
|
|
584
|
+
## What it's for
|
|
585
|
+
|
|
586
|
+
The classic use case: decode the session cookie during SSR so the layout renders the right subject-aware UI:
|
|
587
|
+
|
|
588
|
+
```tsx
|
|
589
|
+
// src/pages/layout.tsx
|
|
590
|
+
import { useServerRequest } from '@voltro/web'
|
|
591
|
+
import { SubjectProvider } from '@voltro/plugin-auth/web'
|
|
592
|
+
import { decodeSubjectFromRequest } from './lib/auth'
|
|
593
|
+
|
|
594
|
+
export default function Layout({ children }) {
|
|
595
|
+
const req = useServerRequest()
|
|
596
|
+
const subject = decodeSubjectFromRequest(req)
|
|
597
|
+
return <SubjectProvider subject={subject}>{children}</SubjectProvider>
|
|
598
|
+
}
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
`decodeSubjectFromRequest` is app code (it knows the cookie name + how to verify the signature). The hook just hands you the cookies.
|
|
602
|
+
|
|
603
|
+
## Reading query params
|
|
604
|
+
|
|
605
|
+
For SSR pages that need to parse the URL's query string:
|
|
606
|
+
|
|
607
|
+
```tsx
|
|
608
|
+
import { useSearchParams } from '@voltro/web'
|
|
609
|
+
|
|
610
|
+
export const renderMode = 'ssr' as const
|
|
611
|
+
|
|
612
|
+
export default function SearchPage() {
|
|
613
|
+
// Native `URLSearchParams`, resolved server-side from the SSR request
|
|
614
|
+
// URL (correct in the first paint) and from `window.location.search`
|
|
615
|
+
// on the client — same call site on both sides.
|
|
616
|
+
const q = useSearchParams().get('q') ?? ''
|
|
617
|
+
const results = q ? searchDocs(q) : []
|
|
618
|
+
return /* … */
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
`useSearchParams()` returns the request's query as a native `URLSearchParams`. SPA pages
|
|
623
|
+
that must react to router-pushed query changes without a reload re-render through the router
|
|
624
|
+
(`useNavigate`/`useLocation`); the hook then re-resolves on that render.
|
|
625
|
+
|
|
626
|
+
## Reading cookies
|
|
627
|
+
|
|
628
|
+
```ts
|
|
629
|
+
const req = useServerRequest()
|
|
630
|
+
const sessionCookie = req?.cookies['voltro:session']
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
`cookies` is already parsed — no manual `Cookie:` header splitting needed.
|
|
634
|
+
|
|
635
|
+
## Reading headers
|
|
636
|
+
|
|
637
|
+
```ts
|
|
638
|
+
const req = useServerRequest()
|
|
639
|
+
const userAgent = req?.headers['user-agent']
|
|
640
|
+
const tenant = req?.headers['x-tenant']
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
Headers are lower-cased keys. Multi-value headers are joined with `, `.
|
|
644
|
+
|
|
645
|
+
## Conditional rendering by SSR vs client
|
|
646
|
+
|
|
647
|
+
```tsx
|
|
648
|
+
const req = useServerRequest()
|
|
649
|
+
if (req) {
|
|
650
|
+
// SSR — req available
|
|
651
|
+
} else {
|
|
652
|
+
// Post-hydration — read browser-side equivalents
|
|
653
|
+
const ua = navigator.userAgent
|
|
654
|
+
}
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
For values that need to flow from SSR to client (cookie-derived subject, locale), serialise them via `SubjectProvider`-style context. The client doesn't re-read cookies — it inherits what the SSR pass decoded.
|
|
658
|
+
|
|
659
|
+
## What it's NOT for
|
|
660
|
+
|
|
661
|
+
- **Reading database state.** Use a `loader` — it runs server-side too, but with a typed `params` + abort signal + a clear server/client divide.
|
|
662
|
+
- **Triggering side effects during render.** React's render must be pure. For SSR side effects (logging a request), use middleware in `app.config.ts.runtime`.
|
|
663
|
+
- **Client-side cookie reading.** The session cookie is `HttpOnly` — JS can't read it. The server decoded it during SSR + handed you the subject; that's what the client sees.
|
|
664
|
+
|
|
665
|
+
## Mounting check pattern
|
|
666
|
+
|
|
667
|
+
```tsx
|
|
668
|
+
const req = useServerRequest()
|
|
669
|
+
const isSSR = req !== null
|
|
670
|
+
|
|
671
|
+
return (
|
|
672
|
+
<div>
|
|
673
|
+
{isSSR ? <ServerOnlyBranch /> : <ClientOnlyBranch />}
|
|
674
|
+
</div>
|
|
675
|
+
)
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
Use sparingly — divergent SSR / client rendering causes hydration mismatches. The cleaner pattern is `useEffect(() => setHydrated(true), [])` + render the same JSX in both paths.
|
|
679
|
+
|
|
680
|
+
## See also
|
|
681
|
+
|
|
682
|
+
- [Loaders & meta](/docs/routing/loaders-and-meta) — server-side data fetch (preferred for data)
|
|
683
|
+
- [Authentication / React](/docs/authentication/react) — `SubjectProvider` pattern
|
|
684
|
+
- [Wire protocol](/docs/data/wire-protocol) — the server's view of the request
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
<!-- source: en/reference/runtime-context.md -->
|
|
691
|
+
## Runtime context
|
|
692
|
+
|
|
693
|
+
_The `AppContext` passed to server executors._
|
|
694
|
+
|
|
695
|
+
Every server executor receives an `AppContext` as its second argument:
|
|
696
|
+
|
|
697
|
+
```ts
|
|
698
|
+
import type { AppContext } from '@voltro/runtime'
|
|
699
|
+
|
|
700
|
+
export default async (input: Input, ctx: AppContext) => {
|
|
701
|
+
const subject = ctx.request.subject
|
|
702
|
+
const rows = await ctx.store.query(/* ... */)
|
|
703
|
+
}
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
Query, mutation, action, stream, and workflow files export both descriptor metadata and the default executor. The default executor receives `ctx`; descriptor objects stay data-only and browser-safe.
|
|
707
|
+
|
|
708
|
+
## `ctx.request`
|
|
709
|
+
|
|
710
|
+
Per-call runtime metadata:
|
|
711
|
+
|
|
712
|
+
```ts
|
|
713
|
+
ctx.request.subject // user, apiKey, system, or anonymous subject
|
|
714
|
+
ctx.request.traceId // trace id shared with client/server spans
|
|
715
|
+
ctx.request.spanId // current span id when tracing is active
|
|
716
|
+
ctx.request.clientId // WebSocket connection id for RPC calls
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
Most auth and tenant checks read `ctx.request.subject`:
|
|
720
|
+
|
|
721
|
+
```ts
|
|
722
|
+
const tenantId = ctx.request.subject.tenantId
|
|
723
|
+
if (ctx.request.subject.id == null) throw new Error('sign-in required')
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
## `ctx.store`
|
|
727
|
+
|
|
728
|
+
The typed mutation store. It applies schema mixin behavior for `audit()`, `tenant()`, and `softDelete()` where configured.
|
|
729
|
+
|
|
730
|
+
```ts
|
|
731
|
+
await ctx.store.insert('notes', { title: 'Hello' })
|
|
732
|
+
await ctx.store.update('notes', noteId, { title: 'Updated' })
|
|
733
|
+
await ctx.store.delete('notes', noteId)
|
|
734
|
+
|
|
735
|
+
const rows = await ctx.store.query(database.notes.descriptor)
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Mutations receive a transactional store view. Actions and streams receive a normal store view; writes from them are not automatically rolled back as one unit.
|
|
739
|
+
|
|
740
|
+
## `ctx.cache`
|
|
741
|
+
|
|
742
|
+
Async cache facade for request handlers:
|
|
743
|
+
|
|
744
|
+
```ts
|
|
745
|
+
const value = await ctx.cache.wrap(
|
|
746
|
+
`summary:${id}`,
|
|
747
|
+
{ ttlMs: 60_000, tags: ['notes'] },
|
|
748
|
+
() => computeSummary(id),
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
await ctx.cache.invalidateTag('notes')
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
Mutation `target` metadata can invalidate matching tagged cache entries automatically.
|
|
755
|
+
|
|
756
|
+
## `ctx.kv`
|
|
757
|
+
|
|
758
|
+
Durable key-value facade — always present, and unlike `ctx.cache` its entries are **never evicted for capacity** (they live until deleted or their TTL lapses). Use it for state you can't recompute; the default backend on a sql app is the database, so values survive restarts.
|
|
759
|
+
|
|
760
|
+
```ts
|
|
761
|
+
const state = await ctx.kv.getOrElse(`onboarding:${userId}`, () => ({ step: 0 }))
|
|
762
|
+
await ctx.kv.set(`onboarding:${userId}`, { step: state.step + 1 })
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
`get` / `getOrElse` / `set` (optional `{ ttlMs }`) / `delete` / `has` / `list(prefix)` / `clear`. See **[Durable key-value](/docs/caching/key-value)**.
|
|
766
|
+
|
|
767
|
+
## `ctx.webhooks`
|
|
768
|
+
|
|
769
|
+
Present only when the webhooks plugin is configured. Plugin-specific packages expose typed helpers for their optional context slots; the core runtime keeps the slot structurally typed so apps do not pay for unused plugins.
|
|
770
|
+
|
|
771
|
+
## `ctx.workflows`
|
|
772
|
+
|
|
773
|
+
Present when the API app has discovered workflows. It starts durable work and controls existing runs:
|
|
774
|
+
|
|
775
|
+
```ts
|
|
776
|
+
const run = await ctx.workflows.start('notes.summarise', { noteId })
|
|
777
|
+
await ctx.workflows.signal({ id: run.id }, 'approval', { approved: true })
|
|
778
|
+
const approval = await ctx.workflows.update({ id: run.id }, 'approve', { decision: true })
|
|
779
|
+
const latest = await ctx.workflows.query('notes.summarise', run.executionId)
|
|
780
|
+
const snapshot = await ctx.workflows.wait('notes.summarise', run.executionId)
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
Use `signal(...)` for fire-and-forget external events. Use `update(...)` when the caller needs a tracked result from the workflow's `awaitUpdate(...)` handler.
|
|
784
|
+
|
|
785
|
+
Inside a mutation, `ctx.workflows.start(...)` queues the actual launch until after the transaction commits. If the mutation rolls back, the workflow is not started. `ctx.workflows.run(...)` and `start(..., { wait: true })` are intentionally rejected inside mutation transactions.
|
|
786
|
+
|
|
787
|
+
Workflow starts record their start source (`workflow-rpc`, `app-context`, `schedule:<name>`, `incoming:<id>`, or `inspect`). Request-backed starts also carry the current request trace, and authenticated request starts carry the resolved subject into the run context. Keep authorization-critical tenant/user ids in the workflow payload so resumed work stays deterministic.
|
|
788
|
+
|
|
789
|
+
## Effect Services
|
|
790
|
+
|
|
791
|
+
Server executors may return `Effect`s. Actions and streams receive the base platform layer, including `HttpClient`. AI helpers are imported from `@voltro/ai`:
|
|
792
|
+
|
|
793
|
+
```ts
|
|
794
|
+
import { generateText } from '@voltro/ai'
|
|
795
|
+
import { Effect } from 'effect'
|
|
796
|
+
|
|
797
|
+
export default (input: { prompt: string }) =>
|
|
798
|
+
Effect.gen(function* () {
|
|
799
|
+
const { text } = yield* generateText({ prompt: input.prompt })
|
|
800
|
+
return { text }
|
|
801
|
+
})
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
## Context By Primitive
|
|
805
|
+
|
|
806
|
+
| Server file | Transaction | Typical store usage | External I/O | Returns |
|
|
807
|
+
|---|---|---|---|---|
|
|
808
|
+
| `*.query.server.ts` | No | Read/query | Avoid | Query descriptor or computed value |
|
|
809
|
+
| `*.mutation.server.ts` | Yes | Atomic writes | Avoid | Unary output |
|
|
810
|
+
| `*.action.server.ts` | No | Optional, non-atomic | Yes | Unary output |
|
|
811
|
+
| `*.stream.server.ts` | No | Optional, non-atomic | Yes | `Stream`, `Effect<Stream>`, or `Promise<Stream>` |
|
|
812
|
+
| `*.workflow.tsx` | Step-specific | Durable reads/writes | Yes, inside activities | Workflow result |
|
|
813
|
+
|
|
814
|
+
Use mutations for atomic state changes, actions for one-shot side effects, streams for progressive element output, and workflows for durable multi-step work.
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
---
|
|
819
|
+
|
|
820
|
+
<!-- source: en/reference/startup.md -->
|
|
821
|
+
## Startup hooks
|
|
822
|
+
|
|
823
|
+
_"*.startup.tsx — run code once when the app boots: warm a cache, open a long-lived connection, start a background consumer. Default-export a function; register teardown via onShutdown."_
|
|
824
|
+
|
|
825
|
+
A **startup hook** runs code once when the app boots — after migrations, once the rpc server is listening. Use it for the long-lived, app-singleton work that doesn't fit any request-driven primitive: warm a cache, open a persistent connection (a message-broker consumer, a websocket to an upstream), start a background interval, or register a process-wide service.
|
|
826
|
+
|
|
827
|
+
A `*.startup.tsx` (or `*.startup.ts`) file **default-exports a function** — there's no `define*` wrapper:
|
|
828
|
+
|
|
829
|
+
```tsx
|
|
830
|
+
// startup/warmCache.startup.ts
|
|
831
|
+
import type { StartupContext } from '@voltro/runtime'
|
|
832
|
+
|
|
833
|
+
export default async ({ store, log, onShutdown, id }: StartupContext) => {
|
|
834
|
+
log.info('warming the dashboard cache')
|
|
835
|
+
const timer = setInterval(() => void refreshDashboardCache(store), 60_000)
|
|
836
|
+
|
|
837
|
+
// Register teardown — runs on SIGTERM / SIGINT, LIFO across all startups.
|
|
838
|
+
onShutdown(() => clearInterval(timer))
|
|
839
|
+
}
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
Discovery walks every `*.startup.tsx`; the `default` export must be a function. Multiple startups in one app are fine — each gets its own scope and is torn down independently.
|
|
843
|
+
|
|
844
|
+
## The context
|
|
845
|
+
|
|
846
|
+
```ts
|
|
847
|
+
interface StartupContext {
|
|
848
|
+
readonly store: DataStore // already-migrated
|
|
849
|
+
readonly log: SyncLogger // scope=startup:<id>
|
|
850
|
+
readonly onShutdown: (cb: () => void | Promise<void>) => void // register teardown
|
|
851
|
+
readonly id: string // basename without .startup.tsx
|
|
852
|
+
}
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
- **`store`** is the same framework `DataStore` handlers use — already migrated by the time the hook runs.
|
|
856
|
+
- **`log`** is scoped to the file's basename, so `voltro logs --filter startup:<id>` isolates a hook's output.
|
|
857
|
+
- **`onShutdown(cb)`** registers a teardown callback. On `SIGTERM` / `SIGINT` the framework runs every registered callback in **reverse** order (LIFO), awaiting each — with a hard 5s timeout per callback so a hung teardown can't block shutdown. Always release what you acquire here.
|
|
858
|
+
- **`id`** is the stable basename (no `.startup.tsx`), handy for keys / log scoping.
|
|
859
|
+
|
|
860
|
+
## Lifecycle
|
|
861
|
+
|
|
862
|
+
- **Runs once, at boot** — after the schema is migrated and the rpc server is listening. A throw/rejection is logged and does NOT block boot; the rpc surface stays up regardless.
|
|
863
|
+
- **Long-lived** — the function may hold sockets, intervals, or a fiber that resolves only on shutdown. That's the point — startups are for persistent process-wide work, unlike [seeds](/docs/database/seeds) which run once and return.
|
|
864
|
+
- **Torn down cleanly** — every `onShutdown` callback fires on process exit (LIFO), so connections and timers release without leaking across supervisor restarts.
|
|
865
|
+
|
|
866
|
+
## When to use it vs the alternatives
|
|
867
|
+
|
|
868
|
+
| Need | Use |
|
|
869
|
+
|---|---|
|
|
870
|
+
| Seed rows once at boot, then return | [`*.seed.ts`](/docs/database/seeds) |
|
|
871
|
+
| Hold a long-lived connection / interval / background consumer for the process lifetime | `*.startup.tsx` (this) |
|
|
872
|
+
| React to every commit on a table | [`*.subscribe.ts`](/docs/data/subscribers) |
|
|
873
|
+
| Periodic work on a cron schedule | [`*.cron.tsx`](/docs/scheduling/overview) |
|
|
874
|
+
| Durable, crash-surviving multi-step work | [`*.workflow.tsx`](/docs/workflows/overview) |
|
|
875
|
+
|
|
876
|
+
If the work is "do X once and finish", it's a seed. If it's "keep X running until the process stops", it's a startup.
|
|
877
|
+
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
---
|
|
881
|
+
|
|
882
|
+
<!-- source: en/reference/templates.md -->
|
|
883
|
+
## App templates
|
|
884
|
+
|
|
885
|
+
_The scaffolding catalogue — every api / web / serverless template, what it demonstrates, and when to pick it._
|
|
886
|
+
|
|
887
|
+
Every template is a dogfooded, runnable reference. Scaffold one with
|
|
888
|
+
`voltro create-project --api <id> --web <id>` (or `voltro add-app <name>
|
|
889
|
+
--template <id>`), and run `voltro list-templates` for the live list.
|
|
890
|
+
|
|
891
|
+
Templates come in **three kinds**, matching the three things you deploy:
|
|
892
|
+
|
|
893
|
+
- **`api`** — a long-running backend (`app.config` `type: 'api'`).
|
|
894
|
+
- **`web`** — a frontend (`type: 'web'`); gets a dev-server port.
|
|
895
|
+
- **`serverless`** — a bundle of standalone [`*.serverless.ts`
|
|
896
|
+
functions](/docs/deployment/serverless-functions) shipped on their own with
|
|
897
|
+
`voltro serverless`. No long-running server, no port — add it to a project
|
|
898
|
+
with `voltro add-app`.
|
|
899
|
+
|
|
900
|
+
## API backends (`kind: api`)
|
|
901
|
+
|
|
902
|
+
| Template | What you get |
|
|
903
|
+
|---|---|
|
|
904
|
+
| `api-backend` | The minimal base: `app.config` + a `*.entity.ts` schema + one streaming query + one mutation. Tenant-aware out of the box. Start here. |
|
|
905
|
+
| `api-backend-mail` | `api-backend` + [`@voltro/plugin-mail`](/docs/plugins/mail) and a React-Email welcome template you preview/send from the dashboard. |
|
|
906
|
+
| `api-backend-storage` | `api-backend` + [`@voltro/plugin-storage`](/docs/plugins/storage): public (CDN-direct) + private (policy + per-object grants) objects, with upload examples. |
|
|
907
|
+
| `api-backend-mariadb` | MariaDB-backed: binlog CDC real-time, file storage, tenant-aware schema — the shape for K8s multi-replica apps. |
|
|
908
|
+
| [`api-durable`](/docs/templates/api-durable) | The whole durable + reactive surface in one order-fulfillment domain: a workflow (`step`/`sleep`/`awaitSignal`), an event trigger, a cron schedule, a table subscriber, a materialized aggregate, and a startup hook. |
|
|
909
|
+
| [`api-ai`](/docs/templates/api-ai) | A RAG support agent: a `vectorEmbedding()` docs table, a search tool, a real `defineAgent`/`defineAgentExecutor` model loop, and a `generateObject` action. |
|
|
910
|
+
| [`api-data-advanced`](/docs/templates/api-data-advanced) | The advanced schema DSL: `*.entity.ts`/`*.relations.ts` split + eager `.with()`, full-text search, `dbEnum`, array/generated/encrypted columns, and declarative query caching. |
|
|
911
|
+
| [`api-auth`](/docs/templates/api-auth) | Real user auth — `@voltro/plugin-auth` turnkey: password sign-up/in/out over HttpOnly session cookies + a strategy resolving the session to a typed Subject. Zero-infra boot. |
|
|
912
|
+
| [`api-rest`](/docs/templates/api-rest) | Public REST API — `defineRestRoute` (query/path/body, scope guards, Idempotency-Key) + `@voltro/plugin-openapi` (OpenAPI 3.1 spec + Swagger UI at `/docs`). Zero-infra boot. |
|
|
913
|
+
| [`api-saas`](/docs/templates/api-saas) | The SaaS plugin bundle — billing entitlements + notifications + analytics + presence, wired turnkey; one `projects.create` exercises three together. Zero-infra boot. |
|
|
914
|
+
| [`api-observability`](/docs/templates/api-observability) | Production-readiness — Prometheus `/metrics` + a custom counter, Sentry (inert without a DSN), tracing, and a `@voltro/testing` unit test (`voltro test`). Zero-infra boot. |
|
|
915
|
+
| [`api-webhooks`](/docs/templates/api-webhooks) | First-class webhooks both ways — a signature-verified incoming `*.webhook.tsx` receiver + an outgoing `defineOutgoingEvent` emitted via a durable signed delivery workflow. Zero-infra boot. |
|
|
916
|
+
|
|
917
|
+
## Web frontends (`kind: web`)
|
|
918
|
+
|
|
919
|
+
| Template | Render mode | What it demonstrates |
|
|
920
|
+
|---|---|---|
|
|
921
|
+
| `frontend-blank` | — | Empty React + layout shell + one page. The blank canvas. |
|
|
922
|
+
| [`frontend-app`](/docs/templates/app) | `full` · reactive | **The reactive end-to-end loop** — a web frontend wired to an api (`useSubscription` + `useMutation` + auto-optimistic). The only fullstack template; pairs with `api-backend`. |
|
|
923
|
+
| `frontend-landing` | `static` · `interactive: 'none'` | Marketing page that ships **zero JS** on the wire. |
|
|
924
|
+
| `frontend-static-blog` | `static` + `islands` | **SSG from a content source**: dynamic `[slug]` routes pre-rendered via `getStaticPaths`, per-post `meta` from loader data, and ONE island (reading-progress bar) showing selective hydration. |
|
|
925
|
+
| `frontend-spa` | `spa` | A **pure client-rendered** tool — state lives in the browser (`localStorage`), so there's nothing to SSR. Shows where a single-page app is the right call. |
|
|
926
|
+
| `frontend-ssr` | `ssr` + `isr` | **Server-rendered pages**: a per-request SSR page (`useServerRequest()` for cookies/headers) + ISR pages (`revalidate`, `staleWhileRevalidate`, tenant-aware caching). Needs a runtime (`voltro start`), not a CDN. Self-contained; swap the loader for `ctx.query` to read from your api. |
|
|
927
|
+
| `frontend-contact` | `static` + serverless | **Static page + a serverless email form** (see combos below). |
|
|
928
|
+
| `frontend-docs` | `static` (catch-all SSG) | Docs site: `docs/[...slug].tsx`, per-section layout, URL-prefix i18n. |
|
|
929
|
+
| `changelog` | `static` | Release-notes site: MDX in `content/releases/`, rendered list + per-release pages, RSS feed. |
|
|
930
|
+
|
|
931
|
+
## Serverless functions (`kind: serverless`)
|
|
932
|
+
|
|
933
|
+
| Template | What you get |
|
|
934
|
+
|---|---|
|
|
935
|
+
| `edge-functions` | A **library of `*.serverless.ts` functions** covering the range of what edge functions do — pure compute, request-header/geo, outbound HTTP (fire-and-forget + fetch-and-transform), Web Crypto HMAC verify, an LLM call, and status-controlled errors. Run with `voltro serverless dev`, ship to node / Cloudflare / Scaleway. |
|
|
936
|
+
|
|
937
|
+
## Static + serverless combos
|
|
938
|
+
|
|
939
|
+
The most common shape for a no-always-on-server product: **a static page on a
|
|
940
|
+
CDN + a serverless function for the one dynamic bit.** Two templates show it:
|
|
941
|
+
|
|
942
|
+
- **`frontend-contact`** — a static marketing page whose contact form (an
|
|
943
|
+
island) POSTs to `functions/sendMessage.serverless.ts`, which sends mail via
|
|
944
|
+
Resend. The page ships to a CDN; the function scales to zero. The function's
|
|
945
|
+
local dev server sends CORS headers (same as the edge hosts), so the
|
|
946
|
+
cross-origin form works out of the box.
|
|
947
|
+
- **`edge-functions` + any static web template** — add the function library to
|
|
948
|
+
a project that also has a `frontend-static-blog` or `frontend-landing`, and
|
|
949
|
+
wire the page to whichever function it needs.
|
|
950
|
+
|
|
951
|
+
```bash
|
|
952
|
+
# Scaffold the combo:
|
|
953
|
+
voltro create-project acme --web frontend-contact --no-input
|
|
954
|
+
# → apps/acme/contact/ (static page + functions/sendMessage.serverless.ts)
|
|
955
|
+
|
|
956
|
+
# Run both halves locally (two terminals):
|
|
957
|
+
pnpm --filter @acme/contact dev # the static site
|
|
958
|
+
pnpm --filter @acme/contact fn:dev # the serverless function on :8910
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
## Picking a template
|
|
962
|
+
|
|
963
|
+
| You want… | Start with |
|
|
964
|
+
|---|---|
|
|
965
|
+
| A reactive backend (queries/mutations/workflows) | `api-backend` (+ `-mail` / `-storage` for those features) |
|
|
966
|
+
| A marketing / brochure site | `frontend-landing` |
|
|
967
|
+
| A blog or content site generated at build time | `frontend-static-blog` |
|
|
968
|
+
| A heavily-interactive tool with no backend | `frontend-spa` |
|
|
969
|
+
| Server-rendered pages (per-request SSR, or cached + revalidated ISR) | `frontend-ssr` |
|
|
970
|
+
| A static site that needs ONE dynamic endpoint (email, webhook, …) | `frontend-contact` |
|
|
971
|
+
| Docs | `frontend-docs` |
|
|
972
|
+
| Standalone functions to deploy to the edge | `edge-functions` |
|
|
973
|
+
|
|
974
|
+
## See also
|
|
975
|
+
|
|
976
|
+
- [Scaffolding](/docs/cli/scaffolding) — the `create-project` / `add-app` / `list-templates` commands + writing your own template
|
|
977
|
+
- [Serverless functions](/docs/deployment/serverless-functions) — the `*.serverless.ts` model + deploy targets
|
|
978
|
+
- [Static-site deploy](/docs/deployment/static-sites) — shipping a web template's `dist/` to a CDN
|