@voltro/cli 0.28.0 → 0.30.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 +655 -0
- package/THIRD-PARTY-NOTICES.md +90 -4
- package/dist/{apiBuild-eUM32r1u.js → apiBuild-BceikpQf.js} +3 -3
- package/dist/apiBuild-CxvR1Dwt.js +2 -0
- package/dist/bin.js +3 -3
- package/dist/checkCommand-BMr-X48w.js +2427 -0
- package/dist/checkCommand-BOk9y264.js +2 -0
- package/dist/cliRuntime-Oh517vCV.js +96 -0
- package/dist/{commands-CSkrUI1h.js → commands-D4Y1-Nlu.js} +8575 -9366
- package/dist/{dbCommand-CpYgmSw4.js → dbCommand-C7hbl5pk.js} +345 -266
- package/dist/dbCommand-D8nhyppR.js +2 -0
- package/dist/{dev-DlBWWnJQ.js → dev-Cr-teYsz.js} +1 -1
- package/dist/{dev-CEZwJhmb.js → dev-DmnkH-wr.js} +2939 -1832
- package/dist/fileConventions-Cof68_BL.js +33 -0
- package/dist/{frameworkTableAssembly-BwHU9Euq.js → frameworkTableAssembly-BzAajuUv.js} +115 -99
- package/dist/frameworkTableAssembly-DU0aOxjL.js +2 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +1 -1
- package/dist/{inspect-CUCCzw2I.js → inspect-BUUjt773.js} +349 -192
- package/dist/inspect-mmBuRXmy.js +2 -0
- package/dist/{inspectMetrics-BU90mvJN.js → inspectMetrics-BeJdP_3b.js} +1068 -978
- package/dist/{manifestBuild-BnzAxp2O.js → manifestBuild-BLrVuSlM.js} +1 -1
- package/dist/manifestBuild-Dj8Jjoto.js +2 -0
- package/dist/sdkgen-CqpOq_fy.js +582 -0
- package/dist/serveCommand-Ciq_wIY8.js +1577 -0
- package/dist/serveEntry.js +2 -2
- package/dist/{start-BGXIf6zT.js → start-ocqB1seX.js} +283 -270
- package/dist/startEntry.js +2 -2
- package/package.json +17 -17
- package/templates/AGENTS.core.md +50 -0
- package/templates/AGENTS.md +54 -2
- package/templates/agent-docs/_index.md +4 -2
- package/templates/agent-docs/_manifest.json +26 -5
- package/templates/agent-docs/ai.md +191 -0
- package/templates/agent-docs/cli.md +143 -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/internationalization.md +169 -8
- package/templates/agent-docs/local-first-mobile.md +426 -0
- package/templates/agent-docs/routing.md +94 -3
- package/templates/agent-docs/schema-driven-ui.md +12 -0
- package/templates/agent-docs/security.md +17 -0
- package/templates/agent-docs/templates/apibackends.md +210 -1
- package/templates/agent-docs/templates/appshells.md +144 -1
- package/templates/agent-docs/templates/mobile.md +60 -0
- package/templates/agent-docs/templates/overview.md +17 -7
- package/templates/agent-docs/whats-new.md +238 -158
- package/templates/agent-docs/workflows.md +376 -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-collab/README.md +70 -0
- package/templates/apps/api-collab/app.config.ts +28 -0
- package/templates/apps/api-collab/database/schema.ts +63 -0
- package/templates/apps/api-collab/mutations/documents.create.mutation.server.ts +19 -0
- package/templates/apps/api-collab/mutations/documents.create.mutation.ts +36 -0
- package/templates/apps/api-collab/mutations/documents.setBody.mutation.server.ts +20 -0
- package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +30 -0
- package/templates/apps/api-collab/package.json +30 -0
- package/templates/apps/api-collab/queries/documents.query.server.ts +16 -0
- package/templates/apps/api-collab/queries/documents.query.ts +23 -0
- package/templates/apps/api-collab/template.json +6 -0
- package/templates/apps/api-collab/tests/documents.setBody.test.ts +89 -0
- package/templates/apps/api-collab/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/app.config.ts +1 -1
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-admin/src/locales/index.ts +1 -1
- package/templates/apps/frontend-app/app.config.ts +1 -1
- package/templates/apps/frontend-app/package.json +8 -8
- package/templates/apps/frontend-app/src/locales/index.ts +1 -1
- package/templates/apps/frontend-app/src/pages/layout.tsx +1 -1
- package/templates/apps/frontend-app/src/pages/page.tsx +1 -1
- 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/app.config.ts +1 -1
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-blank/src/locales/index.ts +1 -1
- package/templates/apps/frontend-blank/src/pages/layout.tsx +1 -1
- package/templates/apps/frontend-blank/src/pages/page.tsx +1 -1
- 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-collab/README.md +80 -0
- package/templates/apps/frontend-collab/app.config.ts +38 -0
- package/templates/apps/frontend-collab/package.json +33 -0
- package/templates/apps/frontend-collab/src/locales/de.ts +29 -0
- package/templates/apps/frontend-collab/src/locales/en.ts +31 -0
- package/templates/apps/frontend-collab/src/locales/index.ts +15 -0
- package/templates/apps/frontend-collab/src/pages/layout.tsx +39 -0
- package/templates/apps/frontend-collab/src/pages/page.test.tsx +150 -0
- package/templates/apps/frontend-collab/src/pages/page.tsx +186 -0
- package/templates/apps/frontend-collab/template.json +6 -0
- package/templates/apps/frontend-collab/tsconfig.json +11 -0
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/app.config.ts +1 -1
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-dashboard/src/locales/index.ts +1 -1
- 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/app.config.ts +1 -1
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-spa/src/locales/index.ts +1 -1
- package/templates/apps/frontend-spa/src/pages/layout.tsx +1 -1
- package/templates/apps/frontend-spa/src/pages/page.tsx +1 -1
- package/templates/apps/frontend-ssr/app.config.ts +1 -1
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr/src/locales/index.ts +1 -1
- package/templates/apps/frontend-ssr/src/pages/layout.tsx +1 -1
- package/templates/apps/frontend-ssr/src/pages/page.tsx +1 -1
- package/templates/apps/frontend-ssr-api/app.config.ts +1 -1
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-ssr-api/src/locales/index.ts +1 -1
- package/templates/apps/frontend-ssr-api/src/pages/layout.tsx +1 -1
- 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/apps/mobile-app/README.md +58 -0
- package/templates/apps/mobile-app/app.config.ts +33 -0
- package/templates/apps/mobile-app/babel.config.js +5 -0
- package/templates/apps/mobile-app/metro.config.js +20 -0
- package/templates/apps/mobile-app/package.json +33 -0
- package/templates/apps/mobile-app/src/app/_layout.tsx +61 -0
- package/templates/apps/mobile-app/src/app/index.tsx +62 -0
- package/templates/apps/mobile-app/src/app/orders/[id].tsx +19 -0
- package/templates/apps/mobile-app/src/app/settings.tsx +51 -0
- package/templates/apps/mobile-app/src/client.ts +74 -0
- package/templates/apps/mobile-app/src/lib/deeplinks.ts +39 -0
- package/templates/apps/mobile-app/src/lib/notifications.ts +31 -0
- package/templates/apps/mobile-app/src/lib/sync.ts +29 -0
- package/templates/apps/mobile-app/src/persistence.ts +38 -0
- package/templates/apps/mobile-app/template.json +6 -0
- package/templates/apps/mobile-app/tests/deeplinks.test.ts +32 -0
- package/templates/apps/mobile-app/tests/notifications.test.ts +26 -0
- package/templates/apps/mobile-app/tests/sync.test.ts +34 -0
- package/templates/apps/mobile-app/tsconfig.json +10 -0
- package/templates/apps/mobile-app/voltro.mobile.ts +11 -0
- package/templates/baselines/compose/docker-compose.prod.yml +15 -0
- package/templates/baselines/compose-mariadb/docker-compose.prod.yml +15 -0
- package/templates/patches/@effect__cluster@0.60.0.patch +6 -6
- package/dist/apiBuild-DgBS9ayv.js +0 -2
- package/dist/dbCommand-DvguqlzF.js +0 -2
- 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-ZTn-dPFa.js +0 -1425
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { i as e, n as t, r as n, s as r, t as i } from "./seedRunner-Bqxgp7HZ.js";
|
|
2
2
|
import { n as a } from "./appModuleLoader-C9r9mxZt.js";
|
|
3
|
-
import { t as o } from "./frameworkTableAssembly-
|
|
4
|
-
import {
|
|
3
|
+
import { t as o } from "./frameworkTableAssembly-BzAajuUv.js";
|
|
4
|
+
import { b as s } from "./fileConventions-Cof68_BL.js";
|
|
5
5
|
import { a as c, n as l, t as u } from "./cliArgs-qdZSElM3.js";
|
|
6
6
|
import { basename as d, join as f, relative as p, resolve as m } from "node:path";
|
|
7
7
|
import { pathToFileURL as h } from "node:url";
|
|
@@ -10,17 +10,17 @@ import { createLogger as _ } from "@voltro/logger";
|
|
|
10
10
|
import { promises as v, writeSync as y } from "node:fs";
|
|
11
11
|
import { InMemoryDataStore as ee } from "@voltro/runtime";
|
|
12
12
|
import { SqlClient as b } from "@effect/sql";
|
|
13
|
-
import { actorsTable as te, frameworkTables as x, isMigrationDefinition as ne, quoteIdent as
|
|
14
|
-
import { createHash as
|
|
15
|
-
import { DEFAULT_CDC_CHANNEL as
|
|
13
|
+
import { actorsTable as te, frameworkTables as x, isMigrationDefinition as ne, quoteIdent as S, registerCoreTables as re } from "@voltro/database";
|
|
14
|
+
import { createHash as ie } from "node:crypto";
|
|
15
|
+
import { DEFAULT_CDC_CHANNEL as ae, applyPlan as C, applySchema as w, assessRollingDeployGate as oe, declaredSnapshot as se, destructiveScope as ce, detectReactiveTriggerDrift as le, fingerprintSchema as T, ignoreTablesFromEnv as E, introspectSchema as ue, pendingFileMigrationIds as de, planMigrations as D, reactiveTriggerRepairSql as fe, rollbackFileBasedMigration as pe, rollingDeployUnsafeOps as me, runFileBasedMigrations as he, shortFingerprint as O, squashMigrationPlans as ge, unblockLossy as _e } from "@voltro/database/sql";
|
|
16
16
|
//#region src/cliError.ts
|
|
17
|
-
var
|
|
17
|
+
var k = class extends Error {
|
|
18
18
|
isCliError = !0;
|
|
19
19
|
code;
|
|
20
20
|
constructor(e, t = 1) {
|
|
21
21
|
super(e), this.name = "CliError", this.code = t;
|
|
22
22
|
}
|
|
23
|
-
},
|
|
23
|
+
}, ve = (e) => typeof e == "object" && !!e && e.isCliError === !0, ye = (e) => typeof e != "string", be = (e, t, n, r) => {
|
|
24
24
|
let i = [], a = new Map(t.map((e) => [e.name, e])), o = new Map(n.map((e) => [e.name, e])), s = new Set(e.leaveUnset ?? []);
|
|
25
25
|
r > 0 && i.push({
|
|
26
26
|
kind: "target-not-empty",
|
|
@@ -37,7 +37,7 @@ var ye = class extends Error {
|
|
|
37
37
|
});
|
|
38
38
|
continue;
|
|
39
39
|
}
|
|
40
|
-
if (
|
|
40
|
+
if (ye(n)) {
|
|
41
41
|
c.push({
|
|
42
42
|
target: t,
|
|
43
43
|
source: n.expr,
|
|
@@ -80,23 +80,23 @@ var ye = class extends Error {
|
|
|
80
80
|
remintIds: f,
|
|
81
81
|
fatal: i.some((e) => e.fatal)
|
|
82
82
|
};
|
|
83
|
-
},
|
|
83
|
+
}, xe = (e, t) => [
|
|
84
84
|
`back up '${e.from}' to a restorable dump`,
|
|
85
85
|
`copy rows: INSERT INTO ${e.into} (${t.assignments.map((e) => e.target).join(", ")}) SELECT … FROM ${e.from}`,
|
|
86
86
|
...t.remintIds ? ["record old id → new id in a translation table", `rewrite every reference to '${e.from}', JSON columns included`] : [],
|
|
87
87
|
"verify: row counts match and a sample round-trips",
|
|
88
88
|
`drop '${e.from}' — LAST, and only after the verify`
|
|
89
|
-
],
|
|
89
|
+
], Se = (e, t, n) => {
|
|
90
90
|
let r = [`adopt · ${e.from} (${n} row(s)) → ${e.into}`, ""];
|
|
91
91
|
for (let e of t.assignments) r.push(` ${e.target.padEnd(24)} ← ${e.raw ? `${e.source} (raw)` : e.source}`);
|
|
92
92
|
t.problems.length > 0 && r.push("");
|
|
93
93
|
for (let e of t.problems) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.detail}`);
|
|
94
94
|
if (r.push(""), t.fatal) return r.push(" REFUSED — fix the problems above. Nothing was written."), r;
|
|
95
95
|
r.push(" would run, in this order:");
|
|
96
|
-
for (let [n, i] of
|
|
96
|
+
for (let [n, i] of xe(e, t).entries()) r.push(` ${n + 1}. ${i}`);
|
|
97
97
|
return r;
|
|
98
|
-
},
|
|
99
|
-
let a =
|
|
98
|
+
}, Ce = (e) => e === "mysql" || e === "mariadb" ? (e) => `\`${e}\`` : e === "mssql" ? (e) => `[${e}]` : (e) => `"${e}"`, we = (e) => `${e}__adopt_snapshot`, Te = (e, t, n, r, i) => {
|
|
99
|
+
let a = Ce(n), o = we(e.from), s = t.assignments.map((e) => e.target), c = t.assignments.map((e) => e.raw ? e.source : a(e.source));
|
|
100
100
|
return i !== void 0 && (s.unshift(r), c.unshift(i)), {
|
|
101
101
|
snapshot: n === "mssql" ? `SELECT * INTO ${a(o)} FROM ${a(e.from)}` : `CREATE TABLE ${a(o)} AS SELECT * FROM ${a(e.from)}`,
|
|
102
102
|
copy: `INSERT INTO ${a(e.into)} (${s.map(a).join(", ")}) SELECT ${c.join(", ")} FROM ${a(e.from)}`,
|
|
@@ -106,18 +106,18 @@ var ye = class extends Error {
|
|
|
106
106
|
drop: `DROP TABLE ${a(e.from)}`,
|
|
107
107
|
dropSnapshot: `DROP TABLE ${a(o)}`
|
|
108
108
|
};
|
|
109
|
-
},
|
|
109
|
+
}, A = (e) => {
|
|
110
110
|
let t = e[0];
|
|
111
111
|
if (t === void 0) return 0;
|
|
112
112
|
let n = t.n ?? t.N ?? Object.values(t)[0];
|
|
113
113
|
return Number(n ?? 0);
|
|
114
|
-
},
|
|
114
|
+
}, Ee = async (e, t, n, r, i, a, o = {}) => {
|
|
115
115
|
if (t.fatal) throw Error("adopt: the plan was refused — nothing was run");
|
|
116
|
-
let s =
|
|
116
|
+
let s = Te(e, t, n, r, i), c = we(e.from), l = A(await a(s.verifySource));
|
|
117
117
|
await a(s.snapshot);
|
|
118
118
|
for (let e of s.translations) await a(e);
|
|
119
119
|
await a(s.copy);
|
|
120
|
-
let u =
|
|
120
|
+
let u = A(await a(s.verifyTarget));
|
|
121
121
|
return u === l ? o.keepSource === !0 ? {
|
|
122
122
|
copied: u,
|
|
123
123
|
sourceRows: l,
|
|
@@ -137,7 +137,68 @@ var ye = class extends Error {
|
|
|
137
137
|
snapshotTable: c,
|
|
138
138
|
sourceDropped: !1
|
|
139
139
|
};
|
|
140
|
-
},
|
|
140
|
+
}, De = [
|
|
141
|
+
"token",
|
|
142
|
+
"secret",
|
|
143
|
+
"password",
|
|
144
|
+
"apikey",
|
|
145
|
+
"api_key",
|
|
146
|
+
"credential",
|
|
147
|
+
"privatekey",
|
|
148
|
+
"private_key"
|
|
149
|
+
], Oe = ["_voltro_audit_log", "_voltro_row_history"], ke = (e, t) => {
|
|
150
|
+
switch (t) {
|
|
151
|
+
case "postgres": return `LOWER(CAST(${e} AS text))`;
|
|
152
|
+
case "mysql":
|
|
153
|
+
case "mariadb": return `LOWER(CAST(${e} AS CHAR))`;
|
|
154
|
+
case "mssql": return `LOWER(CAST(${e} AS NVARCHAR(MAX)))`;
|
|
155
|
+
case "sqlite":
|
|
156
|
+
case "turso": return `LOWER(CAST(${e} AS TEXT))`;
|
|
157
|
+
}
|
|
158
|
+
}, Ae = (e, t, n = De) => n.map((n) => `${ke(e, t)} LIKE '%${n}%'`).join("\n OR "), je = async (e, t) => {
|
|
159
|
+
let n = await g.runPromise(e.unsafe(t));
|
|
160
|
+
return Number(Object.values(n[0] ?? { n: 0 })[0] ?? 0);
|
|
161
|
+
}, Me = async (e, t, n, r) => {
|
|
162
|
+
let i = [];
|
|
163
|
+
for (let a of n) {
|
|
164
|
+
let n = r(a.table), o;
|
|
165
|
+
try {
|
|
166
|
+
o = await je(e, `SELECT COUNT(*) AS n FROM ${n}`);
|
|
167
|
+
} catch {
|
|
168
|
+
i.push({
|
|
169
|
+
table: a.table,
|
|
170
|
+
column: a.column,
|
|
171
|
+
scanned: void 0,
|
|
172
|
+
hits: void 0
|
|
173
|
+
});
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
let s = await je(e, `SELECT COUNT(*) AS n FROM ${n}\n WHERE ${Ae(r(a.column), t)}`);
|
|
177
|
+
i.push({
|
|
178
|
+
table: a.table,
|
|
179
|
+
column: a.column,
|
|
180
|
+
scanned: o,
|
|
181
|
+
hits: s
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return i;
|
|
185
|
+
}, Ne = (e, t) => {
|
|
186
|
+
let n = [`credential scan · dialect ${t}`, ""];
|
|
187
|
+
for (let t of e) {
|
|
188
|
+
if (t.scanned === void 0) {
|
|
189
|
+
n.push(` · ${t.table} — table not present (nothing scanned)`);
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (t.scanned === 0) {
|
|
193
|
+
n.push(` · ${t.table} — table is EMPTY (0 rows scanned; this is not a clean bill of health)`);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
let e = (t.hits ?? 0) > 0 ? "✗" : "✓";
|
|
197
|
+
n.push(` ${e} ${t.table}.${t.column} — ${t.hits} of ${t.scanned} row(s) match a credential-shaped key`);
|
|
198
|
+
}
|
|
199
|
+
let r = e.reduce((e, t) => e + (t.hits ?? 0), 0);
|
|
200
|
+
return n.push(""), r > 0 ? n.push(` ${r} row(s) carry something credential-shaped.`, " Purge them AND rotate the credentials — assume anything written to a log", " table has been read. Then stop putting the credential on the Subject:", " `connectionCredentials(...)` keeps it in the framework vault.") : e.every((e) => e.scanned === void 0 || e.scanned === 0) ? n.push(" NOTHING WAS SCANNED. This run proves nothing about your data.") : n.push(" No credential-shaped keys found in the rows that exist."), n.push("", " Run this on EVERY environment. A dev database is not a sample of production."), n.join("\n");
|
|
201
|
+
}, Pe = (e) => e.some((e) => (e.hits ?? 0) > 0) ? 1 : e.every((e) => e.scanned === void 0 || e.scanned === 0) ? 2 : 0, j = /\.migration\.ts$/, M = async (e, t) => {
|
|
141
202
|
let n = [];
|
|
142
203
|
for (let r of e) {
|
|
143
204
|
let e;
|
|
@@ -157,7 +218,7 @@ var ye = class extends Error {
|
|
|
157
218
|
idInFile: i.id,
|
|
158
219
|
idFromFile: d(r).replace(/\.migration\.ts$/, "")
|
|
159
220
|
});
|
|
160
|
-
let a = await v.readFile(r, "utf8").catch(() => ""), o =
|
|
221
|
+
let a = await v.readFile(r, "utf8").catch(() => ""), o = ie("sha256").update(a).digest("hex").slice(0, 16);
|
|
161
222
|
n.push({
|
|
162
223
|
file: r,
|
|
163
224
|
definition: i,
|
|
@@ -165,13 +226,13 @@ var ye = class extends Error {
|
|
|
165
226
|
});
|
|
166
227
|
}
|
|
167
228
|
return [...n].sort((e, t) => e.definition.id < t.definition.id ? -1 : 1);
|
|
168
|
-
},
|
|
229
|
+
}, N = (e) => {
|
|
169
230
|
let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase(), n = t === "memory" ? "postgres" : t;
|
|
170
|
-
return g.runPromise(
|
|
171
|
-
},
|
|
231
|
+
return g.runPromise(w(x, n).pipe(g.provide(e)));
|
|
232
|
+
}, P = (e) => g.runPromise(g.gen(function* () {
|
|
172
233
|
let e = yield* (yield* b.SqlClient)`SELECT id, status, hash, "appliedAt" FROM "_voltro_migrations"`.pipe(g.orElseSucceed(() => []));
|
|
173
234
|
return new Map(e.map((e) => [e.id, e]));
|
|
174
|
-
}).pipe(g.provide(e))),
|
|
235
|
+
}).pipe(g.provide(e))), F = (e, t) => g.runPromise(g.gen(function* () {
|
|
175
236
|
yield* (yield* b.SqlClient)`
|
|
176
237
|
INSERT INTO "_voltro_migrations"
|
|
177
238
|
(id, name, hash, status, "appliedAt", "durationMs", "errorMessage", "errorStack")
|
|
@@ -187,14 +248,14 @@ var ye = class extends Error {
|
|
|
187
248
|
"errorStack" = EXCLUDED."errorStack",
|
|
188
249
|
"updatedAt" = now()
|
|
189
250
|
`;
|
|
190
|
-
}).pipe(g.provide(e))),
|
|
251
|
+
}).pipe(g.provide(e))), Fe = async (e, t, n) => {
|
|
191
252
|
if (e.length === 0) return {
|
|
192
253
|
applied: [],
|
|
193
254
|
failed: null,
|
|
194
255
|
skipped: []
|
|
195
256
|
};
|
|
196
|
-
await
|
|
197
|
-
let r = await
|
|
257
|
+
await N(t);
|
|
258
|
+
let r = await P(t), i = [], a = [], o = null;
|
|
198
259
|
for (let s of e) {
|
|
199
260
|
let e = r.get(s.definition.id);
|
|
200
261
|
if (e && e.status === "applied") {
|
|
@@ -215,7 +276,7 @@ var ye = class extends Error {
|
|
|
215
276
|
name: s.definition.name
|
|
216
277
|
});
|
|
217
278
|
let c = Date.now(), l = [];
|
|
218
|
-
await
|
|
279
|
+
await F(t, {
|
|
219
280
|
id: s.definition.id,
|
|
220
281
|
name: s.definition.name,
|
|
221
282
|
hash: s.hash,
|
|
@@ -256,7 +317,7 @@ var ye = class extends Error {
|
|
|
256
317
|
...a.stack ? { stack: a.stack } : {}
|
|
257
318
|
}
|
|
258
319
|
};
|
|
259
|
-
o = f, i.push(f), await
|
|
320
|
+
o = f, i.push(f), await F(t, {
|
|
260
321
|
id: s.definition.id,
|
|
261
322
|
name: s.definition.name,
|
|
262
323
|
hash: s.hash,
|
|
@@ -275,7 +336,7 @@ var ye = class extends Error {
|
|
|
275
336
|
status: "applied",
|
|
276
337
|
durationMs: Date.now() - c,
|
|
277
338
|
steps: l
|
|
278
|
-
}), await
|
|
339
|
+
}), await F(t, {
|
|
279
340
|
id: s.definition.id,
|
|
280
341
|
name: s.definition.name,
|
|
281
342
|
hash: s.hash,
|
|
@@ -295,9 +356,9 @@ var ye = class extends Error {
|
|
|
295
356
|
failed: o,
|
|
296
357
|
skipped: a
|
|
297
358
|
};
|
|
298
|
-
},
|
|
299
|
-
await
|
|
300
|
-
let i = await
|
|
359
|
+
}, Ie = async (e, t, n, r = {}) => {
|
|
360
|
+
await N(t);
|
|
361
|
+
let i = await P(t), a = e.filter((e) => i.get(e.definition.id)?.status === "applied");
|
|
301
362
|
if (a.length === 0) return n.info("migration: nothing to roll back"), {
|
|
302
363
|
applied: [],
|
|
303
364
|
failed: null,
|
|
@@ -350,7 +411,7 @@ var ye = class extends Error {
|
|
|
350
411
|
status: "applied",
|
|
351
412
|
durationMs: Date.now() - r,
|
|
352
413
|
steps: i
|
|
353
|
-
}), await
|
|
414
|
+
}), await F(t, {
|
|
354
415
|
id: e.definition.id,
|
|
355
416
|
name: e.definition.name,
|
|
356
417
|
hash: e.hash,
|
|
@@ -366,9 +427,9 @@ var ye = class extends Error {
|
|
|
366
427
|
failed: c,
|
|
367
428
|
skipped: []
|
|
368
429
|
};
|
|
369
|
-
},
|
|
370
|
-
await
|
|
371
|
-
let n = await
|
|
430
|
+
}, I = async (e, t) => {
|
|
431
|
+
await N(t);
|
|
432
|
+
let n = await P(t);
|
|
372
433
|
return e.map((e) => {
|
|
373
434
|
let t = n.get(e.definition.id);
|
|
374
435
|
return t ? {
|
|
@@ -385,7 +446,7 @@ var ye = class extends Error {
|
|
|
385
446
|
hashMatches: !0
|
|
386
447
|
};
|
|
387
448
|
});
|
|
388
|
-
},
|
|
449
|
+
}, L = (e) => {
|
|
389
450
|
if (!e.failed) return "";
|
|
390
451
|
let t = e.failed, n = [
|
|
391
452
|
`✗ Migration failed: ${t.migrationId} / step '${t.error?.stepName ?? "?"}'`,
|
|
@@ -398,7 +459,7 @@ var ye = class extends Error {
|
|
|
398
459
|
""
|
|
399
460
|
];
|
|
400
461
|
return e.applied.length > 0 && n.unshift("", `(${e.applied.length} migration(s) applied successfully before the failure.)`), n.join("\n");
|
|
401
|
-
},
|
|
462
|
+
}, R = _({ scope: "voltro:db" }), Le = /* @__PURE__ */ new Set([
|
|
402
463
|
"node_modules",
|
|
403
464
|
"dist",
|
|
404
465
|
"build",
|
|
@@ -406,17 +467,17 @@ var ye = class extends Error {
|
|
|
406
467
|
".vite",
|
|
407
468
|
".next",
|
|
408
469
|
".git"
|
|
409
|
-
]),
|
|
470
|
+
]), z = async (e, t) => {
|
|
410
471
|
let n = [], r = async (e) => {
|
|
411
472
|
let i = await v.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
412
473
|
for (let a of i) {
|
|
413
|
-
if (
|
|
474
|
+
if (Le.has(a.name)) continue;
|
|
414
475
|
let i = f(e, a.name);
|
|
415
476
|
a.isDirectory() ? await r(i) : a.isFile() && t.test(a.name) && n.push(i);
|
|
416
477
|
}
|
|
417
478
|
};
|
|
418
479
|
return await r(e), n;
|
|
419
|
-
},
|
|
480
|
+
}, B = (e) => m(e.find((e) => !e.startsWith("-")) ?? process.cwd()), V = () => {
|
|
420
481
|
let e = process.env.DB_MAX_CONNECTIONS ?? process.env.PG_MAX_CONNECTIONS, t = e !== void 0 && e !== "" ? Number(e) : NaN, n = Number.isInteger(t) && t > 0 ? { maxConnections: t } : {}, r = process.env.DB_SCHEMA, i = r && r.length > 0 ? { schema: r } : {}, a = process.env.DB_DIRECT_URL ?? process.env.DB_MIGRATE_URL ?? process.env.DB_URL ?? process.env.DB_PRIMARY_URL;
|
|
421
482
|
return a ? {
|
|
422
483
|
url: a,
|
|
@@ -431,63 +492,63 @@ var ye = class extends Error {
|
|
|
431
492
|
...n,
|
|
432
493
|
...i
|
|
433
494
|
};
|
|
434
|
-
},
|
|
495
|
+
}, H = (e) => {
|
|
435
496
|
let t = e;
|
|
436
497
|
if (typeof t != "object" || !t) return !1;
|
|
437
498
|
if (t.code === "ERR_OUT_OF_RANGE" || t.cause?.code === "ERR_OUT_OF_RANGE") return !0;
|
|
438
499
|
let n = `${t.message ?? ""} ${t.cause?.message ?? ""} ${t.stack ?? ""}`;
|
|
439
500
|
return /ERR_OUT_OF_RANGE|out of range/i.test(n) && /pg-protocol|parseDataRow|buffer-reader|BufferReader/i.test(n);
|
|
440
|
-
},
|
|
501
|
+
}, U = "Live-DB introspection got a corrupt / misaligned response from the database. This happens when `voltro db plan` / `apply` / `drift` / `migrate` runs through a connection POOLER (Supabase Supavisor / PgBouncer) on a LARGE schema — the pooler mis-frames a big catalog response. The introspection already batches by table to keep responses small; if a batch still trips it:\n 1. Lower the batch: set `VOLTRO_INTROSPECT_BATCH` to a smaller number of tables (default 20 — try 5 or 1). This keeps `db plan` on the normal pooler, no direct connection needed.\n 2. Or use a DIRECT (non-pooler) connection: set `DB_DIRECT_URL` (or `DB_MIGRATE_URL`) to the direct database URL — the runtime keeps `DB_URL`, so only the migration path is affected. (Supabase: direct = `db.<project-ref>.supabase.co` [IPv6-only on newer projects]; pooler = `aws-0-<region>.pooler.supabase.com`.)", W = () => {
|
|
441
502
|
let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
442
|
-
if (e === "memory") throw new
|
|
503
|
+
if (e === "memory") throw new k("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
|
|
443
504
|
return e;
|
|
444
|
-
},
|
|
445
|
-
let t =
|
|
446
|
-
if (r.length === 0) return
|
|
505
|
+
}, G = (e) => S(e, W()), K = (e) => e === "mysql" || e === "mariadb" ? "table_schema = DATABASE()" : e === "mssql" ? "table_schema = SCHEMA_NAME()" : "table_schema = 'public'", q = async () => (await r(W())).makeSqlLayer(V()), Re = async (e) => {
|
|
506
|
+
let t = B(e), n = e.includes("--dry-run"), r = await M(await z(t, j), R);
|
|
507
|
+
if (r.length === 0) return R.info("no migrations found", { root: t }), 0;
|
|
447
508
|
if (n) {
|
|
448
509
|
let e = /* @__PURE__ */ new Set();
|
|
449
510
|
try {
|
|
450
|
-
let t = await
|
|
511
|
+
let t = await I(r, await q());
|
|
451
512
|
e = new Set(t.filter((e) => e.status === "applied").map((e) => e.id));
|
|
452
513
|
} catch {
|
|
453
|
-
|
|
514
|
+
R.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
|
|
454
515
|
}
|
|
455
516
|
let t = r.filter((t) => !e.has(t.definition.id));
|
|
456
|
-
if (t.length === 0) return
|
|
457
|
-
|
|
517
|
+
if (t.length === 0) return R.info("migrate --dry-run: nothing pending — every migration is applied"), 0;
|
|
518
|
+
R.info(`migrate --dry-run: ${t.length} pending (nothing applied)`);
|
|
458
519
|
for (let e of t) console.log(` pending ${e.definition.id}`);
|
|
459
520
|
return 0;
|
|
460
521
|
}
|
|
461
|
-
let i = await
|
|
462
|
-
return i.failed ? (process.stderr.write(
|
|
522
|
+
let i = await Fe(r, await q(), R);
|
|
523
|
+
return i.failed ? (process.stderr.write(L(i) + "\n"), 1) : (R.info("migrate complete", {
|
|
463
524
|
applied: i.applied.length,
|
|
464
525
|
skipped: i.skipped.length
|
|
465
526
|
}), 0);
|
|
466
|
-
},
|
|
467
|
-
let t =
|
|
468
|
-
if (r.length === 0) return
|
|
469
|
-
let i = await
|
|
470
|
-
return i.failed ? (process.stderr.write(
|
|
471
|
-
},
|
|
472
|
-
let t = await
|
|
527
|
+
}, ze = async (e) => {
|
|
528
|
+
let t = B(e), n = l(e, "--to"), r = await M(await z(t, j), R);
|
|
529
|
+
if (r.length === 0) return R.info("no migrations found", { root: t }), 0;
|
|
530
|
+
let i = await Ie(r, await q(), R, n ? { to: n } : {});
|
|
531
|
+
return i.failed ? (process.stderr.write(L(i) + "\n"), 1) : (R.info("rollback complete", { reverted: i.applied.length }), 0);
|
|
532
|
+
}, Be = async (e) => {
|
|
533
|
+
let t = await M(await z(B(e), j), R);
|
|
473
534
|
if (t.length === 0) return console.log("no migrations found"), 0;
|
|
474
|
-
let n = await
|
|
535
|
+
let n = await I(t, await q());
|
|
475
536
|
for (let e of n) {
|
|
476
537
|
let t = e.status === "applied" ? "✓" : e.status === "failed" ? "✗" : e.status === "reverted" ? "↩" : "·", n = e.hashMatches ? "" : " (hash drift!)", r = e.appliedAt ? ` @ ${e.appliedAt.toISOString()}` : "";
|
|
477
538
|
console.log(` ${t} ${e.id} [${e.status}] ${e.name}${r}${n}`);
|
|
478
539
|
}
|
|
479
540
|
return 0;
|
|
480
|
-
},
|
|
541
|
+
}, Ve = async (e) => {
|
|
481
542
|
let t = (l(e, "--store") ?? process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
482
|
-
if (t === "memory") return
|
|
543
|
+
if (t === "memory") return R.warn("voltro db seed: using in-memory store (explicit opt-in via --store memory / DB_DIALECT=memory)"), R.warn(" rows written by this run will NOT persist beyond the process. set DB_DIALECT=postgres or unset to seed the real DB."), {
|
|
483
544
|
store: new ee(),
|
|
484
545
|
dispose: async () => {},
|
|
485
546
|
dialectId: "memory"
|
|
486
547
|
};
|
|
487
548
|
let n = await r(t);
|
|
488
|
-
|
|
549
|
+
R.info(`voltro db seed: target store=${n.id} — rows will persist`);
|
|
489
550
|
let i = await n.makeStore({
|
|
490
|
-
sqlLayer: n.makeSqlLayer(
|
|
551
|
+
sqlLayer: n.makeSqlLayer(V()),
|
|
491
552
|
changeStrategy: "inline"
|
|
492
553
|
}), a = i;
|
|
493
554
|
return {
|
|
@@ -497,77 +558,77 @@ var ye = class extends Error {
|
|
|
497
558
|
},
|
|
498
559
|
dialectId: n.id
|
|
499
560
|
};
|
|
500
|
-
},
|
|
501
|
-
let a =
|
|
502
|
-
if (c.length === 0) return
|
|
503
|
-
let { store: u, dispose: d, dialectId: f } = await
|
|
561
|
+
}, He = async (r) => {
|
|
562
|
+
let a = B(r), o = l(r, "--id"), s = l(r, "--lifecycle"), c = await t(await z(a, i), R);
|
|
563
|
+
if (c.length === 0) return R.info("no seeds found", { root: a }), 0;
|
|
564
|
+
let { store: u, dispose: d, dialectId: f } = await Ve(r);
|
|
504
565
|
try {
|
|
505
|
-
return o ? +((await e(c, o, u,
|
|
566
|
+
return o ? +((await e(c, o, u, R)).status === "failed") : +((await n((s ? c.filter((e) => e.definition.lifecycle === s) : c.filter((e) => e.definition.lifecycle === "boot")).map((e) => ({
|
|
506
567
|
...e,
|
|
507
568
|
definition: {
|
|
508
569
|
...e.definition,
|
|
509
570
|
lifecycle: "boot"
|
|
510
571
|
}
|
|
511
|
-
})), u,
|
|
572
|
+
})), u, R, { force: !0 })).filter((e) => e.status === "failed").length > 0);
|
|
512
573
|
} finally {
|
|
513
574
|
await d().catch((e) => {
|
|
514
|
-
|
|
575
|
+
R.warn("voltro db seed: store dispose failed", { error: String(e) });
|
|
515
576
|
});
|
|
516
577
|
}
|
|
517
|
-
},
|
|
518
|
-
let t = await
|
|
578
|
+
}, Ue = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", We = async (e) => {
|
|
579
|
+
let t = await z(e, s), n = [];
|
|
519
580
|
for (let r of t) {
|
|
520
581
|
let t;
|
|
521
582
|
try {
|
|
522
583
|
t = await import(h(r).href);
|
|
523
584
|
} catch (t) {
|
|
524
|
-
|
|
585
|
+
R.warn("db: failed to import schema file", {
|
|
525
586
|
file: p(e, r),
|
|
526
587
|
error: String(t)
|
|
527
588
|
});
|
|
528
589
|
continue;
|
|
529
590
|
}
|
|
530
|
-
for (let e of Object.values(t))
|
|
591
|
+
for (let e of Object.values(t)) Ue(e) && n.push(e);
|
|
531
592
|
}
|
|
532
593
|
return n;
|
|
533
|
-
},
|
|
594
|
+
}, Ge = (e) => {
|
|
534
595
|
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
535
596
|
t.has("actors") || t.set("actors", te);
|
|
536
597
|
let n = t.get("actors"), r = t.get("tenants");
|
|
537
|
-
return
|
|
598
|
+
return re({
|
|
538
599
|
actors: () => n,
|
|
539
600
|
...r === void 0 ? {} : { tenants: () => r }
|
|
540
601
|
}), [...t.values()];
|
|
541
|
-
},
|
|
602
|
+
}, Ke = (e) => {
|
|
542
603
|
let t = /* @__PURE__ */ new Map();
|
|
543
604
|
for (let n of e) {
|
|
544
605
|
let e = n.tableName;
|
|
545
606
|
e !== void 0 && !t.has(e) && t.set(e, n);
|
|
546
607
|
}
|
|
547
608
|
return [...t.values()];
|
|
548
|
-
},
|
|
549
|
-
let t =
|
|
609
|
+
}, qe = (e) => {
|
|
610
|
+
let t = ce(process.env.VOLTRO_DESTRUCTIVE_OK);
|
|
550
611
|
if (t === void 0 || !e.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy")) return {
|
|
551
612
|
plan: e,
|
|
552
613
|
acknowledged: !1
|
|
553
614
|
};
|
|
554
|
-
let n =
|
|
615
|
+
let n = _e(e, t);
|
|
555
616
|
return {
|
|
556
617
|
plan: n,
|
|
557
618
|
acknowledged: !n.operations.some((e) => e.blocked)
|
|
558
619
|
};
|
|
559
|
-
},
|
|
620
|
+
}, J = async (e, t) => g.runPromise(t.pipe(g.provide(e))), Y = async (e) => {
|
|
560
621
|
try {
|
|
561
|
-
return await
|
|
622
|
+
return await J(e, b.SqlClient.pipe(g.flatMap(ue)));
|
|
562
623
|
} catch (e) {
|
|
563
624
|
let t = String(e?.message ?? e);
|
|
564
|
-
if (
|
|
565
|
-
let n = /* @__PURE__ */ Error(`${
|
|
625
|
+
if (H(e) || /connection error/i.test(t)) {
|
|
626
|
+
let n = /* @__PURE__ */ Error(`${U}\n\nOriginal error: ${t}`);
|
|
566
627
|
throw n.cause = e, n;
|
|
567
628
|
}
|
|
568
629
|
throw e;
|
|
569
630
|
}
|
|
570
|
-
},
|
|
631
|
+
}, Je = {
|
|
571
632
|
safe: "\x1B[32m✓\x1B[0m",
|
|
572
633
|
"needs-default": "\x1B[32m⊕\x1B[0m",
|
|
573
634
|
"needs-backfill": "\x1B[33m⊕\x1B[0m",
|
|
@@ -575,7 +636,7 @@ var ye = class extends Error {
|
|
|
575
636
|
lossy: "\x1B[31m✗\x1B[0m",
|
|
576
637
|
"online-required": "\x1B[36m◷\x1B[0m",
|
|
577
638
|
"multi-step": "\x1B[35m⇉\x1B[0m"
|
|
578
|
-
},
|
|
639
|
+
}, Ye = (e) => {
|
|
579
640
|
let t = e.op, n;
|
|
580
641
|
switch (t.kind) {
|
|
581
642
|
case "create-table":
|
|
@@ -639,9 +700,9 @@ var ye = class extends Error {
|
|
|
639
700
|
n = `ALTER TABLE ${t.table} DROP CHECK ${t.column}`;
|
|
640
701
|
break;
|
|
641
702
|
}
|
|
642
|
-
let r =
|
|
703
|
+
let r = Je[e.classification], i = e.reason ? ` [2m# ${e.reason}[0m` : "", a = e.blocked ? `\n [31m! ${e.blocked.fix}[0m` : "";
|
|
643
704
|
return ` ${r} ${n}${i}${a}`;
|
|
644
|
-
},
|
|
705
|
+
}, Xe = async (e, t) => {
|
|
645
706
|
let n = e.operations.filter((e) => e.classification === "needs-default" && e.op.kind === "add-column");
|
|
646
707
|
if (n.length === 0) return;
|
|
647
708
|
let r = [];
|
|
@@ -650,7 +711,7 @@ var ye = class extends Error {
|
|
|
650
711
|
if (i === void 0 || a === void 0) continue;
|
|
651
712
|
let o = 0;
|
|
652
713
|
try {
|
|
653
|
-
let e = await
|
|
714
|
+
let e = await J(t, b.SqlClient.pipe(g.flatMap((e) => e.unsafe(`SELECT COUNT(*) AS n FROM ${S(i, W())}`))));
|
|
654
715
|
o = Number(Object.values(e[0] ?? { n: 0 })[0] ?? 0);
|
|
655
716
|
} catch {
|
|
656
717
|
continue;
|
|
@@ -662,15 +723,15 @@ var ye = class extends Error {
|
|
|
662
723
|
for (let e of r) console.log(e);
|
|
663
724
|
console.log(" (the shape is correct either way — this is the VALUE a state diff cannot know)");
|
|
664
725
|
}
|
|
665
|
-
},
|
|
666
|
-
if (
|
|
667
|
-
let n =
|
|
726
|
+
}, X = async (e, t) => {
|
|
727
|
+
if (W() !== "postgres") return;
|
|
728
|
+
let n = ae, r;
|
|
668
729
|
try {
|
|
669
|
-
r = await
|
|
730
|
+
r = await J(t, b.SqlClient.pipe(g.flatMap((t) => le(t, e, n))));
|
|
670
731
|
} catch {
|
|
671
732
|
return;
|
|
672
733
|
}
|
|
673
|
-
let i =
|
|
734
|
+
let i = fe({
|
|
674
735
|
tables: e,
|
|
675
736
|
dialect: "postgres",
|
|
676
737
|
missing: r.missing,
|
|
@@ -678,53 +739,53 @@ var ye = class extends Error {
|
|
|
678
739
|
channel: n
|
|
679
740
|
});
|
|
680
741
|
if (i.length === 0) return;
|
|
681
|
-
|
|
742
|
+
R.info(`db apply: installing change triggers on ${r.missing.length} table(s)` + (r.stale.length > 0 ? `, removing ${r.stale.length} stale one(s)` : ""));
|
|
682
743
|
let a = 0;
|
|
683
|
-
for (let e of i) await
|
|
684
|
-
|
|
744
|
+
for (let e of i) await J(t, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(e)))), a += 1;
|
|
745
|
+
R.info(`db apply: change triggers converged (${a} statement(s))`, {
|
|
685
746
|
installed: r.missing.length,
|
|
686
747
|
removed: r.stale.length
|
|
687
748
|
});
|
|
688
|
-
},
|
|
749
|
+
}, Z = (e) => {
|
|
689
750
|
let t = e.summary, n = e.operations.length;
|
|
690
751
|
if (console.log(`\nschema diff: ${n} operation${n === 1 ? "" : "s"}, ${t.blocked} blocked\n`), n === 0) {
|
|
691
752
|
console.log(" (schema is up to date)\n");
|
|
692
753
|
return;
|
|
693
754
|
}
|
|
694
|
-
for (let t of e.operations) console.log(
|
|
695
|
-
console.log(), console.log(` safe: ${t.safe} needs-default: ${t.needsDefault} needs-backfill: ${t.needsBackfill}`), console.log(` rename: ${t.needsRenameAnnotation} lossy: ${t.lossy} blocked: ${t.blocked}`), console.log(` fingerprint: ${e.fromFingerprint.slice(0, 16)} → ${e.toFingerprint.slice(0, 16)}\n`),
|
|
696
|
-
},
|
|
697
|
-
let t =
|
|
755
|
+
for (let t of e.operations) console.log(Ye(t));
|
|
756
|
+
console.log(), console.log(` safe: ${t.safe} needs-default: ${t.needsDefault} needs-backfill: ${t.needsBackfill}`), console.log(` rename: ${t.needsRenameAnnotation} lossy: ${t.lossy} blocked: ${t.blocked}`), console.log(` fingerprint: ${e.fromFingerprint.slice(0, 16)} → ${e.toFingerprint.slice(0, 16)}\n`), Ze(e);
|
|
757
|
+
}, Ze = (e) => {
|
|
758
|
+
let t = me(e.operations);
|
|
698
759
|
if (t.length !== 0) {
|
|
699
760
|
console.log(` ⚠ ${t.length} operation(s) UNSAFE under a rolling deploy`), console.log(" (old + new instances overlap → old code breaks against the new schema):");
|
|
700
761
|
for (let e of t) console.log(` • ${e.op.op.kind}: ${e.reason}`), console.log(` → ${e.remedy}`);
|
|
701
762
|
console.log(" Deploy with a maintenance window / scale-to-zero, or split into"), console.log(" expand → deploy → contract. See docs: deployment/production-hardening.\n");
|
|
702
763
|
}
|
|
703
|
-
},
|
|
704
|
-
let n =
|
|
764
|
+
}, Qe = (e, t) => {
|
|
765
|
+
let n = oe({
|
|
705
766
|
enabled: process.env.VOLTRO_ROLLING_DEPLOY === "1",
|
|
706
767
|
forced: t.includes("--force"),
|
|
707
|
-
unsafeCount:
|
|
768
|
+
unsafeCount: me(e.operations).length
|
|
708
769
|
});
|
|
709
|
-
return n.refuse ? (
|
|
710
|
-
},
|
|
770
|
+
return n.refuse ? (R.error(`db apply: ${n.message}`), !0) : (n.warnForced && R.warn(`db apply: ${n.message}`), !1);
|
|
771
|
+
}, $e = (e) => {
|
|
711
772
|
let t = l(e, "--online-after") ?? process.env.VOLTRO_ONLINE_THRESHOLD;
|
|
712
773
|
if (t === void 0) return;
|
|
713
774
|
let n = Number(t);
|
|
714
775
|
if (!Number.isInteger(n) || n < 0) throw Error(`--online-after / VOLTRO_ONLINE_THRESHOLD must be a non-negative integer, got "${t}"`);
|
|
715
776
|
return n;
|
|
716
|
-
},
|
|
717
|
-
let t = await
|
|
777
|
+
}, Q = async (e) => {
|
|
778
|
+
let t = await We(e);
|
|
718
779
|
if (t.length === 0) return;
|
|
719
780
|
let n = await o({ root: e });
|
|
720
|
-
return
|
|
781
|
+
return Ge(Ke([
|
|
721
782
|
...t,
|
|
722
783
|
...x,
|
|
723
784
|
...n
|
|
724
785
|
]));
|
|
725
|
-
},
|
|
726
|
-
let t =
|
|
727
|
-
if (r === void 0) return
|
|
786
|
+
}, et = async (e) => {
|
|
787
|
+
let t = $e(e), n = B(e), r = await Q(n);
|
|
788
|
+
if (r === void 0) return R.warn("db plan: no schema files found", {
|
|
728
789
|
root: n,
|
|
729
790
|
hint: "looked for *.entity.ts / *.schema.ts / schema.ts"
|
|
730
791
|
}), 1;
|
|
@@ -734,115 +795,115 @@ var ye = class extends Error {
|
|
|
734
795
|
a && (o.authorization = `Bearer ${a}`);
|
|
735
796
|
try {
|
|
736
797
|
let e = n.endsWith("/_voltro/inspect/migrations") ? n : `${n.replace(/\/+$/, "")}/_voltro/inspect/migrations`, i = await fetch(e, { headers: o });
|
|
737
|
-
if (!i.ok) return
|
|
798
|
+
if (!i.ok) return R.error(`db plan --against: remote returned ${i.status}`, { url: e }), 1;
|
|
738
799
|
let a = (await i.json()).drift?.liveSnapshot;
|
|
739
|
-
if (!a) return
|
|
740
|
-
let s =
|
|
800
|
+
if (!a) return R.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
|
|
801
|
+
let s = D({
|
|
741
802
|
declared: r,
|
|
742
803
|
live: a,
|
|
743
|
-
dialect:
|
|
744
|
-
...
|
|
804
|
+
dialect: W(),
|
|
805
|
+
...E(),
|
|
745
806
|
...t === void 0 ? {} : { onlineAfter: t }
|
|
746
807
|
});
|
|
747
|
-
return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`),
|
|
808
|
+
return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), Z(s), s.summary.blocked > 0 ? 2 : 0;
|
|
748
809
|
} catch (e) {
|
|
749
|
-
return
|
|
810
|
+
return R.error(`db plan --against: ${e.message}`), 1;
|
|
750
811
|
}
|
|
751
812
|
}
|
|
752
|
-
let a =
|
|
813
|
+
let a = D({
|
|
753
814
|
declared: r,
|
|
754
|
-
live: await
|
|
755
|
-
dialect:
|
|
756
|
-
...
|
|
815
|
+
live: await Y(await q()),
|
|
816
|
+
dialect: W(),
|
|
817
|
+
...E(),
|
|
757
818
|
...t === void 0 ? {} : { onlineAfter: t }
|
|
758
819
|
});
|
|
759
|
-
return e.includes("--json") ? (y(1, `${JSON.stringify(a, null, 2)}\n`), a.summary.blocked > 0 ? 2 : 0) : (
|
|
760
|
-
},
|
|
820
|
+
return e.includes("--json") ? (y(1, `${JSON.stringify(a, null, 2)}\n`), a.summary.blocked > 0 ? 2 : 0) : (Z(a), a.summary.blocked > 0 ? 2 : 0);
|
|
821
|
+
}, tt = async (e, t, n) => {
|
|
761
822
|
let r;
|
|
762
823
|
try {
|
|
763
824
|
let t = await v.readFile(m(e), "utf8");
|
|
764
825
|
r = JSON.parse(t);
|
|
765
826
|
} catch (t) {
|
|
766
|
-
return
|
|
827
|
+
return R.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
|
|
767
828
|
}
|
|
768
|
-
if (!Array.isArray(r?.operations) || typeof r?.fromFingerprint != "string" || typeof r?.toFingerprint != "string") return
|
|
769
|
-
let i = await
|
|
770
|
-
if (i === void 0) return
|
|
771
|
-
let a = await
|
|
772
|
-
await
|
|
773
|
-
let o = await
|
|
774
|
-
if (o.length > 0) return
|
|
775
|
-
let s =
|
|
829
|
+
if (!Array.isArray(r?.operations) || typeof r?.fromFingerprint != "string" || typeof r?.toFingerprint != "string") return R.error(`db apply --plan: '${e}' is not a valid plan file (expected the output of \`voltro db plan --json\`).`), 1;
|
|
830
|
+
let i = await Q(n);
|
|
831
|
+
if (i === void 0) return R.warn("db apply --plan: no schema files found", { root: n }), 1;
|
|
832
|
+
let a = await q();
|
|
833
|
+
await J(a, w(x, W()));
|
|
834
|
+
let o = await J(a, b.SqlClient.pipe(g.flatMap((e) => de(e, n)))).catch(() => []);
|
|
835
|
+
if (o.length > 0) return R.error(`db apply --plan: refusing — ${o.length} pending file-based migration(s).`), console.error(` ${o.join("\n ")}`), console.error(""), console.error(" These perform the changes a state diff cannot infer, so they change the shape"), console.error(" this plan was computed against. Apply them and regenerate the plan:"), console.error(""), console.error(" voltro db files ."), console.error(" voltro db plan --json > plan.json"), console.error(" voltro db apply --plan plan.json"), 1;
|
|
836
|
+
let s = D({
|
|
776
837
|
declared: i,
|
|
777
|
-
live: await
|
|
778
|
-
dialect:
|
|
779
|
-
...
|
|
838
|
+
live: await Y(a),
|
|
839
|
+
dialect: W(),
|
|
840
|
+
...E()
|
|
780
841
|
});
|
|
781
|
-
if (s.fromFingerprint !== r.fromFingerprint) return
|
|
782
|
-
if (s.toFingerprint !== r.toFingerprint) return
|
|
783
|
-
let c =
|
|
784
|
-
if (u.summary.blocked > 0 && !c.acknowledged) return
|
|
785
|
-
if (c.acknowledged &&
|
|
786
|
-
if (u.operations.length === 0) return
|
|
787
|
-
|
|
788
|
-
let d = (process.env.NODE_ENV ?? "dev").toLowerCase(), f = l(t, "--note"), p = await
|
|
842
|
+
if (s.fromFingerprint !== r.fromFingerprint) return R.error("db apply --plan: refusing — the live schema has drifted since this plan was generated."), console.error(` plan.from=${r.fromFingerprint.slice(0, 16)} live=${s.fromFingerprint.slice(0, 16)}`), console.error(" Regenerate against the current live schema: `voltro db plan --json > plan.json`"), 2;
|
|
843
|
+
if (s.toFingerprint !== r.toFingerprint) return R.error("db apply --plan: refusing — the declared schema differs from what this plan targets."), console.error(` plan.to=${r.toFingerprint.slice(0, 16)} declared=${s.toFingerprint.slice(0, 16)}`), console.error(" The schema files changed since the plan was generated. Regenerate the plan."), 2;
|
|
844
|
+
let c = qe(s), u = c.plan;
|
|
845
|
+
if (u.summary.blocked > 0 && !c.acknowledged) return Z(u), R.error(`db apply --plan: refusing — ${s.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (R.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), R.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
|
|
846
|
+
if (c.acknowledged && R.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"), Qe(u, t)) return 2;
|
|
847
|
+
if (u.operations.length === 0) return R.info("db apply --plan: schema is up to date — no DDL to apply (change triggers are reconciled separately below)"), await X(i, a), 0;
|
|
848
|
+
Z(u);
|
|
849
|
+
let d = (process.env.NODE_ENV ?? "dev").toLowerCase(), f = l(t, "--note"), p = await J(a, b.SqlClient.pipe(g.flatMap((e) => C(e, u, {
|
|
789
850
|
declared: i,
|
|
790
851
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:plan",
|
|
791
852
|
environment: d === "production" ? "prod" : d === "staging" ? "staging" : "dev",
|
|
792
853
|
source: "file",
|
|
793
854
|
...f ? { notes: f } : {},
|
|
794
|
-
replan: () => g.promise(() =>
|
|
855
|
+
replan: () => g.promise(() => Y(a)).pipe(g.map((e) => D({
|
|
795
856
|
declared: i,
|
|
796
857
|
live: e,
|
|
797
|
-
dialect:
|
|
798
|
-
...
|
|
858
|
+
dialect: W(),
|
|
859
|
+
...E()
|
|
799
860
|
})))
|
|
800
861
|
}))));
|
|
801
|
-
return
|
|
862
|
+
return R.info(`db apply --plan: applied ${p.appliedOps} op(s) in ${p.durationMs}ms`, {
|
|
802
863
|
id: p.id,
|
|
803
864
|
fingerprint: p.fingerprint.slice(0, 16)
|
|
804
|
-
}), await
|
|
805
|
-
},
|
|
806
|
-
let t =
|
|
807
|
-
if (r) return
|
|
808
|
-
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return
|
|
809
|
-
let i = await
|
|
810
|
-
if (i === void 0) return
|
|
811
|
-
let a = await
|
|
812
|
-
if ("failed" in o) return
|
|
813
|
-
await
|
|
814
|
-
let s =
|
|
865
|
+
}), await X(i, a), 0;
|
|
866
|
+
}, nt = async (e) => {
|
|
867
|
+
let t = B(e), n = $e(e), r = l(e, "--plan");
|
|
868
|
+
if (r) return tt(r, e, t);
|
|
869
|
+
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return R.error("voltro db apply: refusing — auto-apply on prod is not allowed."), console.error("In production, schema changes go through `voltro db plan --json > plan.json`"), console.error("then `voltro db apply --plan plan.json` as an explicit step in your deploy pipeline."), 3;
|
|
870
|
+
let i = await Q(t);
|
|
871
|
+
if (i === void 0) return R.warn("db apply: no schema files found", { root: t }), 1;
|
|
872
|
+
let a = await q(), o = await dt(t, a);
|
|
873
|
+
if ("failed" in o) return R.error("db apply: a file-based migration FAILED — the schema diff was NOT run"), console.error(o.failed), console.error("Fix the migration and re-run. `voltro db files` runs them alone."), 1;
|
|
874
|
+
await J(a, w(x, W()));
|
|
875
|
+
let s = D({
|
|
815
876
|
declared: i,
|
|
816
|
-
live: await
|
|
817
|
-
dialect:
|
|
818
|
-
...
|
|
877
|
+
live: await Y(a),
|
|
878
|
+
dialect: W(),
|
|
879
|
+
...E(),
|
|
819
880
|
...n === void 0 ? {} : { onlineAfter: n }
|
|
820
881
|
});
|
|
821
|
-
|
|
822
|
-
let c =
|
|
823
|
-
if (u.summary.blocked > 0 && !c.acknowledged) return
|
|
824
|
-
if (c.acknowledged &&
|
|
825
|
-
if (u.operations.length === 0) return
|
|
826
|
-
let d = l(e, "--note"), f = await
|
|
882
|
+
Z(s), await Xe(s, a);
|
|
883
|
+
let c = qe(s), u = c.plan;
|
|
884
|
+
if (u.summary.blocked > 0 && !c.acknowledged) return R.error(`db apply: refusing — ${u.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (R.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), R.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
|
|
885
|
+
if (c.acknowledged && R.warn(`db apply: proceeding with ${s.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`), Qe(u, e)) return 2;
|
|
886
|
+
if (u.operations.length === 0) return R.info("db apply: schema is up to date — no DDL to apply (change triggers are reconciled separately below)"), await st(a), l(e, "--note") !== void 0 && (R.warn("db apply: --note was ignored — an empty plan writes no history row to attach it to."), console.error("To record that the CURRENT live schema is the accepted baseline, use `voltro db drift --accept`.")), await X(i, a), 0;
|
|
887
|
+
let d = l(e, "--note"), f = await J(a, b.SqlClient.pipe(g.flatMap((e) => C(e, u, {
|
|
827
888
|
declared: i,
|
|
828
889
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user",
|
|
829
890
|
environment: (process.env.NODE_ENV ?? "dev").toLowerCase() === "staging" ? "staging" : "dev",
|
|
830
891
|
source: "auto-diff",
|
|
831
892
|
...d ? { notes: d } : {},
|
|
832
|
-
replan: () => g.promise(() =>
|
|
893
|
+
replan: () => g.promise(() => Y(a)).pipe(g.map((e) => D({
|
|
833
894
|
declared: i,
|
|
834
895
|
live: e,
|
|
835
|
-
dialect:
|
|
836
|
-
...
|
|
896
|
+
dialect: W(),
|
|
897
|
+
...E(),
|
|
837
898
|
...n === void 0 ? {} : { onlineAfter: n }
|
|
838
899
|
})))
|
|
839
900
|
}))));
|
|
840
|
-
return
|
|
901
|
+
return R.info(`db apply: applied ${f.appliedOps} op(s) in ${f.durationMs}ms`, {
|
|
841
902
|
id: f.id,
|
|
842
903
|
fingerprint: f.fingerprint.slice(0, 16)
|
|
843
|
-
}), await
|
|
844
|
-
},
|
|
845
|
-
let t = Math.max(1, Number(l(e, "--limit") ?? "20")), n = await
|
|
904
|
+
}), await X(i, a), 0;
|
|
905
|
+
}, rt = async (e) => {
|
|
906
|
+
let t = Math.max(1, Number(l(e, "--limit") ?? "20")), n = await J(await q(), b.SqlClient.pipe(g.flatMap((e) => e`
|
|
846
907
|
SELECT ${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
847
908
|
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
848
909
|
${e("durationMs")}, ${e("notes")}, ${e("appliedAt")}
|
|
@@ -850,25 +911,25 @@ var ye = class extends Error {
|
|
|
850
911
|
ORDER BY ${e("appliedAt")} DESC
|
|
851
912
|
LIMIT ${t}
|
|
852
913
|
`)));
|
|
853
|
-
if (n.length === 0) return
|
|
914
|
+
if (n.length === 0) return R.info("db plans: no planner-based migrations applied yet"), console.log("Run `voltro db apply` after a schema change to start a plan history."), 0;
|
|
854
915
|
console.log(`\n${n.length} plan${n.length === 1 ? "" : "s"} (newest first):\n`);
|
|
855
916
|
for (let e of n) {
|
|
856
917
|
let t = 0;
|
|
857
918
|
try {
|
|
858
|
-
t = JSON.parse(
|
|
919
|
+
t = JSON.parse(ct(e.operations)).length;
|
|
859
920
|
} catch {}
|
|
860
|
-
console.log(` ${e.id} fp=${
|
|
921
|
+
console.log(` ${e.id} fp=${O(e.fingerprint)} env=${e.environment} src=${e.source} ${t} op(s) ${e.durationMs}ms ${$(e.appliedAt)} by=${e.appliedBy}`), e.notes && console.log(` [2mnote: ${e.notes}[0m`);
|
|
861
922
|
}
|
|
862
923
|
return console.log(), 0;
|
|
863
|
-
},
|
|
924
|
+
}, it = async (e) => {
|
|
864
925
|
let t = e[0];
|
|
865
926
|
if (!t) return console.error("voltro db restore-snapshot <plan-id>"), console.error(" Renames soft-dropped columns/tables from a plan back to their original names."), console.error(" Requires the plan to have been applied with VOLTRO_SOFT_DROP=1."), 2;
|
|
866
|
-
let n = await
|
|
927
|
+
let n = await q(), r = await J(n, b.SqlClient.pipe(g.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}
|
|
867
928
|
FROM ${e("_voltro_migration_plans")} WHERE ${e("id")} = ${t}`)));
|
|
868
929
|
if (r.length === 0) return console.error(`restore-snapshot: plan '${t}' not found`), 1;
|
|
869
930
|
let i;
|
|
870
931
|
try {
|
|
871
|
-
i = JSON.parse(
|
|
932
|
+
i = JSON.parse(ct(r[0].operations));
|
|
872
933
|
} catch {
|
|
873
934
|
return console.error(`restore-snapshot: plan '${t}' has corrupted operations JSON`), 1;
|
|
874
935
|
}
|
|
@@ -882,9 +943,9 @@ var ye = class extends Error {
|
|
|
882
943
|
if (a.length === 0 && o.length === 0) return console.log(`restore-snapshot: plan '${t}' has no drop-column / drop-table ops to restore`), 0;
|
|
883
944
|
let s = 0;
|
|
884
945
|
for (let e of a) {
|
|
885
|
-
let t = await
|
|
946
|
+
let t = await J(n, b.SqlClient.pipe(g.flatMap((t) => t`
|
|
886
947
|
SELECT column_name FROM information_schema.columns
|
|
887
|
-
WHERE ${t.unsafe(
|
|
948
|
+
WHERE ${t.unsafe(K(W()))}
|
|
888
949
|
AND table_name = ${e.table}
|
|
889
950
|
AND column_name LIKE ${e.column + "__dropped_%"}
|
|
890
951
|
ORDER BY column_name DESC LIMIT 1
|
|
@@ -894,13 +955,13 @@ var ye = class extends Error {
|
|
|
894
955
|
continue;
|
|
895
956
|
}
|
|
896
957
|
let r = t[0].column_name;
|
|
897
|
-
await
|
|
958
|
+
await J(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE ${G(e.table)} RENAME COLUMN ${G(r)} TO ${G(e.column)}`)))), s++, console.log(` ↩ restored ${e.table}.${e.column} (from ${r})`);
|
|
898
959
|
}
|
|
899
960
|
let c = 0;
|
|
900
961
|
for (let e of o) {
|
|
901
|
-
let t = await
|
|
962
|
+
let t = await J(n, b.SqlClient.pipe(g.flatMap((t) => t`
|
|
902
963
|
SELECT table_name FROM information_schema.tables
|
|
903
|
-
WHERE ${t.unsafe(
|
|
964
|
+
WHERE ${t.unsafe(K(W()))}
|
|
904
965
|
AND table_name LIKE ${e.table + "__dropped_%"}
|
|
905
966
|
ORDER BY table_name DESC LIMIT 1
|
|
906
967
|
`)));
|
|
@@ -909,57 +970,57 @@ var ye = class extends Error {
|
|
|
909
970
|
continue;
|
|
910
971
|
}
|
|
911
972
|
let r = t[0].table_name;
|
|
912
|
-
await
|
|
973
|
+
await J(n, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE ${G(r)} RENAME TO ${G(e.table)}`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
|
|
913
974
|
}
|
|
914
975
|
return console.log(`\n✓ restored ${s} column(s) + ${c} table(s) from plan '${t}'\n`), 0;
|
|
915
|
-
},
|
|
976
|
+
}, at = async (e) => {
|
|
916
977
|
let t = l(e, "--before");
|
|
917
978
|
if (!t) return console.error("voltro db gc-snapshots --before <date> [--dry-run]"), console.error(" Permanently drops soft-dropped columns/tables (<name>__dropped_<ts>) older than <date>."), console.error(" <date> is ISO — e.g. 2026-01-01 or 2026-01-01T00:00:00Z. NOT reversible."), 2;
|
|
918
979
|
let n = t.replace(/[^0-9]/g, "");
|
|
919
980
|
if (n.length < 8) return console.error(`gc-snapshots: --before must be at least a full date (YYYY-MM-DD), got "${t}"`), 2;
|
|
920
|
-
let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await
|
|
981
|
+
let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await q(), o = await J(a, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
921
982
|
SELECT table_name, column_name FROM information_schema.columns
|
|
922
|
-
WHERE ${e.unsafe(
|
|
923
|
-
`))), s = await
|
|
983
|
+
WHERE ${e.unsafe(K(W()))} AND column_name LIKE ${"%__dropped_%"}
|
|
984
|
+
`))), s = await J(a, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
924
985
|
SELECT table_name FROM information_schema.tables
|
|
925
|
-
WHERE ${e.unsafe(
|
|
986
|
+
WHERE ${e.unsafe(K(W()))} AND table_name LIKE ${"%__dropped_%"}
|
|
926
987
|
`))), c = (e) => {
|
|
927
988
|
let t = /__dropped_(\d{14})$/.exec(e);
|
|
928
989
|
return t ? t[1] : null;
|
|
929
990
|
}, u = 0;
|
|
930
991
|
for (let e of o) {
|
|
931
992
|
let t = c(e.column_name);
|
|
932
|
-
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await
|
|
993
|
+
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await J(a, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE ${G(e.table_name)} DROP COLUMN ${G(e.column_name)}`)))), u++);
|
|
933
994
|
}
|
|
934
995
|
for (let e of s) {
|
|
935
996
|
let t = c(e.table_name);
|
|
936
|
-
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await
|
|
997
|
+
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await J(a, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(`DROP TABLE ${G(e.table_name)}`)))), u++);
|
|
937
998
|
}
|
|
938
999
|
return console.log(`\n${i ? `[dry-run] ${u} snapshot(s) would be dropped` : `✓ dropped ${u} snapshot(s)`} (older than ${r}).\n`), 0;
|
|
939
|
-
},
|
|
1000
|
+
}, ot = async (e) => {
|
|
940
1001
|
let t = l(e, "--before");
|
|
941
1002
|
if (!t) return console.error("voltro db squash --before <iso-date> [--note \"<text>\"]"), 2;
|
|
942
1003
|
let n = t;
|
|
943
1004
|
if (Number.isNaN(new Date(n).getTime())) return console.error(`voltro db squash: --before '${n}' is not a valid ISO date`), 2;
|
|
944
|
-
let r = l(e, "--note"), i =
|
|
945
|
-
if (a === void 0) return
|
|
946
|
-
let o =
|
|
1005
|
+
let r = l(e, "--note"), i = B(e), a = await Q(i);
|
|
1006
|
+
if (a === void 0) return R.warn("db squash: no schema files found", { root: i }), 1;
|
|
1007
|
+
let o = T(se(a)), s = await q();
|
|
947
1008
|
try {
|
|
948
|
-
let e = await
|
|
1009
|
+
let e = await J(s, ge({
|
|
949
1010
|
before: n,
|
|
950
1011
|
fingerprint: o,
|
|
951
1012
|
appliedBy: process.env.USER ?? "cli",
|
|
952
1013
|
environment: process.env.NODE_ENV === "production" ? "prod" : "dev",
|
|
953
1014
|
...r === void 0 ? {} : { note: r }
|
|
954
1015
|
}));
|
|
955
|
-
return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${
|
|
1016
|
+
return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${O(e.snapshotFingerprint)}\n`), 0;
|
|
956
1017
|
} catch (e) {
|
|
957
1018
|
return console.error(`squash refused: ${e.message}`), 1;
|
|
958
1019
|
}
|
|
959
|
-
},
|
|
1020
|
+
}, st = async (e) => {
|
|
960
1021
|
try {
|
|
961
|
-
let t =
|
|
962
|
-
await
|
|
1022
|
+
let t = T(await Y(e));
|
|
1023
|
+
await J(e, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
963
1024
|
UPDATE ${e("_voltro_migration_plans")}
|
|
964
1025
|
SET ${e("liveFingerprint")} = ${t}
|
|
965
1026
|
WHERE ${e("id")} = (
|
|
@@ -971,7 +1032,7 @@ var ye = class extends Error {
|
|
|
971
1032
|
AND ${e("liveFingerprint")} IS NULL
|
|
972
1033
|
`)));
|
|
973
1034
|
} catch {}
|
|
974
|
-
},
|
|
1035
|
+
}, ct = (e) => typeof e == "string" ? e : e == null ? "" : JSON.stringify(e), $ = (e) => e instanceof Date ? e.toISOString() : String(e ?? ""), lt = (e) => e === void 0 ? {
|
|
975
1036
|
accept: !1,
|
|
976
1037
|
reason: "no-declared-schema"
|
|
977
1038
|
} : e > 0 ? {
|
|
@@ -980,38 +1041,38 @@ var ye = class extends Error {
|
|
|
980
1041
|
} : {
|
|
981
1042
|
accept: !0,
|
|
982
1043
|
reason: "converged"
|
|
983
|
-
},
|
|
984
|
-
let t = e.includes("--accept"), n = await
|
|
1044
|
+
}, ut = async (e) => {
|
|
1045
|
+
let t = e.includes("--accept"), n = await q(), r = await J(n, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
985
1046
|
SELECT * FROM ${e("_voltro_migration_plans")}
|
|
986
1047
|
ORDER BY ${e("appliedAt")} DESC
|
|
987
1048
|
LIMIT 1
|
|
988
|
-
`))), i = await
|
|
989
|
-
if (r.length === 0) return
|
|
1049
|
+
`))), i = await Y(n), a = T(i);
|
|
1050
|
+
if (r.length === 0) return R.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${O(a)}`), console.log("No baseline to compare against. Run `voltro db apply` to lock in the current state."), 0;
|
|
990
1051
|
let o = r[0];
|
|
991
|
-
if (o.liveFingerprint === null || o.liveFingerprint === void 0) return
|
|
992
|
-
if (o.liveFingerprint === a) return
|
|
1052
|
+
if (o.liveFingerprint === null || o.liveFingerprint === void 0) return R.warn("db drift: no drift baseline recorded yet — cannot compare"), console.log(), console.log(` last applied: ${o.id} at ${$(o.appliedAt)}`), console.log(` live now: ${O(a)}`), console.log(), console.log("That row was written before the framework recorded a live-schema baseline,"), console.log("and the value it does carry is the DECLARED snapshot's hash — not comparable"), console.log("with a live one. Run `voltro db apply` once to establish the baseline (a no-op"), console.log("apply backfills it too); use `voltro db plan` meanwhile, which compares declared"), console.log("against live directly."), console.log(), console.log("Exit code 3 — NOT 0. \"Did not compare\" is not \"clean\", and a gate on the exit"), console.log("code has to be able to tell them apart."), 3;
|
|
1053
|
+
if (o.liveFingerprint === a) return R.info(`db drift: live matches last applied (${O(a)})`), 0;
|
|
993
1054
|
let s = await (async () => {
|
|
994
1055
|
try {
|
|
995
|
-
let t = await
|
|
996
|
-
return t === void 0 ? void 0 :
|
|
1056
|
+
let t = await Q(B(e));
|
|
1057
|
+
return t === void 0 ? void 0 : D({
|
|
997
1058
|
declared: t,
|
|
998
1059
|
live: i,
|
|
999
|
-
dialect:
|
|
1000
|
-
...
|
|
1060
|
+
dialect: W(),
|
|
1061
|
+
...E()
|
|
1001
1062
|
}).operations;
|
|
1002
1063
|
} catch {
|
|
1003
1064
|
return;
|
|
1004
1065
|
}
|
|
1005
1066
|
})();
|
|
1006
1067
|
if (t) {
|
|
1007
|
-
let e =
|
|
1008
|
-
if (e.reason === "no-declared-schema") return
|
|
1068
|
+
let e = lt(s?.length);
|
|
1069
|
+
if (e.reason === "no-declared-schema") return R.error("db drift --accept: no schema files found — cannot verify the live schema is current."), 1;
|
|
1009
1070
|
if (!e.accept && s !== void 0) {
|
|
1010
|
-
|
|
1071
|
+
R.error(`db drift --accept: refusing — ${s.length} operation(s) still pending against your declared schema.`), console.log();
|
|
1011
1072
|
for (let e of s.slice(0, 10)) console.log(` - ${e.op.kind} ${"table" in e.op ? `[${e.op.table}]` : ""}`);
|
|
1012
1073
|
return s.length > 10 && console.log(` … and ${s.length - 10} more`), console.log(), console.log("`--accept` records the CURRENT live schema as the applied baseline. It is for a"), console.log("change that already landed out of band and left the schema correct — not for one"), console.log("that has not landed. Run `voltro db apply` instead; it writes a real baseline."), 4;
|
|
1013
1074
|
}
|
|
1014
|
-
return await
|
|
1075
|
+
return await J(n, b.SqlClient.pipe(g.flatMap((e) => e`
|
|
1015
1076
|
UPDATE ${e("_voltro_migration_plans")}
|
|
1016
1077
|
SET ${e("liveFingerprint")} = ${a}
|
|
1017
1078
|
WHERE ${e("id")} = (
|
|
@@ -1020,43 +1081,43 @@ var ye = class extends Error {
|
|
|
1020
1081
|
ORDER BY ${e("appliedAt")} DESC LIMIT 1
|
|
1021
1082
|
) AS ${e("newest")}
|
|
1022
1083
|
)
|
|
1023
|
-
`))),
|
|
1084
|
+
`))), R.info(`db drift: accepted the live schema as the baseline (${O(a)})`), console.log(), console.log("`voltro db plan` reports 0 operations against this schema, so the live state already"), console.log("matches your declaration — only the ledger did not know. It does now."), console.log(), console.log("No history entry was invented: the latest row's baseline was updated in place,"), console.log("because no migration ran."), 0;
|
|
1024
1085
|
}
|
|
1025
|
-
return
|
|
1026
|
-
},
|
|
1086
|
+
return R.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${O(o.liveFingerprint)} at ${$(o.appliedAt)} (${o.id})`), console.log(` live now: ${O(a)}`), console.log(), console.log("Something changed the live schema after the last apply. This command can see"), console.log("THAT it changed, not what or who — the fingerprints are hashes, not a diff."), console.log(), s === void 0 ? (console.log("No schema files found here, so the declared side could not be compared."), console.log("Run `voltro db plan` from your project root for the actual difference."), 4) : s.length === 0 ? (console.log("Your DECLARED schema is already satisfied — `voltro db plan` reports 0 operations"), console.log("against this database. So the live schema is not wrong, only unrecorded: something"), console.log("applied a change without going through the planner (a hand-run ALTER, a DBA"), console.log("window, a restored dump), or it touched a table your code does not declare."), console.log(), console.log("If that was deliberate and the schema is right, record it:"), console.log(), console.log(" voltro db drift --accept"), console.log(), console.log("It updates the latest ledger row's baseline to the live schema and invents no"), console.log("history entry. Drift then measures from here."), 4) : (console.log(`\`voltro db plan\` reports ${s.length} operation(s) still pending against your`), console.log("declared schema, so this is not merely an unrecorded change — run it for the diff,"), console.log("then `voltro db apply`, which writes a fresh baseline of its own."), 4);
|
|
1087
|
+
}, dt = async (e, t) => {
|
|
1027
1088
|
let n = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
|
|
1028
1089
|
try {
|
|
1029
|
-
let r = await
|
|
1090
|
+
let r = await J(t, b.SqlClient.pipe(g.flatMap((t) => he(t, {
|
|
1030
1091
|
projectRoot: e,
|
|
1031
1092
|
env: n,
|
|
1032
1093
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
|
|
1033
1094
|
}))));
|
|
1034
|
-
return r.applied.length > 0 &&
|
|
1095
|
+
return r.applied.length > 0 && R.info(`db apply: applied ${r.applied.length} pending file-based migration(s) first`, { applied: r.applied.map((e) => `${e.id} (${e.durationMs}ms)`) }), { applied: r.applied.length };
|
|
1035
1096
|
} catch (e) {
|
|
1036
1097
|
return { failed: e };
|
|
1037
1098
|
}
|
|
1038
|
-
},
|
|
1039
|
-
let t =
|
|
1040
|
-
await
|
|
1041
|
-
let i = await
|
|
1099
|
+
}, ft = async (e) => {
|
|
1100
|
+
let t = B(e), n = await q(), r = (process.env.NODE_ENV ?? "").toLowerCase() === "production" ? "prod" : (process.env.NODE_ENV ?? "").toLowerCase() === "staging" ? "staging" : "dev";
|
|
1101
|
+
await J(n, w(x, W()));
|
|
1102
|
+
let i = await J(n, b.SqlClient.pipe(g.flatMap((e) => he(e, {
|
|
1042
1103
|
projectRoot: t,
|
|
1043
1104
|
env: r,
|
|
1044
1105
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
|
|
1045
1106
|
}))));
|
|
1046
|
-
return i.applied.length === 0 ? (
|
|
1107
|
+
return i.applied.length === 0 ? (R.info(`db files: no pending file-based migrations (${i.skipped.length} already applied)`), 0) : (R.info(`db files: applied ${i.applied.length} migration(s)`, {
|
|
1047
1108
|
applied: i.applied.map((e) => `${e.id} (${e.durationMs}ms)`),
|
|
1048
1109
|
skipped: i.skipped.length
|
|
1049
1110
|
}), 0);
|
|
1050
|
-
},
|
|
1051
|
-
let t =
|
|
1052
|
-
if (!n) return
|
|
1053
|
-
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return
|
|
1054
|
-
let r = await
|
|
1111
|
+
}, pt = async (e) => {
|
|
1112
|
+
let t = B(e), n = e.find((e) => !e.startsWith("--"));
|
|
1113
|
+
if (!n) return R.error("voltro db rollback-file <migration-id>"), 2;
|
|
1114
|
+
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return R.error("voltro db rollback-file: refusing on prod — schema rollback runs as an explicit deploy step."), 3;
|
|
1115
|
+
let r = await J(await q(), b.SqlClient.pipe(g.flatMap((e) => pe(e, {
|
|
1055
1116
|
projectRoot: t,
|
|
1056
1117
|
id: n
|
|
1057
1118
|
}))));
|
|
1058
|
-
return
|
|
1059
|
-
},
|
|
1119
|
+
return R.info(`db rollback-file: rolled back ${r.id} in ${r.durationMs}ms`), 0;
|
|
1120
|
+
}, mt = () => {
|
|
1060
1121
|
let e = /* @__PURE__ */ new Date();
|
|
1061
1122
|
return [
|
|
1062
1123
|
e.getUTCFullYear(),
|
|
@@ -1066,7 +1127,7 @@ var ye = class extends Error {
|
|
|
1066
1127
|
String(e.getUTCMinutes()).padStart(2, "0"),
|
|
1067
1128
|
String(e.getUTCSeconds()).padStart(2, "0")
|
|
1068
1129
|
].join("");
|
|
1069
|
-
},
|
|
1130
|
+
}, ht = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", gt = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
|
|
1070
1131
|
import { Effect } from 'effect'
|
|
1071
1132
|
|
|
1072
1133
|
// A hand-written migration. Reach for this when the DECLARATIVE differ can't
|
|
@@ -1098,24 +1159,24 @@ export default defineMigration({
|
|
|
1098
1159
|
}),
|
|
1099
1160
|
],
|
|
1100
1161
|
})
|
|
1101
|
-
`,
|
|
1102
|
-
plan:
|
|
1103
|
-
apply:
|
|
1104
|
-
plans:
|
|
1105
|
-
drift:
|
|
1106
|
-
files:
|
|
1107
|
-
"rollback-file":
|
|
1108
|
-
migrate:
|
|
1109
|
-
rollback:
|
|
1110
|
-
status:
|
|
1111
|
-
seed:
|
|
1162
|
+
`, _t = {
|
|
1163
|
+
plan: et,
|
|
1164
|
+
apply: nt,
|
|
1165
|
+
plans: rt,
|
|
1166
|
+
drift: ut,
|
|
1167
|
+
files: ft,
|
|
1168
|
+
"rollback-file": pt,
|
|
1169
|
+
migrate: Re,
|
|
1170
|
+
rollback: ze,
|
|
1171
|
+
status: Be,
|
|
1172
|
+
seed: He,
|
|
1112
1173
|
generate: async (e) => {
|
|
1113
|
-
let { value: t, rest: n } = c(e, "--root"), { value: r, rest: i } = c(n, "--name"), a = t ? m(t) : process.cwd(), o =
|
|
1114
|
-
return await v.mkdir(l, { recursive: !0 }), await v.writeFile(d,
|
|
1174
|
+
let { value: t, rest: n } = c(e, "--root"), { value: r, rest: i } = c(n, "--name"), a = t ? m(t) : process.cwd(), o = ht(r ?? u(i) ?? "migration"), s = `${mt()}_${o}`, l = f(a, "migrations"), d = f(l, `${s}.migration.ts`);
|
|
1175
|
+
return await v.mkdir(l, { recursive: !0 }), await v.writeFile(d, gt(s, o), "utf8"), R.info(`created ${p(a, d)}`), console.error("Fill in the apply/undo steps, then run `voltro db migrate` (rollback with `voltro db rollback`)."), console.error("For a plain schema shape change you may not need this — `voltro db apply` diffs declared vs live."), 0;
|
|
1115
1176
|
},
|
|
1116
|
-
squash:
|
|
1117
|
-
"restore-snapshot":
|
|
1118
|
-
"gc-snapshots":
|
|
1177
|
+
squash: ot,
|
|
1178
|
+
"restore-snapshot": it,
|
|
1179
|
+
"gc-snapshots": at,
|
|
1119
1180
|
adopt: async (e) => {
|
|
1120
1181
|
let t = (t) => {
|
|
1121
1182
|
let n = e.indexOf(`--${t}`);
|
|
@@ -1129,32 +1190,50 @@ export default defineMigration({
|
|
|
1129
1190
|
into: r,
|
|
1130
1191
|
map: o.map,
|
|
1131
1192
|
...o.leaveUnset ? { leaveUnset: o.leaveUnset } : {}
|
|
1132
|
-
}, c =
|
|
1193
|
+
}, c = W(), l = await q(), u = await J(l, b.SqlClient.pipe(g.flatMap(ue))), d = (e) => u.tables.find((t) => t.name === e)?.columns.map((e) => ({
|
|
1133
1194
|
name: e.name,
|
|
1134
1195
|
nullable: e.nullable,
|
|
1135
1196
|
hasDefault: e.defaultValue !== void 0 && e.defaultValue !== null
|
|
1136
1197
|
})), f = d(n), p = d(r);
|
|
1137
1198
|
if (f === void 0) return console.error(`voltro db adopt: '${n}' does not exist in the database`), 1;
|
|
1138
1199
|
if (p === void 0) return console.error(`voltro db adopt: '${r}' does not exist in the database — boot once so the plugin's table is created`), 1;
|
|
1139
|
-
let h = async (e) => await
|
|
1140
|
-
for (let e of
|
|
1200
|
+
let h = async (e) => await J(l, b.SqlClient.pipe(g.flatMap((t) => t.unsafe(e)))), _ = async (e) => Number(Object.values((await h(`SELECT COUNT(*) AS n FROM "${e}"`))[0] ?? { n: 0 })[0] ?? 0), v = be(s, f, p, await _(r));
|
|
1201
|
+
for (let e of Se(s, v, await _(n))) console.log(e);
|
|
1141
1202
|
if (v.fatal) return 1;
|
|
1142
1203
|
if (!e.includes("--apply")) return console.log(""), console.log(" (dry run — nothing written. Re-run with --apply.)"), 0;
|
|
1143
|
-
let y = await
|
|
1204
|
+
let y = await Ee(s, v, c, "id", void 0, h, { keepSource: e.includes("--keep-source") });
|
|
1144
1205
|
return console.log(""), console.log(` copied ${y.copied}/${y.sourceRows} row(s)`), console.log(` snapshot: ${y.snapshotTable} — restore from here, then drop it yourself`), y.verified ? (console.log(y.sourceDropped ? ` dropped ${n}` : ` ${n} left in place (--keep-source)`), 0) : (console.error(" ✗ counts DIFFER — the source was NOT dropped. Compare the two tables before doing anything else."), 1);
|
|
1206
|
+
},
|
|
1207
|
+
"scan-credentials": async (e) => {
|
|
1208
|
+
let t = W(), n = [];
|
|
1209
|
+
for (let t = 0; t < e.length; t += 1) {
|
|
1210
|
+
if (e[t] !== "--table") continue;
|
|
1211
|
+
let r = e[t + 1];
|
|
1212
|
+
if (r === void 0) throw new k("voltro db scan-credentials: --table needs a value");
|
|
1213
|
+
let [i, a = "subject"] = r.split(":");
|
|
1214
|
+
n.push({
|
|
1215
|
+
table: i,
|
|
1216
|
+
column: a
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
let r = [...Oe.map((e) => ({
|
|
1220
|
+
table: e,
|
|
1221
|
+
column: "subject"
|
|
1222
|
+
})), ...n], i = await J(await q(), b.SqlClient.pipe(g.flatMap((e) => g.promise(() => Me(e, t, r, G)))));
|
|
1223
|
+
return console.log(Ne(i, t)), Pe(i);
|
|
1145
1224
|
}
|
|
1146
|
-
},
|
|
1225
|
+
}, vt = async (e) => {
|
|
1147
1226
|
let [t, ...n] = e;
|
|
1148
1227
|
if (!t) {
|
|
1149
1228
|
console.error("voltro db <subcommand> [args]"), console.error("subcommands:");
|
|
1150
|
-
for (let e of Object.keys(
|
|
1229
|
+
for (let e of Object.keys(_t)) console.error(` ${e}`);
|
|
1151
1230
|
return 1;
|
|
1152
1231
|
}
|
|
1153
|
-
let r =
|
|
1232
|
+
let r = _t[t];
|
|
1154
1233
|
if (!r) return console.error(`voltro db: unknown subcommand '${t}'`), 2;
|
|
1155
|
-
(process.env.DB_DIRECT_URL !== void 0 || process.env.DB_MIGRATE_URL !== void 0) &&
|
|
1234
|
+
(process.env.DB_DIRECT_URL !== void 0 || process.env.DB_MIGRATE_URL !== void 0) && R.info("using a DIRECT (non-pooler) connection for the db-command path (DB_DIRECT_URL/DB_MIGRATE_URL set)");
|
|
1156
1235
|
let i = (e) => {
|
|
1157
|
-
|
|
1236
|
+
H(e) ? R.error(U) : console.error(e), process.exit(1);
|
|
1158
1237
|
};
|
|
1159
1238
|
process.on("uncaughtException", i);
|
|
1160
1239
|
try {
|
|
@@ -1164,4 +1243,4 @@ export default defineMigration({
|
|
|
1164
1243
|
}
|
|
1165
1244
|
};
|
|
1166
1245
|
//#endregion
|
|
1167
|
-
export {
|
|
1246
|
+
export { Ge as a, k as c, lt as i, ve as l, V as n, H as o, We as r, vt as s, U as t };
|