@prisma-next/cli 0.4.2 → 0.4.3
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-By1iVE3z.mjs +34 -0
- package/dist/cli-errors-By1iVE3z.mjs.map +1 -0
- package/dist/{cli-errors-BFYgBH3L.d.mts → cli-errors-DDeVsP2Y.d.mts} +1 -0
- package/dist/cli.mjs +19 -16
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-CrsnY58k.mjs → client-1JqqkiC7.mjs} +45 -20
- package/dist/client-1JqqkiC7.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +7 -7
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +8 -8
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +11 -10
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-schema.mjs +8 -8
- package/dist/commands/db-sign.mjs +8 -8
- package/dist/commands/db-update.mjs +10 -10
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +10 -10
- 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 +56 -57
- package/dist/commands/migration-apply.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +26 -32
- 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 +45 -48
- package/dist/commands/migration-plan.mjs.map +1 -1
- 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 +6 -10
- 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 +28 -29
- 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 +8 -8
- package/dist/{config-loader-C25b63rJ.mjs → config-loader-ih8ViDb_.mjs} +2 -2
- package/dist/config-loader-ih8ViDb_.mjs.map +1 -0
- package/dist/config-loader.mjs +1 -1
- package/dist/contract-emit-DS5NzZh2.mjs +6 -0
- package/dist/contract-emit-RZBWzkop.mjs +329 -0
- package/dist/contract-emit-RZBWzkop.mjs.map +1 -0
- package/dist/contract-emit-rt_Nmdwq.mjs +150 -0
- package/dist/contract-emit-rt_Nmdwq.mjs.map +1 -0
- package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-4Ptgw3Pe.mjs} +1 -1
- package/dist/{contract-enrichment-CAOELa-H.mjs.map → contract-enrichment-4Ptgw3Pe.mjs.map} +1 -1
- package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-Cf5J2wVg.mjs} +11 -19
- package/dist/contract-infer-Cf5J2wVg.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +86 -21
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +7 -7
- package/dist/exports/index.mjs +8 -8
- package/dist/{framework-components-Cr--XBKy.mjs → framework-components-Bgcre3Z6.mjs} +2 -2
- package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-Bgcre3Z6.mjs.map} +1 -1
- package/dist/{init-m8x0UoPY.mjs → init-DAbQMxIR.mjs} +5 -5
- package/dist/{init-m8x0UoPY.mjs.map → init-DAbQMxIR.mjs.map} +1 -1
- package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-LWtXfxm_.mjs} +9 -9
- package/dist/inspect-live-schema-LWtXfxm_.mjs.map +1 -0
- package/dist/migration-cli.d.mts +41 -11
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +308 -84
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-B3B09et6.mjs → migration-command-scaffold-CU452v9h.mjs} +7 -7
- package/dist/{migration-command-scaffold-B3B09et6.mjs.map → migration-command-scaffold-CU452v9h.mjs.map} +1 -1
- package/dist/{migration-status-DUMiH8_G.mjs → migration-status-DoPrFIOQ.mjs} +114 -57
- package/dist/migration-status-DoPrFIOQ.mjs.map +1 -0
- package/dist/{migrations-Bo5WtTla.mjs → migrations-MEoKMiV5.mjs} +42 -21
- package/dist/migrations-MEoKMiV5.mjs.map +1 -0
- package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-DgRGldpT.mjs} +1 -1
- package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-DgRGldpT.mjs.map} +1 -1
- package/dist/{result-handler-Ba3zWQsI.mjs → result-handler-Ch6hVnOo.mjs} +34 -19
- package/dist/{result-handler-Ba3zWQsI.mjs.map → result-handler-Ch6hVnOo.mjs.map} +1 -1
- package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-u2YgKghu.mjs} +1 -1
- package/dist/{terminal-ui-C3ZLwQxK.mjs.map → terminal-ui-u2YgKghu.mjs.map} +1 -1
- package/dist/{verify-Bkycc-Tf.mjs → verify-BT9tgCOH.mjs} +2 -2
- package/dist/{verify-Bkycc-Tf.mjs.map → verify-BT9tgCOH.mjs.map} +1 -1
- package/package.json +16 -15
- 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 +1 -0
- package/src/commands/db-update.ts +1 -1
- package/src/commands/inspect-live-schema.ts +10 -5
- package/src/commands/migration-apply.ts +84 -63
- package/src/commands/migration-new.ts +28 -34
- package/src/commands/migration-plan.ts +80 -56
- package/src/commands/migration-ref.ts +8 -7
- package/src/commands/migration-show.ts +53 -36
- package/src/commands/migration-status.ts +194 -58
- package/src/config-path-validation.ts +0 -1
- package/src/control-api/client.ts +21 -0
- package/src/control-api/operations/contract-emit.ts +198 -115
- package/src/control-api/operations/db-init.ts +10 -6
- package/src/control-api/operations/db-update.ts +10 -6
- package/src/control-api/operations/migration-apply.ts +30 -9
- package/src/control-api/types.ts +69 -7
- package/src/exports/control-api.ts +2 -1
- package/src/migration-cli.ts +445 -122
- package/src/utils/cli-errors.ts +49 -2
- package/src/utils/command-helpers.ts +45 -23
- 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-Cd79vmTH.mjs +0 -5
- package/dist/client-CrsnY58k.mjs.map +0 -1
- package/dist/config-loader-C25b63rJ.mjs.map +0 -1
- package/dist/contract-emit-DxgyXrqV.mjs +0 -6
- 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-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/inspect-live-schema-yrHAvG71.mjs.map +0 -1
- package/dist/migration-status-DUMiH8_G.mjs.map +0 -1
- package/dist/migrations-Bo5WtTla.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/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
|
@@ -112,7 +112,7 @@ async function executeDbUpdateCommand(
|
|
|
112
112
|
label: op.label,
|
|
113
113
|
operationClass: op.operationClass,
|
|
114
114
|
})),
|
|
115
|
-
...ifDefined('
|
|
115
|
+
...ifDefined('preview', result.value.plan.preview),
|
|
116
116
|
},
|
|
117
117
|
...ifDefined(
|
|
118
118
|
'execution',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CoreSchemaView } from '@prisma-next/framework-components/control';
|
|
2
|
-
import {
|
|
2
|
+
import type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';
|
|
3
3
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
4
4
|
import { relative, resolve } from 'pathe';
|
|
5
5
|
import { loadConfig } from '../config-loader';
|
|
@@ -33,6 +33,12 @@ export interface InspectLiveSchemaResult {
|
|
|
33
33
|
readonly config: LoadedCliConfig;
|
|
34
34
|
readonly schema: unknown;
|
|
35
35
|
readonly schemaView: CoreSchemaView | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* PSL AST inferred from the introspected schema, when the configured family
|
|
38
|
+
* implements `PslContractInferCapable`. `undefined` for families that do not
|
|
39
|
+
* support inference (e.g. Mongo today).
|
|
40
|
+
*/
|
|
41
|
+
readonly pslContractAst: PslDocumentAst | undefined;
|
|
36
42
|
readonly target: {
|
|
37
43
|
readonly familyId: string;
|
|
38
44
|
readonly id: string;
|
|
@@ -122,14 +128,12 @@ export async function inspectLiveSchema(
|
|
|
122
128
|
const onProgress = createProgressAdapter({ ui, flags });
|
|
123
129
|
|
|
124
130
|
try {
|
|
125
|
-
const
|
|
131
|
+
const schema = await client.introspect({
|
|
126
132
|
connection: dbConnection,
|
|
127
133
|
onProgress,
|
|
128
134
|
});
|
|
129
|
-
// TODO(TML-2251): Remove SQL-specific branching — SQL should use the same family-agnostic path as Mongo.
|
|
130
|
-
const schema =
|
|
131
|
-
config.family.familyId === 'sql' ? validatePrintableSqlSchemaIR(schemaIR) : schemaIR;
|
|
132
135
|
const schemaView = client.toSchemaView(schema);
|
|
136
|
+
const pslContractAst = client.inferPslContract(schema);
|
|
133
137
|
|
|
134
138
|
const dbUrl = typeof dbConnection === 'string' ? maskConnectionUrl(dbConnection) : undefined;
|
|
135
139
|
|
|
@@ -137,6 +141,7 @@ export async function inspectLiveSchema(
|
|
|
137
141
|
config,
|
|
138
142
|
schema,
|
|
139
143
|
schemaView,
|
|
144
|
+
pslContractAst,
|
|
140
145
|
target: {
|
|
141
146
|
familyId: config.family.familyId,
|
|
142
147
|
id: config.target.targetId,
|