@prisma-next/cli 0.3.0-dev.15 → 0.3.0-dev.162
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 +381 -128
- package/dist/cli-errors-BDCYR5ap.mjs +4 -0
- package/dist/cli-errors-Dzs7Oxz7.d.mts +3 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.js +1 -2346
- package/dist/cli.mjs +245 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/client-yYtotiSX.mjs +1063 -0
- package/dist/client-yYtotiSX.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts +7 -0
- package/dist/commands/contract-emit.d.mts.map +1 -0
- package/dist/commands/contract-emit.mjs +8 -0
- package/dist/commands/contract-infer.d.mts +7 -0
- package/dist/commands/contract-infer.d.mts.map +1 -0
- package/dist/commands/contract-infer.mjs +9 -0
- package/dist/commands/db-init.d.mts +7 -0
- package/dist/commands/db-init.d.mts.map +1 -0
- package/dist/commands/db-init.mjs +125 -0
- package/dist/commands/db-init.mjs.map +1 -0
- package/dist/commands/db-schema.d.mts +7 -0
- package/dist/commands/db-schema.d.mts.map +1 -0
- package/dist/commands/db-schema.mjs +55 -0
- package/dist/commands/db-schema.mjs.map +1 -0
- package/dist/commands/db-sign.d.mts +7 -0
- package/dist/commands/db-sign.d.mts.map +1 -0
- package/dist/commands/db-sign.mjs +136 -0
- package/dist/commands/db-sign.mjs.map +1 -0
- package/dist/commands/db-update.d.mts +7 -0
- package/dist/commands/db-update.d.mts.map +1 -0
- package/dist/commands/db-update.mjs +122 -0
- package/dist/commands/db-update.mjs.map +1 -0
- package/dist/commands/db-verify.d.mts +7 -0
- package/dist/commands/db-verify.d.mts.map +1 -0
- package/dist/commands/db-verify.mjs +322 -0
- package/dist/commands/db-verify.mjs.map +1 -0
- package/dist/commands/migration-apply.d.mts +36 -0
- package/dist/commands/migration-apply.d.mts.map +1 -0
- package/dist/commands/migration-apply.mjs +244 -0
- package/dist/commands/migration-apply.mjs.map +1 -0
- package/dist/commands/migration-new.d.mts +8 -0
- package/dist/commands/migration-new.d.mts.map +1 -0
- package/dist/commands/migration-new.mjs +151 -0
- package/dist/commands/migration-new.mjs.map +1 -0
- package/dist/commands/migration-plan.d.mts +47 -0
- package/dist/commands/migration-plan.d.mts.map +1 -0
- package/dist/commands/migration-plan.mjs +312 -0
- package/dist/commands/migration-plan.mjs.map +1 -0
- package/dist/commands/migration-ref.d.mts +43 -0
- package/dist/commands/migration-ref.d.mts.map +1 -0
- package/dist/commands/migration-ref.mjs +194 -0
- package/dist/commands/migration-ref.mjs.map +1 -0
- package/dist/commands/migration-show.d.mts +28 -0
- package/dist/commands/migration-show.d.mts.map +1 -0
- package/dist/commands/migration-show.mjs +139 -0
- package/dist/commands/migration-show.mjs.map +1 -0
- package/dist/commands/migration-status.d.mts +86 -0
- package/dist/commands/migration-status.d.mts.map +1 -0
- package/dist/commands/migration-status.mjs +8 -0
- package/dist/commands/migration-verify.d.mts +16 -0
- package/dist/commands/migration-verify.d.mts.map +1 -0
- package/dist/commands/migration-verify.mjs +109 -0
- package/dist/commands/migration-verify.mjs.map +1 -0
- package/dist/config-loader-C4VXKl8f.mjs +43 -0
- package/dist/config-loader-C4VXKl8f.mjs.map +1 -0
- package/dist/{config-loader.d.ts → config-loader.d.mts} +8 -3
- package/dist/config-loader.d.mts.map +1 -0
- package/dist/config-loader.mjs +3 -0
- package/dist/contract-emit-Bk_eEDKu.mjs +187 -0
- package/dist/contract-emit-Bk_eEDKu.mjs.map +1 -0
- package/dist/contract-infer-suMDmFSG.mjs +89 -0
- package/dist/contract-infer-suMDmFSG.mjs.map +1 -0
- package/dist/exports/config-types.d.mts +2 -0
- package/dist/exports/config-types.mjs +3 -0
- package/dist/exports/control-api.d.mts +624 -0
- package/dist/exports/control-api.d.mts.map +1 -0
- package/dist/exports/control-api.mjs +109 -0
- package/dist/exports/control-api.mjs.map +1 -0
- package/dist/{load-ts-contract.d.ts → exports/index.d.mts} +12 -7
- package/dist/exports/index.d.mts.map +1 -0
- package/dist/exports/index.mjs +141 -0
- package/dist/exports/index.mjs.map +1 -0
- package/dist/extract-operation-statements-BVlb3jxp.mjs +13 -0
- package/dist/extract-operation-statements-BVlb3jxp.mjs.map +1 -0
- package/dist/extract-sql-ddl-6EVSOThm.mjs +26 -0
- package/dist/extract-sql-ddl-6EVSOThm.mjs.map +1 -0
- package/dist/framework-components-BAsliT4V.mjs +59 -0
- package/dist/framework-components-BAsliT4V.mjs.map +1 -0
- package/dist/inspect-live-schema-HMutsJYh.mjs +91 -0
- package/dist/inspect-live-schema-HMutsJYh.mjs.map +1 -0
- package/dist/migration-command-scaffold-Dg7CKKCg.mjs +105 -0
- package/dist/migration-command-scaffold-Dg7CKKCg.mjs.map +1 -0
- package/dist/migration-status-BqfVmC0w.mjs +1582 -0
- package/dist/migration-status-BqfVmC0w.mjs.map +1 -0
- package/dist/migrations-Bv8oeiY_.mjs +173 -0
- package/dist/migrations-Bv8oeiY_.mjs.map +1 -0
- package/dist/progress-adapter-D4x8SbJa.mjs +43 -0
- package/dist/progress-adapter-D4x8SbJa.mjs.map +1 -0
- package/dist/terminal-ui-N5tR-ob5.mjs +967 -0
- package/dist/terminal-ui-N5tR-ob5.mjs.map +1 -0
- package/dist/verify-WARh5TjK.mjs +385 -0
- package/dist/verify-WARh5TjK.mjs.map +1 -0
- package/package.json +87 -41
- package/src/cli.ts +113 -58
- package/src/commands/contract-emit.ts +237 -144
- package/src/commands/contract-infer-paths.ts +32 -0
- package/src/commands/contract-infer.ts +143 -0
- package/src/commands/db-init.ts +211 -426
- package/src/commands/db-schema.ts +77 -0
- package/src/commands/db-sign.ts +208 -229
- package/src/commands/db-update.ts +236 -0
- package/src/commands/db-verify.ts +504 -184
- package/src/commands/inspect-live-schema.ts +170 -0
- package/src/commands/migration-apply.ts +427 -0
- package/src/commands/migration-new.ts +260 -0
- package/src/commands/migration-plan.ts +519 -0
- package/src/commands/migration-ref.ts +305 -0
- package/src/commands/migration-show.ts +246 -0
- package/src/commands/migration-status.ts +864 -0
- package/src/commands/migration-verify.ts +180 -0
- package/src/config-loader.ts +13 -3
- package/src/control-api/client.ts +428 -46
- package/src/control-api/contract-enrichment.ts +119 -0
- package/src/control-api/errors.ts +9 -0
- package/src/control-api/operations/contract-emit.ts +174 -0
- package/src/control-api/operations/db-init.ts +144 -26
- package/src/control-api/operations/db-update.ts +220 -0
- package/src/control-api/operations/extract-operation-statements.ts +14 -0
- package/src/control-api/operations/extract-sql-ddl.ts +47 -0
- package/src/control-api/operations/migration-apply.ts +191 -0
- package/src/control-api/operations/migration-helpers.ts +49 -0
- package/src/control-api/types.ts +450 -18
- package/src/exports/config-types.ts +4 -3
- package/src/exports/control-api.ts +24 -2
- package/src/load-ts-contract.ts +30 -19
- package/src/utils/cli-errors.ts +14 -8
- package/src/utils/command-helpers.ts +302 -3
- package/src/utils/formatters/emit.ts +67 -0
- package/src/utils/formatters/errors.ts +82 -0
- package/src/utils/formatters/graph-migration-mapper.ts +240 -0
- package/src/utils/formatters/graph-render.ts +1323 -0
- package/src/utils/formatters/graph-types.ts +120 -0
- package/src/utils/formatters/help.ts +380 -0
- package/src/utils/formatters/helpers.ts +28 -0
- package/src/utils/formatters/migrations.ts +346 -0
- package/src/utils/formatters/styled.ts +212 -0
- package/src/utils/formatters/verify.ts +621 -0
- package/src/utils/framework-components.ts +13 -10
- package/src/utils/global-flags.ts +41 -23
- package/src/utils/migration-command-scaffold.ts +184 -0
- package/src/utils/migration-types.ts +12 -0
- package/src/utils/progress-adapter.ts +75 -0
- package/src/utils/result-handler.ts +12 -13
- package/src/utils/shutdown.ts +92 -0
- package/src/utils/suggest-command.ts +31 -0
- package/src/utils/terminal-ui.ts +276 -0
- package/dist/chunk-6EPKRATC.js +0 -91
- package/dist/chunk-6EPKRATC.js.map +0 -1
- package/dist/chunk-DIJPT5TZ.js +0 -967
- package/dist/chunk-DIJPT5TZ.js.map +0 -1
- package/dist/chunk-HWYQOCAJ.js +0 -47
- package/dist/chunk-HWYQOCAJ.js.map +0 -1
- package/dist/chunk-MG7PBERL.js +0 -131
- package/dist/chunk-MG7PBERL.js.map +0 -1
- package/dist/chunk-VI2YETW7.js +0 -38
- package/dist/chunk-VI2YETW7.js.map +0 -1
- package/dist/cli.d.ts +0 -2
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/contract-emit.d.ts +0 -3
- package/dist/commands/contract-emit.d.ts.map +0 -1
- package/dist/commands/contract-emit.js +0 -10
- package/dist/commands/contract-emit.js.map +0 -1
- package/dist/commands/db-init.d.ts +0 -3
- package/dist/commands/db-init.d.ts.map +0 -1
- package/dist/commands/db-init.js +0 -339
- package/dist/commands/db-init.js.map +0 -1
- package/dist/commands/db-introspect.d.ts +0 -3
- package/dist/commands/db-introspect.d.ts.map +0 -1
- package/dist/commands/db-introspect.js +0 -183
- package/dist/commands/db-introspect.js.map +0 -1
- package/dist/commands/db-schema-verify.d.ts +0 -3
- package/dist/commands/db-schema-verify.d.ts.map +0 -1
- package/dist/commands/db-schema-verify.js +0 -161
- package/dist/commands/db-schema-verify.js.map +0 -1
- package/dist/commands/db-sign.d.ts +0 -3
- package/dist/commands/db-sign.d.ts.map +0 -1
- package/dist/commands/db-sign.js +0 -196
- package/dist/commands/db-sign.js.map +0 -1
- package/dist/commands/db-verify.d.ts +0 -3
- package/dist/commands/db-verify.d.ts.map +0 -1
- package/dist/commands/db-verify.js +0 -170
- package/dist/commands/db-verify.js.map +0 -1
- package/dist/config-loader.d.ts.map +0 -1
- package/dist/config-loader.js +0 -7
- package/dist/config-loader.js.map +0 -1
- package/dist/control-api/client.d.ts +0 -13
- package/dist/control-api/client.d.ts.map +0 -1
- package/dist/control-api/operations/db-init.d.ts +0 -27
- package/dist/control-api/operations/db-init.d.ts.map +0 -1
- package/dist/control-api/types.d.ts +0 -203
- package/dist/control-api/types.d.ts.map +0 -1
- package/dist/exports/config-types.d.ts +0 -3
- package/dist/exports/config-types.d.ts.map +0 -1
- package/dist/exports/config-types.js +0 -6
- package/dist/exports/config-types.js.map +0 -1
- package/dist/exports/control-api.d.ts +0 -13
- package/dist/exports/control-api.d.ts.map +0 -1
- package/dist/exports/control-api.js +0 -240
- package/dist/exports/control-api.js.map +0 -1
- package/dist/exports/index.d.ts +0 -4
- package/dist/exports/index.d.ts.map +0 -1
- package/dist/exports/index.js +0 -176
- package/dist/exports/index.js.map +0 -1
- package/dist/load-ts-contract.d.ts.map +0 -1
- package/dist/utils/action.d.ts +0 -16
- package/dist/utils/action.d.ts.map +0 -1
- package/dist/utils/cli-errors.d.ts +0 -7
- package/dist/utils/cli-errors.d.ts.map +0 -1
- package/dist/utils/command-helpers.d.ts +0 -12
- package/dist/utils/command-helpers.d.ts.map +0 -1
- package/dist/utils/framework-components.d.ts +0 -70
- package/dist/utils/framework-components.d.ts.map +0 -1
- package/dist/utils/global-flags.d.ts +0 -25
- package/dist/utils/global-flags.d.ts.map +0 -1
- package/dist/utils/output.d.ts +0 -142
- package/dist/utils/output.d.ts.map +0 -1
- package/dist/utils/result-handler.d.ts +0 -15
- package/dist/utils/result-handler.d.ts.map +0 -1
- package/dist/utils/spinner.d.ts +0 -29
- package/dist/utils/spinner.d.ts.map +0 -1
- package/src/commands/db-introspect.ts +0 -254
- package/src/commands/db-schema-verify.ts +0 -231
- package/src/utils/action.ts +0 -43
- package/src/utils/output.ts +0 -1471
- package/src/utils/spinner.ts +0 -67
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import type {
|
|
3
|
+
VerifyDatabaseResult,
|
|
4
|
+
VerifyDatabaseSchemaResult,
|
|
5
|
+
} from '@prisma-next/framework-components/control';
|
|
4
6
|
import {
|
|
7
|
+
VERIFY_CODE_HASH_MISMATCH,
|
|
8
|
+
VERIFY_CODE_MARKER_MISSING,
|
|
9
|
+
VERIFY_CODE_TARGET_MISMATCH,
|
|
10
|
+
} from '@prisma-next/framework-components/control';
|
|
11
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
12
|
+
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
13
|
+
import { Command } from 'commander';
|
|
14
|
+
import { relative, resolve } from 'pathe';
|
|
15
|
+
import { loadConfig } from '../config-loader';
|
|
16
|
+
import { createControlClient } from '../control-api/client';
|
|
17
|
+
import { ContractValidationError } from '../control-api/errors';
|
|
18
|
+
import {
|
|
19
|
+
CliStructuredError,
|
|
20
|
+
errorContractValidationFailed,
|
|
5
21
|
errorDatabaseConnectionRequired,
|
|
6
22
|
errorDriverRequired,
|
|
7
23
|
errorFileNotFound,
|
|
@@ -10,218 +26,522 @@ import {
|
|
|
10
26
|
errorRuntime,
|
|
11
27
|
errorTargetMismatch,
|
|
12
28
|
errorUnexpected,
|
|
13
|
-
} from '
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
import {
|
|
29
|
+
} from '../utils/cli-errors';
|
|
30
|
+
import {
|
|
31
|
+
addGlobalOptions,
|
|
32
|
+
maskConnectionUrl,
|
|
33
|
+
resolveContractPath,
|
|
34
|
+
setCommandDescriptions,
|
|
35
|
+
setCommandExamples,
|
|
36
|
+
} from '../utils/command-helpers';
|
|
37
|
+
import { formatStyledHeader } from '../utils/formatters/styled';
|
|
22
38
|
import {
|
|
23
|
-
|
|
24
|
-
|
|
39
|
+
type DbVerifyCommandSuccessResult,
|
|
40
|
+
formatSchemaVerifyJson,
|
|
41
|
+
formatSchemaVerifyOutput,
|
|
25
42
|
formatVerifyJson,
|
|
26
43
|
formatVerifyOutput,
|
|
27
|
-
} from '../utils/
|
|
44
|
+
} from '../utils/formatters/verify';
|
|
45
|
+
import type { CommonCommandOptions } from '../utils/global-flags';
|
|
46
|
+
import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
|
|
47
|
+
import { createProgressAdapter } from '../utils/progress-adapter';
|
|
28
48
|
import { handleResult } from '../utils/result-handler';
|
|
29
|
-
import {
|
|
49
|
+
import { TerminalUI } from '../utils/terminal-ui';
|
|
30
50
|
|
|
31
|
-
interface DbVerifyOptions {
|
|
51
|
+
interface DbVerifyOptions extends CommonCommandOptions {
|
|
32
52
|
readonly db?: string;
|
|
33
53
|
readonly config?: string;
|
|
34
|
-
readonly
|
|
35
|
-
readonly
|
|
36
|
-
readonly
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
54
|
+
readonly markerOnly?: boolean;
|
|
55
|
+
readonly schemaOnly?: boolean;
|
|
56
|
+
readonly strict?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
type DbVerifyMode = 'full' | 'marker-only' | 'schema-only';
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Maps a VerifyDatabaseResult failure to a CliStructuredError.
|
|
63
|
+
*/
|
|
64
|
+
function mapVerifyFailure(verifyResult: VerifyDatabaseResult): CliStructuredError {
|
|
65
|
+
if (!verifyResult.ok && verifyResult.code) {
|
|
66
|
+
if (verifyResult.code === VERIFY_CODE_MARKER_MISSING) {
|
|
67
|
+
return errorMarkerMissing();
|
|
68
|
+
}
|
|
69
|
+
if (verifyResult.code === VERIFY_CODE_HASH_MISMATCH) {
|
|
70
|
+
const storageMatch = verifyResult.marker?.storageHash === verifyResult.contract.storageHash;
|
|
71
|
+
const profileMatch =
|
|
72
|
+
!verifyResult.contract.profileHash ||
|
|
73
|
+
verifyResult.marker?.profileHash === verifyResult.contract.profileHash;
|
|
74
|
+
|
|
75
|
+
if (!storageMatch) {
|
|
76
|
+
return errorHashMismatch({
|
|
77
|
+
why: 'Contract storageHash does not match database marker',
|
|
78
|
+
expected: verifyResult.contract.storageHash,
|
|
79
|
+
...ifDefined('actual', verifyResult.marker?.storageHash),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return errorHashMismatch({
|
|
84
|
+
why: profileMatch
|
|
85
|
+
? 'Contract hash does not match database marker'
|
|
86
|
+
: 'Contract profileHash does not match database marker',
|
|
87
|
+
...ifDefined('expected', verifyResult.contract.profileHash),
|
|
88
|
+
...ifDefined('actual', verifyResult.marker?.profileHash),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (verifyResult.code === VERIFY_CODE_TARGET_MISMATCH) {
|
|
92
|
+
return errorTargetMismatch(
|
|
93
|
+
verifyResult.target.expected,
|
|
94
|
+
verifyResult.target.actual ?? 'unknown',
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
// Unknown code - fall through to runtime error
|
|
98
|
+
}
|
|
99
|
+
return errorRuntime(verifyResult.summary);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
type DbVerifyFailure = CliStructuredError | VerifyDatabaseSchemaResult;
|
|
103
|
+
|
|
104
|
+
function errorInvalidVerifyMode(options: {
|
|
105
|
+
readonly why: string;
|
|
106
|
+
readonly fix: string;
|
|
107
|
+
}): CliStructuredError {
|
|
108
|
+
return new CliStructuredError('4012', 'Invalid verify mode', {
|
|
109
|
+
domain: 'CLI',
|
|
110
|
+
why: options.why,
|
|
111
|
+
fix: options.fix,
|
|
112
|
+
docsUrl: 'https://pris.ly/db-verify',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function resolveDbVerifyMode(options: DbVerifyOptions): Result<DbVerifyMode, CliStructuredError> {
|
|
117
|
+
if (options.markerOnly && options.schemaOnly) {
|
|
118
|
+
return notOk(
|
|
119
|
+
errorInvalidVerifyMode({
|
|
120
|
+
why: '`--marker-only` and `--schema-only` cannot be used together',
|
|
121
|
+
fix: 'Choose one mode: omit both to check the marker and schema, use `--marker-only` to check only the marker, or use `--schema-only` to check only the live schema.',
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (options.markerOnly && options.strict) {
|
|
127
|
+
return notOk(
|
|
128
|
+
errorInvalidVerifyMode({
|
|
129
|
+
why: '`--strict` requires schema verification, but `--marker-only` skips it',
|
|
130
|
+
fix: 'Remove `--strict`, or use `db verify` / `db verify --schema-only` when you want to check the live schema in strict mode.',
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (options.schemaOnly) {
|
|
136
|
+
return ok('schema-only');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (options.markerOnly) {
|
|
140
|
+
return ok('marker-only');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return ok('full');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function formatDbVerifyModeLabel(mode: DbVerifyMode, strict: boolean): string {
|
|
147
|
+
if (mode === 'marker-only') {
|
|
148
|
+
return 'marker only';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (mode === 'schema-only') {
|
|
152
|
+
return `schema only (${strict ? 'strict' : 'tolerant'})`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return `full (marker + schema, ${strict ? 'strict' : 'tolerant'})`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function formatDbVerifyInvocation(mode: DbVerifyMode, strict: boolean): string {
|
|
159
|
+
const args = ['db verify'];
|
|
160
|
+
|
|
161
|
+
if (mode === 'marker-only') {
|
|
162
|
+
args.push('--marker-only');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (mode === 'schema-only') {
|
|
166
|
+
args.push('--schema-only');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (strict) {
|
|
170
|
+
args.push('--strict');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return args.join(' ');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function createDbVerifyConnectionRequiredError(options: {
|
|
177
|
+
readonly configPath: string;
|
|
178
|
+
readonly mode: DbVerifyMode;
|
|
179
|
+
readonly strict: boolean;
|
|
180
|
+
}): CliStructuredError {
|
|
181
|
+
const invocation = formatDbVerifyInvocation(options.mode, options.strict);
|
|
182
|
+
return errorDatabaseConnectionRequired({
|
|
183
|
+
why: `Database connection is required for ${invocation} (set db.connection in ${options.configPath}, or pass --db <url>)`,
|
|
184
|
+
retryCommand: `prisma-next ${invocation} --db <url>`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function renderVerifyHeader(
|
|
189
|
+
paths: { configPath: string; contractPath: string },
|
|
190
|
+
options: DbVerifyOptions,
|
|
191
|
+
mode: DbVerifyMode,
|
|
192
|
+
flags: GlobalFlags,
|
|
193
|
+
ui: TerminalUI,
|
|
194
|
+
): void {
|
|
195
|
+
if (flags.json || flags.quiet) return;
|
|
196
|
+
|
|
197
|
+
const description =
|
|
198
|
+
mode === 'schema-only'
|
|
199
|
+
? 'Check whether the live database schema matches your contract'
|
|
200
|
+
: mode === 'marker-only'
|
|
201
|
+
? 'Check whether the database marker matches your contract'
|
|
202
|
+
: 'Check whether the database marker and live schema match your contract';
|
|
203
|
+
|
|
204
|
+
const details: Array<{ label: string; value: string }> = [
|
|
205
|
+
{ label: 'config', value: paths.configPath },
|
|
206
|
+
{ label: 'contract', value: paths.contractPath },
|
|
207
|
+
{ label: 'mode', value: formatDbVerifyModeLabel(mode, options.strict ?? false) },
|
|
208
|
+
];
|
|
209
|
+
if (options.db) {
|
|
210
|
+
details.push({ label: 'database', value: maskConnectionUrl(options.db) });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
ui.stderr(
|
|
214
|
+
formatStyledHeader({
|
|
215
|
+
command: 'db verify',
|
|
216
|
+
description,
|
|
217
|
+
url: 'https://pris.ly/db-verify',
|
|
218
|
+
details,
|
|
219
|
+
flags,
|
|
220
|
+
}),
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function resolveVerifyPaths(options: DbVerifyOptions) {
|
|
225
|
+
const config = await loadConfig(options.config);
|
|
226
|
+
const configPath = options.config
|
|
227
|
+
? relative(process.cwd(), resolve(options.config))
|
|
228
|
+
: 'prisma-next.config.ts';
|
|
229
|
+
const contractPathAbsolute = resolveContractPath(config);
|
|
230
|
+
const contractPath = relative(process.cwd(), contractPathAbsolute);
|
|
231
|
+
return { config, configPath, contractPathAbsolute, contractPath };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
type VerifyPaths = Awaited<ReturnType<typeof resolveVerifyPaths>>;
|
|
235
|
+
|
|
236
|
+
interface VerifySetup extends VerifyPaths {
|
|
237
|
+
readonly contractJson: Record<string, unknown>;
|
|
238
|
+
readonly dbConnection: string;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function resolveVerifySetup(
|
|
242
|
+
paths: VerifyPaths,
|
|
243
|
+
options: DbVerifyOptions,
|
|
244
|
+
mode: DbVerifyMode,
|
|
245
|
+
): Promise<Result<VerifySetup, CliStructuredError>> {
|
|
246
|
+
const { config, configPath, contractPathAbsolute, contractPath } = paths;
|
|
247
|
+
|
|
248
|
+
let contractJsonContent: string;
|
|
249
|
+
try {
|
|
250
|
+
contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');
|
|
251
|
+
} catch (error) {
|
|
252
|
+
if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
|
|
253
|
+
return notOk(
|
|
254
|
+
errorFileNotFound(contractPathAbsolute, {
|
|
255
|
+
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
256
|
+
fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`,
|
|
257
|
+
}),
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
return notOk(
|
|
261
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
262
|
+
why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,
|
|
263
|
+
}),
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
let contractJson: Record<string, unknown>;
|
|
268
|
+
try {
|
|
269
|
+
contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;
|
|
270
|
+
} catch (error) {
|
|
271
|
+
return notOk(
|
|
272
|
+
errorContractValidationFailed(
|
|
273
|
+
`Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
274
|
+
{ where: { path: contractPathAbsolute } },
|
|
275
|
+
),
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const dbConnection = options.db ?? config.db?.connection;
|
|
280
|
+
if (typeof dbConnection !== 'string' || dbConnection.length === 0) {
|
|
281
|
+
return notOk(
|
|
282
|
+
createDbVerifyConnectionRequiredError({
|
|
283
|
+
configPath,
|
|
284
|
+
mode,
|
|
285
|
+
strict: options.strict ?? false,
|
|
286
|
+
}),
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!config.driver) {
|
|
291
|
+
return notOk(
|
|
292
|
+
errorDriverRequired({
|
|
293
|
+
why: `Config.driver is required for ${formatDbVerifyInvocation(mode, options.strict ?? false)}`,
|
|
294
|
+
}),
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return ok({ ...paths, contractJson, dbConnection });
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function createVerifyClient(setup: VerifySetup) {
|
|
302
|
+
return createControlClient({
|
|
303
|
+
family: setup.config.family,
|
|
304
|
+
target: setup.config.target,
|
|
305
|
+
adapter: setup.config.adapter,
|
|
306
|
+
driver: setup.config.driver!,
|
|
307
|
+
extensionPacks: setup.config.extensionPacks ?? [],
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function wrapVerifyError(
|
|
312
|
+
error: unknown,
|
|
313
|
+
contractPathAbsolute: string,
|
|
314
|
+
modeLabel: string,
|
|
315
|
+
): Result<never, CliStructuredError> {
|
|
316
|
+
if (error instanceof CliStructuredError) {
|
|
317
|
+
return notOk(error);
|
|
318
|
+
}
|
|
319
|
+
if (error instanceof ContractValidationError) {
|
|
320
|
+
return notOk(
|
|
321
|
+
errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
|
|
322
|
+
where: { path: contractPathAbsolute },
|
|
323
|
+
}),
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
return notOk(
|
|
327
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
328
|
+
why: `Unexpected error during ${modeLabel}: ${error instanceof Error ? error.message : String(error)}`,
|
|
329
|
+
}),
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Executes the db verify command and returns a structured Result.
|
|
335
|
+
*/
|
|
336
|
+
async function executeDbVerifyCommand(
|
|
337
|
+
options: DbVerifyOptions,
|
|
338
|
+
flags: GlobalFlags,
|
|
339
|
+
ui: TerminalUI,
|
|
340
|
+
mode: Extract<DbVerifyMode, 'full' | 'marker-only'>,
|
|
341
|
+
): Promise<Result<DbVerifyCommandSuccessResult, DbVerifyFailure>> {
|
|
342
|
+
const startTime = Date.now();
|
|
343
|
+
const paths = await resolveVerifyPaths(options);
|
|
344
|
+
renderVerifyHeader(paths, options, mode, flags, ui);
|
|
345
|
+
|
|
346
|
+
const setupResult = await resolveVerifySetup(paths, options, mode);
|
|
347
|
+
if (!setupResult.ok) return setupResult;
|
|
348
|
+
const { contractJson, dbConnection, contractPathAbsolute } = setupResult.value;
|
|
349
|
+
|
|
350
|
+
const client = createVerifyClient(setupResult.value);
|
|
351
|
+
const onProgress = createProgressAdapter({ ui, flags });
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
const verifyResult = await client.verify({
|
|
355
|
+
contract: contractJson,
|
|
356
|
+
connection: dbConnection,
|
|
357
|
+
onProgress,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// If verification failed, map to CLI structured error
|
|
361
|
+
if (!verifyResult.ok) {
|
|
362
|
+
return notOk(mapVerifyFailure(verifyResult));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (mode === 'marker-only') {
|
|
366
|
+
return ok({
|
|
367
|
+
ok: true,
|
|
368
|
+
mode: 'marker-only',
|
|
369
|
+
summary: 'Database marker matches contract',
|
|
370
|
+
contract: verifyResult.contract,
|
|
371
|
+
marker: verifyResult.marker,
|
|
372
|
+
target: verifyResult.target,
|
|
373
|
+
...ifDefined('missingCodecs', verifyResult.missingCodecs),
|
|
374
|
+
...ifDefined('codecCoverageSkipped', verifyResult.codecCoverageSkipped),
|
|
375
|
+
warning: 'Schema verification skipped because --marker-only was provided',
|
|
376
|
+
meta: {
|
|
377
|
+
...(verifyResult.meta ?? {}),
|
|
378
|
+
schemaVerification: 'skipped',
|
|
379
|
+
},
|
|
380
|
+
timings: { total: Date.now() - startTime },
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const schemaVerifyResult = await client.schemaVerify({
|
|
385
|
+
contract: contractJson,
|
|
386
|
+
strict: options.strict ?? false,
|
|
387
|
+
onProgress,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
if (!schemaVerifyResult.ok) {
|
|
391
|
+
return notOk(schemaVerifyResult);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return ok({
|
|
395
|
+
ok: true,
|
|
396
|
+
mode: 'full',
|
|
397
|
+
summary: 'Database marker and schema match contract',
|
|
398
|
+
contract: verifyResult.contract,
|
|
399
|
+
marker: verifyResult.marker,
|
|
400
|
+
target: verifyResult.target,
|
|
401
|
+
...ifDefined('missingCodecs', verifyResult.missingCodecs),
|
|
402
|
+
...ifDefined('codecCoverageSkipped', verifyResult.codecCoverageSkipped),
|
|
403
|
+
schema: {
|
|
404
|
+
summary: schemaVerifyResult.summary,
|
|
405
|
+
counts: schemaVerifyResult.schema.counts,
|
|
406
|
+
strict: schemaVerifyResult.meta?.strict ?? false,
|
|
407
|
+
},
|
|
408
|
+
meta: {
|
|
409
|
+
...(verifyResult.meta ?? {}),
|
|
410
|
+
schemaVerification: 'performed',
|
|
411
|
+
},
|
|
412
|
+
timings: { total: Date.now() - startTime },
|
|
413
|
+
});
|
|
414
|
+
} catch (error) {
|
|
415
|
+
return wrapVerifyError(error, contractPathAbsolute, 'db verify');
|
|
416
|
+
} finally {
|
|
417
|
+
await client.close();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async function executeDbSchemaOnlyVerifyCommand(
|
|
422
|
+
options: DbVerifyOptions,
|
|
423
|
+
flags: GlobalFlags,
|
|
424
|
+
ui: TerminalUI,
|
|
425
|
+
): Promise<Result<VerifyDatabaseSchemaResult, CliStructuredError>> {
|
|
426
|
+
const paths = await resolveVerifyPaths(options);
|
|
427
|
+
renderVerifyHeader(paths, options, 'schema-only', flags, ui);
|
|
428
|
+
|
|
429
|
+
const setupResult = await resolveVerifySetup(paths, options, 'schema-only');
|
|
430
|
+
if (!setupResult.ok) return setupResult;
|
|
431
|
+
const { contractJson, dbConnection, contractPathAbsolute } = setupResult.value;
|
|
432
|
+
|
|
433
|
+
const client = createVerifyClient(setupResult.value);
|
|
434
|
+
const onProgress = createProgressAdapter({ ui, flags });
|
|
435
|
+
|
|
436
|
+
try {
|
|
437
|
+
const schemaVerifyResult = await client.schemaVerify({
|
|
438
|
+
contract: contractJson,
|
|
439
|
+
strict: options.strict ?? false,
|
|
440
|
+
connection: dbConnection,
|
|
441
|
+
onProgress,
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
return ok(schemaVerifyResult);
|
|
445
|
+
} catch (error) {
|
|
446
|
+
return wrapVerifyError(error, contractPathAbsolute, 'db verify --schema-only');
|
|
447
|
+
} finally {
|
|
448
|
+
await client.close();
|
|
449
|
+
}
|
|
44
450
|
}
|
|
45
451
|
|
|
46
452
|
export function createDbVerifyCommand(): Command {
|
|
47
453
|
const command = new Command('verify');
|
|
48
454
|
setCommandDescriptions(
|
|
49
455
|
command,
|
|
50
|
-
'Check whether the database
|
|
51
|
-
'Verifies
|
|
52
|
-
'
|
|
456
|
+
'Check whether the database marker and live schema match your contract',
|
|
457
|
+
'Verifies the database marker first, then checks the database schema matches your contract.\n' +
|
|
458
|
+
'Use `--marker-only` for marker-only verification, `--schema-only` to skip marker checks and\n' +
|
|
459
|
+
'inspect only the live schema, and `--strict` to fail if the database includes elements\n' +
|
|
460
|
+
'not present in the contract.',
|
|
53
461
|
);
|
|
54
|
-
command
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
462
|
+
setCommandExamples(command, [
|
|
463
|
+
'prisma-next db verify --db $DATABASE_URL',
|
|
464
|
+
'prisma-next db verify --db $DATABASE_URL --strict',
|
|
465
|
+
'prisma-next db verify --db $DATABASE_URL --schema-only',
|
|
466
|
+
'prisma-next db verify --db $DATABASE_URL --schema-only --strict',
|
|
467
|
+
'prisma-next db verify --db $DATABASE_URL --marker-only',
|
|
468
|
+
'prisma-next db verify --db $DATABASE_URL --json',
|
|
469
|
+
]);
|
|
470
|
+
addGlobalOptions(command)
|
|
61
471
|
.option('--db <url>', 'Database connection string')
|
|
62
472
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
63
|
-
.option('--
|
|
64
|
-
.option(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.option(
|
|
69
|
-
|
|
473
|
+
.option('--marker-only', 'Skip schema verification and only check the database marker')
|
|
474
|
+
.option(
|
|
475
|
+
'--schema-only',
|
|
476
|
+
'Skip marker verification and only check whether the live schema satisfies the contract',
|
|
477
|
+
)
|
|
478
|
+
.option(
|
|
479
|
+
'--strict',
|
|
480
|
+
'Strict mode: schema elements not present in the contract are considered an error',
|
|
481
|
+
false,
|
|
482
|
+
)
|
|
70
483
|
.action(async (options: DbVerifyOptions) => {
|
|
71
484
|
const flags = parseGlobalFlags(options);
|
|
485
|
+
const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
|
|
72
486
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
? relative(process.cwd(), resolve(options.config))
|
|
79
|
-
: 'prisma-next.config.ts';
|
|
80
|
-
const contractPathAbsolute = config.contract?.output
|
|
81
|
-
? resolve(config.contract.output)
|
|
82
|
-
: resolve('src/prisma/contract.json');
|
|
83
|
-
// Convert to relative path for display
|
|
84
|
-
const contractPath = relative(process.cwd(), contractPathAbsolute);
|
|
85
|
-
|
|
86
|
-
// Output header (only for human-readable output)
|
|
87
|
-
if (flags.json !== 'object' && !flags.quiet) {
|
|
88
|
-
const details: Array<{ label: string; value: string }> = [
|
|
89
|
-
{ label: 'config', value: configPath },
|
|
90
|
-
{ label: 'contract', value: contractPath },
|
|
91
|
-
];
|
|
92
|
-
if (options.db) {
|
|
93
|
-
details.push({ label: 'database', value: options.db });
|
|
94
|
-
}
|
|
95
|
-
const header = formatStyledHeader({
|
|
96
|
-
command: 'db verify',
|
|
97
|
-
description: 'Check whether the database has been signed with your contract',
|
|
98
|
-
url: 'https://pris.ly/db-verify',
|
|
99
|
-
details,
|
|
100
|
-
flags,
|
|
101
|
-
});
|
|
102
|
-
console.log(header);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Load contract file (file I/O)
|
|
106
|
-
let contractJsonContent: string;
|
|
107
|
-
try {
|
|
108
|
-
contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');
|
|
109
|
-
} catch (error) {
|
|
110
|
-
if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
|
|
111
|
-
throw errorFileNotFound(contractPathAbsolute, {
|
|
112
|
-
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
throw errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
116
|
-
why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
const contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;
|
|
120
|
-
|
|
121
|
-
// Resolve database connection (--db flag or config.db.connection)
|
|
122
|
-
const dbConnection = options.db ?? config.db?.connection;
|
|
123
|
-
if (!dbConnection) {
|
|
124
|
-
throw errorDatabaseConnectionRequired();
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Check for driver
|
|
128
|
-
if (!config.driver) {
|
|
129
|
-
throw errorDriverRequired();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Store driver descriptor after null check
|
|
133
|
-
const driverDescriptor = config.driver;
|
|
134
|
-
|
|
135
|
-
const driver = await withSpinner(() => driverDescriptor.create(dbConnection), {
|
|
136
|
-
message: 'Connecting to database...',
|
|
137
|
-
flags,
|
|
138
|
-
});
|
|
487
|
+
const modeResult = resolveDbVerifyMode(options);
|
|
488
|
+
if (!modeResult.ok) {
|
|
489
|
+
const exitCode = handleResult(modeResult as Result<never, CliStructuredError>, flags, ui);
|
|
490
|
+
process.exit(exitCode);
|
|
491
|
+
}
|
|
139
492
|
|
|
140
|
-
|
|
141
|
-
// Create family instance
|
|
142
|
-
const stack = createControlPlaneStack({
|
|
143
|
-
target: config.target,
|
|
144
|
-
adapter: config.adapter,
|
|
145
|
-
driver: driverDescriptor,
|
|
146
|
-
extensionPacks: config.extensionPacks,
|
|
147
|
-
});
|
|
148
|
-
const familyInstance = config.family.create(stack);
|
|
149
|
-
|
|
150
|
-
// Validate contract using instance validator
|
|
151
|
-
const contractIR = familyInstance.validateContractIR(contractJson) as ContractIR;
|
|
152
|
-
assertContractRequirementsSatisfied({ contract: contractIR, stack });
|
|
153
|
-
|
|
154
|
-
// Call family instance verify method
|
|
155
|
-
let verifyResult: VerifyDatabaseResult;
|
|
156
|
-
try {
|
|
157
|
-
verifyResult = await withSpinner(
|
|
158
|
-
() =>
|
|
159
|
-
familyInstance.verify({
|
|
160
|
-
driver,
|
|
161
|
-
contractIR,
|
|
162
|
-
expectedTargetId: config.target.targetId,
|
|
163
|
-
contractPath: contractPathAbsolute,
|
|
164
|
-
configPath,
|
|
165
|
-
}),
|
|
166
|
-
{
|
|
167
|
-
message: 'Verifying database schema...',
|
|
168
|
-
flags,
|
|
169
|
-
},
|
|
170
|
-
);
|
|
171
|
-
} catch (error) {
|
|
172
|
-
// Wrap errors from verify() in structured error
|
|
173
|
-
throw errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
174
|
-
why: `Failed to verify database: ${error instanceof Error ? error.message : String(error)}`,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
493
|
+
const mode = modeResult.value;
|
|
177
494
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
});
|
|
495
|
+
if (mode === 'schema-only') {
|
|
496
|
+
const result = await executeDbSchemaOnlyVerifyCommand(options, flags, ui);
|
|
497
|
+
const exitCode = handleResult(result, flags, ui, (schemaVerifyResult) => {
|
|
498
|
+
if (flags.json) {
|
|
499
|
+
ui.output(formatSchemaVerifyJson(schemaVerifyResult));
|
|
500
|
+
} else {
|
|
501
|
+
const output = formatSchemaVerifyOutput(schemaVerifyResult, flags);
|
|
502
|
+
if (output) {
|
|
503
|
+
ui.log(output);
|
|
188
504
|
}
|
|
189
|
-
if (verifyResult.code === 'PN-RTM-3003') {
|
|
190
|
-
throw errorTargetMismatch(
|
|
191
|
-
verifyResult.target.expected,
|
|
192
|
-
verifyResult.target.actual ?? 'unknown',
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
throw errorRuntime(verifyResult.summary);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Add blank line after all async operations if spinners were shown
|
|
199
|
-
if (!flags.quiet && flags.json !== 'object' && process.stdout.isTTY) {
|
|
200
|
-
console.log('');
|
|
201
505
|
}
|
|
506
|
+
});
|
|
202
507
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
// Ensure driver connection is closed
|
|
206
|
-
await driver.close();
|
|
508
|
+
if (result.ok && !result.value.ok) {
|
|
509
|
+
process.exit(1);
|
|
207
510
|
}
|
|
208
|
-
});
|
|
209
511
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
512
|
+
process.exit(exitCode);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const result = await executeDbVerifyCommand(options, flags, ui, mode);
|
|
516
|
+
|
|
517
|
+
if (result.ok) {
|
|
518
|
+
if (flags.json) {
|
|
519
|
+
ui.output(formatVerifyJson(result.value));
|
|
216
520
|
} else {
|
|
217
|
-
|
|
218
|
-
const output = formatVerifyOutput(verifyResult, flags);
|
|
521
|
+
const output = formatVerifyOutput(result.value, flags);
|
|
219
522
|
if (output) {
|
|
220
|
-
|
|
523
|
+
ui.log(output);
|
|
221
524
|
}
|
|
222
525
|
}
|
|
223
|
-
|
|
224
|
-
|
|
526
|
+
process.exit(0);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
if (CliStructuredError.is(result.failure)) {
|
|
530
|
+
const exitCode = handleResult(result as Result<never, CliStructuredError>, flags, ui);
|
|
531
|
+
process.exit(exitCode);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (flags.json) {
|
|
535
|
+
ui.output(formatSchemaVerifyJson(result.failure));
|
|
536
|
+
} else {
|
|
537
|
+
// Always show schema-drift failures, even in quiet mode — exiting 1 without
|
|
538
|
+
// diagnostics is unhelpful.
|
|
539
|
+
const output = formatSchemaVerifyOutput(result.failure, { ...flags, quiet: false });
|
|
540
|
+
if (output) {
|
|
541
|
+
ui.log(output);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
process.exit(1);
|
|
225
545
|
});
|
|
226
546
|
|
|
227
547
|
return command;
|