@prisma-next/cli 0.5.0-dev.36 → 0.5.0-dev.37

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.
Files changed (48) hide show
  1. package/dist/cli.mjs +4 -4
  2. package/dist/{client-Buy8_40Y.mjs → client-1JqqkiC7.mjs} +3 -2
  3. package/dist/client-1JqqkiC7.mjs.map +1 -0
  4. package/dist/commands/contract-emit.mjs +1 -1
  5. package/dist/commands/contract-infer.mjs +1 -1
  6. package/dist/commands/db-init.mjs +3 -3
  7. package/dist/commands/db-schema.mjs +2 -2
  8. package/dist/commands/db-sign.mjs +2 -2
  9. package/dist/commands/db-update.mjs +3 -3
  10. package/dist/commands/db-verify.mjs +2 -2
  11. package/dist/commands/migration-apply.d.mts +3 -0
  12. package/dist/commands/migration-apply.d.mts.map +1 -1
  13. package/dist/commands/migration-apply.mjs +46 -23
  14. package/dist/commands/migration-apply.mjs.map +1 -1
  15. package/dist/commands/migration-new.mjs +3 -3
  16. package/dist/commands/migration-new.mjs.map +1 -1
  17. package/dist/commands/migration-plan.mjs +1 -1
  18. package/dist/commands/migration-ref.mjs +1 -1
  19. package/dist/commands/migration-show.mjs +2 -2
  20. package/dist/commands/migration-status.d.mts +18 -1
  21. package/dist/commands/migration-status.d.mts.map +1 -1
  22. package/dist/commands/migration-status.mjs +2 -2
  23. package/dist/{contract-emit-DWtGQYCD.mjs → contract-emit-rt_Nmdwq.mjs} +2 -2
  24. package/dist/{contract-emit-DWtGQYCD.mjs.map → contract-emit-rt_Nmdwq.mjs.map} +1 -1
  25. package/dist/{contract-infer-DmLiksNp.mjs → contract-infer-Cf5J2wVg.mjs} +3 -3
  26. package/dist/{contract-infer-DmLiksNp.mjs.map → contract-infer-Cf5J2wVg.mjs.map} +1 -1
  27. package/dist/exports/control-api.d.mts +8 -0
  28. package/dist/exports/control-api.d.mts.map +1 -1
  29. package/dist/exports/control-api.mjs +1 -1
  30. package/dist/exports/index.mjs +1 -1
  31. package/dist/{inspect-live-schema-6nsKS6m5.mjs → inspect-live-schema-LWtXfxm_.mjs} +3 -3
  32. package/dist/{inspect-live-schema-6nsKS6m5.mjs.map → inspect-live-schema-LWtXfxm_.mjs.map} +1 -1
  33. package/dist/{migration-command-scaffold-Ck1_lcsf.mjs → migration-command-scaffold-CU452v9h.mjs} +3 -3
  34. package/dist/{migration-command-scaffold-Ck1_lcsf.mjs.map → migration-command-scaffold-CU452v9h.mjs.map} +1 -1
  35. package/dist/{migration-status-DYJIRnK3.mjs → migration-status-DoPrFIOQ.mjs} +103 -34
  36. package/dist/migration-status-DoPrFIOQ.mjs.map +1 -0
  37. package/dist/{result-handler-BmVh8AeV.mjs → result-handler-Ch6hVnOo.mjs} +26 -3
  38. package/dist/{result-handler-BmVh8AeV.mjs.map → result-handler-Ch6hVnOo.mjs.map} +1 -1
  39. package/package.json +14 -14
  40. package/src/commands/migration-apply.ts +72 -16
  41. package/src/commands/migration-new.ts +2 -2
  42. package/src/commands/migration-status.ts +171 -23
  43. package/src/control-api/operations/migration-apply.ts +1 -0
  44. package/src/control-api/types.ts +8 -0
  45. package/src/utils/command-helpers.ts +36 -1
  46. package/src/utils/formatters/graph-migration-mapper.ts +5 -1
  47. package/dist/client-Buy8_40Y.mjs.map +0 -1
  48. package/dist/migration-status-DYJIRnK3.mjs.map +0 -1
@@ -1,6 +1,10 @@
1
1
  import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';
2
2
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
3
- import { MigrationToolsError } from '@prisma-next/migration-tools/errors';
3
+ import {
4
+ errorNoInvariantPath,
5
+ errorUnknownInvariant,
6
+ MigrationToolsError,
7
+ } from '@prisma-next/migration-tools/errors';
4
8
  import type { MigrationEdge, MigrationGraph } from '@prisma-next/migration-tools/graph';
