@voltro/cli 0.27.0 → 0.29.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 +513 -0
- package/THIRD-PARTY-NOTICES.md +88 -2
- package/dist/{apiBuild-D22_EpoR.js → apiBuild-BESZbTjA.js} +4 -4
- package/dist/apiBuild-C8d74T6g.js +2 -0
- package/dist/bin.js +3 -3
- package/dist/checkCommand-BPAtyWs8.js +2420 -0
- package/dist/checkCommand-DBvZvCh1.js +2 -0
- package/dist/cliRuntime-Oh517vCV.js +96 -0
- package/dist/{commands-jBX8no1I.js → commands-82BDEktg.js} +6476 -7672
- package/dist/dbCommand-CaIVTp-a.js +2 -0
- package/dist/{dbCommand-uuNCrFAb.js → dbCommand-bCBbk6tz.js} +3 -3
- package/dist/{dev-DcbIJrWg.js → dev-Cg4BKLmi.js} +3034 -2619
- package/dist/dev-DeEJj5T4.js +3 -0
- package/dist/fileConventions-Cof68_BL.js +33 -0
- package/dist/{frameworkTableAssembly-BwHU9Euq.js → frameworkTableAssembly-4Db40V23.js} +2 -2
- package/dist/frameworkTableAssembly-BTNirAAk.js +2 -0
- package/dist/index.js +1 -1
- package/dist/{inspect-CUCCzw2I.js → inspect-BMvQpwqy.js} +211 -190
- package/dist/inspect-DHeaMZak.js +2 -0
- package/dist/{inspectMetrics-BU90mvJN.js → inspectMetrics-DHYygE4s.js} +999 -961
- package/dist/{manifestBuild-BnzAxp2O.js → manifestBuild-C4R1EdHn.js} +1 -1
- package/dist/manifestBuild-Ck_90gZy.js +2 -0
- package/dist/sdkgen-CW5NbYF0.js +582 -0
- package/dist/{seedRunner-D6eu-u5U.js → seedRunner-Bqxgp7HZ.js} +60 -59
- package/dist/serveCommand-BXl8mhZD.js +1489 -0
- package/dist/serveEntry.js +3 -3
- package/dist/{start-BGXIf6zT.js → start-CsCIaW4-.js} +282 -269
- package/dist/startEntry.js +2 -2
- package/package.json +17 -17
- package/templates/AGENTS.md +2 -1
- package/templates/agent-docs/_index.md +2 -1
- package/templates/agent-docs/_manifest.json +10 -1
- package/templates/agent-docs/ai.md +135 -0
- package/templates/agent-docs/cli.md +124 -6
- package/templates/agent-docs/configuration.md +32 -0
- package/templates/agent-docs/data.md +282 -0
- package/templates/agent-docs/database/migrations.md +47 -0
- package/templates/agent-docs/database/misc.md +57 -0
- package/templates/agent-docs/database/schema.md +3 -1
- package/templates/agent-docs/deployment.md +56 -0
- package/templates/agent-docs/internationalization.md +161 -1
- package/templates/agent-docs/local-first-mobile.md +414 -0
- package/templates/agent-docs/routing.md +93 -2
- package/templates/agent-docs/schema-driven-ui.md +12 -0
- package/templates/agent-docs/templates/apibackends.md +1 -1
- package/templates/agent-docs/testing.md +42 -0
- package/templates/agent-docs/whats-new.md +165 -131
- package/templates/agent-docs/workflows.md +11 -0
- package/templates/apps/api-ai/package.json +7 -7
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-sqlite/.env.example +19 -0
- package/templates/apps/api-backend-sqlite/README.md +38 -0
- package/templates/apps/api-backend-sqlite/app.config.ts +35 -0
- package/templates/apps/api-backend-sqlite/database/schema.ts +54 -0
- package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.server.ts +19 -0
- package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.ts +37 -0
- package/templates/apps/api-backend-sqlite/package.json +30 -0
- package/templates/apps/api-backend-sqlite/queries/notes.query.server.ts +14 -0
- package/templates/apps/api-backend-sqlite/queries/notes.query.ts +20 -0
- package/templates/apps/api-backend-sqlite/template.json +6 -0
- package/templates/apps/api-backend-sqlite/tests/notes.create.test.ts +50 -0
- package/templates/apps/api-backend-sqlite/tsconfig.json +5 -0
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-cms/README.md +104 -0
- package/templates/apps/api-cms/actions/content.get.action.server.ts +27 -0
- package/templates/apps/api-cms/actions/content.get.action.ts +19 -0
- package/templates/apps/api-cms/actions/content.types.action.server.ts +26 -0
- package/templates/apps/api-cms/actions/content.types.action.ts +40 -0
- package/templates/apps/api-cms/actions/me.action.server.ts +18 -0
- package/templates/apps/api-cms/actions/me.action.ts +16 -0
- package/templates/apps/api-cms/app.config.ts +61 -0
- package/templates/apps/api-cms/content/blogPost.contentType.ts +39 -0
- package/templates/apps/api-cms/content/index.ts +18 -0
- package/templates/apps/api-cms/content/page.contentType.ts +24 -0
- package/templates/apps/api-cms/database/schema.ts +64 -0
- package/templates/apps/api-cms/mutations/content.publish.mutation.server.ts +19 -0
- package/templates/apps/api-cms/mutations/content.publish.mutation.ts +15 -0
- package/templates/apps/api-cms/mutations/content.saveDraft.mutation.server.ts +36 -0
- package/templates/apps/api-cms/mutations/content.saveDraft.mutation.ts +32 -0
- package/templates/apps/api-cms/mutations/content.unpublish.mutation.server.ts +19 -0
- package/templates/apps/api-cms/mutations/content.unpublish.mutation.ts +11 -0
- package/templates/apps/api-cms/package.json +32 -0
- package/templates/apps/api-cms/queries/content.list.query.server.ts +0 -0
- package/templates/apps/api-cms/queries/content.list.query.ts +27 -0
- package/templates/apps/api-cms/template.json +6 -0
- package/templates/apps/api-cms/tests/content.descriptors.test.ts +64 -0
- package/templates/apps/api-cms/tests/content.write.test.ts +85 -0
- package/templates/apps/api-cms/tsconfig.json +5 -0
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-saas-starter/README.md +103 -0
- package/templates/apps/api-saas-starter/actions/me.action.server.ts +18 -0
- package/templates/apps/api-saas-starter/actions/me.action.ts +20 -0
- package/templates/apps/api-saas-starter/app.config.ts +87 -0
- package/templates/apps/api-saas-starter/database/schema.ts +57 -0
- package/templates/apps/api-saas-starter/mutations/invites.create.mutation.server.ts +26 -0
- package/templates/apps/api-saas-starter/mutations/invites.create.mutation.ts +18 -0
- package/templates/apps/api-saas-starter/mutations/projects.create.mutation.server.ts +29 -0
- package/templates/apps/api-saas-starter/mutations/projects.create.mutation.ts +18 -0
- package/templates/apps/api-saas-starter/package.json +32 -0
- package/templates/apps/api-saas-starter/queries/invites.list.query.server.ts +14 -0
- package/templates/apps/api-saas-starter/queries/invites.list.query.ts +17 -0
- package/templates/apps/api-saas-starter/queries/projects.list.query.server.ts +14 -0
- package/templates/apps/api-saas-starter/queries/projects.list.query.ts +18 -0
- package/templates/apps/api-saas-starter/template.json +6 -0
- package/templates/apps/api-saas-starter/tests/projects.create.test.ts +62 -0
- package/templates/apps/api-saas-starter/tests/session.test.ts +33 -0
- package/templates/apps/api-saas-starter/tsconfig.json +5 -0
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-status/README.md +88 -0
- package/templates/apps/api-status/app.config.ts +36 -0
- package/templates/apps/api-status/authz.ts +33 -0
- package/templates/apps/api-status/database/schema.ts +70 -0
- package/templates/apps/api-status/mutations/components.create.mutation.server.ts +19 -0
- package/templates/apps/api-status/mutations/components.create.mutation.ts +16 -0
- package/templates/apps/api-status/mutations/incidents.create.mutation.server.ts +25 -0
- package/templates/apps/api-status/mutations/incidents.create.mutation.ts +22 -0
- package/templates/apps/api-status/mutations/incidents.resolve.mutation.server.ts +24 -0
- package/templates/apps/api-status/mutations/incidents.resolve.mutation.ts +19 -0
- package/templates/apps/api-status/mutations/incidents.update.mutation.server.ts +26 -0
- package/templates/apps/api-status/mutations/incidents.update.mutation.ts +21 -0
- package/templates/apps/api-status/package.json +30 -0
- package/templates/apps/api-status/queries/components.list.query.server.ts +14 -0
- package/templates/apps/api-status/queries/components.list.query.ts +13 -0
- package/templates/apps/api-status/queries/incidents.live.query.server.ts +14 -0
- package/templates/apps/api-status/queries/incidents.live.query.ts +20 -0
- package/templates/apps/api-status/queries/updates.list.query.server.ts +14 -0
- package/templates/apps/api-status/queries/updates.list.query.ts +17 -0
- package/templates/apps/api-status/template.json +6 -0
- package/templates/apps/api-status/tests/status.test.ts +70 -0
- package/templates/apps/api-status/tsconfig.json +5 -0
- package/templates/apps/api-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +9 -9
- package/templates/apps/changelog/package.json +6 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +8 -8
- package/templates/apps/frontend-auth/README.md +78 -0
- package/templates/apps/frontend-auth/app.config.ts +34 -0
- package/templates/apps/frontend-auth/package.json +32 -0
- package/templates/apps/frontend-auth/src/components/AuthShell.tsx +35 -0
- package/templates/apps/frontend-auth/src/components/PasswordStrength.tsx +33 -0
- package/templates/apps/frontend-auth/src/config.ts +11 -0
- package/templates/apps/frontend-auth/src/globals.css +105 -0
- package/templates/apps/frontend-auth/src/globals.d.ts +6 -0
- package/templates/apps/frontend-auth/src/lib/auth.ts +34 -0
- package/templates/apps/frontend-auth/src/lib/redirect.test.ts +24 -0
- package/templates/apps/frontend-auth/src/lib/redirect.ts +29 -0
- package/templates/apps/frontend-auth/src/locales/de.ts +66 -0
- package/templates/apps/frontend-auth/src/locales/en.ts +76 -0
- package/templates/apps/frontend-auth/src/locales/index.ts +14 -0
- package/templates/apps/frontend-auth/src/pages/forgot/page.tsx +51 -0
- package/templates/apps/frontend-auth/src/pages/layout.tsx +12 -0
- package/templates/apps/frontend-auth/src/pages/login/page.test.tsx +53 -0
- package/templates/apps/frontend-auth/src/pages/login/page.tsx +67 -0
- package/templates/apps/frontend-auth/src/pages/logout/page.tsx +28 -0
- package/templates/apps/frontend-auth/src/pages/magic/page.tsx +51 -0
- package/templates/apps/frontend-auth/src/pages/page.tsx +26 -0
- package/templates/apps/frontend-auth/src/pages/reset/page.test.tsx +51 -0
- package/templates/apps/frontend-auth/src/pages/reset/page.tsx +63 -0
- package/templates/apps/frontend-auth/src/pages/signup/page.tsx +60 -0
- package/templates/apps/frontend-auth/src/pages/verify/page.tsx +50 -0
- package/templates/apps/frontend-auth/template.json +6 -0
- package/templates/apps/frontend-auth/tsconfig.json +5 -0
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-cms/README.md +47 -0
- package/templates/apps/frontend-cms/app.config.ts +37 -0
- package/templates/apps/frontend-cms/package.json +33 -0
- package/templates/apps/frontend-cms/src/config.ts +8 -0
- package/templates/apps/frontend-cms/src/globals.css +105 -0
- package/templates/apps/frontend-cms/src/globals.d.ts +6 -0
- package/templates/apps/frontend-cms/src/lib/api.ts +58 -0
- package/templates/apps/frontend-cms/src/locales/de.ts +50 -0
- package/templates/apps/frontend-cms/src/locales/en.ts +55 -0
- package/templates/apps/frontend-cms/src/locales/index.ts +14 -0
- package/templates/apps/frontend-cms/src/pages/(app)/error.tsx +18 -0
- package/templates/apps/frontend-cms/src/pages/(app)/layout.test.tsx +31 -0
- package/templates/apps/frontend-cms/src/pages/(app)/layout.tsx +62 -0
- package/templates/apps/frontend-cms/src/pages/(app)/not-found.tsx +14 -0
- package/templates/apps/frontend-cms/src/pages/(app)/page.test.tsx +110 -0
- package/templates/apps/frontend-cms/src/pages/(app)/page.tsx +159 -0
- package/templates/apps/frontend-cms/src/pages/layout.tsx +12 -0
- package/templates/apps/frontend-cms/src/pages/login/page.test.tsx +58 -0
- package/templates/apps/frontend-cms/src/pages/login/page.tsx +93 -0
- package/templates/apps/frontend-cms/template.json +6 -0
- package/templates/apps/frontend-cms/tsconfig.json +5 -0
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/package.json +7 -7
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-portal/README.md +71 -0
- package/templates/apps/frontend-portal/app.config.ts +37 -0
- package/templates/apps/frontend-portal/package.json +32 -0
- package/templates/apps/frontend-portal/src/config.ts +8 -0
- package/templates/apps/frontend-portal/src/globals.css +93 -0
- package/templates/apps/frontend-portal/src/globals.d.ts +6 -0
- package/templates/apps/frontend-portal/src/lib/api.ts +62 -0
- package/templates/apps/frontend-portal/src/locales/de.ts +86 -0
- package/templates/apps/frontend-portal/src/locales/en.ts +93 -0
- package/templates/apps/frontend-portal/src/locales/index.ts +14 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/api-keys/page.test.tsx +71 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/api-keys/page.tsx +124 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/billing/page.test.tsx +86 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/billing/page.tsx +97 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/error.tsx +19 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/invoices/page.tsx +45 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/layout.test.tsx +31 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/layout.tsx +63 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/not-found.tsx +15 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/page.tsx +39 -0
- package/templates/apps/frontend-portal/src/pages/(portal)/profile/page.tsx +79 -0
- package/templates/apps/frontend-portal/src/pages/layout.tsx +12 -0
- package/templates/apps/frontend-portal/src/pages/login/page.test.tsx +58 -0
- package/templates/apps/frontend-portal/src/pages/login/page.tsx +96 -0
- package/templates/apps/frontend-portal/template.json +6 -0
- package/templates/apps/frontend-portal/tsconfig.json +5 -0
- package/templates/apps/frontend-saas/README.md +73 -0
- package/templates/apps/frontend-saas/app.config.ts +46 -0
- package/templates/apps/frontend-saas/package.json +32 -0
- package/templates/apps/frontend-saas/src/config.ts +8 -0
- package/templates/apps/frontend-saas/src/globals.css +85 -0
- package/templates/apps/frontend-saas/src/globals.d.ts +6 -0
- package/templates/apps/frontend-saas/src/lib/api.ts +47 -0
- package/templates/apps/frontend-saas/src/locales/de.ts +72 -0
- package/templates/apps/frontend-saas/src/locales/en.ts +81 -0
- package/templates/apps/frontend-saas/src/locales/index.ts +14 -0
- package/templates/apps/frontend-saas/src/pages/(marketing)/layout.tsx +30 -0
- package/templates/apps/frontend-saas/src/pages/(marketing)/login/page.test.tsx +58 -0
- package/templates/apps/frontend-saas/src/pages/(marketing)/login/page.tsx +96 -0
- package/templates/apps/frontend-saas/src/pages/(marketing)/page.tsx +27 -0
- package/templates/apps/frontend-saas/src/pages/dashboard/billing/page.tsx +72 -0
- package/templates/apps/frontend-saas/src/pages/dashboard/error.tsx +20 -0
- package/templates/apps/frontend-saas/src/pages/dashboard/layout.test.tsx +32 -0
- package/templates/apps/frontend-saas/src/pages/dashboard/layout.tsx +69 -0
- package/templates/apps/frontend-saas/src/pages/dashboard/not-found.tsx +17 -0
- package/templates/apps/frontend-saas/src/pages/dashboard/page.test.tsx +113 -0
- package/templates/apps/frontend-saas/src/pages/dashboard/page.tsx +76 -0
- package/templates/apps/frontend-saas/src/pages/dashboard/team/page.tsx +73 -0
- package/templates/apps/frontend-saas/src/pages/layout.tsx +12 -0
- package/templates/apps/frontend-saas/template.json +6 -0
- package/templates/apps/frontend-saas/tsconfig.json +5 -0
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/package.json +6 -6
- package/templates/apps/frontend-status/README.md +51 -0
- package/templates/apps/frontend-status/app.config.ts +39 -0
- package/templates/apps/frontend-status/package.json +32 -0
- package/templates/apps/frontend-status/src/config.ts +7 -0
- package/templates/apps/frontend-status/src/globals.css +70 -0
- package/templates/apps/frontend-status/src/globals.d.ts +6 -0
- package/templates/apps/frontend-status/src/lib/status.ts +81 -0
- package/templates/apps/frontend-status/src/locales/de.ts +41 -0
- package/templates/apps/frontend-status/src/locales/en.ts +45 -0
- package/templates/apps/frontend-status/src/locales/index.ts +13 -0
- package/templates/apps/frontend-status/src/pages/layout.tsx +27 -0
- package/templates/apps/frontend-status/src/pages/page.test.tsx +123 -0
- package/templates/apps/frontend-status/src/pages/page.tsx +136 -0
- package/templates/apps/frontend-status/template.json +6 -0
- package/templates/apps/frontend-status/tsconfig.json +11 -0
- package/templates/baselines/compose/docker-compose.prod.yml +15 -0
- package/templates/baselines/compose-mariadb/docker-compose.prod.yml +15 -0
- package/dist/apiBuild-BrjrVJJh.js +0 -2
- package/dist/dbCommand-DrzXimKf.js +0 -2
- package/dist/dev-DNkso403.js +0 -3
- package/dist/fileConventions-3bffWssN.js +0 -30
- package/dist/frameworkTableAssembly-lrjZtk0G.js +0 -2
- package/dist/inspect-gt8bq-Tz.js +0 -2
- package/dist/manifestBuild-ifczArzr.js +0 -2
- package/dist/serveCommand-DfkisVWP.js +0 -1310
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Schema for the {{projectName}} status page.
|
|
2
|
+
//
|
|
3
|
+
// These tables are PUBLIC and NOT tenant-scoped on purpose: a status page is
|
|
4
|
+
// read by anyone, so the live queries must return rows for an anonymous caller.
|
|
5
|
+
// (Adding `tenant()` here would AND-merge `tenantId = subject.tenantId` and an
|
|
6
|
+
// anonymous visitor — tenantId null — would see nothing.) Write access is
|
|
7
|
+
// controlled by the rbac `status:write` guard on the mutations instead.
|
|
8
|
+
//
|
|
9
|
+
// `actors` + `tenants` are the framework core tables.
|
|
10
|
+
import {
|
|
11
|
+
databaseHandle,
|
|
12
|
+
id,
|
|
13
|
+
table,
|
|
14
|
+
text,
|
|
15
|
+
timestamp,
|
|
16
|
+
type InferRow,
|
|
17
|
+
} from '@voltro/database'
|
|
18
|
+
|
|
19
|
+
export const actors = table('actors', {
|
|
20
|
+
id: id(),
|
|
21
|
+
kind: text().oneOf(['user', 'serviceAccount', 'apiKey', 'system']),
|
|
22
|
+
displayName: text().nullable(),
|
|
23
|
+
createdAt: timestamp().default('now'),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export const tenants = table('tenants', {
|
|
27
|
+
id: id(),
|
|
28
|
+
name: text(),
|
|
29
|
+
createdAt: timestamp().default('now'),
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// A monitored service component (API, Dashboard, Webhooks, …).
|
|
33
|
+
export const components = table('components', {
|
|
34
|
+
id: id({ prefix: 'cmp' }),
|
|
35
|
+
name: text(),
|
|
36
|
+
status: text().oneOf(['operational', 'degraded', 'partial_outage', 'major_outage']),
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// An incident. `impact` drives the public banner colour; `status` walks the
|
|
40
|
+
// lifecycle investigating → identified → monitoring → resolved.
|
|
41
|
+
export const incidents = table('incidents', {
|
|
42
|
+
id: id({ prefix: 'inc' }),
|
|
43
|
+
title: text(),
|
|
44
|
+
impact: text().oneOf(['minor', 'major', 'critical']),
|
|
45
|
+
status: text().oneOf(['investigating', 'identified', 'monitoring', 'resolved']),
|
|
46
|
+
startedAt: timestamp().default('now'),
|
|
47
|
+
resolvedAt: timestamp().nullable(),
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
// A timeline entry posted against an incident. The public page renders these in
|
|
51
|
+
// order under each incident.
|
|
52
|
+
export const incidentUpdates = table('incident_updates', {
|
|
53
|
+
id: id({ prefix: 'iu' }),
|
|
54
|
+
incidentId: text(),
|
|
55
|
+
body: text(),
|
|
56
|
+
status: text().oneOf(['investigating', 'identified', 'monitoring', 'resolved']),
|
|
57
|
+
createdAt: timestamp().default('now'),
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
export type Component = InferRow<typeof components>
|
|
61
|
+
export type Incident = InferRow<typeof incidents>
|
|
62
|
+
export type IncidentUpdate = InferRow<typeof incidentUpdates>
|
|
63
|
+
|
|
64
|
+
export const database = databaseHandle({
|
|
65
|
+
actors,
|
|
66
|
+
tenants,
|
|
67
|
+
components,
|
|
68
|
+
incidents,
|
|
69
|
+
incidentUpdates,
|
|
70
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Effect } from 'effect'
|
|
2
|
+
import type { AppContext } from '@voltro/runtime'
|
|
3
|
+
import { EffectStore } from '@voltro/runtime'
|
|
4
|
+
|
|
5
|
+
const execute = (input: { name: string }, _ctx: AppContext) =>
|
|
6
|
+
Effect.gen(function* () {
|
|
7
|
+
const store = yield* EffectStore
|
|
8
|
+
const row = yield* store.insert('components', {
|
|
9
|
+
name: input.name,
|
|
10
|
+
status: 'operational',
|
|
11
|
+
})
|
|
12
|
+
return {
|
|
13
|
+
id: row['id'] as string,
|
|
14
|
+
name: row['name'] as string,
|
|
15
|
+
status: row['status'] as string,
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
export default execute
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Add a monitored component — OPERATOR ONLY. Seed your components once
|
|
2
|
+
// (API, Dashboard, Webhooks, …); the public page lists them with their status.
|
|
3
|
+
import { defineMutation } from '@voltro/protocol'
|
|
4
|
+
import { Schema } from 'effect'
|
|
5
|
+
|
|
6
|
+
export const createComponent = defineMutation({
|
|
7
|
+
name: 'components.create',
|
|
8
|
+
target: { table: 'components', op: 'insert' },
|
|
9
|
+
guards: [{ scope: 'status:write' }],
|
|
10
|
+
input: Schema.Struct({ name: Schema.NonEmptyString }),
|
|
11
|
+
output: Schema.Struct({
|
|
12
|
+
id: Schema.String,
|
|
13
|
+
name: Schema.String,
|
|
14
|
+
status: Schema.String,
|
|
15
|
+
}),
|
|
16
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Authz-free here: by the time this runs the framework has already enforced the
|
|
2
|
+
// `status:write` guard from the descriptor. A new incident opens in the
|
|
3
|
+
// `investigating` state; `startedAt` is stamped by the column default.
|
|
4
|
+
import { Effect } from 'effect'
|
|
5
|
+
import type { AppContext } from '@voltro/runtime'
|
|
6
|
+
import { EffectStore } from '@voltro/runtime'
|
|
7
|
+
|
|
8
|
+
const execute = (input: { title: string; impact: string }, _ctx: AppContext) =>
|
|
9
|
+
Effect.gen(function* () {
|
|
10
|
+
const store = yield* EffectStore
|
|
11
|
+
const row = yield* store.insert('incidents', {
|
|
12
|
+
title: input.title,
|
|
13
|
+
impact: input.impact,
|
|
14
|
+
status: 'investigating',
|
|
15
|
+
resolvedAt: null,
|
|
16
|
+
})
|
|
17
|
+
return {
|
|
18
|
+
id: row['id'] as string,
|
|
19
|
+
title: row['title'] as string,
|
|
20
|
+
impact: row['impact'] as string,
|
|
21
|
+
status: row['status'] as string,
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export default execute
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Declare an incident — OPERATOR ONLY. The `guards: [{ scope: 'status:write' }]`
|
|
2
|
+
// is enforced by the framework before the executor runs; `voltro check` sees it
|
|
3
|
+
// statically. Declaring `guards:` merges `ScopeError` into the wire-error union
|
|
4
|
+
// automatically (no `error:` needed).
|
|
5
|
+
import { defineMutation } from '@voltro/protocol'
|
|
6
|
+
import { Schema } from 'effect'
|
|
7
|
+
|
|
8
|
+
export const createIncident = defineMutation({
|
|
9
|
+
name: 'incidents.create',
|
|
10
|
+
target: { table: 'incidents', op: 'insert' },
|
|
11
|
+
guards: [{ scope: 'status:write' }],
|
|
12
|
+
input: Schema.Struct({
|
|
13
|
+
title: Schema.NonEmptyString,
|
|
14
|
+
impact: Schema.Literal('minor', 'major', 'critical'),
|
|
15
|
+
}),
|
|
16
|
+
output: Schema.Struct({
|
|
17
|
+
id: Schema.String,
|
|
18
|
+
title: Schema.String,
|
|
19
|
+
impact: Schema.String,
|
|
20
|
+
status: Schema.String,
|
|
21
|
+
}),
|
|
22
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Effect } from 'effect'
|
|
2
|
+
import type { AppContext } from '@voltro/runtime'
|
|
3
|
+
import { EffectStore } from '@voltro/runtime'
|
|
4
|
+
|
|
5
|
+
const execute = (input: { incidentId: string; body: string }, _ctx: AppContext) =>
|
|
6
|
+
Effect.gen(function* () {
|
|
7
|
+
const store = yield* EffectStore
|
|
8
|
+
const row = yield* store.update('incidents', input.incidentId, {
|
|
9
|
+
status: 'resolved',
|
|
10
|
+
resolvedAt: new Date(),
|
|
11
|
+
})
|
|
12
|
+
// A closing timeline entry (default body when the operator left it blank).
|
|
13
|
+
yield* store.insert('incident_updates', {
|
|
14
|
+
incidentId: input.incidentId,
|
|
15
|
+
body: input.body === '' ? 'Resolved.' : input.body,
|
|
16
|
+
status: 'resolved',
|
|
17
|
+
})
|
|
18
|
+
return {
|
|
19
|
+
id: input.incidentId,
|
|
20
|
+
status: (row?.['status'] as string | undefined) ?? 'resolved',
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export default execute
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Resolve an incident — OPERATOR ONLY. Sets `status: 'resolved'` + stamps
|
|
2
|
+
// `resolvedAt`, and appends a final `resolved` timeline entry. The public page's
|
|
3
|
+
// banner goes green and the incident archives into the history.
|
|
4
|
+
import { defineMutation } from '@voltro/protocol'
|
|
5
|
+
import { Schema } from 'effect'
|
|
6
|
+
|
|
7
|
+
export const resolveIncident = defineMutation({
|
|
8
|
+
name: 'incidents.resolve',
|
|
9
|
+
target: { table: 'incidents', op: 'update' },
|
|
10
|
+
guards: [{ scope: 'status:write' }],
|
|
11
|
+
input: Schema.Struct({
|
|
12
|
+
incidentId: Schema.NonEmptyString,
|
|
13
|
+
body: Schema.String,
|
|
14
|
+
}),
|
|
15
|
+
output: Schema.Struct({
|
|
16
|
+
id: Schema.String,
|
|
17
|
+
status: Schema.String,
|
|
18
|
+
}),
|
|
19
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Effect } from 'effect'
|
|
2
|
+
import type { AppContext } from '@voltro/runtime'
|
|
3
|
+
import { EffectStore } from '@voltro/runtime'
|
|
4
|
+
|
|
5
|
+
const execute = (
|
|
6
|
+
input: { incidentId: string; body: string; status: string },
|
|
7
|
+
_ctx: AppContext,
|
|
8
|
+
) =>
|
|
9
|
+
Effect.gen(function* () {
|
|
10
|
+
const store = yield* EffectStore
|
|
11
|
+
// Advance the incident's own status so the banner reflects the latest note.
|
|
12
|
+
yield* store.update('incidents', input.incidentId, { status: input.status })
|
|
13
|
+
// Append the timeline entry.
|
|
14
|
+
const row = yield* store.insert('incident_updates', {
|
|
15
|
+
incidentId: input.incidentId,
|
|
16
|
+
body: input.body,
|
|
17
|
+
status: input.status,
|
|
18
|
+
})
|
|
19
|
+
return {
|
|
20
|
+
id: row['id'] as string,
|
|
21
|
+
incidentId: row['incidentId'] as string,
|
|
22
|
+
status: row['status'] as string,
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
export default execute
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Post a timeline update to an open incident — OPERATOR ONLY. Writes an
|
|
2
|
+
// `incident_updates` row AND advances the incident's own `status` so the public
|
|
3
|
+
// banner + timeline move together.
|
|
4
|
+
import { defineMutation } from '@voltro/protocol'
|
|
5
|
+
import { Schema } from 'effect'
|
|
6
|
+
|
|
7
|
+
export const updateIncident = defineMutation({
|
|
8
|
+
name: 'incidents.update',
|
|
9
|
+
target: { table: 'incident_updates', op: 'insert' },
|
|
10
|
+
guards: [{ scope: 'status:write' }],
|
|
11
|
+
input: Schema.Struct({
|
|
12
|
+
incidentId: Schema.NonEmptyString,
|
|
13
|
+
body: Schema.NonEmptyString,
|
|
14
|
+
status: Schema.Literal('investigating', 'identified', 'monitoring'),
|
|
15
|
+
}),
|
|
16
|
+
output: Schema.Struct({
|
|
17
|
+
id: Schema.String,
|
|
18
|
+
incidentId: Schema.String,
|
|
19
|
+
status: Schema.String,
|
|
20
|
+
}),
|
|
21
|
+
})
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/{{appName}}",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "voltro dev .",
|
|
8
|
+
"lint": "voltro doctor .",
|
|
9
|
+
"migrate": "voltro migrate",
|
|
10
|
+
"test": "voltro test",
|
|
11
|
+
"typecheck": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@effect/platform": "^0.97.0",
|
|
15
|
+
"@effect/rpc": "^0.76.0",
|
|
16
|
+
"@voltro/cli": "0.29.0",
|
|
17
|
+
"@voltro/database": "0.29.0",
|
|
18
|
+
"@voltro/env": "0.29.0",
|
|
19
|
+
"@voltro/plugin-rbac": "0.29.0",
|
|
20
|
+
"@voltro/protocol": "0.29.0",
|
|
21
|
+
"@voltro/runtime": "0.29.0",
|
|
22
|
+
"@voltro/sql-postgres": "0.29.0",
|
|
23
|
+
"effect": "^3.22.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@voltro/testing": "0.29.0",
|
|
27
|
+
"typescript": "^6.0.3",
|
|
28
|
+
"vitest": "^4.1.10"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AppContext } from '@voltro/runtime'
|
|
2
|
+
|
|
3
|
+
const execute = (_input: Record<string, never>, _ctx: AppContext) => ({
|
|
4
|
+
descriptor: {
|
|
5
|
+
table: 'components' as const,
|
|
6
|
+
predicate: undefined,
|
|
7
|
+
order: [{ column: 'name' as const, direction: 'asc' as const }],
|
|
8
|
+
take: 100,
|
|
9
|
+
skip: undefined,
|
|
10
|
+
projection: undefined,
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export default execute
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Live list of service components + their current status. Public (no guard).
|
|
2
|
+
import { defineQuery } from '@voltro/protocol'
|
|
3
|
+
import { Schema } from 'effect'
|
|
4
|
+
|
|
5
|
+
export const componentsList = defineQuery({
|
|
6
|
+
name: 'components.list',
|
|
7
|
+
input: Schema.Struct({}),
|
|
8
|
+
output: Schema.Struct({
|
|
9
|
+
id: Schema.String,
|
|
10
|
+
name: Schema.String,
|
|
11
|
+
status: Schema.String,
|
|
12
|
+
}),
|
|
13
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AppContext } from '@voltro/runtime'
|
|
2
|
+
|
|
3
|
+
const execute = (_input: Record<string, never>, _ctx: AppContext) => ({
|
|
4
|
+
descriptor: {
|
|
5
|
+
table: 'incidents' as const,
|
|
6
|
+
predicate: undefined,
|
|
7
|
+
order: [{ column: 'startedAt' as const, direction: 'desc' as const }],
|
|
8
|
+
take: 100,
|
|
9
|
+
skip: undefined,
|
|
10
|
+
projection: undefined,
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export default execute
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The live incident feed — the heart of the status page. NO `guards:`, so an
|
|
2
|
+
// anonymous visitor's browser can subscribe: `useSubscription('app',
|
|
3
|
+
// 'incidents.live')`. Every insert/update to `incidents` lands as a delta, so a
|
|
4
|
+
// newly-declared incident (or a resolve) shows up on every open status page
|
|
5
|
+
// within seconds — no polling.
|
|
6
|
+
import { defineQuery } from '@voltro/protocol'
|
|
7
|
+
import { Schema } from 'effect'
|
|
8
|
+
|
|
9
|
+
export const incidentsLive = defineQuery({
|
|
10
|
+
name: 'incidents.live',
|
|
11
|
+
input: Schema.Struct({}),
|
|
12
|
+
output: Schema.Struct({
|
|
13
|
+
id: Schema.String,
|
|
14
|
+
title: Schema.String,
|
|
15
|
+
impact: Schema.String,
|
|
16
|
+
status: Schema.String,
|
|
17
|
+
startedAt: Schema.Date,
|
|
18
|
+
resolvedAt: Schema.NullOr(Schema.Date),
|
|
19
|
+
}),
|
|
20
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AppContext } from '@voltro/runtime'
|
|
2
|
+
|
|
3
|
+
const execute = (_input: Record<string, never>, _ctx: AppContext) => ({
|
|
4
|
+
descriptor: {
|
|
5
|
+
table: 'incident_updates' as const,
|
|
6
|
+
predicate: undefined,
|
|
7
|
+
order: [{ column: 'createdAt' as const, direction: 'desc' as const }],
|
|
8
|
+
take: 200,
|
|
9
|
+
skip: undefined,
|
|
10
|
+
projection: undefined,
|
|
11
|
+
},
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
export default execute
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Live feed of incident timeline entries. Public (no guard). The page joins
|
|
2
|
+
// these to incidents by `incidentId` client-side to render each incident's
|
|
3
|
+
// update history.
|
|
4
|
+
import { defineQuery } from '@voltro/protocol'
|
|
5
|
+
import { Schema } from 'effect'
|
|
6
|
+
|
|
7
|
+
export const updatesList = defineQuery({
|
|
8
|
+
name: 'updates.list',
|
|
9
|
+
input: Schema.Struct({}),
|
|
10
|
+
output: Schema.Struct({
|
|
11
|
+
id: Schema.String,
|
|
12
|
+
incidentId: Schema.String,
|
|
13
|
+
body: Schema.String,
|
|
14
|
+
status: Schema.String,
|
|
15
|
+
createdAt: Schema.Date,
|
|
16
|
+
}),
|
|
17
|
+
})
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "api-status",
|
|
3
|
+
"kind": "api",
|
|
4
|
+
"summary": "The backend for a public status page: ungated live queries (incidents.live, updates.list, components.list) any visitor can subscribe to, plus operator-only mutations (create/update/resolve incidents, manage components) gated by a @voltro/plugin-rbac `status:write` scope. Reads are public so an anonymous status page works; writes need an operator. Pairs with the frontend-status web template. Boots zero-infra.",
|
|
5
|
+
"tags": ["api", "status-page", "incidents", "rbac", "live", "useSubscription", "operator"]
|
|
6
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Authorization + descriptor tests, driven through the REAL rbac plugin.
|
|
2
|
+
//
|
|
3
|
+
// `makeTestContext({ plugins: [...] })` + `invoke` composes this app's actual
|
|
4
|
+
// interceptor chain and enforces each descriptor's `guards:` with the same
|
|
5
|
+
// `checkGuardsEffect` the serve pipeline calls — so we exercise the whole path
|
|
6
|
+
// (tenant → role → compiled scope → guard), not a hand-stamped `subject.scopes`.
|
|
7
|
+
// The asymmetry that defines a status page is the thing under test: reads are
|
|
8
|
+
// public, writes are operator-only.
|
|
9
|
+
//
|
|
10
|
+
// Run with `voltro test` (vitest).
|
|
11
|
+
|
|
12
|
+
import { describe, it, expect } from 'vitest'
|
|
13
|
+
import { Schema } from 'effect'
|
|
14
|
+
import { makeTestContext, mockStore, invoke } from '@voltro/testing'
|
|
15
|
+
import { rbacPlugin } from '@voltro/plugin-rbac'
|
|
16
|
+
import type { Subject } from '@voltro/protocol'
|
|
17
|
+
|
|
18
|
+
import { roles, demoRolesForTenant } from '../authz'
|
|
19
|
+
import { database } from '../database/schema' // registers incidents / components / updates / core
|
|
20
|
+
import { createIncident } from '../mutations/incidents.create.mutation'
|
|
21
|
+
import createIncidentHandler from '../mutations/incidents.create.mutation.server'
|
|
22
|
+
import { incidentsLive } from '../queries/incidents.live.query'
|
|
23
|
+
import incidentsLiveHandler from '../queries/incidents.live.query.server'
|
|
24
|
+
|
|
25
|
+
/** The app's real plugin, built from the app's real role map. */
|
|
26
|
+
const plugin = () => rbacPlugin({ roles, resolveRoles: (subject) => demoRolesForTenant(subject.tenantId) })
|
|
27
|
+
|
|
28
|
+
const subjectFor = (tenantId: string | null, id: string | null = 'u_1', type = 'user'): Subject =>
|
|
29
|
+
({ type, id, tenantId, scopes: [] }) as Subject
|
|
30
|
+
|
|
31
|
+
const ctxFor = (subject: Subject) =>
|
|
32
|
+
makeTestContext({ subject, store: mockStore({ incidents: [], components: [], incident_updates: [] }), plugins: [plugin()] })
|
|
33
|
+
|
|
34
|
+
describe('incidents.create — operator-gated', () => {
|
|
35
|
+
it('an operator (tenant `ops`) may declare an incident', async () => {
|
|
36
|
+
const ctx = ctxFor(subjectFor('ops'))
|
|
37
|
+
const inc = await invoke(createIncident, createIncidentHandler, { title: 'API degraded', impact: 'major' }, ctx)
|
|
38
|
+
expect(inc.title).toBe('API degraded')
|
|
39
|
+
expect(inc.status).toBe('investigating')
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
it('an anonymous visitor is refused with a typed ScopeError, and nothing is written', async () => {
|
|
43
|
+
const ctx = ctxFor(subjectFor(null, null, 'anonymous'))
|
|
44
|
+
await expect(invoke(createIncident, createIncidentHandler, { title: 'nope', impact: 'minor' }, ctx))
|
|
45
|
+
.rejects.toMatchObject({ _tag: 'ScopeError', required: 'status:write' })
|
|
46
|
+
expect(await ctx.store.query(database.incidents.descriptor)).toHaveLength(0)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('a signed-in NON-operator is also refused', async () => {
|
|
50
|
+
const ctx = ctxFor(subjectFor('marketing'))
|
|
51
|
+
await expect(invoke(createIncident, createIncidentHandler, { title: 'nope', impact: 'minor' }, ctx))
|
|
52
|
+
.rejects.toMatchObject({ _tag: 'ScopeError', required: 'status:write' })
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
describe('incidents.live — reads are PUBLIC', () => {
|
|
57
|
+
it('an anonymous visitor may subscribe (no guard on the query)', async () => {
|
|
58
|
+
const ctx = ctxFor(subjectFor(null, null, 'anonymous'))
|
|
59
|
+
await expect(invoke(incidentsLive, incidentsLiveHandler, {}, ctx)).resolves.toBeDefined()
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe('incidents.create — descriptor', () => {
|
|
64
|
+
it('accepts a valid impact and rejects an unknown one', () => {
|
|
65
|
+
const decode = Schema.decodeUnknownSync(createIncident.input)
|
|
66
|
+
expect(decode({ title: 'x', impact: 'critical' })).toEqual({ title: 'x', impact: 'critical' })
|
|
67
|
+
expect(() => decode({ title: 'x', impact: 'catastrophic' })).toThrow()
|
|
68
|
+
expect(() => decode({ title: '', impact: 'minor' })).toThrow()
|
|
69
|
+
})
|
|
70
|
+
})
|
|
@@ -13,17 +13,17 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/database": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.
|
|
20
|
-
"@voltro/plugin-versioning": "0.
|
|
21
|
-
"@voltro/protocol": "0.
|
|
22
|
-
"@voltro/runtime": "0.
|
|
16
|
+
"@voltro/cli": "0.29.0",
|
|
17
|
+
"@voltro/database": "0.29.0",
|
|
18
|
+
"@voltro/env": "0.29.0",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.29.0",
|
|
20
|
+
"@voltro/plugin-versioning": "0.29.0",
|
|
21
|
+
"@voltro/protocol": "0.29.0",
|
|
22
|
+
"@voltro/runtime": "0.29.0",
|
|
23
23
|
"effect": "^3.22.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/testing": "0.
|
|
26
|
+
"@voltro/testing": "0.29.0",
|
|
27
27
|
"typescript": "^6.0.3",
|
|
28
28
|
"vitest": "^4.1.10"
|
|
29
29
|
}
|
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@effect/platform": "^0.97.0",
|
|
15
15
|
"@effect/rpc": "^0.76.0",
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/database": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/plugin-multitenancy": "0.
|
|
20
|
-
"@voltro/plugin-webhooks": "0.
|
|
21
|
-
"@voltro/protocol": "0.
|
|
22
|
-
"@voltro/runtime": "0.
|
|
23
|
-
"@voltro/sql-postgres": "0.
|
|
16
|
+
"@voltro/cli": "0.29.0",
|
|
17
|
+
"@voltro/database": "0.29.0",
|
|
18
|
+
"@voltro/env": "0.29.0",
|
|
19
|
+
"@voltro/plugin-multitenancy": "0.29.0",
|
|
20
|
+
"@voltro/plugin-webhooks": "0.29.0",
|
|
21
|
+
"@voltro/protocol": "0.29.0",
|
|
22
|
+
"@voltro/runtime": "0.29.0",
|
|
23
|
+
"@voltro/sql-postgres": "0.29.0",
|
|
24
24
|
"effect": "^3.22.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@voltro/testing": "0.
|
|
27
|
+
"@voltro/testing": "0.29.0",
|
|
28
28
|
"typescript": "^6.0.3",
|
|
29
29
|
"vitest": "^4.1.10"
|
|
30
30
|
}
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@voltro/changelog": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/web": "0.
|
|
15
|
+
"@voltro/changelog": "0.29.0",
|
|
16
|
+
"@voltro/cli": "0.29.0",
|
|
17
|
+
"@voltro/client": "0.29.0",
|
|
18
|
+
"@voltro/env": "0.29.0",
|
|
19
|
+
"@voltro/i18n": "0.29.0",
|
|
20
|
+
"@voltro/web": "0.29.0",
|
|
21
21
|
"react": "^19.0.0",
|
|
22
22
|
"react-dom": "^19.0.0"
|
|
23
23
|
},
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"test": "voltro test"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/ui-shadcn": "0.
|
|
21
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.29.0",
|
|
16
|
+
"@voltro/cli": "0.29.0",
|
|
17
|
+
"@voltro/client": "0.29.0",
|
|
18
|
+
"@voltro/env": "0.29.0",
|
|
19
|
+
"@voltro/i18n": "0.29.0",
|
|
20
|
+
"@voltro/ui-shadcn": "0.29.0",
|
|
21
|
+
"@voltro/web": "0.29.0",
|
|
22
22
|
"react": "^19.0.0",
|
|
23
23
|
"react-dom": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/devtools": "0.
|
|
26
|
+
"@voltro/devtools": "0.29.0",
|
|
27
27
|
"@types/react": "^19.0.0",
|
|
28
28
|
"@types/react-dom": "^19.0.0",
|
|
29
29
|
"typescript": "^6.0.3",
|
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
"typecheck": "tsc --noEmit"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@{{projectName}}/api": "0.
|
|
16
|
-
"@voltro/cli": "0.
|
|
17
|
-
"@voltro/client": "0.
|
|
18
|
-
"@voltro/env": "0.
|
|
19
|
-
"@voltro/i18n": "0.
|
|
20
|
-
"@voltro/ui-shadcn": "0.
|
|
21
|
-
"@voltro/web": "0.
|
|
15
|
+
"@{{projectName}}/api": "0.29.0",
|
|
16
|
+
"@voltro/cli": "0.29.0",
|
|
17
|
+
"@voltro/client": "0.29.0",
|
|
18
|
+
"@voltro/env": "0.29.0",
|
|
19
|
+
"@voltro/i18n": "0.29.0",
|
|
20
|
+
"@voltro/ui-shadcn": "0.29.0",
|
|
21
|
+
"@voltro/web": "0.29.0",
|
|
22
22
|
"react": "^19.0.0",
|
|
23
23
|
"react-dom": "^19.0.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@voltro/devtools": "0.
|
|
26
|
+
"@voltro/devtools": "0.29.0",
|
|
27
27
|
"@types/react": "^19.0.0",
|
|
28
28
|
"@types/react-dom": "^19.0.0",
|
|
29
29
|
"typescript": "^6.0.3",
|