@voltro/cli 0.33.0 → 0.34.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 +1801 -0
- package/bin/nodeEnvironment.d.mts +30 -0
- package/bin/nodeEnvironment.mjs +158 -0
- package/bin/voltro.mjs +69 -5
- package/dist/addCommand-BNeoeSxe.js +124 -0
- package/dist/addCommand-aXSQveak.js +2 -0
- package/dist/agentsMd-BTchIZku.js +2 -0
- package/dist/agentsMd-mhQMF1bx.js +254 -0
- package/dist/apiBuild-BICVGnEC.js +2 -0
- package/dist/{apiBuild-h9VHtnlw.js → apiBuild-DZF_f0_I.js} +46 -46
- package/dist/appPort-B_HpJ_ck.js +48 -0
- package/dist/baselineCommand-C2ClWZN3.js +2 -0
- package/dist/baselineCommand-DIttzO8A.js +227 -0
- package/dist/bin.js +71 -28
- package/dist/build-CE7Ca9gm.js +711 -0
- package/dist/cacheCommand-DA4OH9xt.js +42 -0
- package/dist/capabilitiesCommand-nq_pz5xd.js +123 -0
- package/dist/checkCommand-Dv8r9tGO.js +231 -0
- package/dist/checkCommand-YZDmkAGd.js +2 -0
- package/dist/{cliArgs-qdZSElM3.js → cliArgs-D4p8n7EE.js} +12 -1
- package/dist/cliError-BmdYnghb.js +10 -0
- package/dist/cliOutput-D1tSBoRM.js +15 -0
- package/dist/{cliRuntime-Oh517vCV.js → cliRuntime-Dh7UDinH.js} +20 -20
- package/dist/cloudClient-DWL-Hw_T.js +67 -0
- package/dist/cloudCmd-Cvv5HGaZ.js +364 -0
- package/dist/clusterCmd-CNXKlcvD.js +54 -0
- package/dist/codegen-CYM3Zqrf.js +605 -0
- package/dist/codegen-ChBi_hVa.js +2 -0
- package/dist/codegenCommand-DC6w2tNZ.js +30 -0
- package/dist/codemodRunner-DRRqXR74.js +5243 -0
- package/dist/commandRunner-BLAEFLjp.js +47 -0
- package/dist/commands-gutsz-Ac.js +808 -0
- package/dist/connectionConfig-UFlIEiys.js +66 -0
- package/dist/dashboardCommand-3YG8p-UA.js +25 -0
- package/dist/dataCommand-qL0r7fPO.js +535 -0
- package/dist/dataProfile-dW-PsfLB.js +15 -0
- package/dist/{dbCommand-DTLKAfbA.js → dbCommand-B6X0FZbq.js} +551 -401
- package/dist/dbCommand-CpdKLeQq.js +2 -0
- package/dist/{dev-C_P8FLSx.js → dev-CerMd0mW.js} +3122 -3129
- package/dist/dev-CoG-ZPx8.js +3 -0
- package/dist/devActivity-Dx_3nnGv.js +100 -0
- package/dist/devActivity.js +1 -1
- package/dist/dialectDriver-CgXnDfec.js +39 -0
- package/dist/discover-C9XKJDco.js +25 -0
- package/dist/doctorCommand-BvqGBwNG.js +2 -0
- package/dist/{checkCommand-xGhRFFg2.js → doctorCommand-CnDXQxfa.js} +520 -1278
- package/dist/dormancyCommand-Dszo57d6.js +69 -0
- package/dist/e2eCmd-BRabZww-.js +147 -0
- package/dist/embeddingsCommand-C0sKVRo1.js +73 -0
- package/dist/envCommand-DPgdV1Bq.js +60 -0
- package/dist/evalCommand-6RUfPen4.js +118 -0
- package/dist/evolveCommand-DHpkgjgH.js +281 -0
- package/dist/fileTaxonomy-CJfgOllU.js +457 -0
- package/dist/frameworkTableAssembly-BGHmck-x.js +2 -0
- package/dist/{frameworkTableAssembly-BwIrO5nv.js → frameworkTableAssembly-DkkP6BgC.js} +184 -148
- package/dist/generateCommand-oibemh97.js +147 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +4 -3
- package/dist/infoCommand-BJw9nLUR.js +60 -0
- package/dist/{inspect-BUUjt773.js → inspect-CBqFtAKA.js} +82 -40
- package/dist/inspect-C_T_WGvl.js +2 -0
- package/dist/inspectCmd-Bppy-GGw.js +224 -0
- package/dist/inspectFetch-Cm8_wVvp.js +151 -0
- package/dist/inspectMetrics-CfdKLh6t.js +72 -0
- package/dist/loadEnv-D9nEOClM.js +44 -0
- package/dist/logFileSink-C_D2wRN1.js +105 -0
- package/dist/logsCmd-CCca3KRZ.js +260 -0
- package/dist/manifestBuild-ChsKAhmn.js +2 -0
- package/dist/{manifestBuild-BLrVuSlM.js → manifestBuild-sxpwdKY1.js} +1 -1
- package/dist/metaCommands-7MJfZ5cf.js +196 -0
- package/dist/migrate-CBwOt_iV.js +83 -0
- package/dist/mssqlClusterPatch-_4cE_nun.js +44 -0
- package/dist/newCommand-COWOJ1_E.js +156 -0
- package/dist/nodeEnvironment-cGFAj1J8.js +28 -0
- package/dist/packageCommand-Cug_3Ogl.js +271 -0
- package/dist/pageConvention-cEiRxdab.js +5 -0
- package/dist/privacyCommand-C-Df56U_.js +146 -0
- package/dist/projectScaffold-DmzEKHib.js +2 -0
- package/dist/projectScaffold-LMMtaavR.js +814 -0
- package/dist/renderModeScan-D7J1B7Kw.js +105 -0
- package/dist/renderProfile-1OWWAAtx.js +81 -0
- package/dist/runtimeRegistry-DMeKfTHP.js +81 -0
- package/dist/runtimeTrace-CRxalXTs.js +91 -0
- package/dist/scheduleCmd--jksTrf6.js +69 -0
- package/dist/scheduleManifestCmd-D2x0CTTY.js +249 -0
- package/dist/schemaIr-UJybUUZW.js +103 -0
- package/dist/{sdkgen-C81QIkiL.js → sdkgen-CYJscZC7.js} +111 -209
- package/dist/seedRunner-TFHHiToI.js +329 -0
- package/dist/serveCommand-B_isw7q4.js +1647 -0
- package/dist/serveCommand-DOvbgRnQ.js +2 -0
- package/dist/serveEntry.js +5 -5
- package/dist/serverlessCommand-CfJZy6dS.js +482 -0
- package/dist/start-9LiUOfES.js +1087 -0
- package/dist/start-B-9Nsp-S.js +3 -0
- package/dist/startEntry.js +2 -2
- package/dist/staticCommand-Dr2M6tpU.js +304 -0
- package/dist/storageCommand-Co6NfLqN.js +42 -0
- package/dist/templates-De8IR5-c.js +102 -0
- package/dist/test-CI6iDsYc.js +115 -0
- package/dist/tracesCmd-CkEZQrtt.js +232 -0
- package/dist/tsconfigPaths-BWXBWgcl.js +107 -0
- package/dist/tsxLoader-EuXmSJ1K.js +51 -0
- package/dist/typecheckCommand-BlsWiCNq.js +61 -0
- package/dist/updateCommand-Bkptutss.js +585 -0
- package/dist/updateCommand-us1_hdIC.js +2 -0
- package/dist/{inspectMetrics-1xzTKAFx.js → webDev-CBYvPqQr.js} +975 -1559
- package/dist/webDev-Cg-fFiyd2.js +2 -0
- package/dist/webhookDiscovery-CrGAfhIG.js +2 -0
- package/dist/webhookDiscovery-D7VaeMlz.js +51 -0
- package/dist/webhooksCommand-CID96Rga.js +267 -0
- package/dist/workflowsCmd-D1VTmLMY.js +608 -0
- package/package.json +179 -17
- package/templates/AGENTS.core.md +58 -3
- package/templates/AGENTS.md +64 -7
- package/templates/agent-docs/_index.md +6 -4
- package/templates/agent-docs/_manifest.json +22 -5
- package/templates/agent-docs/ai.md +370 -0
- package/templates/agent-docs/authentication.md +265 -31
- package/templates/agent-docs/caching.md +6 -0
- package/templates/agent-docs/cli.md +794 -50
- package/templates/agent-docs/data.md +550 -11
- package/templates/agent-docs/database/migrations.md +174 -25
- package/templates/agent-docs/database/misc.md +156 -40
- package/templates/agent-docs/database/querying.md +19 -1
- package/templates/agent-docs/database/scaling.md +60 -0
- package/templates/agent-docs/database/schema.md +1 -0
- package/templates/agent-docs/database/seedsdialects.md +208 -19
- package/templates/agent-docs/database/transactions.md +68 -0
- package/templates/agent-docs/deployment.md +69 -3
- package/templates/agent-docs/introduction.md +87 -16
- package/templates/agent-docs/local-first-mobile.md +79 -4
- package/templates/agent-docs/multi-tenancy.md +95 -20
- package/templates/agent-docs/observability.md +58 -3
- package/templates/agent-docs/plugins/ai-flows.md +161 -2
- package/templates/agent-docs/plugins/analytics-postgres.md +1 -1
- package/templates/agent-docs/plugins/audit.md +37 -1
- package/templates/agent-docs/plugins/auth-social.md +143 -0
- package/templates/agent-docs/plugins/auth-workos.md +4 -2
- package/templates/agent-docs/plugins/auth.md +131 -6
- package/templates/agent-docs/plugins/billing.md +132 -15
- package/templates/agent-docs/plugins/cdc-out.md +46 -7
- package/templates/agent-docs/plugins/clickhouse.md +1 -1
- package/templates/agent-docs/plugins/duckdb.md +1 -1
- package/templates/agent-docs/plugins/flags.md +132 -0
- package/templates/agent-docs/plugins/governance.md +105 -7
- package/templates/agent-docs/plugins/multitenancy.md +9 -4
- package/templates/agent-docs/plugins/presence.md +13 -2
- package/templates/agent-docs/plugins/ratelimit.md +9 -0
- package/templates/agent-docs/plugins/search.md +157 -6
- package/templates/agent-docs/plugins/sso-saml.md +47 -8
- package/templates/agent-docs/plugins/webhooks.md +105 -0
- package/templates/agent-docs/plugins.md +150 -18
- package/templates/agent-docs/reference.md +60 -3
- package/templates/agent-docs/releases.md +1117 -0
- package/templates/agent-docs/routing.md +43 -25
- package/templates/agent-docs/scheduling.md +10 -0
- package/templates/agent-docs/schema-driven-ui.md +92 -12
- package/templates/agent-docs/security.md +426 -0
- package/templates/agent-docs/templates/apibackends.md +87 -18
- package/templates/agent-docs/templates/appshells.md +32 -14
- package/templates/agent-docs/templates/overview.md +13 -8
- package/templates/agent-docs/testing.md +211 -14
- package/templates/agent-docs/whats-new.md +1700 -104
- package/templates/agent-docs/workflows.md +130 -14
- package/templates/apps/api-ai/actions/summarize.action.ts +11 -0
- package/templates/apps/api-ai/package.json +8 -7
- package/templates/apps/api-auth/actions/me.action.ts +13 -0
- package/templates/apps/api-auth/package.json +9 -8
- package/templates/apps/api-backend/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend/package.json +12 -8
- package/templates/apps/api-backend/queries/notes.query.ts +30 -8
- package/templates/apps/api-backend-deactivation/actions/users.get.action.ts +14 -0
- package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.ts +8 -0
- package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.ts +10 -0
- package/templates/apps/api-backend-deactivation/package.json +8 -7
- package/templates/apps/api-backend-mail/actions/sendWelcome.action.ts +17 -0
- package/templates/apps/api-backend-mail/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend-mail/package.json +9 -8
- package/templates/apps/api-backend-mail/queries/notes.query.ts +30 -8
- package/templates/apps/api-backend-mariadb/.env.example +14 -0
- package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend-mariadb/package.json +10 -9
- package/templates/apps/api-backend-mariadb/queries/notes.query.ts +30 -8
- package/templates/apps/api-backend-sqlite/.env.example +14 -0
- package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend-sqlite/package.json +9 -8
- package/templates/apps/api-backend-sqlite/queries/notes.query.ts +30 -8
- package/templates/apps/api-backend-storage/actions/uploadAvatar.action.ts +13 -0
- package/templates/apps/api-backend-storage/actions/uploadDocument.action.ts +12 -0
- package/templates/apps/api-backend-storage/mutations/notes.create.mutation.ts +9 -0
- package/templates/apps/api-backend-storage/package.json +9 -8
- package/templates/apps/api-backend-storage/queries/notes.query.ts +30 -8
- package/templates/apps/api-cms/actions/content.get.action.ts +7 -0
- package/templates/apps/api-cms/actions/content.types.action.ts +6 -0
- package/templates/apps/api-cms/actions/me.action.ts +13 -0
- package/templates/apps/api-cms/app.config.ts +19 -0
- package/templates/apps/api-cms/authz.ts +63 -0
- package/templates/apps/api-cms/mutations/content.publish.mutation.ts +10 -0
- package/templates/apps/api-cms/mutations/content.saveDraft.mutation.ts +10 -0
- package/templates/apps/api-cms/mutations/content.unpublish.mutation.ts +5 -0
- package/templates/apps/api-cms/package.json +11 -10
- package/templates/apps/api-cms/queries/content.list.query.ts +23 -7
- package/templates/apps/api-cms/tests/accessDecisions.test.ts +121 -0
- package/templates/apps/api-cms/tests/content.descriptors.test.ts +8 -4
- package/templates/apps/api-collab/mutations/documents.create.mutation.ts +8 -0
- package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +13 -0
- package/templates/apps/api-collab/package.json +9 -8
- package/templates/apps/api-collab/queries/documents.query.ts +19 -7
- package/templates/apps/api-data-advanced/package.json +9 -8
- package/templates/apps/api-data-advanced/queries/authors.withBooks.query.ts +12 -0
- package/templates/apps/api-data-advanced/queries/books.search.query.ts +9 -0
- package/templates/apps/api-durable/mutations/orders.approve.mutation.ts +17 -0
- package/templates/apps/api-durable/mutations/orders.place.mutation.ts +9 -0
- package/templates/apps/api-durable/package.json +9 -8
- package/templates/apps/api-feature-flags/actions/notes.export.action.ts +16 -0
- package/templates/apps/api-feature-flags/mutations/notes.create.mutation.ts +12 -0
- package/templates/apps/api-feature-flags/package.json +10 -9
- package/templates/apps/api-governance/README.md +30 -8
- package/templates/apps/api-governance/actions/profiles.get.action.server.ts +17 -1
- package/templates/apps/api-governance/actions/profiles.get.action.ts +29 -4
- package/templates/apps/api-governance/database/schema.ts +16 -4
- package/templates/apps/api-governance/mutations/profiles.create.mutation.ts +12 -0
- package/templates/apps/api-governance/package.json +9 -8
- package/templates/apps/api-kv/actions/sync.pull.action.ts +15 -0
- package/templates/apps/api-kv/actions/sync.reset.action.ts +13 -0
- package/templates/apps/api-kv/actions/sync.status.action.ts +7 -0
- package/templates/apps/api-kv/package.json +9 -8
- package/templates/apps/api-kv/queries/events.list.query.ts +19 -8
- package/templates/apps/api-moderation/mutations/comments.create.mutation.ts +11 -0
- package/templates/apps/api-moderation/mutations/posts.create.mutation.ts +13 -0
- package/templates/apps/api-moderation/package.json +9 -8
- package/templates/apps/api-observability/mutations/notes.create.mutation.ts +8 -0
- package/templates/apps/api-observability/package.json +9 -8
- package/templates/apps/api-observability/queries/notes.list.query.ts +13 -0
- package/templates/apps/api-ratelimit/mutations/notes.create.mutation.ts +14 -0
- package/templates/apps/api-ratelimit/package.json +9 -8
- package/templates/apps/api-rbac/package.json +9 -8
- package/templates/apps/api-rest/package.json +8 -7
- package/templates/apps/api-saas/mutations/projects.create.mutation.ts +13 -0
- package/templates/apps/api-saas/package.json +12 -11
- package/templates/apps/api-saas/queries/projects.list.query.ts +11 -0
- package/templates/apps/api-saas-starter/actions/me.action.ts +13 -0
- package/templates/apps/api-saas-starter/app.config.ts +19 -0
- package/templates/apps/api-saas-starter/authz.ts +75 -0
- package/templates/apps/api-saas-starter/mutations/invites.create.mutation.ts +10 -0
- package/templates/apps/api-saas-starter/mutations/projects.create.mutation.ts +9 -0
- package/templates/apps/api-saas-starter/package.json +15 -11
- package/templates/apps/api-saas-starter/queries/invites.list.query.ts +19 -8
- package/templates/apps/api-saas-starter/queries/projects.list.query.ts +18 -7
- package/templates/apps/api-saas-starter/tests/accessDecisions.test.ts +135 -0
- package/templates/apps/api-search/mutations/articles.create.mutation.ts +14 -0
- package/templates/apps/api-search/package.json +9 -8
- package/templates/apps/api-search/queries/articles.list.query.ts +21 -8
- package/templates/apps/api-status/README.md +10 -3
- package/templates/apps/api-status/app.config.ts +8 -3
- package/templates/apps/api-status/authz.ts +5 -3
- package/templates/apps/api-status/package.json +9 -8
- package/templates/apps/api-status/queries/components.list.query.ts +14 -6
- package/templates/apps/api-status/queries/incidents.live.query.ts +23 -10
- package/templates/apps/api-status/queries/updates.list.query.ts +16 -9
- package/templates/apps/api-status/tests/status.test.ts +9 -1
- package/templates/apps/api-versioning/actions/documents.asOf.action.ts +12 -0
- package/templates/apps/api-versioning/actions/documents.history.action.ts +13 -0
- package/templates/apps/api-versioning/mutations/documents.create.mutation.ts +9 -0
- package/templates/apps/api-versioning/mutations/documents.update.mutation.ts +12 -0
- package/templates/apps/api-versioning/package.json +9 -8
- package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.ts +16 -0
- package/templates/apps/api-webhooks/package.json +10 -9
- package/templates/apps/api-webhooks/queries/orders.list.query.ts +10 -0
- package/templates/apps/changelog/package.json +8 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +10 -8
- package/templates/apps/frontend-admin/src/lib/admin.ts +20 -9
- package/templates/apps/frontend-admin/src/locales/de.ts +11 -1
- package/templates/apps/frontend-admin/src/locales/en.ts +13 -1
- package/templates/apps/frontend-admin/src/pages/admin/[entity]/page.tsx +65 -22
- package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +130 -23
- package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +3 -2
- package/templates/apps/frontend-admin/src/pages/admin/page.test.tsx +19 -2
- package/templates/apps/frontend-admin/src/pages/admin/page.tsx +9 -4
- package/templates/apps/frontend-app/app.config.ts +4 -3
- package/templates/apps/frontend-app/package.json +11 -8
- package/templates/apps/frontend-app/src/lib/api.ts +25 -0
- package/templates/apps/frontend-app/src/pages/page.test.tsx +130 -82
- package/templates/apps/frontend-app/src/pages/page.tsx +14 -18
- package/templates/apps/frontend-auth/package.json +10 -8
- package/templates/apps/frontend-blank/package.json +9 -7
- package/templates/apps/frontend-cms/package.json +11 -9
- package/templates/apps/frontend-collab/package.json +12 -9
- package/templates/apps/frontend-collab/src/pages/page.test.tsx +122 -78
- package/templates/apps/frontend-contact/package.json +9 -7
- package/templates/apps/frontend-dashboard/package.json +9 -7
- package/templates/apps/frontend-docs/package.json +9 -7
- package/templates/apps/frontend-i18n/package.json +8 -6
- package/templates/apps/frontend-landing/package.json +9 -7
- package/templates/apps/frontend-portal/package.json +10 -8
- package/templates/apps/frontend-saas/app.config.ts +10 -6
- package/templates/apps/frontend-saas/package.json +10 -8
- package/templates/apps/frontend-saas/src/lib/api.ts +27 -32
- package/templates/apps/frontend-saas/src/pages/dashboard/billing/page.tsx +7 -8
- package/templates/apps/frontend-saas/src/pages/dashboard/page.test.tsx +27 -3
- package/templates/apps/frontend-saas/src/pages/dashboard/page.tsx +4 -4
- package/templates/apps/frontend-saas/src/pages/dashboard/team/page.tsx +3 -4
- package/templates/apps/frontend-spa/package.json +9 -7
- package/templates/apps/frontend-ssr/package.json +9 -7
- package/templates/apps/frontend-ssr-api/package.json +10 -8
- package/templates/apps/frontend-static-blog/package.json +8 -6
- package/templates/apps/frontend-status/package.json +10 -8
- package/templates/apps/mobile-app/README.md +1 -0
- package/templates/apps/mobile-app/package.json +4 -2
- package/templates/apps/mobile-app/src/app/index.tsx +22 -12
- package/templates/apps/mobile-app/src/app/orders/[id].tsx +1 -1
- package/templates/apps/mobile-app/src/lib/api.ts +34 -0
- package/templates/apps/mobile-app/voltro.mobile.ts +4 -2
- package/templates/baselines/bare/.env.example +14 -0
- package/templates/baselines/bare/baseline.json +4 -4
- package/templates/baselines/compose/.env.example +14 -0
- package/templates/baselines/compose/README.md +1 -1
- package/templates/baselines/compose/baseline.json +5 -5
- package/templates/baselines/compose-mariadb/.env.example +14 -0
- package/templates/baselines/compose-mariadb/README.md +1 -1
- package/templates/baselines/compose-mariadb/baseline.json +5 -5
- package/templates/baselines/helm/.env.example +14 -0
- package/templates/baselines/helm/baseline.json +4 -4
- package/dist/apiBuild-C-x9YacA.js +0 -2
- package/dist/checkCommand-DRovTKza.js +0 -2
- package/dist/commands-CJfepbm4.js +0 -11541
- package/dist/dbCommand-b1gum4td.js +0 -2
- package/dist/dev-iiMtlkfs.js +0 -3
- package/dist/devActivity-BhIu6ncs.js +0 -159
- package/dist/frameworkTableAssembly-D-EebUQX.js +0 -2
- package/dist/inspect-mmBuRXmy.js +0 -2
- package/dist/manifestBuild-Dj8Jjoto.js +0 -2
- package/dist/seedRunner-Bqxgp7HZ.js +0 -230
- package/dist/serveCommand-DdaM4Hup.js +0 -1608
- package/dist/start-C0koT0UO.js +0 -1084
- /package/templates/apps/api-ai/actions/{summarize.action.server.tsx → summarize.action.server.ts} +0 -0
|
@@ -1,26 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
}, xe = (e) => typeof e == "object" && !!e && e.isCliError === !0, Se = (e) => typeof e != "string", Ce = (e, t, n, r) => {
|
|
1
|
+
import { n as e, r as t } from "./nodeEnvironment-cGFAj1J8.js";
|
|
2
|
+
import { t as n } from "./cliError-BmdYnghb.js";
|
|
3
|
+
import { n as r } from "./appModuleLoader-C9r9mxZt.js";
|
|
4
|
+
import { a as i, i as a, n as o, t as s } from "./cliArgs-D4p8n7EE.js";
|
|
5
|
+
import { r as c } from "./dialectDriver-CgXnDfec.js";
|
|
6
|
+
import { a as l } from "./connectionConfig-UFlIEiys.js";
|
|
7
|
+
import { i as u, n as d, r as f, s as p, t as m } from "./seedRunner-TFHHiToI.js";
|
|
8
|
+
import { n as h, o as g, y as _ } from "./frameworkTableAssembly-DkkP6BgC.js";
|
|
9
|
+
import { b as v, x as y } from "./fileConventions-Cof68_BL.js";
|
|
10
|
+
import { basename as b, join as x, relative as S, resolve as C } from "node:path";
|
|
11
|
+
import { promises as w, writeSync as ee } from "node:fs";
|
|
12
|
+
import { pathToFileURL as T } from "node:url";
|
|
13
|
+
import { createLogger as te } from "@voltro/logger";
|
|
14
|
+
import { Effect as E } from "effect";
|
|
15
|
+
import { SqlClient as D } from "@effect/sql";
|
|
16
|
+
import { DEFAULT_CDC_CHANNEL as ne, applyPlan as re, applySchema as O, assessRollingDeployGate as k, branchRehearsalExitCode as ie, declaredSnapshot as A, destructiveScope as ae, fingerprintSchema as oe, formatBranchRehearsal as se, ignoreTablesFromEnv as j, introspectSchema as ce, pendingFileMigrationIds as le, planMigrations as M, recordUpToDate as ue, rehearseMigrationOnBranch as de, rollbackFileBasedMigration as fe, rollingDeployUnsafeOps as pe, runFileBasedMigrations as me, shortFingerprint as N, squashMigrationPlans as he, unblockLossy as ge } from "@voltro/database/sql";
|
|
17
|
+
import { actorsTable as _e, branchNamespaceName as ve, frameworkTables as P, isEncrypted as F, isMigrationDefinition as ye, makeNamespaceBranchExecutor as be, provisionBranch as xe, quoteIdent as Se, registerCoreTables as Ce, resolveBranchMechanism as we, teardownBranch as Te } from "@voltro/database";
|
|
18
|
+
import { InMemoryDataStore as Ee, makeFieldCipher as De } from "@voltro/runtime";
|
|
19
|
+
import { createHash as Oe } from "node:crypto";
|
|
20
|
+
//#region src/adoptPlan.ts
|
|
21
|
+
var ke = (e) => typeof e != "string", Ae = (e, t, n, r) => {
|
|
24
22
|
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
23
|
r > 0 && i.push({
|
|
26
24
|
kind: "target-not-empty",
|
|
@@ -37,7 +35,7 @@ var O = class extends Error {
|
|
|
37
35
|
});
|
|
38
36
|
continue;
|
|
39
37
|
}
|
|
40
|
-
if (
|
|
38
|
+
if (ke(n)) {
|
|
41
39
|
c.push({
|
|
42
40
|
target: t,
|
|
43
41
|
source: n.expr,
|
|
@@ -80,23 +78,23 @@ var O = class extends Error {
|
|
|
80
78
|
remintIds: f,
|
|
81
79
|
fatal: i.some((e) => e.fatal)
|
|
82
80
|
};
|
|
83
|
-
},
|
|
81
|
+
}, je = (e, t) => [
|
|
84
82
|
`back up '${e.from}' to a restorable dump`,
|
|
85
83
|
`copy rows: INSERT INTO ${e.into} (${t.assignments.map((e) => e.target).join(", ")}) SELECT … FROM ${e.from}`,
|
|
86
84
|
...t.remintIds ? ["record old id → new id in a translation table", `rewrite every reference to '${e.from}', JSON columns included`] : [],
|
|
87
85
|
"verify: row counts match and a sample round-trips",
|
|
88
86
|
`drop '${e.from}' — LAST, and only after the verify`
|
|
89
|
-
],
|
|
87
|
+
], Me = (e, t, n) => {
|
|
90
88
|
let r = [`adopt · ${e.from} (${n} row(s)) → ${e.into}`, ""];
|
|
91
89
|
for (let e of t.assignments) r.push(` ${e.target.padEnd(24)} ← ${e.raw ? `${e.source} (raw)` : e.source}`);
|
|
92
90
|
t.problems.length > 0 && r.push("");
|
|
93
91
|
for (let e of t.problems) r.push(` ${e.fatal ? "✗" : "⚠"} ${e.detail}`);
|
|
94
92
|
if (r.push(""), t.fatal) return r.push(" REFUSED — fix the problems above. Nothing was written."), r;
|
|
95
93
|
r.push(" would run, in this order:");
|
|
96
|
-
for (let [n, i] of
|
|
94
|
+
for (let [n, i] of je(e, t).entries()) r.push(` ${n + 1}. ${i}`);
|
|
97
95
|
return r;
|
|
98
|
-
},
|
|
99
|
-
let a =
|
|
96
|
+
}, Ne = (e) => e === "mysql" || e === "mariadb" ? (e) => `\`${e}\`` : e === "mssql" ? (e) => `[${e}]` : (e) => `"${e}"`, Pe = (e) => `${e}__adopt_snapshot`, Fe = (e, t, n, r, i) => {
|
|
97
|
+
let a = Ne(n), o = Pe(e.from), s = t.assignments.map((e) => e.target), c = t.assignments.map((e) => e.raw ? e.source : a(e.source));
|
|
100
98
|
return i !== void 0 && (s.unshift(r), c.unshift(i)), {
|
|
101
99
|
snapshot: n === "mssql" ? `SELECT * INTO ${a(o)} FROM ${a(e.from)}` : `CREATE TABLE ${a(o)} AS SELECT * FROM ${a(e.from)}`,
|
|
102
100
|
copy: `INSERT INTO ${a(e.into)} (${s.map(a).join(", ")}) SELECT ${c.join(", ")} FROM ${a(e.from)}`,
|
|
@@ -106,18 +104,18 @@ var O = class extends Error {
|
|
|
106
104
|
drop: `DROP TABLE ${a(e.from)}`,
|
|
107
105
|
dropSnapshot: `DROP TABLE ${a(o)}`
|
|
108
106
|
};
|
|
109
|
-
},
|
|
107
|
+
}, Ie = (e) => {
|
|
110
108
|
let t = e[0];
|
|
111
109
|
if (t === void 0) return 0;
|
|
112
110
|
let n = t.n ?? t.N ?? Object.values(t)[0];
|
|
113
111
|
return Number(n ?? 0);
|
|
114
|
-
},
|
|
112
|
+
}, Le = async (e, t, n, r, i, a, o = {}) => {
|
|
115
113
|
if (t.fatal) throw Error("adopt: the plan was refused — nothing was run");
|
|
116
|
-
let s =
|
|
114
|
+
let s = Fe(e, t, n, r, i), c = Pe(e.from), l = Ie(await a(s.verifySource));
|
|
117
115
|
await a(s.snapshot);
|
|
118
116
|
for (let e of s.translations) await a(e);
|
|
119
117
|
await a(s.copy);
|
|
120
|
-
let u =
|
|
118
|
+
let u = Ie(await a(s.verifyTarget));
|
|
121
119
|
return u === l ? o.keepSource === !0 ? {
|
|
122
120
|
copied: u,
|
|
123
121
|
sourceRows: l,
|
|
@@ -137,7 +135,7 @@ var O = class extends Error {
|
|
|
137
135
|
snapshotTable: c,
|
|
138
136
|
sourceDropped: !1
|
|
139
137
|
};
|
|
140
|
-
},
|
|
138
|
+
}, I = [
|
|
141
139
|
"token",
|
|
142
140
|
"secret",
|
|
143
141
|
"password",
|
|
@@ -146,7 +144,7 @@ var O = class extends Error {
|
|
|
146
144
|
"credential",
|
|
147
145
|
"privatekey",
|
|
148
146
|
"private_key"
|
|
149
|
-
],
|
|
147
|
+
], Re = [
|
|
150
148
|
{
|
|
151
149
|
table: "_voltro_audit_log",
|
|
152
150
|
column: "subject"
|
|
@@ -191,7 +189,7 @@ var O = class extends Error {
|
|
|
191
189
|
table: "_voltro_row_history",
|
|
192
190
|
column: "subjectId"
|
|
193
191
|
}
|
|
194
|
-
],
|
|
192
|
+
], ze = (e, t) => {
|
|
195
193
|
switch (t) {
|
|
196
194
|
case "postgres": return `LOWER(CAST(${e} AS text))`;
|
|
197
195
|
case "mysql":
|
|
@@ -200,17 +198,17 @@ var O = class extends Error {
|
|
|
200
198
|
case "sqlite":
|
|
201
199
|
case "turso": return `LOWER(CAST(${e} AS TEXT))`;
|
|
202
200
|
}
|
|
203
|
-
},
|
|
201
|
+
}, Be = (e, t, n = I) => n.map((n) => `${ze(e, t)} LIKE '%${n}%'`).join("\n OR "), Ve = ["_omitted", "__redacted"], He = (e, t) => {
|
|
204
202
|
let n = e.toLowerCase();
|
|
205
203
|
return t.some((e) => n.includes(e));
|
|
206
|
-
},
|
|
204
|
+
}, Ue = (e, t = []) => {
|
|
207
205
|
if (Array.isArray(e)) {
|
|
208
|
-
for (let n of e)
|
|
206
|
+
for (let n of e) Ue(n, t);
|
|
209
207
|
return t;
|
|
210
208
|
}
|
|
211
|
-
if (typeof e == "object" && e) for (let [n, r] of Object.entries(e)) t.push(n),
|
|
209
|
+
if (typeof e == "object" && e) for (let [n, r] of Object.entries(e)) t.push(n), Ue(r, t);
|
|
212
210
|
return t;
|
|
213
|
-
},
|
|
211
|
+
}, We = (e) => Array.isArray(e) ? e.map(We) : typeof e == "object" && e ? Object.fromEntries(Object.entries(e).map(([e, t]) => [e, Ve.includes(e) ? null : We(t)])) : e, Ge = (e, t = I) => {
|
|
214
212
|
let n;
|
|
215
213
|
try {
|
|
216
214
|
n = JSON.parse(e);
|
|
@@ -222,33 +220,33 @@ var O = class extends Error {
|
|
|
222
220
|
};
|
|
223
221
|
}
|
|
224
222
|
return {
|
|
225
|
-
asKey:
|
|
226
|
-
redactionMarkerOnly: !
|
|
223
|
+
asKey: Ue(n).some((e) => He(e, t)),
|
|
224
|
+
redactionMarkerOnly: !He(JSON.stringify(We(n)) ?? "", t),
|
|
227
225
|
undecidable: !1
|
|
228
226
|
};
|
|
229
|
-
},
|
|
227
|
+
}, Ke = (e) => ({
|
|
230
228
|
examined: e.length,
|
|
231
229
|
asKey: e.filter((e) => e.asKey).length,
|
|
232
230
|
redactionMarkerOnly: e.filter((e) => e.redactionMarkerOnly && !e.asKey).length,
|
|
233
231
|
undecidable: e.filter((e) => e.undecidable).length
|
|
234
|
-
}),
|
|
232
|
+
}), L = (e) => {
|
|
235
233
|
let t = e.classification;
|
|
236
234
|
return t === void 0 || e.hits === void 0 || e.hits === 0 ? !1 : t.examined === e.hits && t.undecidable === 0 && t.asKey === 0 && t.redactionMarkerOnly === e.hits;
|
|
237
|
-
},
|
|
238
|
-
let i = `WHERE ${
|
|
235
|
+
}, qe = (e, t, n, r) => {
|
|
236
|
+
let i = `WHERE ${Be(t, n)}`, a = ze(t, n).replace(/^LOWER\((.*)\)$/s, "$1");
|
|
239
237
|
return n === "mssql" ? `SELECT TOP (${r}) ${a} AS v FROM ${e}\n ${i}` : `SELECT ${a} AS v FROM ${e}\n ${i}\n LIMIT ${r}`;
|
|
240
|
-
},
|
|
238
|
+
}, Je = async (e, t, n, r, i) => (await E.runPromise(e.unsafe(qe(t, n, r, i)))).map((e) => {
|
|
241
239
|
let t = Object.values(e)[0];
|
|
242
240
|
return typeof t == "string" ? t : JSON.stringify(t ?? null);
|
|
243
|
-
}),
|
|
244
|
-
let n = await
|
|
241
|
+
}), Ye = async (e, t) => {
|
|
242
|
+
let n = await E.runPromise(e.unsafe(t));
|
|
245
243
|
return Number(Object.values(n[0] ?? { n: 0 })[0] ?? 0);
|
|
246
|
-
},
|
|
244
|
+
}, Xe = async (e, t, n, r) => {
|
|
247
245
|
let i = [];
|
|
248
246
|
for (let a of n) {
|
|
249
247
|
let n = r(a.table), o;
|
|
250
248
|
try {
|
|
251
|
-
o = await
|
|
249
|
+
o = await Ye(e, `SELECT COUNT(*) AS n FROM ${n}`);
|
|
252
250
|
} catch {
|
|
253
251
|
i.push({
|
|
254
252
|
table: a.table,
|
|
@@ -260,9 +258,9 @@ var O = class extends Error {
|
|
|
260
258
|
continue;
|
|
261
259
|
}
|
|
262
260
|
try {
|
|
263
|
-
let s = await
|
|
264
|
-
if (s > 0) for (let i of
|
|
265
|
-
let o = await
|
|
261
|
+
let s = await Ye(e, `SELECT COUNT(*) AS n FROM ${n}\n WHERE ${Be(r(a.column), t)}`), c = [];
|
|
262
|
+
if (s > 0) for (let i of I) try {
|
|
263
|
+
let o = await Ye(e, `SELECT COUNT(*) AS n FROM ${n}\n WHERE ${Be(r(a.column), t, [i])}`);
|
|
266
264
|
o > 0 && c.push({
|
|
267
265
|
needle: i,
|
|
268
266
|
rows: o
|
|
@@ -270,7 +268,7 @@ var O = class extends Error {
|
|
|
270
268
|
} catch {}
|
|
271
269
|
let l;
|
|
272
270
|
if (s > 0) try {
|
|
273
|
-
l =
|
|
271
|
+
l = Ke((await Je(e, n, r(a.column), t, 500)).map((e) => Ge(e)));
|
|
274
272
|
} catch {}
|
|
275
273
|
i.push({
|
|
276
274
|
table: a.table,
|
|
@@ -291,8 +289,8 @@ var O = class extends Error {
|
|
|
291
289
|
}
|
|
292
290
|
}
|
|
293
291
|
return i;
|
|
294
|
-
},
|
|
295
|
-
let n =
|
|
292
|
+
}, Ze = (e, t) => {
|
|
293
|
+
let n = I.length, r = [`credential scan · dialect ${t}`, ""], i = /* @__PURE__ */ new Set();
|
|
296
294
|
for (let t of e) {
|
|
297
295
|
if (t.unavailable === "column") {
|
|
298
296
|
r.push(` · ${t.table}.${t.column} — column is not present on this deployment (${t.scanned} row(s) exist but this column was not scanned)`);
|
|
@@ -306,34 +304,34 @@ var O = class extends Error {
|
|
|
306
304
|
r.push(` · ${t.table} — table is EMPTY (0 rows scanned; this is not a clean bill of health)`);
|
|
307
305
|
continue;
|
|
308
306
|
}
|
|
309
|
-
let e =
|
|
307
|
+
let e = L(t), n = (t.hits ?? 0) === 0 ? "✓" : e ? "~" : "✗";
|
|
310
308
|
r.push(` ${n} ${t.table}.${t.column} — ${t.hits} of ${t.scanned} row(s) contain a credential-shaped NAME (anywhere in the value)`), (t.matchedNeedles?.length ?? 0) > 0 && r.push(` matched (rows per needle, may overlap): ${t.matchedNeedles.map((e) => `${e.needle} (${e.rows})`).join(", ")}`);
|
|
311
309
|
let a = t.classification;
|
|
312
|
-
a !== void 0 && (r.push(` examined ${a.examined} of ${t.hits} matched row(s)${a.examined < (t.hits ?? 0) ? " (capped at 500)" : ""}: ${a.asKey} as a JSON KEY, ${a.redactionMarkerOnly} only inside a redaction marker${a.undecidable > 0 ? `, ${a.undecidable} not parseable as JSON` : ""}`), e && r.push(` EXPLAINED — every match is \`${
|
|
310
|
+
a !== void 0 && (r.push(` examined ${a.examined} of ${t.hits} matched row(s)${a.examined < (t.hits ?? 0) ? " (capped at 500)" : ""}: ${a.asKey} as a JSON KEY, ${a.redactionMarkerOnly} only inside a redaction marker${a.undecidable > 0 ? `, ${a.undecidable} not parseable as JSON` : ""}`), e && r.push(` EXPLAINED — every match is \`${Ve.join("` / `")}\`, the framework's own`, " record that a `.sensitive()` column was left OUT. Nothing to purge, nothing to rotate."));
|
|
313
311
|
}
|
|
314
|
-
let a = e.reduce((e, t) => e + (
|
|
312
|
+
let a = e.reduce((e, t) => e + (L(t) ? 0 : t.hits ?? 0), 0), o = e.reduce((e, t) => e + (L(t) ? t.hits ?? 0 : 0), 0);
|
|
315
313
|
return r.push(""), o > 0 && r.push(` ${o} row(s) matched only the framework's own redaction markers and are NOT findings.`), a > 0 ? r.push(` ${a} 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 || e.unavailable !== void 0) ? r.push(" NOTHING WAS SCANNED. This run proves nothing about your data.") : r.push(" No credential-shaped keys found in the rows that exist."), r.push("", ` These are NAME heuristics (${n} of them), not proof. A credential in a`, " column whose key is not credential-shaped — `keyValue`, `pat`, `authz` —", " is invisible here. A clean run means \"no credential-shaped NAME found\",", " which is not the same as \"no credential\".", "", " Run this on EVERY environment. A dev database is not a sample of production."), r.join("\n");
|
|
316
|
-
},
|
|
314
|
+
}, Qe = (e) => e.some((e) => (e.hits ?? 0) > 0 && !L(e)) ? 1 : e.every((e) => e.unavailable !== void 0 || e.scanned === void 0 || e.scanned === 0) ? 2 : 0, $e = /\.migration\.ts$/, R = async (e, t) => {
|
|
317
315
|
let n = [];
|
|
318
316
|
for (let r of e) {
|
|
319
317
|
let e;
|
|
320
318
|
try {
|
|
321
|
-
e = await import(
|
|
319
|
+
e = await import(T(r).href);
|
|
322
320
|
} catch (e) {
|
|
323
321
|
t.warn("migration: failed to import", { file: r }, e);
|
|
324
322
|
continue;
|
|
325
323
|
}
|
|
326
|
-
let i = (e.default &&
|
|
324
|
+
let i = (e.default && ye(e.default) ? e.default : void 0) ?? Object.values(e).find(ye);
|
|
327
325
|
if (!i) {
|
|
328
326
|
t.warn("migration: no defineMigration export", { file: r });
|
|
329
327
|
continue;
|
|
330
328
|
}
|
|
331
|
-
i.id !==
|
|
329
|
+
i.id !== b(r).replace(/\.migration\.ts$/, "") && t.warn("migration: id does not match filename — id wins for ordering, but rename the file for clarity", {
|
|
332
330
|
file: r,
|
|
333
331
|
idInFile: i.id,
|
|
334
|
-
idFromFile:
|
|
332
|
+
idFromFile: b(r).replace(/\.migration\.ts$/, "")
|
|
335
333
|
});
|
|
336
|
-
let a = await
|
|
334
|
+
let a = await w.readFile(r, "utf8").catch(() => ""), o = Oe("sha256").update(a).digest("hex").slice(0, 16);
|
|
337
335
|
n.push({
|
|
338
336
|
file: r,
|
|
339
337
|
definition: i,
|
|
@@ -341,14 +339,14 @@ var O = class extends Error {
|
|
|
341
339
|
});
|
|
342
340
|
}
|
|
343
341
|
return [...n].sort((e, t) => e.definition.id < t.definition.id ? -1 : 1);
|
|
344
|
-
},
|
|
342
|
+
}, et = (e) => {
|
|
345
343
|
let t = (process.env.DB_DIALECT ?? "postgres").toLowerCase(), n = t === "memory" ? "postgres" : t;
|
|
346
|
-
return
|
|
347
|
-
},
|
|
348
|
-
let e = yield* (yield*
|
|
344
|
+
return E.runPromise(O(P, n).pipe(E.provide(e)));
|
|
345
|
+
}, tt = (e) => E.runPromise(E.gen(function* () {
|
|
346
|
+
let e = yield* (yield* D.SqlClient)`SELECT id, status, hash, "appliedAt" FROM "_voltro_migrations"`.pipe(E.orElseSucceed(() => []));
|
|
349
347
|
return new Map(e.map((e) => [e.id, e]));
|
|
350
|
-
}).pipe(
|
|
351
|
-
yield* (yield*
|
|
348
|
+
}).pipe(E.provide(e))), z = (e, t) => E.runPromise(E.gen(function* () {
|
|
349
|
+
yield* (yield* D.SqlClient)`
|
|
352
350
|
INSERT INTO "_voltro_migrations"
|
|
353
351
|
(id, name, hash, status, "appliedAt", "durationMs", "errorMessage", "errorStack")
|
|
354
352
|
VALUES
|
|
@@ -363,14 +361,14 @@ var O = class extends Error {
|
|
|
363
361
|
"errorStack" = EXCLUDED."errorStack",
|
|
364
362
|
"updatedAt" = now()
|
|
365
363
|
`;
|
|
366
|
-
}).pipe(
|
|
364
|
+
}).pipe(E.provide(e))), nt = async (e, t, n) => {
|
|
367
365
|
if (e.length === 0) return {
|
|
368
366
|
applied: [],
|
|
369
367
|
failed: null,
|
|
370
368
|
skipped: []
|
|
371
369
|
};
|
|
372
|
-
await
|
|
373
|
-
let r = await
|
|
370
|
+
await et(t);
|
|
371
|
+
let r = await tt(t), i = [], a = [], o = null;
|
|
374
372
|
for (let s of e) {
|
|
375
373
|
let e = r.get(s.definition.id);
|
|
376
374
|
if (e && e.status === "applied") {
|
|
@@ -405,7 +403,7 @@ var O = class extends Error {
|
|
|
405
403
|
for (let e of s.definition.steps) {
|
|
406
404
|
let n = Date.now();
|
|
407
405
|
try {
|
|
408
|
-
await
|
|
406
|
+
await E.runPromise(e.apply.pipe(E.provide(t))), l.push({
|
|
409
407
|
stepName: e.name,
|
|
410
408
|
status: "applied",
|
|
411
409
|
durationMs: Date.now() - n
|
|
@@ -471,9 +469,9 @@ var O = class extends Error {
|
|
|
471
469
|
failed: o,
|
|
472
470
|
skipped: a
|
|
473
471
|
};
|
|
474
|
-
},
|
|
475
|
-
await
|
|
476
|
-
let i = await
|
|
472
|
+
}, rt = async (e, t, n, r = {}) => {
|
|
473
|
+
await et(t);
|
|
474
|
+
let i = await tt(t), a = e.filter((e) => i.get(e.definition.id)?.status === "applied");
|
|
477
475
|
if (a.length === 0) return n.info("migration: nothing to roll back"), {
|
|
478
476
|
applied: [],
|
|
479
477
|
failed: null,
|
|
@@ -491,7 +489,7 @@ var O = class extends Error {
|
|
|
491
489
|
for (let n = e.definition.steps.length - 1; n >= 0; n--) {
|
|
492
490
|
let o = e.definition.steps[n], s = Date.now();
|
|
493
491
|
try {
|
|
494
|
-
await
|
|
492
|
+
await E.runPromise(o.undo.pipe(E.provide(t))), i.push({
|
|
495
493
|
stepName: o.name,
|
|
496
494
|
status: "applied",
|
|
497
495
|
durationMs: Date.now() - s
|
|
@@ -542,9 +540,9 @@ var O = class extends Error {
|
|
|
542
540
|
failed: c,
|
|
543
541
|
skipped: []
|
|
544
542
|
};
|
|
545
|
-
},
|
|
546
|
-
await
|
|
547
|
-
let n = await
|
|
543
|
+
}, it = async (e, t) => {
|
|
544
|
+
await et(t);
|
|
545
|
+
let n = await tt(t);
|
|
548
546
|
return e.map((e) => {
|
|
549
547
|
let t = n.get(e.definition.id);
|
|
550
548
|
return t ? {
|
|
@@ -561,7 +559,7 @@ var O = class extends Error {
|
|
|
561
559
|
hashMatches: !0
|
|
562
560
|
};
|
|
563
561
|
});
|
|
564
|
-
},
|
|
562
|
+
}, at = (e) => {
|
|
565
563
|
if (!e.failed) return "";
|
|
566
564
|
let t = e.failed, n = [
|
|
567
565
|
`✗ Migration failed: ${t.migrationId} / step '${t.error?.stepName ?? "?"}'`,
|
|
@@ -574,22 +572,22 @@ var O = class extends Error {
|
|
|
574
572
|
""
|
|
575
573
|
];
|
|
576
574
|
return e.applied.length > 0 && n.unshift("", `(${e.applied.length} migration(s) applied successfully before the failure.)`), n.join("\n");
|
|
577
|
-
},
|
|
575
|
+
}, ot = (e) => {
|
|
578
576
|
let t = e.split(".");
|
|
579
577
|
if (t.length !== 2 || !t[0] || !t[1]) throw Error(`voltro db encrypt-column: expected <table>.<column>, got "${e}"`);
|
|
580
578
|
return {
|
|
581
579
|
table: t[0],
|
|
582
580
|
column: t[1]
|
|
583
581
|
};
|
|
584
|
-
},
|
|
585
|
-
let n =
|
|
582
|
+
}, st = (e) => 49 + 4 * Math.ceil(e / 3), ct = (e, t) => {
|
|
583
|
+
let n = st(e);
|
|
586
584
|
return {
|
|
587
585
|
longestPlaintextBytes: e,
|
|
588
586
|
longestCiphertext: n,
|
|
589
587
|
declaredMaxLength: t,
|
|
590
588
|
fits: t === void 0 || n <= t
|
|
591
589
|
};
|
|
592
|
-
},
|
|
590
|
+
}, lt = (e, t) => `${e.table}.${e.column} is too narrow: its longest value is ${t.longestPlaintextBytes} byte(s), which encrypts to ${t.longestCiphertext} characters, and the column is declared as ${t.declaredMaxLength}. Widen it first (\`.maxLength(${t.longestCiphertext})\` or more, then \`voltro db apply\`) and run this again. Nothing was written.`, ut = (e, t) => {
|
|
593
591
|
let n = t ? "would encrypt" : "encrypted";
|
|
594
592
|
return [
|
|
595
593
|
` ${e.table}.${e.column}`,
|
|
@@ -598,10 +596,10 @@ var O = class extends Error {
|
|
|
598
596
|
` null / empty ${e.empty}`,
|
|
599
597
|
` ${n.padEnd(18)} ${e.encrypted}`
|
|
600
598
|
].join("\n");
|
|
601
|
-
},
|
|
599
|
+
}, dt = (e, t, n) => {
|
|
602
600
|
if (e == null) return;
|
|
603
601
|
let r = typeof e == "string" ? e : String(e);
|
|
604
|
-
if (r === "" ||
|
|
602
|
+
if (r === "" || F(r)) return;
|
|
605
603
|
let i = t.encrypt(r), a;
|
|
606
604
|
try {
|
|
607
605
|
a = t.decrypt(i);
|
|
@@ -610,13 +608,13 @@ var O = class extends Error {
|
|
|
610
608
|
}
|
|
611
609
|
if (a !== r) throw Error(`${n.table}.${n.column}: round-trip mismatch — the cipher did not return what it was given. Nothing was written.`);
|
|
612
610
|
return i;
|
|
613
|
-
},
|
|
611
|
+
}, ft = (e) => Buffer.byteLength(e, "utf8"), pt = (e, t) => {
|
|
614
612
|
if (e !== void 0) try {
|
|
615
613
|
return t.decrypt(e), !0;
|
|
616
614
|
} catch {
|
|
617
615
|
return !1;
|
|
618
616
|
}
|
|
619
|
-
},
|
|
617
|
+
}, mt = (e, t) => `${e.table}.${e.column} already holds values encrypted with a DIFFERENT key than ${t} — the active key cannot decrypt them. Continuing would encrypt the remaining rows under a second key, and the column would then be readable with neither key alone. Nothing was written. Point ${t} at the key the existing rows were written with.`, ht = (e) => [
|
|
620
618
|
"",
|
|
621
619
|
" NEXT — this converted the rows that exist. New writes are still plaintext",
|
|
622
620
|
" until the column carries the modifier:",
|
|
@@ -626,7 +624,7 @@ var O = class extends Error {
|
|
|
626
624
|
" Order does not matter: a read returns a non-ciphertext value unchanged, so",
|
|
627
625
|
" the column may hold a mix while you deploy. Run this again afterwards to",
|
|
628
626
|
" catch anything written in between — it skips what is already encrypted."
|
|
629
|
-
].join("\n"), B =
|
|
627
|
+
].join("\n"), B = te({ scope: "voltro:db" }), gt = /* @__PURE__ */ new Set([
|
|
630
628
|
"node_modules",
|
|
631
629
|
"dist",
|
|
632
630
|
"build",
|
|
@@ -636,46 +634,50 @@ var O = class extends Error {
|
|
|
636
634
|
".git"
|
|
637
635
|
]), V = async (e, t) => {
|
|
638
636
|
let n = [], r = async (e) => {
|
|
639
|
-
let i = await
|
|
637
|
+
let i = await w.readdir(e, { withFileTypes: !0 }).catch(() => []);
|
|
640
638
|
for (let a of i) {
|
|
641
|
-
if (
|
|
642
|
-
let i =
|
|
639
|
+
if (gt.has(a.name)) continue;
|
|
640
|
+
let i = x(e, a.name);
|
|
643
641
|
a.isDirectory() ? await r(i) : a.isFile() && t.test(a.name) && n.push(i);
|
|
644
642
|
}
|
|
645
643
|
};
|
|
646
644
|
return await r(e), n;
|
|
647
|
-
},
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
645
|
+
}, _t = [
|
|
646
|
+
"--against",
|
|
647
|
+
"--batch",
|
|
648
|
+
"--before",
|
|
649
|
+
"--id",
|
|
650
|
+
"--key-env",
|
|
651
|
+
"--lifecycle",
|
|
652
|
+
"--limit",
|
|
653
|
+
"--name",
|
|
654
|
+
"--note",
|
|
655
|
+
"--online-after",
|
|
656
|
+
"--plan",
|
|
657
|
+
"--pr",
|
|
658
|
+
"--prefer",
|
|
659
|
+
"--root",
|
|
660
|
+
"--seed",
|
|
661
|
+
"--store",
|
|
662
|
+
"--to",
|
|
663
|
+
"--token"
|
|
664
|
+
], H = (e) => C(s(e, _t) ?? process.cwd()), U = () => l("migration"), vt = (e) => {
|
|
663
665
|
let t = e;
|
|
664
666
|
if (typeof t != "object" || !t) return !1;
|
|
665
667
|
if (t.code === "ERR_OUT_OF_RANGE" || t.cause?.code === "ERR_OUT_OF_RANGE") return !0;
|
|
666
668
|
let n = `${t.message ?? ""} ${t.cause?.message ?? ""} ${t.stack ?? ""}`;
|
|
667
669
|
return /ERR_OUT_OF_RANGE|out of range/i.test(n) && /pg-protocol|parseDataRow|buffer-reader|BufferReader/i.test(n);
|
|
668
|
-
},
|
|
670
|
+
}, yt = "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 = () => {
|
|
669
671
|
let e = (process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
670
|
-
if (e === "memory") throw new
|
|
672
|
+
if (e === "memory") throw new n("voltro db: subcommands require a real dialect, not DB_DIALECT=memory.");
|
|
671
673
|
return e;
|
|
672
|
-
}, G = (e) =>
|
|
673
|
-
let t = H(e), n = e.includes("--dry-run"), r = await
|
|
674
|
+
}, G = (e) => Se(e, W()), K = (e) => e === "mysql" || e === "mariadb" ? "table_schema = DATABASE()" : e === "mssql" ? "table_schema = SCHEMA_NAME()" : "table_schema = 'public'", q = async () => (await c(W())).makeSqlLayer(U()), bt = async (e) => {
|
|
675
|
+
let t = H(e), n = e.includes("--dry-run"), r = await R(await V(t, $e), B);
|
|
674
676
|
if (r.length === 0) return B.info("no migrations found", { root: t }), 0;
|
|
675
677
|
if (n) {
|
|
676
678
|
let e = /* @__PURE__ */ new Set();
|
|
677
679
|
try {
|
|
678
|
-
let t = await
|
|
680
|
+
let t = await it(r, await q());
|
|
679
681
|
e = new Set(t.filter((e) => e.status === "applied").map((e) => e.id));
|
|
680
682
|
} catch {
|
|
681
683
|
B.warn("migrate --dry-run: could not read the migration ledger — showing ALL as pending");
|
|
@@ -686,116 +688,116 @@ var O = class extends Error {
|
|
|
686
688
|
for (let e of t) console.log(` pending ${e.definition.id}`);
|
|
687
689
|
return 0;
|
|
688
690
|
}
|
|
689
|
-
let i = await
|
|
690
|
-
return i.failed ? (process.stderr.write(
|
|
691
|
+
let i = await nt(r, await q(), B);
|
|
692
|
+
return i.failed ? (process.stderr.write(at(i) + "\n"), 1) : (B.info("migrate complete", {
|
|
691
693
|
applied: i.applied.length,
|
|
692
694
|
skipped: i.skipped.length
|
|
693
695
|
}), 0);
|
|
694
|
-
},
|
|
695
|
-
let t = H(e), n =
|
|
696
|
+
}, xt = async (e) => {
|
|
697
|
+
let t = H(e), n = o(e, "--to"), r = await R(await V(t, $e), B);
|
|
696
698
|
if (r.length === 0) return B.info("no migrations found", { root: t }), 0;
|
|
697
|
-
let i = await
|
|
698
|
-
return i.failed ? (process.stderr.write(
|
|
699
|
-
},
|
|
700
|
-
let t = await
|
|
699
|
+
let i = await rt(r, await q(), B, n ? { to: n } : {});
|
|
700
|
+
return i.failed ? (process.stderr.write(at(i) + "\n"), 1) : (B.info("rollback complete", { reverted: i.applied.length }), 0);
|
|
701
|
+
}, St = async (e) => {
|
|
702
|
+
let t = await R(await V(H(e), $e), B);
|
|
701
703
|
if (t.length === 0) return console.log("no migrations found"), 0;
|
|
702
|
-
let n = await
|
|
704
|
+
let n = await it(t, await q());
|
|
703
705
|
for (let e of n) {
|
|
704
706
|
let t = e.status === "applied" ? "✓" : e.status === "failed" ? "✗" : e.status === "reverted" ? "↩" : "·", n = e.hashMatches ? "" : " (hash drift!)", r = e.appliedAt ? ` @ ${e.appliedAt.toISOString()}` : "";
|
|
705
707
|
console.log(` ${t} ${e.id} [${e.status}] ${e.name}${r}${n}`);
|
|
706
708
|
}
|
|
707
709
|
return 0;
|
|
708
|
-
},
|
|
709
|
-
let t = (
|
|
710
|
+
}, Ct = async (e) => {
|
|
711
|
+
let t = (o(e, "--store") ?? process.env.DB_DIALECT ?? "postgres").toLowerCase();
|
|
710
712
|
if (t === "memory") return B.warn("voltro db seed: using in-memory store (explicit opt-in via --store memory / DB_DIALECT=memory)"), B.warn(" rows written by this run will NOT persist beyond the process. set DB_DIALECT=postgres or unset to seed the real DB."), {
|
|
711
|
-
store: new
|
|
713
|
+
store: new Ee(),
|
|
712
714
|
dispose: async () => {},
|
|
713
715
|
dialectId: "memory"
|
|
714
716
|
};
|
|
715
|
-
let n = await
|
|
717
|
+
let n = await c(t);
|
|
716
718
|
B.info(`voltro db seed: target store=${n.id} — rows will persist`);
|
|
717
|
-
let
|
|
719
|
+
let r = await n.makeStore({
|
|
718
720
|
sqlLayer: n.makeSqlLayer(U()),
|
|
719
721
|
changeStrategy: "inline"
|
|
720
|
-
}),
|
|
722
|
+
}), i = r;
|
|
721
723
|
return {
|
|
722
|
-
store:
|
|
724
|
+
store: r,
|
|
723
725
|
dispose: async () => {
|
|
724
|
-
typeof
|
|
726
|
+
typeof i.close == "function" && await i.close(5e3);
|
|
725
727
|
},
|
|
726
728
|
dialectId: n.id
|
|
727
729
|
};
|
|
728
|
-
},
|
|
729
|
-
let
|
|
730
|
-
if (
|
|
731
|
-
let { store:
|
|
730
|
+
}, wt = async (e) => {
|
|
731
|
+
let t = H(e), n = o(e, "--id"), r = o(e, "--lifecycle"), i = await d(await V(t, y), B);
|
|
732
|
+
if (i.length === 0) return B.info("no seeds found", { root: t }), 0;
|
|
733
|
+
let { store: a, dispose: s, dialectId: c } = await Ct(e);
|
|
732
734
|
try {
|
|
733
|
-
return
|
|
735
|
+
return n ? +((await u(i, n, a, B)).status === "failed") : +((await f((r ? i.filter((e) => e.definition.lifecycle === r) : i.filter((e) => e.definition.lifecycle === "boot")).map((e) => ({
|
|
734
736
|
...e,
|
|
735
737
|
definition: {
|
|
736
738
|
...e.definition,
|
|
737
739
|
lifecycle: "boot"
|
|
738
740
|
}
|
|
739
|
-
})),
|
|
741
|
+
})), a, B, { force: !0 })).filter((e) => e.status === "failed").length > 0);
|
|
740
742
|
} finally {
|
|
741
|
-
await
|
|
743
|
+
await s().catch((e) => {
|
|
742
744
|
B.warn("voltro db seed: store dispose failed", { error: String(e) });
|
|
743
745
|
});
|
|
744
746
|
}
|
|
745
|
-
},
|
|
746
|
-
let t = await V(e,
|
|
747
|
+
}, Tt = (e) => typeof e == "object" && !!e && "tableName" in e && "fields" in e && typeof e.tableName == "string", Et = async (e) => {
|
|
748
|
+
let t = await V(e, v), n = [];
|
|
747
749
|
for (let r of t) {
|
|
748
750
|
let t;
|
|
749
751
|
try {
|
|
750
|
-
t = await import(
|
|
752
|
+
t = await import(T(r).href);
|
|
751
753
|
} catch (t) {
|
|
752
754
|
B.warn("db: failed to import schema file", {
|
|
753
|
-
file:
|
|
755
|
+
file: S(e, r),
|
|
754
756
|
error: String(t)
|
|
755
757
|
});
|
|
756
758
|
continue;
|
|
757
759
|
}
|
|
758
|
-
for (let e of Object.values(t))
|
|
760
|
+
for (let e of Object.values(t)) Tt(e) && n.push(e);
|
|
759
761
|
}
|
|
760
762
|
return n;
|
|
761
|
-
},
|
|
763
|
+
}, Dt = (e) => {
|
|
762
764
|
let t = new Map(e.map((e) => [e.tableName, e]));
|
|
763
|
-
t.has("actors") || t.set("actors",
|
|
765
|
+
t.has("actors") || t.set("actors", _e);
|
|
764
766
|
let n = t.get("actors"), r = t.get("tenants");
|
|
765
|
-
return
|
|
767
|
+
return Ce({
|
|
766
768
|
actors: () => n,
|
|
767
769
|
...r === void 0 ? {} : { tenants: () => r }
|
|
768
770
|
}), [...t.values()];
|
|
769
|
-
},
|
|
771
|
+
}, Ot = (e) => {
|
|
770
772
|
let t = /* @__PURE__ */ new Map();
|
|
771
773
|
for (let n of e) {
|
|
772
774
|
let e = n.tableName;
|
|
773
775
|
e !== void 0 && !t.has(e) && t.set(e, n);
|
|
774
776
|
}
|
|
775
777
|
return [...t.values()];
|
|
776
|
-
},
|
|
777
|
-
let t =
|
|
778
|
+
}, kt = (e) => {
|
|
779
|
+
let t = ae(process.env.VOLTRO_DESTRUCTIVE_OK);
|
|
778
780
|
if (t === void 0 || !e.operations.filter((e) => e.blocked).every((e) => e.classification === "lossy")) return {
|
|
779
781
|
plan: e,
|
|
780
782
|
acknowledged: !1
|
|
781
783
|
};
|
|
782
|
-
let n =
|
|
784
|
+
let n = ge(e, t);
|
|
783
785
|
return {
|
|
784
786
|
plan: n,
|
|
785
787
|
acknowledged: !n.operations.some((e) => e.blocked)
|
|
786
788
|
};
|
|
787
|
-
}, J = async (e, t) =>
|
|
789
|
+
}, J = async (e, t) => E.runPromise(t.pipe(E.provide(e))), Y = async (e) => {
|
|
788
790
|
try {
|
|
789
|
-
return await J(e,
|
|
791
|
+
return await J(e, D.SqlClient.pipe(E.flatMap(ce)));
|
|
790
792
|
} catch (e) {
|
|
791
793
|
let t = String(e?.message ?? e);
|
|
792
|
-
if (
|
|
793
|
-
let n = /* @__PURE__ */ Error(`${
|
|
794
|
+
if (vt(e) || /connection error/i.test(t)) {
|
|
795
|
+
let n = /* @__PURE__ */ Error(`${yt}\n\nOriginal error: ${t}`);
|
|
794
796
|
throw n.cause = e, n;
|
|
795
797
|
}
|
|
796
798
|
throw e;
|
|
797
799
|
}
|
|
798
|
-
},
|
|
800
|
+
}, At = {
|
|
799
801
|
safe: "\x1B[32m✓\x1B[0m",
|
|
800
802
|
"needs-default": "\x1B[32m⊕\x1B[0m",
|
|
801
803
|
"needs-backfill": "\x1B[33m⊕\x1B[0m",
|
|
@@ -803,7 +805,7 @@ var O = class extends Error {
|
|
|
803
805
|
lossy: "\x1B[31m✗\x1B[0m",
|
|
804
806
|
"online-required": "\x1B[36m◷\x1B[0m",
|
|
805
807
|
"multi-step": "\x1B[35m⇉\x1B[0m"
|
|
806
|
-
},
|
|
808
|
+
}, jt = (e) => {
|
|
807
809
|
let t = e.op, n;
|
|
808
810
|
switch (t.kind) {
|
|
809
811
|
case "create-table":
|
|
@@ -867,9 +869,9 @@ var O = class extends Error {
|
|
|
867
869
|
n = `ALTER TABLE ${t.table} DROP CHECK ${t.column}`;
|
|
868
870
|
break;
|
|
869
871
|
}
|
|
870
|
-
let r =
|
|
872
|
+
let r = At[e.classification], i = e.reason ? ` [2m# ${e.reason}[0m` : "", a = e.blocked ? `\n [31m! ${e.blocked.fix}[0m` : "";
|
|
871
873
|
return ` ${r} ${n}${i}${a}`;
|
|
872
|
-
},
|
|
874
|
+
}, Mt = async (e, t) => {
|
|
873
875
|
let n = e.operations.filter((e) => e.classification === "needs-default" && e.op.kind === "add-column");
|
|
874
876
|
if (n.length === 0) return;
|
|
875
877
|
let r = [];
|
|
@@ -878,7 +880,7 @@ var O = class extends Error {
|
|
|
878
880
|
if (i === void 0 || a === void 0) continue;
|
|
879
881
|
let o = 0;
|
|
880
882
|
try {
|
|
881
|
-
let e = await J(t,
|
|
883
|
+
let e = await J(t, D.SqlClient.pipe(E.flatMap((e) => e.unsafe(`SELECT COUNT(*) AS n FROM ${Se(i, W())}`))));
|
|
882
884
|
o = Number(Object.values(e[0] ?? { n: 0 })[0] ?? 0);
|
|
883
885
|
} catch {
|
|
884
886
|
continue;
|
|
@@ -890,187 +892,334 @@ var O = class extends Error {
|
|
|
890
892
|
for (let e of r) console.log(e);
|
|
891
893
|
console.log(" (the shape is correct either way — this is the VALUE a state diff cannot know)");
|
|
892
894
|
}
|
|
893
|
-
},
|
|
895
|
+
}, Nt = async (e, t) => {
|
|
894
896
|
if (W() !== "postgres") return;
|
|
895
|
-
let n =
|
|
897
|
+
let n = ne, r;
|
|
896
898
|
try {
|
|
897
|
-
r = await J(t,
|
|
899
|
+
r = await J(t, D.SqlClient.pipe(E.flatMap((t) => p(t, e, n))));
|
|
898
900
|
} catch {
|
|
899
901
|
return;
|
|
900
902
|
}
|
|
901
|
-
let i =
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
903
|
+
let { drift: i, statements: a } = r;
|
|
904
|
+
if (a.length === 0) return;
|
|
905
|
+
B.info(i.missing.length + i.stale.length > 0 ? `db apply: installing change triggers on ${i.missing.length} table(s)` + (i.stale.length > 0 ? `, removing ${i.stale.length} stale one(s)` : "") + (i.functionOutdated ? ", replacing an outdated notify function" : "") : "db apply: replacing an outdated change-notify function (no table drift)");
|
|
906
|
+
let o = 0;
|
|
907
|
+
for (let e of a) await J(t, D.SqlClient.pipe(E.flatMap((t) => t.unsafe(e)))), o += 1;
|
|
908
|
+
B.info(`db apply: change triggers converged (${o} statement(s))`, {
|
|
909
|
+
installed: i.missing.length,
|
|
910
|
+
removed: i.stale.length,
|
|
911
|
+
functionReplaced: i.functionOutdated
|
|
907
912
|
});
|
|
908
|
-
|
|
909
|
-
B.info(`db apply: installing change triggers on ${r.missing.length} table(s)` + (r.stale.length > 0 ? `, removing ${r.stale.length} stale one(s)` : ""));
|
|
910
|
-
let a = 0;
|
|
911
|
-
for (let e of i) await J(t, x.SqlClient.pipe(g.flatMap((t) => t.unsafe(e)))), a += 1;
|
|
912
|
-
B.info(`db apply: change triggers converged (${a} statement(s))`, {
|
|
913
|
-
installed: r.missing.length,
|
|
914
|
-
removed: r.stale.length
|
|
915
|
-
});
|
|
916
|
-
}, Z = (e) => {
|
|
913
|
+
}, X = (e) => {
|
|
917
914
|
let t = e.summary, n = e.operations.length;
|
|
918
915
|
if (console.log(`\nschema diff: ${n} operation${n === 1 ? "" : "s"}, ${t.blocked} blocked\n`), n === 0) {
|
|
919
916
|
console.log(" (schema is up to date)\n");
|
|
920
917
|
return;
|
|
921
918
|
}
|
|
922
|
-
for (let t of e.operations) console.log(
|
|
923
|
-
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`),
|
|
924
|
-
},
|
|
925
|
-
let t =
|
|
919
|
+
for (let t of e.operations) console.log(jt(t));
|
|
920
|
+
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`), Pt(e);
|
|
921
|
+
}, Pt = (e) => {
|
|
922
|
+
let t = pe(e.operations);
|
|
926
923
|
if (t.length !== 0) {
|
|
927
924
|
console.log(` ⚠ ${t.length} operation(s) UNSAFE under a rolling deploy`), console.log(" (old + new instances overlap → old code breaks against the new schema):");
|
|
928
925
|
for (let e of t) console.log(` • ${e.op.op.kind}: ${e.reason}`), console.log(` → ${e.remedy}`);
|
|
929
926
|
console.log(" Deploy with a maintenance window / scale-to-zero, or split into"), console.log(" expand → deploy → contract. See docs: deployment/production-hardening.\n");
|
|
930
927
|
}
|
|
931
|
-
},
|
|
932
|
-
let n =
|
|
928
|
+
}, Ft = (e, t) => {
|
|
929
|
+
let n = k({
|
|
933
930
|
enabled: process.env.VOLTRO_ROLLING_DEPLOY === "1",
|
|
934
931
|
forced: t.includes("--force"),
|
|
935
|
-
unsafeCount:
|
|
932
|
+
unsafeCount: pe(e.operations).length
|
|
936
933
|
});
|
|
937
934
|
return n.refuse ? (B.error(`db apply: ${n.message}`), !0) : (n.warnForced && B.warn(`db apply: ${n.message}`), !1);
|
|
938
|
-
},
|
|
939
|
-
let t =
|
|
935
|
+
}, It = (e) => {
|
|
936
|
+
let t = o(e, "--online-after") ?? process.env.VOLTRO_ONLINE_THRESHOLD;
|
|
940
937
|
if (t === void 0) return;
|
|
941
938
|
let n = Number(t);
|
|
942
939
|
if (!Number.isInteger(n) || n < 0) throw Error(`--online-after / VOLTRO_ONLINE_THRESHOLD must be a non-negative integer, got "${t}"`);
|
|
943
940
|
return n;
|
|
944
|
-
},
|
|
945
|
-
let t = await
|
|
941
|
+
}, Z = async (e) => {
|
|
942
|
+
let t = await Et(e);
|
|
946
943
|
if (t.length === 0) return;
|
|
947
|
-
let n = await
|
|
948
|
-
return
|
|
944
|
+
let n = await h({ root: e });
|
|
945
|
+
return Dt(Ot([
|
|
949
946
|
...t,
|
|
950
|
-
...
|
|
947
|
+
...P,
|
|
951
948
|
...n
|
|
952
949
|
]));
|
|
953
|
-
},
|
|
954
|
-
let
|
|
950
|
+
}, Q = async (e, t, n) => {
|
|
951
|
+
let r;
|
|
952
|
+
try {
|
|
953
|
+
await J(n, _(await g(e), (e, t) => {
|
|
954
|
+
B.info(`db apply: ${e}`, t ?? {});
|
|
955
|
+
}));
|
|
956
|
+
} catch (e) {
|
|
957
|
+
r = e;
|
|
958
|
+
}
|
|
959
|
+
return await Nt(t, n), r === void 0 ? 0 : (B.error("db apply: a plugin migration FAILED — the schema was applied, the plugin steps were not", { error: r instanceof Error ? r.message : String(r) }), 1);
|
|
960
|
+
}, Lt = async (e, t) => {
|
|
961
|
+
let n = { close: async () => {} }, r = await V(e, y);
|
|
962
|
+
if (r.length === 0) return n;
|
|
963
|
+
let i = await d(r, B), a = i.filter((e) => e.definition.lifecycle === "onSchemaChange");
|
|
964
|
+
if (a.length === 0) return n;
|
|
965
|
+
let { store: o, dispose: s } = await Ct(t), c = m(i, o, B.child("seeds"));
|
|
966
|
+
return B.info(`db apply: ${a.length} onSchemaChange seed(s) armed for this apply`, { ids: a.map((e) => e.definition.id) }), { close: async () => {
|
|
967
|
+
c(), await s().catch((e) => {
|
|
968
|
+
B.warn("db apply: seed store dispose failed", { error: String(e) });
|
|
969
|
+
});
|
|
970
|
+
} };
|
|
971
|
+
}, Rt = async (e) => {
|
|
972
|
+
let t = It(e), n = H(e), r = await Z(n);
|
|
955
973
|
if (r === void 0) return B.warn("db plan: no schema files found", {
|
|
956
974
|
root: n,
|
|
957
975
|
hint: "looked for *.entity.ts / *.schema.ts / schema.ts"
|
|
958
976
|
}), 1;
|
|
959
|
-
let i =
|
|
977
|
+
let i = o(e, "--against");
|
|
960
978
|
if (i) {
|
|
961
|
-
let n = i, a =
|
|
962
|
-
a && (
|
|
979
|
+
let n = i, a = o(e, "--token") ?? process.env.VOLTRO_INSPECT_TOKEN, s = { accept: "application/json" };
|
|
980
|
+
a && (s.authorization = `Bearer ${a}`);
|
|
963
981
|
try {
|
|
964
|
-
let e = n.endsWith("/_voltro/inspect/migrations") ? n : `${n.replace(/\/+$/, "")}/_voltro/inspect/migrations`, i = await fetch(e, { headers:
|
|
982
|
+
let e = n.endsWith("/_voltro/inspect/migrations") ? n : `${n.replace(/\/+$/, "")}/_voltro/inspect/migrations`, i = await fetch(e, { headers: s });
|
|
965
983
|
if (!i.ok) return B.error(`db plan --against: remote returned ${i.status}`, { url: e }), 1;
|
|
966
984
|
let a = (await i.json()).drift?.liveSnapshot;
|
|
967
985
|
if (!a) return B.error("db plan --against: remote inspect payload missing drift.liveSnapshot", { url: e }), 1;
|
|
968
|
-
let
|
|
986
|
+
let o = M({
|
|
969
987
|
declared: r,
|
|
970
988
|
live: a,
|
|
971
989
|
dialect: W(),
|
|
972
|
-
...
|
|
990
|
+
...j(),
|
|
973
991
|
...t === void 0 ? {} : { onlineAfter: t }
|
|
974
992
|
});
|
|
975
|
-
return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`),
|
|
993
|
+
return console.log(`\n[--against ${e}] — diff vs remote live schema:\n`), X(o), o.summary.blocked > 0 ? 2 : 0;
|
|
976
994
|
} catch (e) {
|
|
977
995
|
return B.error(`db plan --against: ${e.message}`), 1;
|
|
978
996
|
}
|
|
979
997
|
}
|
|
980
|
-
let a =
|
|
998
|
+
let a = M({
|
|
981
999
|
declared: r,
|
|
982
1000
|
live: await Y(await q()),
|
|
983
1001
|
dialect: W(),
|
|
984
|
-
...
|
|
1002
|
+
...j(),
|
|
985
1003
|
...t === void 0 ? {} : { onlineAfter: t }
|
|
986
1004
|
});
|
|
987
|
-
return e.includes("--json") ? (
|
|
988
|
-
},
|
|
1005
|
+
return e.includes("--json") ? (ee(1, `${JSON.stringify(a, null, 2)}\n`), a.summary.blocked > 0 ? 2 : 0) : (X(a), a.summary.blocked > 0 ? 2 : 0);
|
|
1006
|
+
}, $ = t, zt = async (e, t, n) => {
|
|
989
1007
|
let r;
|
|
990
1008
|
try {
|
|
991
|
-
let t = await
|
|
1009
|
+
let t = await w.readFile(C(e), "utf8");
|
|
992
1010
|
r = JSON.parse(t);
|
|
993
1011
|
} catch (t) {
|
|
994
1012
|
return B.error(`db apply --plan: cannot read/parse plan file '${e}': ${t.message}`), 1;
|
|
995
1013
|
}
|
|
996
1014
|
if (!Array.isArray(r?.operations) || typeof r?.fromFingerprint != "string" || typeof r?.toFingerprint != "string") return B.error(`db apply --plan: '${e}' is not a valid plan file (expected the output of \`voltro db plan --json\`).`), 1;
|
|
997
|
-
let i = await
|
|
1015
|
+
let i = await Z(n);
|
|
998
1016
|
if (i === void 0) return B.warn("db apply --plan: no schema files found", { root: n }), 1;
|
|
999
|
-
let a = await q();
|
|
1000
|
-
|
|
1001
|
-
let
|
|
1002
|
-
if (o.length > 0) return B.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;
|
|
1003
|
-
let s = E({
|
|
1017
|
+
let a = await q(), s = await J(a, D.SqlClient.pipe(E.flatMap((e) => le(e, n)))).catch(() => []);
|
|
1018
|
+
if (s.length > 0) return B.error(`db apply --plan: refusing — ${s.length} pending file-based migration(s).`), console.error(` ${s.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;
|
|
1019
|
+
let c = M({
|
|
1004
1020
|
declared: i,
|
|
1005
1021
|
live: await Y(a),
|
|
1006
1022
|
dialect: W(),
|
|
1007
|
-
...
|
|
1023
|
+
...j()
|
|
1008
1024
|
});
|
|
1009
|
-
if (
|
|
1010
|
-
if (
|
|
1011
|
-
let
|
|
1012
|
-
if (u.summary.blocked > 0 && !
|
|
1013
|
-
if (
|
|
1014
|
-
if (u.operations.length === 0)
|
|
1015
|
-
|
|
1016
|
-
|
|
1025
|
+
if (c.fromFingerprint !== r.fromFingerprint) return B.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=${c.fromFingerprint.slice(0, 16)}`), console.error(" Regenerate against the current live schema: `voltro db plan --json > plan.json`"), 2;
|
|
1026
|
+
if (c.toFingerprint !== r.toFingerprint) return B.error("db apply --plan: refusing — the declared schema differs from what this plan targets."), console.error(` plan.to=${r.toFingerprint.slice(0, 16)} declared=${c.toFingerprint.slice(0, 16)}`), console.error(" The schema files changed since the plan was generated. Regenerate the plan."), 2;
|
|
1027
|
+
let l = kt(c), u = l.plan;
|
|
1028
|
+
if (u.summary.blocked > 0 && !l.acknowledged) return X(u), B.error(`db apply --plan: refusing — ${c.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (B.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), B.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
|
|
1029
|
+
if (l.acknowledged && B.warn("db apply --plan: proceeding with acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)"), Ft(u, t)) return 2;
|
|
1030
|
+
if (u.operations.length === 0) {
|
|
1031
|
+
let e = await J(a, D.SqlClient.pipe(E.flatMap((e) => ue(e, u, {
|
|
1032
|
+
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:plan",
|
|
1033
|
+
environment: $(),
|
|
1034
|
+
source: "file"
|
|
1035
|
+
}))));
|
|
1036
|
+
return B.info(e === "recorded" ? `db apply --plan: schema is up to date — no DDL to apply, recorded fingerprint=${u.toFingerprint.slice(0, 16)} (change triggers are reconciled separately below)` : "db apply --plan: schema is up to date — no DDL to apply (change triggers are reconciled separately below)"), Q(n, i, a);
|
|
1037
|
+
}
|
|
1038
|
+
X(u);
|
|
1039
|
+
let d = o(t, "--note"), f = await J(a, D.SqlClient.pipe(E.flatMap((e) => re(e, u, {
|
|
1017
1040
|
declared: i,
|
|
1018
1041
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:plan",
|
|
1019
|
-
environment:
|
|
1042
|
+
environment: $(),
|
|
1020
1043
|
source: "file",
|
|
1021
|
-
...
|
|
1022
|
-
replan: () =>
|
|
1044
|
+
...d ? { notes: d } : {},
|
|
1045
|
+
replan: () => E.promise(() => Y(a)).pipe(E.map((e) => M({
|
|
1023
1046
|
declared: i,
|
|
1024
1047
|
live: e,
|
|
1025
1048
|
dialect: W(),
|
|
1026
|
-
...
|
|
1049
|
+
...j()
|
|
1027
1050
|
})))
|
|
1028
1051
|
}))));
|
|
1029
|
-
return B.info(`db apply --plan: applied ${
|
|
1030
|
-
id:
|
|
1031
|
-
fingerprint:
|
|
1032
|
-
}),
|
|
1033
|
-
},
|
|
1034
|
-
let t = H(e), n =
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1052
|
+
return B.info(`db apply --plan: applied ${f.appliedOps} op(s) in ${f.durationMs}ms`, {
|
|
1053
|
+
id: f.id,
|
|
1054
|
+
fingerprint: f.fingerprint.slice(0, 16)
|
|
1055
|
+
}), Q(n, i, a);
|
|
1056
|
+
}, Bt = async (e) => {
|
|
1057
|
+
let t = H(e), n = await Lt(t, e);
|
|
1058
|
+
try {
|
|
1059
|
+
return await Vt(e, t);
|
|
1060
|
+
} finally {
|
|
1061
|
+
await n.close();
|
|
1062
|
+
}
|
|
1063
|
+
}, Vt = async (t, n) => {
|
|
1064
|
+
let r = It(t), i = o(t, "--plan");
|
|
1065
|
+
if (i) return zt(i, t, n);
|
|
1066
|
+
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return B.error(`voltro db apply: refusing — auto-apply on prod is not allowed.${e()}`), 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;
|
|
1067
|
+
let a = await Z(n);
|
|
1068
|
+
if (a === void 0) return B.warn("db apply: no schema files found", { root: n }), 1;
|
|
1069
|
+
let s = await q(), c = await rn(n, s);
|
|
1070
|
+
if ("failed" in c) return B.error("db apply: a file-based migration FAILED — the schema diff was NOT run"), console.error(c.failed), console.error("Fix the migration and re-run. `voltro db files` runs them alone."), 1;
|
|
1071
|
+
let l = M({
|
|
1072
|
+
declared: a,
|
|
1073
|
+
live: await Y(s),
|
|
1045
1074
|
dialect: W(),
|
|
1046
|
-
...
|
|
1047
|
-
...
|
|
1075
|
+
...j(),
|
|
1076
|
+
...r === void 0 ? {} : { onlineAfter: r }
|
|
1048
1077
|
});
|
|
1049
|
-
|
|
1050
|
-
let
|
|
1051
|
-
if (
|
|
1052
|
-
if (
|
|
1053
|
-
if (
|
|
1054
|
-
|
|
1055
|
-
|
|
1078
|
+
X(l), await Mt(l, s);
|
|
1079
|
+
let u = kt(l), d = u.plan;
|
|
1080
|
+
if (d.summary.blocked > 0 && !u.acknowledged) return B.error(`db apply: refusing — ${d.summary.blocked} blocked operation(s). See the fix hints above.`), process.env.VOLTRO_DESTRUCTIVE_OK !== void 0 && (B.error(" VOLTRO_DESTRUCTIVE_OK is set but did not cover this plan — it only relaxes"), B.error(" `lossy` operations, and only the tables it names. Anything else stays refused.")), 2;
|
|
1081
|
+
if (u.acknowledged && B.warn(`db apply: proceeding with ${l.summary.blocked} acknowledged lossy operation(s) (VOLTRO_DESTRUCTIVE_OK)`), Ft(d, t)) return 2;
|
|
1082
|
+
if (d.operations.length === 0) {
|
|
1083
|
+
B.info("db apply: schema is up to date — no DDL to apply (change triggers are reconciled separately below)");
|
|
1084
|
+
let e = await J(s, D.SqlClient.pipe(E.flatMap((e) => ue(e, d, {
|
|
1085
|
+
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:apply",
|
|
1086
|
+
environment: $(),
|
|
1087
|
+
source: "auto-diff"
|
|
1088
|
+
}))));
|
|
1089
|
+
return e === "recorded" && B.info(`db apply: recorded fingerprint=${d.toFingerprint.slice(0, 16)} (0 operations)`), await Qt(s), o(t, "--note") !== void 0 && e !== "recorded" && (B.warn("db apply: --note was ignored — this schema is already the latest recorded one, so there is no new history row to attach it to."), console.error("To record that the CURRENT live schema is the accepted baseline, use `voltro db drift --accept`.")), Q(n, a, s);
|
|
1090
|
+
}
|
|
1091
|
+
let f = o(t, "--note"), p = await J(s, D.SqlClient.pipe(E.flatMap((e) => re(e, d, {
|
|
1092
|
+
declared: a,
|
|
1056
1093
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user",
|
|
1057
|
-
environment: (
|
|
1094
|
+
environment: $(),
|
|
1058
1095
|
source: "auto-diff",
|
|
1059
|
-
...
|
|
1060
|
-
replan: () =>
|
|
1061
|
-
declared:
|
|
1096
|
+
...f ? { notes: f } : {},
|
|
1097
|
+
replan: () => E.promise(() => Y(s)).pipe(E.map((e) => M({
|
|
1098
|
+
declared: a,
|
|
1062
1099
|
live: e,
|
|
1063
1100
|
dialect: W(),
|
|
1064
|
-
...
|
|
1065
|
-
...
|
|
1101
|
+
...j(),
|
|
1102
|
+
...r === void 0 ? {} : { onlineAfter: r }
|
|
1066
1103
|
})))
|
|
1067
1104
|
}))));
|
|
1068
|
-
return B.info(`db apply: applied ${
|
|
1069
|
-
id:
|
|
1070
|
-
fingerprint:
|
|
1071
|
-
}),
|
|
1072
|
-
},
|
|
1073
|
-
let t =
|
|
1105
|
+
return B.info(`db apply: applied ${p.appliedOps} op(s) in ${p.durationMs}ms`, {
|
|
1106
|
+
id: p.id,
|
|
1107
|
+
fingerprint: p.fingerprint.slice(0, 16)
|
|
1108
|
+
}), Q(n, a, s);
|
|
1109
|
+
}, Ht = (e) => {
|
|
1110
|
+
let t = [];
|
|
1111
|
+
for (let n of e.tables) for (let e of n.columns) {
|
|
1112
|
+
let r = e.references;
|
|
1113
|
+
r !== void 0 && t.push({
|
|
1114
|
+
table: n.name,
|
|
1115
|
+
column: e.name,
|
|
1116
|
+
targetTable: r.table,
|
|
1117
|
+
targetColumn: r.column,
|
|
1118
|
+
...r.onDelete === void 0 ? {} : { onDelete: r.onDelete }
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
return t;
|
|
1122
|
+
}, Ut = (e) => {
|
|
1123
|
+
let t = [];
|
|
1124
|
+
for (let n of e.tables) for (let e of n.indexes) e.expression !== !0 && e.name !== `${n.name}_pkey` && e.columns.length !== 0 && t.push({
|
|
1125
|
+
table: n.name,
|
|
1126
|
+
columns: e.columns,
|
|
1127
|
+
unique: e.unique,
|
|
1128
|
+
name: e.name
|
|
1129
|
+
});
|
|
1130
|
+
return t;
|
|
1131
|
+
}, Wt = (e, t) => {
|
|
1132
|
+
let n = (e) => JSON.stringify(e.op), r = new Set(e.operations.map(n)), i = new Set(t.operations.map(n)), a = [...t.operations.filter((e) => !r.has(n(e))), ...e.operations.filter((e) => !i.has(n(e)))];
|
|
1133
|
+
return {
|
|
1134
|
+
...t,
|
|
1135
|
+
operations: a,
|
|
1136
|
+
summary: {
|
|
1137
|
+
...t.summary,
|
|
1138
|
+
blocked: 0
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
}, Gt = (e) => {
|
|
1142
|
+
if (e === void 0) return "empty";
|
|
1143
|
+
if (e === "empty" || e === "copy") return e;
|
|
1144
|
+
throw e === "fresh" ? new n("voltro db branch: --seed fresh is not implemented — the branch lifecycle has no executor step that runs boot seeds, so the branch would come up EMPTY while this reported it as seeded. Use --seed copy (the parent's rows) or --seed empty (schema only).") : new n(`voltro db branch: --seed must be one of empty|copy, got "${e}"`);
|
|
1145
|
+
}, Kt = (e) => {
|
|
1146
|
+
if (e !== void 0) {
|
|
1147
|
+
if (e === "namespace" || e === "neon-cow") return e;
|
|
1148
|
+
throw new n(`voltro db branch: --prefer must be one of namespace|neon-cow, got "${e}"`);
|
|
1149
|
+
}
|
|
1150
|
+
}, qt = async (e) => {
|
|
1151
|
+
let t = H(e), r = Gt(o(e, "--seed")), i = Kt(o(e, "--prefer")), a = e.includes("--keep"), s = e.includes("--json"), l = It(e), u = W();
|
|
1152
|
+
if (u !== "postgres") return B.error(`voltro db branch: not supported on '${u}'.`), console.error(" The branch PLAN is dialect-agnostic; the shipped namespace EXECUTOR is Postgres-only"), console.error(" (CREATE SCHEMA / CREATE TABLE … LIKE … INCLUDING ALL / \"-quoted identifiers)."), console.error(" MySQL, MariaDB, SQLite and SQL Server need their own BranchExecutor — see"), console.error(" makeNamespaceBranchExecutor in @voltro/database."), 1;
|
|
1153
|
+
let d = await Z(t);
|
|
1154
|
+
if (d === void 0) return B.warn("db branch: no schema files found", {
|
|
1155
|
+
root: t,
|
|
1156
|
+
hint: "looked for *.entity.ts / *.schema.ts / schema.ts"
|
|
1157
|
+
}), 1;
|
|
1158
|
+
let f = U(), p = we({
|
|
1159
|
+
seed: r,
|
|
1160
|
+
...f.url === void 0 ? {} : { dbUrl: f.url },
|
|
1161
|
+
...i === void 0 ? {} : { prefer: i }
|
|
1162
|
+
});
|
|
1163
|
+
if (p === "neon-cow") return B.error("voltro db branch: this connection resolves to the Neon copy-on-write mechanism, which the CLI cannot execute."), console.error(" A Neon CoW branch is a single call to Neon's branch REST API and needs a Neon API token;"), console.error(" the framework ships the PLAN, and the cloud control plane executes it."), console.error(" Re-run with `--prefer namespace` to rehearse on a schema-per-branch copy instead"), console.error(` (portable, no vendor API — it copies ${r === "copy" ? "every row" : "the schema only"}).`), 1;
|
|
1164
|
+
let m = f.schema ?? "public", h = o(e, "--name") ?? b(t), g = o(e, "--pr"), _ = g === void 0 ? 0 : Number(g);
|
|
1165
|
+
if (!Number.isInteger(_) || _ < 0) throw new n(`voltro db branch: --pr must be a non-negative integer, got "${String(g)}"`);
|
|
1166
|
+
let v = ve(h, _), y = await c(u), x = y.makeSqlLayer(f), S = y.makeSqlLayer({
|
|
1167
|
+
...f,
|
|
1168
|
+
schema: v
|
|
1169
|
+
}), C = await Y(x), w = M({
|
|
1170
|
+
declared: d,
|
|
1171
|
+
live: C,
|
|
1172
|
+
dialect: u,
|
|
1173
|
+
...j(),
|
|
1174
|
+
...l === void 0 ? {} : { onlineAfter: l }
|
|
1175
|
+
}), T = C.tables.map((e) => e.name), te = Ht(C), ne = Ut(C), O = be({ run: (e) => J(x, D.SqlClient.pipe(E.flatMap((t) => t.unsafe(e)), E.asVoid)) }), k = async () => M({
|
|
1176
|
+
declared: d,
|
|
1177
|
+
live: await Y(S),
|
|
1178
|
+
dialect: u,
|
|
1179
|
+
...j(),
|
|
1180
|
+
...l === void 0 ? {} : { onlineAfter: l }
|
|
1181
|
+
}), A = await de({
|
|
1182
|
+
branchId: v,
|
|
1183
|
+
mechanism: p,
|
|
1184
|
+
branchedTables: T.length,
|
|
1185
|
+
replayedForeignKeys: te.length,
|
|
1186
|
+
keep: a,
|
|
1187
|
+
log: (e) => {
|
|
1188
|
+
s || B.info(`db branch: ${e}`);
|
|
1189
|
+
},
|
|
1190
|
+
provision: async () => {
|
|
1191
|
+
await xe({
|
|
1192
|
+
appSlug: h,
|
|
1193
|
+
prNumber: _,
|
|
1194
|
+
seed: r,
|
|
1195
|
+
tableNames: T,
|
|
1196
|
+
parentNamespace: m,
|
|
1197
|
+
foreignKeys: te,
|
|
1198
|
+
indexNames: ne,
|
|
1199
|
+
prefer: "namespace",
|
|
1200
|
+
...f.url === void 0 ? {} : { dbUrl: f.url }
|
|
1201
|
+
}, O);
|
|
1202
|
+
},
|
|
1203
|
+
fidelityPlan: async () => Wt(w, await k()),
|
|
1204
|
+
plan: k,
|
|
1205
|
+
apply: async (e) => {
|
|
1206
|
+
await J(S, D.SqlClient.pipe(E.flatMap((t) => re(t, e, {
|
|
1207
|
+
declared: d,
|
|
1208
|
+
appliedBy: "cli:db-branch",
|
|
1209
|
+
environment: $(),
|
|
1210
|
+
source: "auto-diff",
|
|
1211
|
+
notes: `migration rehearsal on branch ${v}`,
|
|
1212
|
+
replan: () => E.promise(k)
|
|
1213
|
+
}))));
|
|
1214
|
+
},
|
|
1215
|
+
replan: async () => await k(),
|
|
1216
|
+
teardown: async () => {
|
|
1217
|
+
await Te(v, "namespace", O);
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
return s ? ee(1, `${JSON.stringify(A, null, 2)}\n`) : console.log(`\n${se(A)}\n`), ie(A);
|
|
1221
|
+
}, Jt = async (e) => {
|
|
1222
|
+
let t = Math.max(1, Number(o(e, "--limit") ?? "20")), n = await J(await q(), D.SqlClient.pipe(E.flatMap((e) => e`
|
|
1074
1223
|
SELECT ${e("id")}, ${e("fingerprint")}, ${e("operations")},
|
|
1075
1224
|
${e("appliedBy")}, ${e("environment")}, ${e("source")},
|
|
1076
1225
|
${e("durationMs")}, ${e("notes")}, ${e("appliedAt")}
|
|
@@ -1083,20 +1232,20 @@ var O = class extends Error {
|
|
|
1083
1232
|
for (let e of n) {
|
|
1084
1233
|
let t = 0;
|
|
1085
1234
|
try {
|
|
1086
|
-
t = JSON.parse(
|
|
1235
|
+
t = JSON.parse($t(e.operations)).length;
|
|
1087
1236
|
} catch {}
|
|
1088
|
-
console.log(` ${e.id} fp=${
|
|
1237
|
+
console.log(` ${e.id} fp=${N(e.fingerprint)} env=${e.environment} src=${e.source} ${t} op(s) ${e.durationMs}ms ${en(e.appliedAt)} by=${e.appliedBy}`), e.notes && console.log(` [2mnote: ${e.notes}[0m`);
|
|
1089
1238
|
}
|
|
1090
1239
|
return console.log(), 0;
|
|
1091
|
-
},
|
|
1240
|
+
}, Yt = async (e) => {
|
|
1092
1241
|
let t = e[0];
|
|
1093
1242
|
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;
|
|
1094
|
-
let n = await q(), r = await J(n,
|
|
1243
|
+
let n = await q(), r = await J(n, D.SqlClient.pipe(E.flatMap((e) => e`SELECT ${e("id")}, ${e("operations")}
|
|
1095
1244
|
FROM ${e("_voltro_migration_plans")} WHERE ${e("id")} = ${t}`)));
|
|
1096
1245
|
if (r.length === 0) return console.error(`restore-snapshot: plan '${t}' not found`), 1;
|
|
1097
1246
|
let i;
|
|
1098
1247
|
try {
|
|
1099
|
-
i = JSON.parse(
|
|
1248
|
+
i = JSON.parse($t(r[0].operations));
|
|
1100
1249
|
} catch {
|
|
1101
1250
|
return console.error(`restore-snapshot: plan '${t}' has corrupted operations JSON`), 1;
|
|
1102
1251
|
}
|
|
@@ -1110,7 +1259,7 @@ var O = class extends Error {
|
|
|
1110
1259
|
if (a.length === 0 && o.length === 0) return console.log(`restore-snapshot: plan '${t}' has no drop-column / drop-table ops to restore`), 0;
|
|
1111
1260
|
let s = 0;
|
|
1112
1261
|
for (let e of a) {
|
|
1113
|
-
let t = await J(n,
|
|
1262
|
+
let t = await J(n, D.SqlClient.pipe(E.flatMap((t) => t`
|
|
1114
1263
|
SELECT column_name FROM information_schema.columns
|
|
1115
1264
|
WHERE ${t.unsafe(K(W()))}
|
|
1116
1265
|
AND table_name = ${e.table}
|
|
@@ -1122,11 +1271,11 @@ var O = class extends Error {
|
|
|
1122
1271
|
continue;
|
|
1123
1272
|
}
|
|
1124
1273
|
let r = t[0].column_name;
|
|
1125
|
-
await J(n,
|
|
1274
|
+
await J(n, D.SqlClient.pipe(E.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})`);
|
|
1126
1275
|
}
|
|
1127
1276
|
let c = 0;
|
|
1128
1277
|
for (let e of o) {
|
|
1129
|
-
let t = await J(n,
|
|
1278
|
+
let t = await J(n, D.SqlClient.pipe(E.flatMap((t) => t`
|
|
1130
1279
|
SELECT table_name FROM information_schema.tables
|
|
1131
1280
|
WHERE ${t.unsafe(K(W()))}
|
|
1132
1281
|
AND table_name LIKE ${e.table + "__dropped_%"}
|
|
@@ -1137,57 +1286,57 @@ var O = class extends Error {
|
|
|
1137
1286
|
continue;
|
|
1138
1287
|
}
|
|
1139
1288
|
let r = t[0].table_name;
|
|
1140
|
-
await J(n,
|
|
1289
|
+
await J(n, D.SqlClient.pipe(E.flatMap((t) => t.unsafe(`ALTER TABLE ${G(r)} RENAME TO ${G(e.table)}`)))), c++, console.log(` ↩ restored table ${e.table} (from ${r})`);
|
|
1141
1290
|
}
|
|
1142
1291
|
return console.log(`\n✓ restored ${s} column(s) + ${c} table(s) from plan '${t}'\n`), 0;
|
|
1143
|
-
},
|
|
1144
|
-
let t =
|
|
1292
|
+
}, Xt = async (e) => {
|
|
1293
|
+
let t = o(e, "--before");
|
|
1145
1294
|
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;
|
|
1146
1295
|
let n = t.replace(/[^0-9]/g, "");
|
|
1147
1296
|
if (n.length < 8) return console.error(`gc-snapshots: --before must be at least a full date (YYYY-MM-DD), got "${t}"`), 2;
|
|
1148
|
-
let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await q(),
|
|
1297
|
+
let r = (n + "00000000000000").slice(0, 14), i = e.includes("--dry-run"), a = await q(), s = await J(a, D.SqlClient.pipe(E.flatMap((e) => e`
|
|
1149
1298
|
SELECT table_name, column_name FROM information_schema.columns
|
|
1150
1299
|
WHERE ${e.unsafe(K(W()))} AND column_name LIKE ${"%__dropped_%"}
|
|
1151
|
-
`))),
|
|
1300
|
+
`))), c = await J(a, D.SqlClient.pipe(E.flatMap((e) => e`
|
|
1152
1301
|
SELECT table_name FROM information_schema.tables
|
|
1153
1302
|
WHERE ${e.unsafe(K(W()))} AND table_name LIKE ${"%__dropped_%"}
|
|
1154
|
-
`))),
|
|
1303
|
+
`))), l = (e) => {
|
|
1155
1304
|
let t = /__dropped_(\d{14})$/.exec(e);
|
|
1156
1305
|
return t ? t[1] : null;
|
|
1157
1306
|
}, u = 0;
|
|
1158
|
-
for (let e of o) {
|
|
1159
|
-
let t = c(e.column_name);
|
|
1160
|
-
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await J(a, x.SqlClient.pipe(g.flatMap((t) => t.unsafe(`ALTER TABLE ${G(e.table_name)} DROP COLUMN ${G(e.column_name)}`)))), u++);
|
|
1161
|
-
}
|
|
1162
1307
|
for (let e of s) {
|
|
1163
|
-
let t =
|
|
1164
|
-
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop
|
|
1308
|
+
let t = l(e.column_name);
|
|
1309
|
+
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop column" : "dropping column"} ${e.table_name}.${e.column_name}`), i || await J(a, D.SqlClient.pipe(E.flatMap((t) => t.unsafe(`ALTER TABLE ${G(e.table_name)} DROP COLUMN ${G(e.column_name)}`)))), u++);
|
|
1310
|
+
}
|
|
1311
|
+
for (let e of c) {
|
|
1312
|
+
let t = l(e.table_name);
|
|
1313
|
+
t === null || t >= r || (console.log(` ${i ? "[dry-run] would drop table" : "dropping table"} ${e.table_name}`), i || await J(a, D.SqlClient.pipe(E.flatMap((t) => t.unsafe(`DROP TABLE ${G(e.table_name)}`)))), u++);
|
|
1165
1314
|
}
|
|
1166
1315
|
return console.log(`\n${i ? `[dry-run] ${u} snapshot(s) would be dropped` : `✓ dropped ${u} snapshot(s)`} (older than ${r}).\n`), 0;
|
|
1167
|
-
},
|
|
1168
|
-
let t =
|
|
1316
|
+
}, Zt = async (e) => {
|
|
1317
|
+
let t = o(e, "--before");
|
|
1169
1318
|
if (!t) return console.error("voltro db squash --before <iso-date> [--note \"<text>\"]"), 2;
|
|
1170
1319
|
let n = t;
|
|
1171
1320
|
if (Number.isNaN(new Date(n).getTime())) return console.error(`voltro db squash: --before '${n}' is not a valid ISO date`), 2;
|
|
1172
|
-
let r =
|
|
1321
|
+
let r = o(e, "--note"), i = H(e), a = await Z(i);
|
|
1173
1322
|
if (a === void 0) return B.warn("db squash: no schema files found", { root: i }), 1;
|
|
1174
|
-
let
|
|
1323
|
+
let s = oe(A(a)), c = await q();
|
|
1175
1324
|
try {
|
|
1176
|
-
let e = await J(
|
|
1325
|
+
let e = await J(c, he({
|
|
1177
1326
|
before: n,
|
|
1178
|
-
fingerprint:
|
|
1327
|
+
fingerprint: s,
|
|
1179
1328
|
appliedBy: process.env.USER ?? "cli",
|
|
1180
|
-
environment:
|
|
1329
|
+
environment: $(),
|
|
1181
1330
|
...r === void 0 ? {} : { note: r }
|
|
1182
1331
|
}));
|
|
1183
|
-
return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${
|
|
1332
|
+
return console.log(`\n✓ squashed ${e.squashedCount} migration plan(s)\n snapshot id: ${e.snapshotId}\n fingerprint: ${N(e.snapshotFingerprint)}\n`), 0;
|
|
1184
1333
|
} catch (e) {
|
|
1185
1334
|
return console.error(`squash refused: ${e.message}`), 1;
|
|
1186
1335
|
}
|
|
1187
|
-
},
|
|
1336
|
+
}, Qt = async (e) => {
|
|
1188
1337
|
try {
|
|
1189
|
-
let t =
|
|
1190
|
-
await J(e,
|
|
1338
|
+
let t = oe(await Y(e));
|
|
1339
|
+
await J(e, D.SqlClient.pipe(E.flatMap((e) => e`
|
|
1191
1340
|
UPDATE ${e("_voltro_migration_plans")}
|
|
1192
1341
|
SET ${e("liveFingerprint")} = ${t}
|
|
1193
1342
|
WHERE ${e("id")} = (
|
|
@@ -1199,7 +1348,7 @@ var O = class extends Error {
|
|
|
1199
1348
|
AND ${e("liveFingerprint")} IS NULL
|
|
1200
1349
|
`)));
|
|
1201
1350
|
} catch {}
|
|
1202
|
-
},
|
|
1351
|
+
}, $t = (e) => typeof e == "string" ? e : e == null ? "" : JSON.stringify(e), en = (e) => e instanceof Date ? e.toISOString() : String(e ?? ""), tn = (e) => e === void 0 ? {
|
|
1203
1352
|
accept: !1,
|
|
1204
1353
|
reason: "no-declared-schema"
|
|
1205
1354
|
} : e > 0 ? {
|
|
@@ -1208,38 +1357,38 @@ var O = class extends Error {
|
|
|
1208
1357
|
} : {
|
|
1209
1358
|
accept: !0,
|
|
1210
1359
|
reason: "converged"
|
|
1211
|
-
},
|
|
1212
|
-
let t = e.includes("--accept"), n = await q(), r = await J(n,
|
|
1360
|
+
}, nn = async (e) => {
|
|
1361
|
+
let t = e.includes("--accept"), n = await q(), r = await J(n, D.SqlClient.pipe(E.flatMap((e) => e`
|
|
1213
1362
|
SELECT * FROM ${e("_voltro_migration_plans")}
|
|
1214
1363
|
ORDER BY ${e("appliedAt")} DESC
|
|
1215
1364
|
LIMIT 1
|
|
1216
|
-
`))), i = await Y(n), a =
|
|
1217
|
-
if (r.length === 0) return B.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${
|
|
1365
|
+
`))), i = await Y(n), a = oe(i);
|
|
1366
|
+
if (r.length === 0) return B.info("db drift: no _voltro_migration_plans rows yet"), console.log(`live user-schema fingerprint: ${N(a)}`), console.log("No baseline to compare against. Run `voltro db apply` to lock in the current state."), 0;
|
|
1218
1367
|
let o = r[0];
|
|
1219
|
-
if (o.liveFingerprint === null || o.liveFingerprint === void 0) return B.warn("db drift: no drift baseline recorded yet — cannot compare"), console.log(), console.log(` last applied: ${o.id} at ${
|
|
1220
|
-
if (o.liveFingerprint === a) return B.info(`db drift: live matches last applied (${
|
|
1368
|
+
if (o.liveFingerprint === null || o.liveFingerprint === void 0) return B.warn("db drift: no drift baseline recorded yet — cannot compare"), console.log(), console.log(` last applied: ${o.id} at ${en(o.appliedAt)}`), console.log(` live now: ${N(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;
|
|
1369
|
+
if (o.liveFingerprint === a) return B.info(`db drift: live matches last applied (${N(a)})`), 0;
|
|
1221
1370
|
let s = await (async () => {
|
|
1222
1371
|
try {
|
|
1223
|
-
let t = await
|
|
1224
|
-
return t === void 0 ? void 0 :
|
|
1372
|
+
let t = await Z(H(e));
|
|
1373
|
+
return t === void 0 ? void 0 : M({
|
|
1225
1374
|
declared: t,
|
|
1226
1375
|
live: i,
|
|
1227
1376
|
dialect: W(),
|
|
1228
|
-
...
|
|
1377
|
+
...j()
|
|
1229
1378
|
}).operations;
|
|
1230
1379
|
} catch {
|
|
1231
1380
|
return;
|
|
1232
1381
|
}
|
|
1233
1382
|
})();
|
|
1234
1383
|
if (t) {
|
|
1235
|
-
let e =
|
|
1384
|
+
let e = tn(s?.length);
|
|
1236
1385
|
if (e.reason === "no-declared-schema") return B.error("db drift --accept: no schema files found — cannot verify the live schema is current."), 1;
|
|
1237
1386
|
if (!e.accept && s !== void 0) {
|
|
1238
1387
|
B.error(`db drift --accept: refusing — ${s.length} operation(s) still pending against your declared schema.`), console.log();
|
|
1239
1388
|
for (let e of s.slice(0, 10)) console.log(` - ${e.op.kind} ${"table" in e.op ? `[${e.op.table}]` : ""}`);
|
|
1240
1389
|
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;
|
|
1241
1390
|
}
|
|
1242
|
-
return await J(n,
|
|
1391
|
+
return await J(n, D.SqlClient.pipe(E.flatMap((e) => e`
|
|
1243
1392
|
UPDATE ${e("_voltro_migration_plans")}
|
|
1244
1393
|
SET ${e("liveFingerprint")} = ${a}
|
|
1245
1394
|
WHERE ${e("id")} = (
|
|
@@ -1248,13 +1397,13 @@ var O = class extends Error {
|
|
|
1248
1397
|
ORDER BY ${e("appliedAt")} DESC LIMIT 1
|
|
1249
1398
|
) AS ${e("newest")}
|
|
1250
1399
|
)
|
|
1251
|
-
`))), B.info(`db drift: accepted the live schema as the baseline (${
|
|
1400
|
+
`))), B.info(`db drift: accepted the live schema as the baseline (${N(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;
|
|
1252
1401
|
}
|
|
1253
|
-
return B.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${
|
|
1254
|
-
},
|
|
1255
|
-
let n = (
|
|
1402
|
+
return B.error("db drift: live schema DIVERGED from last applied state"), console.log(), console.log(` baseline: ${N(o.liveFingerprint)} at ${en(o.appliedAt)} (${o.id})`), console.log(` live now: ${N(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);
|
|
1403
|
+
}, rn = async (e, t) => {
|
|
1404
|
+
let n = $();
|
|
1256
1405
|
try {
|
|
1257
|
-
let r = await J(t,
|
|
1406
|
+
let r = await J(t, D.SqlClient.pipe(E.flatMap((t) => me(t, {
|
|
1258
1407
|
projectRoot: e,
|
|
1259
1408
|
env: n,
|
|
1260
1409
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
|
|
@@ -1263,10 +1412,10 @@ var O = class extends Error {
|
|
|
1263
1412
|
} catch (e) {
|
|
1264
1413
|
return { failed: e };
|
|
1265
1414
|
}
|
|
1266
|
-
},
|
|
1267
|
-
let t = H(e), n = await q(), r = (
|
|
1268
|
-
await J(n,
|
|
1269
|
-
let i = await J(n,
|
|
1415
|
+
}, an = async (e) => {
|
|
1416
|
+
let t = H(e), n = await q(), r = $();
|
|
1417
|
+
await J(n, O(P, W()));
|
|
1418
|
+
let i = await J(n, D.SqlClient.pipe(E.flatMap((e) => me(e, {
|
|
1270
1419
|
projectRoot: t,
|
|
1271
1420
|
env: r,
|
|
1272
1421
|
appliedBy: process.env.USER ? `cli:${process.env.USER}` : "cli:user"
|
|
@@ -1275,16 +1424,16 @@ var O = class extends Error {
|
|
|
1275
1424
|
applied: i.applied.map((e) => `${e.id} (${e.durationMs}ms)`),
|
|
1276
1425
|
skipped: i.skipped.length
|
|
1277
1426
|
}), 0);
|
|
1278
|
-
},
|
|
1279
|
-
let
|
|
1280
|
-
if (!
|
|
1281
|
-
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return B.error("voltro db rollback-file: refusing on prod — schema rollback runs as an explicit deploy step."), 3;
|
|
1282
|
-
let
|
|
1283
|
-
projectRoot:
|
|
1284
|
-
id:
|
|
1427
|
+
}, on = async (t) => {
|
|
1428
|
+
let n = H(t), r = s(t, _t);
|
|
1429
|
+
if (!r) return B.error("voltro db rollback-file <migration-id>"), 2;
|
|
1430
|
+
if ((process.env.NODE_ENV ?? "").toLowerCase() === "production") return B.error("voltro db rollback-file: refusing on prod — schema rollback runs as an explicit deploy step." + e()), 3;
|
|
1431
|
+
let i = await J(await q(), D.SqlClient.pipe(E.flatMap((e) => fe(e, {
|
|
1432
|
+
projectRoot: n,
|
|
1433
|
+
id: r
|
|
1285
1434
|
}))));
|
|
1286
|
-
return B.info(`db rollback-file: rolled back ${
|
|
1287
|
-
},
|
|
1435
|
+
return B.info(`db rollback-file: rolled back ${i.id} in ${i.durationMs}ms`), 0;
|
|
1436
|
+
}, sn = () => {
|
|
1288
1437
|
let e = /* @__PURE__ */ new Date();
|
|
1289
1438
|
return [
|
|
1290
1439
|
e.getUTCFullYear(),
|
|
@@ -1294,7 +1443,7 @@ var O = class extends Error {
|
|
|
1294
1443
|
String(e.getUTCMinutes()).padStart(2, "0"),
|
|
1295
1444
|
String(e.getUTCSeconds()).padStart(2, "0")
|
|
1296
1445
|
].join("");
|
|
1297
|
-
},
|
|
1446
|
+
}, cn = (e) => e.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "migration", ln = (e, t) => `import { defineMigration, SqlClient } from '@voltro/database'
|
|
1298
1447
|
import { Effect } from 'effect'
|
|
1299
1448
|
|
|
1300
1449
|
// A hand-written migration. Reach for this when the DECLARATIVE differ can't
|
|
@@ -1326,99 +1475,100 @@ export default defineMigration({
|
|
|
1326
1475
|
}),
|
|
1327
1476
|
],
|
|
1328
1477
|
})
|
|
1329
|
-
`,
|
|
1330
|
-
plan:
|
|
1331
|
-
apply:
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1478
|
+
`, un = {
|
|
1479
|
+
plan: Rt,
|
|
1480
|
+
apply: Bt,
|
|
1481
|
+
branch: qt,
|
|
1482
|
+
plans: Jt,
|
|
1483
|
+
drift: nn,
|
|
1484
|
+
files: an,
|
|
1485
|
+
"rollback-file": on,
|
|
1486
|
+
migrate: bt,
|
|
1487
|
+
rollback: xt,
|
|
1488
|
+
status: St,
|
|
1489
|
+
seed: wt,
|
|
1340
1490
|
generate: async (e) => {
|
|
1341
|
-
let { value: t, rest: n } =
|
|
1342
|
-
return await
|
|
1491
|
+
let { value: t, rest: n } = i(e, "--root"), { value: r, rest: a } = i(n, "--name"), o = t ? C(t) : process.cwd(), c = cn(r ?? s(a, []) ?? "migration"), l = `${sn()}_${c}`, u = x(o, "migrations"), d = x(u, `${l}.migration.ts`);
|
|
1492
|
+
return await w.mkdir(u, { recursive: !0 }), await w.writeFile(d, ln(l, c), "utf8"), B.info(`created ${S(o, 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;
|
|
1343
1493
|
},
|
|
1344
|
-
squash:
|
|
1345
|
-
"restore-snapshot":
|
|
1346
|
-
"gc-snapshots":
|
|
1494
|
+
squash: Zt,
|
|
1495
|
+
"restore-snapshot": Yt,
|
|
1496
|
+
"gc-snapshots": Xt,
|
|
1347
1497
|
adopt: async (e) => {
|
|
1348
1498
|
let t = (t) => {
|
|
1349
1499
|
let n = e.indexOf(`--${t}`);
|
|
1350
1500
|
return n === -1 ? void 0 : e[n + 1];
|
|
1351
|
-
}, n = t("from"),
|
|
1352
|
-
if (n === void 0 ||
|
|
1353
|
-
let o = (await
|
|
1354
|
-
if (o?.map === void 0) return console.error(`voltro db adopt: ${
|
|
1501
|
+
}, n = t("from"), i = t("into"), a = t("map");
|
|
1502
|
+
if (n === void 0 || i === void 0 || a === void 0) return console.error("voltro db adopt --from <table> --into <table> --map <file.ts> [--apply] [--keep-source]"), console.error(" Dry run unless --apply. The map file default-exports { map, leaveUnset? }."), 1;
|
|
1503
|
+
let o = (await r(C(process.cwd(), a))).default;
|
|
1504
|
+
if (o?.map === void 0) return console.error(`voltro db adopt: ${a} must default-export { map: { <targetColumn>: <source> } }`), 1;
|
|
1355
1505
|
let s = {
|
|
1356
1506
|
from: n,
|
|
1357
|
-
into:
|
|
1507
|
+
into: i,
|
|
1358
1508
|
map: o.map,
|
|
1359
1509
|
...o.leaveUnset ? { leaveUnset: o.leaveUnset } : {}
|
|
1360
|
-
}, c = W(), l = await q(), u = await J(l,
|
|
1510
|
+
}, c = W(), l = await q(), u = await J(l, D.SqlClient.pipe(E.flatMap(ce))), d = (e) => u.tables.find((t) => t.name === e)?.columns.map((e) => ({
|
|
1361
1511
|
name: e.name,
|
|
1362
1512
|
nullable: e.nullable,
|
|
1363
1513
|
hasDefault: e.defaultValue !== void 0 && e.defaultValue !== null
|
|
1364
|
-
})), f = d(n), p = d(
|
|
1514
|
+
})), f = d(n), p = d(i);
|
|
1365
1515
|
if (f === void 0) return console.error(`voltro db adopt: '${n}' does not exist in the database`), 1;
|
|
1366
|
-
if (p === void 0) return console.error(`voltro db adopt: '${
|
|
1367
|
-
let
|
|
1368
|
-
for (let e of
|
|
1369
|
-
if (
|
|
1516
|
+
if (p === void 0) return console.error(`voltro db adopt: '${i}' does not exist in the database — boot once so the plugin's table is created`), 1;
|
|
1517
|
+
let m = async (e) => await J(l, D.SqlClient.pipe(E.flatMap((t) => t.unsafe(e)))), h = async (e) => Number(Object.values((await m(`SELECT COUNT(*) AS n FROM "${e}"`))[0] ?? { n: 0 })[0] ?? 0), g = Ae(s, f, p, await h(i));
|
|
1518
|
+
for (let e of Me(s, g, await h(n))) console.log(e);
|
|
1519
|
+
if (g.fatal) return 1;
|
|
1370
1520
|
if (!e.includes("--apply")) return console.log(""), console.log(" (dry run — nothing written. Re-run with --apply.)"), 0;
|
|
1371
|
-
let
|
|
1372
|
-
return console.log(""), console.log(` copied ${
|
|
1521
|
+
let _ = await Le(s, g, c, "id", void 0, m, { keepSource: e.includes("--keep-source") });
|
|
1522
|
+
return console.log(""), console.log(` copied ${_.copied}/${_.sourceRows} row(s)`), console.log(` snapshot: ${_.snapshotTable} — restore from here, then drop it yourself`), _.verified ? (console.log(_.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);
|
|
1373
1523
|
},
|
|
1374
1524
|
"scan-credentials": async (e) => {
|
|
1375
|
-
let t = W(),
|
|
1525
|
+
let t = W(), r = [];
|
|
1376
1526
|
for (let t = 0; t < e.length; t += 1) {
|
|
1377
1527
|
if (e[t] !== "--table") continue;
|
|
1378
|
-
let
|
|
1379
|
-
if (
|
|
1380
|
-
let [
|
|
1381
|
-
|
|
1382
|
-
table:
|
|
1383
|
-
column:
|
|
1528
|
+
let i = e[t + 1];
|
|
1529
|
+
if (i === void 0) throw new n("voltro db scan-credentials: --table needs a value");
|
|
1530
|
+
let [a, o = "subject"] = i.split(":");
|
|
1531
|
+
r.push({
|
|
1532
|
+
table: a,
|
|
1533
|
+
column: o
|
|
1384
1534
|
});
|
|
1385
1535
|
}
|
|
1386
|
-
let
|
|
1387
|
-
return console.log(
|
|
1536
|
+
let i = [...Re, ...r], a = await J(await q(), D.SqlClient.pipe(E.flatMap((e) => E.promise(() => Xe(e, t, i, G)))));
|
|
1537
|
+
return console.log(Ze(a, t)), Qe(a);
|
|
1388
1538
|
},
|
|
1389
1539
|
"encrypt-column": async (e) => {
|
|
1390
|
-
let t = W(),
|
|
1540
|
+
let t = W(), r = e.includes("--dry-run"), i = e.includes("--yes"), o = (t) => {
|
|
1391
1541
|
let n = e.indexOf(t);
|
|
1392
1542
|
return n >= 0 ? e[n + 1] : void 0;
|
|
1393
|
-
},
|
|
1394
|
-
if (!
|
|
1395
|
-
let
|
|
1396
|
-
if (
|
|
1397
|
-
let
|
|
1398
|
-
for (let e of
|
|
1399
|
-
let
|
|
1400
|
-
let
|
|
1401
|
-
if (
|
|
1402
|
-
let
|
|
1403
|
-
return
|
|
1404
|
-
})))),
|
|
1405
|
-
for (let e of
|
|
1406
|
-
let
|
|
1407
|
-
if (!
|
|
1408
|
-
if (
|
|
1409
|
-
let
|
|
1410
|
-
if (!
|
|
1543
|
+
}, s = Math.max(1, Number(o("--batch") ?? 500)), c = o("--key-env") ?? "FIELD_ENCRYPTION_KEY", l = process.env[c];
|
|
1544
|
+
if (!l) throw new n(`voltro db encrypt-column: ${c} is not set. It must be the SAME key your app runs with (\`governancePlugin({ fieldEncryption: { key } })\`) — encrypting with a different one produces rows the app cannot read. Use --key-env to name a different variable.`);
|
|
1545
|
+
let u = De(l), d = a(e, _t).filter((e) => e.includes(".")).map(ot);
|
|
1546
|
+
if (d.length === 0) throw new n("voltro db encrypt-column: name at least one <table>.<column>");
|
|
1547
|
+
let f = await q(), p = [];
|
|
1548
|
+
for (let e of d) {
|
|
1549
|
+
let a = G(e.table), o = G(e.column), l = await J(f, D.SqlClient.pipe(E.flatMap((r) => E.promise(async () => {
|
|
1550
|
+
let i = await E.runPromise(r.unsafe(`SELECT character_maximum_length AS n FROM information_schema.columns\n WHERE ${K(t)} AND table_name = '${e.table}' AND column_name = '${e.column}'`));
|
|
1551
|
+
if (i.length === 0) throw new n(`voltro db encrypt-column: ${e.table}.${e.column} does not exist on this database`);
|
|
1552
|
+
let a = Object.values(i[0])[0];
|
|
1553
|
+
return a == null ? void 0 : Number(a);
|
|
1554
|
+
})))), d = await J(f, D.SqlClient.pipe(E.flatMap((e) => E.promise(async () => await E.runPromise(e.unsafe(`SELECT ${G("id")} AS id, ${o} AS v FROM ${a}`)))))), m = 0;
|
|
1555
|
+
for (let e of d) typeof e.v != "string" || F(e.v) || (m = Math.max(m, ft(e.v)));
|
|
1556
|
+
let h = ct(m, l);
|
|
1557
|
+
if (!h.fits) return console.error(` ✗ ${lt(e, h)}`), 1;
|
|
1558
|
+
if (pt(d.map((e) => e.v).find((e) => typeof e == "string" && F(e)), u) === !1) return console.error(` ✗ ${mt(e, c)}`), 1;
|
|
1559
|
+
let g = d.filter((t) => dt(t.v, u, e) !== void 0).length;
|
|
1560
|
+
if (!r && !i) return console.error(` ✗ ${e.table}.${e.column}: ${g} row(s) would be rewritten in place. Re-run with --yes, or --dry-run to see the counts without writing.`), 1;
|
|
1411
1561
|
let _ = 0, v = 0, y = 0, b = [];
|
|
1412
|
-
for (let t of
|
|
1562
|
+
for (let t of d) {
|
|
1413
1563
|
if (t.v === null || t.v === void 0 || t.v === "") {
|
|
1414
1564
|
v += 1;
|
|
1415
1565
|
continue;
|
|
1416
1566
|
}
|
|
1417
|
-
if (typeof t.v == "string" &&
|
|
1567
|
+
if (typeof t.v == "string" && F(t.v)) {
|
|
1418
1568
|
_ += 1;
|
|
1419
1569
|
continue;
|
|
1420
1570
|
}
|
|
1421
|
-
let n =
|
|
1571
|
+
let n = dt(t.v, u, e);
|
|
1422
1572
|
if (n === void 0) {
|
|
1423
1573
|
v += 1;
|
|
1424
1574
|
continue;
|
|
@@ -1428,37 +1578,37 @@ export default defineMigration({
|
|
|
1428
1578
|
value: n
|
|
1429
1579
|
}), y += 1;
|
|
1430
1580
|
}
|
|
1431
|
-
if (!
|
|
1432
|
-
let n = b.slice(t, t +
|
|
1433
|
-
await J(
|
|
1434
|
-
for (let r of n) await
|
|
1581
|
+
if (!r && b.length > 0) for (let t = 0; t < b.length; t += s) {
|
|
1582
|
+
let n = b.slice(t, t + s);
|
|
1583
|
+
await J(f, D.SqlClient.pipe(E.flatMap((t) => E.promise(async () => {
|
|
1584
|
+
for (let r of n) await E.runPromise(t`UPDATE ${t(e.table)} SET ${t(e.column)} = ${r.value} WHERE ${t("id")} = ${r.id}`);
|
|
1435
1585
|
})))), B.info(`encrypt-column ${e.table}.${e.column}: ${Math.min(t + n.length, b.length)}/${b.length}`);
|
|
1436
1586
|
}
|
|
1437
|
-
|
|
1587
|
+
p.push({
|
|
1438
1588
|
table: e.table,
|
|
1439
1589
|
column: e.column,
|
|
1440
|
-
scanned:
|
|
1590
|
+
scanned: d.length,
|
|
1441
1591
|
alreadyEncrypted: _,
|
|
1442
1592
|
empty: v,
|
|
1443
1593
|
encrypted: y
|
|
1444
1594
|
});
|
|
1445
1595
|
}
|
|
1446
|
-
console.log(`field encryption · dialect ${t}${
|
|
1447
|
-
for (let e of
|
|
1448
|
-
return
|
|
1596
|
+
console.log(`field encryption · dialect ${t}${r ? " · DRY RUN, nothing written" : ""}`), console.log("");
|
|
1597
|
+
for (let e of p) console.log(ut(e, r));
|
|
1598
|
+
return r || console.log(ht(d)), 0;
|
|
1449
1599
|
}
|
|
1450
|
-
},
|
|
1600
|
+
}, dn = async (e) => {
|
|
1451
1601
|
let [t, ...n] = e;
|
|
1452
1602
|
if (!t) {
|
|
1453
1603
|
console.error("voltro db <subcommand> [args]"), console.error("subcommands:");
|
|
1454
|
-
for (let e of Object.keys(
|
|
1604
|
+
for (let e of Object.keys(un)) console.error(` ${e}`);
|
|
1455
1605
|
return 1;
|
|
1456
1606
|
}
|
|
1457
|
-
let r =
|
|
1607
|
+
let r = un[t];
|
|
1458
1608
|
if (!r) return console.error(`voltro db: unknown subcommand '${t}'`), 2;
|
|
1459
1609
|
(process.env.DB_DIRECT_URL !== void 0 || process.env.DB_MIGRATE_URL !== void 0) && B.info("using a DIRECT (non-pooler) connection for the db-command path (DB_DIRECT_URL/DB_MIGRATE_URL set)");
|
|
1460
1610
|
let i = (e) => {
|
|
1461
|
-
|
|
1611
|
+
vt(e) ? B.error(yt) : console.error(e), process.exit(1);
|
|
1462
1612
|
};
|
|
1463
1613
|
process.on("uncaughtException", i);
|
|
1464
1614
|
try {
|
|
@@ -1468,4 +1618,4 @@ export default defineMigration({
|
|
|
1468
1618
|
}
|
|
1469
1619
|
};
|
|
1470
1620
|
//#endregion
|
|
1471
|
-
export {
|
|
1621
|
+
export { Dt as a, tn as i, U as n, vt as o, Et as r, dn as s, yt as t };
|