5
9
  import {
6
10
  findPath,
@@ -8,7 +12,7 @@ import {
8
12
  findReachableLeaves,
9
13
  } from '@prisma-next/migration-tools/migration-graph';
10
14
  import type { MigrationPackage } from '@prisma-next/migration-tools/package';
11
- import type { Refs } from '@prisma-next/migration-tools/refs';
15
+ import type { RefEntry, Refs } from '@prisma-next/migration-tools/refs';
12
16
  import { readRefs, resolveRef } from '@prisma-next/migration-tools/refs';
13
17
  import { ifDefined } from '@prisma-next/utils/defined';
14
18
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
@@ -25,6 +29,7 @@ import {
25
29
  } from '../utils/cli-errors';
26
30
  import {
27
31
  addGlobalOptions,
32
+ collectDeclaredInvariants,
28
33
  loadMigrationPackages,
29
34
  maskConnectionUrl,
30
35
  readContractEnvelope,
@@ -32,6 +37,7 @@ import {
32
37
  setCommandDescriptions,
33
38
  setCommandExamples,
34
39
  toPathDecisionResult,
40
+ toStructuralEdge,
35
41
  } from '../utils/command-helpers';
36
42
  import {
37
43
  type EdgeStatus,
@@ -80,17 +86,33 @@ export interface MigrationStatusResult {
80
86
  readonly targetHash: string;
81
87
  readonly contractHash: string;
82
88
  readonly refs?: readonly StatusRef[];
89
+ /** Required invariants from the active ref, sorted ascending. Always present (`[]` when no `--ref` or the ref declares none) — knowable offline. */
90
+ readonly requiredInvariants: readonly string[];
91
+ /**
92
+ * Invariants the marker has applied at least once, intersected with
93
+ * `requiredInvariants` for display relevance. JSON consumers see only the
94
+ * subset overlapping the active ref's required set — the full unfiltered
95
+ * marker invariant list lives on `marker.invariants` (control plane) and
96
+ * is not surfaced here. Present only in `mode === 'online'`; absent when
97
+ * offline (the marker is unknown, not empty).
98
+ */
99
+ readonly appliedInvariants?: readonly string[];
100
+ /** required − applied. Present only in `mode === 'online'`; absent when offline. */
101
+ readonly missingInvariants?: readonly string[];
83
102
  readonly pathDecision?: {
84
103
  readonly fromHash: string;
85
104
  readonly toHash: string;
86
105
  readonly alternativeCount: number;
87
106
  readonly tieBreakReasons: readonly string[];
88
107
  readonly refName?: string;
108
+ readonly requiredInvariants: readonly string[];
109
+ readonly satisfiedInvariants: readonly string[];
89
110
  readonly selectedPath: readonly {
90
111
  readonly dirName: string;
91
112
  readonly migrationHash: string;
92
113
  readonly from: string;
93
114
  readonly to: string;
115
+ readonly invariants: readonly string[];
94
116
  }[];
95
117
  };
96
118
  readonly summary: string;
@@ -357,6 +379,7 @@ async function executeMigrationStatusCommand(
357
379
 
358
380
  let activeRefName: string | undefined;
359
381
  let activeRefHash: string | undefined;
382
+ let activeRefEntry: RefEntry | undefined;
360
383
  let allRefs: Refs = {};
361
384
  try {
362
385
  allRefs = await readRefs(refsDir);
@@ -370,7 +393,8 @@ async function executeMigrationStatusCommand(
370
393
  if (options.ref) {
371
394
  activeRefName = options.ref;
372
395
  try {
373
- activeRefHash = resolveRef(allRefs, activeRefName).hash;
396
+ activeRefEntry = resolveRef(allRefs, activeRefName);
397
+ activeRefHash = activeRefEntry.hash;
374
398
  } catch (error) {
375
399
  if (MigrationToolsError.is(error)) {
376
400
  return notOk(mapMigrationToolsError(error));
@@ -379,6 +403,8 @@ async function executeMigrationStatusCommand(
379
403
  }
380
404
  }
381
405
 
406
+ const requiredInvariants: readonly string[] = [...(activeRefEntry?.invariants ?? [])].sort();
407
+
382
408
  const statusRefs: StatusRef[] = Object.entries(allRefs).map(([name, entry]) => ({
383
409
  name,
384
410
  hash: entry.hash,
@@ -396,6 +422,12 @@ async function executeMigrationStatusCommand(
396
422
  if (activeRefName) {
397
423
  details.push({ label: 'ref', value: activeRefName });
398
424
  }
425
+ if (activeRefEntry && activeRefEntry.invariants.length > 0) {
426
+ details.push({
427
+ label: 'required',
428
+ value: formatInvariantList(activeRefEntry.invariants),
429
+ });
430
+ }
399
431
  const header = formatStyledHeader({
400
432
  command: 'migration status',
401
433
  description: 'Show migration history and applied status',
@@ -453,6 +485,7 @@ async function executeMigrationStatusCommand(
453
485
  contractHash,
454
486
  summary: 'No migrations found',
455
487
  diagnostics,
488
+ requiredInvariants,
456
489
  });
457
490
  }
458
491
 
@@ -480,6 +513,7 @@ async function executeMigrationStatusCommand(
480
513
  }
481
514
 
482
515
  let markerHash: string | undefined;
516
+ let markerInvariants: readonly string[] = [];
483
517
  let mode: 'online' | 'offline' = 'offline';
484
518
 
485
519
  if (dbConnection && hasDriver) {
@@ -492,7 +526,9 @@ async function executeMigrationStatusCommand(
492
526
  });
493
527
  try {
494
528
  await client.connect(dbConnection);
495
- markerHash = (await client.readMarker())?.storageHash;
529
+ const marker = await client.readMarker();
530
+ markerHash = marker?.storageHash;
531
+ markerInvariants = marker?.invariants ?? [];
496
532
  mode = 'online';
497
533
  } catch {
498
534
  if (!flags.json && !flags.quiet) {
@@ -503,6 +539,32 @@ async function executeMigrationStatusCommand(
503
539
  }
504
540
  }
505
541
 
542
+ // Pre-check unknown invariants. Online: union the graph's declared
543
+ // invariants with the marker's recorded set so a retired-but-applied
544
+ // invariant doesn't surface as MIGRATION.UNKNOWN_INVARIANT — apply would
545
+ // route fine because marker subtraction empties `effectiveRequired`.
546
+ // Offline: keep the check graph-strict (the marker is unknown, and a
547
+ // missing declarer is the dominant signal we can offer).
548
+ if (activeRefEntry && activeRefEntry.invariants.length > 0) {
549
+ const declared = collectDeclaredInvariants(graph);
550
+ const known = new Set<string>(declared);
551
+ if (mode === 'online') {
552
+ for (const id of markerInvariants) known.add(id);
553
+ }
554
+ const unknown = activeRefEntry.invariants.filter((id) => !known.has(id));
555
+ if (unknown.length > 0) {
556
+ return notOk(
557
+ mapMigrationToolsError(
558
+ errorUnknownInvariant({
559
+ ...ifDefined('refName', activeRefName),
560
+ unknown,
561
+ declared: [...declared].sort(),
562
+ }),
563
+ ),
564
+ );
565
+ }
566
+ }
567
+
506
568
  // Marker exists but is not in the migration graph and doesn't match the
507
569
  // contract hash. The DB is at an unknown state relative to the graph.
508
570
  // Bail out early with a clear diagnostic instead of rendering a confusing
@@ -548,6 +610,7 @@ async function executeMigrationStatusCommand(
548
610
  summary: `${bundles.length} migration(s) on disk`,
549
611
  diagnostics,
550
612
  markerHash,
613
+ requiredInvariants,
551
614
  ...(statusRefs.length > 0 ? { refs: statusRefs } : {}),
552
615
  });
553
616
  }
@@ -588,6 +651,7 @@ async function executeMigrationStatusCommand(
588
651
  summary: `${bundles.length} migration(s) on disk`,
589
652
  diagnostics,
590
653
  ...ifDefined('markerHash', markerHash),
654
+ requiredInvariants,
591
655
  ...(statusRefs.length > 0 ? { refs: statusRefs } : {}),
592
656
  graph,
593
657
  bundles,
@@ -612,14 +676,39 @@ async function executeMigrationStatusCommand(
612
676
  const pendingCount = edgeStatuses.filter((e) => e.status === 'pending').length;
613
677
  const appliedCount = edgeStatuses.filter((e) => e.status === 'applied').length;
614
678
 
679
+ let appliedInvariants: readonly string[] | undefined;
680
+ let missingInvariants: readonly string[] | undefined;
681
+ let effectiveRequired = new Set<string>();
682
+ if (mode === 'online') {
683
+ // Mirrors `migration-apply.ts`: compute `effectiveRequired = required −
684
+ // marker.invariants` directly, then derive the display fields from it.
685
+ // `appliedInvariants` is the intersection (`required ∩ marker`), which
686
+ // is what JSON consumers see for the active ref; the unfiltered set
687
+ // lives on `marker.invariants`.
688
+ const markerSet = new Set(markerInvariants);
689
+ effectiveRequired = new Set(requiredInvariants.filter((id) => !markerSet.has(id)));
690
+ appliedInvariants = requiredInvariants.filter((id) => markerSet.has(id));
691
+ missingInvariants = [...effectiveRequired].sort();
692
+ }
693
+
694
+ // The marker can match the structural target while still missing required
695
+ // invariants — for example, a self-edge that provides X, applied via a ref
696
+ // declaring X. `pendingCount` (structural) says zero in that case but
697
+ // `effectiveRequired` is non-empty, so up-to-date messaging would mislead.
698
+ const hasInvariantWork = effectiveRequired.size > 0;
699
+ const missingList = [...effectiveRequired].sort().join(', ');
700
+
615
701
  let summary: string;
616
702
  if (mode === 'online') {
617
703
  if (markerHash !== undefined && !graph.nodes.has(markerHash) && markerHash === contractHash) {
618
704
  summary = `${bundles.length} migration(s) on disk`;
619
705
  } else if (activeRefHash && markerHash !== undefined) {
620
- summary = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName!);
621
- } else if (pendingCount === 0) {
706
+ const distance = summarizeRefDistance(graph, markerHash, activeRefHash, activeRefName!);
707
+ summary = hasInvariantWork ? `${distance} missing invariant(s): ${missingList}` : distance;
708
+ } else if (pendingCount === 0 && !hasInvariantWork) {
622
709
  summary = `Database is up to date (${appliedCount} migration${appliedCount !== 1 ? 's' : ''} applied)`;
710
+ } else if (pendingCount === 0 && hasInvariantWork) {
711
+ summary = `Missing invariant(s): ${missingList} — run 'prisma-next migration apply --ref ${activeRefName ?? '<ref>'}' to apply`;
623
712
  } else if (markerHash === undefined) {
624
713
  summary = `${pendingCount} pending migration(s) — database has no marker`;
625
714
  } else {
@@ -629,6 +718,37 @@ async function executeMigrationStatusCommand(
629
718
  summary = `${entries.length} migration(s) on disk`;
630
719
  }
631
720
 
721
+ let pathDecision: MigrationStatusResult['pathDecision'];
722
+ let routingUnreachable = false;
723
+ if (mode === 'online') {
724
+ const originHash = markerHash ?? EMPTY_CONTRACT_HASH;
725
+ const outcome = findPathWithDecision(graph, originHash, targetHash, {
726
+ ...ifDefined('refName', activeRefName),
727
+ required: effectiveRequired,
728
+ });
729
+ if (outcome.kind === 'ok') {
730
+ pathDecision = toPathDecisionResult(outcome.decision);
731
+ } else if (outcome.kind === 'unsatisfiable') {
732
+ return notOk(
733
+ mapMigrationToolsError(
734
+ errorNoInvariantPath({
735
+ ...ifDefined('refName', activeRefName),
736
+ required: [...effectiveRequired].sort(),
737
+ missing: outcome.missing,
738
+ structuralPath: outcome.structuralPath.map(toStructuralEdge),
739
+ }),
740
+ ),
741
+ );
742
+ } else {
743
+ // outcome.kind === 'unreachable' — origin (marker) has no structural
744
+ // path to the active target. `pendingCount` and `hasInvariantWork`
745
+ // both report zero in this case, but emitting MIGRATION.UP_TO_DATE
746
+ // would be wrong: the database simply cannot reach the requested
747
+ // ref/contract from its current state. Suppress UP_TO_DATE below.
748
+ routingUnreachable = true;
749
+ }
750
+ }
751
+
632
752
  if (mode === 'online') {
633
753
  if (markerHash !== undefined && !graph.nodes.has(markerHash) && markerHash === contractHash) {
634
754
  diagnostics.push({
@@ -645,7 +765,16 @@ async function executeMigrationStatusCommand(
645
765
  message: `${pendingCount} migration(s) pending`,
646
766
  hints: ["Run 'prisma-next migration apply' to apply pending migrations"],
647
767
  });
648
- } else {
768
+ } else if (hasInvariantWork) {
769
+ diagnostics.push({
770
+ code: 'MIGRATION.INVARIANTS_PENDING',
771
+ severity: 'info',
772
+ message: `Missing required invariant(s): ${missingList}`,
773
+ hints: [
774
+ `Run 'prisma-next migration apply --ref ${activeRefName ?? '<ref>'}' to apply a path that covers the required invariants`,
775
+ ],
776
+ });
777
+ } else if (!routingUnreachable) {
649
778
  diagnostics.push({
650
779
  code: 'MIGRATION.UP_TO_DATE',
651
780
  severity: 'info',
@@ -655,14 +784,6 @@ async function executeMigrationStatusCommand(
655
784
  }
656
785
  }
657
786
 
658
- let pathDecision: MigrationStatusResult['pathDecision'];
659
- if (mode === 'online' && markerHash !== undefined) {
660
- const decision = findPathWithDecision(graph, markerHash, targetHash, activeRefName);
661
- if (decision) {
662
- pathDecision = toPathDecisionResult(decision);
663
- }
664
- }
665
-
666
787
  const result: MigrationStatusResult = {
667
788
  ok: true,
668
789
  mode,
@@ -672,6 +793,9 @@ async function executeMigrationStatusCommand(
672
793
  summary,
673
794
  diagnostics,
674
795
  ...ifDefined('markerHash', markerHash),
796
+ requiredInvariants,
797
+ ...ifDefined('appliedInvariants', appliedInvariants),
798
+ ...ifDefined('missingInvariants', missingInvariants),
675
799
  ...(statusRefs.length > 0 ? { refs: statusRefs } : {}),
676
800
  ...ifDefined('pathDecision', pathDecision),
677
801
  graph,
@@ -712,13 +836,19 @@ export function createMigrationStatusCommand(): Command {
712
836
 
713
837
  const exitCode = handleResult(result, flags, ui, (statusResult) => {
714
838
  if (flags.json) {
839
+ // Strip non-JSON-shape fields before emitting. These belong to
840
+ // the in-memory result so the human renderer can avoid
841
+ // recomputing them, but they would either bloat the wire format
842
+ // (graph, bundles, edgeStatuses) or expose internals
843
+ // (activeRefHash, activeRefName, diverged) that consumers should
844
+ // read off `pathDecision` / `refs` instead.
715
845
  const {
716
- graph: _g,
717
- bundles: _b,
718
- edgeStatuses: _es,
719
- activeRefHash: _arh,
720
- activeRefName: _arn,
721
- diverged: _d,
846
+ graph: _graph,
847
+ bundles: _bundles,
848
+ edgeStatuses: _edgeStatuses,
849
+ activeRefHash: _activeRefHash,
850
+ activeRefName: _activeRefName,
851
+ diverged: _diverged,
722
852
  ...jsonResult
723
853
  } = statusResult;
724
854
  ui.output(JSON.stringify(jsonResult, null, 2));
@@ -777,7 +907,7 @@ function formatLegend(colorize: boolean): string {
777
907
  return c(dim, parts.join(' '));
778
908
  }
779
909
 
780
- function formatStatusSummary(result: MigrationStatusResult, colorize: boolean): string {
910
+ export function formatStatusSummary(result: MigrationStatusResult, colorize: boolean): string {
781
911
  const c = (fn: (s: string) => string, s: string) => (colorize ? fn(s) : s);
782
912
  const lines: string[] = [];
783
913
 
@@ -785,11 +915,16 @@ function formatStatusSummary(result: MigrationStatusResult, colorize: boolean):
785
915
  const pendingCount = result.migrations.filter((e) => e.status === 'pending').length;
786
916
 
787
917
  const hasWarnings = result.diagnostics?.some((d) => d.severity === 'warn') ?? false;
918
+ // INVARIANTS_PENDING is filed at severity 'info' (per ADR 208) so the
919
+ // warn-severity check above doesn't see it. It still represents pending
920
+ // work, so it must promote the summary off the success icon.
921
+ const hasInvariantPending =
922
+ result.diagnostics?.some((d) => d.code === 'MIGRATION.INVARIANTS_PENDING') ?? false;
788
923
 
789
924
  if (result.mode === 'online') {
790
925
  if (hasUnknown || hasWarnings) {
791
926
  lines.push(`${c(yellow, '⚠')} ${result.summary}`);
792
- } else if (pendingCount === 0) {
927
+ } else if (pendingCount === 0 && !hasInvariantPending) {
793
928
  lines.push(`${c(cyan, '✔')} ${result.summary}`);
794
929
  } else {
795
930
  lines.push(`${c(yellow, '⧗')} ${result.summary}`);
@@ -798,6 +933,15 @@ function formatStatusSummary(result: MigrationStatusResult, colorize: boolean):
798
933
  lines.push(result.summary);
799
934
  }
800
935
 
936
+ if (result.requiredInvariants.length > 0) {
937
+ if (result.appliedInvariants !== undefined && result.missingInvariants !== undefined) {
938
+ lines.push(`${c(dim, 'applied ')}${formatInvariantList(result.appliedInvariants)}`);
939
+ lines.push(`${c(dim, 'missing ')}${formatInvariantList(result.missingInvariants)}`);
940
+ } else {
941
+ lines.push(`${c(dim, 'applied ')}(unknown — connect a database to evaluate)`);
942
+ }
943
+ }
944
+
801
945
  const warnings = result.diagnostics?.filter((d) => d.severity === 'warn') ?? [];
802
946
  for (const diag of warnings) {
803
947
  lines.push(`${c(yellow, '⚠')} ${diag.message}`);
@@ -809,6 +953,10 @@ function formatStatusSummary(result: MigrationStatusResult, colorize: boolean):
809
953
  return lines.join('\n');
810
954
  }
811
955
 
956
+ function formatInvariantList(ids: readonly string[]): string {
957
+ return ids.length === 0 ? '(none)' : ids.join(', ');
958
+ }
959
+
812
960
  function summarizeRefDistance(
813
961
  graph: MigrationGraph,
814
962
  markerHash: string,
@@ -147,6 +147,7 @@ export async function executeMigrationApply<TFamilyId extends string, TTargetId
147
147
  origin: migration.from === null ? null : { storageHash: migration.from },
148
148
  destination: { storageHash: migration.to },
149
149
  operations,
150
+ providedInvariants: migration.providedInvariants,
150
151
  };
151
152
 
152
153
  const destinationContract = familyInstance.validateContract(migration.toContract);
@@ -452,6 +452,14 @@ export interface MigrationApplyStep {
452
452
  readonly to: string;
453
453
  readonly toContract: Contract;
454
454
  readonly operations: readonly MigrationPlanOperation[];
455
+ /**
456
+ * Sorted, deduplicated invariant ids from `migration.json.providedInvariants`.
457
+ * Verified at load time by `readMigrationPackage` (manifest copy must equal
458
+ * the value derived from `ops.json`). The control-api passes this through
459
+ * to the runner via `MigrationPlan.providedInvariants` so target runners
460
+ * read the canonical set instead of re-deriving from `operations`.
461
+ */
462
+ readonly providedInvariants: readonly string[];
455
463
  }
456
464
 
457
465
  /**
@@ -1,7 +1,8 @@
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 { MigrationGraph } from '@prisma-next/migration-tools/graph';
4
+ import type { NoInvariantPathStructuralEdge } from '@prisma-next/migration-tools/errors';
5
+ import type { MigrationEdge, MigrationGraph } from '@prisma-next/migration-tools/graph';
5
6
  import { readMigrationsDir } from '@prisma-next/migration-tools/io';
6
7
  import type { PathDecision } from '@prisma-next/migration-tools/migration-graph';
7
8
  import { reconstructGraph } from '@prisma-next/migration-tools/migration-graph';
@@ -110,14 +111,45 @@ export interface PathDecisionResult {
110
111
  readonly alternativeCount: number;
111
112
  readonly tieBreakReasons: readonly string[];
112
113
  readonly refName?: string;
114
+ readonly requiredInvariants: readonly string[];
115
+ readonly satisfiedInvariants: readonly string[];
113
116
  readonly selectedPath: readonly {
114
117
  readonly dirName: string;
115
118
  readonly migrationHash: string;
116
119
  readonly from: string;
117
120
  readonly to: string;
121
+ readonly invariants: readonly string[];
118
122
  }[];
119
123
  }
120
124
 
125
+ export function collectDeclaredInvariants(graph: MigrationGraph): ReadonlySet<string> {
126
+ const declared = new Set<string>();
127
+ for (const edges of graph.forwardChain.values()) {
128
+ for (const edge of edges) {
129
+ for (const inv of edge.invariants) {
130
+ declared.add(inv);
131
+ }
132
+ }
133
+ }
134
+ return declared;
135
+ }
136
+
137
+ /**
138
+ * Maps a `MigrationEdge` to the structural-edge shape used in the
139
+ * `MIGRATION.NO_INVARIANT_PATH` error envelope. Shared between
140
+ * `migration apply` and `migration status` so both commands surface
141
+ * the same JSON wire shape when an invariant-aware route is unsatisfiable.
142
+ */
143
+ export function toStructuralEdge(edge: MigrationEdge): NoInvariantPathStructuralEdge {
144
+ return {
145
+ dirName: edge.dirName,
146
+ migrationHash: edge.migrationHash,
147
+ from: edge.from,
148
+ to: edge.to,
149
+ invariants: edge.invariants,
150
+ };
151
+ }
152
+
121
153
  /**
122
154
  * Maps a PathDecision to the slim CLI output representation.
123
155
  */
@@ -127,12 +159,15 @@ export function toPathDecisionResult(decision: PathDecision): PathDecisionResult
127
159
  toHash: decision.toHash,
128
160
  alternativeCount: decision.alternativeCount,
129
161
  tieBreakReasons: decision.tieBreakReasons,
162
+ requiredInvariants: decision.requiredInvariants ?? [],
163
+ satisfiedInvariants: decision.satisfiedInvariants ?? [],
130
164
  ...ifDefined('refName', decision.refName),
131
165
  selectedPath: decision.selectedPath.map((entry) => ({
132
166
  dirName: entry.dirName,
133
167
  migrationHash: entry.migrationHash,
134
168
  from: entry.from,
135
169
  to: entry.to,
170
+ invariants: entry.invariants,
136
171
  })),
137
172
  };
138
173
  }
@@ -106,7 +106,11 @@ export function migrationGraphToRenderInput(input: MigrationGraphInput): Migrati
106
106
  for (const entry of entries) {
107
107
  const status = statusByDirName.get(entry.dirName);
108
108
  const icon = status ? STATUS_ICON[status] : '';
109
- const label = `${entry.dirName}${icon}`;
109
+ const invariantsSuffix =
110
+ entry.invariants.length > 0
111
+ ? ` provides [${entry.invariants.map((id) => JSON.stringify(id)).join(', ')}]`
112
+ : '';
113
+ const label = `${entry.dirName}${icon}${invariantsSuffix}`;
110
114
 
111
115
  edgeList.push({
112
116
  from: toShortId(entry.from),
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-Buy8_40Y.mjs","names":["plannerResult: MigrationPlannerResult","migrationPlan: MigrationPlan","runnerResult: MigrationRunnerResult","plannerResult: MigrationPlannerResult","runnerResult: MigrationRunnerResult","applied: MigrationApplyAppliedEntry[]","runnerResult: MigrationRunnerResult","contract: Contract","contractRaw: unknown","emitContractArtifacts"],"sources":["../src/control-api/errors.ts","../src/control-api/operations/migration-helpers.ts","../src/control-api/operations/db-init.ts","../src/control-api/operations/db-update.ts","../src/control-api/operations/migration-apply.ts","../src/control-api/client.ts"],"sourcesContent":["export class ContractValidationError extends Error {\n override readonly cause?: unknown;\n\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = 'ContractValidationError';\n this.cause = cause;\n }\n}\n","import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';\nimport type { ControlActionName, OnControlProgress } from '../types';\n\n/**\n * Strips operation objects to their public shape (id, label, operationClass).\n * Used at the API boundary to avoid leaking internal fields (precheck, execute, postcheck, etc.).\n */\nexport function stripOperations(\n operations: readonly MigrationPlanOperation[],\n): ReadonlyArray<{ readonly id: string; readonly label: string; readonly operationClass: string }> {\n return operations.map((op) => ({\n id: op.id,\n label: op.label,\n operationClass: op.operationClass,\n }));\n}\n\n/**\n * Creates per-operation progress callbacks for the runner.\n * Returns undefined when no onProgress callback is provided.\n */\nexport function createOperationCallbacks(\n onProgress: OnControlProgress | undefined,\n action: ControlActionName,\n parentSpanId: string,\n) {\n if (!onProgress) {\n return undefined;\n }\n return {\n onOperationStart: (op: MigrationPlanOperation) => {\n onProgress({\n action,\n kind: 'spanStart',\n spanId: `operation:${op.id}`,\n parentSpanId,\n label: op.label,\n });\n },\n onOperationComplete: (op: MigrationPlanOperation) => {\n onProgress({\n action,\n kind: 'spanEnd',\n spanId: `operation:${op.id}`,\n outcome: 'ok',\n });\n },\n };\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n MigrationPlan,\n MigrationPlannerResult,\n MigrationRunnerResult,\n TargetMigrationsCapability,\n} from '@prisma-next/framework-components/control';\nimport { hasOperationPreview } from '@prisma-next/framework-components/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport type { DbInitResult, DbInitSuccess, OnControlProgress } from '../types';\nimport { createOperationCallbacks, stripOperations } from './migration-helpers';\n\n/**\n * Options for executing dbInit operation.\n */\nexport interface ExecuteDbInitOptions<TFamilyId extends string, TTargetId extends string> {\n readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;\n readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;\n readonly contract: Contract;\n readonly mode: 'plan' | 'apply';\n readonly migrations: TargetMigrationsCapability<\n TFamilyId,\n TTargetId,\n ControlFamilyInstance<TFamilyId, unknown>\n >;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;\n /** Optional progress callback for observing operation progress */\n readonly onProgress?: OnControlProgress;\n}\n\n/**\n * Executes the dbInit operation.\n *\n * This is the core logic extracted from the CLI command, without any file I/O,\n * process.exit(), or console output. It uses the Result pattern to return\n * success or failure details.\n *\n * @param options - The options for executing dbInit\n * @returns Result with DbInitSuccess on success, DbInitFailure on failure\n */\nexport async function executeDbInit<TFamilyId extends string, TTargetId extends string>(\n options: ExecuteDbInitOptions<TFamilyId, TTargetId>,\n): Promise<DbInitResult> {\n const { driver, familyInstance, contract, mode, migrations, frameworkComponents, onProgress } =\n options;\n\n // Create planner and runner from target migrations capability\n const planner = migrations.createPlanner(familyInstance);\n const runner = migrations.createRunner(familyInstance);\n\n // Introspect live schema\n const introspectSpanId = 'introspect';\n onProgress?.({\n action: 'dbInit',\n kind: 'spanStart',\n spanId: introspectSpanId,\n label: 'Introspecting database schema',\n });\n const schemaIR = await familyInstance.introspect({ driver });\n onProgress?.({\n action: 'dbInit',\n kind: 'spanEnd',\n spanId: introspectSpanId,\n outcome: 'ok',\n });\n\n // Policy for init mode (additive only)\n const policy = { allowedOperationClasses: ['additive'] as const };\n\n // Plan migration\n const planSpanId = 'plan';\n onProgress?.({\n action: 'dbInit',\n kind: 'spanStart',\n spanId: planSpanId,\n label: 'Planning migration',\n });\n const plannerResult: MigrationPlannerResult = await planner.plan({\n contract,\n schema: schemaIR,\n policy,\n // `db init` reconciles against the live introspected schema; there is no\n // prior contract to derive a \"from\" identity from. The required\n // `fromContract: null` makes that structural fact visible at the call\n // site (vs. silently letting the planner default to a baseline plan).\n fromContract: null,\n frameworkComponents,\n });\n\n if (plannerResult.kind === 'failure') {\n onProgress?.({\n action: 'dbInit',\n kind: 'spanEnd',\n spanId: planSpanId,\n outcome: 'error',\n });\n return notOk({\n code: 'PLANNING_FAILED' as const,\n summary: 'Migration planning failed due to conflicts',\n conflicts: plannerResult.conflicts,\n why: undefined,\n meta: undefined,\n });\n }\n\n const migrationPlan: MigrationPlan = plannerResult.plan;\n onProgress?.({\n action: 'dbInit',\n kind: 'spanEnd',\n spanId: planSpanId,\n outcome: 'ok',\n });\n\n // Check for existing marker - handle idempotency and mismatch errors\n const checkMarkerSpanId = 'checkMarker';\n onProgress?.({\n action: 'dbInit',\n kind: 'spanStart',\n spanId: checkMarkerSpanId,\n label: 'Checking database signature',\n });\n const existingMarker = await familyInstance.readMarker({ driver });\n if (existingMarker) {\n const markerMatchesDestination =\n existingMarker.storageHash === migrationPlan.destination.storageHash &&\n (!migrationPlan.destination.profileHash ||\n existingMarker.profileHash === migrationPlan.destination.profileHash);\n\n if (markerMatchesDestination) {\n // Already at destination - return success with no operations\n onProgress?.({\n action: 'dbInit',\n kind: 'spanEnd',\n spanId: checkMarkerSpanId,\n outcome: 'skipped',\n });\n const result: DbInitSuccess = {\n mode,\n plan: { operations: [] },\n destination: {\n storageHash: migrationPlan.destination.storageHash,\n ...ifDefined('profileHash', migrationPlan.destination.profileHash),\n },\n ...ifDefined(\n 'execution',\n mode === 'apply' ? { operationsPlanned: 0, operationsExecuted: 0 } : undefined,\n ),\n ...ifDefined(\n 'marker',\n mode === 'apply'\n ? {\n storageHash: existingMarker.storageHash,\n profileHash: existingMarker.profileHash,\n }\n : undefined,\n ),\n summary: 'Database already at target contract state',\n };\n return ok(result);\n }\n\n // Marker exists but doesn't match destination - fail\n onProgress?.({\n action: 'dbInit',\n kind: 'spanEnd',\n spanId: checkMarkerSpanId,\n outcome: 'error',\n });\n return notOk({\n code: 'MARKER_ORIGIN_MISMATCH' as const,\n summary: 'Existing contract marker does not match plan destination',\n marker: {\n storageHash: existingMarker.storageHash,\n profileHash: existingMarker.profileHash,\n },\n destination: {\n storageHash: migrationPlan.destination.storageHash,\n profileHash: migrationPlan.destination.profileHash,\n },\n why: undefined,\n conflicts: undefined,\n meta: undefined,\n });\n }\n\n onProgress?.({\n action: 'dbInit',\n kind: 'spanEnd',\n spanId: checkMarkerSpanId,\n outcome: 'ok',\n });\n\n // Plan mode - don't execute\n if (mode === 'plan') {\n const preview = hasOperationPreview(familyInstance)\n ? familyInstance.toOperationPreview(migrationPlan.operations)\n : undefined;\n const result: DbInitSuccess = {\n mode: 'plan',\n plan: {\n operations: stripOperations(migrationPlan.operations),\n ...ifDefined('preview', preview),\n },\n destination: {\n storageHash: migrationPlan.destination.storageHash,\n ...ifDefined('profileHash', migrationPlan.destination.profileHash),\n },\n summary: `Planned ${migrationPlan.operations.length} operation(s)`,\n };\n return ok(result);\n }\n\n // Apply mode - execute runner\n const applySpanId = 'apply';\n onProgress?.({\n action: 'dbInit',\n kind: 'spanStart',\n spanId: applySpanId,\n label: 'Applying migration plan',\n });\n\n const callbacks = createOperationCallbacks(onProgress, 'dbInit', applySpanId);\n\n const runnerResult: MigrationRunnerResult = await runner.execute({\n plan: migrationPlan,\n driver,\n destinationContract: contract,\n policy,\n ...ifDefined('callbacks', callbacks),\n // db init plans and applies back-to-back from a fresh introspection, so per-operation\n // pre/postchecks and the idempotency probe are usually redundant overhead. We still\n // enforce marker/origin compatibility and a full schema verification after apply.\n executionChecks: {\n prechecks: false,\n postchecks: false,\n idempotencyChecks: false,\n },\n frameworkComponents,\n });\n\n if (!runnerResult.ok) {\n onProgress?.({\n action: 'dbInit',\n kind: 'spanEnd',\n spanId: applySpanId,\n outcome: 'error',\n });\n return notOk({\n code: 'RUNNER_FAILED' as const,\n summary: runnerResult.failure.summary,\n why: runnerResult.failure.why,\n meta: runnerResult.failure.meta,\n conflicts: undefined,\n });\n }\n\n const execution = runnerResult.value;\n\n onProgress?.({\n action: 'dbInit',\n kind: 'spanEnd',\n spanId: applySpanId,\n outcome: 'ok',\n });\n\n const result: DbInitSuccess = {\n mode: 'apply',\n plan: {\n operations: stripOperations(migrationPlan.operations),\n },\n destination: {\n storageHash: migrationPlan.destination.storageHash,\n ...ifDefined('profileHash', migrationPlan.destination.profileHash),\n },\n execution: {\n operationsPlanned: execution.operationsPlanned,\n operationsExecuted: execution.operationsExecuted,\n },\n marker: migrationPlan.destination.profileHash\n ? {\n storageHash: migrationPlan.destination.storageHash,\n profileHash: migrationPlan.destination.profileHash,\n }\n : { storageHash: migrationPlan.destination.storageHash },\n summary: `Applied ${execution.operationsExecuted} operation(s), database signed`,\n };\n return ok(result);\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n MigrationPlannerResult,\n MigrationRunnerResult,\n TargetMigrationsCapability,\n} from '@prisma-next/framework-components/control';\nimport { hasOperationPreview } from '@prisma-next/framework-components/control';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport type { DbUpdateResult, DbUpdateSuccess, OnControlProgress } from '../types';\nimport { createOperationCallbacks, stripOperations } from './migration-helpers';\n\n// F12: db update allows additive, widening, and destructive operations.\nconst DB_UPDATE_POLICY = {\n allowedOperationClasses: ['additive', 'widening', 'destructive'] as const,\n} as const;\n\n/**\n * Options for the executeDbUpdate operation.\n * Config-agnostic: receives pre-resolved driver, family, contract, and migrations capability.\n */\nexport interface ExecuteDbUpdateOptions<TFamilyId extends string, TTargetId extends string> {\n readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;\n readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;\n readonly contract: Contract;\n readonly mode: 'plan' | 'apply';\n readonly migrations: TargetMigrationsCapability<\n TFamilyId,\n TTargetId,\n ControlFamilyInstance<TFamilyId, unknown>\n >;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;\n readonly acceptDataLoss?: boolean;\n /** Optional progress callback for observing operation progress. */\n readonly onProgress?: OnControlProgress;\n}\n\n/**\n * Executes the db update operation: introspect → plan → (optionally) apply → marker.\n *\n * db update is a pure reconciliation command: it introspects the live schema, plans the diff\n * to the destination contract, and applies operations. The marker is bookkeeping only — written\n * after apply so that `verify` and `db init` can reference it, but never read or validated\n * by db update itself. The runner creates the marker table if it does not exist.\n */\nexport async function executeDbUpdate<TFamilyId extends string, TTargetId extends string>(\n options: ExecuteDbUpdateOptions<TFamilyId, TTargetId>,\n): Promise<DbUpdateResult> {\n const { driver, familyInstance, contract, mode, migrations, frameworkComponents, onProgress } =\n options;\n\n const planner = migrations.createPlanner(familyInstance);\n const runner = migrations.createRunner(familyInstance);\n\n const introspectSpanId = 'introspect';\n onProgress?.({\n action: 'dbUpdate',\n kind: 'spanStart',\n spanId: introspectSpanId,\n label: 'Introspecting database schema',\n });\n const schemaIR = await familyInstance.introspect({ driver });\n onProgress?.({\n action: 'dbUpdate',\n kind: 'spanEnd',\n spanId: introspectSpanId,\n outcome: 'ok',\n });\n\n const policy = DB_UPDATE_POLICY;\n\n const planSpanId = 'plan';\n onProgress?.({\n action: 'dbUpdate',\n kind: 'spanStart',\n spanId: planSpanId,\n label: 'Planning migration',\n });\n const plannerResult: MigrationPlannerResult = await planner.plan({\n contract,\n schema: schemaIR,\n policy,\n // `db update` reconciles against the live introspected schema; there is\n // no prior contract to derive a \"from\" identity from. The required\n // `fromContract: null` makes that structural fact visible at the call\n // site (vs. silently letting the planner default to a baseline plan).\n fromContract: null,\n frameworkComponents,\n });\n if (plannerResult.kind === 'failure') {\n onProgress?.({\n action: 'dbUpdate',\n kind: 'spanEnd',\n spanId: planSpanId,\n outcome: 'error',\n });\n return notOk({\n code: 'PLANNING_FAILED',\n summary: 'Migration planning failed due to conflicts',\n conflicts: plannerResult.conflicts,\n why: undefined,\n meta: undefined,\n });\n }\n onProgress?.({\n action: 'dbUpdate',\n kind: 'spanEnd',\n spanId: planSpanId,\n outcome: 'ok',\n });\n\n const migrationPlan = plannerResult.plan;\n\n if (mode === 'plan') {\n const preview = hasOperationPreview(familyInstance)\n ? familyInstance.toOperationPreview(migrationPlan.operations)\n : undefined;\n const result: DbUpdateSuccess = {\n mode: 'plan',\n plan: {\n operations: stripOperations(migrationPlan.operations),\n ...ifDefined('preview', preview),\n },\n destination: {\n storageHash: migrationPlan.destination.storageHash,\n ...ifDefined('profileHash', migrationPlan.destination.profileHash),\n },\n summary: `Planned ${migrationPlan.operations.length} operation(s)`,\n };\n return ok(result);\n }\n\n // When applying, require explicit acceptance for destructive operations\n if (!options.acceptDataLoss) {\n const destructiveOps = migrationPlan.operations\n .filter((op) => op.operationClass === 'destructive')\n .map((op) => ({ id: op.id, label: op.label }));\n if (destructiveOps.length > 0) {\n return notOk({\n code: 'DESTRUCTIVE_CHANGES',\n summary: `Planned ${destructiveOps.length} destructive operation(s) that require confirmation`,\n why: 'Destructive operations require confirmation — re-run with -y to accept',\n conflicts: undefined,\n meta: { destructiveOperations: destructiveOps },\n });\n }\n }\n\n const applySpanId = 'apply';\n onProgress?.({\n action: 'dbUpdate',\n kind: 'spanStart',\n spanId: applySpanId,\n label: 'Applying migration plan',\n });\n\n const callbacks = createOperationCallbacks(onProgress, 'dbUpdate', applySpanId);\n\n const runnerResult: MigrationRunnerResult = await runner.execute({\n plan: migrationPlan,\n driver,\n destinationContract: contract,\n policy,\n ...(callbacks ? { callbacks } : {}),\n // db update plans and applies from a single introspection pass, so per-operation pre/postchecks\n // and idempotency probes are intentionally disabled to avoid redundant roundtrips.\n executionChecks: {\n prechecks: false,\n postchecks: false,\n idempotencyChecks: false,\n },\n frameworkComponents,\n });\n\n if (!runnerResult.ok) {\n onProgress?.({\n action: 'dbUpdate',\n kind: 'spanEnd',\n spanId: applySpanId,\n outcome: 'error',\n });\n return notOk({\n code: 'RUNNER_FAILED',\n summary: runnerResult.failure.summary,\n why: runnerResult.failure.why,\n meta: runnerResult.failure.meta,\n conflicts: undefined,\n });\n }\n\n const execution = runnerResult.value;\n onProgress?.({\n action: 'dbUpdate',\n kind: 'spanEnd',\n spanId: applySpanId,\n outcome: 'ok',\n });\n\n const result: DbUpdateSuccess = {\n mode: 'apply',\n plan: {\n operations: stripOperations(migrationPlan.operations),\n },\n destination: {\n storageHash: migrationPlan.destination.storageHash,\n ...ifDefined('profileHash', migrationPlan.destination.profileHash),\n },\n execution: {\n operationsPlanned: execution.operationsPlanned,\n operationsExecuted: execution.operationsExecuted,\n },\n marker: migrationPlan.destination.profileHash\n ? {\n storageHash: migrationPlan.destination.storageHash,\n profileHash: migrationPlan.destination.profileHash,\n }\n : { storageHash: migrationPlan.destination.storageHash },\n summary:\n execution.operationsExecuted === 0\n ? 'Database already matches contract, signature updated'\n : `Applied ${execution.operationsExecuted} operation(s), signature updated`,\n };\n return ok(result);\n}\n","import type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n MigrationRunnerResult,\n TargetMigrationsCapability,\n} from '@prisma-next/framework-components/control';\nimport { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport type {\n MigrationApplyAppliedEntry,\n MigrationApplyResult,\n MigrationApplyStep,\n OnControlProgress,\n} from '../types';\n\nexport interface ExecuteMigrationApplyOptions<TFamilyId extends string, TTargetId extends string> {\n readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;\n readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;\n readonly originHash: string;\n readonly destinationHash: string;\n readonly pendingMigrations: readonly MigrationApplyStep[];\n readonly migrations: TargetMigrationsCapability<\n TFamilyId,\n TTargetId,\n ControlFamilyInstance<TFamilyId, unknown>\n >;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;\n readonly targetId: string;\n readonly onProgress?: OnControlProgress;\n}\n\n/**\n * Apply a sequence of migration packages against the configured driver.\n *\n * Validates the path's continuity (origin → ... → destination, no gaps),\n * then drives the family/target's migration runner over each package's\n * operations in order, surfacing per-migration progress through `onProgress`.\n *\n * The `pendingMigrations` parameter is trusted input. Callers are responsible\n * for upstream verification of the originating migration packages — typically\n * by loading them via `readMigrationPackage` from\n * `@prisma-next/migration-tools/io`, which performs hash-integrity checks at\n * the load boundary. This operation does not re-verify the packages and\n * assumes the `(metadata, ops)` pairs on disk have not been tampered with\n * since emit.\n */\nexport async function executeMigrationApply<TFamilyId extends string, TTargetId extends string>(\n options: ExecuteMigrationApplyOptions<TFamilyId, TTargetId>,\n): Promise<MigrationApplyResult> {\n const {\n driver,\n familyInstance,\n originHash,\n destinationHash,\n pendingMigrations,\n migrations,\n frameworkComponents,\n targetId,\n onProgress,\n } = options;\n\n if (pendingMigrations.length === 0) {\n if (originHash !== destinationHash) {\n return notOk({\n code: 'MIGRATION_PATH_NOT_FOUND' as const,\n summary: 'No migrations provided for requested origin and destination',\n why: `Requested ${originHash} -> ${destinationHash} but pendingMigrations is empty`,\n meta: { originHash, destinationHash },\n });\n }\n return ok({\n migrationsApplied: 0,\n markerHash: originHash,\n applied: [],\n summary: 'Already up to date',\n });\n }\n\n const firstMigration = pendingMigrations[0]!;\n const lastMigration = pendingMigrations[pendingMigrations.length - 1]!;\n // Manifest `from` is `string | null` (null = baseline). The live-marker\n // layer encodes \"no prior state\" as EMPTY_CONTRACT_HASH; bridge here so the\n // string comparisons below work uniformly.\n const firstFromMarker = firstMigration.from ?? EMPTY_CONTRACT_HASH;\n if (firstFromMarker !== originHash || lastMigration.to !== destinationHash) {\n return notOk({\n code: 'MIGRATION_PATH_NOT_FOUND' as const,\n summary: 'Migration apply path does not match requested origin and destination',\n why: `Path resolved as ${firstFromMarker} -> ${lastMigration.to}, but requested ${originHash} -> ${destinationHash}`,\n meta: {\n originHash,\n destinationHash,\n pathOrigin: firstFromMarker,\n pathDestination: lastMigration.to,\n },\n });\n }\n\n for (let i = 1; i < pendingMigrations.length; i++) {\n const previous = pendingMigrations[i - 1]!;\n const current = pendingMigrations[i]!;\n const currentFromMarker = current.from ?? EMPTY_CONTRACT_HASH;\n if (previous.to !== currentFromMarker) {\n return notOk({\n code: 'MIGRATION_PATH_NOT_FOUND' as const,\n summary: 'Migration apply path contains a discontinuity between adjacent migrations',\n why: `Migration \"${previous.dirName}\" ends at ${previous.to}, but next migration \"${current.dirName}\" starts at ${currentFromMarker}`,\n meta: {\n originHash,\n destinationHash,\n previousDirName: previous.dirName,\n previousTo: previous.to,\n currentDirName: current.dirName,\n currentFrom: currentFromMarker,\n discontinuityIndex: i,\n },\n });\n }\n }\n\n const runner = migrations.createRunner(familyInstance);\n const applied: MigrationApplyAppliedEntry[] = [];\n\n for (const migration of pendingMigrations) {\n const migrationSpanId = `migration:${migration.dirName}`;\n onProgress?.({\n action: 'migrationApply',\n kind: 'spanStart',\n spanId: migrationSpanId,\n label: `Applying ${migration.dirName}`,\n });\n\n const { operations } = migration;\n\n // Allow all operation classes. The policy gate belongs at plan time, not\n // apply time — the planner already decided what to emit. Restricting here\n // would be a tautology (the allowed set would just mirror what's in ops).\n const policy = {\n allowedOperationClasses: ['additive', 'widening', 'destructive', 'data'] as const,\n };\n\n // Manifest `from === null` means \"no prior state\" — the runner expects\n // `origin: null` for a fresh database (no marker present).\n const plan = {\n targetId,\n origin: migration.from === null ? null : { storageHash: migration.from },\n destination: { storageHash: migration.to },\n operations,\n };\n\n const destinationContract = familyInstance.validateContract(migration.toContract);\n\n const runnerResult: MigrationRunnerResult = await runner.execute({\n plan,\n driver,\n destinationContract,\n policy,\n executionChecks: {\n prechecks: true,\n postchecks: true,\n idempotencyChecks: true,\n },\n frameworkComponents,\n });\n\n if (!runnerResult.ok) {\n onProgress?.({\n action: 'migrationApply',\n kind: 'spanEnd',\n spanId: migrationSpanId,\n outcome: 'error',\n });\n return notOk({\n code: 'RUNNER_FAILED' as const,\n summary: runnerResult.failure.summary,\n why: runnerResult.failure.why,\n meta: {\n migration: migration.dirName,\n from: migration.from,\n to: migration.to,\n ...(runnerResult.failure.meta ?? {}),\n },\n });\n }\n\n onProgress?.({\n action: 'migrationApply',\n kind: 'spanEnd',\n spanId: migrationSpanId,\n outcome: 'ok',\n });\n\n applied.push({\n dirName: migration.dirName,\n from: migration.from,\n to: migration.to,\n operationsExecuted: runnerResult.value.operationsExecuted,\n });\n }\n\n const finalHash = pendingMigrations[pendingMigrations.length - 1]!.to;\n const totalOps = applied.reduce((sum, a) => sum + a.operationsExecuted, 0);\n\n return ok({\n migrationsApplied: applied.length,\n markerHash: finalHash,\n applied,\n summary: `Applied ${applied.length} migration(s) (${totalOps} operation(s)), marker at ${finalHash}`,\n });\n}\n","import type { Contract, ContractMarkerRecord } from '@prisma-next/contract/types';\nimport { emit as emitContractArtifacts } from '@prisma-next/emitter';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlDriverInstance,\n ControlFamilyInstance,\n ControlStack,\n CoreSchemaView,\n MigrationPlanOperation,\n OperationPreview,\n SignDatabaseResult,\n VerifyDatabaseResult,\n VerifyDatabaseSchemaResult,\n} from '@prisma-next/framework-components/control';\nimport {\n createControlStack,\n hasMigrations,\n hasOperationPreview,\n hasPslContractInfer,\n hasSchemaView,\n} from '@prisma-next/framework-components/control';\nimport type { PslDocumentAst } from '@prisma-next/framework-components/psl-ast';\nimport { ifDefined } from '@prisma-next/utils/defined';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { assertFrameworkComponentsCompatible } from '../utils/framework-components';\nimport { enrichContract } from './contract-enrichment';\nimport { ContractValidationError } from './errors';\nimport { executeDbInit } from './operations/db-init';\nimport { executeDbUpdate } from './operations/db-update';\nimport { executeMigrationApply } from './operations/migration-apply';\nimport type {\n ControlActionName,\n ControlClient,\n ControlClientOptions,\n DbInitOptions,\n DbInitResult,\n DbUpdateOptions,\n DbUpdateResult,\n EmitOptions,\n EmitResult,\n IntrospectOptions,\n MigrationApplyOptions,\n MigrationApplyResult,\n OnControlProgress,\n SchemaVerifyOptions,\n SignOptions,\n VerifyOptions,\n} from './types';\n\n/**\n * Creates a programmatic control client for Prisma Next operations.\n *\n * The client accepts framework component descriptors at creation time,\n * manages driver lifecycle via connect()/close(), and exposes domain\n * operations that delegate to the existing family instance methods.\n *\n * @see {@link ControlClient} for the client interface\n * @see README.md \"Programmatic Control API\" section for usage examples\n */\nexport function createControlClient(options: ControlClientOptions): ControlClient {\n return new ControlClientImpl(options);\n}\n\n/**\n * Implementation of ControlClient.\n * Manages initialization and connection state, delegates operations to family instance.\n */\nclass ControlClientImpl implements ControlClient {\n private readonly options: ControlClientOptions;\n private stack: ControlStack | null = null;\n private driver: ControlDriverInstance<string, string> | null = null;\n private familyInstance: ControlFamilyInstance<string, unknown> | null = null;\n private frameworkComponents: ReadonlyArray<\n TargetBoundComponentDescriptor<string, string>\n > | null = null;\n private initialized = false;\n private readonly defaultConnection: unknown;\n\n constructor(options: ControlClientOptions) {\n this.options = options;\n this.defaultConnection = options.connection;\n }\n\n init(): void {\n if (this.initialized) {\n return; // Idempotent\n }\n\n this.stack = createControlStack({\n family: this.options.family,\n target: this.options.target,\n adapter: this.options.adapter,\n driver: this.options.driver,\n extensionPacks: this.options.extensionPacks,\n });\n\n this.familyInstance = this.options.family.create(this.stack);\n\n // Validate and type-narrow framework components\n const rawComponents = [\n this.options.target,\n this.options.adapter,\n ...(this.options.extensionPacks ?? []),\n ];\n this.frameworkComponents = assertFrameworkComponentsCompatible(\n this.options.family.familyId,\n this.options.target.targetId,\n rawComponents,\n );\n\n this.initialized = true;\n }\n\n async connect(connection?: unknown): Promise<void> {\n // Auto-init if needed\n this.init();\n\n if (this.driver) {\n throw new Error('Already connected. Call close() before reconnecting.');\n }\n\n // Resolve connection: argument > default from options\n const resolvedConnection = connection ?? this.defaultConnection;\n if (resolvedConnection === undefined) {\n throw new Error(\n 'No connection provided. Pass a connection to connect() or provide a default connection when creating the client.',\n );\n }\n\n // Check for driver descriptor\n if (!this.stack?.driver) {\n throw new Error(\n 'Driver is not configured. Pass a driver descriptor when creating the control client to enable database operations.',\n );\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: required for runtime connection type flexibility\n this.driver = await this.stack.driver.create(resolvedConnection as any);\n }\n\n async close(): Promise<void> {\n if (this.driver) {\n await this.driver.close();\n this.driver = null;\n }\n }\n\n private async ensureConnected(): Promise<{\n driver: ControlDriverInstance<string, string>;\n familyInstance: ControlFamilyInstance<string, unknown>;\n frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<string, string>>;\n }> {\n // Auto-init if needed\n this.init();\n\n // Auto-connect if not connected and default connection is available\n if (!this.driver && this.defaultConnection !== undefined) {\n await this.connect(this.defaultConnection);\n }\n\n if (!this.driver || !this.familyInstance || !this.frameworkComponents) {\n throw new Error('Not connected. Call connect(connection) first.');\n }\n return {\n driver: this.driver,\n familyInstance: this.familyInstance,\n frameworkComponents: this.frameworkComponents,\n };\n }\n\n private async connectWithProgress(\n connection: unknown | undefined,\n action: ControlActionName,\n onProgress?: OnControlProgress,\n ): Promise<void> {\n if (connection === undefined) return;\n onProgress?.({\n action,\n kind: 'spanStart',\n spanId: 'connect',\n label: 'Connecting to database...',\n });\n try {\n await this.connect(connection);\n onProgress?.({ action, kind: 'spanEnd', spanId: 'connect', outcome: 'ok' });\n } catch (error) {\n onProgress?.({ action, kind: 'spanEnd', spanId: 'connect', outcome: 'error' });\n throw error;\n }\n }\n\n async verify(options: VerifyOptions): Promise<VerifyDatabaseResult> {\n const { onProgress } = options;\n await this.connectWithProgress(options.connection, 'verify', onProgress);\n const { driver, familyInstance } = await this.ensureConnected();\n\n // Validate contract using family instance\n let contract: Contract;\n try {\n contract = familyInstance.validateContract(options.contract);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new ContractValidationError(message, error);\n }\n\n // Emit verify span\n onProgress?.({\n action: 'verify',\n kind: 'spanStart',\n spanId: 'verify',\n label: 'Verifying database marker...',\n });\n\n try {\n // Delegate to family instance verify method\n // Note: We pass empty strings for contractPath/configPath since the programmatic\n // API doesn't deal with file paths. The family instance accepts these as optional\n // metadata for error reporting.\n const result = await familyInstance.verify({\n driver,\n contract,\n expectedTargetId: this.options.target.targetId,\n contractPath: '',\n });\n\n onProgress?.({\n action: 'verify',\n kind: 'spanEnd',\n spanId: 'verify',\n outcome: result.ok ? 'ok' : 'error',\n });\n\n return result;\n } catch (error) {\n onProgress?.({\n action: 'verify',\n kind: 'spanEnd',\n spanId: 'verify',\n outcome: 'error',\n });\n throw error;\n }\n }\n\n async schemaVerify(options: SchemaVerifyOptions): Promise<VerifyDatabaseSchemaResult> {\n const { onProgress } = options;\n await this.connectWithProgress(options.connection, 'schemaVerify', onProgress);\n const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();\n\n // Validate contract using family instance\n let contract: Contract;\n try {\n contract = familyInstance.validateContract(options.contract);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new ContractValidationError(message, error);\n }\n\n // Emit schemaVerify span\n onProgress?.({\n action: 'schemaVerify',\n kind: 'spanStart',\n spanId: 'schemaVerify',\n label: 'Verifying database schema...',\n });\n\n try {\n // Delegate to family instance schemaVerify method\n const result = await familyInstance.schemaVerify({\n driver,\n contract,\n strict: options.strict ?? false,\n contractPath: '',\n frameworkComponents,\n });\n\n onProgress?.({\n action: 'schemaVerify',\n kind: 'spanEnd',\n spanId: 'schemaVerify',\n outcome: result.ok ? 'ok' : 'error',\n });\n\n return result;\n } catch (error) {\n onProgress?.({\n action: 'schemaVerify',\n kind: 'spanEnd',\n spanId: 'schemaVerify',\n outcome: 'error',\n });\n throw error;\n }\n }\n\n async sign(options: SignOptions): Promise<SignDatabaseResult> {\n const { onProgress } = options;\n await this.connectWithProgress(options.connection, 'sign', onProgress);\n const { driver, familyInstance } = await this.ensureConnected();\n\n // Validate contract using family instance\n let contract: Contract;\n try {\n contract = familyInstance.validateContract(options.contract);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new ContractValidationError(message, error);\n }\n\n // Emit sign span\n onProgress?.({\n action: 'sign',\n kind: 'spanStart',\n spanId: 'sign',\n label: 'Signing database...',\n });\n\n try {\n // Delegate to family instance sign method\n const result = await familyInstance.sign({\n driver,\n contract,\n contractPath: options.contractPath ?? '',\n ...ifDefined('configPath', options.configPath),\n });\n\n onProgress?.({\n action: 'sign',\n kind: 'spanEnd',\n spanId: 'sign',\n outcome: 'ok',\n });\n\n return result;\n } catch (error) {\n onProgress?.({\n action: 'sign',\n kind: 'spanEnd',\n spanId: 'sign',\n outcome: 'error',\n });\n throw error;\n }\n }\n\n async dbInit(options: DbInitOptions): Promise<DbInitResult> {\n const { onProgress } = options;\n await this.connectWithProgress(options.connection, 'dbInit', onProgress);\n const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();\n\n if (!hasMigrations(this.options.target)) {\n throw new Error(`Target \"${this.options.target.targetId}\" does not support migrations`);\n }\n\n let contract: Contract;\n try {\n contract = familyInstance.validateContract(options.contract);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new ContractValidationError(message, error);\n }\n\n return executeDbInit({\n driver,\n familyInstance,\n contract,\n mode: options.mode,\n migrations: this.options.target.migrations,\n frameworkComponents,\n ...ifDefined('onProgress', onProgress),\n });\n }\n\n async dbUpdate(options: DbUpdateOptions): Promise<DbUpdateResult> {\n const { onProgress } = options;\n await this.connectWithProgress(options.connection, 'dbUpdate', onProgress);\n const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();\n\n if (!hasMigrations(this.options.target)) {\n throw new Error(`Target \"${this.options.target.targetId}\" does not support migrations`);\n }\n\n let contract: Contract;\n try {\n contract = familyInstance.validateContract(options.contract);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new ContractValidationError(message, error);\n }\n\n return executeDbUpdate({\n driver,\n familyInstance,\n contract,\n mode: options.mode,\n migrations: this.options.target.migrations,\n frameworkComponents,\n ...ifDefined('acceptDataLoss', options.acceptDataLoss),\n ...ifDefined('onProgress', onProgress),\n });\n }\n\n async readMarker(): Promise<ContractMarkerRecord | null> {\n const { driver, familyInstance } = await this.ensureConnected();\n return familyInstance.readMarker({ driver });\n }\n\n async migrationApply(options: MigrationApplyOptions): Promise<MigrationApplyResult> {\n const { onProgress } = options;\n await this.connectWithProgress(options.connection, 'migrationApply', onProgress);\n const { driver, familyInstance, frameworkComponents } = await this.ensureConnected();\n\n if (!hasMigrations(this.options.target)) {\n throw new Error(`Target \"${this.options.target.targetId}\" does not support migrations`);\n }\n\n return executeMigrationApply({\n driver,\n familyInstance,\n originHash: options.originHash,\n destinationHash: options.destinationHash,\n pendingMigrations: options.pendingMigrations,\n migrations: this.options.target.migrations,\n frameworkComponents,\n targetId: this.options.target.targetId,\n ...(onProgress ? { onProgress } : {}),\n });\n }\n\n async introspect(options?: IntrospectOptions): Promise<unknown> {\n const onProgress = options?.onProgress;\n await this.connectWithProgress(options?.connection, 'introspect', onProgress);\n const { driver, familyInstance } = await this.ensureConnected();\n\n // TODO: Pass schema option to familyInstance.introspect when schema filtering is implemented\n const _schema = options?.schema;\n void _schema;\n\n // Emit introspect span\n onProgress?.({\n action: 'introspect',\n kind: 'spanStart',\n spanId: 'introspect',\n label: 'Introspecting database schema...',\n });\n\n try {\n const result = await familyInstance.introspect({ driver });\n\n onProgress?.({\n action: 'introspect',\n kind: 'spanEnd',\n spanId: 'introspect',\n outcome: 'ok',\n });\n\n return result;\n } catch (error) {\n onProgress?.({\n action: 'introspect',\n kind: 'spanEnd',\n spanId: 'introspect',\n outcome: 'error',\n });\n throw error;\n }\n }\n\n toSchemaView(schemaIR: unknown): CoreSchemaView | undefined {\n this.init();\n if (this.familyInstance && hasSchemaView(this.familyInstance)) {\n return this.familyInstance.toSchemaView(schemaIR);\n }\n return undefined;\n }\n\n inferPslContract(schemaIR: unknown): PslDocumentAst | undefined {\n this.init();\n if (this.familyInstance && hasPslContractInfer(this.familyInstance)) {\n return this.familyInstance.inferPslContract(schemaIR);\n }\n return undefined;\n }\n\n toOperationPreview(operations: readonly MigrationPlanOperation[]): OperationPreview | undefined {\n this.init();\n if (this.familyInstance && hasOperationPreview(this.familyInstance)) {\n return this.familyInstance.toOperationPreview(operations);\n }\n return undefined;\n }\n\n async emit(options: EmitOptions): Promise<EmitResult> {\n const { onProgress, contractConfig } = options;\n\n // Ensure initialized (creates stack and family instance)\n // emit() does NOT require a database connection\n this.init();\n\n if (!this.familyInstance) {\n throw new Error('Family instance was not initialized. This is a bug.');\n }\n\n let contractRaw: unknown;\n onProgress?.({\n action: 'emit',\n kind: 'spanStart',\n spanId: 'resolveSource',\n label: 'Resolving contract source...',\n });\n\n try {\n const stack = this.stack!;\n const sourceContext = {\n composedExtensionPacks: stack.extensionPacks.map((p) => p.id),\n scalarTypeDescriptors: stack.scalarTypeDescriptors,\n authoringContributions: stack.authoringContributions,\n codecLookup: stack.codecLookup,\n controlMutationDefaults: stack.controlMutationDefaults,\n resolvedInputs: contractConfig.source.inputs ?? [],\n };\n const providerResult = await contractConfig.source.load(sourceContext);\n if (!providerResult.ok) {\n onProgress?.({\n action: 'emit',\n kind: 'spanEnd',\n spanId: 'resolveSource',\n outcome: 'error',\n });\n\n return notOk({\n code: 'CONTRACT_SOURCE_INVALID',\n summary: providerResult.failure.summary,\n why: providerResult.failure.summary,\n meta: providerResult.failure.meta,\n diagnostics: providerResult.failure,\n });\n }\n contractRaw = providerResult.value;\n\n onProgress?.({\n action: 'emit',\n kind: 'spanEnd',\n spanId: 'resolveSource',\n outcome: 'ok',\n });\n } catch (error) {\n onProgress?.({\n action: 'emit',\n kind: 'spanEnd',\n spanId: 'resolveSource',\n outcome: 'error',\n });\n\n const message = error instanceof Error ? error.message : String(error);\n return notOk({\n code: 'CONTRACT_SOURCE_INVALID',\n summary: 'Failed to resolve contract source',\n why: message,\n diagnostics: {\n summary: 'Contract source provider threw an exception',\n diagnostics: [\n {\n code: 'PROVIDER_THROW',\n message,\n },\n ],\n },\n meta: undefined,\n });\n }\n\n // Emit contract\n onProgress?.({\n action: 'emit',\n kind: 'spanStart',\n spanId: 'emit',\n label: 'Emitting contract...',\n });\n\n try {\n const enrichedIR = enrichContract(contractRaw as Contract, this.frameworkComponents ?? []);\n\n try {\n this.familyInstance.validateContract(enrichedIR);\n } catch (error) {\n onProgress?.({\n action: 'emit',\n kind: 'spanEnd',\n spanId: 'emit',\n outcome: 'error',\n });\n const message = error instanceof Error ? error.message : String(error);\n return notOk({\n code: 'CONTRACT_VALIDATION_FAILED',\n summary: 'Contract validation failed',\n why: message,\n meta: undefined,\n });\n }\n\n const result = await emitContractArtifacts(\n enrichedIR,\n this.stack!,\n this.options.family.emission,\n );\n\n onProgress?.({\n action: 'emit',\n kind: 'spanEnd',\n spanId: 'emit',\n outcome: 'ok',\n });\n\n return ok({\n storageHash: result.storageHash,\n ...ifDefined('executionHash', result.executionHash),\n profileHash: result.profileHash,\n contractJson: result.contractJson,\n contractDts: result.contractDts,\n });\n } catch (error) {\n onProgress?.({\n action: 'emit',\n kind: 'spanEnd',\n spanId: 'emit',\n outcome: 'error',\n });\n\n return notOk({\n code: 'EMIT_FAILED',\n summary: 'Failed to emit contract',\n why: error instanceof Error ? error.message : String(error),\n meta: undefined,\n });\n }\n }\n}\n"],"mappings":"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"}