@prisma-next/cli 0.5.0-dev.7 → 0.5.0-dev.71
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 +56 -21
- package/dist/cli-errors-D3_sMh2K.mjs +33 -0
- package/dist/cli-errors-D3_sMh2K.mjs.map +1 -0
- package/dist/cli-errors-QH8kf-C2.d.mts +3 -0
- package/dist/cli.mjs +16 -78
- package/dist/cli.mjs.map +1 -1
- package/dist/client-0ZX24FXF.mjs +1398 -0
- package/dist/client-0ZX24FXF.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +2 -4
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +2 -4
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +14 -13
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.d.mts.map +1 -1
- package/dist/commands/db-schema.mjs +5 -7
- package/dist/commands/db-schema.mjs.map +1 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +8 -9
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.d.mts.map +1 -1
- package/dist/commands/db-update.mjs +13 -13
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.d.mts.map +1 -1
- package/dist/commands/db-verify.mjs +1 -321
- package/dist/commands/migration-apply.d.mts +5 -2
- package/dist/commands/migration-apply.d.mts.map +1 -1
- package/dist/commands/migration-apply.mjs +64 -66
- package/dist/commands/migration-apply.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts +0 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +33 -40
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +14 -5
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -347
- package/dist/commands/migration-ref.d.mts +1 -1
- package/dist/commands/migration-ref.d.mts.map +1 -1
- package/dist/commands/migration-ref.mjs +7 -12
- package/dist/commands/migration-ref.mjs.map +1 -1
- package/dist/commands/migration-show.d.mts +13 -7
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +34 -36
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +23 -5
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +2 -4
- package/dist/{config-loader-C25b63rJ.mjs → config-loader-B6sJjXTv.mjs} +3 -5
- package/dist/config-loader-B6sJjXTv.mjs.map +1 -0
- package/dist/config-loader.d.mts +0 -1
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/config-loader.mjs +2 -3
- package/dist/contract-emit-B3ChISB_.mjs +338 -0
- package/dist/contract-emit-B3ChISB_.mjs.map +1 -0
- package/dist/contract-emit-DkMqO7f2.mjs +148 -0
- package/dist/contract-emit-DkMqO7f2.mjs.map +1 -0
- package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-CF6ogEJ_.mjs} +4 -6
- package/dist/contract-enrichment-CF6ogEJ_.mjs.map +1 -0
- package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-BDKAE0B0.mjs} +12 -22
- package/dist/contract-infer-BDKAE0B0.mjs.map +1 -0
- package/dist/db-verify-B4TdDKOI.mjs +403 -0
- package/dist/db-verify-B4TdDKOI.mjs.map +1 -0
- package/dist/exports/config-types.mjs +1 -2
- package/dist/exports/control-api.d.mts +287 -29
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +4 -6
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +28 -30
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts +2 -4
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +2 -3
- package/dist/{framework-components-Cr--XBKy.mjs → framework-components-gwAHl7ml.mjs} +3 -4
- package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-gwAHl7ml.mjs.map} +1 -1
- package/dist/{init-C5220SY9.mjs → init-Deo7U8_U.mjs} +26 -35
- package/dist/init-Deo7U8_U.mjs.map +1 -0
- package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-BAgQMYpD.mjs} +10 -11
- package/dist/inspect-live-schema-BAgQMYpD.mjs.map +1 -0
- package/dist/migration-cli.d.mts +41 -12
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +309 -86
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-B8J702Uh.mjs} +8 -9
- package/dist/migration-command-scaffold-B8J702Uh.mjs.map +1 -0
- package/dist/migration-plan-BcKNnTM7.mjs +530 -0
- package/dist/migration-plan-BcKNnTM7.mjs.map +1 -0
- package/dist/{migration-status-DUMiH8_G.mjs → migration-status-CjwB2of-.mjs} +117 -64
- package/dist/migration-status-CjwB2of-.mjs.map +1 -0
- package/dist/{migrations-Bo5WtTla.mjs → migrations-CIK94AJf.mjs} +43 -23
- package/dist/migrations-CIK94AJf.mjs.map +1 -0
- package/dist/{output-BpcQrnnq.mjs → output-DnjfCC_u.mjs} +9 -3
- package/dist/output-DnjfCC_u.mjs.map +1 -0
- package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-xASh41wr.mjs} +2 -2
- package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-xASh41wr.mjs.map} +1 -1
- package/dist/{result-handler-Ba3zWQsI.mjs → result-handler-DWb1rFS-.mjs} +52 -27
- package/dist/result-handler-DWb1rFS-.mjs.map +1 -0
- package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-zaRDhJnP.mjs} +2 -6
- package/dist/{terminal-ui-C3ZLwQxK.mjs.map → terminal-ui-zaRDhJnP.mjs.map} +1 -1
- package/dist/{verify-Bkycc-Tf.mjs → verify-BEIa9638.mjs} +3 -4
- package/dist/verify-BEIa9638.mjs.map +1 -0
- package/package.json +28 -26
- package/src/cli.ts +32 -6
- package/src/commands/contract-emit.ts +67 -163
- package/src/commands/contract-infer.ts +7 -20
- package/src/commands/db-init.ts +14 -3
- package/src/commands/db-update.ts +8 -4
- package/src/commands/db-verify.ts +47 -15
- package/src/commands/init/index.ts +1 -1
- package/src/commands/init/init.ts +2 -2
- package/src/commands/init/templates/code-templates.ts +12 -4
- package/src/commands/inspect-live-schema.ts +10 -5
- package/src/commands/migration-apply.ts +92 -71
- package/src/commands/migration-new.ts +42 -45
- package/src/commands/migration-plan.ts +147 -64
- package/src/commands/migration-ref.ts +8 -7
- package/src/commands/migration-show.ts +60 -41
- package/src/commands/migration-status.ts +196 -60
- package/src/config-path-validation.ts +0 -1
- package/src/control-api/client.ts +69 -1
- package/src/control-api/contract-enrichment.ts +6 -4
- package/src/control-api/operations/contract-emit.ts +198 -115
- package/src/control-api/operations/db-apply-aggregate.ts +446 -0
- package/src/control-api/operations/db-init.ts +51 -253
- package/src/control-api/operations/db-update.ts +66 -183
- package/src/control-api/operations/db-verify.ts +342 -0
- package/src/control-api/operations/migration-apply.ts +37 -9
- package/src/control-api/types.ts +125 -7
- package/src/exports/control-api.ts +15 -3
- package/src/load-ts-contract.ts +28 -26
- package/src/migration-cli.ts +445 -122
- package/src/utils/cli-errors.ts +49 -2
- package/src/utils/combine-schema-results.ts +84 -0
- package/src/utils/command-helpers.ts +69 -25
- package/src/utils/contract-space-aggregate-loader.ts +236 -0
- package/src/utils/contract-space-extension-migrations-pass.ts +120 -0
- package/src/utils/contract-space-migrate-pass.ts +156 -0
- package/src/utils/emit-queue.ts +26 -0
- package/src/utils/formatters/graph-migration-mapper.ts +7 -3
- package/src/utils/formatters/migrations.ts +62 -26
- package/src/utils/publish-contract-artifact-pair.ts +134 -0
- package/dist/cli-errors-BFYgBH3L.d.mts +0 -4
- package/dist/cli-errors-Cd79vmTH.mjs +0 -5
- package/dist/client-CrsnY58k.mjs +0 -997
- package/dist/client-CrsnY58k.mjs.map +0 -1
- package/dist/commands/db-verify.mjs.map +0 -1
- package/dist/commands/migration-plan.mjs.map +0 -1
- package/dist/config-loader-C25b63rJ.mjs.map +0 -1
- package/dist/contract-emit--feXyNd7.mjs +0 -4
- package/dist/contract-emit-NJ01hiiv.mjs +0 -195
- package/dist/contract-emit-NJ01hiiv.mjs.map +0 -1
- package/dist/contract-emit-V5SSitUT.mjs +0 -122
- package/dist/contract-emit-V5SSitUT.mjs.map +0 -1
- package/dist/contract-enrichment-CAOELa-H.mjs.map +0 -1
- package/dist/contract-infer-D9cC3rJm.mjs.map +0 -1
- package/dist/extract-operation-statements-DsFfxXVZ.mjs +0 -13
- package/dist/extract-operation-statements-DsFfxXVZ.mjs.map +0 -1
- package/dist/extract-sql-ddl-D9UbZDyz.mjs +0 -26
- package/dist/extract-sql-ddl-D9UbZDyz.mjs.map +0 -1
- package/dist/init-C5220SY9.mjs.map +0 -1
- package/dist/inspect-live-schema-yrHAvG71.mjs.map +0 -1
- package/dist/migration-command-scaffold-B3B09et6.mjs.map +0 -1
- package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
- package/dist/migrations-Bo5WtTla.mjs.map +0 -1
- package/dist/output-BpcQrnnq.mjs.map +0 -1
- package/dist/result-handler-Ba3zWQsI.mjs.map +0 -1
- package/dist/validate-contract-deps-B_Cs29TL.mjs +0 -37
- package/dist/validate-contract-deps-B_Cs29TL.mjs.map +0 -1
- package/dist/verify-Bkycc-Tf.mjs.map +0 -1
- package/src/control-api/operations/extract-operation-statements.ts +0 -14
- package/src/control-api/operations/extract-sql-ddl.ts +0 -47
package/src/cli.ts
CHANGED
|
@@ -50,8 +50,18 @@ program.configureOutput({
|
|
|
50
50
|
writeErr: () => {
|
|
51
51
|
// Suppress all default error output - we handle errors in exitOverride
|
|
52
52
|
},
|
|
53
|
-
writeOut: () => {
|
|
54
|
-
//
|
|
53
|
+
writeOut: (str) => {
|
|
54
|
+
// Commander routes explicitly-requested `--help` (success-path help)
|
|
55
|
+
// through writeOut; per the Style Guide § Output Conventions rule 8,
|
|
56
|
+
// user-requested help is data and goes to stdout. Error-path help
|
|
57
|
+
// (e.g. usage shown after an unknown command) goes through writeErr,
|
|
58
|
+
// which stays suppressed because we render that ourselves with the
|
|
59
|
+
// matching error envelope.
|
|
60
|
+
//
|
|
61
|
+
// Explicit `--version` is short-circuited before `program.parse()`
|
|
62
|
+
// (see the argv pre-scan at the bottom of this file), so it does not
|
|
63
|
+
// reach this writer.
|
|
64
|
+
process.stdout.write(str);
|
|
55
65
|
},
|
|
56
66
|
});
|
|
57
67
|
|
|
@@ -261,14 +271,25 @@ const helpCommand = new Command('help')
|
|
|
261
271
|
.action(() => {
|
|
262
272
|
const flags = parseGlobalFlags({});
|
|
263
273
|
const helpText = formatRootHelp({ program, flags });
|
|
264
|
-
//
|
|
265
|
-
|
|
274
|
+
// The `help` command was invoked explicitly: help is the data the
|
|
275
|
+
// caller asked for. Per Style Guide § Output Conventions rule 8,
|
|
276
|
+
// explicit help goes to stdout with exit code 0.
|
|
277
|
+
process.stdout.write(`${helpText}\n`);
|
|
266
278
|
process.exit(0);
|
|
267
279
|
});
|
|
268
280
|
|
|
269
281
|
program.addCommand(helpCommand);
|
|
270
282
|
|
|
271
|
-
// Set help as the default action when no command is provided
|
|
283
|
+
// Set help as the default action when no command is provided. The user
|
|
284
|
+
// did not invoke `--help`; we are voluntarily showing usage to help them
|
|
285
|
+
// recover from an underspecified invocation, so the help text is
|
|
286
|
+
// decoration around an implicit "what did you want me to do?" and goes
|
|
287
|
+
// to stderr (Style Guide § Output Conventions rule 8).
|
|
288
|
+
//
|
|
289
|
+
// FOLLOW-UP: the exit code here is 0 today, but a no-arg invocation is
|
|
290
|
+
// arguably a usage error (PRECONDITION → exit 2) for consistency with
|
|
291
|
+
// the unknown-command path. Out of scope for the explicit-help routing
|
|
292
|
+
// work; revisit when tightening exit-code semantics across the CLI.
|
|
272
293
|
program.action(() => {
|
|
273
294
|
const flags = parseGlobalFlags({});
|
|
274
295
|
const helpText = formatRootHelp({ program, flags });
|
|
@@ -304,7 +325,12 @@ if (args.length > 0) {
|
|
|
304
325
|
process.stderr.write(`${helpText}\n`);
|
|
305
326
|
process.exit(2);
|
|
306
327
|
} else if (command.commands.length > 0 && args.length === 1) {
|
|
307
|
-
// Parent command called with no subcommand
|
|
328
|
+
// Parent command called with no subcommand. Same shape as the
|
|
329
|
+
// no-args case above: the user did not request help, we are
|
|
330
|
+
// voluntarily rendering it as decoration around an underspecified
|
|
331
|
+
// invocation, so it goes to stderr per Style Guide § Output
|
|
332
|
+
// Conventions rule 8. Exit code 0 today; the FOLLOW-UP note on
|
|
333
|
+
// `program.action` applies here too (arguably should be 2).
|
|
308
334
|
const flags = parseGlobalFlags({});
|
|
309
335
|
const helpText = formatCommandHelp({ command, flags });
|
|
310
336
|
process.stderr.write(`${helpText}\n`);
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
1
|
import { getEmittedArtifactPaths } from '@prisma-next/emitter';
|
|
3
2
|
import { errorContractConfigMissing } from '@prisma-next/errors/control';
|
|
3
|
+
import { ifDefined } from '@prisma-next/utils/defined';
|
|
4
4
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
5
5
|
import { Command } from 'commander';
|
|
6
6
|
import { dirname, relative, resolve } from 'pathe';
|
|
7
7
|
import { loadConfig } from '../config-loader';
|
|
8
|
-
import {
|
|
9
|
-
import type {
|
|
10
|
-
import {
|
|
11
|
-
CliStructuredError,
|
|
12
|
-
errorContractValidationFailed,
|
|
13
|
-
errorRuntime,
|
|
14
|
-
errorUnexpected,
|
|
15
|
-
} from '../utils/cli-errors';
|
|
8
|
+
import { executeContractEmit } from '../control-api/operations/contract-emit';
|
|
9
|
+
import type { ContractEmitResult } from '../control-api/types';
|
|
10
|
+
import { CliStructuredError, errorUnexpected } from '../utils/cli-errors';
|
|
16
11
|
import {
|
|
17
12
|
addGlobalOptions,
|
|
18
13
|
setCommandDescriptions,
|
|
@@ -34,74 +29,29 @@ interface ContractEmitOptions extends CommonCommandOptions {
|
|
|
34
29
|
readonly config?: string;
|
|
35
30
|
}
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return diagnostics.map((diagnostic) => {
|
|
42
|
-
const location =
|
|
43
|
-
diagnostic.sourceId && diagnostic.span
|
|
44
|
-
? ` (${diagnostic.sourceId}:${diagnostic.span.start.line}:${diagnostic.span.start.column})`
|
|
45
|
-
: diagnostic.sourceId
|
|
46
|
-
? ` (${diagnostic.sourceId})`
|
|
47
|
-
: '';
|
|
48
|
-
return {
|
|
49
|
-
kind: diagnostic.code,
|
|
50
|
-
message: `${diagnostic.message}${location}`,
|
|
51
|
-
};
|
|
52
|
-
});
|
|
32
|
+
interface HeaderPaths {
|
|
33
|
+
readonly displayConfigPath: string;
|
|
34
|
+
readonly outputJsonPath: string;
|
|
35
|
+
readonly outputDtsPath: string;
|
|
53
36
|
}
|
|
54
37
|
|
|
55
38
|
/**
|
|
56
|
-
*
|
|
39
|
+
* Pre-load the config just to compute display paths for the styled header. The
|
|
40
|
+
* actual emit work goes through `executeContractEmit`, which loads the config
|
|
41
|
+
* itself; the redundant load here is bounded and lets the header render before
|
|
42
|
+
* the emit spans start.
|
|
57
43
|
*/
|
|
58
|
-
function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return errorRuntime(failure.summary, {
|
|
65
|
-
why: failure.why ?? 'Contract source provider failed',
|
|
66
|
-
fix: 'Check your contract source provider in prisma-next.config.ts and ensure it returns Result<Contract, Diagnostics>',
|
|
67
|
-
...(issues.length > 0 ? { meta: { issues } } : {}),
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (failure.code === 'CONTRACT_VALIDATION_FAILED') {
|
|
72
|
-
return errorContractValidationFailed(
|
|
73
|
-
failure.why ?? 'Contract validation failed while emitting',
|
|
74
|
-
context?.configPath ? { where: { path: context.configPath } } : undefined,
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (failure.code === 'EMIT_FAILED') {
|
|
79
|
-
return errorRuntime(failure.summary, {
|
|
80
|
-
why: failure.why ?? 'Failed to emit contract',
|
|
81
|
-
fix: 'Check your contract configuration and ensure the source is valid',
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Exhaustive check - TypeScript will error if a new code is added but not handled
|
|
86
|
-
const exhaustive: never = failure.code;
|
|
87
|
-
throw new Error(`Unhandled EmitFailure code: ${exhaustive}`);
|
|
88
|
-
}
|
|
44
|
+
async function resolveHeaderPaths(
|
|
45
|
+
configOption: string | undefined,
|
|
46
|
+
): Promise<Result<HeaderPaths, CliStructuredError>> {
|
|
47
|
+
const displayConfigPath = configOption
|
|
48
|
+
? relative(process.cwd(), resolve(configOption))
|
|
49
|
+
: 'prisma-next.config.ts';
|
|
89
50
|
|
|
90
|
-
/**
|
|
91
|
-
* Executes the contract emit command and returns a structured Result.
|
|
92
|
-
*/
|
|
93
|
-
async function executeContractEmitCommand(
|
|
94
|
-
options: ContractEmitOptions,
|
|
95
|
-
flags: GlobalFlags,
|
|
96
|
-
ui: TerminalUI,
|
|
97
|
-
startTime: number,
|
|
98
|
-
): Promise<Result<EmitContractResult, CliStructuredError>> {
|
|
99
|
-
// Load config
|
|
100
51
|
let config: Awaited<ReturnType<typeof loadConfig>>;
|
|
101
52
|
try {
|
|
102
|
-
config = await loadConfig(
|
|
53
|
+
config = await loadConfig(configOption);
|
|
103
54
|
} catch (error) {
|
|
104
|
-
// Convert thrown CliStructuredError to Result
|
|
105
55
|
if (error instanceof CliStructuredError) {
|
|
106
56
|
return notOk(error);
|
|
107
57
|
}
|
|
@@ -112,33 +62,19 @@ async function executeContractEmitCommand(
|
|
|
112
62
|
);
|
|
113
63
|
}
|
|
114
64
|
|
|
115
|
-
|
|
116
|
-
? relative(process.cwd(), resolve(options.config))
|
|
117
|
-
: 'prisma-next.config.ts';
|
|
118
|
-
|
|
119
|
-
// Resolve contract from config
|
|
120
|
-
if (!config.contract) {
|
|
65
|
+
if (!config.contract?.output) {
|
|
121
66
|
return notOk(
|
|
122
67
|
errorContractConfigMissing({
|
|
123
|
-
why: 'Config.contract is required for emit. Define it in your config: contract: { source: ..., output: ... }',
|
|
68
|
+
why: 'Config.contract.output is required for emit. Define it in your config: contract: { source: ..., output: ... }',
|
|
124
69
|
}),
|
|
125
70
|
);
|
|
126
71
|
}
|
|
127
72
|
|
|
128
|
-
// Contract config is already normalized by defineConfig() with defaults applied
|
|
129
|
-
const contractConfig = config.contract;
|
|
130
|
-
|
|
131
|
-
// Resolve artifact paths from config (already normalized by defineConfig() with defaults)
|
|
132
|
-
if (!contractConfig.output) {
|
|
133
|
-
return notOk(
|
|
134
|
-
errorContractConfigMissing({
|
|
135
|
-
why: 'Contract config must have output path. This should not happen if defineConfig() was used.',
|
|
136
|
-
}),
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
let outputPaths: ReturnType<typeof getEmittedArtifactPaths>;
|
|
140
73
|
try {
|
|
141
|
-
|
|
74
|
+
const { jsonPath: outputJsonPath, dtsPath: outputDtsPath } = getEmittedArtifactPaths(
|
|
75
|
+
config.contract.output,
|
|
76
|
+
);
|
|
77
|
+
return ok({ displayConfigPath, outputJsonPath, outputDtsPath });
|
|
142
78
|
} catch (error) {
|
|
143
79
|
return notOk(
|
|
144
80
|
errorContractConfigMissing({
|
|
@@ -146,96 +82,65 @@ async function executeContractEmitCommand(
|
|
|
146
82
|
}),
|
|
147
83
|
);
|
|
148
84
|
}
|
|
149
|
-
|
|
85
|
+
}
|
|
150
86
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
{ label: 'config', value: configPath },
|
|
161
|
-
{ label: 'contract', value: contractPath },
|
|
162
|
-
{ label: 'types', value: typesPath },
|
|
163
|
-
],
|
|
164
|
-
flags,
|
|
165
|
-
});
|
|
166
|
-
ui.stderr(header);
|
|
87
|
+
async function executeContractEmitCommand(
|
|
88
|
+
options: ContractEmitOptions,
|
|
89
|
+
flags: GlobalFlags,
|
|
90
|
+
ui: TerminalUI,
|
|
91
|
+
startTime: number,
|
|
92
|
+
): Promise<Result<EmitContractResult, CliStructuredError>> {
|
|
93
|
+
const headerPathsResult = await resolveHeaderPaths(options.config);
|
|
94
|
+
if (!headerPathsResult.ok) {
|
|
95
|
+
return headerPathsResult;
|
|
167
96
|
}
|
|
97
|
+
const { displayConfigPath, outputJsonPath, outputDtsPath } = headerPathsResult.value;
|
|
168
98
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
99
|
+
if (!flags.json && !flags.quiet) {
|
|
100
|
+
ui.stderr(
|
|
101
|
+
formatStyledHeader({
|
|
102
|
+
command: 'contract emit',
|
|
103
|
+
description: 'Emit your contract artifacts',
|
|
104
|
+
url: 'https://pris.ly/contract-emit',
|
|
105
|
+
details: [
|
|
106
|
+
{ label: 'config', value: displayConfigPath },
|
|
107
|
+
{ label: 'contract', value: relative(process.cwd(), outputJsonPath) },
|
|
108
|
+
{ label: 'types', value: relative(process.cwd(), outputDtsPath) },
|
|
109
|
+
],
|
|
110
|
+
flags,
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
176
114
|
|
|
177
|
-
// Create progress adapter
|
|
178
115
|
const onProgress = createProgressAdapter({ ui, flags });
|
|
116
|
+
const configPath = options.config ? resolve(options.config) : 'prisma-next.config.ts';
|
|
179
117
|
|
|
118
|
+
let result: ContractEmitResult;
|
|
180
119
|
try {
|
|
181
|
-
|
|
182
|
-
const result = await client.emit({
|
|
183
|
-
contractConfig: {
|
|
184
|
-
source: contractConfig.source,
|
|
185
|
-
output: outputJsonPath,
|
|
186
|
-
},
|
|
187
|
-
onProgress,
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
// Handle failures by mapping to CLI structured error
|
|
191
|
-
if (!result.ok) {
|
|
192
|
-
return notOk(mapEmitFailure(result.failure, { configPath }));
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Create directories if needed
|
|
196
|
-
mkdirSync(dirname(outputJsonPath), { recursive: true });
|
|
197
|
-
mkdirSync(dirname(outputDtsPath), { recursive: true });
|
|
198
|
-
|
|
199
|
-
// Write the results to files
|
|
200
|
-
writeFileSync(outputJsonPath, result.value.contractJson, 'utf-8');
|
|
201
|
-
writeFileSync(outputDtsPath, result.value.contractDts, 'utf-8');
|
|
202
|
-
|
|
203
|
-
// Validate that contract.d.ts type imports are resolvable
|
|
204
|
-
const { validateContractDeps } = await import('../utils/validate-contract-deps');
|
|
205
|
-
const depsValidation = validateContractDeps(result.value.contractDts, dirname(outputDtsPath));
|
|
206
|
-
if (depsValidation.warning) {
|
|
207
|
-
ui.warn(depsValidation.warning);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Convert success result to CLI output format
|
|
211
|
-
const emitResult: EmitContractResult = {
|
|
212
|
-
storageHash: result.value.storageHash,
|
|
213
|
-
...(result.value.executionHash ? { executionHash: result.value.executionHash } : {}),
|
|
214
|
-
profileHash: result.value.profileHash,
|
|
215
|
-
outDir: dirname(outputJsonPath),
|
|
216
|
-
files: {
|
|
217
|
-
json: outputJsonPath,
|
|
218
|
-
dts: outputDtsPath,
|
|
219
|
-
},
|
|
220
|
-
timings: { total: Date.now() - startTime },
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
return ok(emitResult);
|
|
120
|
+
result = await executeContractEmit({ configPath, onProgress });
|
|
224
121
|
} catch (error) {
|
|
225
|
-
// Use static type guard to work across module boundaries
|
|
226
122
|
if (CliStructuredError.is(error)) {
|
|
227
123
|
return notOk(error);
|
|
228
124
|
}
|
|
229
|
-
|
|
230
|
-
// Wrap unexpected errors
|
|
231
125
|
return notOk(
|
|
232
126
|
errorUnexpected('Unexpected error during contract emit', {
|
|
233
127
|
why: error instanceof Error ? error.message : String(error),
|
|
234
128
|
}),
|
|
235
129
|
);
|
|
236
|
-
} finally {
|
|
237
|
-
await client.close();
|
|
238
130
|
}
|
|
131
|
+
|
|
132
|
+
if (result.validationWarning) {
|
|
133
|
+
ui.warn(result.validationWarning);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return ok({
|
|
137
|
+
storageHash: result.storageHash,
|
|
138
|
+
...ifDefined('executionHash', result.executionHash),
|
|
139
|
+
profileHash: result.profileHash,
|
|
140
|
+
outDir: dirname(result.files.json),
|
|
141
|
+
files: result.files,
|
|
142
|
+
timings: { total: Date.now() - startTime },
|
|
143
|
+
});
|
|
239
144
|
}
|
|
240
145
|
|
|
241
146
|
export function createContractEmitCommand(): Command {
|
|
@@ -260,7 +165,6 @@ export function createContractEmitCommand(): Command {
|
|
|
260
165
|
|
|
261
166
|
const result = await executeContractEmitCommand(options, flags, ui, startTime);
|
|
262
167
|
|
|
263
|
-
// Handle result - formats output and returns exit code
|
|
264
168
|
const exitCode = handleResult(result, flags, ui, (emitResult) => {
|
|
265
169
|
if (flags.json) {
|
|
266
170
|
ui.output(formatEmitJson(emitResult));
|
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { errorRuntime } from '@prisma-next/errors/execution';
|
|
3
|
-
import { printPsl
|
|
4
|
-
import {
|
|
5
|
-
createPostgresDefaultMapping,
|
|
6
|
-
createPostgresTypeMap,
|
|
7
|
-
extractEnumInfo,
|
|
8
|
-
parseRawDefault,
|
|
9
|
-
} from '@prisma-next/psl-printer/postgres';
|
|
3
|
+
import { printPsl } from '@prisma-next/psl-printer';
|
|
10
4
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
11
5
|
import { Command } from 'commander';
|
|
12
6
|
import { dirname, relative } from 'pathe';
|
|
@@ -59,26 +53,19 @@ async function executeContractInferCommand(
|
|
|
59
53
|
return inspectResult;
|
|
60
54
|
}
|
|
61
55
|
|
|
62
|
-
const { config, target, meta } = inspectResult.value;
|
|
56
|
+
const { config, target, meta, pslContractAst } = inspectResult.value;
|
|
63
57
|
|
|
64
|
-
if (
|
|
58
|
+
if (!pslContractAst) {
|
|
65
59
|
return notOk(
|
|
66
|
-
errorRuntime(
|
|
67
|
-
why: 'contract
|
|
68
|
-
fix: 'Use
|
|
60
|
+
errorRuntime('contract infer is not supported for this family', {
|
|
61
|
+
why: 'The configured family does not implement the PslContractInferCapable capability, so an inferred PSL contract cannot be produced from the live database schema.',
|
|
62
|
+
fix: 'Use a family that supports contract inference (e.g. SQL/Postgres).',
|
|
69
63
|
}),
|
|
70
64
|
);
|
|
71
65
|
}
|
|
72
66
|
|
|
73
|
-
const schema = validatePrintableSqlSchemaIR(inspectResult.value.schema);
|
|
74
67
|
const outputPath = resolveContractInferOutputPath(options, config.contract?.output);
|
|
75
|
-
const
|
|
76
|
-
const pslContent = printPsl(schema, {
|
|
77
|
-
defaultMapping: createPostgresDefaultMapping(),
|
|
78
|
-
typeMap: createPostgresTypeMap(enumInfo.typeNames),
|
|
79
|
-
enumInfo,
|
|
80
|
-
parseRawDefault,
|
|
81
|
-
});
|
|
68
|
+
const pslContent = printPsl(pslContractAst);
|
|
82
69
|
|
|
83
70
|
if (existsSync(outputPath) && !flags.json && !flags.quiet) {
|
|
84
71
|
ui.stderr(`\u26A0 Overwriting existing file: ${relative(process.cwd(), outputPath)}`);
|
package/src/commands/db-init.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from '../utils/cli-errors';
|
|
14
14
|
import type { MigrationCommandOptions } from '../utils/command-helpers';
|
|
15
15
|
import {
|
|
16
|
+
resolveMigrationPaths,
|
|
16
17
|
sanitizeErrorMessage,
|
|
17
18
|
setCommandDescriptions,
|
|
18
19
|
setCommandExamples,
|
|
@@ -111,14 +112,23 @@ async function executeDbInitCommand(
|
|
|
111
112
|
if (!ctxResult.ok) {
|
|
112
113
|
return ctxResult;
|
|
113
114
|
}
|
|
114
|
-
const { client, contractJson, dbConnection, onProgress, contractPathAbsolute } =
|
|
115
|
+
const { client, config, contractJson, dbConnection, onProgress, contractPathAbsolute } =
|
|
116
|
+
ctxResult.value;
|
|
117
|
+
|
|
118
|
+
// The aggregate loader (loader → planner → runner pipeline) catches
|
|
119
|
+
// layout / drift / disjointness violations on its own; the legacy
|
|
120
|
+
// per-space precheck + marker-check helpers are no longer needed at
|
|
121
|
+
// this surface. Marker-vs-on-disk drift surfaces through the planner's
|
|
122
|
+
// graph-walk strategy.
|
|
123
|
+
const { migrationsDir } = resolveMigrationPaths(options.config, config);
|
|
115
124
|
|
|
116
125
|
try {
|
|
117
|
-
|
|
126
|
+
await client.connect(dbConnection);
|
|
127
|
+
|
|
118
128
|
const result = await client.dbInit({
|
|
119
129
|
contract: contractJson,
|
|
120
130
|
mode: options.dryRun ? 'plan' : 'apply',
|
|
121
|
-
|
|
131
|
+
migrationsDir,
|
|
122
132
|
onProgress,
|
|
123
133
|
});
|
|
124
134
|
|
|
@@ -142,6 +152,7 @@ async function executeDbInitCommand(
|
|
|
142
152
|
label: op.label,
|
|
143
153
|
operationClass: op.operationClass,
|
|
144
154
|
})),
|
|
155
|
+
...ifDefined('preview', result.value.plan.preview),
|
|
145
156
|
},
|
|
146
157
|
...(result.value.execution
|
|
147
158
|
? {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '../utils/cli-errors';
|
|
15
15
|
import type { MigrationCommandOptions } from '../utils/command-helpers';
|
|
16
16
|
import {
|
|
17
|
+
resolveMigrationPaths,
|
|
17
18
|
sanitizeErrorMessage,
|
|
18
19
|
setCommandDescriptions,
|
|
19
20
|
setCommandExamples,
|
|
@@ -80,14 +81,17 @@ async function executeDbUpdateCommand(
|
|
|
80
81
|
if (!ctxResult.ok) {
|
|
81
82
|
return ctxResult;
|
|
82
83
|
}
|
|
83
|
-
const { client, contractJson, dbConnection, onProgress, contractPathAbsolute } =
|
|
84
|
+
const { client, config, contractJson, dbConnection, onProgress, contractPathAbsolute } =
|
|
85
|
+
ctxResult.value;
|
|
86
|
+
const { migrationsDir } = resolveMigrationPaths(options.config, config);
|
|
84
87
|
|
|
85
88
|
try {
|
|
86
|
-
|
|
89
|
+
await client.connect(dbConnection);
|
|
90
|
+
|
|
87
91
|
const result = await client.dbUpdate({
|
|
88
92
|
contract: contractJson,
|
|
89
93
|
mode: options.dryRun ? 'plan' : 'apply',
|
|
90
|
-
|
|
94
|
+
migrationsDir,
|
|
91
95
|
...(flags.yes ? { acceptDataLoss: true } : {}),
|
|
92
96
|
onProgress,
|
|
93
97
|
});
|
|
@@ -112,7 +116,7 @@ async function executeDbUpdateCommand(
|
|
|
112
116
|
label: op.label,
|
|
113
117
|
operationClass: op.operationClass,
|
|
114
118
|
})),
|
|
115
|
-
...ifDefined('
|
|
119
|
+
...ifDefined('preview', result.value.plan.preview),
|
|
116
120
|
},
|
|
117
121
|
...ifDefined(
|
|
118
122
|
'execution',
|
|
@@ -27,10 +27,12 @@ import {
|
|
|
27
27
|
errorTargetMismatch,
|
|
28
28
|
errorUnexpected,
|
|
29
29
|
} from '../utils/cli-errors';
|
|
30
|
+
import { combineSchemaResults } from '../utils/combine-schema-results';
|
|
30
31
|
import {
|
|
31
32
|
addGlobalOptions,
|
|
32
33
|
maskConnectionUrl,
|
|
33
34
|
resolveContractPath,
|
|
35
|
+
resolveMigrationPaths,
|
|
34
36
|
setCommandDescriptions,
|
|
35
37
|
setCommandExamples,
|
|
36
38
|
} from '../utils/command-helpers';
|
|
@@ -346,22 +348,42 @@ async function executeDbVerifyCommand(
|
|
|
346
348
|
const setupResult = await resolveVerifySetup(paths, options, mode);
|
|
347
349
|
if (!setupResult.ok) return setupResult;
|
|
348
350
|
const { contractJson, dbConnection, contractPathAbsolute } = setupResult.value;
|
|
351
|
+
const { migrationsDir } = resolveMigrationPaths(options.config, setupResult.value.config);
|
|
349
352
|
|
|
350
353
|
const client = createVerifyClient(setupResult.value);
|
|
351
354
|
const onProgress = createProgressAdapter({ ui, flags });
|
|
352
355
|
|
|
353
356
|
try {
|
|
357
|
+
// Single-contract marker verification preserved for the existing
|
|
358
|
+
// marker / target / hash failure surface (`PN-RUN-3001/3002/3003`).
|
|
359
|
+
// The aggregate verifier (run below for the per-space marker /
|
|
360
|
+
// schema checks) does not duplicate this: it concerns itself with
|
|
361
|
+
// marker-vs-on-disk and orphan-marker drift, not the
|
|
362
|
+
// hash-mismatch-against-the-app-contract lane that today's
|
|
363
|
+
// `client.verify` covers.
|
|
354
364
|
const verifyResult = await client.verify({
|
|
355
365
|
contract: contractJson,
|
|
356
366
|
connection: dbConnection,
|
|
357
367
|
onProgress,
|
|
358
368
|
});
|
|
359
369
|
|
|
360
|
-
// If verification failed, map to CLI structured error
|
|
361
370
|
if (!verifyResult.ok) {
|
|
362
371
|
return notOk(mapVerifyFailure(verifyResult));
|
|
363
372
|
}
|
|
364
373
|
|
|
374
|
+
// Aggregate verifier (loader → verifier pipeline). Runs the layout
|
|
375
|
+
// precheck, marker-aware per-space verifier, and (full mode only)
|
|
376
|
+
// per-space pre-projected schema verification (closes F23).
|
|
377
|
+
const aggregateResult = await client.dbVerify({
|
|
378
|
+
contract: contractJson,
|
|
379
|
+
migrationsDir,
|
|
380
|
+
strict: options.strict ?? false,
|
|
381
|
+
skipSchema: mode === 'marker-only',
|
|
382
|
+
skipMarker: false,
|
|
383
|
+
onProgress,
|
|
384
|
+
});
|
|
385
|
+
if (!aggregateResult.ok) return notOk(aggregateResult.failure);
|
|
386
|
+
|
|
365
387
|
if (mode === 'marker-only') {
|
|
366
388
|
return ok({
|
|
367
389
|
ok: true,
|
|
@@ -381,14 +403,13 @@ async function executeDbVerifyCommand(
|
|
|
381
403
|
});
|
|
382
404
|
}
|
|
383
405
|
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
return notOk(schemaVerifyResult);
|
|
406
|
+
const combined = combineSchemaResults(
|
|
407
|
+
aggregateResult.value.schemaResults,
|
|
408
|
+
aggregateResult.value.appSpaceId,
|
|
409
|
+
options.strict ?? false,
|
|
410
|
+
);
|
|
411
|
+
if (!combined.ok) {
|
|
412
|
+
return notOk(combined);
|
|
392
413
|
}
|
|
393
414
|
|
|
394
415
|
return ok({
|
|
@@ -401,9 +422,9 @@ async function executeDbVerifyCommand(
|
|
|
401
422
|
...ifDefined('missingCodecs', verifyResult.missingCodecs),
|
|
402
423
|
...ifDefined('codecCoverageSkipped', verifyResult.codecCoverageSkipped),
|
|
403
424
|
schema: {
|
|
404
|
-
summary:
|
|
405
|
-
counts:
|
|
406
|
-
strict:
|
|
425
|
+
summary: combined.summary,
|
|
426
|
+
counts: combined.schema.counts,
|
|
427
|
+
strict: combined.meta?.strict ?? false,
|
|
407
428
|
},
|
|
408
429
|
meta: {
|
|
409
430
|
...(verifyResult.meta ?? {}),
|
|
@@ -429,19 +450,30 @@ async function executeDbSchemaOnlyVerifyCommand(
|
|
|
429
450
|
const setupResult = await resolveVerifySetup(paths, options, 'schema-only');
|
|
430
451
|
if (!setupResult.ok) return setupResult;
|
|
431
452
|
const { contractJson, dbConnection, contractPathAbsolute } = setupResult.value;
|
|
453
|
+
const { migrationsDir } = resolveMigrationPaths(options.config, setupResult.value.config);
|
|
432
454
|
|
|
433
455
|
const client = createVerifyClient(setupResult.value);
|
|
434
456
|
const onProgress = createProgressAdapter({ ui, flags });
|
|
435
457
|
|
|
436
458
|
try {
|
|
437
|
-
|
|
459
|
+
await client.connect(dbConnection);
|
|
460
|
+
const aggregateResult = await client.dbVerify({
|
|
438
461
|
contract: contractJson,
|
|
462
|
+
migrationsDir,
|
|
439
463
|
strict: options.strict ?? false,
|
|
440
|
-
|
|
464
|
+
skipSchema: false,
|
|
465
|
+
skipMarker: true,
|
|
441
466
|
onProgress,
|
|
442
467
|
});
|
|
468
|
+
if (!aggregateResult.ok) return notOk(aggregateResult.failure);
|
|
443
469
|
|
|
444
|
-
return ok(
|
|
470
|
+
return ok(
|
|
471
|
+
combineSchemaResults(
|
|
472
|
+
aggregateResult.value.schemaResults,
|
|
473
|
+
aggregateResult.value.appSpaceId,
|
|
474
|
+
options.strict ?? false,
|
|
475
|
+
),
|
|
476
|
+
);
|
|
445
477
|
} catch (error) {
|
|
446
478
|
return wrapVerifyError(error, contractPathAbsolute, 'db verify --schema-only');
|
|
447
479
|
} finally {
|
|
@@ -113,7 +113,7 @@ export function createInitCommand(): Command {
|
|
|
113
113
|
* we honour it (e.g. testing flows where stdin is stubbed).
|
|
114
114
|
*
|
|
115
115
|
* Exported so callers and tests can derive the same value without
|
|
116
|
-
* touching `process` globals
|
|
116
|
+
* touching `process` globals.
|
|
117
117
|
*/
|
|
118
118
|
export function deriveCanPrompt(opts: {
|
|
119
119
|
readonly flagsInteractive: boolean | undefined;
|
|
@@ -569,7 +569,7 @@ async function runInstall(ctx: {
|
|
|
569
569
|
/**
|
|
570
570
|
* FR2.1 — set when the user already declares `@types/node` directly in
|
|
571
571
|
* `dependencies` or `devDependencies`. We then skip adding it so a
|
|
572
|
-
*
|
|
572
|
+
* locked major (e.g. `^18` for a Node 18 runtime) survives `init`
|
|
573
573
|
* unchanged. Transitive presence is intentionally ignored: detecting
|
|
574
574
|
* it requires lockfile introspection and the realistic clobber risk
|
|
575
575
|
* is the direct-pin case.
|
|
@@ -585,7 +585,7 @@ async function runInstall(ctx: {
|
|
|
585
585
|
// Pin it as a devDep rather than relying on a transitive resolution
|
|
586
586
|
// through `dotenv` (whose types bundle is internal and not guaranteed
|
|
587
587
|
// across versions). Skip when the user already declares `@types/node`
|
|
588
|
-
// directly so a
|
|
588
|
+
// directly so a locked major (e.g. `^18` for a Node 18 runtime) is
|
|
589
589
|
// preserved. Listed last so the install log still leads with the
|
|
590
590
|
// user-relevant `prisma-next` line.
|
|
591
591
|
const devDeps = hasTypesNode ? ['prisma-next'] : ['prisma-next', '@types/node'];
|