@prisma-next/cli 0.3.0-dev.53 → 0.3.0-dev.55
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/README.md +24 -0
- package/dist/cli.mjs +5 -3
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-BSZKpZTF.mjs → client-B7f4PZZ1.mjs} +367 -170
- package/dist/client-B7f4PZZ1.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +7 -6
- package/dist/commands/contract-emit.mjs.map +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +28 -76
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-introspect.d.mts.map +1 -1
- package/dist/commands/db-introspect.mjs +12 -17
- package/dist/commands/db-introspect.mjs.map +1 -1
- package/dist/commands/db-schema-verify.d.mts.map +1 -1
- package/dist/commands/db-schema-verify.mjs +5 -4
- package/dist/commands/db-schema-verify.mjs.map +1 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +6 -5
- package/dist/commands/db-sign.mjs.map +1 -1
- 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 +120 -0
- package/dist/commands/db-update.mjs.map +1 -0
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +5 -4
- package/dist/commands/db-verify.mjs.map +1 -1
- package/dist/{config-loader-BJ8HsEdA.mjs → config-loader-DqKf1qSa.mjs} +1 -1
- package/dist/{config-loader-BJ8HsEdA.mjs.map → config-loader-DqKf1qSa.mjs.map} +1 -1
- package/dist/config-loader.mjs +1 -1
- package/dist/exports/control-api.d.mts +96 -6
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +1 -3
- package/dist/exports/index.mjs.map +1 -1
- package/dist/migration-command-scaffold-BELw_do2.mjs +95 -0
- package/dist/migration-command-scaffold-BELw_do2.mjs.map +1 -0
- package/dist/{result-handler-BZPY7HX4.mjs → result-handler-BhmrXIvT.mjs} +63 -13
- package/dist/result-handler-BhmrXIvT.mjs.map +1 -0
- package/package.json +14 -10
- package/src/cli.ts +5 -0
- package/src/commands/contract-emit.ts +22 -6
- package/src/commands/db-init.ts +89 -197
- package/src/commands/db-introspect.ts +4 -8
- package/src/commands/db-schema-verify.ts +11 -2
- package/src/commands/db-sign.ts +13 -4
- package/src/commands/db-update.ts +220 -0
- package/src/commands/db-verify.ts +11 -2
- package/src/control-api/client.ts +109 -145
- package/src/control-api/errors.ts +9 -0
- package/src/control-api/operations/db-init.ts +39 -34
- package/src/control-api/operations/db-update.ts +221 -0
- package/src/control-api/operations/extract-sql-ddl.ts +47 -0
- package/src/control-api/operations/migration-helpers.ts +49 -0
- package/src/control-api/types.ts +104 -4
- package/src/exports/control-api.ts +5 -0
- package/src/utils/cli-errors.ts +2 -0
- package/src/utils/command-helpers.ts +81 -3
- package/src/utils/migration-command-scaffold.ts +189 -0
- package/src/utils/output.ts +43 -13
- package/dist/client-BSZKpZTF.mjs.map +0 -1
- package/dist/result-handler-BZPY7HX4.mjs.map +0 -1
package/src/commands/db-init.ts
CHANGED
|
@@ -1,51 +1,34 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { relative, resolve } from 'node:path';
|
|
3
1
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
4
2
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
5
3
|
import { Command } from 'commander';
|
|
6
|
-
import {
|
|
7
|
-
import { createControlClient } from '../control-api/client';
|
|
4
|
+
import { ContractValidationError } from '../control-api/errors';
|
|
8
5
|
import type { DbInitFailure } from '../control-api/types';
|
|
9
6
|
import {
|
|
10
7
|
CliStructuredError,
|
|
11
8
|
errorContractValidationFailed,
|
|
12
|
-
errorDatabaseConnectionRequired,
|
|
13
|
-
errorDriverRequired,
|
|
14
|
-
errorFileNotFound,
|
|
15
9
|
errorJsonFormatNotSupported,
|
|
16
10
|
errorMigrationPlanningFailed,
|
|
11
|
+
errorRunnerFailed,
|
|
17
12
|
errorRuntime,
|
|
18
|
-
errorTargetMigrationNotSupported,
|
|
19
13
|
errorUnexpected,
|
|
20
14
|
} from '../utils/cli-errors';
|
|
21
|
-
import {
|
|
15
|
+
import type { MigrationCommandOptions } from '../utils/command-helpers';
|
|
16
|
+
import { sanitizeErrorMessage, setCommandDescriptions } from '../utils/command-helpers';
|
|
22
17
|
import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
|
|
23
18
|
import {
|
|
24
|
-
|
|
19
|
+
addMigrationCommandOptions,
|
|
20
|
+
prepareMigrationContext,
|
|
21
|
+
} from '../utils/migration-command-scaffold';
|
|
22
|
+
import {
|
|
25
23
|
formatCommandHelp,
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
formatMigrationApplyOutput,
|
|
25
|
+
formatMigrationJson,
|
|
26
|
+
formatMigrationPlanOutput,
|
|
27
|
+
type MigrationCommandResult,
|
|
30
28
|
} from '../utils/output';
|
|
31
|
-
import { createProgressAdapter } from '../utils/progress-adapter';
|
|
32
29
|
import { handleResult } from '../utils/result-handler';
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
readonly db?: string;
|
|
36
|
-
readonly config?: string;
|
|
37
|
-
readonly plan?: boolean;
|
|
38
|
-
readonly json?: string | boolean;
|
|
39
|
-
readonly quiet?: boolean;
|
|
40
|
-
readonly q?: boolean;
|
|
41
|
-
readonly verbose?: boolean;
|
|
42
|
-
readonly v?: boolean;
|
|
43
|
-
readonly vv?: boolean;
|
|
44
|
-
readonly trace?: boolean;
|
|
45
|
-
readonly timestamps?: boolean;
|
|
46
|
-
readonly color?: boolean;
|
|
47
|
-
readonly 'no-color'?: boolean;
|
|
48
|
-
}
|
|
31
|
+
type DbInitOptions = MigrationCommandOptions;
|
|
49
32
|
|
|
50
33
|
/**
|
|
51
34
|
* Maps a DbInitFailure to a CliStructuredError for consistent error handling.
|
|
@@ -77,9 +60,9 @@ function mapDbInitFailure(failure: DbInitFailure): CliStructuredError {
|
|
|
77
60
|
}
|
|
78
61
|
|
|
79
62
|
return errorRuntime(
|
|
80
|
-
`Existing
|
|
63
|
+
`Existing database signature does not match plan destination.${mismatchParts.length > 0 ? ` Mismatch in ${mismatchParts.join(' and ')}.` : ''}`,
|
|
81
64
|
{
|
|
82
|
-
why: 'Database has an existing
|
|
65
|
+
why: 'Database has an existing signature (marker) that does not match the target contract',
|
|
83
66
|
fix: 'If bootstrapping, drop/reset the database then re-run `prisma-next db init`; otherwise reconcile schema/marker using your migration workflow',
|
|
84
67
|
meta: {
|
|
85
68
|
code: 'MARKER_ORIGIN_MISMATCH',
|
|
@@ -93,13 +76,12 @@ function mapDbInitFailure(failure: DbInitFailure): CliStructuredError {
|
|
|
93
76
|
}
|
|
94
77
|
|
|
95
78
|
if (failure.code === 'RUNNER_FAILED') {
|
|
96
|
-
return
|
|
79
|
+
return errorRunnerFailed(failure.summary, {
|
|
97
80
|
why: failure.why ?? 'Migration runner failed',
|
|
98
81
|
fix: 'Fix the schema mismatch (db init is additive-only), or drop/reset the database and re-run `prisma-next db init`',
|
|
99
|
-
meta
|
|
100
|
-
code: 'RUNNER_FAILED',
|
|
101
|
-
|
|
102
|
-
},
|
|
82
|
+
...(failure.meta
|
|
83
|
+
? { meta: { code: 'RUNNER_FAILED', ...failure.meta } }
|
|
84
|
+
: { meta: { code: 'RUNNER_FAILED' } }),
|
|
103
85
|
});
|
|
104
86
|
}
|
|
105
87
|
|
|
@@ -115,110 +97,20 @@ async function executeDbInitCommand(
|
|
|
115
97
|
options: DbInitOptions,
|
|
116
98
|
flags: GlobalFlags,
|
|
117
99
|
startTime: number,
|
|
118
|
-
): Promise<Result<
|
|
119
|
-
//
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
: '
|
|
124
|
-
const contractPathAbsolute = config.contract?.output
|
|
125
|
-
? resolve(config.contract.output)
|
|
126
|
-
: resolve('src/prisma/contract.json');
|
|
127
|
-
const contractPath = relative(process.cwd(), contractPathAbsolute);
|
|
128
|
-
|
|
129
|
-
// Output header
|
|
130
|
-
if (flags.json !== 'object' && !flags.quiet) {
|
|
131
|
-
const details: Array<{ label: string; value: string }> = [
|
|
132
|
-
{ label: 'config', value: configPath },
|
|
133
|
-
{ label: 'contract', value: contractPath },
|
|
134
|
-
];
|
|
135
|
-
if (options.db) {
|
|
136
|
-
details.push({ label: 'database', value: options.db });
|
|
137
|
-
}
|
|
138
|
-
if (options.plan) {
|
|
139
|
-
details.push({ label: 'mode', value: 'plan (dry run)' });
|
|
140
|
-
}
|
|
141
|
-
const header = formatStyledHeader({
|
|
142
|
-
command: 'db init',
|
|
143
|
-
description: 'Bootstrap a database to match the current contract',
|
|
144
|
-
url: 'https://pris.ly/db-init',
|
|
145
|
-
details,
|
|
146
|
-
flags,
|
|
147
|
-
});
|
|
148
|
-
console.log(header);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Load contract file
|
|
152
|
-
let contractJsonContent: string;
|
|
153
|
-
try {
|
|
154
|
-
contractJsonContent = await readFile(contractPathAbsolute, 'utf-8');
|
|
155
|
-
} catch (error) {
|
|
156
|
-
if (error instanceof Error && (error as { code?: string }).code === 'ENOENT') {
|
|
157
|
-
return notOk(
|
|
158
|
-
errorFileNotFound(contractPathAbsolute, {
|
|
159
|
-
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
160
|
-
fix: `Run \`prisma-next contract emit\` to generate ${contractPath}, or update \`config.contract.output\` in ${configPath}`,
|
|
161
|
-
}),
|
|
162
|
-
);
|
|
163
|
-
}
|
|
164
|
-
return notOk(
|
|
165
|
-
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
166
|
-
why: `Failed to read contract file: ${error instanceof Error ? error.message : String(error)}`,
|
|
167
|
-
}),
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
let contractJson: Record<string, unknown>;
|
|
172
|
-
try {
|
|
173
|
-
contractJson = JSON.parse(contractJsonContent) as Record<string, unknown>;
|
|
174
|
-
} catch (error) {
|
|
175
|
-
return notOk(
|
|
176
|
-
errorContractValidationFailed(
|
|
177
|
-
`Contract JSON is invalid: ${error instanceof Error ? error.message : String(error)}`,
|
|
178
|
-
{ where: { path: contractPathAbsolute } },
|
|
179
|
-
),
|
|
180
|
-
);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Resolve database connection (--db flag or config.db.connection)
|
|
184
|
-
const dbConnection = options.db ?? config.db?.connection;
|
|
185
|
-
if (!dbConnection) {
|
|
186
|
-
return notOk(
|
|
187
|
-
errorDatabaseConnectionRequired({
|
|
188
|
-
why: `Database connection is required for db init (set db.connection in ${configPath}, or pass --db <url>)`,
|
|
189
|
-
}),
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// Check for driver
|
|
194
|
-
if (!config.driver) {
|
|
195
|
-
return notOk(errorDriverRequired({ why: 'Config.driver is required for db init' }));
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Check target supports migrations via the migrations capability
|
|
199
|
-
if (!config.target.migrations) {
|
|
200
|
-
return notOk(
|
|
201
|
-
errorTargetMigrationNotSupported({
|
|
202
|
-
why: `Target "${config.target.id}" does not support migrations`,
|
|
203
|
-
}),
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Create control client
|
|
208
|
-
const client = createControlClient({
|
|
209
|
-
family: config.family,
|
|
210
|
-
target: config.target,
|
|
211
|
-
adapter: config.adapter,
|
|
212
|
-
driver: config.driver,
|
|
213
|
-
extensionPacks: config.extensionPacks ?? [],
|
|
100
|
+
): Promise<Result<MigrationCommandResult, CliStructuredError>> {
|
|
101
|
+
// Prepare shared migration context (config, contract, connection, client)
|
|
102
|
+
const ctxResult = await prepareMigrationContext(options, flags, {
|
|
103
|
+
commandName: 'db init',
|
|
104
|
+
description: 'Bootstrap a database to match the current contract',
|
|
105
|
+
url: 'https://pris.ly/db-init',
|
|
214
106
|
});
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
107
|
+
if (!ctxResult.ok) {
|
|
108
|
+
return ctxResult;
|
|
109
|
+
}
|
|
110
|
+
const { client, contractJson, dbConnection, onProgress, contractPathAbsolute } = ctxResult.value;
|
|
218
111
|
|
|
219
112
|
try {
|
|
220
113
|
// Call dbInit with connection and progress callback
|
|
221
|
-
// Connection happens inside dbInit with a 'connect' progress span
|
|
222
114
|
const result = await client.dbInit({
|
|
223
115
|
contractIR: contractJson,
|
|
224
116
|
mode: options.plan ? 'plan' : 'apply',
|
|
@@ -232,15 +124,14 @@ async function executeDbInitCommand(
|
|
|
232
124
|
}
|
|
233
125
|
|
|
234
126
|
// Convert success result to CLI output format
|
|
235
|
-
const
|
|
236
|
-
const dbInitResult: DbInitResult = {
|
|
127
|
+
const dbInitResult: MigrationCommandResult = {
|
|
237
128
|
ok: true,
|
|
238
129
|
mode: result.value.mode,
|
|
239
130
|
plan: {
|
|
240
|
-
targetId: config.target.targetId,
|
|
131
|
+
targetId: ctxResult.value.config.target.targetId,
|
|
241
132
|
destination: {
|
|
242
|
-
storageHash: result.value.
|
|
243
|
-
...ifDefined('profileHash', profileHash),
|
|
133
|
+
storageHash: result.value.destination.storageHash,
|
|
134
|
+
...ifDefined('profileHash', result.value.destination.profileHash),
|
|
244
135
|
},
|
|
245
136
|
operations: result.value.plan.operations.map((op) => ({
|
|
246
137
|
id: op.id,
|
|
@@ -271,15 +162,26 @@ async function executeDbInitCommand(
|
|
|
271
162
|
return ok(dbInitResult);
|
|
272
163
|
} catch (error) {
|
|
273
164
|
// Driver already throws CliStructuredError for connection failures
|
|
274
|
-
// Use static type guard to work across module boundaries
|
|
275
165
|
if (CliStructuredError.is(error)) {
|
|
276
166
|
return notOk(error);
|
|
277
167
|
}
|
|
278
168
|
|
|
279
|
-
|
|
169
|
+
if (error instanceof ContractValidationError) {
|
|
170
|
+
return notOk(
|
|
171
|
+
errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
|
|
172
|
+
where: { path: contractPathAbsolute },
|
|
173
|
+
}),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
178
|
+
const safeMessage = sanitizeErrorMessage(
|
|
179
|
+
rawMessage,
|
|
180
|
+
typeof dbConnection === 'string' ? dbConnection : undefined,
|
|
181
|
+
);
|
|
280
182
|
return notOk(
|
|
281
|
-
errorUnexpected(
|
|
282
|
-
why: `Unexpected error during db init: ${
|
|
183
|
+
errorUnexpected(safeMessage, {
|
|
184
|
+
why: `Unexpected error during db init: ${safeMessage}`,
|
|
283
185
|
}),
|
|
284
186
|
);
|
|
285
187
|
} finally {
|
|
@@ -291,65 +193,55 @@ export function createDbInitCommand(): Command {
|
|
|
291
193
|
const command = new Command('init');
|
|
292
194
|
setCommandDescriptions(
|
|
293
195
|
command,
|
|
294
|
-
'Bootstrap a database to match the current contract and
|
|
196
|
+
'Bootstrap a database to match the current contract and sign it',
|
|
295
197
|
'Initializes a database to match your emitted contract using additive-only operations.\n' +
|
|
296
198
|
'Creates any missing tables, columns, indexes, and constraints defined in your contract.\n' +
|
|
297
199
|
'Leaves existing compatible structures in place, surfaces conflicts when destructive changes\n' +
|
|
298
|
-
'would be required, and
|
|
200
|
+
'would be required, and signs the database to track contract state. Use --plan to\n' +
|
|
299
201
|
'preview changes without applying.',
|
|
300
202
|
);
|
|
301
|
-
command
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
.
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const result = notOk(
|
|
325
|
-
errorJsonFormatNotSupported({
|
|
326
|
-
command: 'db init',
|
|
327
|
-
format: 'ndjson',
|
|
328
|
-
supportedFormats: ['object'],
|
|
329
|
-
}),
|
|
330
|
-
);
|
|
331
|
-
const exitCode = handleResult(result, flags);
|
|
332
|
-
process.exit(exitCode);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const result = await executeDbInitCommand(options, flags, startTime);
|
|
203
|
+
addMigrationCommandOptions(command);
|
|
204
|
+
command.configureHelp({
|
|
205
|
+
formatHelp: (cmd) => {
|
|
206
|
+
const flags = parseGlobalFlags({});
|
|
207
|
+
return formatCommandHelp({ command: cmd, flags });
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
command.action(async (options: DbInitOptions) => {
|
|
211
|
+
const flags = parseGlobalFlags(options);
|
|
212
|
+
const startTime = Date.now();
|
|
213
|
+
|
|
214
|
+
// Validate JSON format option
|
|
215
|
+
if (flags.json === 'ndjson') {
|
|
216
|
+
const result = notOk(
|
|
217
|
+
errorJsonFormatNotSupported({
|
|
218
|
+
command: 'db init',
|
|
219
|
+
format: 'ndjson',
|
|
220
|
+
supportedFormats: ['object'],
|
|
221
|
+
}),
|
|
222
|
+
);
|
|
223
|
+
const exitCode = handleResult(result, flags);
|
|
224
|
+
process.exit(exitCode);
|
|
225
|
+
}
|
|
336
226
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
227
|
+
const result = await executeDbInitCommand(options, flags, startTime);
|
|
228
|
+
|
|
229
|
+
const exitCode = handleResult(result, flags, (dbInitResult) => {
|
|
230
|
+
if (flags.json === 'object') {
|
|
231
|
+
console.log(formatMigrationJson(dbInitResult));
|
|
232
|
+
} else {
|
|
233
|
+
const output =
|
|
234
|
+
dbInitResult.mode === 'plan'
|
|
235
|
+
? formatMigrationPlanOutput(dbInitResult, flags)
|
|
236
|
+
: formatMigrationApplyOutput(dbInitResult, flags);
|
|
237
|
+
if (output) {
|
|
238
|
+
console.log(output);
|
|
348
239
|
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
process.exit(exitCode);
|
|
240
|
+
}
|
|
352
241
|
});
|
|
353
242
|
|
|
243
|
+
process.exit(exitCode);
|
|
244
|
+
});
|
|
245
|
+
|
|
354
246
|
return command;
|
|
355
247
|
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
errorJsonFormatNotSupported,
|
|
13
13
|
errorUnexpected,
|
|
14
14
|
} from '../utils/cli-errors';
|
|
15
|
-
import { setCommandDescriptions } from '../utils/command-helpers';
|
|
15
|
+
import { maskConnectionUrl, setCommandDescriptions } from '../utils/command-helpers';
|
|
16
16
|
import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
|
|
17
17
|
import {
|
|
18
18
|
formatCommandHelp,
|
|
@@ -63,13 +63,9 @@ async function executeDbIntrospectCommand(
|
|
|
63
63
|
{ label: 'config', value: configPath },
|
|
64
64
|
];
|
|
65
65
|
if (options.db) {
|
|
66
|
-
|
|
67
|
-
const maskedUrl = options.db.replace(/:([^:@]+)@/, ':****@');
|
|
68
|
-
details.push({ label: 'database', value: maskedUrl });
|
|
66
|
+
details.push({ label: 'database', value: maskConnectionUrl(options.db) });
|
|
69
67
|
} else if (config.db?.connection && typeof config.db.connection === 'string') {
|
|
70
|
-
|
|
71
|
-
const maskedUrl = config.db.connection.replace(/:([^:@]+)@/, ':****@');
|
|
72
|
-
details.push({ label: 'database', value: maskedUrl });
|
|
68
|
+
details.push({ label: 'database', value: maskConnectionUrl(config.db.connection) });
|
|
73
69
|
}
|
|
74
70
|
const header = formatStyledHeader({
|
|
75
71
|
command: 'db introspect',
|
|
@@ -127,7 +123,7 @@ async function executeDbIntrospectCommand(
|
|
|
127
123
|
|
|
128
124
|
// Get masked connection URL for meta (only for string connections)
|
|
129
125
|
const connectionForMeta =
|
|
130
|
-
typeof dbConnection === 'string' ? dbConnection
|
|
126
|
+
typeof dbConnection === 'string' ? maskConnectionUrl(dbConnection) : undefined;
|
|
131
127
|
|
|
132
128
|
const introspectResult: IntrospectSchemaResult<unknown> = {
|
|
133
129
|
ok: true,
|
|
@@ -5,6 +5,7 @@ import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
|
5
5
|
import { Command } from 'commander';
|
|
6
6
|
import { loadConfig } from '../config-loader';
|
|
7
7
|
import { createControlClient } from '../control-api/client';
|
|
8
|
+
import { ContractValidationError } from '../control-api/errors';
|
|
8
9
|
import {
|
|
9
10
|
CliStructuredError,
|
|
10
11
|
errorContractValidationFailed,
|
|
@@ -14,7 +15,7 @@ import {
|
|
|
14
15
|
errorJsonFormatNotSupported,
|
|
15
16
|
errorUnexpected,
|
|
16
17
|
} from '../utils/cli-errors';
|
|
17
|
-
import { setCommandDescriptions } from '../utils/command-helpers';
|
|
18
|
+
import { maskConnectionUrl, setCommandDescriptions } from '../utils/command-helpers';
|
|
18
19
|
import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
|
|
19
20
|
import {
|
|
20
21
|
formatCommandHelp,
|
|
@@ -65,7 +66,7 @@ async function executeDbSchemaVerifyCommand(
|
|
|
65
66
|
{ label: 'contract', value: contractPath },
|
|
66
67
|
];
|
|
67
68
|
if (options.db) {
|
|
68
|
-
details.push({ label: 'database', value: options.db });
|
|
69
|
+
details.push({ label: 'database', value: maskConnectionUrl(options.db) });
|
|
69
70
|
}
|
|
70
71
|
const header = formatStyledHeader({
|
|
71
72
|
command: 'db schema-verify',
|
|
@@ -156,6 +157,14 @@ async function executeDbSchemaVerifyCommand(
|
|
|
156
157
|
return notOk(error);
|
|
157
158
|
}
|
|
158
159
|
|
|
160
|
+
if (error instanceof ContractValidationError) {
|
|
161
|
+
return notOk(
|
|
162
|
+
errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
|
|
163
|
+
where: { path: contractPathAbsolute },
|
|
164
|
+
}),
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
159
168
|
// Wrap unexpected errors
|
|
160
169
|
return notOk(
|
|
161
170
|
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
package/src/commands/db-sign.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
|
8
8
|
import { Command } from 'commander';
|
|
9
9
|
import { loadConfig } from '../config-loader';
|
|
10
10
|
import { createControlClient } from '../control-api/client';
|
|
11
|
+
import { ContractValidationError } from '../control-api/errors';
|
|
11
12
|
import {
|
|
12
13
|
CliStructuredError,
|
|
13
14
|
errorContractValidationFailed,
|
|
@@ -17,7 +18,7 @@ import {
|
|
|
17
18
|
errorJsonFormatNotSupported,
|
|
18
19
|
errorUnexpected,
|
|
19
20
|
} from '../utils/cli-errors';
|
|
20
|
-
import { setCommandDescriptions } from '../utils/command-helpers';
|
|
21
|
+
import { maskConnectionUrl, setCommandDescriptions } from '../utils/command-helpers';
|
|
21
22
|
import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
|
|
22
23
|
import {
|
|
23
24
|
formatCommandHelp,
|
|
@@ -77,7 +78,7 @@ async function executeDbSignCommand(
|
|
|
77
78
|
{ label: 'contract', value: contractPath },
|
|
78
79
|
];
|
|
79
80
|
if (options.db) {
|
|
80
|
-
details.push({ label: 'database', value: options.db });
|
|
81
|
+
details.push({ label: 'database', value: maskConnectionUrl(options.db) });
|
|
81
82
|
}
|
|
82
83
|
const header = formatStyledHeader({
|
|
83
84
|
command: 'db sign',
|
|
@@ -186,6 +187,14 @@ async function executeDbSignCommand(
|
|
|
186
187
|
return notOk(error);
|
|
187
188
|
}
|
|
188
189
|
|
|
190
|
+
if (error instanceof ContractValidationError) {
|
|
191
|
+
return notOk(
|
|
192
|
+
errorContractValidationFailed(`Contract validation failed: ${error.message}`, {
|
|
193
|
+
where: { path: contractPathAbsolute },
|
|
194
|
+
}),
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
189
198
|
// Wrap unexpected errors
|
|
190
199
|
return notOk(
|
|
191
200
|
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
@@ -203,8 +212,8 @@ export function createDbSignCommand(): Command {
|
|
|
203
212
|
command,
|
|
204
213
|
'Sign the database with your contract so you can safely run queries',
|
|
205
214
|
'Verifies that your database schema satisfies the emitted contract, and if so, writes or\n' +
|
|
206
|
-
'updates the
|
|
207
|
-
'in CI/deployment pipelines. The
|
|
215
|
+
'updates the database signature. This command is idempotent and safe to run\n' +
|
|
216
|
+
'in CI/deployment pipelines. The signature records that this database instance is aligned\n' +
|
|
208
217
|
'with a specific contract version.',
|
|
209
218
|
);
|
|
210
219
|
command
|