@prisma-next/cli 0.3.0-dev.55 → 0.3.0-dev.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +145 -6
- package/dist/cli-errors-JlPTsazx.mjs +3 -0
- package/dist/cli.mjs +29 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-B7f4PZZ1.mjs → client-PimzSD1f.mjs} +155 -82
- package/dist/client-PimzSD1f.mjs.map +1 -0
- package/dist/commands/contract-emit.mjs +5 -3
- package/dist/commands/contract-emit.mjs.map +1 -1
- package/dist/commands/db-init.mjs +5 -4
- package/dist/commands/db-init.mjs.map +1 -1
- package/dist/commands/db-introspect.mjs +5 -3
- package/dist/commands/db-introspect.mjs.map +1 -1
- package/dist/commands/db-schema-verify.d.mts.map +1 -1
- package/dist/commands/db-schema-verify.mjs +6 -4
- package/dist/commands/db-schema-verify.mjs.map +1 -1
- package/dist/commands/db-sign.d.mts.map +1 -1
- package/dist/commands/db-sign.mjs +6 -4
- package/dist/commands/db-sign.mjs.map +1 -1
- package/dist/commands/db-update.mjs +5 -4
- 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 +6 -4
- package/dist/commands/db-verify.mjs.map +1 -1
- package/dist/commands/migration-apply.d.mts +23 -0
- package/dist/commands/migration-apply.d.mts.map +1 -0
- package/dist/commands/migration-apply.mjs +249 -0
- package/dist/commands/migration-apply.mjs.map +1 -0
- package/dist/commands/migration-plan.d.mts +25 -0
- package/dist/commands/migration-plan.d.mts.map +1 -0
- package/dist/commands/migration-plan.mjs +266 -0
- package/dist/commands/migration-plan.mjs.map +1 -0
- package/dist/commands/migration-show.d.mts +28 -0
- package/dist/commands/migration-show.d.mts.map +1 -0
- package/dist/commands/migration-show.mjs +138 -0
- package/dist/commands/migration-show.mjs.map +1 -0
- package/dist/commands/migration-status.d.mts +35 -0
- package/dist/commands/migration-status.d.mts.map +1 -0
- package/dist/commands/migration-status.mjs +259 -0
- package/dist/commands/migration-status.mjs.map +1 -0
- package/dist/commands/migration-verify.d.mts +16 -0
- package/dist/commands/migration-verify.d.mts.map +1 -0
- package/dist/commands/migration-verify.mjs +86 -0
- package/dist/commands/migration-verify.mjs.map +1 -0
- package/dist/{config-loader-DqKf1qSa.mjs → config-loader-PPf4CtDj.mjs} +4 -3
- package/dist/config-loader-PPf4CtDj.mjs.map +1 -0
- package/dist/config-loader.d.mts +1 -1
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/config-loader.mjs +1 -1
- package/dist/exports/config-types.d.mts +1 -1
- package/dist/exports/config-types.mjs +1 -1
- package/dist/exports/control-api.d.mts +100 -2
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +3 -2
- package/dist/exports/control-api.mjs.map +1 -1
- package/dist/exports/index.mjs +1 -1
- package/dist/extract-sql-ddl-BmlKvk4o.mjs +26 -0
- package/dist/extract-sql-ddl-BmlKvk4o.mjs.map +1 -0
- package/dist/framework-components-CjV_jD8f.mjs +59 -0
- package/dist/framework-components-CjV_jD8f.mjs.map +1 -0
- package/dist/{migration-command-scaffold-BELw_do2.mjs → migration-command-scaffold-DfY_F3ev.mjs} +7 -5
- package/dist/migration-command-scaffold-DfY_F3ev.mjs.map +1 -0
- package/dist/progress-adapter-DENrzF6I.mjs +49 -0
- package/dist/progress-adapter-DENrzF6I.mjs.map +1 -0
- package/dist/{result-handler-BhmrXIvT.mjs → result-handler-iA9JtUC7.mjs} +158 -51
- package/dist/result-handler-iA9JtUC7.mjs.map +1 -0
- package/package.json +34 -12
- package/src/cli.ts +38 -0
- package/src/commands/db-schema-verify.ts +6 -4
- package/src/commands/db-sign.ts +6 -4
- package/src/commands/db-verify.ts +6 -4
- package/src/commands/migration-apply.ts +431 -0
- package/src/commands/migration-plan.ts +446 -0
- package/src/commands/migration-show.ts +255 -0
- package/src/commands/migration-status.ts +436 -0
- package/src/commands/migration-verify.ts +151 -0
- package/src/config-loader.ts +13 -3
- package/src/control-api/client.ts +31 -0
- package/src/control-api/operations/migration-apply.ts +195 -0
- package/src/control-api/types.ts +113 -1
- package/src/exports/config-types.ts +3 -3
- package/src/utils/command-helpers.ts +11 -0
- package/src/utils/migration-command-scaffold.ts +7 -6
- package/src/utils/output.ts +305 -3
- package/dist/client-B7f4PZZ1.mjs.map +0 -1
- package/dist/config-loader-DqKf1qSa.mjs.map +0 -1
- package/dist/migration-command-scaffold-BELw_do2.mjs.map +0 -1
- package/dist/result-handler-BhmrXIvT.mjs.map +0 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import type { TargetBoundComponentDescriptor } from '@prisma-next/contract/framework-components';
|
|
2
|
+
import type { ContractIR } from '@prisma-next/contract/ir';
|
|
3
|
+
import { EMPTY_CONTRACT_HASH } from '@prisma-next/core-control-plane/constants';
|
|
4
|
+
import type {
|
|
5
|
+
ControlDriverInstance,
|
|
6
|
+
ControlFamilyInstance,
|
|
7
|
+
MigrationPlanOperation,
|
|
8
|
+
MigrationRunnerResult,
|
|
9
|
+
TargetMigrationsCapability,
|
|
10
|
+
} from '@prisma-next/core-control-plane/types';
|
|
11
|
+
import { notOk, ok } from '@prisma-next/utils/result';
|
|
12
|
+
import type {
|
|
13
|
+
MigrationApplyAppliedEntry,
|
|
14
|
+
MigrationApplyResult,
|
|
15
|
+
MigrationApplyStep,
|
|
16
|
+
OnControlProgress,
|
|
17
|
+
} from '../types';
|
|
18
|
+
|
|
19
|
+
export interface ExecuteMigrationApplyOptions<TFamilyId extends string, TTargetId extends string> {
|
|
20
|
+
readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
|
|
21
|
+
readonly familyInstance: ControlFamilyInstance<TFamilyId>;
|
|
22
|
+
readonly originHash: string;
|
|
23
|
+
readonly destinationHash: string;
|
|
24
|
+
readonly pendingMigrations: readonly MigrationApplyStep[];
|
|
25
|
+
readonly migrations: TargetMigrationsCapability<
|
|
26
|
+
TFamilyId,
|
|
27
|
+
TTargetId,
|
|
28
|
+
ControlFamilyInstance<TFamilyId>
|
|
29
|
+
>;
|
|
30
|
+
readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;
|
|
31
|
+
readonly targetId: string;
|
|
32
|
+
readonly onProgress?: OnControlProgress;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function executeMigrationApply<TFamilyId extends string, TTargetId extends string>(
|
|
36
|
+
options: ExecuteMigrationApplyOptions<TFamilyId, TTargetId>,
|
|
37
|
+
): Promise<MigrationApplyResult> {
|
|
38
|
+
const {
|
|
39
|
+
driver,
|
|
40
|
+
familyInstance,
|
|
41
|
+
originHash,
|
|
42
|
+
destinationHash,
|
|
43
|
+
pendingMigrations,
|
|
44
|
+
migrations,
|
|
45
|
+
frameworkComponents,
|
|
46
|
+
targetId,
|
|
47
|
+
onProgress,
|
|
48
|
+
} = options;
|
|
49
|
+
|
|
50
|
+
if (pendingMigrations.length === 0) {
|
|
51
|
+
if (originHash !== destinationHash) {
|
|
52
|
+
return notOk({
|
|
53
|
+
code: 'MIGRATION_PATH_NOT_FOUND' as const,
|
|
54
|
+
summary: 'No migrations provided for requested origin and destination',
|
|
55
|
+
why: `Requested ${originHash} -> ${destinationHash} but pendingMigrations is empty`,
|
|
56
|
+
meta: { originHash, destinationHash },
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return ok({
|
|
60
|
+
migrationsApplied: 0,
|
|
61
|
+
markerHash: originHash,
|
|
62
|
+
applied: [],
|
|
63
|
+
summary: 'Already up to date',
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const firstMigration = pendingMigrations[0]!;
|
|
68
|
+
const lastMigration = pendingMigrations[pendingMigrations.length - 1]!;
|
|
69
|
+
if (firstMigration.from !== originHash || lastMigration.to !== destinationHash) {
|
|
70
|
+
return notOk({
|
|
71
|
+
code: 'MIGRATION_PATH_NOT_FOUND' as const,
|
|
72
|
+
summary: 'Migration apply path does not match requested origin and destination',
|
|
73
|
+
why: `Path resolved as ${firstMigration.from} -> ${lastMigration.to}, but requested ${originHash} -> ${destinationHash}`,
|
|
74
|
+
meta: {
|
|
75
|
+
originHash,
|
|
76
|
+
destinationHash,
|
|
77
|
+
pathOrigin: firstMigration.from,
|
|
78
|
+
pathDestination: lastMigration.to,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (let i = 1; i < pendingMigrations.length; i++) {
|
|
84
|
+
const previous = pendingMigrations[i - 1]!;
|
|
85
|
+
const current = pendingMigrations[i]!;
|
|
86
|
+
if (previous.to !== current.from) {
|
|
87
|
+
return notOk({
|
|
88
|
+
code: 'MIGRATION_PATH_NOT_FOUND' as const,
|
|
89
|
+
summary: 'Migration apply path contains a discontinuity between adjacent migrations',
|
|
90
|
+
why: `Migration "${previous.dirName}" ends at ${previous.to}, but next migration "${current.dirName}" starts at ${current.from}`,
|
|
91
|
+
meta: {
|
|
92
|
+
originHash,
|
|
93
|
+
destinationHash,
|
|
94
|
+
previousDirName: previous.dirName,
|
|
95
|
+
previousTo: previous.to,
|
|
96
|
+
currentDirName: current.dirName,
|
|
97
|
+
currentFrom: current.from,
|
|
98
|
+
discontinuityIndex: i,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const runner = migrations.createRunner(familyInstance);
|
|
105
|
+
const applied: MigrationApplyAppliedEntry[] = [];
|
|
106
|
+
|
|
107
|
+
for (const migration of pendingMigrations) {
|
|
108
|
+
const migrationSpanId = `migration:${migration.dirName}`;
|
|
109
|
+
onProgress?.({
|
|
110
|
+
action: 'migrationApply',
|
|
111
|
+
kind: 'spanStart',
|
|
112
|
+
spanId: migrationSpanId,
|
|
113
|
+
label: `Applying ${migration.dirName}`,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const operations = migration.operations as readonly MigrationPlanOperation[];
|
|
117
|
+
|
|
118
|
+
// Allow all operation classes. The policy gate belongs at plan time, not
|
|
119
|
+
// apply time — the planner already decided what to emit. Restricting here
|
|
120
|
+
// would be a tautology (the allowed set would just mirror what's in ops).
|
|
121
|
+
const policy = {
|
|
122
|
+
allowedOperationClasses: ['additive', 'widening', 'destructive'] as const,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// EMPTY_CONTRACT_HASH means "no prior state" — the runner expects origin: null
|
|
126
|
+
// for a fresh database (no marker present).
|
|
127
|
+
const plan = {
|
|
128
|
+
targetId,
|
|
129
|
+
origin: migration.from === EMPTY_CONTRACT_HASH ? null : { storageHash: migration.from },
|
|
130
|
+
destination: { storageHash: migration.to },
|
|
131
|
+
operations,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const destinationContract = familyInstance.validateContractIR(
|
|
135
|
+
migration.toContract as ContractIR,
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const runnerResult: MigrationRunnerResult = await runner.execute({
|
|
139
|
+
plan,
|
|
140
|
+
driver,
|
|
141
|
+
destinationContract,
|
|
142
|
+
policy,
|
|
143
|
+
executionChecks: {
|
|
144
|
+
prechecks: true,
|
|
145
|
+
postchecks: true,
|
|
146
|
+
idempotencyChecks: true,
|
|
147
|
+
},
|
|
148
|
+
frameworkComponents,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
if (!runnerResult.ok) {
|
|
152
|
+
onProgress?.({
|
|
153
|
+
action: 'migrationApply',
|
|
154
|
+
kind: 'spanEnd',
|
|
155
|
+
spanId: migrationSpanId,
|
|
156
|
+
outcome: 'error',
|
|
157
|
+
});
|
|
158
|
+
return notOk({
|
|
159
|
+
code: 'RUNNER_FAILED' as const,
|
|
160
|
+
summary: runnerResult.failure.summary,
|
|
161
|
+
why: runnerResult.failure.why,
|
|
162
|
+
meta: {
|
|
163
|
+
migration: migration.dirName,
|
|
164
|
+
from: migration.from,
|
|
165
|
+
to: migration.to,
|
|
166
|
+
...(runnerResult.failure.meta ?? {}),
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
onProgress?.({
|
|
172
|
+
action: 'migrationApply',
|
|
173
|
+
kind: 'spanEnd',
|
|
174
|
+
spanId: migrationSpanId,
|
|
175
|
+
outcome: 'ok',
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
applied.push({
|
|
179
|
+
dirName: migration.dirName,
|
|
180
|
+
from: migration.from,
|
|
181
|
+
to: migration.to,
|
|
182
|
+
operationsExecuted: runnerResult.value.operationsExecuted,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const finalHash = pendingMigrations[pendingMigrations.length - 1]!.to;
|
|
187
|
+
const totalOps = applied.reduce((sum, a) => sum + a.operationsExecuted, 0);
|
|
188
|
+
|
|
189
|
+
return ok({
|
|
190
|
+
migrationsApplied: applied.length,
|
|
191
|
+
markerHash: finalHash,
|
|
192
|
+
applied,
|
|
193
|
+
summary: `Applied ${applied.length} migration(s) (${totalOps} operation(s)), marker at ${finalHash}`,
|
|
194
|
+
});
|
|
195
|
+
}
|
package/src/control-api/types.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ContractSourceDiagnostics,
|
|
3
3
|
ContractSourceProvider,
|
|
4
|
-
} from '@prisma-next/
|
|
4
|
+
} from '@prisma-next/config/config-types';
|
|
5
|
+
import type { ContractMarkerRecord } from '@prisma-next/contract/types';
|
|
5
6
|
import type { CoreSchemaView } from '@prisma-next/core-control-plane/schema-view';
|
|
6
7
|
import type {
|
|
7
8
|
ControlAdapterDescriptor,
|
|
@@ -61,6 +62,7 @@ export interface ControlClientOptions {
|
|
|
61
62
|
export type ControlActionName =
|
|
62
63
|
| 'dbInit'
|
|
63
64
|
| 'dbUpdate'
|
|
65
|
+
| 'migrationApply'
|
|
64
66
|
| 'verify'
|
|
65
67
|
| 'schemaVerify'
|
|
66
68
|
| 'sign'
|
|
@@ -419,6 +421,95 @@ export interface EmitFailure {
|
|
|
419
421
|
*/
|
|
420
422
|
export type EmitResult = Result<EmitSuccess, EmitFailure>;
|
|
421
423
|
|
|
424
|
+
// ============================================================================
|
|
425
|
+
// Migration Apply Types
|
|
426
|
+
// ============================================================================
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* A pre-planned migration step ready for execution.
|
|
430
|
+
* Contains the manifest metadata and the serialized operations from ops.json.
|
|
431
|
+
*/
|
|
432
|
+
export interface MigrationApplyStep {
|
|
433
|
+
readonly dirName: string;
|
|
434
|
+
readonly from: string;
|
|
435
|
+
readonly to: string;
|
|
436
|
+
readonly toContract: unknown;
|
|
437
|
+
readonly operations: ReadonlyArray<{
|
|
438
|
+
readonly id: string;
|
|
439
|
+
readonly label: string;
|
|
440
|
+
readonly operationClass: string;
|
|
441
|
+
readonly [key: string]: unknown;
|
|
442
|
+
}>;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Options for the migrationApply operation.
|
|
447
|
+
*/
|
|
448
|
+
export interface MigrationApplyOptions {
|
|
449
|
+
/**
|
|
450
|
+
* Hash of the database state this apply path starts from.
|
|
451
|
+
* This is resolved by the caller (typically the CLI orchestration layer).
|
|
452
|
+
*/
|
|
453
|
+
readonly originHash: string;
|
|
454
|
+
/**
|
|
455
|
+
* Hash of the target contract this apply path must reach.
|
|
456
|
+
* This is resolved by the caller (typically the CLI orchestration layer).
|
|
457
|
+
*/
|
|
458
|
+
readonly destinationHash: string;
|
|
459
|
+
/**
|
|
460
|
+
* Ordered list of migrations to execute from originHash to destinationHash.
|
|
461
|
+
* The execution layer does not choose defaults; it only executes this explicit path.
|
|
462
|
+
*/
|
|
463
|
+
readonly pendingMigrations: readonly MigrationApplyStep[];
|
|
464
|
+
/**
|
|
465
|
+
* Database connection. If provided, migrationApply will connect before executing.
|
|
466
|
+
* If omitted, the client must already be connected.
|
|
467
|
+
*/
|
|
468
|
+
readonly connection?: unknown;
|
|
469
|
+
/** Optional progress callback for observing operation progress */
|
|
470
|
+
readonly onProgress?: OnControlProgress;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Record of a successfully applied migration.
|
|
475
|
+
*/
|
|
476
|
+
export interface MigrationApplyAppliedEntry {
|
|
477
|
+
readonly dirName: string;
|
|
478
|
+
readonly from: string;
|
|
479
|
+
readonly to: string;
|
|
480
|
+
readonly operationsExecuted: number;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Successful migrationApply result.
|
|
485
|
+
*/
|
|
486
|
+
export interface MigrationApplySuccess {
|
|
487
|
+
readonly migrationsApplied: number;
|
|
488
|
+
readonly markerHash: string;
|
|
489
|
+
readonly applied: readonly MigrationApplyAppliedEntry[];
|
|
490
|
+
readonly summary: string;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Failure codes for migrationApply operation.
|
|
495
|
+
*/
|
|
496
|
+
export type MigrationApplyFailureCode = 'RUNNER_FAILED' | 'MIGRATION_PATH_NOT_FOUND';
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Failure details for migrationApply operation.
|
|
500
|
+
*/
|
|
501
|
+
export interface MigrationApplyFailure {
|
|
502
|
+
readonly code: MigrationApplyFailureCode;
|
|
503
|
+
readonly summary: string;
|
|
504
|
+
readonly why: string | undefined;
|
|
505
|
+
readonly meta: Record<string, unknown> | undefined;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Result type for migrationApply operation.
|
|
510
|
+
*/
|
|
511
|
+
export type MigrationApplyResult = Result<MigrationApplySuccess, MigrationApplyFailure>;
|
|
512
|
+
|
|
422
513
|
// ============================================================================
|
|
423
514
|
// Standalone Contract Emit Types
|
|
424
515
|
// ============================================================================
|
|
@@ -547,6 +638,27 @@ export interface ControlClient {
|
|
|
547
638
|
*/
|
|
548
639
|
dbUpdate(options: DbUpdateOptions): Promise<DbUpdateResult>;
|
|
549
640
|
|
|
641
|
+
/**
|
|
642
|
+
* Reads the contract marker from the database.
|
|
643
|
+
* Returns null if no marker exists (fresh database).
|
|
644
|
+
*
|
|
645
|
+
* @throws If not connected or infrastructure failure
|
|
646
|
+
*/
|
|
647
|
+
readMarker(): Promise<ContractMarkerRecord | null>;
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Applies pre-planned on-disk migrations to the database.
|
|
651
|
+
* Each migration runs in its own transaction with full execution checks.
|
|
652
|
+
* Resume-safe: re-running after failure picks up from the last applied migration.
|
|
653
|
+
*
|
|
654
|
+
* @param options.originHash - Explicit source hash for the apply path
|
|
655
|
+
* @param options.destinationHash - Explicit destination hash for the apply path
|
|
656
|
+
* @param options.pendingMigrations - Ordered migrations to execute
|
|
657
|
+
* @returns Result pattern: Ok with applied details, NotOk with failure details
|
|
658
|
+
* @throws If not connected, target doesn't support migrations, or infrastructure failure
|
|
659
|
+
*/
|
|
660
|
+
migrationApply(options: MigrationApplyOptions): Promise<MigrationApplyResult>;
|
|
661
|
+
|
|
550
662
|
/**
|
|
551
663
|
* Introspects the database schema.
|
|
552
664
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// Re-export
|
|
1
|
+
// Re-export config package types for convenience
|
|
2
2
|
export type {
|
|
3
3
|
ContractConfig,
|
|
4
4
|
PrismaNextConfig,
|
|
5
|
-
} from '@prisma-next/
|
|
6
|
-
export { defineConfig } from '@prisma-next/
|
|
5
|
+
} from '@prisma-next/config/config-types';
|
|
6
|
+
export { defineConfig } from '@prisma-next/config/config-types';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Command } from 'commander';
|
|
2
|
+
import { resolve } from 'pathe';
|
|
2
3
|
|
|
3
4
|
const longDescriptions = new WeakMap<Command, string>();
|
|
4
5
|
|
|
@@ -46,6 +47,16 @@ export interface MigrationCommandOptions {
|
|
|
46
47
|
readonly 'no-color'?: boolean;
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Resolves the absolute path to contract.json from the config.
|
|
52
|
+
* Centralises the fallback logic shared by every command that reads the contract.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveContractPath(config: { contract?: { output?: string } }): string {
|
|
55
|
+
return config.contract?.output
|
|
56
|
+
? resolve(config.contract.output)
|
|
57
|
+
: resolve('src/prisma/contract.json');
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
/**
|
|
50
61
|
* Masks credentials in a database connection URL.
|
|
51
62
|
* Handles standard URLs (username + password + query params) and libpq-style key=value strings.
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
errorTargetMigrationNotSupported,
|
|
15
15
|
errorUnexpected,
|
|
16
16
|
} from './cli-errors';
|
|
17
|
-
import { maskConnectionUrl } from './command-helpers';
|
|
17
|
+
import { maskConnectionUrl, resolveContractPath } from './command-helpers';
|
|
18
18
|
import type { GlobalFlags } from './global-flags';
|
|
19
19
|
import { formatStyledHeader } from './output';
|
|
20
20
|
import { createProgressAdapter } from './progress-adapter';
|
|
@@ -61,9 +61,7 @@ export async function prepareMigrationContext(
|
|
|
61
61
|
const configPath = options.config
|
|
62
62
|
? relative(process.cwd(), resolve(options.config))
|
|
63
63
|
: 'prisma-next.config.ts';
|
|
64
|
-
const contractPathAbsolute = config
|
|
65
|
-
? resolve(config.contract.output)
|
|
66
|
-
: resolve('src/prisma/contract.json');
|
|
64
|
+
const contractPathAbsolute = resolveContractPath(config);
|
|
67
65
|
const contractPath = relative(process.cwd(), contractPathAbsolute);
|
|
68
66
|
|
|
69
67
|
// Output header
|
|
@@ -138,8 +136,11 @@ export async function prepareMigrationContext(
|
|
|
138
136
|
);
|
|
139
137
|
}
|
|
140
138
|
|
|
141
|
-
// Check target supports migrations
|
|
142
|
-
|
|
139
|
+
// Check target supports migrations via optional descriptor capability
|
|
140
|
+
const targetWithMigrations = config.target as typeof config.target & {
|
|
141
|
+
readonly migrations?: unknown;
|
|
142
|
+
};
|
|
143
|
+
if (!targetWithMigrations.migrations) {
|
|
143
144
|
return notOk(
|
|
144
145
|
errorTargetMigrationNotSupported({
|
|
145
146
|
why: `Target "${config.target.id}" does not support migrations`,
|