@prisma-next/cli 0.7.0 → 0.8.0-dev.10
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 +7 -8
- package/dist/{cli-errors-D3_sMh2K.mjs → cli-errors-CF60g2cG.mjs} +40 -2
- package/dist/cli-errors-CF60g2cG.mjs.map +1 -0
- package/dist/cli.mjs +70 -21
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-BCnP7cHo.mjs → client-XkUw4xD0.mjs} +17 -13
- package/dist/client-XkUw4xD0.mjs.map +1 -0
- package/dist/{command-helpers-BeZHkxV8.mjs → command-helpers-D3vL5yi8.mjs} +29 -6
- package/dist/command-helpers-D3vL5yi8.mjs.map +1 -0
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.mjs +7 -7
- package/dist/commands/db-schema.mjs +5 -5
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +67 -25
- 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 +37 -9
- package/dist/commands/db-update.mjs.map +1 -1
- package/dist/commands/db-verify.mjs +1 -1
- package/dist/commands/migrate.d.mts +28 -0
- package/dist/commands/migrate.d.mts.map +1 -0
- package/dist/commands/{migration-apply.mjs → migrate.mjs} +54 -36
- package/dist/commands/migrate.mjs.map +1 -0
- package/dist/commands/migration-check.d.mts +18 -0
- package/dist/commands/migration-check.d.mts.map +1 -0
- package/dist/commands/migration-check.mjs +284 -0
- package/dist/commands/migration-check.mjs.map +1 -0
- package/dist/commands/migration-graph.d.mts +16 -0
- package/dist/commands/migration-graph.d.mts.map +1 -0
- package/dist/commands/migration-graph.mjs +141 -0
- package/dist/commands/migration-graph.mjs.map +1 -0
- package/dist/commands/migration-list.d.mts +20 -0
- package/dist/commands/migration-list.d.mts.map +1 -0
- package/dist/commands/migration-list.mjs +107 -0
- package/dist/commands/migration-list.mjs.map +1 -0
- package/dist/commands/migration-log.d.mts +21 -0
- package/dist/commands/migration-log.d.mts.map +1 -0
- package/dist/commands/migration-log.mjs +146 -0
- package/dist/commands/migration-log.mjs.map +1 -0
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +21 -20
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +2 -2
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +1 -1
- package/dist/commands/migration-show.d.mts +1 -1
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +85 -47
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +3 -15
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +732 -1
- package/dist/commands/migration-status.mjs.map +1 -0
- package/dist/commands/ref.d.mts +34 -0
- package/dist/commands/ref.d.mts.map +1 -0
- package/dist/commands/{migration-ref.mjs → ref.mjs} +28 -57
- package/dist/commands/ref.mjs.map +1 -0
- package/dist/{contract-emit-B77TsJqf.mjs → contract-emit-CgoFk9AU.mjs} +8 -4
- package/dist/contract-emit-CgoFk9AU.mjs.map +1 -0
- package/dist/{contract-emit-9DBda5Ou.mjs → contract-emit-GpxW5RLe.mjs} +6 -6
- package/dist/{contract-emit-9DBda5Ou.mjs.map → contract-emit-GpxW5RLe.mjs.map} +1 -1
- package/dist/{contract-infer-ByxhPjpW.mjs → contract-infer-D8edZOCi.mjs} +5 -5
- package/dist/{contract-infer-ByxhPjpW.mjs.map → contract-infer-D8edZOCi.mjs.map} +1 -1
- package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs → contract-space-aggregate-loader-D68YpuPR.mjs} +3 -3
- package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs.map → contract-space-aggregate-loader-D68YpuPR.mjs.map} +1 -1
- package/dist/{db-verify-Czm5T-J4.mjs → db-verify-DtRB9iHJ.mjs} +7 -7
- package/dist/{db-verify-Czm5T-J4.mjs.map → db-verify-DtRB9iHJ.mjs.map} +1 -1
- package/dist/errors-Cw6kyTyV.mjs +56 -0
- package/dist/errors-Cw6kyTyV.mjs.map +1 -0
- package/dist/exports/control-api.d.mts +1 -1
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +2 -2
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/{framework-components-ChqVUxR-.mjs → framework-components-xFLFpZUO.mjs} +2 -2
- package/dist/{framework-components-ChqVUxR-.mjs.map → framework-components-xFLFpZUO.mjs.map} +1 -1
- package/dist/{global-flags-Icqpxk23.d.mts → global-flags-DGmw6Kqg.d.mts} +1 -1
- package/dist/{global-flags-Icqpxk23.d.mts.map → global-flags-DGmw6Kqg.d.mts.map} +1 -1
- package/dist/{migration-status-By9G5p2H.mjs → graph-render-eJDcLWny.mjs} +3 -692
- package/dist/graph-render-eJDcLWny.mjs.map +1 -0
- package/dist/{init-BRKnARU6.mjs → init-Dm0QZPUA.mjs} +412 -208
- package/dist/init-Dm0QZPUA.mjs.map +1 -0
- package/dist/{inspect-live-schema-DxdBd4Er.mjs → inspect-live-schema-CPPqCips.mjs} +4 -4
- package/dist/{inspect-live-schema-DxdBd4Er.mjs.map → inspect-live-schema-CPPqCips.mjs.map} +1 -1
- package/dist/migration-cli.mjs +1 -1
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-BdV8JYXV.mjs → migration-command-scaffold-B_ezTTwX.mjs} +4 -4
- package/dist/{migration-command-scaffold-BdV8JYXV.mjs.map → migration-command-scaffold-B_ezTTwX.mjs.map} +1 -1
- package/dist/{migration-plan-mRu5K81L.mjs → migration-plan-DWB-NTxH.mjs} +62 -30
- package/dist/migration-plan-DWB-NTxH.mjs.map +1 -0
- package/dist/migration-types-D2FW63pr.d.mts +15 -0
- package/dist/migration-types-D2FW63pr.d.mts.map +1 -0
- package/dist/{migrations-CTsyBXCA.mjs → migrations-DyUf5lTt.mjs} +2 -2
- package/dist/migrations-DyUf5lTt.mjs.map +1 -0
- package/dist/{output-B16Kefzx.mjs → output-B60Gw5fu.mjs} +12 -11
- package/dist/{output-B16Kefzx.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
- package/dist/{result-handler-rmPVKIP2.mjs → result-handler-Bm_6dDYg.mjs} +2 -2
- package/dist/{result-handler-rmPVKIP2.mjs.map → result-handler-Bm_6dDYg.mjs.map} +1 -1
- package/dist/{terminal-ui-C_hFNbAn.mjs → terminal-ui-XtOQsqe9.mjs} +2 -54
- package/dist/terminal-ui-XtOQsqe9.mjs.map +1 -0
- package/dist/{types-LItU7E4l.d.mts → types-BS_wpjAY.d.mts} +2 -2
- package/dist/{types-LItU7E4l.d.mts.map → types-BS_wpjAY.d.mts.map} +1 -1
- package/dist/{verify-CiwNWM9N.mjs → verify-D7ypCCe6.mjs} +1 -1
- package/dist/{verify-CiwNWM9N.mjs.map → verify-D7ypCCe6.mjs.map} +1 -1
- package/package.json +41 -25
- package/src/cli.ts +78 -15
- package/src/commands/db-sign.ts +102 -32
- package/src/commands/db-update.ts +56 -4
- package/src/commands/init/agent-skill-install.ts +232 -0
- package/src/commands/init/errors.ts +32 -0
- package/src/commands/init/exit-codes.ts +10 -0
- package/src/commands/init/index.ts +9 -1
- package/src/commands/init/init.ts +82 -7
- package/src/commands/init/inputs.ts +23 -0
- package/src/commands/init/output.ts +22 -17
- package/src/commands/init/templates/code-templates.ts +4 -1
- package/src/commands/{migration-apply.ts → migrate.ts} +54 -70
- package/src/commands/migration-check/exit-codes.ts +3 -0
- package/src/commands/migration-check.ts +369 -0
- package/src/commands/migration-graph.ts +184 -0
- package/src/commands/migration-list.ts +155 -0
- package/src/commands/migration-log.ts +218 -0
- package/src/commands/migration-new.ts +17 -9
- package/src/commands/migration-plan.ts +77 -27
- package/src/commands/migration-show.ts +132 -60
- package/src/commands/migration-status.ts +77 -64
- package/src/commands/{migration-ref.ts → ref.ts} +32 -86
- package/src/control-api/client.ts +11 -4
- package/src/control-api/operations/apply-aggregate.ts +4 -4
- package/src/control-api/operations/contract-emit.ts +13 -1
- package/src/control-api/operations/db-apply-aggregate.ts +3 -2
- package/src/control-api/operations/db-verify.ts +1 -1
- package/src/control-api/operations/migration-apply.ts +4 -3
- package/src/control-api/types.ts +1 -2
- package/src/migration-cli.ts +1 -1
- package/src/utils/cli-errors.ts +37 -0
- package/src/utils/command-helpers.ts +28 -3
- package/src/utils/contract-space-aggregate-loader.ts +2 -2
- package/src/utils/contract-space-seed-phase.ts +2 -2
- package/src/utils/formatters/help.ts +12 -2
- package/src/utils/formatters/migrations.ts +2 -2
- package/dist/agent-skill-mongo.md +0 -138
- package/dist/agent-skill-postgres.md +0 -106
- package/dist/cli-errors-D3_sMh2K.mjs.map +0 -1
- package/dist/client-BCnP7cHo.mjs.map +0 -1
- package/dist/command-helpers-BeZHkxV8.mjs.map +0 -1
- package/dist/commands/migration-apply.d.mts +0 -51
- package/dist/commands/migration-apply.d.mts.map +0 -1
- package/dist/commands/migration-apply.mjs.map +0 -1
- package/dist/commands/migration-ref.d.mts +0 -45
- package/dist/commands/migration-ref.d.mts.map +0 -1
- package/dist/commands/migration-ref.mjs.map +0 -1
- package/dist/contract-emit-B77TsJqf.mjs.map +0 -1
- package/dist/init-BRKnARU6.mjs.map +0 -1
- package/dist/migration-plan-mRu5K81L.mjs.map +0 -1
- package/dist/migration-status-By9G5p2H.mjs.map +0 -1
- package/dist/migrations-CTsyBXCA.mjs.map +0 -1
- package/dist/terminal-ui-C_hFNbAn.mjs.map +0 -1
- package/src/commands/init/templates/agent-skill-mongo.md +0 -138
- package/src/commands/init/templates/agent-skill-postgres.md +0 -106
- package/src/commands/init/templates/agent-skill.ts +0 -41
- /package/dist/{cli-errors-B9OBbled.d.mts → cli-errors-DdcjVLJV.d.mts} +0 -0
|
@@ -236,3 +236,35 @@ export function errorInitEmitFailed(options: {
|
|
|
236
236
|
},
|
|
237
237
|
});
|
|
238
238
|
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* The project-level agent-skill install (`npx skills add
|
|
242
|
+
* prisma/prisma-next#v<version>`) failed after a successful dependency
|
|
243
|
+
* install + emit. The project's scaffold remains on disk; the user
|
|
244
|
+
* can either fix the underlying issue (network, registry, PATH) and
|
|
245
|
+
* run the install command manually, or re-run `init --no-skill` to
|
|
246
|
+
* proceed without the skill.
|
|
247
|
+
*
|
|
248
|
+
* Non-rolling-back, matching the existing install/emit failure
|
|
249
|
+
* semantics. Maps to exit code `6 = SKILL_INSTALL_FAILED`.
|
|
250
|
+
*/
|
|
251
|
+
export function errorInitSkillInstallFailed(options: {
|
|
252
|
+
readonly skillInstallCommand: string;
|
|
253
|
+
readonly filesWritten: readonly string[];
|
|
254
|
+
readonly cause: string;
|
|
255
|
+
}): CliStructuredError {
|
|
256
|
+
return new CliStructuredError('5013', 'Failed to install Prisma Next skills', {
|
|
257
|
+
domain: 'CLI',
|
|
258
|
+
why: `\`${options.skillInstallCommand}\` exited with an error: ${options.cause}`,
|
|
259
|
+
fix:
|
|
260
|
+
'Either:\n' +
|
|
261
|
+
` - Re-run \`prisma-next init --no-skill${options.filesWritten.length > 0 ? ' --force' : ''}\` to skip the skill install for this run, or\n` +
|
|
262
|
+
` - Fix the underlying issue (network, npm registry, \`npx skills\` on PATH) and install manually:\n ${options.skillInstallCommand}`,
|
|
263
|
+
docsUrl: 'https://prisma-next.dev/docs/cli/init#agent-skill',
|
|
264
|
+
meta: {
|
|
265
|
+
filesWritten: options.filesWritten,
|
|
266
|
+
skillInstallCommand: options.skillInstallCommand,
|
|
267
|
+
cause: options.cause,
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
}
|
|
@@ -60,3 +60,13 @@ export const INIT_EXIT_INSTALL_FAILED = 4;
|
|
|
60
60
|
* emit` manually.
|
|
61
61
|
*/
|
|
62
62
|
export const INIT_EXIT_EMIT_FAILED = 5;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The project-level Prisma Next skills install (`npx skills add
|
|
66
|
+
* prisma/prisma-next#v<version>`) failed after a successful dependency
|
|
67
|
+
* install + emit. The scaffolded project files remain on disk; the
|
|
68
|
+
* user can fix the underlying issue (network, registry reachability,
|
|
69
|
+
* `npx skills` not on PATH) and run the install manually, or re-run
|
|
70
|
+
* `init` with `--no-skill` to skip it.
|
|
71
|
+
*/
|
|
72
|
+
export const INIT_EXIT_SKILL_INSTALL_FAILED = 6;
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
INIT_EXIT_INTERNAL_ERROR,
|
|
12
12
|
INIT_EXIT_OK,
|
|
13
13
|
INIT_EXIT_PRECONDITION,
|
|
14
|
+
INIT_EXIT_SKILL_INSTALL_FAILED,
|
|
14
15
|
INIT_EXIT_USER_ABORTED,
|
|
15
16
|
} from './exit-codes';
|
|
16
17
|
|
|
@@ -33,6 +34,7 @@ interface InitCommandOptions extends CommonCommandOptions {
|
|
|
33
34
|
readonly probeDb?: boolean;
|
|
34
35
|
readonly strictProbe?: boolean;
|
|
35
36
|
readonly install?: boolean;
|
|
37
|
+
readonly skill?: boolean;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
export function createInitCommand(): Command {
|
|
@@ -52,7 +54,8 @@ export function createInitCommand(): Command {
|
|
|
52
54
|
` ${INIT_EXIT_PRECONDITION} PRECONDITION Bad flags / missing prerequisite (e.g. no package.json).\n` +
|
|
53
55
|
` ${INIT_EXIT_USER_ABORTED} USER_ABORTED User cancelled an interactive prompt.\n` +
|
|
54
56
|
` ${INIT_EXIT_INSTALL_FAILED} INSTALL_FAILED Dependency installation failed (init-specific).\n` +
|
|
55
|
-
` ${INIT_EXIT_EMIT_FAILED} EMIT_FAILED \`contract emit\` failed after install (init-specific)
|
|
57
|
+
` ${INIT_EXIT_EMIT_FAILED} EMIT_FAILED \`contract emit\` failed after install (init-specific).\n` +
|
|
58
|
+
` ${INIT_EXIT_SKILL_INSTALL_FAILED} SKILL_INSTALL_FAILED Agent-skill install failed (re-run with --no-skill to skip).`,
|
|
56
59
|
);
|
|
57
60
|
setCommandExamples(command, [
|
|
58
61
|
'prisma-next init',
|
|
@@ -60,6 +63,7 @@ export function createInitCommand(): Command {
|
|
|
60
63
|
'prisma-next init --yes --target mongodb --authoring typescript --json',
|
|
61
64
|
'prisma-next init --yes --force --target postgres --authoring psl # overwrite an existing scaffold',
|
|
62
65
|
'prisma-next init --no-install # skip pnpm/npm install + emit',
|
|
66
|
+
'prisma-next init --no-skill # skip the agent-skill install (air-gapped / restricted env)',
|
|
63
67
|
]);
|
|
64
68
|
|
|
65
69
|
return addGlobalOptions(command)
|
|
@@ -83,6 +87,10 @@ export function createInitCommand(): Command {
|
|
|
83
87
|
'Treat a failed --probe-db as fatal (no-op without --probe-db; init is offline-by-default)',
|
|
84
88
|
)
|
|
85
89
|
.option('--no-install', 'Skip dependency installation and contract emission')
|
|
90
|
+
.option(
|
|
91
|
+
'--no-skill',
|
|
92
|
+
'Skip Prisma Next skills install (air-gapped CI, restricted registries, etc.)',
|
|
93
|
+
)
|
|
86
94
|
.action(async (options: InitCommandOptions) => {
|
|
87
95
|
const { runInit } = await import('./init');
|
|
88
96
|
const flags = parseGlobalFlags(options);
|
|
@@ -7,6 +7,13 @@ import { CliStructuredError } from '../../utils/cli-errors';
|
|
|
7
7
|
import { formatErrorJson, formatErrorOutput } from '../../utils/formatters/errors';
|
|
8
8
|
import type { GlobalFlags } from '../../utils/global-flags';
|
|
9
9
|
import { TerminalUI } from '../../utils/terminal-ui';
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_AGENT_SKILL_SOURCES,
|
|
12
|
+
formatClaudeSkillInstallCommand,
|
|
13
|
+
formatSkillInstallCommand,
|
|
14
|
+
LEGACY_SKILL_FILE,
|
|
15
|
+
runProjectLevelSkillInstall,
|
|
16
|
+
} from './agent-skill-install';
|
|
10
17
|
import {
|
|
11
18
|
detectPackageManager,
|
|
12
19
|
formatAddArgs,
|
|
@@ -29,6 +36,7 @@ import {
|
|
|
29
36
|
INIT_EXIT_INTERNAL_ERROR,
|
|
30
37
|
INIT_EXIT_OK,
|
|
31
38
|
INIT_EXIT_PRECONDITION,
|
|
39
|
+
INIT_EXIT_SKILL_INSTALL_FAILED,
|
|
32
40
|
INIT_EXIT_USER_ABORTED,
|
|
33
41
|
} from './exit-codes';
|
|
34
42
|
import { mergeGitattributes, requiredGitattributesLines } from './hygiene-gitattributes';
|
|
@@ -48,7 +56,6 @@ import {
|
|
|
48
56
|
} from './output';
|
|
49
57
|
import { type ProbeOutcome, type ProbeOverrides, probeServerVersion } from './probe-db';
|
|
50
58
|
import { findStaleArtefacts, removeDependency } from './reinit-cleanup';
|
|
51
|
-
import { agentSkillMd } from './templates/agent-skill';
|
|
52
59
|
import { configFile, dbFile, starterSchema, targetPackageName } from './templates/code-templates';
|
|
53
60
|
import { envExampleContent, envFileContent, MIN_SERVER_VERSION } from './templates/env';
|
|
54
61
|
import { quickReferenceMd } from './templates/quick-reference';
|
|
@@ -72,6 +79,15 @@ interface InstallReport {
|
|
|
72
79
|
readonly deps: readonly string[];
|
|
73
80
|
readonly devDeps: readonly string[];
|
|
74
81
|
readonly warnings: readonly string[];
|
|
82
|
+
/**
|
|
83
|
+
* The package manager that actually ran. Equal to the detected `pm`
|
|
84
|
+
* on the common path; differs when the FR7.2 pnpm → npm fallback
|
|
85
|
+
* fired, in which case it's `'npm'`. Threaded into the agent-skill
|
|
86
|
+
* install so the runner stays consistent with the install we just
|
|
87
|
+
* ran — re-trying through `pnpm dlx` when `pnpm install` just failed
|
|
88
|
+
* for workspace/catalog reasons would fail again for the same reason.
|
|
89
|
+
*/
|
|
90
|
+
readonly effectivePm: PackageManager;
|
|
75
91
|
}
|
|
76
92
|
|
|
77
93
|
/**
|
|
@@ -156,10 +172,6 @@ export async function runInit(
|
|
|
156
172
|
path: 'prisma-next.md',
|
|
157
173
|
content: quickReferenceMd(inputs.target, inputs.authoring, inputs.schemaPath, pkgRun),
|
|
158
174
|
},
|
|
159
|
-
{
|
|
160
|
-
path: '.agents/skills/prisma-next/SKILL.md',
|
|
161
|
-
content: agentSkillMd(inputs.target, inputs.authoring, inputs.schemaPath, pkgRun),
|
|
162
|
-
},
|
|
163
175
|
{ path: '.env.example', content: envExampleContent(inputs.target) },
|
|
164
176
|
];
|
|
165
177
|
|
|
@@ -172,6 +184,14 @@ export async function runInit(
|
|
|
172
184
|
// and missing-on-disk-at-write-time is tolerated.
|
|
173
185
|
const filesToDelete: string[] = inputs.reinit ? [...findStaleArtefacts(baseDir, schemaDir)] : [];
|
|
174
186
|
|
|
187
|
+
// `init` delegates the skill to `npx skills add prisma/prisma-next#v<version>`,
|
|
188
|
+
// so a hand-rolled `.agents/skills/prisma-next/SKILL.md` in the project
|
|
189
|
+
// would shadow the published package. Queue it for deletion on every
|
|
190
|
+
// run (not gated on `--reinit`).
|
|
191
|
+
if (existsSync(join(baseDir, LEGACY_SKILL_FILE))) {
|
|
192
|
+
filesToDelete.push(LEGACY_SKILL_FILE);
|
|
193
|
+
}
|
|
194
|
+
|
|
175
195
|
// FR3.2: a real `.env` is only written when the user opted in. Never
|
|
176
196
|
// overwrite an existing `.env` — secrets live there and clobbering
|
|
177
197
|
// them is the most damaging possible side-effect of `init`.
|
|
@@ -416,6 +436,57 @@ export async function runInit(
|
|
|
416
436
|
}
|
|
417
437
|
}
|
|
418
438
|
|
|
439
|
+
// Agent-skill install. Project-level is unconditional modulo
|
|
440
|
+
// `--no-skill`. We deliberately do **not** offer a user-level
|
|
441
|
+
// (global) install path: the skill's behaviour and surface track
|
|
442
|
+
// the project's Prisma Next version, and a host-wide install would
|
|
443
|
+
// have to pick a single version for every project on the machine,
|
|
444
|
+
// which breaks the version-locking invariant the rest of the
|
|
445
|
+
// framework relies on. A project-level failure is fatal
|
|
446
|
+
// (`INIT_EXIT_SKILL_INSTALL_FAILED`).
|
|
447
|
+
//
|
|
448
|
+
// Runs after install + emit because (1) `node_modules` is in place,
|
|
449
|
+
// so any consumers downstream are coherent, and (2) the user has
|
|
450
|
+
// seen the scaffolding succeed before this network-bound step
|
|
451
|
+
// potentially fails. We skip the install when the user passed
|
|
452
|
+
// `--no-install` for the same reason — no `node_modules` means the
|
|
453
|
+
// workspace isn't ready to consume the skill yet anyway.
|
|
454
|
+
const manualProjectSkillCommands = DEFAULT_AGENT_SKILL_SOURCES.flatMap((source) => [
|
|
455
|
+
formatSkillInstallCommand(install.effectivePm, source),
|
|
456
|
+
formatClaudeSkillInstallCommand(install.effectivePm, source),
|
|
457
|
+
]);
|
|
458
|
+
const manualProjectSkillSummary = manualProjectSkillCommands.map((c) => `\`${c}\``).join(' && ');
|
|
459
|
+
let skillRegistered = false;
|
|
460
|
+
if (!inputs.installProjectSkill) {
|
|
461
|
+
warnings.push(
|
|
462
|
+
`Skipped Prisma Next agent-skill install (--no-skill). To install the skills later, run: ${manualProjectSkillSummary}`,
|
|
463
|
+
);
|
|
464
|
+
} else if (install.skipped) {
|
|
465
|
+
warnings.push(
|
|
466
|
+
`Skipped Prisma Next agent-skill install because --no-install was passed. After you run install manually, install the skills with: ${manualProjectSkillSummary}`,
|
|
467
|
+
);
|
|
468
|
+
} else {
|
|
469
|
+
const spinner = ui.spinner();
|
|
470
|
+
spinner.start('Registering Prisma Next skills with the agent runtime...');
|
|
471
|
+
try {
|
|
472
|
+
const project = await runProjectLevelSkillInstall({
|
|
473
|
+
baseDir,
|
|
474
|
+
pm: install.effectivePm,
|
|
475
|
+
filesWritten,
|
|
476
|
+
});
|
|
477
|
+
spinner.stop(
|
|
478
|
+
`Registered Prisma Next skills (project level) — ran ${project.commands.map((c) => `\`${c}\``).join(', ')}`,
|
|
479
|
+
);
|
|
480
|
+
skillRegistered = true;
|
|
481
|
+
} catch (error) {
|
|
482
|
+
spinner.stop('Agent-skill install failed');
|
|
483
|
+
if (CliStructuredError.is(error)) {
|
|
484
|
+
return emitError(ui, flags, error);
|
|
485
|
+
}
|
|
486
|
+
throw error;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
419
490
|
const output: InitOutput = {
|
|
420
491
|
ok: true,
|
|
421
492
|
target: inputs.target === 'mongo' ? 'mongodb' : 'postgres',
|
|
@@ -434,6 +505,7 @@ export async function runInit(
|
|
|
434
505
|
contractEmitted,
|
|
435
506
|
emitCommand,
|
|
436
507
|
schemaPath: inputs.schemaPath,
|
|
508
|
+
skillRegistered,
|
|
437
509
|
}),
|
|
438
510
|
warnings,
|
|
439
511
|
};
|
|
@@ -525,6 +597,8 @@ export function exitCodeForError(error: { readonly code: string }): number {
|
|
|
525
597
|
return INIT_EXIT_EMIT_FAILED;
|
|
526
598
|
case '5009': // invalid output document — internal bug in prisma-next
|
|
527
599
|
return INIT_EXIT_INTERNAL_ERROR;
|
|
600
|
+
case '5013': // agent-skill install failed
|
|
601
|
+
return INIT_EXIT_SKILL_INSTALL_FAILED;
|
|
528
602
|
default:
|
|
529
603
|
// Any unexpected code is treated as an internal bug rather than
|
|
530
604
|
// mis-routed to PRECONDITION. Adding a new code requires an
|
|
@@ -646,7 +720,7 @@ async function runInstall(ctx: {
|
|
|
646
720
|
'Manual steps',
|
|
647
721
|
);
|
|
648
722
|
}
|
|
649
|
-
return { skipped: true, deps: [], devDeps: [], warnings: catalogWarnings };
|
|
723
|
+
return { skipped: true, deps: [], devDeps: [], warnings: catalogWarnings, effectivePm: pm };
|
|
650
724
|
}
|
|
651
725
|
|
|
652
726
|
const exec = promisify(execFile);
|
|
@@ -661,7 +735,7 @@ async function runInstall(ctx: {
|
|
|
661
735
|
try {
|
|
662
736
|
await runPair(pm);
|
|
663
737
|
spinner.stop(`Installed ${allPackages}`);
|
|
664
|
-
return { skipped: false, deps, devDeps, warnings: catalogWarnings };
|
|
738
|
+
return { skipped: false, deps, devDeps, warnings: catalogWarnings, effectivePm: pm };
|
|
665
739
|
} catch (err) {
|
|
666
740
|
const stderrText = redactSecrets(readChildStderr(err));
|
|
667
741
|
|
|
@@ -693,6 +767,7 @@ async function runInstall(ctx: {
|
|
|
693
767
|
// catalog-honour warning to avoid a contradictory message
|
|
694
768
|
// pair.
|
|
695
769
|
warnings: [fallbackWarning],
|
|
770
|
+
effectivePm: 'npm',
|
|
696
771
|
};
|
|
697
772
|
} catch (npmErr) {
|
|
698
773
|
spinner.stop('Installation failed');
|
|
@@ -31,6 +31,13 @@ export interface InitFlagOptions {
|
|
|
31
31
|
readonly probeDb?: boolean;
|
|
32
32
|
readonly strictProbe?: boolean;
|
|
33
33
|
readonly install?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* `--no-skill` — skip the project-level skill install entirely.
|
|
36
|
+
* Documented escape hatch for air-gapped CI, restricted registries,
|
|
37
|
+
* and any environment where `npx skills` is
|
|
38
|
+
* not reachable.
|
|
39
|
+
*/
|
|
40
|
+
readonly skill?: boolean;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
/**
|
|
@@ -61,6 +68,14 @@ export interface ResolvedInitInputs {
|
|
|
61
68
|
* is added separately via the install step.
|
|
62
69
|
*/
|
|
63
70
|
readonly removePreviousFacade: string | null;
|
|
71
|
+
/**
|
|
72
|
+
* Whether to run `npx skills add prisma/prisma-next#v<version>` at the
|
|
73
|
+
* project level after install + emit. True by default; `--no-skill`
|
|
74
|
+
* sets it to `false`. The skill is always project-level (never
|
|
75
|
+
* user-level / global) so its version stays locked to the project's
|
|
76
|
+
* Prisma Next version — see `agent-skill-install.ts`.
|
|
77
|
+
*/
|
|
78
|
+
readonly installProjectSkill: boolean;
|
|
64
79
|
}
|
|
65
80
|
|
|
66
81
|
const TARGET_ALIASES: ReadonlyMap<string, TargetId> = new Map([
|
|
@@ -161,6 +176,13 @@ export async function resolveInitInputs(ctx: {
|
|
|
161
176
|
autoAcceptPrompts,
|
|
162
177
|
});
|
|
163
178
|
|
|
179
|
+
// Skill-install gating. `--no-skill` (commander parses
|
|
180
|
+
// `options.skill === false`) is the only escape hatch; otherwise
|
|
181
|
+
// project-level install is unconditional. The skill is always
|
|
182
|
+
// installed at the project level so its version tracks the
|
|
183
|
+
// project's Prisma Next release.
|
|
184
|
+
const installProjectSkill = options.skill !== false;
|
|
185
|
+
|
|
164
186
|
return {
|
|
165
187
|
target: finalTarget,
|
|
166
188
|
authoring: finalAuthoring,
|
|
@@ -171,6 +193,7 @@ export async function resolveInitInputs(ctx: {
|
|
|
171
193
|
strictProbe: Boolean(options.strictProbe),
|
|
172
194
|
reinit,
|
|
173
195
|
removePreviousFacade,
|
|
196
|
+
installProjectSkill,
|
|
174
197
|
};
|
|
175
198
|
}
|
|
176
199
|
|
|
@@ -120,27 +120,32 @@ export function buildNextSteps(options: {
|
|
|
120
120
|
readonly contractEmitted: boolean;
|
|
121
121
|
readonly emitCommand: string;
|
|
122
122
|
readonly schemaPath: string;
|
|
123
|
+
/**
|
|
124
|
+
* Whether the project-level Prisma Next skills install actually ran
|
|
125
|
+
* and succeeded during this `init`. When false (the user passed
|
|
126
|
+
* `--no-skill` or `--no-install`, so the install was skipped), the
|
|
127
|
+
* "registered with your agent runtime" step is omitted — the skip is
|
|
128
|
+
* already surfaced in the warnings array with a manual-install hint.
|
|
129
|
+
*/
|
|
130
|
+
readonly skillRegistered: boolean;
|
|
123
131
|
}): string[] {
|
|
124
132
|
const steps: string[] = [];
|
|
125
|
-
|
|
133
|
+
let stepNumber = 1;
|
|
134
|
+
const push = (text: string): void => {
|
|
135
|
+
steps.push(`${stepNumber}. ${text}`);
|
|
136
|
+
stepNumber += 1;
|
|
137
|
+
};
|
|
138
|
+
push('Set DATABASE_URL in your environment (export it or add it to .env).');
|
|
126
139
|
if (!options.contractEmitted) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
steps.push(
|
|
130
|
-
'4. Open prisma-next.md for a quick reference on how to write your first typed query.',
|
|
131
|
-
);
|
|
132
|
-
steps.push(
|
|
133
|
-
'5. The .agents/skills/prisma-next/SKILL.md file is wired up for AI-coding agents in this project.',
|
|
134
|
-
);
|
|
140
|
+
push(`Emit the contract: \`${options.emitCommand}\``);
|
|
141
|
+
push(`Edit your schema at ${options.schemaPath}, then re-run the emit command.`);
|
|
135
142
|
} else {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
steps.push(
|
|
143
|
-
'4. The .agents/skills/prisma-next/SKILL.md file is wired up for AI-coding agents in this project.',
|
|
143
|
+
push(`Edit your schema at ${options.schemaPath}, then re-run \`${options.emitCommand}\`.`);
|
|
144
|
+
}
|
|
145
|
+
push('Open prisma-next.md for a quick reference on how to write your first typed query.');
|
|
146
|
+
if (options.skillRegistered) {
|
|
147
|
+
push(
|
|
148
|
+
'Prisma Next skills are registered with your agent runtime — open the project in your IDE and ask the agent to add a model, run a query, or plan a migration.',
|
|
144
149
|
);
|
|
145
150
|
}
|
|
146
151
|
return steps;
|
|
@@ -253,7 +253,10 @@ export function dbFile(target: TargetId): string {
|
|
|
253
253
|
import type { Contract } from './contract.d';
|
|
254
254
|
import contractJson from './contract.json' with { type: 'json' };
|
|
255
255
|
|
|
256
|
-
export const db = postgres<Contract>({
|
|
256
|
+
export const db = postgres<Contract>({
|
|
257
|
+
contractJson,
|
|
258
|
+
url: process.env['DATABASE_URL']!,
|
|
259
|
+
});
|
|
257
260
|
`;
|
|
258
261
|
}
|
|
259
262
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import type { Contract } from '@prisma-next/contract/types';
|
|
3
3
|
import { errorUnknownInvariant, MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
4
|
+
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
4
5
|
import type { RefEntry } from '@prisma-next/migration-tools/refs';
|
|
5
|
-
import { readRefs
|
|
6
|
+
import { readRefs } from '@prisma-next/migration-tools/refs';
|
|
6
7
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
7
8
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
8
9
|
import { Command } from 'commander';
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
errorTargetMigrationNotSupported,
|
|
26
27
|
errorUnexpected,
|
|
27
28
|
mapMigrationToolsError,
|
|
29
|
+
mapRefResolutionError,
|
|
28
30
|
} from '../utils/cli-errors';
|
|
29
31
|
import {
|
|
30
32
|
addGlobalOptions,
|
|
@@ -44,29 +46,16 @@ import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
|
|
|
44
46
|
import { handleResult } from '../utils/result-handler';
|
|
45
47
|
import { TerminalUI } from '../utils/terminal-ui';
|
|
46
48
|
|
|
47
|
-
interface
|
|
49
|
+
interface MigrateCommandOptions extends CommonCommandOptions {
|
|
48
50
|
readonly db?: string;
|
|
49
51
|
readonly config?: string;
|
|
50
|
-
readonly
|
|
52
|
+
readonly to?: string;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
* Per-space breakdown of an apply run. The CLI command surfaces these
|
|
55
|
-
* for both the JSON shape (`appliedSpaces[]`) and the human-readable
|
|
56
|
-
* formatter (per-space block — same shape `db init` / `db update`
|
|
57
|
-
* use, M6 sub-spec § Output shape contract).
|
|
58
|
-
*/
|
|
59
|
-
export interface MigrationApplyResult {
|
|
55
|
+
export interface MigrateResult {
|
|
60
56
|
readonly ok: boolean;
|
|
61
|
-
/** Number of contract spaces that had non-zero pending operations applied. */
|
|
62
57
|
readonly migrationsApplied: number;
|
|
63
|
-
/** Total contract spaces visible in the aggregate (pending + already-up-to-date). */
|
|
64
58
|
readonly migrationsTotal: number;
|
|
65
|
-
/**
|
|
66
|
-
* Marker hash for the **app member** post-apply. Surfaced for
|
|
67
|
-
* back-compat with single-space callers; per-space markers live on
|
|
68
|
-
* `perSpace[].marker.storageHash`.
|
|
69
|
-
*/
|
|
70
59
|
readonly markerHash: string;
|
|
71
60
|
readonly applied: readonly {
|
|
72
61
|
readonly spaceId: string;
|
|
@@ -77,17 +66,7 @@ export interface MigrationApplyResult {
|
|
|
77
66
|
readonly operationsExecuted: number;
|
|
78
67
|
}[];
|
|
79
68
|
readonly summary: string;
|
|
80
|
-
/**
|
|
81
|
-
* Per-space breakdown in canonical schedule order (extensions
|
|
82
|
-
* alphabetically, then app). Always present for the aggregate-walking
|
|
83
|
-
* apply path.
|
|
84
|
-
*/
|
|
85
69
|
readonly perSpace: readonly AggregatePerSpaceExecutionEntry[];
|
|
86
|
-
/**
|
|
87
|
-
* Path-decision data for the app member. Surfaced for back-compat
|
|
88
|
-
* with single-space callers (cli-journeys invariant tests).
|
|
89
|
-
* Absent for no-op applies where the app had nothing to do.
|
|
90
|
-
*/
|
|
91
70
|
readonly pathDecision?: MigrationApplyPathDecision;
|
|
92
71
|
readonly timings: {
|
|
93
72
|
readonly total: number;
|
|
@@ -97,17 +76,17 @@ export interface MigrationApplyResult {
|
|
|
97
76
|
function mapApplyFailure(failure: MigrationApplyFailure): CliStructuredErrorType {
|
|
98
77
|
return errorRuntime(failure.summary, {
|
|
99
78
|
why: failure.why ?? 'Migration runner failed',
|
|
100
|
-
fix: 'Fix the issue and re-run `prisma-next
|
|
79
|
+
fix: 'Fix the issue and re-run `prisma-next migrate --to <contract>` — previously applied migrations are preserved.',
|
|
101
80
|
meta: failure.meta ?? {},
|
|
102
81
|
});
|
|
103
82
|
}
|
|
104
83
|
|
|
105
|
-
async function
|
|
106
|
-
options:
|
|
84
|
+
async function executeMigrateCommand(
|
|
85
|
+
options: MigrateCommandOptions,
|
|
107
86
|
flags: GlobalFlags,
|
|
108
87
|
ui: TerminalUI,
|
|
109
88
|
startTime: number,
|
|
110
|
-
): Promise<Result<
|
|
89
|
+
): Promise<Result<MigrateResult, CliStructuredErrorType>> {
|
|
111
90
|
const config = await loadConfig(options.config);
|
|
112
91
|
const { configPath, migrationsDir, appMigrationsDir, appMigrationsRelative, refsDir } =
|
|
113
92
|
resolveMigrationPaths(options.config, config);
|
|
@@ -116,8 +95,8 @@ async function executeMigrationApplyCommand(
|
|
|
116
95
|
if (!dbConnection) {
|
|
117
96
|
return notOk(
|
|
118
97
|
errorDatabaseConnectionRequired({
|
|
119
|
-
why: `Database connection is required for
|
|
120
|
-
commandName: '
|
|
98
|
+
why: `Database connection is required for migrate (set db.connection in ${configPath}, or pass --db <url>)`,
|
|
99
|
+
commandName: 'migrate',
|
|
121
100
|
}),
|
|
122
101
|
);
|
|
123
102
|
}
|
|
@@ -125,7 +104,7 @@ async function executeMigrationApplyCommand(
|
|
|
125
104
|
if (!config.driver) {
|
|
126
105
|
return notOk(
|
|
127
106
|
errorDriverRequired({
|
|
128
|
-
why: 'Config.driver is required for
|
|
107
|
+
why: 'Config.driver is required for migrate',
|
|
129
108
|
}),
|
|
130
109
|
);
|
|
131
110
|
}
|
|
@@ -139,12 +118,22 @@ async function executeMigrationApplyCommand(
|
|
|
139
118
|
}
|
|
140
119
|
|
|
141
120
|
let refEntry: RefEntry | undefined;
|
|
142
|
-
const
|
|
121
|
+
const toArg = options.to;
|
|
143
122
|
|
|
144
|
-
if (
|
|
123
|
+
if (toArg) {
|
|
145
124
|
try {
|
|
146
125
|
const refs = await readRefs(refsDir);
|
|
147
|
-
|
|
126
|
+
const { graph } = await loadMigrationPackages(appMigrationsDir);
|
|
127
|
+
const refResult = parseContractRef(toArg, { graph, refs });
|
|
128
|
+
if (!refResult.ok) {
|
|
129
|
+
return notOk(mapRefResolutionError(refResult.failure));
|
|
130
|
+
}
|
|
131
|
+
if (refResult.value.provenance.kind === 'ref') {
|
|
132
|
+
const resolved = refs[refResult.value.provenance.refName];
|
|
133
|
+
if (resolved) refEntry = resolved;
|
|
134
|
+
} else {
|
|
135
|
+
refEntry = { hash: refResult.value.hash, invariants: [] };
|
|
136
|
+
}
|
|
148
137
|
} catch (error) {
|
|
149
138
|
if (MigrationToolsError.is(error)) {
|
|
150
139
|
return notOk(mapMigrationToolsError(error));
|
|
@@ -153,8 +142,6 @@ async function executeMigrationApplyCommand(
|
|
|
153
142
|
}
|
|
154
143
|
}
|
|
155
144
|
|
|
156
|
-
// Resolve and parse the contract envelope. The aggregate-walking
|
|
157
|
-
// operation needs the validated app contract to load the aggregate.
|
|
158
145
|
const contractPathAbsolute = resolveContractPath(config);
|
|
159
146
|
let contractRaw: Contract;
|
|
160
147
|
try {
|
|
@@ -165,7 +152,7 @@ async function executeMigrationApplyCommand(
|
|
|
165
152
|
return notOk(
|
|
166
153
|
errorFileNotFound(contractPathAbsolute, {
|
|
167
154
|
why: `Contract file not found at ${contractPathAbsolute}`,
|
|
168
|
-
fix: 'Run `prisma-next contract emit` to generate a valid contract.json, then retry
|
|
155
|
+
fix: 'Run `prisma-next contract emit` to generate a valid contract.json, then retry.',
|
|
169
156
|
}),
|
|
170
157
|
);
|
|
171
158
|
}
|
|
@@ -188,21 +175,19 @@ async function executeMigrationApplyCommand(
|
|
|
188
175
|
value: maskConnectionUrl(dbConnection),
|
|
189
176
|
});
|
|
190
177
|
}
|
|
191
|
-
if (
|
|
192
|
-
details.push({ label: '
|
|
178
|
+
if (toArg) {
|
|
179
|
+
details.push({ label: 'to', value: toArg });
|
|
193
180
|
}
|
|
194
181
|
const header = formatStyledHeader({
|
|
195
|
-
command: '
|
|
196
|
-
description: 'Apply planned migrations to the database',
|
|
197
|
-
url: 'https://pris.ly/
|
|
182
|
+
command: 'migrate',
|
|
183
|
+
description: 'Apply planned migrations to advance the database',
|
|
184
|
+
url: 'https://pris.ly/migrate',
|
|
198
185
|
details,
|
|
199
186
|
flags,
|
|
200
187
|
});
|
|
201
188
|
ui.stderr(header);
|
|
202
189
|
}
|
|
203
190
|
|
|
204
|
-
// Load app-space migration packages — the aggregate operation
|
|
205
|
-
// needs them to hydrate the app member's graph for graph-walk.
|
|
206
191
|
let appPackages: Awaited<ReturnType<typeof loadMigrationPackages>>;
|
|
207
192
|
try {
|
|
208
193
|
appPackages = await loadMigrationPackages(appMigrationsDir);
|
|
@@ -224,13 +209,6 @@ async function executeMigrationApplyCommand(
|
|
|
224
209
|
try {
|
|
225
210
|
await client.connect(dbConnection);
|
|
226
211
|
|
|
227
|
-
// Pre-check unknown invariants against `(declared by app graph) ∪
|
|
228
|
-
// (already on the app marker)`. The marker side of the union
|
|
229
|
-
// catches the case where the ref carries an invariant whose
|
|
230
|
-
// declaring migration was retired (history rewritten) but whose
|
|
231
|
-
// id is recorded on the marker — surfacing UNKNOWN_INVARIANT
|
|
232
|
-
// there would be misleading because the database has already
|
|
233
|
-
// satisfied the requirement.
|
|
234
212
|
if (refEntry && refEntry.invariants.length > 0) {
|
|
235
213
|
const allMarkers = await client.readAllMarkers();
|
|
236
214
|
const appMarker = allMarkers.get('app') ?? null;
|
|
@@ -242,7 +220,7 @@ async function executeMigrationApplyCommand(
|
|
|
242
220
|
return notOk(
|
|
243
221
|
mapMigrationToolsError(
|
|
244
222
|
errorUnknownInvariant({
|
|
245
|
-
...ifDefined('refName',
|
|
223
|
+
...ifDefined('refName', toArg),
|
|
246
224
|
unknown,
|
|
247
225
|
declared: [...declared].sort(),
|
|
248
226
|
}),
|
|
@@ -261,7 +239,7 @@ async function executeMigrationApplyCommand(
|
|
|
261
239
|
appMigrationPackages: appPackages.bundles,
|
|
262
240
|
...ifDefined('refHash', refEntry?.hash),
|
|
263
241
|
...(refEntry?.invariants ? { refInvariants: refEntry.invariants } : {}),
|
|
264
|
-
...(refEntry !== undefined ? ifDefined('refName',
|
|
242
|
+
...(refEntry !== undefined ? ifDefined('refName', toArg) : {}),
|
|
265
243
|
});
|
|
266
244
|
|
|
267
245
|
if (!applyResult.ok) {
|
|
@@ -290,7 +268,7 @@ async function executeMigrationApplyCommand(
|
|
|
290
268
|
}
|
|
291
269
|
return notOk(
|
|
292
270
|
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
293
|
-
why: `Unexpected error during
|
|
271
|
+
why: `Unexpected error during migrate: ${error instanceof Error ? error.message : String(error)}`,
|
|
294
272
|
}),
|
|
295
273
|
);
|
|
296
274
|
} finally {
|
|
@@ -298,23 +276,29 @@ async function executeMigrationApplyCommand(
|
|
|
298
276
|
}
|
|
299
277
|
}
|
|
300
278
|
|
|
301
|
-
export function
|
|
302
|
-
const command = new Command('
|
|
279
|
+
export function createMigrateCommand(): Command {
|
|
280
|
+
const command = new Command('migrate');
|
|
303
281
|
setCommandDescriptions(
|
|
304
282
|
command,
|
|
305
|
-
'Apply planned migrations to the database',
|
|
283
|
+
'Apply planned migrations to advance the database',
|
|
306
284
|
'Walks every contract space (app + extensions) and applies pending\n' +
|
|
307
285
|
'on-disk migrations in canonical order (extensions alphabetically,\n' +
|
|
308
|
-
'then app). Graph-walks the on-disk migration graph for every space
|
|
309
|
-
|
|
310
|
-
"transaction; per-space failure rolls back every space's writes.",
|
|
286
|
+
'then app). Graph-walks the on-disk migration graph for every space.\n' +
|
|
287
|
+
'Use --to to target a specific contract (hash, ref name, migration dir).',
|
|
311
288
|
);
|
|
312
|
-
setCommandExamples(command, [
|
|
289
|
+
setCommandExamples(command, [
|
|
290
|
+
'prisma-next migrate --db $DATABASE_URL',
|
|
291
|
+
'prisma-next migrate --to production --db $DATABASE_URL',
|
|
292
|
+
'prisma-next migrate --to sha256:abc123 --db $DATABASE_URL',
|
|
293
|
+
]);
|
|
313
294
|
addGlobalOptions(command)
|
|
314
295
|
.option('--db <url>', 'Database connection string')
|
|
315
296
|
.option('--config <path>', 'Path to prisma-next.config.ts')
|
|
316
|
-
.option(
|
|
317
|
-
|
|
297
|
+
.option(
|
|
298
|
+
'--to <contract>',
|
|
299
|
+
'Target contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',
|
|
300
|
+
)
|
|
301
|
+
.action(async (options: MigrateCommandOptions) => {
|
|
318
302
|
const flags = parseGlobalFlags(options);
|
|
319
303
|
const startTime = Date.now();
|
|
320
304
|
|
|
@@ -323,13 +307,13 @@ export function createMigrationApplyCommand(): Command {
|
|
|
323
307
|
interactive: flags.interactive,
|
|
324
308
|
});
|
|
325
309
|
|
|
326
|
-
const result = await
|
|
310
|
+
const result = await executeMigrateCommand(options, flags, ui, startTime);
|
|
327
311
|
|
|
328
|
-
const exitCode = handleResult(result, flags, ui, (
|
|
312
|
+
const exitCode = handleResult(result, flags, ui, (migrateResult) => {
|
|
329
313
|
if (flags.json) {
|
|
330
|
-
ui.output(JSON.stringify(
|
|
314
|
+
ui.output(JSON.stringify(migrateResult, null, 2));
|
|
331
315
|
} else if (!flags.quiet) {
|
|
332
|
-
ui.log(formatMigrationApplyCommandOutput(
|
|
316
|
+
ui.log(formatMigrationApplyCommandOutput(migrateResult, flags));
|
|
333
317
|
}
|
|
334
318
|
});
|
|
335
319
|
|