@prisma/orm-toolchain 0.16.0-dev.36
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/LICENSE +201 -0
- package/README.md +28 -0
- package/dist/aggregate-B4mhR4Sk.mjs +1012 -0
- package/dist/aggregate-B4mhR4Sk.mjs.map +1 -0
- package/dist/aggregate-BflrjWPO.d.mts +803 -0
- package/dist/aggregate-BflrjWPO.d.mts.map +1 -0
- package/dist/bin__prisma-next.mjs +800 -0
- package/dist/bin__prisma-next.mjs.map +1 -0
- package/dist/cli-telemetry.d.mts +367 -0
- package/dist/cli-telemetry.d.mts.map +1 -0
- package/dist/cli-telemetry.mjs +48 -0
- package/dist/cli-telemetry.mjs.map +1 -0
- package/dist/cli-telemetry__sender.d.mts +1 -0
- package/dist/cli-telemetry__sender.mjs +1 -0
- package/dist/cli.d.mts +31 -0
- package/dist/cli.d.mts.map +1 -0
- package/dist/cli.mjs +180 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/cli__commands__contract-emit.d.mts +2 -0
- package/dist/cli__commands__contract-emit.mjs +2 -0
- package/dist/cli__commands__contract-infer.d.mts +7 -0
- package/dist/cli__commands__contract-infer.d.mts.map +1 -0
- package/dist/cli__commands__contract-infer.mjs +2 -0
- package/dist/cli__commands__db-init.d.mts +7 -0
- package/dist/cli__commands__db-init.d.mts.map +1 -0
- package/dist/cli__commands__db-init.mjs +2 -0
- package/dist/cli__commands__db-schema.d.mts +7 -0
- package/dist/cli__commands__db-schema.d.mts.map +1 -0
- package/dist/cli__commands__db-schema.mjs +2 -0
- package/dist/cli__commands__db-sign.d.mts +7 -0
- package/dist/cli__commands__db-sign.d.mts.map +1 -0
- package/dist/cli__commands__db-sign.mjs +2 -0
- package/dist/cli__commands__db-update.d.mts +7 -0
- package/dist/cli__commands__db-update.d.mts.map +1 -0
- package/dist/cli__commands__db-update.mjs +2 -0
- package/dist/cli__commands__db-verify.d.mts +7 -0
- package/dist/cli__commands__db-verify.d.mts.map +1 -0
- package/dist/cli__commands__db-verify.mjs +2 -0
- package/dist/cli__commands__migrate.d.mts +66 -0
- package/dist/cli__commands__migrate.d.mts.map +1 -0
- package/dist/cli__commands__migrate.mjs +2 -0
- package/dist/cli__commands__migration-check.d.mts +64 -0
- package/dist/cli__commands__migration-check.d.mts.map +1 -0
- package/dist/cli__commands__migration-check.mjs +3 -0
- package/dist/cli__commands__migration-graph.d.mts +36 -0
- package/dist/cli__commands__migration-graph.d.mts.map +1 -0
- package/dist/cli__commands__migration-graph.mjs +2 -0
- package/dist/cli__commands__migration-list.d.mts +79 -0
- package/dist/cli__commands__migration-list.d.mts.map +1 -0
- package/dist/cli__commands__migration-list.mjs +2 -0
- package/dist/cli__commands__migration-log.d.mts +20 -0
- package/dist/cli__commands__migration-log.d.mts.map +1 -0
- package/dist/cli__commands__migration-log.mjs +2 -0
- package/dist/cli__commands__migration-new.d.mts +7 -0
- package/dist/cli__commands__migration-new.d.mts.map +1 -0
- package/dist/cli__commands__migration-new.mjs +2 -0
- package/dist/cli__commands__migration-plan.d.mts +79 -0
- package/dist/cli__commands__migration-plan.d.mts.map +1 -0
- package/dist/cli__commands__migration-plan.mjs +2 -0
- package/dist/cli__commands__migration-show.d.mts +27 -0
- package/dist/cli__commands__migration-show.d.mts.map +1 -0
- package/dist/cli__commands__migration-show.mjs +2 -0
- package/dist/cli__commands__migration-status.d.mts +55 -0
- package/dist/cli__commands__migration-status.d.mts.map +1 -0
- package/dist/cli__commands__migration-status.mjs +3 -0
- package/dist/cli__commands__ref.d.mts +34 -0
- package/dist/cli__commands__ref.d.mts.map +1 -0
- package/dist/cli__commands__ref.mjs +2 -0
- package/dist/cli__commands__telemetry.d.mts +7 -0
- package/dist/cli__commands__telemetry.d.mts.map +1 -0
- package/dist/cli__commands__telemetry.mjs +2 -0
- package/dist/cli__config-types.d.mts +2 -0
- package/dist/cli__config-types.mjs +2 -0
- package/dist/cli__control-api.d.mts +141 -0
- package/dist/cli__control-api.d.mts.map +1 -0
- package/dist/cli__control-api.mjs +4 -0
- package/dist/cli__init-output.d.mts +37 -0
- package/dist/cli__init-output.d.mts.map +1 -0
- package/dist/cli__init-output.mjs +2 -0
- package/dist/cli__migration-cli.d.mts +79 -0
- package/dist/cli__migration-cli.d.mts.map +1 -0
- package/dist/cli__migration-cli.mjs +407 -0
- package/dist/cli__migration-cli.mjs.map +1 -0
- package/dist/client-75qrAPqf-DZAVUxFa.mjs +1606 -0
- package/dist/client-75qrAPqf-DZAVUxFa.mjs.map +1 -0
- package/dist/command-helpers-CAoXqAY9-DWUBcTXM.mjs +1671 -0
- package/dist/command-helpers-CAoXqAY9-DWUBcTXM.mjs.map +1 -0
- package/dist/config-loader.d.mts +18 -0
- package/dist/config-loader.d.mts.map +1 -0
- package/dist/config-loader.mjs +2 -0
- package/dist/constants-CM-w38DQ-Cz_EXbOc.mjs +10 -0
- package/dist/constants-CM-w38DQ-Cz_EXbOc.mjs.map +1 -0
- package/dist/constants-Dkcn8Qu4.d.mts +10 -0
- package/dist/constants-Dkcn8Qu4.d.mts.map +1 -0
- package/dist/contract-at-errors-BX7w-p19-SxA8eaV2.mjs +36 -0
- package/dist/contract-at-errors-BX7w-p19-SxA8eaV2.mjs.map +1 -0
- package/dist/contract-emit-0hSjbuxp-BBOKldTZ.mjs +148 -0
- package/dist/contract-emit-0hSjbuxp-BBOKldTZ.mjs.map +1 -0
- package/dist/contract-emit-B65_UiFn.d.mts +7 -0
- package/dist/contract-emit-B65_UiFn.d.mts.map +1 -0
- package/dist/contract-emit-B6odMoem-CK3diBPk.mjs +359 -0
- package/dist/contract-emit-B6odMoem-CK3diBPk.mjs.map +1 -0
- package/dist/contract-enrichment-DrMvvY2A-DxNGeF4d.mjs +56 -0
- package/dist/contract-enrichment-DrMvvY2A-DxNGeF4d.mjs.map +1 -0
- package/dist/contract-infer-BnkqpftN-DVM2eKJ3.mjs +73 -0
- package/dist/contract-infer-BnkqpftN-DVM2eKJ3.mjs.map +1 -0
- package/dist/contract-snapshot-store-CbqynbtE.d.mts +28 -0
- package/dist/contract-snapshot-store-CbqynbtE.d.mts.map +1 -0
- package/dist/contract-snapshot-store-sf5nP4mb-MLpG-ihh.mjs +118 -0
- package/dist/contract-snapshot-store-sf5nP4mb-MLpG-ihh.mjs.map +1 -0
- package/dist/contract-space-aggregate-loader-CFH-336b-BhAyRQEL.mjs +302 -0
- package/dist/contract-space-aggregate-loader-CFH-336b-BhAyRQEL.mjs.map +1 -0
- package/dist/db-init-Vbkb_TKZ.mjs +153 -0
- package/dist/db-init-Vbkb_TKZ.mjs.map +1 -0
- package/dist/db-schema-ClQCu3xY.mjs +47 -0
- package/dist/db-schema-ClQCu3xY.mjs.map +1 -0
- package/dist/db-sign-jjXDgQ1u.mjs +175 -0
- package/dist/db-sign-jjXDgQ1u.mjs.map +1 -0
- package/dist/db-update-BQlrqtDD.mjs +183 -0
- package/dist/db-update-BQlrqtDD.mjs.map +1 -0
- package/dist/db-verify-CKPsRFrj-3udprZQD.mjs +396 -0
- package/dist/db-verify-CKPsRFrj-3udprZQD.mjs.map +1 -0
- package/dist/domain-type-generation-DAn9y5d2-BgoT5uKI.mjs +334 -0
- package/dist/domain-type-generation-DAn9y5d2-BgoT5uKI.mjs.map +1 -0
- package/dist/domain-type-generation-PkAb39ob-Ct8AGqow.d.mts +63 -0
- package/dist/domain-type-generation-PkAb39ob-Ct8AGqow.d.mts.map +1 -0
- package/dist/emitter.d.mts +75 -0
- package/dist/emitter.d.mts.map +1 -0
- package/dist/emitter.mjs +3 -0
- package/dist/emitter__domain-type-generation.d.mts +2 -0
- package/dist/emitter__domain-type-generation.mjs +2 -0
- package/dist/emitter__type-expression-safety.d.mts +6 -0
- package/dist/emitter__type-expression-safety.d.mts.map +1 -0
- package/dist/emitter__type-expression-safety.mjs +2 -0
- package/dist/errors--qsXZUtL.d.mts +72 -0
- package/dist/errors--qsXZUtL.d.mts.map +1 -0
- package/dist/errors-IpZm1Skp-BH495dRd.mjs +378 -0
- package/dist/errors-IpZm1Skp-BH495dRd.mjs.map +1 -0
- package/dist/exports-BN0vFTxG.mjs +276 -0
- package/dist/exports-BN0vFTxG.mjs.map +1 -0
- package/dist/exports-B_xhKZmC.mjs +1436 -0
- package/dist/exports-B_xhKZmC.mjs.map +1 -0
- package/dist/exports-D3-NwrMA-CadaBGQ3.mjs +154 -0
- package/dist/exports-D3-NwrMA-CadaBGQ3.mjs.map +1 -0
- package/dist/exports-Dl2aGAk7.mjs +119 -0
- package/dist/exports-Dl2aGAk7.mjs.map +1 -0
- package/dist/format-ByDm2goZ-DKLyhNHo.mjs +101 -0
- package/dist/format-ByDm2goZ-DKLyhNHo.mjs.map +1 -0
- package/dist/framework-components-CjshQKHA-Dlp6Leo5.mjs +58 -0
- package/dist/framework-components-CjshQKHA-Dlp6Leo5.mjs.map +1 -0
- package/dist/global-flags-BpoOYtNZ-CHZD3fud.d.mts +35 -0
- package/dist/global-flags-BpoOYtNZ-CHZD3fud.d.mts.map +1 -0
- package/dist/graph-bDjJ4GL9-BhFevsJS.d.mts +28 -0
- package/dist/graph-bDjJ4GL9-BhFevsJS.d.mts.map +1 -0
- package/dist/graph-membership-DW6nnbyQ-Bp2KcVwR.mjs +14 -0
- package/dist/graph-membership-DW6nnbyQ-Bp2KcVwR.mjs.map +1 -0
- package/dist/hash-DH8hl5Rq-BpM2t3Nu.mjs +64 -0
- package/dist/hash-DH8hl5Rq-BpM2t3Nu.mjs.map +1 -0
- package/dist/hash-XZYYKJ-i.d.mts +53 -0
- package/dist/hash-XZYYKJ-i.d.mts.map +1 -0
- package/dist/import-roots-B6CtsWXb.d.mts +119 -0
- package/dist/import-roots-B6CtsWXb.d.mts.map +1 -0
- package/dist/import-roots-NDOIEDVj.mjs +217 -0
- package/dist/import-roots-NDOIEDVj.mjs.map +1 -0
- package/dist/init-DHctXAI9-DSlaaYXN.mjs +2186 -0
- package/dist/init-DHctXAI9-DSlaaYXN.mjs.map +1 -0
- package/dist/inspect-live-schema-CnwYTRZ0-BErPHMni.mjs +91 -0
- package/dist/inspect-live-schema-CnwYTRZ0-BErPHMni.mjs.map +1 -0
- package/dist/invariants-B5wLrgQS.d.mts +39 -0
- package/dist/invariants-B5wLrgQS.d.mts.map +1 -0
- package/dist/invariants-lXzy6txi-DAoP4nTE.mjs +57 -0
- package/dist/invariants-lXzy6txi-DAoP4nTE.mjs.map +1 -0
- package/dist/io-C8TdyBKt-DloEBi70.mjs +313 -0
- package/dist/io-C8TdyBKt-DloEBi70.mjs.map +1 -0
- package/dist/io-DYfeLJxR.d.mts +1 -0
- package/dist/io-sr4CO9_t-C3eV_kn-.d.mts +125 -0
- package/dist/io-sr4CO9_t-C3eV_kn-.d.mts.map +1 -0
- package/dist/ledger-origin-BGRN77Oy.d.mts +6 -0
- package/dist/ledger-origin-BGRN77Oy.d.mts.map +1 -0
- package/dist/ledger-origin-D7PN4Fon.mjs +9 -0
- package/dist/ledger-origin-D7PN4Fon.mjs.map +1 -0
- package/dist/metadata-2nbOAGsV-uYNeq2Cp.d.mts +2 -0
- package/dist/metadata-BtscDay2.d.mts +1 -0
- package/dist/migrate-Cb_iv264.mjs +500 -0
- package/dist/migrate-Cb_iv264.mjs.map +1 -0
- package/dist/migration-B2ljMJim.d.mts +155 -0
- package/dist/migration-B2ljMJim.d.mts.map +1 -0
- package/dist/migration-BuUmO7It.mjs +209 -0
- package/dist/migration-BuUmO7It.mjs.map +1 -0
- package/dist/migration-check-CcG-fB8r-CDs5C9vt.mjs +597 -0
- package/dist/migration-check-CcG-fB8r-CDs5C9vt.mjs.map +1 -0
- package/dist/migration-command-scaffold-BCuoeAki-B1pVGX8d.mjs +103 -0
- package/dist/migration-command-scaffold-BCuoeAki-B1pVGX8d.mjs.map +1 -0
- package/dist/migration-graph-CBf9ORe7-B4xAF8GN.mjs +500 -0
- package/dist/migration-graph-CBf9ORe7-B4xAF8GN.mjs.map +1 -0
- package/dist/migration-graph-DkoCC0Qy.mjs +185 -0
- package/dist/migration-graph-DkoCC0Qy.mjs.map +1 -0
- package/dist/migration-graph-Dqndu0i5.d.mts +9 -0
- package/dist/migration-graph-Dqndu0i5.d.mts.map +1 -0
- package/dist/migration-graph-command-render-BKrg6BPv-BP8Pfj4J.mjs +1945 -0
- package/dist/migration-graph-command-render-BKrg6BPv-BP8Pfj4J.mjs.map +1 -0
- package/dist/migration-graph-tarTxJ99-BrGBANeE.d.mts +124 -0
- package/dist/migration-graph-tarTxJ99-BrGBANeE.d.mts.map +1 -0
- package/dist/migration-list-BFBeo1_X-c0kqYokL.mjs +228 -0
- package/dist/migration-list-BFBeo1_X-c0kqYokL.mjs.map +1 -0
- package/dist/migration-log-CS5IWcNq-BQ-32Low.mjs +220 -0
- package/dist/migration-log-CS5IWcNq-BQ-32Low.mjs.map +1 -0
- package/dist/migration-new-BurCjyaQ.mjs +168 -0
- package/dist/migration-new-BurCjyaQ.mjs.map +1 -0
- package/dist/migration-path-target-D3Sj55PI-BICblCuk.mjs +38 -0
- package/dist/migration-path-target-D3Sj55PI-BICblCuk.mjs.map +1 -0
- package/dist/migration-plan-q_L84PN1-BWE0LTH6.mjs +759 -0
- package/dist/migration-plan-q_L84PN1-BWE0LTH6.mjs.map +1 -0
- package/dist/migration-show-Bx5I5aW9.mjs +171 -0
- package/dist/migration-show-Bx5I5aW9.mjs.map +1 -0
- package/dist/migration-status-CFlzlRmu-DewBN8Ys.mjs +444 -0
- package/dist/migration-status-CFlzlRmu-DewBN8Ys.mjs.map +1 -0
- package/dist/migration-tools.d.mts +23 -0
- package/dist/migration-tools.mjs +17 -0
- package/dist/migration-tools__aggregate.d.mts +2 -0
- package/dist/migration-tools__aggregate.mjs +2 -0
- package/dist/migration-tools__constants.d.mts +2 -0
- package/dist/migration-tools__constants.mjs +2 -0
- package/dist/migration-tools__contract-snapshot-store.d.mts +2 -0
- package/dist/migration-tools__contract-snapshot-store.mjs +2 -0
- package/dist/migration-tools__errors.d.mts +2 -0
- package/dist/migration-tools__errors.mjs +2 -0
- package/dist/migration-tools__graph.d.mts +2 -0
- package/dist/migration-tools__graph.mjs +1 -0
- package/dist/migration-tools__hash.d.mts +2 -0
- package/dist/migration-tools__hash.mjs +2 -0
- package/dist/migration-tools__invariants.d.mts +2 -0
- package/dist/migration-tools__invariants.mjs +2 -0
- package/dist/migration-tools__io.d.mts +3 -0
- package/dist/migration-tools__io.mjs +2 -0
- package/dist/migration-tools__ledger-origin.d.mts +2 -0
- package/dist/migration-tools__ledger-origin.mjs +2 -0
- package/dist/migration-tools__metadata.d.mts +3 -0
- package/dist/migration-tools__metadata.mjs +1 -0
- package/dist/migration-tools__migration-graph.d.mts +3 -0
- package/dist/migration-tools__migration-graph.mjs +3 -0
- package/dist/migration-tools__migration-ts.d.mts +2 -0
- package/dist/migration-tools__migration-ts.mjs +2 -0
- package/dist/migration-tools__migration.d.mts +2 -0
- package/dist/migration-tools__migration.mjs +2 -0
- package/dist/migration-tools__package.d.mts +3 -0
- package/dist/migration-tools__package.mjs +1 -0
- package/dist/migration-tools__ref-resolution.d.mts +2 -0
- package/dist/migration-tools__ref-resolution.mjs +2 -0
- package/dist/migration-tools__refs.d.mts +2 -0
- package/dist/migration-tools__refs.mjs +2 -0
- package/dist/migration-tools__spaces.d.mts +3 -0
- package/dist/migration-tools__spaces.mjs +3 -0
- package/dist/migration-ts-CNaAtK8C.d.mts +38 -0
- package/dist/migration-ts-CNaAtK8C.d.mts.map +1 -0
- package/dist/migration-ts-FyF55c4M.mjs +67 -0
- package/dist/migration-ts-FyF55c4M.mjs.map +1 -0
- package/dist/op-schema-BSVHzEQN-DIWt28D8.mjs +13 -0
- package/dist/op-schema-BSVHzEQN-DIWt28D8.mjs.map +1 -0
- package/dist/output-DWNYqZGc-D2sRgLAm.mjs +109 -0
- package/dist/output-DWNYqZGc-D2sRgLAm.mjs.map +1 -0
- package/dist/package-CfWiDNrr-ZR4HM_Iz.d.mts +21 -0
- package/dist/package-CfWiDNrr-ZR4HM_Iz.d.mts.map +1 -0
- package/dist/package-N9PPdFV_.d.mts +3 -0
- package/dist/progress-adapter-CjAeTxY_-BXYSd1eY.mjs +43 -0
- package/dist/progress-adapter-CjAeTxY_-BXYSd1eY.mjs.map +1 -0
- package/dist/project-import-root-CpwLCDLY-DkVwAnWj.mjs +99 -0
- package/dist/project-import-root-CpwLCDLY-DkVwAnWj.mjs.map +1 -0
- package/dist/publish-surface.d.mts +3 -0
- package/dist/publish-surface.mjs +3 -0
- package/dist/publish-surface__import-roots.d.mts +2 -0
- package/dist/publish-surface__import-roots.mjs +2 -0
- package/dist/publish-surface__shells.d.mts +2 -0
- package/dist/publish-surface__shells.mjs +2 -0
- package/dist/quick-reference-mongo.md +114 -0
- package/dist/quick-reference-postgres.md +93 -0
- package/dist/readme-mongo.md +35 -0
- package/dist/readme-postgres.md +34 -0
- package/dist/ref-B6cTthTi.mjs +171 -0
- package/dist/ref-B6cTthTi.mjs.map +1 -0
- package/dist/ref-advancement-CSPMoQs3-fEipZrdu.mjs +57 -0
- package/dist/ref-advancement-CSPMoQs3-fEipZrdu.mjs.map +1 -0
- package/dist/ref-resolution-BjJBWPjX.mjs +250 -0
- package/dist/ref-resolution-BjJBWPjX.mjs.map +1 -0
- package/dist/ref-resolution-COGOSL57.d.mts +127 -0
- package/dist/ref-resolution-COGOSL57.d.mts.map +1 -0
- package/dist/refs-BDbB9BRB-D6Za3Q49.mjs +241 -0
- package/dist/refs-BDbB9BRB-D6Za3Q49.mjs.map +1 -0
- package/dist/refs-BNCUu58Y-BIso5Jfg.d.mts +43 -0
- package/dist/refs-BNCUu58Y-BIso5Jfg.d.mts.map +1 -0
- package/dist/schemas-CeGMYFYX-h3OcU__O.d.mts +192 -0
- package/dist/schemas-CeGMYFYX-h3OcU__O.d.mts.map +1 -0
- package/dist/schemas-KhXMzNA_-CzZ7GFX_.mjs +112 -0
- package/dist/schemas-KhXMzNA_-CzZ7GFX_.mjs.map +1 -0
- package/dist/shells-BenrJHp2.mjs +602 -0
- package/dist/shells-BenrJHp2.mjs.map +1 -0
- package/dist/shells-COHV_Ozf.d.mts +135 -0
- package/dist/shells-COHV_Ozf.d.mts.map +1 -0
- package/dist/spaces-C27bA-xc.mjs +267 -0
- package/dist/spaces-C27bA-xc.mjs.map +1 -0
- package/dist/spaces-DxFj5y8l.d.mts +415 -0
- package/dist/spaces-DxFj5y8l.d.mts.map +1 -0
- package/dist/telemetry-CmbCa3Ld-CmE5zcIG.mjs +120 -0
- package/dist/telemetry-CmbCa3Ld-CmE5zcIG.mjs.map +1 -0
- package/dist/terminal-ui-BN4QOsm5-Bf3pim46.d.mts +133 -0
- package/dist/terminal-ui-BN4QOsm5-Bf3pim46.d.mts.map +1 -0
- package/dist/type-expression-safety-CgHTWypx.mjs +8 -0
- package/dist/type-expression-safety-CgHTWypx.mjs.map +1 -0
- package/dist/types-DZWEs4si-D8UlyJI0.d.mts +893 -0
- package/dist/types-DZWEs4si-D8UlyJI0.d.mts.map +1 -0
- package/dist/verify-DnVNjc5C-BGdsblBK.mjs +283 -0
- package/dist/verify-DnVNjc5C-BGdsblBK.mjs.map +1 -0
- package/dist/verify-contract-spaces-BZDcJsuA-De8v4Z4T.mjs +252 -0
- package/dist/verify-contract-spaces-BZDcJsuA-De8v4Z4T.mjs.map +1 -0
- package/dist/verify-contract-spaces-Cc1u_wJo-BilPJS2S.d.mts +136 -0
- package/dist/verify-contract-spaces-Cc1u_wJo-BilPJS2S.d.mts.map +1 -0
- package/dist/vite-plugin-contract-emit.d.mts +54 -0
- package/dist/vite-plugin-contract-emit.d.mts.map +1 -0
- package/dist/vite-plugin-contract-emit.mjs +303 -0
- package/dist/vite-plugin-contract-emit.mjs.map +1 -0
- package/package.json +127 -0
|
@@ -0,0 +1,1671 @@
|
|
|
1
|
+
import { c as spaceMigrationDirectory, t as APP_SPACE_ID$1 } from "./verify-contract-spaces-BZDcJsuA-De8v4Z4T.mjs";
|
|
2
|
+
import { CliStructuredError as CliStructuredError$1, errorConfigValidation as errorConfigValidation$1, errorContractConfigMissing as errorContractConfigMissing$1, errorContractValidationFailed, errorDatabaseConnectionRequired, errorDriverRequired, errorFileNotFound, errorInvalidOutputFormat, errorMigrationPlanningFailed, errorOutputFormatMutex, errorTargetMigrationNotSupported, errorUnexpected as errorUnexpected$1 } from "@prisma/orm-framework/errors/control";
|
|
3
|
+
import { ifDefined } from "@prisma/orm-framework/utils/defined";
|
|
4
|
+
import { notOk } from "@prisma/orm-framework/utils/result";
|
|
5
|
+
import { relative, resolve } from "pathe";
|
|
6
|
+
import { readFile } from "node:fs/promises";
|
|
7
|
+
import { hasMigrations } from "@prisma/orm-framework/components/control";
|
|
8
|
+
import { ERROR_CODE_DESTRUCTIVE_CHANGES, errorDestructiveChanges, errorHashMismatch, errorMarkerMissing, errorRunnerFailed, errorRuntime, errorRuntime as errorRuntime$1, errorTargetMismatch } from "@prisma/orm-framework/errors/execution";
|
|
9
|
+
import "@prisma/orm-framework/errors/migration";
|
|
10
|
+
import { blindCast } from "@prisma/orm-framework/utils/casts";
|
|
11
|
+
import { blue, bold, cyan, dim, green, magenta, red, yellow } from "colorette";
|
|
12
|
+
import wrapAnsi from "wrap-ansi";
|
|
13
|
+
import stringWidth from "string-width";
|
|
14
|
+
import stripAnsi from "strip-ansi";
|
|
15
|
+
import { isCI } from "ci-info";
|
|
16
|
+
import * as clack from "@clack/prompts";
|
|
17
|
+
//#region ../../../1-framework/3-tooling/cli/dist/command-helpers-CAoXqAY9.mjs
|
|
18
|
+
function errorRefSetHashNotInGraph(resolvedHash, reachableHashes, graphTipHash) {
|
|
19
|
+
const reachableList = reachableHashes.length > 0 ? reachableHashes.join(", ") : "(none — migration graph is empty)";
|
|
20
|
+
const fix = reachableHashes.length > 0 ? graphTipHash !== null ? `Set the ref to a graph-node hash such as ${graphTipHash}, or run \`prisma-next migration plan\` to extend the graph.` : "Set the ref to a hash that appears in the migration graph." : "Run `prisma-next migration plan` first.";
|
|
21
|
+
return errorRuntime(`Resolved contract hash is not in the migration graph: ${resolvedHash}`, {
|
|
22
|
+
why: reachableHashes.length > 0 ? `The migration graph reaches ${reachableList}; resolved ${resolvedHash} isn't a graph node.` : "The migration graph is empty — no hashes reachable.",
|
|
23
|
+
fix,
|
|
24
|
+
meta: {
|
|
25
|
+
code: "MIGRATION.HASH_NOT_IN_GRAPH",
|
|
26
|
+
resolvedHash,
|
|
27
|
+
reachableHashes: [...reachableHashes],
|
|
28
|
+
...graphTipHash !== null ? { graphTipHash } : {}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function errorRefSetEmptySentinel(hash) {
|
|
33
|
+
return errorRuntime(`Cannot set ref to the empty-database sentinel: ${hash}`, {
|
|
34
|
+
why: "The empty-database sentinel is a planner internal; it is not a valid ref target.",
|
|
35
|
+
fix: "Set the ref to a contract hash from the migration graph, or use another ref name.",
|
|
36
|
+
meta: {
|
|
37
|
+
code: "MIGRATION.REF_SET_EMPTY_SENTINEL",
|
|
38
|
+
hash
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* `--legend` was combined with a machine-readable or silent output flag.
|
|
44
|
+
* The legend is human-only decoration on stderr.
|
|
45
|
+
*/
|
|
46
|
+
function errorLegendHumanOnly(conflictingFlag) {
|
|
47
|
+
return errorRuntime("`--legend` is only available for human-readable output", {
|
|
48
|
+
why: `\`--legend\` prints a glyph key to stderr and cannot be combined with ${conflictingFlag}.`,
|
|
49
|
+
fix: `Omit ${conflictingFlag} to print the legend alongside the tree, or omit --legend when using ${conflictingFlag}.`,
|
|
50
|
+
meta: {
|
|
51
|
+
code: "MIGRATION.LEGEND_HUMAN_ONLY",
|
|
52
|
+
conflictingFlag
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `--space <id>` was given a value that doesn't satisfy the contract-space
|
|
58
|
+
* naming rule (`[a-z][a-z0-9_-]{0,63}` per `isValidSpaceId`). Fires before
|
|
59
|
+
* any fs work — the input is syntactically rejected the same way an on-disk
|
|
60
|
+
* directory with that name would be skipped by the enumerator.
|
|
61
|
+
*/
|
|
62
|
+
function errorInvalidSpaceId(spaceId) {
|
|
63
|
+
return errorRuntime(`Invalid contract space id: ${spaceId}`, {
|
|
64
|
+
why: "Contract space ids must match [a-z][a-z0-9_-]{0,63} (lowercase, starts with a letter, max 64 characters — the rule applied to every on-disk space directory).",
|
|
65
|
+
fix: "Pass a space id that matches the directory naming rule, or omit --space to list every space.",
|
|
66
|
+
meta: {
|
|
67
|
+
code: "MIGRATION.INVALID_SPACE_ID",
|
|
68
|
+
spaceId
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* `migration list --space <id>` was given a contract-space id that has no
|
|
74
|
+
* directory under `migrations/`. Distinct from "the space exists but is
|
|
75
|
+
* empty" — that path renders the empty-state line and exits 0 per the
|
|
76
|
+
* slice spec § Empty-state. This error fires only when `<projectMigrationsDir>/<spaceId>`
|
|
77
|
+
* does not exist on disk.
|
|
78
|
+
*
|
|
79
|
+
* `availableSpaces` lists the contract-space directory names actually
|
|
80
|
+
* present, sorted lex-asc, so the diagnostic can suggest a near match
|
|
81
|
+
* without making the user reach for `ls`.
|
|
82
|
+
*/
|
|
83
|
+
function errorSpaceNotFound(spaceId, availableSpaces) {
|
|
84
|
+
const availableList = availableSpaces.length > 0 ? availableSpaces.join(", ") : "(none — no contract spaces on disk yet)";
|
|
85
|
+
const fix = availableSpaces.length > 0 ? `Pick one of: ${availableList}. Run \`prisma-next migration list\` (no --space) to see every space's migrations.` : "Author a migration with `prisma-next migration new` to create the first contract-space directory.";
|
|
86
|
+
return errorRuntime(`Unknown contract space: ${spaceId}`, {
|
|
87
|
+
why: `No directory named "${spaceId}" exists under the migrations root.`,
|
|
88
|
+
fix,
|
|
89
|
+
meta: {
|
|
90
|
+
code: "MIGRATION.SPACE_NOT_FOUND",
|
|
91
|
+
spaceId,
|
|
92
|
+
availableSpaces: [...availableSpaces]
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function errorRefSetBundleNotFound(hash) {
|
|
97
|
+
return errorRuntime(`No migration bundle matches graph-node hash ${hash}`, {
|
|
98
|
+
why: `The hash is a graph node but no on-disk bundle has metadata.to = ${hash}.`,
|
|
99
|
+
fix: "Run `pnpm fixtures:check`, or re-emit the migration that produces this hash so its bundle is restored.",
|
|
100
|
+
meta: {
|
|
101
|
+
code: "MIGRATION.REF_SET_BUNDLE_NOT_FOUND",
|
|
102
|
+
hash
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
function errorPlanForgotTheFlag(resolvedHash, reachableRefs, graphTipHash) {
|
|
107
|
+
const reachableList = reachableRefs.length > 0 ? reachableRefs.map((r) => `${r.name} (${r.hash})`).join(", ") : "(none)";
|
|
108
|
+
const refFix = reachableRefs.length > 0 ? `Run migration plan with ${reachableRefs.map((r) => `--from ${r.name}`).join(" or ")}.` : graphTipHash !== null ? `Run migration plan --from ${graphTipHash}.` : "Commit pending migrations first, then run migration plan.";
|
|
109
|
+
return errorRuntime(`Resolved from-hash is not in the migration graph: ${resolvedHash}`, {
|
|
110
|
+
why: `The migration graph reaches ${reachableList}; resolved ${resolvedHash} isn't a graph node.`,
|
|
111
|
+
fix: refFix,
|
|
112
|
+
meta: {
|
|
113
|
+
code: "MIGRATION.HASH_NOT_IN_GRAPH",
|
|
114
|
+
resolvedHash,
|
|
115
|
+
reachableRefs: reachableRefs.map((r) => r.name),
|
|
116
|
+
...graphTipHash !== null ? { graphTipHash } : {}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* `viaRef: true` (the default) mirrors migration-tools' `errorRefNotResolvable`:
|
|
122
|
+
* a ref name with no pointer file, where the fallback hash isn't a graph
|
|
123
|
+
* node either — there's nothing to materialize a contract from.
|
|
124
|
+
* `viaRef: false` is a distinct, ref-independent case: an explicit `--from
|
|
125
|
+
* <hash>` that doesn't name a ref, on an empty migration graph, so there is
|
|
126
|
+
* no graph node and no ref to resolve a contract through.
|
|
127
|
+
*/
|
|
128
|
+
function errorSnapshotMissing(identifier, options) {
|
|
129
|
+
const viaRef = options?.viaRef !== false;
|
|
130
|
+
const fix = viaRef ? `Create the ref with "prisma-next ref set ${identifier} <hash>" (or advance it via "prisma-next db update --advance-ref ${identifier}"), or pass a hash that is a node in the migration graph.` : `No contract source exists for hash "${identifier}" on an empty migration graph. Use --from with a ref name (its contract resolves through the snapshot store), or run db update first.`;
|
|
131
|
+
return errorRuntime(viaRef ? `Ref "${identifier}" is not resolvable` : `No contract source for from-hash "${identifier}"`, {
|
|
132
|
+
why: viaRef ? `Ref "${identifier}" has no pointer file, and the hash being resolved is not a node in the migration graph either.` : `Hash "${identifier}" is not a node in the migration graph (the graph is empty), and it does not name a ref either.`,
|
|
133
|
+
fix,
|
|
134
|
+
meta: {
|
|
135
|
+
code: "MIGRATION.SNAPSHOT_MISSING",
|
|
136
|
+
identifier,
|
|
137
|
+
viaRef
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
function errorMarkerMismatch(markerHash, reachableHashes, graphTip) {
|
|
142
|
+
const reachableList = reachableHashes.length > 0 ? reachableHashes.join(", ") : "(none — migration graph is empty)";
|
|
143
|
+
const planFromFix = graphTip !== null ? `Run \`prisma-next migration plan --from ${graphTip}\` if the live marker is canonical and the on-disk graph needs catching up.` : "Run `prisma-next migration plan` if the live marker is canonical and the on-disk graph needs catching up.";
|
|
144
|
+
return errorRuntime("Database marker is not reachable in the on-disk migration graph", {
|
|
145
|
+
why: `DB marker is ${markerHash}, but the on-disk migration graph reaches: ${reachableList}.`,
|
|
146
|
+
fix: [
|
|
147
|
+
planFromFix,
|
|
148
|
+
`Run \`prisma-next ref set db ${markerHash}\` if the on-disk graph is canonical and the local \`db\` ref drifted.`,
|
|
149
|
+
"Investigate whether the database was migrated by an out-of-band process."
|
|
150
|
+
].join("\n"),
|
|
151
|
+
meta: {
|
|
152
|
+
code: "MIGRATION.MARKER_MISMATCH",
|
|
153
|
+
markerHash,
|
|
154
|
+
reachableHashes: [...reachableHashes],
|
|
155
|
+
...graphTip !== null ? { graphTip } : {}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function errorPathUnreachable(failure) {
|
|
160
|
+
const meta = failure.meta ?? {};
|
|
161
|
+
const fromHashMeta = typeof meta["fromHash"] === "string" ? meta["fromHash"] : null;
|
|
162
|
+
const planFromHash = fromHashMeta === "<empty>" ? null : fromHashMeta;
|
|
163
|
+
const targetHash = typeof meta["targetHash"] === "string" ? meta["targetHash"] : typeof meta["target"] === "string" ? meta["target"] : null;
|
|
164
|
+
const deadEnds = meta["deadEnds"];
|
|
165
|
+
const deadEndsSuffix = Array.isArray(deadEnds) && deadEnds.length > 0 ? ` Dead-ends: ${deadEnds.map(String).join(", ")}.` : "";
|
|
166
|
+
const neverPlanned = meta["kind"] === "neverPlanned";
|
|
167
|
+
const planCommand = (() => {
|
|
168
|
+
if (neverPlanned) return "prisma-next migration plan --name <slug>";
|
|
169
|
+
if (planFromHash !== null && targetHash !== null) return `prisma-next migration plan --from ${planFromHash} --to ${targetHash} --name <slug>`;
|
|
170
|
+
if (targetHash !== null) return `prisma-next migration plan --to ${targetHash} --name <slug>`;
|
|
171
|
+
if (planFromHash !== null) return `prisma-next migration plan --from ${planFromHash} --name <slug>`;
|
|
172
|
+
return "prisma-next migration plan";
|
|
173
|
+
})();
|
|
174
|
+
const applyCommand = targetHash !== null && !neverPlanned ? `prisma-next migrate --to ${targetHash}` : "prisma-next migrate";
|
|
175
|
+
return errorRuntime(failure.summary, {
|
|
176
|
+
why: failure.why ?? `Cannot reach target "${targetHash ?? "<unknown>"}" from current marker "${fromHashMeta ?? "<unknown>"}".${deadEndsSuffix}`,
|
|
177
|
+
fix: [
|
|
178
|
+
"Plan the missing edge, then apply it:",
|
|
179
|
+
` 1. ${planCommand}`,
|
|
180
|
+
` 2. ${applyCommand}`,
|
|
181
|
+
"A rollback (reverse) plan is expected to contain destructive (DROP) operations — review them before applying.",
|
|
182
|
+
"Narrower cases (rename inference, re-adding a NOT NULL column without a safe default, or a type change that needs data) may additionally need a hint in the planned migration.",
|
|
183
|
+
"Inspect the on-disk graph with `prisma-next migration list`, or `prisma-next migration show <bundle>` for any bundle in the path you expected."
|
|
184
|
+
].join("\n"),
|
|
185
|
+
meta: {
|
|
186
|
+
...meta,
|
|
187
|
+
code: "MIGRATION.PATH_UNREACHABLE"
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Maps a `MigrationToolsError` raised by the migration-tools loader/graph
|
|
193
|
+
* surface (`readMigrationPackage`, `readMigrationsDir`, `readRefs`,
|
|
194
|
+
* `resolveRef`, `reconstructGraph`, ...) into a CLI `errorRuntime` envelope.
|
|
195
|
+
*
|
|
196
|
+
* The full `error.details` payload is forwarded into `meta` so machine
|
|
197
|
+
* consumers (`--json`) see structural fields like `dir`, `storedHash`,
|
|
198
|
+
* `computedHash` (for `MIGRATION.HASH_MISMATCH`) alongside the stable
|
|
199
|
+
* `code`. The user-visible `summary`/`why`/`fix` text is unchanged.
|
|
200
|
+
*
|
|
201
|
+
* Callers are expected to gate on `MigrationToolsError.is(error)` first
|
|
202
|
+
* (mirroring the original inline pattern); non-`MigrationToolsError`
|
|
203
|
+
* values are caller-classified (rethrow, wrap with command-specific
|
|
204
|
+
* `errorUnexpected`, etc.).
|
|
205
|
+
*/
|
|
206
|
+
function mapMigrationToolsError(error) {
|
|
207
|
+
return errorRuntime(error.message, {
|
|
208
|
+
why: error.why,
|
|
209
|
+
fix: error.fix,
|
|
210
|
+
meta: {
|
|
211
|
+
code: error.code,
|
|
212
|
+
...error.details ?? {}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Shared "needs a live database" precondition for read verbs that consult the
|
|
218
|
+
* marker/ledger (`migration log`, `migration status`). A command needs both a
|
|
219
|
+
* connection string and a control-plane driver; either missing yields the same
|
|
220
|
+
* `CONFIG.DB_CONNECTION_REQUIRED` envelope with `meta.missingFlags` (canonical long-form flags
|
|
221
|
+
* per CLI Style Guide §Errors) so callers can react programmatically. Returns
|
|
222
|
+
* `null` when both are present.
|
|
223
|
+
*/
|
|
224
|
+
function requireLiveDatabase(args) {
|
|
225
|
+
if (args.dbConnection && args.hasDriver) return null;
|
|
226
|
+
const missingFlags = args.dbConnection ? [] : ["--db"];
|
|
227
|
+
return errorDatabaseConnectionRequired({
|
|
228
|
+
why: args.why,
|
|
229
|
+
missingFlags,
|
|
230
|
+
...ifDefined("commandName", args.commandName),
|
|
231
|
+
...ifDefined("retryCommand", args.retryCommand)
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Maps a `RefResolutionError` from the contract/migration reference
|
|
236
|
+
* resolver into a CLI structured error envelope.
|
|
237
|
+
*/
|
|
238
|
+
/**
|
|
239
|
+
* A migration ref (dirName or hash-prefix) resolves in more than one contract
|
|
240
|
+
* space. The user must qualify with `--space <id>` to disambiguate.
|
|
241
|
+
*/
|
|
242
|
+
function errorAmbiguousMigrationRef(ref, spaceIds) {
|
|
243
|
+
const spaceList = spaceIds.join(", ");
|
|
244
|
+
return errorRuntime(`Ambiguous migration reference: "${ref}" resolves in multiple spaces — qualify with --space <id>`, {
|
|
245
|
+
why: `"${ref}" matches migrations in spaces: ${spaceList}.`,
|
|
246
|
+
fix: `Qualify with --space <id> to select one space. Available matching spaces: ${spaceList}.`,
|
|
247
|
+
meta: {
|
|
248
|
+
code: "MIGRATION.AMBIGUOUS_MIGRATION_REF",
|
|
249
|
+
ref,
|
|
250
|
+
spaceIds: [...spaceIds]
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
function mapRefResolutionError(error) {
|
|
255
|
+
switch (error.kind) {
|
|
256
|
+
case "not-found": return errorRuntime(`Not a known ${error.grammar} reference: "${error.input}"`, {
|
|
257
|
+
why: `No ${error.grammar} matching "${error.input}" exists in the migration graph or refs index.`,
|
|
258
|
+
fix: error.grammar === "contract" ? "Provide a valid contract hash, ref name, or migration directory name." : "Provide a valid migration directory name or migration hash.",
|
|
259
|
+
meta: {
|
|
260
|
+
input: error.input,
|
|
261
|
+
grammar: error.grammar
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
case "ambiguous": return errorRuntime(`Ambiguous ${error.grammar} reference: "${error.input}"`, {
|
|
265
|
+
why: `"${error.input}" matches multiple ${error.grammar}s: ${error.candidates.join(", ")}`,
|
|
266
|
+
fix: "Provide a longer prefix or use the full hash to disambiguate.",
|
|
267
|
+
meta: {
|
|
268
|
+
input: error.input,
|
|
269
|
+
candidates: error.candidates,
|
|
270
|
+
grammar: error.grammar
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
case "wrong-grammar": return errorRuntime(error.message, {
|
|
274
|
+
why: error.message,
|
|
275
|
+
fix: error.fix,
|
|
276
|
+
meta: {
|
|
277
|
+
input: error.input,
|
|
278
|
+
expectedGrammar: error.expectedGrammar
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
case "invalid-format": return errorRuntime(`Invalid reference format: "${error.input}"`, {
|
|
282
|
+
why: error.reason,
|
|
283
|
+
fix: "Provide a valid contract hash, ref name, or migration directory name.",
|
|
284
|
+
meta: { input: error.input }
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Checks if verbose output is enabled at the specified level.
|
|
290
|
+
*/
|
|
291
|
+
function isVerbose(flags, level) {
|
|
292
|
+
return (flags.verbose ?? 0) >= level;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Creates a color-aware formatter function.
|
|
296
|
+
* Returns a function that applies the color only if colors are enabled.
|
|
297
|
+
*/
|
|
298
|
+
function createColorFormatter(useColor, colorFn) {
|
|
299
|
+
return useColor ? colorFn : (text) => text;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Formats text with dim styling if colors are enabled.
|
|
303
|
+
*/
|
|
304
|
+
function formatDim(useColor, text) {
|
|
305
|
+
return useColor ? dim(text) : text;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Fixed width for left column in help output.
|
|
309
|
+
*/
|
|
310
|
+
const LEFT_COLUMN_WIDTH$1 = 20;
|
|
311
|
+
/**
|
|
312
|
+
* Creates an arrow segment badge with green background and white text.
|
|
313
|
+
* Body: green background with white "prisma-next" text
|
|
314
|
+
* Tip: dark grey arrow pointing right (Powerline separator)
|
|
315
|
+
*/
|
|
316
|
+
function createPrismaNextBadge$1(useColor) {
|
|
317
|
+
if (!useColor) return "prisma-next";
|
|
318
|
+
return bold("prisma-next");
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Creates a padding function.
|
|
322
|
+
*/
|
|
323
|
+
function createPadFunction() {
|
|
324
|
+
return (s, w) => s + " ".repeat(Math.max(0, w - s.length));
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Formats a header line: brand + operation + intent
|
|
328
|
+
*/
|
|
329
|
+
function formatHeaderLine$1(options) {
|
|
330
|
+
if (options.operation) return `${options.brand} ${options.operation} → ${options.intent}`;
|
|
331
|
+
return `${options.brand} ${options.intent}`;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Formats a "Read more" URL line.
|
|
335
|
+
* The "Read more" label is in default color (not cyan), and the URL is blue.
|
|
336
|
+
*/
|
|
337
|
+
function formatReadMoreLine$1(options) {
|
|
338
|
+
const labelPadded = createPadFunction()("Read more", options.maxLabelWidth);
|
|
339
|
+
const valueColored = options.useColor ? blue(options.url) : options.url;
|
|
340
|
+
return `${options.formatDimText("│")} ${labelPadded} ${valueColored}`;
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Pads text to a fixed width, accounting for ANSI escape codes.
|
|
344
|
+
* Uses string-width to measure the actual display width.
|
|
345
|
+
*/
|
|
346
|
+
function padToFixedWidth(text, width) {
|
|
347
|
+
const actualWidth = stringWidth(text);
|
|
348
|
+
const padding = Math.max(0, width - actualWidth);
|
|
349
|
+
return text + " ".repeat(padding);
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Renders a command tree structure.
|
|
353
|
+
* Handles both single-level (subcommands of a command) and multi-level (top-level commands with subcommands) trees.
|
|
354
|
+
*/
|
|
355
|
+
function renderCommandTree(options) {
|
|
356
|
+
const { commands, useColor, formatDimText, hasItemsAfter, continuationPrefix } = options;
|
|
357
|
+
const lines = [];
|
|
358
|
+
if (commands.length === 0) return lines;
|
|
359
|
+
for (let i = 0; i < commands.length; i++) {
|
|
360
|
+
const cmd = commands[i];
|
|
361
|
+
if (!cmd) continue;
|
|
362
|
+
const subcommands = cmd.commands.filter((subcmd) => !subcmd.name().startsWith("_"));
|
|
363
|
+
const isLastCommand = i === commands.length - 1;
|
|
364
|
+
if (subcommands.length > 0) {
|
|
365
|
+
const treePrefix = `${isLastCommand && !hasItemsAfter ? formatDimText("└") : formatDimText("├")}─ `;
|
|
366
|
+
const treePrefixWidth = stringWidth(stripAnsi(treePrefix));
|
|
367
|
+
const remainingWidth = LEFT_COLUMN_WIDTH$1 - treePrefixWidth;
|
|
368
|
+
const commandNamePadded = padToFixedWidth(cmd.name(), remainingWidth);
|
|
369
|
+
const commandNameColored = useColor ? cyan(commandNamePadded) : commandNamePadded;
|
|
370
|
+
lines.push(`${formatDimText("│")} ${treePrefix}${commandNameColored}`);
|
|
371
|
+
for (let j = 0; j < subcommands.length; j++) {
|
|
372
|
+
const subcmd = subcommands[j];
|
|
373
|
+
if (!subcmd) continue;
|
|
374
|
+
const isLastSubcommand = j === subcommands.length - 1;
|
|
375
|
+
const shortDescription = subcmd.description() || "";
|
|
376
|
+
const treeChar = isLastSubcommand ? "└" : "├";
|
|
377
|
+
const continuation = continuationPrefix ?? (isLastCommand && isLastSubcommand && !hasItemsAfter ? " " : formatDimText("│"));
|
|
378
|
+
const subTreePrefix = `${continuation === " " ? " " : continuation} ${formatDimText(treeChar)}─ `;
|
|
379
|
+
const subTreePrefixWidth = stringWidth(stripAnsi(subTreePrefix));
|
|
380
|
+
const subRemainingWidth = LEFT_COLUMN_WIDTH$1 - subTreePrefixWidth;
|
|
381
|
+
const subcommandNamePadded = padToFixedWidth(subcmd.name(), subRemainingWidth);
|
|
382
|
+
const subcommandNameColored = useColor ? cyan(subcommandNamePadded) : subcommandNamePadded;
|
|
383
|
+
lines.push(`${formatDimText("│")} ${subTreePrefix}${subcommandNameColored} ${shortDescription}`);
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
const treePrefix = `${isLastCommand && !hasItemsAfter ? formatDimText("└") : formatDimText("├")}─ `;
|
|
387
|
+
const treePrefixWidth = stringWidth(stripAnsi(treePrefix));
|
|
388
|
+
const remainingWidth = LEFT_COLUMN_WIDTH$1 - treePrefixWidth;
|
|
389
|
+
const commandNamePadded = padToFixedWidth(cmd.name(), remainingWidth);
|
|
390
|
+
const commandNameColored = useColor ? cyan(commandNamePadded) : commandNamePadded;
|
|
391
|
+
const shortDescription = cmd.description() || "";
|
|
392
|
+
lines.push(`${formatDimText("│")} ${treePrefix}${commandNameColored} ${shortDescription}`);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return lines;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Formats the header in the new experimental visual style.
|
|
399
|
+
* This header appears at the start of command output, showing the operation,
|
|
400
|
+
* intent, documentation link, and parameters.
|
|
401
|
+
*/
|
|
402
|
+
function formatStyledHeader(options) {
|
|
403
|
+
const lines = [];
|
|
404
|
+
const useColor = options.flags.color !== false;
|
|
405
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
406
|
+
const brand = createPrismaNextBadge$1(useColor);
|
|
407
|
+
const operation = useColor ? bold(options.command) : options.command;
|
|
408
|
+
const intent = formatDimText(options.description);
|
|
409
|
+
lines.push(formatHeaderLine$1({
|
|
410
|
+
brand,
|
|
411
|
+
operation,
|
|
412
|
+
intent
|
|
413
|
+
}));
|
|
414
|
+
lines.push(formatDimText("│"));
|
|
415
|
+
for (const detail of options.details) {
|
|
416
|
+
const labelPadded = padToFixedWidth(`${detail.label}:`, LEFT_COLUMN_WIDTH$1);
|
|
417
|
+
const labelColored = useColor ? cyan(labelPadded) : labelPadded;
|
|
418
|
+
lines.push(`${formatDimText("│")} ${labelColored} ${detail.value}`);
|
|
419
|
+
}
|
|
420
|
+
if (options.url) {
|
|
421
|
+
lines.push(formatDimText("│"));
|
|
422
|
+
lines.push(formatReadMoreLine$1({
|
|
423
|
+
url: options.url,
|
|
424
|
+
maxLabelWidth: LEFT_COLUMN_WIDTH$1,
|
|
425
|
+
useColor,
|
|
426
|
+
formatDimText
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
429
|
+
lines.push(formatDimText("└"));
|
|
430
|
+
return `${lines.join("\n")}\n`;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Formats a success message in the styled output format.
|
|
434
|
+
*/
|
|
435
|
+
function formatSuccessMessage(flags) {
|
|
436
|
+
return `${createColorFormatter(flags.color !== false, green)("✔")} Success`;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Fixed width for left column in help output.
|
|
440
|
+
* Must match the value in styled.ts.
|
|
441
|
+
*/
|
|
442
|
+
const LEFT_COLUMN_WIDTH = 20;
|
|
443
|
+
/**
|
|
444
|
+
* Minimum width for right column wrapping in help output.
|
|
445
|
+
*/
|
|
446
|
+
const RIGHT_COLUMN_MIN_WIDTH = 40;
|
|
447
|
+
/**
|
|
448
|
+
* Maximum width for right column wrapping in help output (when terminal is wide enough).
|
|
449
|
+
*/
|
|
450
|
+
const RIGHT_COLUMN_MAX_WIDTH = 90;
|
|
451
|
+
/**
|
|
452
|
+
* Gets the terminal width, or returns a default if not available.
|
|
453
|
+
*/
|
|
454
|
+
function getTerminalWidth() {
|
|
455
|
+
const terminalWidth = process.stderr.columns || process.stdout.columns;
|
|
456
|
+
const envWidth = Number.parseInt(process.env["CLI_WIDTH"] || "", 10);
|
|
457
|
+
return terminalWidth || (Number.isFinite(envWidth) ? envWidth : 80);
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Calculates the available width for the right column based on terminal width.
|
|
461
|
+
*/
|
|
462
|
+
function calculateRightColumnWidth() {
|
|
463
|
+
const availableWidth = getTerminalWidth() - 2 - LEFT_COLUMN_WIDTH - 2;
|
|
464
|
+
return Math.max(RIGHT_COLUMN_MIN_WIDTH, Math.min(availableWidth, RIGHT_COLUMN_MAX_WIDTH));
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Creates the CLI brand badge.
|
|
468
|
+
*/
|
|
469
|
+
function createPrismaNextBadge(useColor) {
|
|
470
|
+
return useColor ? bold("prisma-next") : "prisma-next";
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Formats a header line: brand + operation + intent
|
|
474
|
+
*/
|
|
475
|
+
function formatHeaderLine(options) {
|
|
476
|
+
if (options.operation) return `${options.brand} ${options.operation} → ${options.intent}`;
|
|
477
|
+
return `${options.brand} ${options.intent}`;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Wraps text to fit within a specified width using wrap-ansi.
|
|
481
|
+
*/
|
|
482
|
+
function wrapTextAnsi(text, width) {
|
|
483
|
+
return wrapAnsi(text, width, {
|
|
484
|
+
hard: false,
|
|
485
|
+
trim: true
|
|
486
|
+
}).split("\n");
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Formats a default value as "default: <value>" with dimming.
|
|
490
|
+
*/
|
|
491
|
+
function formatDefaultValue(value, useColor) {
|
|
492
|
+
const defaultText = `default: ${String(value)}`;
|
|
493
|
+
return useColor ? dim(defaultText) : defaultText;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Formats a "Read more" URL line.
|
|
497
|
+
*/
|
|
498
|
+
function formatReadMoreLine(options) {
|
|
499
|
+
const labelPadded = `Read more${" ".repeat(Math.max(0, options.maxLabelWidth - 9))}`;
|
|
500
|
+
const valueColored = options.useColor ? blue(options.url) : options.url;
|
|
501
|
+
return `${options.formatDimText("│")} ${labelPadded} ${valueColored}`;
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Formats multiline description with "Prisma Next" in green.
|
|
505
|
+
*/
|
|
506
|
+
function formatMultilineDescription(options) {
|
|
507
|
+
const lines = [];
|
|
508
|
+
const formatGreen = (text) => options.useColor ? green(text) : text;
|
|
509
|
+
const wrapWidth = 24 + calculateRightColumnWidth() - 2;
|
|
510
|
+
for (const descLine of options.descriptionLines) {
|
|
511
|
+
const wrappedLines = wrapTextAnsi(descLine.replace(/Prisma Next/g, (match) => formatGreen(match)), wrapWidth);
|
|
512
|
+
for (const wrappedLine of wrappedLines) lines.push(`${options.formatDimText("│")} ${wrappedLine}`);
|
|
513
|
+
}
|
|
514
|
+
return lines;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Maps command paths to their documentation URLs.
|
|
518
|
+
*/
|
|
519
|
+
function getCommandDocsUrl(commandPath) {
|
|
520
|
+
return {
|
|
521
|
+
"contract emit": "https://pris.ly/contract-emit",
|
|
522
|
+
"contract infer": "https://pris.ly/contract-infer",
|
|
523
|
+
"db schema": "https://pris.ly/db-schema",
|
|
524
|
+
"db verify": "https://pris.ly/db-verify",
|
|
525
|
+
"db update": "https://pris.ly/db-update",
|
|
526
|
+
"migration plan": "https://pris.ly/migration-plan",
|
|
527
|
+
migrate: "https://pris.ly/migrate",
|
|
528
|
+
"migration show": "https://pris.ly/migration-show",
|
|
529
|
+
"migration status": "https://pris.ly/migration-status"
|
|
530
|
+
}[commandPath];
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* Builds the full command path from a command and its parents.
|
|
534
|
+
*/
|
|
535
|
+
function buildCommandPath(command) {
|
|
536
|
+
const parts = [];
|
|
537
|
+
let current = command;
|
|
538
|
+
while (current && current.name() !== "prisma-next") {
|
|
539
|
+
parts.unshift(current.name());
|
|
540
|
+
current = current.parent ?? void 0;
|
|
541
|
+
}
|
|
542
|
+
return parts.join(" ");
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Formats help output for a command using the styled format.
|
|
546
|
+
*/
|
|
547
|
+
function formatCommandHelp(options) {
|
|
548
|
+
const { command, flags } = options;
|
|
549
|
+
const lines = [];
|
|
550
|
+
const useColor = flags.color !== false;
|
|
551
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
552
|
+
const commandPath = buildCommandPath(command);
|
|
553
|
+
const shortDescription = command.description() || "";
|
|
554
|
+
const longDescription = getLongDescription(command);
|
|
555
|
+
const argsSuffix = command.registeredArguments.map((arg) => arg.required ? `<${arg.name()}>` : `[${arg.name()}]`).join(" ");
|
|
556
|
+
const brand = createPrismaNextBadge(useColor);
|
|
557
|
+
const commandWithArgs = argsSuffix ? `${commandPath} ${argsSuffix}` : commandPath;
|
|
558
|
+
const operation = useColor ? bold(commandWithArgs) : commandWithArgs;
|
|
559
|
+
const intent = formatDimText(shortDescription);
|
|
560
|
+
lines.push(formatHeaderLine({
|
|
561
|
+
brand,
|
|
562
|
+
operation,
|
|
563
|
+
intent
|
|
564
|
+
}));
|
|
565
|
+
lines.push(formatDimText("│"));
|
|
566
|
+
const optionsList = command.options.map((opt) => {
|
|
567
|
+
const description = opt.description || "";
|
|
568
|
+
const defaultValue = opt.defaultValue;
|
|
569
|
+
return {
|
|
570
|
+
flags: opt.flags,
|
|
571
|
+
description,
|
|
572
|
+
defaultValue
|
|
573
|
+
};
|
|
574
|
+
});
|
|
575
|
+
const subcommands = command.commands.filter((cmd) => !cmd.name().startsWith("_"));
|
|
576
|
+
if (subcommands.length > 0) {
|
|
577
|
+
const treeLines = renderCommandTree({
|
|
578
|
+
commands: subcommands,
|
|
579
|
+
useColor,
|
|
580
|
+
formatDimText,
|
|
581
|
+
hasItemsAfter: optionsList.length > 0
|
|
582
|
+
});
|
|
583
|
+
lines.push(...treeLines);
|
|
584
|
+
}
|
|
585
|
+
if (subcommands.length > 0 && optionsList.length > 0) lines.push(formatDimText("│"));
|
|
586
|
+
if (optionsList.length > 0) for (const opt of optionsList) {
|
|
587
|
+
const flagsPadded = padToFixedWidth(opt.flags, LEFT_COLUMN_WIDTH);
|
|
588
|
+
let flagsColored = flagsPadded;
|
|
589
|
+
if (useColor) {
|
|
590
|
+
flagsColored = flagsPadded.replace(/(<[^>]+>)/g, (match) => magenta(match));
|
|
591
|
+
flagsColored = cyan(flagsColored);
|
|
592
|
+
}
|
|
593
|
+
const rightColumnWidth = calculateRightColumnWidth();
|
|
594
|
+
const wrappedDescription = wrapTextAnsi(opt.description, rightColumnWidth);
|
|
595
|
+
lines.push(`${formatDimText("│")} ${flagsColored} ${wrappedDescription[0] || ""}`);
|
|
596
|
+
for (let i = 1; i < wrappedDescription.length; i++) {
|
|
597
|
+
const emptyLabel = " ".repeat(LEFT_COLUMN_WIDTH);
|
|
598
|
+
lines.push(`${formatDimText("│")} ${emptyLabel} ${wrappedDescription[i] || ""}`);
|
|
599
|
+
}
|
|
600
|
+
if (opt.defaultValue !== void 0) {
|
|
601
|
+
const emptyLabel = " ".repeat(LEFT_COLUMN_WIDTH);
|
|
602
|
+
const defaultText = formatDefaultValue(opt.defaultValue, useColor);
|
|
603
|
+
lines.push(`${formatDimText("│")} ${emptyLabel} ${defaultText}`);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
const docsUrl = getCommandDocsUrl(commandPath);
|
|
607
|
+
if (docsUrl) {
|
|
608
|
+
lines.push(formatDimText("│"));
|
|
609
|
+
lines.push(formatReadMoreLine({
|
|
610
|
+
url: docsUrl,
|
|
611
|
+
maxLabelWidth: LEFT_COLUMN_WIDTH,
|
|
612
|
+
useColor,
|
|
613
|
+
formatDimText
|
|
614
|
+
}));
|
|
615
|
+
}
|
|
616
|
+
const examples = getCommandExamples(command);
|
|
617
|
+
if (examples && examples.length > 0) {
|
|
618
|
+
lines.push(formatDimText("│"));
|
|
619
|
+
lines.push(`${formatDimText("│")} ${formatDimText("Examples:")}`);
|
|
620
|
+
for (const example of examples) lines.push(`${formatDimText("│")} ${useColor ? dim("$") : "$"} ${example}`);
|
|
621
|
+
}
|
|
622
|
+
const seeAlso = getCommandSeeAlso(command);
|
|
623
|
+
if (seeAlso && seeAlso.length > 0) {
|
|
624
|
+
lines.push(formatDimText("│"));
|
|
625
|
+
lines.push(`${formatDimText("│")} ${formatDimText("See also:")}`);
|
|
626
|
+
for (const ref of seeAlso) lines.push(`${formatDimText("│")} ${ref.verb} ${formatDimText(ref.oneLiner)}`);
|
|
627
|
+
}
|
|
628
|
+
if (longDescription) {
|
|
629
|
+
lines.push(formatDimText("│"));
|
|
630
|
+
const descriptionLines = longDescription.split("\n").filter((line) => line.trim().length > 0);
|
|
631
|
+
lines.push(...formatMultilineDescription({
|
|
632
|
+
descriptionLines,
|
|
633
|
+
useColor,
|
|
634
|
+
formatDimText
|
|
635
|
+
}));
|
|
636
|
+
}
|
|
637
|
+
lines.push(formatDimText("└"));
|
|
638
|
+
return `${lines.join("\n")}\n`;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Formats help output for the root program using the styled format.
|
|
642
|
+
*/
|
|
643
|
+
function formatRootHelp(options) {
|
|
644
|
+
const { program, flags } = options;
|
|
645
|
+
const lines = [];
|
|
646
|
+
const useColor = flags.color !== false;
|
|
647
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
648
|
+
const brand = createPrismaNextBadge(useColor);
|
|
649
|
+
const intent = formatDimText("Manage your data layer");
|
|
650
|
+
lines.push(formatHeaderLine({
|
|
651
|
+
brand,
|
|
652
|
+
operation: "",
|
|
653
|
+
intent
|
|
654
|
+
}));
|
|
655
|
+
lines.push(formatDimText("│"));
|
|
656
|
+
const topLevelCommands = program.commands.filter((cmd) => !cmd.name().startsWith("_") && cmd.name() !== "help");
|
|
657
|
+
const globalOptions = program.options.map((opt) => {
|
|
658
|
+
const description = opt.description || "";
|
|
659
|
+
const defaultValue = opt.defaultValue;
|
|
660
|
+
return {
|
|
661
|
+
flags: opt.flags,
|
|
662
|
+
description,
|
|
663
|
+
defaultValue
|
|
664
|
+
};
|
|
665
|
+
});
|
|
666
|
+
if (topLevelCommands.length > 0) {
|
|
667
|
+
const treeLines = renderCommandTree({
|
|
668
|
+
commands: topLevelCommands,
|
|
669
|
+
useColor,
|
|
670
|
+
formatDimText,
|
|
671
|
+
hasItemsAfter: globalOptions.length > 0
|
|
672
|
+
});
|
|
673
|
+
lines.push(...treeLines);
|
|
674
|
+
}
|
|
675
|
+
if (topLevelCommands.length > 0 && globalOptions.length > 0) lines.push(formatDimText("│"));
|
|
676
|
+
if (globalOptions.length > 0) for (const opt of globalOptions) {
|
|
677
|
+
const flagsPadded = padToFixedWidth(opt.flags, LEFT_COLUMN_WIDTH);
|
|
678
|
+
let flagsColored = flagsPadded;
|
|
679
|
+
if (useColor) {
|
|
680
|
+
flagsColored = flagsPadded.replace(/(<[^>]+>)/g, (match) => magenta(match));
|
|
681
|
+
flagsColored = cyan(flagsColored);
|
|
682
|
+
}
|
|
683
|
+
const rightColumnWidth = calculateRightColumnWidth();
|
|
684
|
+
const wrappedDescription = wrapTextAnsi(opt.description, rightColumnWidth);
|
|
685
|
+
lines.push(`${formatDimText("│")} ${flagsColored} ${wrappedDescription[0] || ""}`);
|
|
686
|
+
for (let i = 1; i < wrappedDescription.length; i++) {
|
|
687
|
+
const emptyLabel = " ".repeat(LEFT_COLUMN_WIDTH);
|
|
688
|
+
lines.push(`${formatDimText("│")} ${emptyLabel} ${wrappedDescription[i] || ""}`);
|
|
689
|
+
}
|
|
690
|
+
if (opt.defaultValue !== void 0) {
|
|
691
|
+
const emptyLabel = " ".repeat(LEFT_COLUMN_WIDTH);
|
|
692
|
+
const defaultText = formatDefaultValue(opt.defaultValue, useColor);
|
|
693
|
+
lines.push(`${formatDimText("│")} ${emptyLabel} ${defaultText}`);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
const formatGreen = (text) => useColor ? green(text) : text;
|
|
697
|
+
const descriptionLines = [`Use ${formatGreen("Prisma Next")} to define your data layer as a contract. Sign your database and application with the same contract to guarantee compatibility. Plan and apply migrations to safely evolve your schema.`];
|
|
698
|
+
if (descriptionLines.length > 0) {
|
|
699
|
+
lines.push(formatDimText("│"));
|
|
700
|
+
lines.push(...formatMultilineDescription({
|
|
701
|
+
descriptionLines,
|
|
702
|
+
useColor,
|
|
703
|
+
formatDimText
|
|
704
|
+
}));
|
|
705
|
+
}
|
|
706
|
+
lines.push(formatDimText("└"));
|
|
707
|
+
return `${lines.join("\n")}\n`;
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Returns true when the process is running in any CI environment recognised
|
|
711
|
+
* by the `ci-info` package. The single source of truth for CI detection
|
|
712
|
+
* across this CLI — colour-output suppression and telemetry-skip both call
|
|
713
|
+
* this helper, so neither path drifts from the other when a new CI provider
|
|
714
|
+
* is added upstream.
|
|
715
|
+
*
|
|
716
|
+
* `ci-info` checks the standard `CI=true` marker plus dozens of
|
|
717
|
+
* provider-specific environment variables (Buildkite, Jenkins, Drone,
|
|
718
|
+
* Bitbucket Pipelines, Azure Pipelines, AWS CodeBuild, …) that the raw
|
|
719
|
+
* `process.env.CI` read misses.
|
|
720
|
+
*
|
|
721
|
+
*/
|
|
722
|
+
function isCI$1() {
|
|
723
|
+
return isCI;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Render a single statement of an `OperationPreview` for the human-readable
|
|
727
|
+
* preview block. SQL statements get a trailing `;` if missing so the rendered
|
|
728
|
+
* preview is byte-identical to the legacy `string[]`-based renderer for SQL
|
|
729
|
+
* targets. Other languages (`'mongodb-shell'`) render verbatim.
|
|
730
|
+
*/
|
|
731
|
+
function renderPreviewStatement(text, language) {
|
|
732
|
+
const trimmed = text.trim();
|
|
733
|
+
if (!trimmed) return void 0;
|
|
734
|
+
if (language === "sql") return trimmed.endsWith(";") ? trimmed : `${trimmed};`;
|
|
735
|
+
return trimmed;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Choose the header label for a preview block. SQL-only previews keep the
|
|
739
|
+
* legacy `DDL preview` label so the rendered output is byte-identical to the
|
|
740
|
+
* pre-aggregate SQL CLI; previews from any other family — or a mix that
|
|
741
|
+
* includes any non-SQL language — use the family-agnostic `Operation preview`
|
|
742
|
+
* label.
|
|
743
|
+
*
|
|
744
|
+
* An empty `statements` array deliberately renders as `Operation preview`
|
|
745
|
+
* rather than `DDL preview`: `Array.prototype.every` is vacuously true for
|
|
746
|
+
* empty arrays, but we have no evidence the preview is SQL-only when no
|
|
747
|
+
* statements are present, so the family-agnostic label is the safer default.
|
|
748
|
+
*/
|
|
749
|
+
function previewBlockHeader(preview) {
|
|
750
|
+
return preview.statements.length > 0 && preview.statements.every((s) => s.language === "sql") ? "DDL preview" : "Operation preview";
|
|
751
|
+
}
|
|
752
|
+
function formatPlannerWarningsBlock(warnings, useColor) {
|
|
753
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
754
|
+
const lines = ["", "Warnings:"];
|
|
755
|
+
for (const warning of warnings) lines.push(` ${formatDimText(`- ${warning.summary}`)}`);
|
|
756
|
+
return lines;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Render the shared per-space execution block consumed by the `db init`
|
|
760
|
+
* / `db update` / `migrate` summaries. Always shows: space
|
|
761
|
+
* label (`Extension space: <id>` or `App space`) → per-op lines under
|
|
762
|
+
* each space → per-space marker hash (when known).
|
|
763
|
+
*
|
|
764
|
+
* `mode` controls the marker label phrasing — `'apply'` shows
|
|
765
|
+
* `marker → <hash>` (post-apply), `'plan'` omits the marker line
|
|
766
|
+
* entirely (no marker has been written yet).
|
|
767
|
+
*/
|
|
768
|
+
function formatPerSpaceBlock(perSpace, mode, useColor) {
|
|
769
|
+
const formatYellow = createColorFormatter(useColor, yellow);
|
|
770
|
+
const formatCyan = createColorFormatter(useColor, cyan);
|
|
771
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
772
|
+
const lines = [];
|
|
773
|
+
for (let s = 0; s < perSpace.length; s++) {
|
|
774
|
+
const space = perSpace[s];
|
|
775
|
+
if (s > 0) lines.push("");
|
|
776
|
+
const header = space.kind === "app" ? formatCyan("App space") : formatCyan(`Extension space: ${space.spaceId}`);
|
|
777
|
+
lines.push(header);
|
|
778
|
+
if (space.operations.length === 0) lines.push(` ${formatDimText("(no operations)")}`);
|
|
779
|
+
else for (let i = 0; i < space.operations.length; i++) {
|
|
780
|
+
const op = space.operations[i];
|
|
781
|
+
const treeChar = i === space.operations.length - 1 ? "└" : "├";
|
|
782
|
+
const destructiveMarker = op.operationClass === "destructive" ? ` ${formatYellow("(destructive)")}` : "";
|
|
783
|
+
lines.push(` ${formatDimText(treeChar)}─ ${op.label}${destructiveMarker}`);
|
|
784
|
+
}
|
|
785
|
+
if (mode === "apply" && space.marker) lines.push(` ${formatDimText(`marker: ${space.marker.storageHash}`)}`);
|
|
786
|
+
}
|
|
787
|
+
return lines;
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Formats human-readable output for migration commands (db init, db update) in plan mode.
|
|
791
|
+
*/
|
|
792
|
+
function formatMigrationPlanOutput(result, flags) {
|
|
793
|
+
if (flags.quiet) return "";
|
|
794
|
+
const lines = [];
|
|
795
|
+
const useColor = flags.color !== false;
|
|
796
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
797
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
798
|
+
const operationCount = result.plan?.operations.length ?? 0;
|
|
799
|
+
const spaceCount = result.perSpace?.length ?? 0;
|
|
800
|
+
if (spaceCount > 0) lines.push(`${formatGreen("✔")} Planned ${operationCount} operation(s) across ${spaceCount} contract space${spaceCount === 1 ? "" : "s"}`);
|
|
801
|
+
else lines.push(`${formatGreen("✔")} Planned ${operationCount} operation(s)`);
|
|
802
|
+
if (result.warnings && result.warnings.length > 0) lines.push(...formatPlannerWarningsBlock(result.warnings, useColor));
|
|
803
|
+
const formatYellow = createColorFormatter(useColor, yellow);
|
|
804
|
+
if (result.perSpace && result.perSpace.length > 0) {
|
|
805
|
+
lines.push("");
|
|
806
|
+
lines.push(...formatPerSpaceBlock(result.perSpace, "plan", useColor));
|
|
807
|
+
if (result.perSpace.some((s) => s.operations.some((op) => op.operationClass === "destructive"))) {
|
|
808
|
+
lines.push("");
|
|
809
|
+
lines.push(`${formatYellow("⚠")} This migration contains destructive operations that may cause data loss.`);
|
|
810
|
+
}
|
|
811
|
+
} else if (result.plan?.operations && result.plan.operations.length > 0) {
|
|
812
|
+
lines.push(`${formatDimText("│")}`);
|
|
813
|
+
for (let i = 0; i < result.plan.operations.length; i++) {
|
|
814
|
+
const op = result.plan.operations[i];
|
|
815
|
+
if (!op) continue;
|
|
816
|
+
const treeChar = i === result.plan.operations.length - 1 ? "└" : "├";
|
|
817
|
+
const destructiveMarker = op.operationClass === "destructive" ? ` ${formatYellow("(destructive)")}` : "";
|
|
818
|
+
lines.push(`${formatDimText(treeChar)}─ ${op.label}${destructiveMarker}`);
|
|
819
|
+
}
|
|
820
|
+
if (result.plan.operations.some((op) => op.operationClass === "destructive")) {
|
|
821
|
+
lines.push("");
|
|
822
|
+
lines.push(`${formatYellow("⚠")} This migration contains destructive operations that may cause data loss.`);
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (result.plan?.destination) {
|
|
826
|
+
lines.push("");
|
|
827
|
+
lines.push(`${formatDimText(`Destination hash: ${result.plan.destination.storageHash}`)}`);
|
|
828
|
+
}
|
|
829
|
+
if (result.plannedAdvanceRef) {
|
|
830
|
+
lines.push("");
|
|
831
|
+
lines.push(formatDimText(`Would advance ref "${result.plannedAdvanceRef.name}" → ${result.plannedAdvanceRef.hash}`));
|
|
832
|
+
}
|
|
833
|
+
const preview = result.plan?.preview;
|
|
834
|
+
if (preview) {
|
|
835
|
+
lines.push("");
|
|
836
|
+
lines.push(`${formatDimText(previewBlockHeader(preview))}`);
|
|
837
|
+
if (preview.statements.length === 0) lines.push(`${formatDimText("No operations.")}`);
|
|
838
|
+
else {
|
|
839
|
+
lines.push("");
|
|
840
|
+
for (const statement of preview.statements) {
|
|
841
|
+
const rendered = renderPreviewStatement(statement.text, statement.language);
|
|
842
|
+
if (rendered) lines.push(rendered);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
if (isVerbose(flags, 1)) lines.push(`${formatDimText(`Total time: ${result.timings.total}ms`)}`);
|
|
847
|
+
lines.push("");
|
|
848
|
+
lines.push(`${formatDimText("This is a dry run. No changes were applied.")}`);
|
|
849
|
+
lines.push(`${formatDimText("Run without --dry-run to apply changes.")}`);
|
|
850
|
+
return lines.join("\n");
|
|
851
|
+
}
|
|
852
|
+
function formatMigrationApplyCommandOutput(result, flags) {
|
|
853
|
+
if (flags.quiet) return "";
|
|
854
|
+
const lines = [];
|
|
855
|
+
const useColor = flags.color !== false;
|
|
856
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
857
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
858
|
+
lines.push(`${formatGreen("✔")} ${result.summary}`);
|
|
859
|
+
if (result.perSpace.length > 0) {
|
|
860
|
+
lines.push("");
|
|
861
|
+
for (const line of formatPerSpaceBlock(result.perSpace, "apply", useColor)) lines.push(line);
|
|
862
|
+
}
|
|
863
|
+
if (result.advancedRef) {
|
|
864
|
+
lines.push("");
|
|
865
|
+
lines.push(formatDimText(`Advanced ref "${result.advancedRef.name}" → ${result.advancedRef.hash}`));
|
|
866
|
+
}
|
|
867
|
+
lines.push("");
|
|
868
|
+
lines.push(formatDimText("Next: prisma-next migration status"));
|
|
869
|
+
if (isVerbose(flags, 1) && result.timings) {
|
|
870
|
+
lines.push("");
|
|
871
|
+
lines.push(formatDimText(`Total time: ${result.timings.total}ms`));
|
|
872
|
+
}
|
|
873
|
+
return lines.join("\n");
|
|
874
|
+
}
|
|
875
|
+
function formatSpaceShowBlock(space, useColor) {
|
|
876
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
877
|
+
const formatYellow = createColorFormatter(useColor, yellow);
|
|
878
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
879
|
+
const lines = [];
|
|
880
|
+
lines.push(`${formatGreen("✔")} ${space.name}`);
|
|
881
|
+
lines.push(`${formatDimText(` from: ${space.fromContract ?? "(baseline)"}`)}`);
|
|
882
|
+
lines.push(`${formatDimText(` to: ${space.toContract}`)}`);
|
|
883
|
+
lines.push(`${formatDimText(` hash: ${space.hash}`)}`);
|
|
884
|
+
lines.push(`${formatDimText(` created: ${space.createdAt}`)}`);
|
|
885
|
+
lines.push("");
|
|
886
|
+
lines.push(`${space.operations.length} operation(s)`);
|
|
887
|
+
if (space.operations.length > 0) {
|
|
888
|
+
lines.push(`${formatDimText("│")}`);
|
|
889
|
+
for (let i = 0; i < space.operations.length; i++) {
|
|
890
|
+
const op = space.operations[i];
|
|
891
|
+
const treeChar = i === space.operations.length - 1 ? "└" : "├";
|
|
892
|
+
const destructiveMarker = op.operationClass === "destructive" ? ` ${formatYellow("(destructive)")}` : "";
|
|
893
|
+
lines.push(`${formatDimText(treeChar)}─ ${op.label}${destructiveMarker}`);
|
|
894
|
+
}
|
|
895
|
+
if (space.operations.some((op) => op.operationClass === "destructive")) {
|
|
896
|
+
lines.push("");
|
|
897
|
+
lines.push(`${formatYellow("⚠")} This migration contains destructive operations that may cause data loss.`);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
if (space.preview.statements.length > 0) {
|
|
901
|
+
lines.push("");
|
|
902
|
+
lines.push(`${formatDimText(previewBlockHeader(space.preview))}`);
|
|
903
|
+
lines.push("");
|
|
904
|
+
for (const statement of space.preview.statements) {
|
|
905
|
+
const rendered = renderPreviewStatement(statement.text, statement.language);
|
|
906
|
+
if (rendered) lines.push(rendered);
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
return lines;
|
|
910
|
+
}
|
|
911
|
+
function formatMigrationShowOutput(result, flags) {
|
|
912
|
+
if (flags.quiet) return "";
|
|
913
|
+
const useColor = flags.color !== false;
|
|
914
|
+
return formatSpaceShowBlock(result.migration, useColor).join("\n");
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Formats human-readable output for migration commands (db init, db update) in apply mode.
|
|
918
|
+
*/
|
|
919
|
+
function formatMigrationApplyOutput(result, flags) {
|
|
920
|
+
if (flags.quiet) return "";
|
|
921
|
+
const lines = [];
|
|
922
|
+
const useColor = flags.color !== false;
|
|
923
|
+
const formatGreen = createColorFormatter(useColor, green);
|
|
924
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
925
|
+
if (result.ok) {
|
|
926
|
+
const executed = result.execution?.operationsExecuted ?? 0;
|
|
927
|
+
const spaceCount = result.perSpace?.length ?? 0;
|
|
928
|
+
if (executed === 0) {
|
|
929
|
+
const acrossClause = spaceCount > 0 ? ` across ${spaceCount} contract space${spaceCount === 1 ? "" : "s"}` : "";
|
|
930
|
+
lines.push(`${formatGreen("✔")} Database already matches contract${acrossClause}`);
|
|
931
|
+
} else if (spaceCount > 0) lines.push(`${formatGreen("✔")} Applied ${executed} operation(s) across ${spaceCount} contract space${spaceCount === 1 ? "" : "s"}`);
|
|
932
|
+
else lines.push(`${formatGreen("✔")} Applied ${executed} operation(s)`);
|
|
933
|
+
if (result.warnings && result.warnings.length > 0) lines.push(...formatPlannerWarningsBlock(result.warnings, useColor));
|
|
934
|
+
if (result.perSpace && result.perSpace.length > 0) {
|
|
935
|
+
lines.push("");
|
|
936
|
+
lines.push(...formatPerSpaceBlock(result.perSpace, "apply", useColor));
|
|
937
|
+
lines.push("");
|
|
938
|
+
lines.push(formatDimText(`Run 'prisma-next migration status' to confirm ${spaceCount === 1 ? "the space is" : "all spaces are"} up to date.`));
|
|
939
|
+
} else if (result.marker) {
|
|
940
|
+
lines.push(`${formatDimText(` App-space marker: ${result.marker.storageHash}`)}`);
|
|
941
|
+
if (result.marker.profileHash) lines.push(`${formatDimText(` Profile hash: ${result.marker.profileHash}`)}`);
|
|
942
|
+
}
|
|
943
|
+
if (isVerbose(flags, 1)) lines.push(`${formatDimText(` Total time: ${result.timings.total}ms`)}`);
|
|
944
|
+
if (result.advancedRef) lines.push(formatDimText(`Advanced ref "${result.advancedRef.name}" → ${result.advancedRef.hash}`));
|
|
945
|
+
}
|
|
946
|
+
return lines.join("\n");
|
|
947
|
+
}
|
|
948
|
+
/**
|
|
949
|
+
* Formats JSON output for migration commands (db init, db update).
|
|
950
|
+
*/
|
|
951
|
+
function formatMigrationJson(result) {
|
|
952
|
+
return JSON.stringify(result, null, 2);
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* The display label for a schema-diff issue in the shared error envelope,
|
|
956
|
+
* derived from which sides are present: expected-only is a missing object,
|
|
957
|
+
* actual-only an extra one, both a mismatch. `undefined` when the entry is not
|
|
958
|
+
* a schema-diff issue (it carries neither side) so the caller can fall through
|
|
959
|
+
* to its generic label.
|
|
960
|
+
*/
|
|
961
|
+
function schemaDiffIssueLabel(issue) {
|
|
962
|
+
const hasExpected = issue.expected !== void 0;
|
|
963
|
+
const hasActual = issue.actual !== void 0;
|
|
964
|
+
if (hasExpected && hasActual) return "mismatch";
|
|
965
|
+
if (hasExpected) return "missing";
|
|
966
|
+
if (hasActual) return "extra";
|
|
967
|
+
}
|
|
968
|
+
/**
|
|
969
|
+
* Formats error output for human-readable display.
|
|
970
|
+
*/
|
|
971
|
+
function formatErrorOutput(error, flags) {
|
|
972
|
+
const lines = [];
|
|
973
|
+
const useColor = flags.color !== false;
|
|
974
|
+
const formatRed = createColorFormatter(useColor, red);
|
|
975
|
+
const formatDimText = (text) => formatDim(useColor, text);
|
|
976
|
+
lines.push(`${formatRed("✖")} ${error.summary} (${error.code})`);
|
|
977
|
+
if (error.why) lines.push(`${formatDimText(` Why: ${error.why}`)}`);
|
|
978
|
+
if (error.fix) lines.push(`${formatDimText(` Fix: ${error.fix}`)}`);
|
|
979
|
+
if (error.where?.path) {
|
|
980
|
+
const whereLine = error.where.line ? `${error.where.path}:${error.where.line}` : error.where.path;
|
|
981
|
+
lines.push(`${formatDimText(` Where: ${whereLine}`)}`);
|
|
982
|
+
}
|
|
983
|
+
if (error.meta?.["conflicts"]) {
|
|
984
|
+
const conflicts = error.meta["conflicts"];
|
|
985
|
+
if (conflicts.length > 0) {
|
|
986
|
+
const maxToShow = isVerbose(flags, 1) ? conflicts.length : Math.min(3, conflicts.length);
|
|
987
|
+
const header = isVerbose(flags, 1) ? " Conflicts:" : ` Conflicts (showing ${maxToShow} of ${conflicts.length}):`;
|
|
988
|
+
lines.push(`${formatDimText(header)}`);
|
|
989
|
+
for (const conflict of conflicts.slice(0, maxToShow)) lines.push(`${formatDimText(` - [${conflict.kind}] ${conflict.summary}`)}`);
|
|
990
|
+
if (!isVerbose(flags, 1) && conflicts.length > maxToShow) lines.push(`${formatDimText(" Re-run with -v/--verbose to see all conflicts")}`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
if (error.meta?.["issues"]) {
|
|
994
|
+
const issues = error.meta["issues"];
|
|
995
|
+
if (issues.length > 0) {
|
|
996
|
+
const maxToShow = isVerbose(flags, 1) ? issues.length : Math.min(3, issues.length);
|
|
997
|
+
const header = isVerbose(flags, 1) ? " Issues:" : ` Issues (showing ${maxToShow} of ${issues.length}):`;
|
|
998
|
+
lines.push(`${formatDimText(header)}`);
|
|
999
|
+
for (const issue of issues.slice(0, maxToShow)) {
|
|
1000
|
+
const label = issue.kind ?? schemaDiffIssueLabel(issue) ?? "issue";
|
|
1001
|
+
const message = issue.message ?? issue.path?.join("/") ?? "";
|
|
1002
|
+
lines.push(`${formatDimText(` - [${label}] ${message}`)}`);
|
|
1003
|
+
}
|
|
1004
|
+
if (!isVerbose(flags, 1) && issues.length > maxToShow) lines.push(`${formatDimText(" Re-run with -v/--verbose to see all issues")}`);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if (error.docsUrl && isVerbose(flags, 1)) lines.push(formatDimText(error.docsUrl));
|
|
1008
|
+
const plannerWarnings = error.meta?.["plannerWarnings"];
|
|
1009
|
+
if (Array.isArray(plannerWarnings) && plannerWarnings.length > 0) {
|
|
1010
|
+
const typedWarnings = blindCast(plannerWarnings);
|
|
1011
|
+
lines.push(...formatPlannerWarningsBlock(typedWarnings, useColor));
|
|
1012
|
+
}
|
|
1013
|
+
if (isVerbose(flags, 2) && error.meta) lines.push(`${formatDimText(` Meta: ${JSON.stringify(error.meta, null, 2)}`)}`);
|
|
1014
|
+
return lines.join("\n");
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Formats error output as JSON.
|
|
1018
|
+
*/
|
|
1019
|
+
function formatErrorJson(error) {
|
|
1020
|
+
return JSON.stringify(error, null, 2);
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Processes a CLI command result, handling both success and error cases.
|
|
1024
|
+
* Formats output appropriately and returns the exit code.
|
|
1025
|
+
* Never throws - returns exit code for commands to use with process.exit().
|
|
1026
|
+
*
|
|
1027
|
+
* Error output:
|
|
1028
|
+
* - JSON mode: JSON error to stdout (piped) via ui.output(), human sees nothing on stderr.
|
|
1029
|
+
* - Interactive: human-readable error to stderr.
|
|
1030
|
+
*/
|
|
1031
|
+
function handleResult(result, flags, ui, onSuccess) {
|
|
1032
|
+
if (result.ok) {
|
|
1033
|
+
if (onSuccess) onSuccess(result.value);
|
|
1034
|
+
return 0;
|
|
1035
|
+
}
|
|
1036
|
+
const envelope = result.failure.toEnvelope();
|
|
1037
|
+
if (flags.json) ui.output(formatErrorJson(envelope));
|
|
1038
|
+
else ui.error(formatErrorOutput(envelope, flags));
|
|
1039
|
+
return result.failure.code === "CLI.INIT_USER_ABORTED" ? 3 : 2;
|
|
1040
|
+
}
|
|
1041
|
+
function localeString(env) {
|
|
1042
|
+
return env["LC_ALL"] ?? env["LC_CTYPE"] ?? env["LANG"] ?? "";
|
|
1043
|
+
}
|
|
1044
|
+
function isUtf8Locale(env) {
|
|
1045
|
+
const locale = localeString(env);
|
|
1046
|
+
if (locale.length === 0) return false;
|
|
1047
|
+
return /UTF-8|utf8/i.test(locale);
|
|
1048
|
+
}
|
|
1049
|
+
function detectGlyphMode(input) {
|
|
1050
|
+
if (!input.isTTY) return "ascii";
|
|
1051
|
+
if (!isUtf8Locale(input.env)) return "ascii";
|
|
1052
|
+
return "unicode";
|
|
1053
|
+
}
|
|
1054
|
+
function createShutdownHandler(options) {
|
|
1055
|
+
const exit = options?.exit ?? ((code) => process.exit(code));
|
|
1056
|
+
const gracePeriodMs = options?.gracePeriodMs ?? 3e3;
|
|
1057
|
+
const controller = new AbortController();
|
|
1058
|
+
let shuttingDown = false;
|
|
1059
|
+
let graceTimer;
|
|
1060
|
+
const onSignal = () => {
|
|
1061
|
+
if (shuttingDown) {
|
|
1062
|
+
exit(130);
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
shuttingDown = true;
|
|
1066
|
+
controller.abort();
|
|
1067
|
+
graceTimer = setTimeout(() => exit(130), gracePeriodMs);
|
|
1068
|
+
graceTimer.unref();
|
|
1069
|
+
};
|
|
1070
|
+
return {
|
|
1071
|
+
signal: controller.signal,
|
|
1072
|
+
isShuttingDown: () => shuttingDown,
|
|
1073
|
+
onSignal,
|
|
1074
|
+
clearGraceTimer: () => {
|
|
1075
|
+
if (graceTimer !== void 0) {
|
|
1076
|
+
clearTimeout(graceTimer);
|
|
1077
|
+
graceTimer = void 0;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
const globalHandler = createShutdownHandler();
|
|
1083
|
+
/**
|
|
1084
|
+
* The global AbortSignal. Pass this to any async operation that should
|
|
1085
|
+
* be cancellable on Ctrl+C (e.g., DB queries, long-running emit).
|
|
1086
|
+
*/
|
|
1087
|
+
const shutdownSignal = globalHandler.signal;
|
|
1088
|
+
/**
|
|
1089
|
+
* Installs SIGINT and SIGTERM handlers. Call once at CLI startup.
|
|
1090
|
+
*
|
|
1091
|
+
* - First signal: aborts the controller, starts a 3s grace timer.
|
|
1092
|
+
* - Second signal: force-exits immediately.
|
|
1093
|
+
*/
|
|
1094
|
+
let installed = false;
|
|
1095
|
+
function installShutdownHandlers() {
|
|
1096
|
+
if (installed) return;
|
|
1097
|
+
installed = true;
|
|
1098
|
+
process.on("SIGINT", globalHandler.onSignal);
|
|
1099
|
+
process.on("SIGTERM", globalHandler.onSignal);
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Composable CLI output abstraction.
|
|
1103
|
+
*
|
|
1104
|
+
* Follows the Unix convention of separating data from decoration:
|
|
1105
|
+
* - **stdout** — data output only (`ui.output()`). This is what scripts and pipes capture.
|
|
1106
|
+
* - **stderr** — all decoration (spinners, logs, notes, intro/outro). Visible in terminal, invisible in pipes.
|
|
1107
|
+
*
|
|
1108
|
+
* Rules:
|
|
1109
|
+
* 1. All methods except `output()` and `error()` write to stderr only in interactive mode.
|
|
1110
|
+
* 2. `output(data)` always writes to stdout — if a command calls it, there is data to emit.
|
|
1111
|
+
* 3. `error()` always writes to stderr — errors matter even when piped.
|
|
1112
|
+
* 4. Decoration is suppressed when piped unless `--format pretty` was explicit (`forcePretty`).
|
|
1113
|
+
* 5. Never write data to stderr — decoration methods are for human context only.
|
|
1114
|
+
* 6. Never write decoration to stdout — it breaks pipes, `$(...)` captures, and `> file` redirects.
|
|
1115
|
+
*/
|
|
1116
|
+
var TerminalUI = class TerminalUI {
|
|
1117
|
+
/**
|
|
1118
|
+
* True when stdout is a TTY (interactive terminal).
|
|
1119
|
+
* False when piped (e.g., `prisma-next db verify | jq`).
|
|
1120
|
+
*/
|
|
1121
|
+
isInteractive;
|
|
1122
|
+
/**
|
|
1123
|
+
* Whether color output is enabled.
|
|
1124
|
+
*/
|
|
1125
|
+
useColor;
|
|
1126
|
+
/**
|
|
1127
|
+
* When true, decoration methods write even on non-TTY stdout
|
|
1128
|
+
* (explicit `--format pretty`).
|
|
1129
|
+
*/
|
|
1130
|
+
forcePretty;
|
|
1131
|
+
/**
|
|
1132
|
+
* Whether stdout is a TTY — used for migration-list graph glyph detection.
|
|
1133
|
+
*/
|
|
1134
|
+
stdoutIsTTY;
|
|
1135
|
+
/**
|
|
1136
|
+
* Process environment snapshot for locale-aware glyph detection.
|
|
1137
|
+
*/
|
|
1138
|
+
env;
|
|
1139
|
+
static stderrOpts = { output: process.stderr };
|
|
1140
|
+
constructor(options) {
|
|
1141
|
+
this.isInteractive = options?.interactive ?? !!process.stdout.isTTY;
|
|
1142
|
+
this.forcePretty = options?.forcePretty ?? false;
|
|
1143
|
+
this.useColor = options?.color ?? (this.isInteractive || this.forcePretty);
|
|
1144
|
+
this.stdoutIsTTY = options?.stdoutIsTTY ?? !!process.stdout.isTTY;
|
|
1145
|
+
this.env = options?.env ?? process.env;
|
|
1146
|
+
}
|
|
1147
|
+
get isTTY() {
|
|
1148
|
+
return this.stdoutIsTTY;
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* Resolve glyph mode for migration list/tree output. `--ascii` forces ASCII;
|
|
1152
|
+
* otherwise delegates to the pure {@link detectGlyphMode} helper.
|
|
1153
|
+
*/
|
|
1154
|
+
resolveGlyphMode(forceAscii) {
|
|
1155
|
+
if (forceAscii) return "ascii";
|
|
1156
|
+
return detectGlyphMode(this.glyphModeInput());
|
|
1157
|
+
}
|
|
1158
|
+
glyphModeInput() {
|
|
1159
|
+
return {
|
|
1160
|
+
isTTY: this.stdoutIsTTY,
|
|
1161
|
+
env: this.env
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
get shouldDecorate() {
|
|
1165
|
+
return this.isInteractive || this.forcePretty;
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Log a message line to stderr. No-op when piped unless forcePretty.
|
|
1169
|
+
*/
|
|
1170
|
+
log(message) {
|
|
1171
|
+
if (!this.shouldDecorate) return;
|
|
1172
|
+
clack.log.message(message, TerminalUI.stderrOpts);
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Log a success message to stderr. No-op when piped unless forcePretty.
|
|
1176
|
+
*/
|
|
1177
|
+
success(message) {
|
|
1178
|
+
if (!this.shouldDecorate) return;
|
|
1179
|
+
clack.log.success(message, TerminalUI.stderrOpts);
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* Log a warning message to stderr. No-op when piped unless forcePretty.
|
|
1183
|
+
*/
|
|
1184
|
+
warn(message) {
|
|
1185
|
+
if (!this.shouldDecorate) return;
|
|
1186
|
+
clack.log.warn(message, TerminalUI.stderrOpts);
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Log an error message to stderr. Always writes (errors matter even in pipes).
|
|
1190
|
+
*/
|
|
1191
|
+
error(message) {
|
|
1192
|
+
clack.log.error(message, TerminalUI.stderrOpts);
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Log an info message to stderr. No-op when piped unless forcePretty.
|
|
1196
|
+
*/
|
|
1197
|
+
info(message) {
|
|
1198
|
+
if (!this.shouldDecorate) return;
|
|
1199
|
+
clack.log.info(message, TerminalUI.stderrOpts);
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Log a step message to stderr. No-op when piped unless forcePretty.
|
|
1203
|
+
*/
|
|
1204
|
+
step(message) {
|
|
1205
|
+
if (!this.shouldDecorate) return;
|
|
1206
|
+
clack.log.step(message, TerminalUI.stderrOpts);
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Display a note box on stderr. No-op when piped unless forcePretty.
|
|
1210
|
+
*/
|
|
1211
|
+
note(message, title) {
|
|
1212
|
+
if (!this.shouldDecorate) return;
|
|
1213
|
+
clack.note(message, title, TerminalUI.stderrOpts);
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Display intro banner on stderr. No-op when piped unless forcePretty.
|
|
1217
|
+
*/
|
|
1218
|
+
intro(title) {
|
|
1219
|
+
if (!this.shouldDecorate) return;
|
|
1220
|
+
clack.intro(title, TerminalUI.stderrOpts);
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* Display outro banner on stderr. No-op when piped unless forcePretty.
|
|
1224
|
+
*/
|
|
1225
|
+
outro(message) {
|
|
1226
|
+
if (!this.shouldDecorate) return;
|
|
1227
|
+
clack.outro(message, TerminalUI.stderrOpts);
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Create a Clack spinner on stderr with a 100ms delay threshold.
|
|
1231
|
+
* The spinner only appears if the operation takes longer than the threshold,
|
|
1232
|
+
* avoiding flicker for fast operations. Returns a no-op spinner when not decorating.
|
|
1233
|
+
*/
|
|
1234
|
+
spinner(delayMs = 100) {
|
|
1235
|
+
const noop = {
|
|
1236
|
+
start: () => {},
|
|
1237
|
+
stop: () => {},
|
|
1238
|
+
cancel: () => {},
|
|
1239
|
+
error: () => {},
|
|
1240
|
+
message: () => {},
|
|
1241
|
+
clear: () => {},
|
|
1242
|
+
get isCancelled() {
|
|
1243
|
+
return false;
|
|
1244
|
+
}
|
|
1245
|
+
};
|
|
1246
|
+
if (!this.shouldDecorate) return noop;
|
|
1247
|
+
let inner;
|
|
1248
|
+
let timer;
|
|
1249
|
+
let pendingMsg;
|
|
1250
|
+
let settled = false;
|
|
1251
|
+
const ensureCleared = () => {
|
|
1252
|
+
if (timer !== void 0) {
|
|
1253
|
+
clearTimeout(timer);
|
|
1254
|
+
timer = void 0;
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
const onAbort = () => {
|
|
1258
|
+
if (!settled) {
|
|
1259
|
+
settled = true;
|
|
1260
|
+
ensureCleared();
|
|
1261
|
+
if (inner) inner.cancel("Interrupted");
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
if (!shutdownSignal.aborted) shutdownSignal.addEventListener("abort", onAbort, { once: true });
|
|
1265
|
+
return {
|
|
1266
|
+
start(msg) {
|
|
1267
|
+
pendingMsg = msg;
|
|
1268
|
+
timer = setTimeout(() => {
|
|
1269
|
+
if (!settled) {
|
|
1270
|
+
inner = clack.spinner(TerminalUI.stderrOpts);
|
|
1271
|
+
inner.start(pendingMsg);
|
|
1272
|
+
}
|
|
1273
|
+
}, delayMs);
|
|
1274
|
+
},
|
|
1275
|
+
stop(msg) {
|
|
1276
|
+
settled = true;
|
|
1277
|
+
ensureCleared();
|
|
1278
|
+
if (inner) inner.stop(msg);
|
|
1279
|
+
},
|
|
1280
|
+
cancel(msg) {
|
|
1281
|
+
settled = true;
|
|
1282
|
+
ensureCleared();
|
|
1283
|
+
if (inner) inner.cancel(msg);
|
|
1284
|
+
},
|
|
1285
|
+
error(msg) {
|
|
1286
|
+
settled = true;
|
|
1287
|
+
ensureCleared();
|
|
1288
|
+
if (inner) inner.error(msg);
|
|
1289
|
+
},
|
|
1290
|
+
message(msg) {
|
|
1291
|
+
pendingMsg = msg;
|
|
1292
|
+
if (inner) inner.message(msg);
|
|
1293
|
+
},
|
|
1294
|
+
clear() {
|
|
1295
|
+
settled = true;
|
|
1296
|
+
ensureCleared();
|
|
1297
|
+
if (inner) inner.clear();
|
|
1298
|
+
},
|
|
1299
|
+
get isCancelled() {
|
|
1300
|
+
return inner?.isCancelled ?? false;
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
/**
|
|
1305
|
+
* Prompt for yes/no confirmation on stderr. Returns true if confirmed.
|
|
1306
|
+
* In non-interactive mode or when cancelled (Ctrl-C), returns false.
|
|
1307
|
+
*/
|
|
1308
|
+
async confirm(message) {
|
|
1309
|
+
if (!this.isInteractive) return false;
|
|
1310
|
+
const result = await clack.confirm({
|
|
1311
|
+
message,
|
|
1312
|
+
...TerminalUI.stderrOpts
|
|
1313
|
+
});
|
|
1314
|
+
if (clack.isCancel(result)) return false;
|
|
1315
|
+
return result;
|
|
1316
|
+
}
|
|
1317
|
+
/**
|
|
1318
|
+
* Write a raw line to stderr. No-op when piped unless forcePretty.
|
|
1319
|
+
* Use for decoration that doesn't fit Clack's log format (e.g. styled headers).
|
|
1320
|
+
*/
|
|
1321
|
+
stderr(message) {
|
|
1322
|
+
if (!this.shouldDecorate) return;
|
|
1323
|
+
process.stderr.write(`${message}\n`);
|
|
1324
|
+
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Write machine-readable data to stdout.
|
|
1327
|
+
* Always writes — if a command calls output(), there is data to emit.
|
|
1328
|
+
*
|
|
1329
|
+
* This is what scripts and pipes capture: `prisma-next db verify --json | jq .ok`
|
|
1330
|
+
*/
|
|
1331
|
+
output(data) {
|
|
1332
|
+
process.stdout.write(`${data}\n`);
|
|
1333
|
+
}
|
|
1334
|
+
green(text) {
|
|
1335
|
+
return this.useColor ? green(text) : text;
|
|
1336
|
+
}
|
|
1337
|
+
red(text) {
|
|
1338
|
+
return this.useColor ? red(text) : text;
|
|
1339
|
+
}
|
|
1340
|
+
cyan(text) {
|
|
1341
|
+
return this.useColor ? cyan(text) : text;
|
|
1342
|
+
}
|
|
1343
|
+
dim(text) {
|
|
1344
|
+
return this.useColor ? dim(text) : text;
|
|
1345
|
+
}
|
|
1346
|
+
bold(text) {
|
|
1347
|
+
return this.useColor ? bold(text) : text;
|
|
1348
|
+
}
|
|
1349
|
+
yellow(text) {
|
|
1350
|
+
return this.useColor ? yellow(text) : text;
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
function createTerminalUI(flags, runtime) {
|
|
1354
|
+
return new TerminalUI({
|
|
1355
|
+
color: flags.color,
|
|
1356
|
+
interactive: flags.interactive,
|
|
1357
|
+
forcePretty: flags.format === "pretty" && flags.explicitFormat,
|
|
1358
|
+
stdoutIsTTY: runtime?.isTTY,
|
|
1359
|
+
env: runtime?.env
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
function isJsonFlagSet(json) {
|
|
1363
|
+
return json === true;
|
|
1364
|
+
}
|
|
1365
|
+
function resolveOutputFormat(options) {
|
|
1366
|
+
const formatOption = options.format;
|
|
1367
|
+
const jsonFlag = isJsonFlagSet(options.json);
|
|
1368
|
+
if (formatOption !== void 0) {
|
|
1369
|
+
if (formatOption !== "pretty" && formatOption !== "json") throw errorInvalidOutputFormat(formatOption);
|
|
1370
|
+
if (jsonFlag && formatOption === "pretty") throw errorOutputFormatMutex();
|
|
1371
|
+
return {
|
|
1372
|
+
format: formatOption,
|
|
1373
|
+
explicitFormat: true
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
if (jsonFlag) return {
|
|
1377
|
+
format: "json",
|
|
1378
|
+
explicitFormat: false
|
|
1379
|
+
};
|
|
1380
|
+
if (!process.stdout.isTTY) return {
|
|
1381
|
+
format: "json",
|
|
1382
|
+
explicitFormat: false
|
|
1383
|
+
};
|
|
1384
|
+
return {
|
|
1385
|
+
format: "pretty",
|
|
1386
|
+
explicitFormat: false
|
|
1387
|
+
};
|
|
1388
|
+
}
|
|
1389
|
+
function inferJsonModeForParseError(options) {
|
|
1390
|
+
if (options.format === "json") return true;
|
|
1391
|
+
if (isJsonFlagSet(options.json) && options.format !== "pretty") return true;
|
|
1392
|
+
if (options.format !== void 0) return false;
|
|
1393
|
+
return !process.stdout.isTTY;
|
|
1394
|
+
}
|
|
1395
|
+
function emitGlobalFlagParseError(error, options) {
|
|
1396
|
+
const jsonMode = inferJsonModeForParseError(options);
|
|
1397
|
+
const flags = {
|
|
1398
|
+
format: jsonMode ? "json" : "pretty",
|
|
1399
|
+
explicitFormat: false,
|
|
1400
|
+
...jsonMode ? { json: true } : {},
|
|
1401
|
+
color: false,
|
|
1402
|
+
verbose: 0,
|
|
1403
|
+
interactive: false
|
|
1404
|
+
};
|
|
1405
|
+
const ui = createTerminalUI(flags);
|
|
1406
|
+
const exitCode = handleResult(notOk(error), flags, ui);
|
|
1407
|
+
process.exit(exitCode);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Parses global flags from CLI options.
|
|
1411
|
+
* Handles verbosity flags (-v, --trace), output format (--format, --json),
|
|
1412
|
+
* quiet mode, color, interactivity (--interactive/--no-interactive), and
|
|
1413
|
+
* auto-accept (-y/--yes).
|
|
1414
|
+
*
|
|
1415
|
+
* On invalid or conflicting format flags, prints a structured CLI error
|
|
1416
|
+
* envelope and exits with code 2.
|
|
1417
|
+
*/
|
|
1418
|
+
function parseGlobalFlagsOrExit(options) {
|
|
1419
|
+
try {
|
|
1420
|
+
return parseGlobalFlags(options);
|
|
1421
|
+
} catch (error) {
|
|
1422
|
+
if (CliStructuredError$1.is(error)) emitGlobalFlagParseError(error, options);
|
|
1423
|
+
throw error;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Parses global flags from CLI options.
|
|
1428
|
+
* Handles verbosity flags (-v, --trace), output format (--format, --json),
|
|
1429
|
+
* quiet mode, color, interactivity (--interactive/--no-interactive), and
|
|
1430
|
+
* auto-accept (-y/--yes).
|
|
1431
|
+
*
|
|
1432
|
+
* Throws {@link CliStructuredError} for invalid or conflicting format flags.
|
|
1433
|
+
*/
|
|
1434
|
+
function parseGlobalFlags(options) {
|
|
1435
|
+
const { format, explicitFormat } = resolveOutputFormat(options);
|
|
1436
|
+
const flags = {
|
|
1437
|
+
format,
|
|
1438
|
+
explicitFormat
|
|
1439
|
+
};
|
|
1440
|
+
if (format === "json") flags.json = true;
|
|
1441
|
+
if (options.quiet || options.q) flags.quiet = true;
|
|
1442
|
+
if (options.trace || process.env["PRISMA_NEXT_TRACE"] === "1") flags.verbose = 2;
|
|
1443
|
+
else if (options.verbose || options.v || process.env["PRISMA_NEXT_DEBUG"] === "1") flags.verbose = 1;
|
|
1444
|
+
else flags.verbose = 0;
|
|
1445
|
+
if (process.env["NO_COLOR"] || flags.json) flags.color = false;
|
|
1446
|
+
else if (options["no-color"]) flags.color = false;
|
|
1447
|
+
else if (options.color !== void 0) flags.color = options.color;
|
|
1448
|
+
else flags.color = process.stdout.isTTY && !isCI$1();
|
|
1449
|
+
if (options["no-interactive"]) flags.interactive = false;
|
|
1450
|
+
else if (options.interactive !== void 0) flags.interactive = options.interactive;
|
|
1451
|
+
else flags.interactive = !!process.stdout.isTTY;
|
|
1452
|
+
if (options.yes || options.y) flags.yes = true;
|
|
1453
|
+
return flags;
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Bridges the two TTY checks (stdout via `flags`, stdin via
|
|
1457
|
+
* `process.stdin.isTTY`) into the `canPrompt` boolean the interactive
|
|
1458
|
+
* `init` flow consumes.
|
|
1459
|
+
*
|
|
1460
|
+
* Per the [Style Guide § Interactivity](../../../../../../../docs/CLI%20Style%20Guide.md#interactivity):
|
|
1461
|
+
*
|
|
1462
|
+
* - `flags.interactive` governs *decoration* (TerminalUI, intro/outro,
|
|
1463
|
+
* spinners) and is derived from stdout-TTY by `parseGlobalFlags`,
|
|
1464
|
+
* honouring `--interactive` / `--no-interactive`.
|
|
1465
|
+
* - Prompting additionally requires a stdin TTY — closing stdin is a
|
|
1466
|
+
* common signal in CI / agent environments even when stdout stays
|
|
1467
|
+
* attached.
|
|
1468
|
+
* - `--interactive` is the explicit override: when the user passes it,
|
|
1469
|
+
* we honour it (e.g. testing flows where stdin is stubbed).
|
|
1470
|
+
*
|
|
1471
|
+
* Single source of truth for the interactive-prompt decision: both the
|
|
1472
|
+
* `init` action handler and the preAction telemetry bridge derive
|
|
1473
|
+
* prompt-eligibility from this helper so they cannot drift. Lives in
|
|
1474
|
+
* `global-flags` (alongside `parseGlobalFlags`) to keep
|
|
1475
|
+
* `utils/telemetry` and `commands/init/index` free of an import cycle.
|
|
1476
|
+
*
|
|
1477
|
+
* Exported so callers and tests can derive the same value without
|
|
1478
|
+
* touching `process` globals.
|
|
1479
|
+
*/
|
|
1480
|
+
function deriveCanPrompt(opts) {
|
|
1481
|
+
if (opts.optionInteractive === true) return true;
|
|
1482
|
+
if (opts.flagsInteractive === false) return false;
|
|
1483
|
+
return opts.stdinIsTTY;
|
|
1484
|
+
}
|
|
1485
|
+
const longDescriptions = /* @__PURE__ */ new WeakMap();
|
|
1486
|
+
const commandExamples = /* @__PURE__ */ new WeakMap();
|
|
1487
|
+
const commandSeeAlso = /* @__PURE__ */ new WeakMap();
|
|
1488
|
+
/**
|
|
1489
|
+
* Sets both short and long descriptions for a command.
|
|
1490
|
+
* The short description is used in command trees and headers.
|
|
1491
|
+
* The long description is shown at the bottom of help output.
|
|
1492
|
+
*/
|
|
1493
|
+
function setCommandDescriptions(command, shortDescription, longDescription) {
|
|
1494
|
+
command.description(shortDescription);
|
|
1495
|
+
if (longDescription) longDescriptions.set(command, longDescription);
|
|
1496
|
+
return command;
|
|
1497
|
+
}
|
|
1498
|
+
/**
|
|
1499
|
+
* Sets copy-pastable examples for a command, shown in help text.
|
|
1500
|
+
*/
|
|
1501
|
+
function setCommandExamples(command, examples) {
|
|
1502
|
+
commandExamples.set(command, examples);
|
|
1503
|
+
return command;
|
|
1504
|
+
}
|
|
1505
|
+
/**
|
|
1506
|
+
* Gets the long description from a command if it was set via setCommandDescriptions.
|
|
1507
|
+
*/
|
|
1508
|
+
function getLongDescription(command) {
|
|
1509
|
+
return longDescriptions.get(command);
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Gets examples from a command if set via setCommandExamples.
|
|
1513
|
+
*/
|
|
1514
|
+
function getCommandExamples(command) {
|
|
1515
|
+
return commandExamples.get(command);
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* Sets cross-references to related commands, rendered in a "See also"
|
|
1519
|
+
* section below the Examples block in help output.
|
|
1520
|
+
*/
|
|
1521
|
+
function setCommandSeeAlso(command, refs) {
|
|
1522
|
+
commandSeeAlso.set(command, refs);
|
|
1523
|
+
return command;
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* Gets the see-also cross-references from a command.
|
|
1527
|
+
*/
|
|
1528
|
+
function getCommandSeeAlso(command) {
|
|
1529
|
+
return commandSeeAlso.get(command);
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* Resolves the absolute path to contract.json from the config.
|
|
1533
|
+
*/
|
|
1534
|
+
function resolveContractPath(config) {
|
|
1535
|
+
if (config.contract?.output === void 0) throw errorRuntime$1("config.contract.output is required to resolve the contract path", {
|
|
1536
|
+
why: "CLI commands read the emitted contract from config.contract.output; the config has no value to read.",
|
|
1537
|
+
fix: "Ensure your prisma-next.config.ts goes through `defineConfig()`, which normalises a default output when the provider supplies an input path, or set `contract.output` explicitly."
|
|
1538
|
+
});
|
|
1539
|
+
return resolve(config.contract.output);
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Resolves the migrations directory and config path from CLI options.
|
|
1543
|
+
* Shared by migrate, migration-plan, and migration-status.
|
|
1544
|
+
*
|
|
1545
|
+
* - `migrationsDir` is the project's top-level `migrations/` directory
|
|
1546
|
+
* (the root that the aggregate loader walks for every contract space).
|
|
1547
|
+
* - `appMigrationsDir` is the app subspace directory under it
|
|
1548
|
+
* (`<migrationsDir>/<APP_SPACE_ID>/`). Every per-app reader / writer
|
|
1549
|
+
* (`migration new`, `migration plan`, `migrate`,
|
|
1550
|
+
* `migration status`, `migration show`, `migration ref`) operates on
|
|
1551
|
+
* this directory. Extensions own their own `migrations/<spaceId>/`.
|
|
1552
|
+
* - `refsDir` is the app's refs directory (`<appMigrationsDir>/refs/`).
|
|
1553
|
+
* The framework does not maintain refs at the migrations root.
|
|
1554
|
+
*/
|
|
1555
|
+
function resolveMigrationPaths(configOption, config) {
|
|
1556
|
+
const configPath = configOption ? relative(process.cwd(), resolve(configOption)) : "prisma-next.config.ts";
|
|
1557
|
+
const migrationsDir = resolve(configOption ? resolve(configOption, "..") : process.cwd(), config.migrations?.dir ?? "migrations");
|
|
1558
|
+
const migrationsRelative = relative(process.cwd(), migrationsDir);
|
|
1559
|
+
const appMigrationsDir = spaceMigrationDirectory(migrationsDir, APP_SPACE_ID$1);
|
|
1560
|
+
return {
|
|
1561
|
+
configPath,
|
|
1562
|
+
migrationsDir,
|
|
1563
|
+
migrationsRelative,
|
|
1564
|
+
appMigrationsDir,
|
|
1565
|
+
appMigrationsRelative: relative(process.cwd(), appMigrationsDir),
|
|
1566
|
+
refsDir: resolve(appMigrationsDir, "refs")
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
function collectDeclaredInvariants(graph) {
|
|
1570
|
+
const declared = /* @__PURE__ */ new Set();
|
|
1571
|
+
for (const edges of graph.forwardChain.values()) for (const edge of edges) for (const inv of edge.invariants) declared.add(inv);
|
|
1572
|
+
return declared;
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* Maps a `MigrationEdge` to the structural-edge shape used in the
|
|
1576
|
+
* `MIGRATION.NO_INVARIANT_PATH` error envelope. Shared between
|
|
1577
|
+
* `migrate` and `migration status` so both commands surface
|
|
1578
|
+
* the same JSON wire shape when an invariant-aware route is unsatisfiable.
|
|
1579
|
+
*/
|
|
1580
|
+
function toStructuralEdge(edge) {
|
|
1581
|
+
return {
|
|
1582
|
+
dirName: edge.dirName,
|
|
1583
|
+
migrationHash: edge.migrationHash,
|
|
1584
|
+
from: edge.from,
|
|
1585
|
+
to: edge.to,
|
|
1586
|
+
invariants: edge.invariants
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
function targetSupportsMigrations(target) {
|
|
1590
|
+
return hasMigrations(target);
|
|
1591
|
+
}
|
|
1592
|
+
function getTargetMigrations(target) {
|
|
1593
|
+
return hasMigrations(target) ? target.migrations : void 0;
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* Reads and parses contract.json, validating the framework-level envelope
|
|
1597
|
+
* fields (storageHash, schemaVersion, target, targetFamily).
|
|
1598
|
+
*
|
|
1599
|
+
* Family-specific validation (storage structure, codec mappings, etc.)
|
|
1600
|
+
* happens downstream in the control client via the family instance.
|
|
1601
|
+
*/
|
|
1602
|
+
async function readContractEnvelope(config) {
|
|
1603
|
+
const contractPath = resolveContractPath(config);
|
|
1604
|
+
const content = await readFile(contractPath, "utf-8");
|
|
1605
|
+
const json = JSON.parse(content);
|
|
1606
|
+
const { schemaVersion, target, targetFamily, profileHash } = json;
|
|
1607
|
+
const storageHash = json["storage"]?.["storageHash"];
|
|
1608
|
+
if (typeof storageHash !== "string") throw new CliStructuredError$1("CONTRACT.VALIDATION_FAILED", `Contract at ${relative(process.cwd(), contractPath)} is missing a valid storage.storageHash. Run \`prisma-next contract emit\` to regenerate.`, { where: { path: contractPath } });
|
|
1609
|
+
if (typeof schemaVersion !== "string") throw new CliStructuredError$1("CONTRACT.VALIDATION_FAILED", `Contract at ${relative(process.cwd(), contractPath)} is missing schemaVersion.`, { where: { path: contractPath } });
|
|
1610
|
+
if (typeof target !== "string") throw new CliStructuredError$1("CONTRACT.VALIDATION_FAILED", `Contract at ${relative(process.cwd(), contractPath)} is missing target.`, { where: { path: contractPath } });
|
|
1611
|
+
if (typeof targetFamily !== "string") throw new CliStructuredError$1("CONTRACT.VALIDATION_FAILED", `Contract at ${relative(process.cwd(), contractPath)} is missing targetFamily.`, { where: { path: contractPath } });
|
|
1612
|
+
return {
|
|
1613
|
+
...json,
|
|
1614
|
+
storageHash,
|
|
1615
|
+
schemaVersion,
|
|
1616
|
+
target,
|
|
1617
|
+
targetFamily,
|
|
1618
|
+
...typeof profileHash === "string" ? { profileHash } : {}
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
/**
|
|
1622
|
+
* Masks credentials in a database connection URL.
|
|
1623
|
+
* Handles standard URLs (username + password + query params) and libpq-style key=value strings.
|
|
1624
|
+
*/
|
|
1625
|
+
function maskConnectionUrl(url) {
|
|
1626
|
+
try {
|
|
1627
|
+
const parsed = new URL(url);
|
|
1628
|
+
if (parsed.username) parsed.username = "****";
|
|
1629
|
+
if (parsed.password) parsed.password = "****";
|
|
1630
|
+
for (const key of [...parsed.searchParams.keys()]) if (/password/i.test(key)) parsed.searchParams.set(key, "****");
|
|
1631
|
+
return parsed.toString();
|
|
1632
|
+
} catch {
|
|
1633
|
+
return url.replace(/password\s*=\s*\S+/gi, "password=****").replace(/user\s*=\s*\S+/gi, "user=****");
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Strips raw connection URL fragments from an error message to prevent credential leakage.
|
|
1638
|
+
* Call this before surfacing driver errors to the user.
|
|
1639
|
+
*/
|
|
1640
|
+
function sanitizeErrorMessage(message, connectionUrl) {
|
|
1641
|
+
if (!connectionUrl) return message;
|
|
1642
|
+
try {
|
|
1643
|
+
const parsed = new URL(connectionUrl);
|
|
1644
|
+
let sanitized = message;
|
|
1645
|
+
sanitized = sanitized.replaceAll(connectionUrl, maskConnectionUrl(connectionUrl));
|
|
1646
|
+
if (parsed.password) sanitized = sanitized.replaceAll(parsed.password, "****");
|
|
1647
|
+
if (parsed.username) sanitized = sanitized.replaceAll(parsed.username, "****");
|
|
1648
|
+
return sanitized;
|
|
1649
|
+
} catch {
|
|
1650
|
+
return message.replace(/password\s*=\s*\S+/gi, "password=****").replace(/user\s*=\s*\S+/gi, "user=****");
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
/**
|
|
1654
|
+
* Registers the global CLI options shared by every command:
|
|
1655
|
+
* --format, --json, -q/--quiet, -v/--verbose, --trace, --color, --no-color,
|
|
1656
|
+
* --interactive, --no-interactive, -y/--yes.
|
|
1657
|
+
*
|
|
1658
|
+
* Also sets up the styled help formatter.
|
|
1659
|
+
*/
|
|
1660
|
+
function addGlobalOptions(command) {
|
|
1661
|
+
return command.configureHelp({ formatHelp: (cmd) => {
|
|
1662
|
+
return formatCommandHelp({
|
|
1663
|
+
command: cmd,
|
|
1664
|
+
flags: parseGlobalFlags({})
|
|
1665
|
+
});
|
|
1666
|
+
} }).option("--format <pretty|json>", "Output format (default: pretty, or json when stdout is not a TTY)").option("--json", "Output as JSON (alias for --format json)").option("-q, --quiet", "Quiet mode: errors only").option("-v, --verbose", "Verbose output: debug info, timings").option("--trace", "Trace output: deep internals, stack traces").option("--color", "Force color output").option("--no-color", "Disable color output").option("--interactive", "Force interactive mode").option("--no-interactive", "Disable interactive prompts").option("-y, --yes", "Auto-accept prompts");
|
|
1667
|
+
}
|
|
1668
|
+
//#endregion
|
|
1669
|
+
export { maskConnectionUrl as $, errorSpaceNotFound as A, formatMigrationJson as B, errorPlanForgotTheFlag as C, errorRunnerFailed as D, errorRefSetHashNotInGraph as E, formatDim as F, formatSuccessMessage as G, formatMigrationShowOutput as H, formatErrorJson as I, installShutdownHandlers as J, getTargetMigrations as K, formatErrorOutput as L, errorTargetMismatch as M, errorUnexpected$1 as N, errorRuntime$1 as O, formatCommandHelp as P, mapRefResolutionError as Q, formatMigrationApplyCommandOutput as R, errorPathUnreachable as S, errorRefSetEmptySentinel as T, formatRootHelp as U, formatMigrationPlanOutput as V, formatStyledHeader as W, isVerbose as X, isCI$1 as Y, mapMigrationToolsError as Z, errorInvalidSpaceId as _, createColorFormatter as a, resolveMigrationPaths as at, errorMarkerMissing as b, errorAmbiguousMigrationRef as c, setCommandExamples as ct, errorContractValidationFailed as d, toStructuralEdge as dt, parseGlobalFlags as et, errorDatabaseConnectionRequired as f, errorHashMismatch as g, errorFileNotFound as h, collectDeclaredInvariants as i, resolveContractPath as it, errorTargetMigrationNotSupported as j, errorSnapshotMissing as k, errorConfigValidation$1 as l, setCommandSeeAlso as lt, errorDriverRequired as m, ERROR_CODE_DESTRUCTIVE_CHANGES as n, readContractEnvelope as nt, createTerminalUI as o, sanitizeErrorMessage as ot, errorDestructiveChanges as p, handleResult as q, addGlobalOptions as r, requireLiveDatabase as rt, deriveCanPrompt as s, setCommandDescriptions as st, CliStructuredError$1 as t, parseGlobalFlagsOrExit as tt, errorContractConfigMissing$1 as u, targetSupportsMigrations as ut, errorLegendHumanOnly as v, errorRefSetBundleNotFound as w, errorMigrationPlanningFailed as x, errorMarkerMismatch as y, formatMigrationApplyOutput as z };
|
|
1670
|
+
|
|
1671
|
+
//# sourceMappingURL=command-helpers-CAoXqAY9-DWUBcTXM.mjs.map
|