@prisma-next/cli 0.11.0 → 0.12.0
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 +13 -9
- package/dist/cli.mjs +259 -12
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-oXO2WCPD.mjs → client-KgJorIvG.mjs} +72 -60
- package/dist/client-KgJorIvG.mjs.map +1 -0
- package/dist/{command-helpers-BSb0tRC8.mjs → command-helpers-Bbw1GbwL.mjs} +646 -46
- package/dist/command-helpers-Bbw1GbwL.mjs.map +1 -0
- package/dist/commands/contract-emit.d.mts.map +1 -1
- package/dist/commands/contract-emit.mjs +1 -1
- package/dist/commands/contract-infer.d.mts.map +1 -1
- package/dist/commands/contract-infer.mjs +1 -1
- package/dist/commands/db-init.d.mts.map +1 -1
- package/dist/commands/db-init.mjs +32 -7
- 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 +3 -4
- 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 +12 -10
- 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 +41 -11
- 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 -1
- package/dist/commands/migrate.d.mts +6 -2
- package/dist/commands/migrate.d.mts.map +1 -1
- package/dist/commands/migrate.mjs +75 -40
- package/dist/commands/migrate.mjs.map +1 -1
- package/dist/commands/migration-check.d.mts +4 -3
- package/dist/commands/migration-check.d.mts.map +1 -1
- package/dist/commands/migration-check.mjs +1 -280
- package/dist/commands/migration-graph.d.mts +13 -2
- package/dist/commands/migration-graph.d.mts.map +1 -1
- package/dist/commands/migration-graph.mjs +2 -137
- package/dist/commands/migration-list.d.mts +64 -4
- package/dist/commands/migration-list.d.mts.map +1 -1
- package/dist/commands/migration-list.mjs +143 -56
- package/dist/commands/migration-list.mjs.map +1 -1
- package/dist/commands/migration-log.d.mts +10 -1
- package/dist/commands/migration-log.d.mts.map +1 -1
- package/dist/commands/migration-log.mjs +10 -15
- package/dist/commands/migration-log.mjs.map +1 -1
- package/dist/commands/migration-new.d.mts.map +1 -1
- package/dist/commands/migration-new.mjs +32 -38
- package/dist/commands/migration-new.mjs.map +1 -1
- package/dist/commands/migration-plan.d.mts +3 -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 +4 -55
- package/dist/commands/migration-show.d.mts.map +1 -1
- package/dist/commands/migration-show.mjs +61 -153
- package/dist/commands/migration-show.mjs.map +1 -1
- package/dist/commands/migration-status.d.mts +12 -49
- package/dist/commands/migration-status.d.mts.map +1 -1
- package/dist/commands/migration-status.mjs +85 -81
- package/dist/commands/migration-status.mjs.map +1 -1
- package/dist/commands/ref.d.mts +1 -1
- package/dist/commands/ref.d.mts.map +1 -1
- package/dist/commands/ref.mjs +38 -10
- package/dist/commands/ref.mjs.map +1 -1
- package/dist/config-loader-B6sJjXTv.mjs.map +1 -1
- package/dist/config-loader.d.mts.map +1 -1
- package/dist/contract-at-errors-BxP-TOMl.mjs +42 -0
- package/dist/contract-at-errors-BxP-TOMl.mjs.map +1 -0
- package/dist/{contract-emit-bcrpT-wD.mjs → contract-emit-D-4jrNve.mjs} +25 -10
- package/dist/contract-emit-D-4jrNve.mjs.map +1 -0
- package/dist/{contract-emit-r4y8Zhf1.mjs → contract-emit-DxcGl4Uq.mjs} +19 -14
- package/dist/contract-emit-DxcGl4Uq.mjs.map +1 -0
- package/dist/{contract-enrichment-Dani0mMW.mjs → contract-enrichment-a0V5Y_mL.mjs} +4 -25
- package/dist/contract-enrichment-a0V5Y_mL.mjs.map +1 -0
- package/dist/{contract-infer-BmySmqVT.mjs → contract-infer-D8uEbJuu.mjs} +4 -5
- package/dist/{contract-infer-BmySmqVT.mjs.map → contract-infer-D8uEbJuu.mjs.map} +1 -1
- package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs +247 -0
- package/dist/contract-space-aggregate-loader-DvZwdkrr.mjs.map +1 -0
- package/dist/{db-verify-BClPs3ph.mjs → db-verify-v_vUKXTU.mjs} +5 -7
- package/dist/{db-verify-BClPs3ph.mjs.map → db-verify-v_vUKXTU.mjs.map} +1 -1
- package/dist/exports/control-api.d.mts +3 -3
- package/dist/exports/control-api.d.mts.map +1 -1
- package/dist/exports/control-api.mjs +3 -3
- package/dist/exports/index.d.mts.map +1 -1
- package/dist/exports/index.mjs +1 -1
- package/dist/exports/index.mjs.map +1 -1
- package/dist/exports/init-output.d.mts.map +1 -1
- package/dist/exports/init-output.mjs +1 -1
- package/dist/extension-pack-inputs-IDvjRCi3.mjs +62 -0
- package/dist/extension-pack-inputs-IDvjRCi3.mjs.map +1 -0
- package/dist/{framework-components-65gOHkHB.mjs → framework-components-fYXjz_in.mjs} +2 -2
- package/dist/{framework-components-65gOHkHB.mjs.map → framework-components-fYXjz_in.mjs.map} +1 -1
- package/dist/global-flags-DEHjV8_s.d.mts +34 -0
- package/dist/global-flags-DEHjV8_s.d.mts.map +1 -0
- package/dist/{graph-render-DJVv0_uf.mjs → graph-render-rFAqZujX.mjs} +2 -2
- package/dist/{graph-render-DJVv0_uf.mjs.map → graph-render-rFAqZujX.mjs.map} +1 -1
- package/dist/{init-BCJZPWE1.mjs → init-Cv9UzWL5.mjs} +20 -269
- package/dist/init-Cv9UzWL5.mjs.map +1 -0
- package/dist/{inspect-live-schema-DSRbFoOL.mjs → inspect-live-schema-C6ohV_oQ.mjs} +4 -5
- package/dist/{inspect-live-schema-DSRbFoOL.mjs.map → inspect-live-schema-C6ohV_oQ.mjs.map} +1 -1
- package/dist/migration-check-BiBJoYYW.mjs +341 -0
- package/dist/migration-check-BiBJoYYW.mjs.map +1 -0
- package/dist/migration-cli.d.mts.map +1 -1
- package/dist/migration-cli.mjs +4 -4
- package/dist/migration-cli.mjs.map +1 -1
- package/dist/{migration-command-scaffold-Bzd9La5c.mjs → migration-command-scaffold-CjvwO6at.mjs} +4 -5
- package/dist/{migration-command-scaffold-Bzd9La5c.mjs.map → migration-command-scaffold-CjvwO6at.mjs.map} +1 -1
- package/dist/migration-graph-D7DVUElV.mjs +1232 -0
- package/dist/migration-graph-D7DVUElV.mjs.map +1 -0
- package/dist/migration-list-styler-BRwF4-gy.mjs +399 -0
- package/dist/migration-list-styler-BRwF4-gy.mjs.map +1 -0
- package/dist/{migration-plan-CFwqw3Gk.mjs → migration-plan-9DJ7q7_z.mjs} +372 -133
- package/dist/migration-plan-9DJ7q7_z.mjs.map +1 -0
- package/dist/{migration-types-BXWvz12q.d.mts → migration-types-D2FW63pr.d.mts} +1 -1
- package/dist/{migration-types-BXWvz12q.d.mts.map → migration-types-D2FW63pr.d.mts.map} +1 -1
- package/dist/{migrations-CwZMa1Ck.mjs → migrations-Cv2jxNNK.mjs} +12 -13
- package/dist/migrations-Cv2jxNNK.mjs.map +1 -0
- package/dist/{output-BlsrGMEF.mjs → output-B60Gw5fu.mjs} +1 -1
- package/dist/{output-BlsrGMEF.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
- package/dist/{progress-adapter-DFfvZcYL.mjs → progress-adapter-C644QK8l.mjs} +1 -1
- package/dist/{progress-adapter-DFfvZcYL.mjs.map → progress-adapter-C644QK8l.mjs.map} +1 -1
- package/dist/ref-advancement-DUZqsue6.mjs +50 -0
- package/dist/ref-advancement-DUZqsue6.mjs.map +1 -0
- package/dist/terminal-ui-5Y6mrg93.d.mts +133 -0
- package/dist/terminal-ui-5Y6mrg93.d.mts.map +1 -0
- package/dist/{types--CqjMdk0.d.mts → types-Dt_SfqFm.d.mts} +28 -28
- package/dist/types-Dt_SfqFm.d.mts.map +1 -0
- package/dist/{verify-Bom75OYI.mjs → verify-DCA9Sldu.mjs} +2 -2
- package/dist/{verify-Bom75OYI.mjs.map → verify-DCA9Sldu.mjs.map} +1 -1
- package/package.json +35 -24
- package/src/commands/contract-emit.ts +19 -7
- package/src/commands/contract-infer.ts +1 -1
- package/src/commands/db-init.ts +48 -2
- package/src/commands/db-sign.ts +9 -5
- package/src/commands/db-update.ts +54 -8
- package/src/commands/init/hygiene-gitattributes.ts +2 -2
- package/src/commands/init/index.ts +2 -1
- package/src/commands/init/templates/code-templates.ts +4 -2
- package/src/commands/init/templates/env.ts +13 -14
- package/src/commands/migrate.ts +125 -44
- package/src/commands/migration-check.ts +43 -83
- package/src/commands/migration-graph.ts +75 -60
- package/src/commands/migration-list.ts +220 -74
- package/src/commands/migration-log.ts +8 -14
- package/src/commands/migration-new.ts +44 -48
- package/src/commands/migration-plan.ts +412 -197
- package/src/commands/migration-show.ts +65 -284
- package/src/commands/migration-status.ts +127 -124
- package/src/commands/ref.ts +53 -8
- package/src/control-api/client.ts +0 -1
- package/src/control-api/contract-enrichment.ts +6 -42
- package/src/control-api/operations/{apply-aggregate.ts → apply.ts} +44 -75
- package/src/control-api/operations/contract-emit.ts +14 -6
- package/src/control-api/operations/{db-apply-aggregate.ts → db-apply.ts} +19 -19
- package/src/control-api/operations/db-init.ts +4 -4
- package/src/control-api/operations/db-update.ts +4 -4
- package/src/control-api/operations/db-verify.ts +15 -11
- package/src/control-api/operations/migration-apply.ts +56 -47
- package/src/control-api/types.ts +26 -27
- package/src/migration-cli.ts +4 -4
- package/src/utils/cli-errors.ts +234 -0
- package/src/utils/command-helpers.ts +9 -24
- package/src/utils/contract-at-errors.ts +96 -0
- package/src/utils/contract-space-aggregate-loader.ts +336 -117
- package/src/utils/formatters/migration-graph-layout.ts +1119 -0
- package/src/utils/formatters/migration-graph-rows.ts +336 -0
- package/src/utils/formatters/migration-graph-tree-render.ts +459 -0
- package/src/utils/formatters/migration-list-data-column.ts +115 -0
- package/src/utils/formatters/migration-list-graph-topology.ts +368 -0
- package/src/utils/formatters/migration-list-render.ts +191 -0
- package/src/utils/formatters/migration-list-styler.ts +63 -0
- package/src/utils/formatters/migration-list-types.ts +21 -0
- package/src/utils/formatters/migrations.ts +37 -46
- package/src/utils/glyph-mode.ts +22 -0
- package/src/utils/integrity-violation-to-check-failure.ts +130 -0
- package/src/utils/plan-resolution.ts +258 -0
- package/src/utils/ref-advancement.ts +68 -0
- package/src/utils/terminal-ui.ts +42 -1
- package/dist/cli-errors-Czmx92Zy.d.mts +0 -3
- package/dist/cli-errors-Djtz98Vm.mjs +0 -71
- package/dist/cli-errors-Djtz98Vm.mjs.map +0 -1
- package/dist/client-oXO2WCPD.mjs.map +0 -1
- package/dist/command-helpers-BSb0tRC8.mjs.map +0 -1
- package/dist/commands/migration-check.mjs.map +0 -1
- package/dist/commands/migration-graph.mjs.map +0 -1
- package/dist/contract-emit-bcrpT-wD.mjs.map +0 -1
- package/dist/contract-emit-r4y8Zhf1.mjs.map +0 -1
- package/dist/contract-enrichment-Dani0mMW.mjs.map +0 -1
- package/dist/contract-space-aggregate-loader-BmNQwlws.mjs +0 -160
- package/dist/contract-space-aggregate-loader-BmNQwlws.mjs.map +0 -1
- package/dist/global-flags-CdE7M0d9.d.mts +0 -15
- package/dist/global-flags-CdE7M0d9.d.mts.map +0 -1
- package/dist/init-BCJZPWE1.mjs.map +0 -1
- package/dist/migration-plan-CFwqw3Gk.mjs.map +0 -1
- package/dist/migrations-CwZMa1Ck.mjs.map +0 -1
- package/dist/rolldown-runtime-twds-ZHy.mjs +0 -14
- package/dist/terminal-ui-BiB_8KNo.mjs +0 -379
- package/dist/terminal-ui-BiB_8KNo.mjs.map +0 -1
- package/dist/types--CqjMdk0.d.mts.map +0 -1
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import type { Contract } from '@prisma-next/contract/types';
|
|
1
2
|
import {
|
|
2
3
|
createControlStack,
|
|
3
4
|
type MigrationPlanOperation,
|
|
4
5
|
} from '@prisma-next/framework-components/control';
|
|
5
6
|
import {
|
|
6
7
|
type ContractMarkerRecordLike,
|
|
8
|
+
type ContractSpaceAggregate,
|
|
7
9
|
graphWalkStrategy,
|
|
10
|
+
loadContractSpaceAggregate,
|
|
11
|
+
requireHeadRef,
|
|
8
12
|
} from '@prisma-next/migration-tools/aggregate';
|
|
9
13
|
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
10
14
|
import {
|
|
@@ -39,7 +43,6 @@ import {
|
|
|
39
43
|
import {
|
|
40
44
|
addGlobalOptions,
|
|
41
45
|
collectDeclaredInvariants,
|
|
42
|
-
loadMigrationPackages,
|
|
43
46
|
maskConnectionUrl,
|
|
44
47
|
readContractEnvelope,
|
|
45
48
|
resolveMigrationPaths,
|
|
@@ -50,9 +53,12 @@ import {
|
|
|
50
53
|
toStructuralEdge,
|
|
51
54
|
} from '../utils/command-helpers';
|
|
52
55
|
import {
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
appContractStandInFromIdentity,
|
|
57
|
+
loadContractRawSafely,
|
|
58
|
+
refuseContractSpaceIntegrity,
|
|
59
|
+
refusePackageCorruptionOnAggregate,
|
|
55
60
|
} from '../utils/contract-space-aggregate-loader';
|
|
61
|
+
import { toDeclaredExtensionsFromRaw } from '../utils/extension-pack-inputs';
|
|
56
62
|
import {
|
|
57
63
|
type EdgeStatus,
|
|
58
64
|
type EdgeStatusKind,
|
|
@@ -184,10 +190,9 @@ export interface MigrationStatusResult {
|
|
|
184
190
|
* migrations directory) where the existing diagnostics already
|
|
185
191
|
* surface the failure.
|
|
186
192
|
*
|
|
187
|
-
* The
|
|
188
|
-
* `
|
|
189
|
-
*
|
|
190
|
-
* detail for extension members lives only on this list.
|
|
193
|
+
* The top-level fields (`migrations`, `markerHash`, `targetHash`,
|
|
194
|
+
* `pathDecision`, …) describe the **app member** specifically.
|
|
195
|
+
* Per-space detail for extension members lives only on this list.
|
|
191
196
|
*/
|
|
192
197
|
readonly spaces?: readonly MigrationStatusSpaceEntry[];
|
|
193
198
|
/** Cross-space pending-migration total (sum of `spaces[].pendingCount`). Present when `spaces` is. */
|
|
@@ -424,55 +429,50 @@ function resolveDisplayChain(
|
|
|
424
429
|
/**
|
|
425
430
|
* Build the aggregate enumeration of contract spaces for the status
|
|
426
431
|
* output. Loads the aggregate from disk (lossy on failure — extension
|
|
427
|
-
* spaces are simply omitted, the
|
|
428
|
-
*
|
|
432
|
+
* spaces are simply omitted, the app member's output keeps working),
|
|
433
|
+
* reads per-space marker rows when online, and uses
|
|
429
434
|
* {@link graphWalkStrategy} to compute each space's pending count.
|
|
430
435
|
*
|
|
431
|
-
*
|
|
432
|
-
*
|
|
433
|
-
* cross-space totals.
|
|
436
|
+
* The aggregate-walking status reports per-space marker + pending
|
|
437
|
+
* state alongside the cross-space totals.
|
|
434
438
|
*/
|
|
435
439
|
export async function loadAggregateStatusSpaces(args: {
|
|
436
|
-
readonly
|
|
437
|
-
readonly
|
|
438
|
-
readonly appContractRaw: unknown;
|
|
439
|
-
readonly extensionPacks: BuildAggregateInputs<string, string>['extensionPacks'];
|
|
440
|
-
readonly deserializeContract: BuildAggregateInputs<string, string>['deserializeContract'];
|
|
440
|
+
readonly aggregate: ContractSpaceAggregate;
|
|
441
|
+
readonly extensionPacks: ReadonlyArray<unknown>;
|
|
441
442
|
readonly markersBySpace: ReadonlyMap<string, ContractMarkerRecordLike> | null;
|
|
442
443
|
}): Promise<readonly MigrationStatusSpaceEntry[]> {
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
// Loader failure (drift, layout violation, etc.) — surfacing it
|
|
454
|
-
// as a status diagnostic would duplicate `migration plan`'s job.
|
|
455
|
-
// The single-space app pipeline still runs; extensions are simply
|
|
456
|
-
// not enumerated.
|
|
444
|
+
const declaredExtensions = toDeclaredExtensionsFromRaw(args.extensionPacks);
|
|
445
|
+
if (
|
|
446
|
+
refuseContractSpaceIntegrity(args.aggregate, {
|
|
447
|
+
declaredExtensions,
|
|
448
|
+
checkContracts: true,
|
|
449
|
+
})
|
|
450
|
+
) {
|
|
451
|
+
// Full integrity refusal (drift, layout violation, etc.) — surfacing
|
|
452
|
+
// it as a status diagnostic would duplicate `migration plan`'s job.
|
|
453
|
+
// The app pipeline still runs; extensions are simply not enumerated.
|
|
457
454
|
return [];
|
|
458
455
|
}
|
|
459
|
-
const aggregate =
|
|
456
|
+
const aggregate = args.aggregate;
|
|
460
457
|
|
|
461
458
|
const orderedMembers = [...aggregate.extensions, aggregate.app];
|
|
462
459
|
const rows: MigrationStatusSpaceEntry[] = [];
|
|
463
460
|
for (const member of orderedMembers) {
|
|
464
461
|
const liveMarker = args.markersBySpace?.get(member.spaceId) ?? null;
|
|
465
462
|
const isApp = member.spaceId === aggregate.app.spaceId;
|
|
463
|
+
// The aggregate passed the integrity gate above, so every member has
|
|
464
|
+
// a resolved head ref (a missing one would have refused the load).
|
|
465
|
+
const headRef = requireHeadRef(member);
|
|
466
466
|
|
|
467
|
-
if (member.
|
|
467
|
+
if (member.graph().nodes.size === 0) {
|
|
468
468
|
rows.push({
|
|
469
469
|
spaceId: member.spaceId,
|
|
470
470
|
kind: isApp ? 'app' : 'extension',
|
|
471
|
-
headHash:
|
|
471
|
+
headHash: headRef.hash,
|
|
472
472
|
...(args.markersBySpace !== null
|
|
473
473
|
? {
|
|
474
474
|
markerHash: liveMarker?.storageHash ?? null,
|
|
475
|
-
status:
|
|
475
|
+
status: headRef.hash === EMPTY_CONTRACT_HASH ? 'up-to-date' : 'never-planned',
|
|
476
476
|
pendingCount: 0,
|
|
477
477
|
}
|
|
478
478
|
: {}),
|
|
@@ -484,7 +484,7 @@ export async function loadAggregateStatusSpaces(args: {
|
|
|
484
484
|
rows.push({
|
|
485
485
|
spaceId: member.spaceId,
|
|
486
486
|
kind: isApp ? 'app' : 'extension',
|
|
487
|
-
headHash:
|
|
487
|
+
headHash: headRef.hash,
|
|
488
488
|
});
|
|
489
489
|
continue;
|
|
490
490
|
}
|
|
@@ -513,7 +513,7 @@ export async function loadAggregateStatusSpaces(args: {
|
|
|
513
513
|
rows.push({
|
|
514
514
|
spaceId: member.spaceId,
|
|
515
515
|
kind: isApp ? 'app' : 'extension',
|
|
516
|
-
headHash:
|
|
516
|
+
headHash: headRef.hash,
|
|
517
517
|
markerHash: liveMarker?.storageHash ?? null,
|
|
518
518
|
pendingCount,
|
|
519
519
|
...(status ? { status } : {}),
|
|
@@ -528,17 +528,6 @@ export async function loadAggregateStatusSpaces(args: {
|
|
|
528
528
|
* the existing `readContractEnvelope` path will report the same
|
|
529
529
|
* problem via a status diagnostic, no need to double-surface.
|
|
530
530
|
*/
|
|
531
|
-
async function loadContractRawSafely(config: {
|
|
532
|
-
contract?: { output?: string };
|
|
533
|
-
}): Promise<unknown | null> {
|
|
534
|
-
try {
|
|
535
|
-
const path = (await import('../utils/command-helpers')).resolveContractPath(config);
|
|
536
|
-
const raw = await (await import('node:fs/promises')).readFile(path, 'utf-8');
|
|
537
|
-
return JSON.parse(raw) as unknown;
|
|
538
|
-
} catch {
|
|
539
|
-
return null;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
531
|
|
|
543
532
|
async function validateOnlineMarkerRead(
|
|
544
533
|
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
@@ -580,8 +569,10 @@ async function executeMigrationStatusCommand(
|
|
|
580
569
|
ui: TerminalUI,
|
|
581
570
|
): Promise<Result<MigrationStatusResult, CliStructuredError>> {
|
|
582
571
|
const config = await loadConfig(options.config);
|
|
583
|
-
const { configPath,
|
|
584
|
-
|
|
572
|
+
const { configPath, appMigrationsRelative, migrationsDir, refsDir } = resolveMigrationPaths(
|
|
573
|
+
options.config,
|
|
574
|
+
config,
|
|
575
|
+
);
|
|
585
576
|
|
|
586
577
|
const dbConnection = options.db ?? config.db?.connection;
|
|
587
578
|
const hasDriver = !!config.driver;
|
|
@@ -599,37 +590,92 @@ async function executeMigrationStatusCommand(
|
|
|
599
590
|
throw error;
|
|
600
591
|
}
|
|
601
592
|
|
|
602
|
-
|
|
593
|
+
const diagnostics: StatusDiagnostic[] = [];
|
|
594
|
+
let contractHash: string = EMPTY_CONTRACT_HASH;
|
|
595
|
+
try {
|
|
596
|
+
const envelope = await readContractEnvelope(config);
|
|
597
|
+
contractHash = envelope.storageHash;
|
|
598
|
+
} catch (error) {
|
|
599
|
+
diagnostics.push({
|
|
600
|
+
code: 'CONTRACT.UNREADABLE',
|
|
601
|
+
severity: 'warn',
|
|
602
|
+
message: `Could not read contract: ${error instanceof Error ? error.message : 'unknown error'}`,
|
|
603
|
+
hints: ["Run 'prisma-next contract emit' to generate a valid contract"],
|
|
604
|
+
});
|
|
605
|
+
}
|
|
603
606
|
|
|
604
|
-
|
|
607
|
+
const contractRawForAggregate = await loadContractRawSafely(config);
|
|
608
|
+
const stack = createControlStack(config);
|
|
609
|
+
const familyInstance = config.family.create(stack);
|
|
610
|
+
const deserializeContract = (json: unknown): Contract => familyInstance.deserializeContract(json);
|
|
611
|
+
const appContractStandIn = appContractStandInFromIdentity({
|
|
612
|
+
contractHash,
|
|
613
|
+
targetId: config.target.id,
|
|
614
|
+
targetFamily: config.target.familyId,
|
|
615
|
+
});
|
|
616
|
+
let appContractForLoad: Contract = appContractStandIn;
|
|
617
|
+
if (contractRawForAggregate !== null) {
|
|
605
618
|
try {
|
|
606
|
-
|
|
619
|
+
appContractForLoad = deserializeContract(contractRawForAggregate);
|
|
620
|
+
} catch (error) {
|
|
621
|
+
diagnostics.push({
|
|
622
|
+
code: 'CONTRACT.UNREADABLE',
|
|
623
|
+
severity: 'warn',
|
|
624
|
+
message: `Could not deserialize contract: ${error instanceof Error ? error.message : 'unknown error'}`,
|
|
625
|
+
hints: ["Run 'prisma-next contract emit' to generate a valid contract"],
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
607
629
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
630
|
+
let aggregate: ContractSpaceAggregate;
|
|
631
|
+
try {
|
|
632
|
+
aggregate = await loadContractSpaceAggregate({
|
|
633
|
+
migrationsDir,
|
|
634
|
+
deserializeContract,
|
|
635
|
+
appContract: appContractForLoad,
|
|
636
|
+
});
|
|
637
|
+
} catch (error) {
|
|
638
|
+
if (MigrationToolsError.is(error)) {
|
|
639
|
+
return notOk(mapMigrationToolsError(error));
|
|
640
|
+
}
|
|
641
|
+
return notOk(
|
|
642
|
+
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
643
|
+
why: `Failed to read migrations directory: ${error instanceof Error ? error.message : String(error)}`,
|
|
644
|
+
}),
|
|
645
|
+
);
|
|
646
|
+
}
|
|
620
647
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
648
|
+
if (contractRawForAggregate !== null) {
|
|
649
|
+
const corruptionFailure = refusePackageCorruptionOnAggregate(aggregate);
|
|
650
|
+
if (corruptionFailure) {
|
|
651
|
+
return notOk(corruptionFailure);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const appGraph = aggregate.app.graph();
|
|
656
|
+
|
|
657
|
+
let fromOverrideHash: string | undefined;
|
|
658
|
+
|
|
659
|
+
if (options.to || options.from) {
|
|
660
|
+
if (options.to) {
|
|
661
|
+
const refResult = parseContractRef(options.to, { graph: appGraph, refs: allRefs });
|
|
662
|
+
if (!refResult.ok) {
|
|
663
|
+
return notOk(mapRefResolutionError(refResult.failure));
|
|
627
664
|
}
|
|
628
|
-
|
|
629
|
-
if (
|
|
630
|
-
|
|
665
|
+
activeRefHash = refResult.value.hash;
|
|
666
|
+
if (refResult.value.provenance.kind === 'ref') {
|
|
667
|
+
const resolvedRefName = refResult.value.provenance.refName;
|
|
668
|
+
activeRefName = resolvedRefName;
|
|
669
|
+
activeRefEntry = allRefs[resolvedRefName];
|
|
631
670
|
}
|
|
632
|
-
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (options.from) {
|
|
674
|
+
const fromResult = parseContractRef(options.from, { graph: appGraph, refs: allRefs });
|
|
675
|
+
if (!fromResult.ok) {
|
|
676
|
+
return notOk(mapRefResolutionError(fromResult.failure));
|
|
677
|
+
}
|
|
678
|
+
fromOverrideHash = fromResult.value.hash;
|
|
633
679
|
}
|
|
634
680
|
}
|
|
635
681
|
|
|
@@ -670,34 +716,8 @@ async function executeMigrationStatusCommand(
|
|
|
670
716
|
ui.stderr(header);
|
|
671
717
|
}
|
|
672
718
|
|
|
673
|
-
const
|
|
674
|
-
|
|
675
|
-
try {
|
|
676
|
-
const envelope = await readContractEnvelope(config);
|
|
677
|
-
contractHash = envelope.storageHash;
|
|
678
|
-
} catch (error) {
|
|
679
|
-
diagnostics.push({
|
|
680
|
-
code: 'CONTRACT.UNREADABLE',
|
|
681
|
-
severity: 'warn',
|
|
682
|
-
message: `Could not read contract: ${error instanceof Error ? error.message : 'unknown error'}`,
|
|
683
|
-
hints: ["Run 'prisma-next contract emit' to generate a valid contract"],
|
|
684
|
-
});
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
let bundles: readonly OnDiskMigrationPackage[];
|
|
688
|
-
let graph: MigrationGraph;
|
|
689
|
-
try {
|
|
690
|
-
({ bundles, graph } = await loadMigrationPackages(appMigrationsDir));
|
|
691
|
-
} catch (error) {
|
|
692
|
-
if (MigrationToolsError.is(error)) {
|
|
693
|
-
return notOk(mapMigrationToolsError(error));
|
|
694
|
-
}
|
|
695
|
-
return notOk(
|
|
696
|
-
errorUnexpected(error instanceof Error ? error.message : String(error), {
|
|
697
|
-
why: `Failed to read migrations directory: ${error instanceof Error ? error.message : String(error)}`,
|
|
698
|
-
}),
|
|
699
|
-
);
|
|
700
|
-
}
|
|
719
|
+
const bundles = aggregate.app.packages;
|
|
720
|
+
const graph = appGraph;
|
|
701
721
|
|
|
702
722
|
if (bundles.length === 0) {
|
|
703
723
|
if (dbConnection && hasDriver) {
|
|
@@ -772,8 +792,8 @@ async function executeMigrationStatusCommand(
|
|
|
772
792
|
mode = 'online';
|
|
773
793
|
// Read every space's marker so the aggregate enumeration can
|
|
774
794
|
// surface per-space marker state. `readAllMarkers` mirrors what
|
|
775
|
-
// `db init` / `db update` already use to drive the
|
|
776
|
-
//
|
|
795
|
+
// `db init` / `db update` already use to drive the planner;
|
|
796
|
+
// here it powers the aggregate status output.
|
|
777
797
|
//
|
|
778
798
|
// Probe for the method first so we only swallow the
|
|
779
799
|
// unsupported-method case: older family instances may not
|
|
@@ -808,32 +828,15 @@ async function executeMigrationStatusCommand(
|
|
|
808
828
|
allMarkers = null;
|
|
809
829
|
}
|
|
810
830
|
|
|
811
|
-
// Build the aggregate enumeration of contract spaces. Lossy on
|
|
812
|
-
// failure (extensions are simply omitted) so the existing
|
|
813
|
-
// single-space app pipeline below still runs even if extensions
|
|
814
|
-
// can't be loaded — a strict failure here would degrade the
|
|
815
|
-
// load-bearing app-space output for unrelated reasons.
|
|
816
|
-
const contractRawForAggregate = await loadContractRawSafely(config);
|
|
817
831
|
let aggregateSpaces: readonly MigrationStatusSpaceEntry[] = [];
|
|
818
832
|
if (contractRawForAggregate !== null) {
|
|
819
|
-
// The aggregate loader needs a typed-Contract producer. Build a
|
|
820
|
-
// real control stack so `deserializeContract` runs against a fully
|
|
821
|
-
// composed family instance — descriptors that read stack members
|
|
822
|
-
// during construction (e.g. codec lookups) get a consistent view.
|
|
823
|
-
const stack = createControlStack(config);
|
|
824
|
-
const familyInstance = config.family.create(stack);
|
|
825
833
|
try {
|
|
826
834
|
aggregateSpaces = await loadAggregateStatusSpaces({
|
|
827
|
-
|
|
828
|
-
migrationsDir,
|
|
829
|
-
appContractRaw: contractRawForAggregate,
|
|
835
|
+
aggregate,
|
|
830
836
|
extensionPacks: config.extensionPacks ?? [],
|
|
831
|
-
deserializeContract: (json: unknown) => familyInstance.deserializeContract(json),
|
|
832
837
|
markersBySpace: allMarkers,
|
|
833
838
|
});
|
|
834
839
|
} catch {
|
|
835
|
-
// Loader failure short-circuits silently — the existing
|
|
836
|
-
// single-space app pipeline below still runs.
|
|
837
840
|
aggregateSpaces = [];
|
|
838
841
|
}
|
|
839
842
|
}
|
|
@@ -1252,7 +1255,7 @@ export function formatStatusSummary(result: MigrationStatusResult, colorize: boo
|
|
|
1252
1255
|
}
|
|
1253
1256
|
|
|
1254
1257
|
// Per-space section. Suppressed when there's no extension space —
|
|
1255
|
-
// the
|
|
1258
|
+
// the top-level output already covers the app member.
|
|
1256
1259
|
// When extensions exist, render every space (including the app)
|
|
1257
1260
|
// for consistency, plus a cross-space pending total + apply hint.
|
|
1258
1261
|
if (result.spaces?.some((s) => s.kind === 'extension')) {
|
package/src/commands/ref.ts
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
|
|
1
3
|
import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
|
|
4
|
+
import { findLatestMigration, isGraphNode } from '@prisma-next/migration-tools/migration-graph';
|
|
2
5
|
import { parseContractRef } from '@prisma-next/migration-tools/ref-resolution';
|
|
3
6
|
import type { RefEntry } from '@prisma-next/migration-tools/refs';
|
|
4
7
|
import {
|
|
5
|
-
|
|
8
|
+
deleteRefPaired,
|
|
6
9
|
readRefs,
|
|
7
10
|
validateRefName,
|
|
8
11
|
validateRefValue,
|
|
9
|
-
|
|
12
|
+
writeRefPaired,
|
|
10
13
|
} from '@prisma-next/migration-tools/refs';
|
|
11
14
|
import { notOk, ok, type Result } from '@prisma-next/utils/result';
|
|
12
15
|
import { Command } from 'commander';
|
|
16
|
+
import { join } from 'pathe';
|
|
13
17
|
import { loadConfig } from '../config-loader';
|
|
14
18
|
import {
|
|
15
19
|
CliStructuredError,
|
|
20
|
+
errorFileNotFound,
|
|
21
|
+
errorRefSetBundleNotFound,
|
|
22
|
+
errorRefSetEmptySentinel,
|
|
23
|
+
errorRefSetHashNotInGraph,
|
|
16
24
|
errorRuntime,
|
|
17
25
|
errorUnexpected,
|
|
18
26
|
mapMigrationToolsError,
|
|
@@ -20,12 +28,13 @@ import {
|
|
|
20
28
|
} from '../utils/cli-errors';
|
|
21
29
|
import {
|
|
22
30
|
addGlobalOptions,
|
|
23
|
-
loadMigrationPackages,
|
|
24
31
|
resolveMigrationPaths,
|
|
25
32
|
setCommandDescriptions,
|
|
26
33
|
} from '../utils/command-helpers';
|
|
34
|
+
import { buildReadAggregate } from '../utils/contract-space-aggregate-loader';
|
|
27
35
|
import { formatCommandHelp } from '../utils/formatters/help';
|
|
28
36
|
import { parseGlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
|
|
37
|
+
import { readContractIR } from '../utils/ref-advancement';
|
|
29
38
|
import { handleResult } from '../utils/result-handler';
|
|
30
39
|
import { createTerminalUI } from '../utils/terminal-ui';
|
|
31
40
|
|
|
@@ -72,14 +81,19 @@ export async function executeRefSetCommand(
|
|
|
72
81
|
|
|
73
82
|
try {
|
|
74
83
|
const config = await loadConfig(options.config);
|
|
75
|
-
const {
|
|
84
|
+
const { migrationsDir, refsDir } = resolveMigrationPaths(options.config, config);
|
|
85
|
+
const loaded = await buildReadAggregate(config, { migrationsDir });
|
|
86
|
+
if (!loaded.ok) {
|
|
87
|
+
return notOk(loaded.failure);
|
|
88
|
+
}
|
|
89
|
+
const graph = loaded.value.aggregate.app.graph();
|
|
90
|
+
const bundles = loaded.value.aggregate.app.packages;
|
|
91
|
+
const refs = loaded.value.aggregate.app.refs;
|
|
76
92
|
|
|
77
93
|
let resolvedHash: string;
|
|
78
94
|
if (validateRefValue(contractInput)) {
|
|
79
95
|
resolvedHash = contractInput;
|
|
80
96
|
} else {
|
|
81
|
-
const { graph } = await loadMigrationPackages(appMigrationsDir);
|
|
82
|
-
const refs = await readRefs(refsDir);
|
|
83
97
|
const refResult = parseContractRef(contractInput, { graph, refs });
|
|
84
98
|
if (!refResult.ok) {
|
|
85
99
|
return notOk(mapRefResolutionError(refResult.failure));
|
|
@@ -87,8 +101,39 @@ export async function executeRefSetCommand(
|
|
|
87
101
|
resolvedHash = refResult.value.hash;
|
|
88
102
|
}
|
|
89
103
|
|
|
104
|
+
if (resolvedHash === EMPTY_CONTRACT_HASH) {
|
|
105
|
+
return notOk(errorRefSetEmptySentinel(resolvedHash));
|
|
106
|
+
}
|
|
107
|
+
if (!isGraphNode(resolvedHash, graph)) {
|
|
108
|
+
const graphTip = findLatestMigration(graph)?.to ?? null;
|
|
109
|
+
return notOk(errorRefSetHashNotInGraph(resolvedHash, [...graph.nodes].sort(), graphTip));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const matchingBundle = bundles.find((bundle) => bundle.metadata.to === resolvedHash);
|
|
113
|
+
if (!matchingBundle) {
|
|
114
|
+
return notOk(errorRefSetBundleNotFound(resolvedHash));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const contractJsonPath = join(matchingBundle.dirPath, 'end-contract.json');
|
|
118
|
+
let contractJson: Record<string, unknown>;
|
|
119
|
+
try {
|
|
120
|
+
const raw = await readFile(contractJsonPath, 'utf-8');
|
|
121
|
+
contractJson = JSON.parse(raw) as Record<string, unknown>;
|
|
122
|
+
} catch (readError) {
|
|
123
|
+
if (readError instanceof Error && (readError as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
124
|
+
return notOk(
|
|
125
|
+
errorFileNotFound(contractJsonPath, {
|
|
126
|
+
why: `Migration bundle for hash ${resolvedHash} is missing its end-contract snapshot at ${contractJsonPath}`,
|
|
127
|
+
fix: 'Run `pnpm fixtures:check`, or re-emit the migration so its end-contract.json is restored.',
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
throw readError;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const contractIR = await readContractIR(contractJson, contractJsonPath);
|
|
90
135
|
const entry: RefEntry = { hash: resolvedHash, invariants: [] };
|
|
91
|
-
await
|
|
136
|
+
await writeRefPaired(refsDir, name, entry, contractIR);
|
|
92
137
|
return ok({ ok: true as const, ref: name, hash: resolvedHash, invariants: [] });
|
|
93
138
|
} catch (error) {
|
|
94
139
|
if (error instanceof CliStructuredError) return notOk(error);
|
|
@@ -103,7 +148,7 @@ export async function executeRefDeleteCommand(
|
|
|
103
148
|
try {
|
|
104
149
|
const config = await loadConfig(options.config);
|
|
105
150
|
const { refsDir } = resolveMigrationPaths(options.config, config);
|
|
106
|
-
await
|
|
151
|
+
await deleteRefPaired(refsDir, name);
|
|
107
152
|
return ok({ ok: true as const, ref: name, deleted: true as const });
|
|
108
153
|
} catch (error) {
|
|
109
154
|
if (error instanceof CliStructuredError) return notOk(error);
|
|
@@ -473,7 +473,6 @@ class ControlClientImpl implements ControlClient {
|
|
|
473
473
|
migrationsDir: options.migrationsDir,
|
|
474
474
|
extensionPacks: this.options.extensionPacks ?? [],
|
|
475
475
|
targetId: this.options.target.targetId,
|
|
476
|
-
appMigrationPackages: options.appMigrationPackages,
|
|
477
476
|
...ifDefined('refHash', options.refHash),
|
|
478
477
|
...ifDefined('refInvariants', options.refInvariants),
|
|
479
478
|
...ifDefined('refName', options.refName),
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Contract } from '@prisma-next/contract/types';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
type
|
|
2
|
+
import {
|
|
3
|
+
mergeCapabilityMatrices,
|
|
4
|
+
type TargetBoundComponentDescriptor,
|
|
5
|
+
} from '@prisma-next/framework-components/components';
|
|
5
6
|
|
|
6
7
|
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
7
8
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -26,37 +27,6 @@ function sortDeepTyped<T>(value: T): T {
|
|
|
26
27
|
return sortDeep(value) as T;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
function extractCapabilityMatrix(value: unknown): CapabilityMatrix {
|
|
30
|
-
if (!isPlainObject(value)) return {};
|
|
31
|
-
|
|
32
|
-
const out: CapabilityMatrix = {};
|
|
33
|
-
for (const [namespace, maybeCaps] of Object.entries(value)) {
|
|
34
|
-
if (!isPlainObject(maybeCaps)) continue;
|
|
35
|
-
const caps: Record<string, boolean> = {};
|
|
36
|
-
for (const [key, flag] of Object.entries(maybeCaps)) {
|
|
37
|
-
if (typeof flag === 'boolean') {
|
|
38
|
-
caps[key] = flag;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
if (Object.keys(caps).length > 0) {
|
|
42
|
-
out[namespace] = caps;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return out;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function mergeCapabilities(left: CapabilityMatrix, right: CapabilityMatrix): CapabilityMatrix {
|
|
50
|
-
const next: CapabilityMatrix = { ...left };
|
|
51
|
-
for (const [namespace, capabilities] of Object.entries(right)) {
|
|
52
|
-
next[namespace] = {
|
|
53
|
-
...(left[namespace] ?? {}),
|
|
54
|
-
...capabilities,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
return next;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
30
|
function extractExtensionPackMeta(
|
|
61
31
|
component: TargetBoundComponentDescriptor<string, string>,
|
|
62
32
|
): Record<string, unknown> {
|
|
@@ -93,16 +63,10 @@ export function enrichContract(
|
|
|
93
63
|
ir: Contract,
|
|
94
64
|
components: ReadonlyArray<TargetBoundComponentDescriptor<string, string>>,
|
|
95
65
|
): Contract {
|
|
96
|
-
|
|
97
|
-
const extensionPacksMeta: Record<string, unknown> = {};
|
|
66
|
+
const mergedCapabilities = mergeCapabilityMatrices(ir.capabilities, components);
|
|
98
67
|
|
|
68
|
+
const extensionPacksMeta: Record<string, unknown> = {};
|
|
99
69
|
for (const component of components) {
|
|
100
|
-
if (component.capabilities) {
|
|
101
|
-
mergedCapabilities = mergeCapabilities(
|
|
102
|
-
mergedCapabilities,
|
|
103
|
-
extractCapabilityMatrix(component.capabilities),
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
70
|
if (component.kind === 'extension') {
|
|
107
71
|
extensionPacksMeta[component.id] = extractExtensionPackMeta(component);
|
|
108
72
|
}
|