@prisma-next/cli 0.5.0-dev.9 → 0.5.1
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 +61 -26
- package/dist/cli-errors-B9OBbled.d.mts +3 -0
- package/dist/cli-errors-D3_sMh2K.mjs +33 -0
- package/dist/cli-errors-D3_sMh2K.mjs.map +1 -0
- package/dist/cli.mjs +16 -78
- package/dist/cli.mjs.map +1 -1
- package/dist/client-BCnP7cHo.mjs +1485 -0
- package/dist/client-BCnP7cHo.mjs.map +1 -0
- package/dist/{result-handler-Ba3zWQsI.mjs → command-helpers-BeZHkxV8.mjs} +70 -47
- package/dist/command-helpers-BeZHkxV8.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 +16 -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 +6 -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 +9 -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 +15 -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 +28 -13
- package/dist/commands/migration-apply.d.mts.map +1 -1
- package/dist/commands/migration-apply.mjs +55 -151
- 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 +34 -40
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +33 -6
- package/dist/commands/migration-plan.d.mts.map +1 -1
- package/dist/commands/migration-plan.mjs +2 -348
- 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 +8 -12
- package/dist/commands/migration-ref.mjs.map +1 -1
- package/dist/commands/migration-show.d.mts +64 -10
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +166 -60
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +126 -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-9DBda5Ou.mjs +150 -0
- package/dist/contract-emit-9DBda5Ou.mjs.map +1 -0
- package/dist/contract-emit-B77TsJqf.mjs +327 -0
- package/dist/contract-emit-B77TsJqf.mjs.map +1 -0
- package/dist/{contract-enrichment-CAOELa-H.mjs → contract-enrichment-Dani0mMW.mjs} +4 -6
- package/dist/contract-enrichment-Dani0mMW.mjs.map +1 -0
- package/dist/{contract-infer-D9cC3rJm.mjs → contract-infer-ByxhPjpW.mjs} +13 -22
- package/dist/contract-infer-ByxhPjpW.mjs.map +1 -0
- package/dist/contract-space-aggregate-loader-BrwKK6Q6.mjs +160 -0
- package/dist/contract-space-aggregate-loader-BrwKK6Q6.mjs.map +1 -0
- package/dist/db-verify-Czm5T-J4.mjs +404 -0
- package/dist/db-verify-Czm5T-J4.mjs.map +1 -0
- package/dist/exports/config-types.mjs +1 -2
- package/dist/exports/control-api.d.mts +101 -586
- 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-ChqVUxR-.mjs} +3 -4
- package/dist/{framework-components-Cr--XBKy.mjs.map → framework-components-ChqVUxR-.mjs.map} +1 -1
- package/dist/global-flags-Icqpxk23.d.mts +12 -0
- package/dist/global-flags-Icqpxk23.d.mts.map +1 -0
- package/dist/helpers-eqdN8tH6.mjs +25 -0
- package/dist/helpers-eqdN8tH6.mjs.map +1 -0
- package/dist/{init-C5220SY9.mjs → init-DETSgw3h.mjs} +40 -49
- package/dist/init-DETSgw3h.mjs.map +1 -0
- package/dist/{inspect-live-schema-yrHAvG71.mjs → inspect-live-schema-DxdBd4Er.mjs} +10 -11
- package/dist/inspect-live-schema-DxdBd4Er.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-BdV8JYXV.mjs} +8 -9
- package/dist/migration-command-scaffold-BdV8JYXV.mjs.map +1 -0
- package/dist/migration-plan-mRu5K81L.mjs +494 -0
- package/dist/migration-plan-mRu5K81L.mjs.map +1 -0
- package/dist/{migration-status-DUMiH8_G.mjs → migration-status-By9G5p2H.mjs} +270 -65
- package/dist/migration-status-By9G5p2H.mjs.map +1 -0
- package/dist/migrations-CTsyBXCA.mjs +229 -0
- package/dist/migrations-CTsyBXCA.mjs.map +1 -0
- package/dist/{output-BpcQrnnq.mjs → output-B16Kefzx.mjs} +9 -3
- package/dist/output-B16Kefzx.mjs.map +1 -0
- package/dist/{progress-adapter-DvQWB1nK.mjs → progress-adapter-DFfvZcYL.mjs} +2 -2
- package/dist/{progress-adapter-DvQWB1nK.mjs.map → progress-adapter-DFfvZcYL.mjs.map} +1 -1
- package/dist/result-handler-rmPVKIP2.mjs +25 -0
- package/dist/result-handler-rmPVKIP2.mjs.map +1 -0
- package/dist/rolldown-runtime-twds-ZHy.mjs +14 -0
- package/dist/{terminal-ui-C3ZLwQxK.mjs → terminal-ui-C_hFNbAn.mjs} +4 -28
- package/dist/terminal-ui-C_hFNbAn.mjs.map +1 -0
- package/dist/types-LItU7E4l.d.mts +856 -0
- package/dist/types-LItU7E4l.d.mts.map +1 -0
- package/dist/{verify-Bkycc-Tf.mjs → verify-CiwNWM9N.mjs} +3 -4
- package/dist/verify-CiwNWM9N.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 +15 -3
- package/src/commands/db-update.ts +9 -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 +26 -18
- package/src/commands/inspect-live-schema.ts +10 -5
- package/src/commands/migration-apply.ts +114 -212
- package/src/commands/migration-new.ts +42 -45
- package/src/commands/migration-plan.ts +213 -75
- package/src/commands/migration-ref.ts +8 -7
- package/src/commands/migration-show.ts +274 -70
- package/src/commands/migration-status.ts +491 -64
- package/src/config-path-validation.ts +0 -1
- package/src/control-api/client.ts +85 -5
- package/src/control-api/contract-enrichment.ts +6 -4
- package/src/control-api/operations/apply-aggregate.ts +290 -0
- package/src/control-api/operations/contract-emit.ts +198 -115
- package/src/control-api/operations/db-apply-aggregate.ts +399 -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 +430 -131
- package/src/control-api/types.ts +278 -29
- 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 +177 -0
- package/src/utils/contract-space-seed-phase.ts +201 -0
- package/src/utils/emit-queue.ts +26 -0
- package/src/utils/extension-pack-inputs.ts +162 -0
- package/src/utils/formatters/graph-migration-mapper.ts +7 -3
- package/src/utils/formatters/migrations.ts +255 -77
- 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 +0 -153
- 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/terminal-ui-C3ZLwQxK.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/utils/cli-errors.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Re-export all domain error factories from @prisma-next/errors for convenience.
|
|
3
|
-
* CLI-specific errors (e.g., Commander
|
|
3
|
+
* CLI-specific errors (e.g., Commander argument validation in the main CLI, or
|
|
4
|
+
* clipanion parse errors in the migration-file CLI) can be added here if needed.
|
|
4
5
|
*/
|
|
5
6
|
export type { CliErrorConflict, CliErrorEnvelope } from '@prisma-next/errors/control';
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
import {
|
|
7
9
|
CliStructuredError,
|
|
8
10
|
errorConfigFileNotFound,
|
|
9
11
|
errorConfigValidation,
|
|
@@ -15,11 +17,33 @@ export {
|
|
|
15
17
|
errorFamilyReadMarkerSqlRequired,
|
|
16
18
|
errorFileNotFound,
|
|
17
19
|
errorMigrationCliInvalidConfigArg,
|
|
20
|
+
errorMigrationCliUnknownFlag,
|
|
18
21
|
errorMigrationPlanningFailed,
|
|
19
22
|
errorQueryRunnerFactoryRequired,
|
|
20
23
|
errorTargetMigrationNotSupported,
|
|
21
24
|
errorUnexpected,
|
|
22
25
|
} from '@prisma-next/errors/control';
|
|
26
|
+
import { errorRuntime } from '@prisma-next/errors/execution';
|
|
27
|
+
import type { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
CliStructuredError,
|
|
31
|
+
errorConfigFileNotFound,
|
|
32
|
+
errorConfigValidation,
|
|
33
|
+
errorContractConfigMissing,
|
|
34
|
+
errorContractMissingExtensionPacks,
|
|
35
|
+
errorContractValidationFailed,
|
|
36
|
+
errorDatabaseConnectionRequired,
|
|
37
|
+
errorDriverRequired,
|
|
38
|
+
errorFamilyReadMarkerSqlRequired,
|
|
39
|
+
errorFileNotFound,
|
|
40
|
+
errorMigrationCliInvalidConfigArg,
|
|
41
|
+
errorMigrationCliUnknownFlag,
|
|
42
|
+
errorMigrationPlanningFailed,
|
|
43
|
+
errorQueryRunnerFactoryRequired,
|
|
44
|
+
errorTargetMigrationNotSupported,
|
|
45
|
+
errorUnexpected,
|
|
46
|
+
};
|
|
23
47
|
export {
|
|
24
48
|
ERROR_CODE_DESTRUCTIVE_CHANGES,
|
|
25
49
|
errorDestructiveChanges,
|
|
@@ -38,3 +62,26 @@ export {
|
|
|
38
62
|
errorUnfilledPlaceholder,
|
|
39
63
|
placeholder,
|
|
40
64
|
} from '@prisma-next/errors/migration';
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Maps a `MigrationToolsError` raised by the migration-tools loader/graph
|
|
68
|
+
* surface (`readMigrationPackage`, `readMigrationsDir`, `readRefs`,
|
|
69
|
+
* `resolveRef`, `reconstructGraph`, ...) into a CLI `errorRuntime` envelope.
|
|
70
|
+
*
|
|
71
|
+
* The full `error.details` payload is forwarded into `meta` so machine
|
|
72
|
+
* consumers (`--json`) see structural fields like `dir`, `storedHash`,
|
|
73
|
+
* `computedHash` (for `MIGRATION.HASH_MISMATCH`) alongside the stable
|
|
74
|
+
* `code`. The user-visible `summary`/`why`/`fix` text is unchanged.
|
|
75
|
+
*
|
|
76
|
+
* Callers are expected to gate on `MigrationToolsError.is(error)` first
|
|
77
|
+
* (mirroring the original inline pattern); non-`MigrationToolsError`
|
|
78
|
+
* values are caller-classified (rethrow, wrap with command-specific
|
|
79
|
+
* `errorUnexpected`, etc.).
|
|
80
|
+
*/
|
|
81
|
+
export function mapMigrationToolsError(error: MigrationToolsError): CliStructuredError {
|
|
82
|
+
return errorRuntime(error.message, {
|
|
83
|
+
why: error.why,
|
|
84
|
+
fix: error.fix,
|
|
85
|
+
meta: { code: error.code, ...(error.details ?? {}) },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { VerifyDatabaseSchemaResult } from '@prisma-next/framework-components/control';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Collapse the aggregate verifier's per-space schema results into a
|
|
5
|
+
* single {@link VerifyDatabaseSchemaResult} for the existing CLI
|
|
6
|
+
* display surface. Concatenates issues across members; sums counts;
|
|
7
|
+
* uses the app member's result as the structural envelope (storage
|
|
8
|
+
* hash, target).
|
|
9
|
+
*
|
|
10
|
+
* **Summary policy.** Preserve the per-family phrasing whenever the
|
|
11
|
+
* combined `ok` flag agrees with the app member's `ok` flag — this is
|
|
12
|
+
* the common case (single-family deployments, single-app deployments)
|
|
13
|
+
* and the family's "satisfies / does not satisfy contract" phrasing
|
|
14
|
+
* stays user-visible. When the app passes but an extension fails (or
|
|
15
|
+
* vice versa) the app's summary contradicts the envelope, so fall back
|
|
16
|
+
* to the first failing member's summary. This keeps family phrasing
|
|
17
|
+
* intact and the envelope internally consistent (`ok: false` ↔ failure
|
|
18
|
+
* summary).
|
|
19
|
+
*/
|
|
20
|
+
export function combineSchemaResults(
|
|
21
|
+
perSpace: ReadonlyMap<string, VerifyDatabaseSchemaResult>,
|
|
22
|
+
appSpaceId: string,
|
|
23
|
+
strict: boolean,
|
|
24
|
+
): VerifyDatabaseSchemaResult {
|
|
25
|
+
const appResult = perSpace.get(appSpaceId) ?? perSpace.values().next().value;
|
|
26
|
+
if (appResult === undefined) {
|
|
27
|
+
throw new Error('Aggregate verifier returned no schema results — this is a wiring bug.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let okAll = true;
|
|
31
|
+
let firstFailure: VerifyDatabaseSchemaResult | undefined;
|
|
32
|
+
let issues: VerifyDatabaseSchemaResult['schema']['issues'] = [];
|
|
33
|
+
const counts = { pass: 0, warn: 0, fail: 0, totalNodes: 0 };
|
|
34
|
+
const childRoots: Array<VerifyDatabaseSchemaResult['schema']['root']> = [];
|
|
35
|
+
for (const [, result] of perSpace) {
|
|
36
|
+
if (!result.ok) {
|
|
37
|
+
okAll = false;
|
|
38
|
+
if (firstFailure === undefined) firstFailure = result;
|
|
39
|
+
}
|
|
40
|
+
issues = [...issues, ...result.schema.issues];
|
|
41
|
+
counts.pass += result.schema.counts.pass;
|
|
42
|
+
counts.warn += result.schema.counts.warn;
|
|
43
|
+
counts.fail += result.schema.counts.fail;
|
|
44
|
+
counts.totalNodes += result.schema.counts.totalNodes;
|
|
45
|
+
childRoots.push(result.schema.root);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// When `okAll !== appResult.ok`, exactly one shape is reachable: app passes
|
|
49
|
+
// (`appResult.ok === true`) and at least one other member failed
|
|
50
|
+
// (`okAll === false`). In that shape the failure was assigned to
|
|
51
|
+
// `firstFailure` during iteration, so non-null assertion is safe. The mirror
|
|
52
|
+
// shape (app fails while every member passes) is impossible because
|
|
53
|
+
// `appResult` either *is* a member of `perSpace` or is the first iterator
|
|
54
|
+
// value; either way its `ok` flag participates in `okAll`.
|
|
55
|
+
const summary =
|
|
56
|
+
okAll === appResult.ok
|
|
57
|
+
? appResult.summary
|
|
58
|
+
: (firstFailure as VerifyDatabaseSchemaResult).summary;
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
ok: okAll,
|
|
62
|
+
...(okAll ? {} : { code: appResult.code ?? 'PN-RUN-3010' }),
|
|
63
|
+
summary,
|
|
64
|
+
contract: appResult.contract,
|
|
65
|
+
target: appResult.target,
|
|
66
|
+
schema: {
|
|
67
|
+
issues,
|
|
68
|
+
root: {
|
|
69
|
+
status: okAll ? 'pass' : 'fail',
|
|
70
|
+
kind: 'aggregate',
|
|
71
|
+
name: 'aggregate',
|
|
72
|
+
contractPath: '',
|
|
73
|
+
code: 'AGGREGATE',
|
|
74
|
+
message: okAll ? 'Aggregate schema matches' : 'Aggregate schema mismatch',
|
|
75
|
+
expected: undefined,
|
|
76
|
+
actual: undefined,
|
|
77
|
+
children: childRoots,
|
|
78
|
+
},
|
|
79
|
+
counts,
|
|
80
|
+
},
|
|
81
|
+
meta: { strict },
|
|
82
|
+
timings: { total: 0 },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import type { ControlTargetDescriptor } from '@prisma-next/framework-components/control';
|
|
3
3
|
import { hasMigrations } from '@prisma-next/framework-components/control';
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
4
|
+
import type { NoInvariantPathStructuralEdge } from '@prisma-next/migration-tools/errors';
|
|
5
|
+
import type { MigrationEdge, MigrationGraph } from '@prisma-next/migration-tools/graph';
|
|
6
6
|
import { readMigrationsDir } from '@prisma-next/migration-tools/io';
|
|
7
|
-
import type {
|
|
7
|
+
import type { PathDecision } from '@prisma-next/migration-tools/migration-graph';
|
|
8
|
+
import { reconstructGraph } from '@prisma-next/migration-tools/migration-graph';
|
|
9
|
+
import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
|
|
10
|
+
import { APP_SPACE_ID, spaceMigrationDirectory } from '@prisma-next/migration-tools/spaces';
|
|
8
11
|
import { ifDefined } from '@prisma-next/utils/defined';
|
|
9
12
|
import type { Command } from 'commander';
|
|
10
13
|
import { relative, resolve } from 'pathe';
|
|
@@ -77,6 +80,16 @@ export function resolveContractPath(config: { contract?: { output?: string } }):
|
|
|
77
80
|
/**
|
|
78
81
|
* Resolves the migrations directory and config path from CLI options.
|
|
79
82
|
* Shared by migration-apply, migration-plan, and migration-status.
|
|
83
|
+
*
|
|
84
|
+
* - `migrationsDir` is the project's top-level `migrations/` directory
|
|
85
|
+
* (the root that the aggregate loader walks for every contract space).
|
|
86
|
+
* - `appMigrationsDir` is the app subspace directory under it
|
|
87
|
+
* (`<migrationsDir>/<APP_SPACE_ID>/`). Every per-app reader / writer
|
|
88
|
+
* (`migration new`, `migration plan`, `migration apply`,
|
|
89
|
+
* `migration status`, `migration show`, `migration ref`) operates on
|
|
90
|
+
* this directory. Extensions own their own `migrations/<spaceId>/`.
|
|
91
|
+
* - `refsDir` is the app's refs directory (`<appMigrationsDir>/refs/`).
|
|
92
|
+
* The framework does not maintain refs at the migrations root.
|
|
80
93
|
*/
|
|
81
94
|
export function resolveMigrationPaths(
|
|
82
95
|
configOption: string | undefined,
|
|
@@ -85,6 +98,8 @@ export function resolveMigrationPaths(
|
|
|
85
98
|
configPath: string;
|
|
86
99
|
migrationsDir: string;
|
|
87
100
|
migrationsRelative: string;
|
|
101
|
+
appMigrationsDir: string;
|
|
102
|
+
appMigrationsRelative: string;
|
|
88
103
|
refsDir: string;
|
|
89
104
|
} {
|
|
90
105
|
const configPath = configOption
|
|
@@ -95,8 +110,17 @@ export function resolveMigrationPaths(
|
|
|
95
110
|
config.migrations?.dir ?? 'migrations',
|
|
96
111
|
);
|
|
97
112
|
const migrationsRelative = relative(process.cwd(), migrationsDir);
|
|
98
|
-
const
|
|
99
|
-
|
|
113
|
+
const appMigrationsDir = spaceMigrationDirectory(migrationsDir, APP_SPACE_ID);
|
|
114
|
+
const appMigrationsRelative = relative(process.cwd(), appMigrationsDir);
|
|
115
|
+
const refsDir = resolve(appMigrationsDir, 'refs');
|
|
116
|
+
return {
|
|
117
|
+
configPath,
|
|
118
|
+
migrationsDir,
|
|
119
|
+
migrationsRelative,
|
|
120
|
+
appMigrationsDir,
|
|
121
|
+
appMigrationsRelative,
|
|
122
|
+
refsDir,
|
|
123
|
+
};
|
|
100
124
|
}
|
|
101
125
|
|
|
102
126
|
/**
|
|
@@ -109,14 +133,45 @@ export interface PathDecisionResult {
|
|
|
109
133
|
readonly alternativeCount: number;
|
|
110
134
|
readonly tieBreakReasons: readonly string[];
|
|
111
135
|
readonly refName?: string;
|
|
136
|
+
readonly requiredInvariants: readonly string[];
|
|
137
|
+
readonly satisfiedInvariants: readonly string[];
|
|
112
138
|
readonly selectedPath: readonly {
|
|
113
139
|
readonly dirName: string;
|
|
114
|
-
readonly
|
|
140
|
+
readonly migrationHash: string;
|
|
115
141
|
readonly from: string;
|
|
116
142
|
readonly to: string;
|
|
143
|
+
readonly invariants: readonly string[];
|
|
117
144
|
}[];
|
|
118
145
|
}
|
|
119
146
|
|
|
147
|
+
export function collectDeclaredInvariants(graph: MigrationGraph): ReadonlySet<string> {
|
|
148
|
+
const declared = new Set<string>();
|
|
149
|
+
for (const edges of graph.forwardChain.values()) {
|
|
150
|
+
for (const edge of edges) {
|
|
151
|
+
for (const inv of edge.invariants) {
|
|
152
|
+
declared.add(inv);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return declared;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Maps a `MigrationEdge` to the structural-edge shape used in the
|
|
161
|
+
* `MIGRATION.NO_INVARIANT_PATH` error envelope. Shared between
|
|
162
|
+
* `migration apply` and `migration status` so both commands surface
|
|
163
|
+
* the same JSON wire shape when an invariant-aware route is unsatisfiable.
|
|
164
|
+
*/
|
|
165
|
+
export function toStructuralEdge(edge: MigrationEdge): NoInvariantPathStructuralEdge {
|
|
166
|
+
return {
|
|
167
|
+
dirName: edge.dirName,
|
|
168
|
+
migrationHash: edge.migrationHash,
|
|
169
|
+
from: edge.from,
|
|
170
|
+
to: edge.to,
|
|
171
|
+
invariants: edge.invariants,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
120
175
|
/**
|
|
121
176
|
* Maps a PathDecision to the slim CLI output representation.
|
|
122
177
|
*/
|
|
@@ -126,12 +181,15 @@ export function toPathDecisionResult(decision: PathDecision): PathDecisionResult
|
|
|
126
181
|
toHash: decision.toHash,
|
|
127
182
|
alternativeCount: decision.alternativeCount,
|
|
128
183
|
tieBreakReasons: decision.tieBreakReasons,
|
|
184
|
+
requiredInvariants: decision.requiredInvariants ?? [],
|
|
185
|
+
satisfiedInvariants: decision.satisfiedInvariants ?? [],
|
|
129
186
|
...ifDefined('refName', decision.refName),
|
|
130
187
|
selectedPath: decision.selectedPath.map((entry) => ({
|
|
131
188
|
dirName: entry.dirName,
|
|
132
|
-
|
|
189
|
+
migrationHash: entry.migrationHash,
|
|
133
190
|
from: entry.from,
|
|
134
191
|
to: entry.to,
|
|
192
|
+
invariants: entry.invariants,
|
|
135
193
|
})),
|
|
136
194
|
};
|
|
137
195
|
}
|
|
@@ -146,13 +204,13 @@ export function getTargetMigrations(target: ControlTargetDescriptor<string, stri
|
|
|
146
204
|
|
|
147
205
|
/**
|
|
148
206
|
* Reads the migrations directory and builds the migration graph from all
|
|
149
|
-
*
|
|
207
|
+
* packages. Throws on I/O or graph errors — callers handle error mapping.
|
|
150
208
|
*
|
|
151
|
-
* Every on-disk
|
|
209
|
+
* Every on-disk package is content-addressed (`migrationHash` is always a
|
|
152
210
|
* string); there is no draft state to filter out.
|
|
153
211
|
*/
|
|
154
|
-
export async function
|
|
155
|
-
bundles: readonly
|
|
212
|
+
export async function loadMigrationPackages(migrationsDir: string): Promise<{
|
|
213
|
+
bundles: readonly OnDiskMigrationPackage[];
|
|
156
214
|
graph: MigrationGraph;
|
|
157
215
|
}> {
|
|
158
216
|
const bundles = await readMigrationsDir(migrationsDir);
|
|
@@ -160,20 +218,6 @@ export async function loadMigrationBundles(migrationsDir: string): Promise<{
|
|
|
160
218
|
return { bundles, graph };
|
|
161
219
|
}
|
|
162
220
|
|
|
163
|
-
export interface MigrationBundleSet {
|
|
164
|
-
readonly bundles: readonly MigrationBundle[];
|
|
165
|
-
readonly graph: MigrationGraph;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Alias of `loadMigrationBundles` retained for naming-clarity in commands
|
|
170
|
-
* that previously needed both attested and draft splits. With the
|
|
171
|
-
* collapse of the draft state, both helpers do the same thing.
|
|
172
|
-
*/
|
|
173
|
-
export async function loadAllBundles(migrationsDir: string): Promise<MigrationBundleSet> {
|
|
174
|
-
return loadMigrationBundles(migrationsDir);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
221
|
/**
|
|
178
222
|
* The subset of the emitted contract.json that the framework layer can
|
|
179
223
|
* safely type. The emitter adds these fields on top of the family-specific
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
+
import type { ControlExtensionDescriptor } from '@prisma-next/framework-components/control';
|
|
3
|
+
import type {
|
|
4
|
+
ContractSpaceAggregate,
|
|
5
|
+
LoadAggregateError,
|
|
6
|
+
LoadAggregateInput,
|
|
7
|
+
LoadAggregateOutput,
|
|
8
|
+
} from '@prisma-next/migration-tools/aggregate';
|
|
9
|
+
import { loadContractSpaceAggregate } from '@prisma-next/migration-tools/aggregate';
|
|
10
|
+
import type { OnDiskMigrationPackage } from '@prisma-next/migration-tools/package';
|
|
11
|
+
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
12
|
+
import { CliStructuredError } from './cli-errors';
|
|
13
|
+
import { toDeclaredExtensionsFromRaw } from './extension-pack-inputs';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Render a {@link LoadAggregateError} into a CLI structured-error
|
|
17
|
+
* envelope. Preserves error codes `5001` (layout) and `5002` (marker /
|
|
18
|
+
* disjointness / etc.) so existing integration tests and downstream
|
|
19
|
+
* tooling continue to assert on the same `meta.violations[]` shape
|
|
20
|
+
* they did under the old precheck/marker-check helpers.
|
|
21
|
+
*/
|
|
22
|
+
export function mapLoadAggregateError(error: LoadAggregateError): CliStructuredError {
|
|
23
|
+
if (error.kind === 'layoutViolation') {
|
|
24
|
+
const lines = error.violations.map((v) => `- [${v.kind}] ${v.spaceId}`);
|
|
25
|
+
const summary =
|
|
26
|
+
error.violations.length === 1
|
|
27
|
+
? 'Contract-space layout violation detected'
|
|
28
|
+
: `Contract-space layout violations detected (${error.violations.length})`;
|
|
29
|
+
return new CliStructuredError('5001', summary, {
|
|
30
|
+
domain: 'MIG',
|
|
31
|
+
why: `The on-disk \`migrations/\` directory and your \`extensionPacks\` declaration are not in agreement.\n${lines.join('\n')}`,
|
|
32
|
+
fix: 'Run `prisma-next migrate` to materialise on-disk artefacts for declared extensions, or remove the orphan directory.',
|
|
33
|
+
docsUrl: 'https://pris.ly/contract-spaces',
|
|
34
|
+
meta: {
|
|
35
|
+
violations: error.violations.map((v) => ({
|
|
36
|
+
kind: v.kind,
|
|
37
|
+
spaceId: v.spaceId,
|
|
38
|
+
})),
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (error.kind === 'disjointnessViolation') {
|
|
43
|
+
return new CliStructuredError(
|
|
44
|
+
'5002',
|
|
45
|
+
`Contract-space disjointness violation: storage element "${error.element}" claimed by multiple spaces`,
|
|
46
|
+
{
|
|
47
|
+
domain: 'MIG',
|
|
48
|
+
why: `Spaces ${error.claimedBy.map((s) => `"${s}"`).join(', ')} all claim the storage element "${error.element}". Each storage element must be owned by exactly one contract space.`,
|
|
49
|
+
fix: 'Update the conflicting contracts so each storage element is claimed by exactly one space.',
|
|
50
|
+
docsUrl: 'https://pris.ly/contract-spaces',
|
|
51
|
+
meta: {
|
|
52
|
+
violations: [
|
|
53
|
+
{
|
|
54
|
+
kind: 'disjointness',
|
|
55
|
+
spaceId: error.claimedBy.join(','),
|
|
56
|
+
element: error.element,
|
|
57
|
+
claimedBy: error.claimedBy,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
if (error.kind === 'integrityFailure') {
|
|
65
|
+
return new CliStructuredError(
|
|
66
|
+
'5002',
|
|
67
|
+
`Contract-space integrity failure for "${error.spaceId}"`,
|
|
68
|
+
{
|
|
69
|
+
domain: 'MIG',
|
|
70
|
+
why: error.detail,
|
|
71
|
+
fix: 'Run `prisma-next migrate` to refresh on-disk artefacts, or restore the on-disk `migrations/` directory from version control.',
|
|
72
|
+
docsUrl: 'https://pris.ly/contract-spaces',
|
|
73
|
+
meta: {
|
|
74
|
+
violations: [{ kind: 'integrity', spaceId: error.spaceId, detail: error.detail }],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (error.kind === 'validationFailure') {
|
|
80
|
+
return new CliStructuredError(
|
|
81
|
+
'5002',
|
|
82
|
+
`Contract-space contract validation failed for "${error.spaceId}"`,
|
|
83
|
+
{
|
|
84
|
+
domain: 'MIG',
|
|
85
|
+
why: error.detail,
|
|
86
|
+
fix: 'Run `prisma-next migrate` to refresh on-disk artefacts, or fix the extension descriptor producing the invalid contract.',
|
|
87
|
+
meta: {
|
|
88
|
+
violations: [{ kind: 'validation', spaceId: error.spaceId, detail: error.detail }],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
// targetMismatch
|
|
94
|
+
return new CliStructuredError('5002', `Contract-space target mismatch for "${error.spaceId}"`, {
|
|
95
|
+
domain: 'MIG',
|
|
96
|
+
why: `Space "${error.spaceId}" targets "${error.actual}" but the project's adapter targets "${error.expected}".`,
|
|
97
|
+
fix: 'Update the extension descriptor to target the configured database, or change the project adapter.',
|
|
98
|
+
meta: {
|
|
99
|
+
violations: [
|
|
100
|
+
{
|
|
101
|
+
kind: 'targetMismatch',
|
|
102
|
+
spaceId: error.spaceId,
|
|
103
|
+
expected: error.expected,
|
|
104
|
+
actual: error.actual,
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Inputs needed to compose the aggregate loader at the CLI surface.
|
|
113
|
+
*
|
|
114
|
+
* Keeps the loader framework-neutral (no `Config` import) by accepting
|
|
115
|
+
* already-resolved structural inputs: validated app contract, target
|
|
116
|
+
* id, migrations root directory, and the set of extension descriptors.
|
|
117
|
+
*/
|
|
118
|
+
export interface BuildAggregateInputs<TFamilyId extends string, TTargetId extends string> {
|
|
119
|
+
readonly targetId: TTargetId;
|
|
120
|
+
readonly migrationsDir: string;
|
|
121
|
+
readonly appContract: Contract;
|
|
122
|
+
readonly extensionPacks: ReadonlyArray<ControlExtensionDescriptor<TFamilyId, TTargetId>>;
|
|
123
|
+
readonly validateContract: (contractJson: unknown) => Contract;
|
|
124
|
+
/**
|
|
125
|
+
* App-space migration packages to hydrate the app member's
|
|
126
|
+
* migration graph with. Defaults to `[]` (matches the `db init` /
|
|
127
|
+
* `db update` daily-driver behaviour, where the app's authored
|
|
128
|
+
* `migrations/` graph is not walked — the planner uses the synth
|
|
129
|
+
* strategy for the app member instead).
|
|
130
|
+
*
|
|
131
|
+
* `migration apply` callers thread the user's authored app-space
|
|
132
|
+
* packages (loaded via `loadMigrationPackages(appMigrationsDir)`)
|
|
133
|
+
* through here so the graph-walk strategy can plot a path through
|
|
134
|
+
* them — the prod-time replay path explicitly forbids synth.
|
|
135
|
+
*/
|
|
136
|
+
readonly appMigrationPackages?: ReadonlyArray<OnDiskMigrationPackage>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Run the aggregate loader at the CLI surface, mapping any
|
|
141
|
+
* {@link LoadAggregateError} into a {@link CliStructuredError} envelope.
|
|
142
|
+
*
|
|
143
|
+
* App-side migration packages flow through `inputs.appMigrationPackages`
|
|
144
|
+
* (defaulting to `[]`). `db init` / `db update` leave it empty: the
|
|
145
|
+
* planner's `synth` strategy is used for the app member (driven by
|
|
146
|
+
* `callerPolicy.ignoreGraphFor`), so the app's authored `migrations/`
|
|
147
|
+
* graph does not need to be walked. `migration apply` threads the
|
|
148
|
+
* already-loaded app-space packages through so the graph-walk strategy
|
|
149
|
+
* can plot a path through them — replay forbids synth.
|
|
150
|
+
*
|
|
151
|
+
* @see specs/contract-space-aggregate-spec.md § Loader.
|
|
152
|
+
*/
|
|
153
|
+
export async function buildContractSpaceAggregate<
|
|
154
|
+
TFamilyId extends string,
|
|
155
|
+
TTargetId extends string,
|
|
156
|
+
>(
|
|
157
|
+
inputs: BuildAggregateInputs<TFamilyId, TTargetId>,
|
|
158
|
+
): Promise<Result<ContractSpaceAggregate, CliStructuredError>> {
|
|
159
|
+
const declaredExtensions = toDeclaredExtensionsFromRaw(
|
|
160
|
+
inputs.extensionPacks as ReadonlyArray<unknown>,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const loadInput: LoadAggregateInput = {
|
|
164
|
+
targetId: inputs.targetId,
|
|
165
|
+
migrationsDir: inputs.migrationsDir,
|
|
166
|
+
appContract: inputs.appContract,
|
|
167
|
+
declaredExtensions,
|
|
168
|
+
validateContract: inputs.validateContract,
|
|
169
|
+
appMigrationPackages: inputs.appMigrationPackages ?? [],
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const result: LoadAggregateOutput = await loadContractSpaceAggregate(loadInput);
|
|
173
|
+
if (!result.ok) {
|
|
174
|
+
return notOk(mapLoadAggregateError(result.failure));
|
|
175
|
+
}
|
|
176
|
+
return ok(result.value.aggregate);
|
|
177
|
+
}
|