@prisma-next/migration-tools 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.
Files changed (118) hide show
  1. package/README.md +4 -4
  2. package/dist/{errors-DGYwcwXs.mjs → errors-vFROOhCR.mjs} +46 -21
  3. package/dist/errors-vFROOhCR.mjs.map +1 -0
  4. package/dist/exports/aggregate.d.mts +328 -204
  5. package/dist/exports/aggregate.d.mts.map +1 -1
  6. package/dist/exports/aggregate.mjs +480 -243
  7. package/dist/exports/aggregate.mjs.map +1 -1
  8. package/dist/exports/errors.d.mts +2 -2
  9. package/dist/exports/errors.d.mts.map +1 -1
  10. package/dist/exports/errors.mjs +1 -1
  11. package/dist/exports/graph.d.mts +1 -1
  12. package/dist/exports/hash.d.mts +8 -9
  13. package/dist/exports/hash.d.mts.map +1 -1
  14. package/dist/exports/hash.mjs +1 -1
  15. package/dist/exports/invariants.d.mts +1 -1
  16. package/dist/exports/invariants.d.mts.map +1 -1
  17. package/dist/exports/invariants.mjs +1 -1
  18. package/dist/exports/io.d.mts +2 -83
  19. package/dist/exports/io.mjs +1 -1
  20. package/dist/exports/metadata.d.mts +2 -2
  21. package/dist/exports/migration-graph.d.mts +9 -2
  22. package/dist/exports/migration-graph.d.mts.map +1 -0
  23. package/dist/exports/migration-graph.mjs +3 -2
  24. package/dist/exports/migration-ts.d.mts.map +1 -1
  25. package/dist/exports/migration-ts.mjs.map +1 -1
  26. package/dist/exports/migration.d.mts +5 -6
  27. package/dist/exports/migration.d.mts.map +1 -1
  28. package/dist/exports/migration.mjs +14 -32
  29. package/dist/exports/migration.mjs.map +1 -1
  30. package/dist/exports/package.d.mts +1 -1
  31. package/dist/exports/ref-resolution.d.mts +2 -2
  32. package/dist/exports/ref-resolution.d.mts.map +1 -1
  33. package/dist/exports/ref-resolution.mjs +1 -1
  34. package/dist/exports/ref-resolution.mjs.map +1 -1
  35. package/dist/exports/refs.d.mts +15 -2
  36. package/dist/exports/refs.d.mts.map +1 -0
  37. package/dist/exports/refs.mjs +3 -2
  38. package/dist/exports/spaces.d.mts +31 -132
  39. package/dist/exports/spaces.d.mts.map +1 -1
  40. package/dist/exports/spaces.mjs +13 -9
  41. package/dist/exports/spaces.mjs.map +1 -1
  42. package/dist/{graph-BrLXqoUc.d.mts → graph-3dLMZp5l.d.mts} +1 -2
  43. package/dist/graph-3dLMZp5l.d.mts.map +1 -0
  44. package/dist/graph-membership-BV23F1IV.mjs +15 -0
  45. package/dist/graph-membership-BV23F1IV.mjs.map +1 -0
  46. package/dist/{hash-Cr4WIr4Z.mjs → hash--Y7vCpN3.mjs} +8 -9
  47. package/dist/hash--Y7vCpN3.mjs.map +1 -0
  48. package/dist/{invariants-0daYEzyo.mjs → invariants-C23nXy1c.mjs} +2 -2
  49. package/dist/{invariants-0daYEzyo.mjs.map → invariants-C23nXy1c.mjs.map} +1 -1
  50. package/dist/{io-BPLfzvZe.mjs → io-BGlPOt9b.mjs} +100 -13
  51. package/dist/io-BGlPOt9b.mjs.map +1 -0
  52. package/dist/io-BH4G3F-i.d.mts +124 -0
  53. package/dist/io-BH4G3F-i.d.mts.map +1 -0
  54. package/dist/metadata-Bp9X04gM.d.mts +2 -0
  55. package/dist/{migration-graph-nlS4TRpn.mjs → migration-graph-BMAqSfv9.mjs} +6 -26
  56. package/dist/migration-graph-BMAqSfv9.mjs.map +1 -0
  57. package/dist/{migration-graph-De0dUZoC.d.mts → migration-graph-CWEM2SLR.d.mts} +6 -6
  58. package/dist/migration-graph-CWEM2SLR.d.mts.map +1 -0
  59. package/dist/op-schema-D5qkXfEf.mjs.map +1 -1
  60. package/dist/{package-DZj8YvD0.d.mts → package-Ca-J_z_0.d.mts} +1 -1
  61. package/dist/package-Ca-J_z_0.d.mts.map +1 -0
  62. package/dist/{read-contract-space-contract-DRueB4Aa.mjs → read-contract-space-contract-TbeXuJXL.mjs} +32 -5
  63. package/dist/read-contract-space-contract-TbeXuJXL.mjs.map +1 -0
  64. package/dist/{refs-BDHo5l_g.mjs → refs-C-_WUrPw.mjs} +97 -4
  65. package/dist/refs-C-_WUrPw.mjs.map +1 -0
  66. package/dist/refs-C7wuYFqZ.d.mts +42 -0
  67. package/dist/refs-C7wuYFqZ.d.mts.map +1 -0
  68. package/dist/snapshot-Bazwo13S.mjs +137 -0
  69. package/dist/snapshot-Bazwo13S.mjs.map +1 -0
  70. package/dist/verify-contract-spaces-BdysZdQk.d.mts +132 -0
  71. package/dist/verify-contract-spaces-BdysZdQk.d.mts.map +1 -0
  72. package/package.json +18 -9
  73. package/src/aggregate/aggregate.ts +266 -0
  74. package/src/aggregate/check-integrity.ts +243 -0
  75. package/src/aggregate/loader.ts +161 -334
  76. package/src/aggregate/planner-types.ts +14 -14
  77. package/src/aggregate/planner.ts +20 -23
  78. package/src/aggregate/project-schema-to-space.ts +3 -8
  79. package/src/aggregate/strategies/graph-walk.ts +15 -10
  80. package/src/aggregate/strategies/synth.ts +4 -4
  81. package/src/aggregate/types.ts +81 -62
  82. package/src/aggregate/verifier.ts +23 -23
  83. package/src/assert-descriptor-self-consistency.ts +6 -0
  84. package/src/compute-extension-space-apply-path.ts +1 -1
  85. package/src/emit-contract-space-artefacts.ts +4 -3
  86. package/src/errors.ts +58 -2
  87. package/src/exports/aggregate.ts +29 -19
  88. package/src/exports/io.ts +2 -0
  89. package/src/exports/metadata.ts +1 -1
  90. package/src/exports/migration-graph.ts +1 -0
  91. package/src/exports/refs.ts +11 -0
  92. package/src/exports/spaces.ts +3 -0
  93. package/src/graph-membership.ts +17 -0
  94. package/src/graph.ts +0 -1
  95. package/src/hash.ts +7 -8
  96. package/src/integrity-violation.ts +114 -0
  97. package/src/io.ts +139 -14
  98. package/src/metadata.ts +1 -1
  99. package/src/migration-base.ts +10 -30
  100. package/src/migration-graph.ts +7 -35
  101. package/src/read-contract-space-head-ref.ts +5 -2
  102. package/src/refs/snapshot.ts +199 -0
  103. package/src/refs.ts +124 -1
  104. package/src/space-layout.ts +30 -0
  105. package/dist/errors-DGYwcwXs.mjs.map +0 -1
  106. package/dist/exports/io.d.mts.map +0 -1
  107. package/dist/graph-BrLXqoUc.d.mts.map +0 -1
  108. package/dist/hash-Cr4WIr4Z.mjs.map +0 -1
  109. package/dist/io-BPLfzvZe.mjs.map +0 -1
  110. package/dist/metadata-BFX0xdz8.d.mts +0 -2
  111. package/dist/migration-graph-De0dUZoC.d.mts.map +0 -1
  112. package/dist/migration-graph-nlS4TRpn.mjs.map +0 -1
  113. package/dist/package-DZj8YvD0.d.mts.map +0 -1
  114. package/dist/read-contract-space-contract-DRueB4Aa.mjs.map +0 -1
  115. package/dist/refs-BDHo5l_g.mjs.map +0 -1
  116. package/dist/refs-CDaNerhT.d.mts +0 -16
  117. package/dist/refs-CDaNerhT.d.mts.map +0 -1
  118. package/src/aggregate/extract-storage-element-names.ts +0 -75
@@ -1,220 +1,344 @@
1
- import { n as OnDiskMigrationPackage } from "../package-DZj8YvD0.mjs";
2
- import { n as MigrationGraph } from "../graph-BrLXqoUc.mjs";
3
- import { t as PathDecision } from "../migration-graph-De0dUZoC.mjs";
1
+ import { n as OnDiskMigrationPackage } from "../package-Ca-J_z_0.mjs";
2
+ import { i as Refs, r as RefLoadProblem } from "../refs-C7wuYFqZ.mjs";
3
+ import { t as ContractSpaceHeadRecord } from "../verify-contract-spaces-BdysZdQk.mjs";
4
+ import { n as MigrationGraph } from "../graph-3dLMZp5l.mjs";
5
+ import { t as PackageLoadProblem } from "../io-BH4G3F-i.mjs";
6
+ import { t as PathDecision } from "../migration-graph-CWEM2SLR.mjs";
4
7
  import { Result } from "@prisma-next/utils/result";
5
8
  import { ControlFamilyInstance, MigrationOperationPolicy, MigrationPlan, MigrationPlanOperation, MigrationPlannerConflict, TargetMigrationsCapability } from "@prisma-next/framework-components/control";
6
9
  import { Contract } from "@prisma-next/contract/types";
7
10
  import { TargetBoundComponentDescriptor } from "@prisma-next/framework-components/components";
8
11
 
9
- //#region src/aggregate/types.d.ts
12
+ //#region src/integrity-violation.d.ts
13
+ /**
14
+ * Every structural problem the migration model can carry.
15
+ *
16
+ * Violations come in three groups:
17
+ *
18
+ * - **Recoverable**: the package or space is retained in the model;
19
+ * the violation is surfaced for policy (report, refuse, or ignore
20
+ * depending on the command).
21
+ * - **Config/contract-dependent**: produced only when the matching
22
+ * `IntegrityQueryOptions` opt is set (declaredExtensions /
23
+ * checkContracts). The model is built without them; they surface
24
+ * when the caller explicitly asks for the broader integrity view.
25
+ * - **Unloadable**: the package is omitted from the model entirely
26
+ * (its on-disk content cannot be parsed into an `OnDiskMigrationPackage`).
27
+ *
28
+ * `checkIntegrity()` on `ContractSpaceAggregate` returns the full set —
29
+ * all violations across all spaces — never bailing at the first hit.
30
+ */
31
+ type IntegrityViolation = {
32
+ readonly kind: 'sameSourceAndTarget';
33
+ readonly spaceId: string;
34
+ readonly dirName: string;
35
+ readonly hash: string;
36
+ } | {
37
+ readonly kind: 'hashMismatch';
38
+ readonly spaceId: string;
39
+ readonly dirName: string;
40
+ readonly stored: string;
41
+ readonly computed: string;
42
+ } | {
43
+ readonly kind: 'providedInvariantsMismatch';
44
+ readonly spaceId: string;
45
+ readonly dirName: string;
46
+ } | {
47
+ readonly kind: 'headRefMissing';
48
+ readonly spaceId: string;
49
+ } | {
50
+ readonly kind: 'headRefNotInGraph';
51
+ readonly spaceId: string;
52
+ readonly hash: string;
53
+ } | {
54
+ readonly kind: 'duplicateMigrationHash';
55
+ readonly spaceId: string;
56
+ readonly migrationHash: string;
57
+ readonly dirNames: readonly string[];
58
+ } | {
59
+ readonly kind: 'refUnreadable';
60
+ readonly spaceId: string;
61
+ readonly refName: string;
62
+ readonly detail: string;
63
+ } | {
64
+ readonly kind: 'orphanSpaceDir';
65
+ readonly spaceId: string;
66
+ } | {
67
+ readonly kind: 'declaredButUnmigrated';
68
+ readonly spaceId: string;
69
+ } | {
70
+ readonly kind: 'targetMismatch';
71
+ readonly spaceId: string;
72
+ readonly expected: string;
73
+ readonly actual: string;
74
+ } | {
75
+ readonly kind: 'disjointness';
76
+ readonly element: string;
77
+ readonly claimedBy: readonly string[];
78
+ } | {
79
+ readonly kind: 'contractUnreadable';
80
+ readonly spaceId: string;
81
+ readonly detail: string;
82
+ } | {
83
+ readonly kind: 'packageUnloadable';
84
+ readonly spaceId: string;
85
+ readonly dirName: string;
86
+ readonly detail: string;
87
+ };
88
+ /**
89
+ * One declared extension entry, drawn from `Config.extensionPacks`.
90
+ *
91
+ * The integrity layer needs only:
92
+ *
93
+ * - `id` — the space id (also the directory name under `migrations/`),
94
+ * used for the layout-drift checks (`orphanSpaceDir` /
95
+ * `declaredButUnmigrated`).
96
+ * - `targetId` — the target the declaring extension was configured for.
97
+ *
98
+ * Typed structurally so the migration-tools layer stays framework-neutral.
99
+ */
100
+ interface DeclaredExtensionEntry {
101
+ readonly id: string;
102
+ readonly targetId: string;
103
+ }
10
104
  /**
11
- * Hydrated migration graph for a single contract space.
12
- *
13
- * `graph` is the structural shortest-path graph (forward / reverse chain,
14
- * deterministic tie-break order) reconstructed from a set of on-disk
15
- * migration packages. `packagesByMigrationHash` is the lookup table the
16
- * graph-walk strategy uses to resolve a path's edge sequence back to the
17
- * concrete `OnDiskMigrationPackage` (and therefore the operation list) for
18
- * apply.
19
- *
20
- * Eagerly hydrated by the loader. Once a `ContractSpaceAggregate` exists,
21
- * downstream consumers do **not** touch the filesystem to walk graphs or
22
- * resolve packages — the aggregate is the boundary.
105
+ * Options controlling which config/contract-dependent violation checks
106
+ * `checkIntegrity()` runs.
107
+ *
108
+ * Both opts default to disabled: a caller without the app contract or
109
+ * declared extensions still gets the structurally-derivable violations
110
+ * (hashMismatch, providedInvariantsMismatch, headRefMissing,
111
+ * headRefNotInGraph, refUnreadable, sameSourceAndTarget, packageUnloadable).
23
112
  */
24
- interface HydratedMigrationGraph {
25
- readonly graph: MigrationGraph;
26
- readonly packagesByMigrationHash: ReadonlyMap<string, OnDiskMigrationPackage>;
113
+ interface IntegrityQueryOptions {
114
+ /**
115
+ * When provided, enables layout-drift checks: `orphanSpaceDir`
116
+ * (a directory exists on disk for an extension not in the list) and
117
+ * `declaredButUnmigrated` (an extension in the list has no on-disk dir).
118
+ */
119
+ readonly declaredExtensions?: readonly DeclaredExtensionEntry[];
120
+ /**
121
+ * When true, enables contract/disjointness/target checks:
122
+ * `contractUnreadable`, `targetMismatch`, `disjointness`.
123
+ */
124
+ readonly checkContracts?: boolean;
125
+ }
126
+ //#endregion
127
+ //#region src/aggregate/types.d.ts
128
+ interface ContractAtOptions {
129
+ readonly refName?: string;
27
130
  }
131
+ type ContractAtResult = {
132
+ readonly provenance: 'snapshot';
133
+ readonly hash: string;
134
+ readonly contractJson: unknown;
135
+ readonly contractDts: string;
136
+ readonly contract: Contract;
137
+ } | {
138
+ readonly provenance: 'graph-node';
139
+ readonly sourceDir: string;
140
+ readonly hash: string;
141
+ readonly contractJson: unknown;
142
+ readonly contractDts: string;
143
+ readonly contract: Contract;
144
+ };
28
145
  /**
29
146
  * One contract space — app or extension — as a member of a
30
147
  * {@link ContractSpaceAggregate}. Every member has the same shape.
31
148
  *
149
+ * A member is a tolerant snapshot of one space's on-disk state, not a
150
+ * validated value: `packages` is the raw migration-package list as read
151
+ * from disk (a hash- or invariants-mismatched package is retained here;
152
+ * a genuinely unparseable one is omitted), and integrity is judged
153
+ * separately by {@link ContractSpaceAggregate.checkIntegrity}.
154
+ *
32
155
  * - `spaceId`: `'app'` for the application, otherwise the extension's
33
156
  * id (validated against `[a-z][a-z0-9_-]{0,63}`).
34
- * - `contract`: the validated contract value for this member. For the
35
- * app, the user's authored contract; for an extension, the on-disk
36
- * `migrations/<spaceId>/contract.json`. Both have already passed the
37
- * family's `deserializeContract` at the loader boundary.
38
- * - `headRef.hash`: the storage hash this member is targeting. For the
39
- * app, equals `contract.storage.storageHash`. For extensions, the
40
- * on-disk `refs/head.json.hash`.
41
- * - `headRef.invariants`: alphabetically sorted, deduplicated invariant
42
- * ids declared on the head ref. Empty for the app member (the app's
43
- * plan is synthesised from the contract IR, no invariants required).
44
- * - `migrations`: the hydrated migration graph for this space. Possibly
45
- * empty (an extension whose on-disk head ref points at the
46
- * empty-contract sentinel and ships no migrations yet, or the app
47
- * when the user hasn't authored any).
157
+ * - `packages`: raw on-disk migration packages, as read; never
158
+ * integrity-validated at load.
159
+ * - `refs`: the user-authored refs under `migrations/<spaceId>/refs/*.json`.
160
+ * - `headRef`: the system head ref read from
161
+ * `migrations/<spaceId>/refs/head.json`, or `null` when absent
162
+ * (represented as a `headRefMissing` violation, never fatal). The app
163
+ * member's head ref is always synthesised from its live contract's
164
+ * storage hash, so it is never `null`.
165
+ * - `graph()`: the migration graph this space's packages induce
166
+ * lazily reconstructed on first call and memoised. Pure structure: a
167
+ * `from === to` self-edge is represented, not rejected.
168
+ * - `contract()`: the deserialized contract for this member lazily
169
+ * produced on first call and memoised. For the app it is the live
170
+ * contract the caller supplied; for an extension it is the on-disk
171
+ * `migrations/<spaceId>/contract.json` run through the family's
172
+ * `deserializeContract`. Throws if the on-disk contract is missing or
173
+ * undeserializable (surfaced as `contractUnreadable` by `checkIntegrity`
174
+ * under `checkContracts`); callers gate before querying it.
175
+ * - `contractAt(hash, opts?)`: materializes the contract at an arbitrary
176
+ * graph node — when `opts.refName` is set, prefer the ref's paired
177
+ * snapshot; else find the package whose `metadata.to === hash` and read
178
+ * its `end-contract.*`. Lazy per `(hash, refName?)` memoisation; throws
179
+ * typed {@link MigrationToolsError} values compatible with CLI mappers.
48
180
  */
49
181
  interface ContractSpaceMember {
50
182
  readonly spaceId: string;
51
- readonly contract: Contract;
52
- readonly headRef: {
53
- readonly hash: string;
54
- readonly invariants: readonly string[];
55
- };
56
- readonly migrations: HydratedMigrationGraph;
183
+ readonly packages: readonly OnDiskMigrationPackage[];
184
+ readonly refs: Refs;
185
+ readonly headRef: ContractSpaceHeadRecord | null;
186
+ graph(): MigrationGraph;
187
+ contract(): Contract;
188
+ contractAt(hash: string, opts?: ContractAtOptions): Promise<ContractAtResult>;
57
189
  }
58
190
  /**
59
- * Typed value carrying the user's app contract plus every loaded
60
- * extension contract space, fully hydrated and internally consistent.
191
+ * Tolerant, queryable snapshot of a project's on-disk migration state:
192
+ * the app contract space plus every extension contract space, each a
193
+ * {@link ContractSpaceMember}.
61
194
  *
62
195
  * Produced once per CLI invocation by `loadContractSpaceAggregate`.
63
- * Every downstream component (planner, verifier, runner adapter)
64
- * consumes this value rather than rebuilding state from disk.
65
- *
66
- * Invariants the loader enforces at construction:
67
- *
68
- * 1. `targetId` is consistent across every member (`contract.target`
69
- * matches `aggregate.targetId`). The aggregate's `targetId` is the
70
- * `Config.adapter.targetId` value the loader was told to use.
71
- * 2. `aggregate.extensions` is sorted alphabetically by `spaceId`.
72
- * Mirrors {@link import('../concatenate-space-apply-inputs').concatenateSpaceApplyInputs}'s
73
- * extension ordering convention so downstream apply order matches
74
- * today's behaviour byte-for-byte.
75
- * 3. No two members claim the same storage element (table / type / etc.).
76
- * 4. For each extension member: `member.headRef.hash` is reachable from
77
- * the empty-contract sentinel in `member.migrations.graph` (or the
78
- * graph is empty and `member.headRef.hash === EMPTY_CONTRACT_HASH`).
79
- * 5. For the app member: `member.headRef.hash` equals
80
- * `member.contract.storage.storageHash`. The app's `migrations`
81
- * is hydrated from the user's authored `migrations/` (or empty if
82
- * none).
83
- *
84
- * The aggregate is **type-uniform** post-construction: app/extension
85
- * distinguishability survives only at the caller-policy layer
86
- * (`ignoreGraphFor: new Set([appSpaceId])`), not on member shape.
196
+ * Building the aggregate never throws on disk content; every consumer
197
+ * obtains spaces / packages / refs / graphs from this one value rather
198
+ * than re-deriving them from disk.
199
+ *
200
+ * - `targetId`: the app contract's target; every member is expected to
201
+ * share it (a mismatch surfaces as a `targetMismatch` violation under
202
+ * `checkContracts`).
203
+ * - `app` / `extensions`: retained as fields for the existing planner /
204
+ * verifier / runner consumers. `extensions` is sorted alphabetically
205
+ * by `spaceId` (the apply-ordering convention).
206
+ * - `listSpaces()` / `hasSpace()` / `space()` / `spaces()`: the query
207
+ * surface the read commands consume — `app` first, then extension ids
208
+ * lex-ascending.
209
+ * - `checkIntegrity()`: judges the loaded model and returns every
210
+ * violation (never bailing at the first). Config/contract-dependent
211
+ * checks run only when the matching {@link IntegrityQueryOptions} opt
212
+ * is set.
87
213
  */
88
214
  interface ContractSpaceAggregate {
89
215
  readonly targetId: string;
90
216
  readonly app: ContractSpaceMember;
91
217
  readonly extensions: readonly ContractSpaceMember[];
218
+ listSpaces(): readonly string[];
219
+ hasSpace(id: string): boolean;
220
+ space(id: string): ContractSpaceMember | undefined;
221
+ spaces(): readonly ContractSpaceMember[];
222
+ checkIntegrity(opts?: IntegrityQueryOptions): readonly IntegrityViolation[];
92
223
  }
93
224
  //#endregion
94
- //#region src/aggregate/loader.d.ts
225
+ //#region src/aggregate/aggregate.d.ts
95
226
  /**
96
- * Single declared extension entry the loader needs from `Config.extensionPacks`.
97
- *
98
- * Only the subset of fields the loader operates on:
99
- *
100
- * - `id` the space id (also the directory name under `migrations/`).
101
- * - `targetId` the configured `Config.adapter.targetId` value the
102
- * declaring extension declared. The loader rejects mismatches against
103
- * the aggregate's `targetId` with `targetMismatch`.
104
- *
105
- * Whether the descriptor declares a contract space is decided by whether
106
- * its corresponding `migrations/<id>/` directory exists on disk
107
- * (materialised by the seed phase before the loader runs); the loader
108
- * never reads the descriptor's `contractJson` itself. That makes the
109
- * aggregate's apply / verify paths byte-for-byte independent of the
110
- * descriptor module — `db verify` succeeds even if the descriptor's
111
- * `contractJson` is a throwing getter.
112
- *
113
- * Typed structurally so the migration-tools layer stays framework-neutral.
227
+ * Resolve a member's head ref, asserting it is present. The apply/verify
228
+ * engine only runs after `checkIntegrity` has refused on `headRefMissing`,
229
+ * so a member reaching the planner / verifier without a head ref is a
230
+ * programming error (the integrity gate was skipped), not a user-facing
231
+ * state. The app member's head ref is always synthesised, so this only
232
+ * ever guards an ungated extension space.
114
233
  */
115
- interface DeclaredExtensionEntry {
116
- readonly id: string;
117
- readonly targetId: string;
118
- }
234
+ declare function requireHeadRef(member: ContractSpaceMember): ContractSpaceHeadRecord;
119
235
  /**
120
- * Inputs for {@link loadContractSpaceAggregate}.
121
- *
122
- * The loader is the **sole** descriptor-import boundary in the M2.5
123
- * pipeline: callers gather the descriptor data (already-validated app
124
- * contract, declared extension entries) and pass it through. Once the
125
- * loader returns, no descriptor module is imported again for this
126
- * aggregate's lifetime.
236
+ * Build a {@link ContractSpaceMember} with lazily-memoised `graph()`,
237
+ * `contract()`, and `contractAt()` facets.
238
+ *
239
+ * `graph()` reconstructs the migration graph from `packages` on first
240
+ * call and caches it. `contract()` calls `resolveContract` on first call
241
+ * and caches the result; a throwing `resolveContract` (e.g. a missing or
242
+ * undeserializable on-disk contract) re-throws on each call rather than
243
+ * caching a value — `checkIntegrity` surfaces that as `contractUnreadable`.
244
+ * `contractAt()` materializes the contract at an arbitrary graph node with
245
+ * the same resolution order as plan-time ref resolution: ref snapshot first
246
+ * (when `opts.refName` is set), else the matching package's `end-contract.*`.
127
247
  */
128
- interface LoadAggregateInput {
248
+ declare function createContractSpaceMember(args: {
249
+ readonly spaceId: string;
250
+ readonly packages: readonly OnDiskMigrationPackage[];
251
+ readonly refs: Refs;
252
+ readonly headRef: ContractSpaceHeadRecord | null;
253
+ readonly refsDir: string;
254
+ readonly resolveContract: () => Contract;
255
+ readonly deserializeContract: (raw: unknown) => Contract;
256
+ }): ContractSpaceMember;
257
+ /**
258
+ * Assemble a {@link ContractSpaceAggregate} value from its members and a
259
+ * `checkIntegrity` implementation. The query methods (`listSpaces` /
260
+ * `hasSpace` / `space` / `spaces`) are derived here so every aggregate —
261
+ * loader-built or test-built — shares one query surface: `app` first,
262
+ * then `extensions` in the order supplied (the loader sorts them
263
+ * lex-ascending by `spaceId`).
264
+ */
265
+ declare function createContractSpaceAggregate(args: {
129
266
  readonly targetId: string;
130
- readonly migrationsDir: string;
131
- readonly appContract: Contract;
132
- readonly declaredExtensions: ReadonlyArray<DeclaredExtensionEntry>;
133
- readonly deserializeContract: (contractJson: unknown) => Contract;
267
+ readonly app: ContractSpaceMember;
268
+ readonly extensions: readonly ContractSpaceMember[];
269
+ readonly checkIntegrity: (opts?: IntegrityQueryOptions) => readonly IntegrityViolation[];
270
+ }): ContractSpaceAggregate;
271
+ //#endregion
272
+ //#region src/aggregate/check-integrity.d.ts
273
+ /**
274
+ * One space's load-time facts that `checkIntegrity` judges: the loaded
275
+ * member, the load-time problems `readMigrationsDir` surfaced for it, and
276
+ * whether it is the app space (the app head ref is synthesised, so the
277
+ * head-ref checks are skipped for it).
278
+ */
279
+ interface IntegritySpaceState {
280
+ readonly member: ContractSpaceMember;
281
+ readonly problems: readonly PackageLoadProblem[];
282
+ /** Per-ref problems: a user ref `*.json` that exists but is unparseable. */
283
+ readonly refProblems: readonly RefLoadProblem[];
134
284
  /**
135
- * Hydrated migration graph for the **app member**.
136
- *
137
- * The framework-neutral migration-tools layer doesn't know how to read
138
- * the user's authored `migrations/` directory (the app member's
139
- * migration-package layout is family-aware: ops.json shape, manifest
140
- * keys, etc.). Callers — the SQL family today — read the user's
141
- * `migrations/` and hand the resulting `OnDiskMigrationPackage[]` through.
142
- *
143
- * Passing `[]` is valid (greenfield project, no authored migrations).
144
- * Equivalent to `migrations/` not existing or being empty.
285
+ * The space's `refs/head.json` problem when it exists but is unparseable.
286
+ * `null` means the head ref was read cleanly or is genuinely absent —
287
+ * the absent case is judged `headRefMissing`, the corrupt case here is
288
+ * judged `refUnreadable` (and suppresses `headRefMissing`).
145
289
  */
146
- readonly appMigrationPackages: ReadonlyArray<OnDiskMigrationPackage>;
290
+ readonly headRefProblem: RefLoadProblem | null;
291
+ readonly isApp: boolean;
292
+ }
293
+ interface IntegrityComputationInput {
294
+ readonly targetId: string;
295
+ readonly spaces: readonly IntegritySpaceState[];
147
296
  }
148
297
  /**
149
- * Discriminated failure variants the loader emits.
150
- *
151
- * Every variant short-circuits at first hit; the loader does not keep
152
- * collecting after the first violation in any phase except for layout
153
- * (where every layout offence is bundled into one `layoutViolation`).
298
+ * Walk the loaded model and return **every** integrity violation — never
299
+ * bailing at the first. Structurally-derivable violations (load-time
300
+ * problems, self-edges, missing / unreachable head refs) are always
301
+ * produced; layout-drift checks require `declaredExtensions`, and
302
+ * contract / target / disjointness checks require `checkContracts`.
154
303
  */
155
- type LoadAggregateError = {
156
- readonly kind: 'layoutViolation';
157
- readonly violations: readonly LayoutViolation[];
158
- } | {
159
- readonly kind: 'integrityFailure';
160
- readonly spaceId: string;
161
- readonly detail: string;
162
- } | {
163
- readonly kind: 'validationFailure';
164
- readonly spaceId: string;
165
- readonly detail: string;
166
- } | {
167
- readonly kind: 'disjointnessViolation';
168
- readonly element: string;
169
- readonly claimedBy: readonly string[];
170
- } | {
171
- readonly kind: 'targetMismatch';
172
- readonly spaceId: string;
173
- readonly expected: string;
174
- readonly actual: string;
175
- };
304
+ declare function computeIntegrityViolations(input: IntegrityComputationInput, opts?: IntegrityQueryOptions): readonly IntegrityViolation[];
305
+ declare function loadProblemToViolation(spaceId: string, problem: PackageLoadProblem): IntegrityViolation;
306
+ //#endregion
307
+ //#region src/aggregate/loader.d.ts
176
308
  /**
177
- * Single layout violation; bundled into a `layoutViolation` error so
178
- * users see every layout offence at once rather than fixing them one
179
- * at a time across re-runs.
180
- *
181
- * - `declaredButUnmigrated`: extension declared in `extensionPacks` with
182
- * a `contractSpace` but no contract-space dir on disk. Remediation:
183
- * `prisma-next migrate`.
184
- * - `orphanSpaceDir`: contract-space dir under `migrations/` for an extension
185
- * not in `extensionPacks`. Remediation: remove the directory, or
186
- * re-add the extension to `extensionPacks`.
309
+ * Inputs for {@link loadContractSpaceAggregate}.
310
+ *
311
+ * Construction reads migration **state** from disk (`migrations/<space>/`
312
+ * packages + refs + head refs). The app's *live* contract is not a disk
313
+ * artefact in Prisma Next it is always compiled from the project's
314
+ * central contract, so the caller always has it and threads it in as
315
+ * `appContract`. `deserializeContract` is held and called lazily only for
316
+ * the on-disk extension contracts (`migrations/<ext>/contract.json`).
187
317
  */
188
- type LayoutViolation = {
189
- readonly kind: 'declaredButUnmigrated';
190
- readonly spaceId: string;
191
- } | {
192
- readonly kind: 'orphanSpaceDir';
193
- readonly spaceId: string;
194
- };
195
- type LoadAggregateOutput = Result<{
196
- readonly aggregate: ContractSpaceAggregate;
197
- }, LoadAggregateError>;
318
+ interface LoadAggregateInput {
319
+ readonly migrationsDir: string;
320
+ readonly deserializeContract: (raw: unknown) => Contract;
321
+ readonly appContract: Contract;
322
+ }
198
323
  /**
199
- * Hydrate a {@link ContractSpaceAggregate} from on-disk state and
200
- * the app contract value the caller supplies.
201
- *
202
- * The loader is the **only** descriptor-import boundary at apply /
203
- * verify time, but it intentionally does **not** read the extension
204
- * descriptor's `contractJson` value. Each extension space's contract
205
- * is read from its on-disk `migrations/<id>/contract.json` mirror; the
206
- * descriptor's role is exhausted by the seed phase that wrote that
207
- * mirror in the first place. The loader composes existing
208
- * migration-tools primitives layout precheck (via
209
- * {@link listContractSpaceDirectories}), integrity checks (via
210
- * {@link readMigrationsDir} / {@link readContractSpaceHeadRef} /
211
- * {@link readContractSpaceContract} / `deserializeContract`), and
212
- * disjointness into a single typed value.
213
- *
214
- * Failure semantics: every failure variant in {@link LoadAggregateError}
215
- * short-circuits the load.
324
+ * Build a tolerant, queryable {@link ContractSpaceAggregate} from on-disk
325
+ * migration state plus the caller's live app contract.
326
+ *
327
+ * Building **never throws on disk content**: a hash- or
328
+ * invariants-mismatched package is retained, an unparseable package is
329
+ * omitted, a missing extension head ref leaves `headRef: null`, and an
330
+ * unreadable on-disk contract defers its failure to `member.contract()`.
331
+ * Every such problem is judged by {@link ContractSpaceAggregate.checkIntegrity}
332
+ * rather than aborting the load. The only rejections are catastrophic I/O
333
+ * (a `migrations/` that exists but is unreadable for reasons other than
334
+ * absence).
335
+ *
336
+ * The app space's head ref is synthesised from the live contract's
337
+ * storage hash (the app contract is authored independently of the
338
+ * migration graph), and `app.contract()` returns the supplied contract.
339
+ * Extension spaces read their contract, refs, and head ref from disk.
216
340
  */
217
- declare function loadContractSpaceAggregate(input: LoadAggregateInput): Promise<LoadAggregateOutput>;
341
+ declare function loadContractSpaceAggregate(input: LoadAggregateInput): Promise<ContractSpaceAggregate>;
218
342
  //#endregion
219
343
  //#region src/aggregate/marker-types.d.ts
220
344
  /**
@@ -236,7 +360,7 @@ interface ContractMarkerRecordLike {
236
360
  //#endregion
237
361
  //#region src/aggregate/planner-types.d.ts
238
362
  /**
239
- * Caller-provided policy for {@link planAggregate}. Today this carries
363
+ * Caller-provided policy for {@link planMigration}. Today this carries
240
364
  * just one knob:
241
365
  *
242
366
  * - `ignoreGraphFor`: `Set<spaceId>`. For listed members, the planner
@@ -275,7 +399,7 @@ interface AggregateCurrentDBState {
275
399
  readonly schemaIntrospection: unknown;
276
400
  }
277
401
  /**
278
- * Inputs to {@link planAggregate}.
402
+ * Inputs to {@link planMigration}.
279
403
  *
280
404
  * The planner is target-agnostic but family-aware: per-member synth
281
405
  * delegates to the family's `createPlanner(familyInstance).plan(...)`,
@@ -284,11 +408,11 @@ interface AggregateCurrentDBState {
284
408
  * threaded through. (`frameworkComponents` is passed verbatim into
285
409
  * `planner.plan(...)` per ADR 212; the planner does not interpret it.)
286
410
  *
287
- * The aggregate planner does **not** receive a `targetId` separately —
411
+ * The planner does **not** receive a `targetId` separately —
288
412
  * it reads `aggregate.targetId` and stamps it onto every emitted
289
413
  * `MigrationPlan` from construction. No placeholder, no patch step.
290
414
  */
291
- interface AggregatePlannerInput<TFamilyId extends string, TTargetId extends string> {
415
+ interface PlannerInput<TFamilyId extends string, TTargetId extends string> {
292
416
  readonly aggregate: ContractSpaceAggregate;
293
417
  readonly currentDBState: AggregateCurrentDBState;
294
418
  readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;
@@ -298,7 +422,7 @@ interface AggregatePlannerInput<TFamilyId extends string, TTargetId extends stri
298
422
  readonly operationPolicy: MigrationOperationPolicy;
299
423
  }
300
424
  /**
301
- * Per-member output of the aggregate planner. The runner ingests this
425
+ * Per-member output of the planner. The runner ingests this
302
426
  * shape directly via a thin `toRunnerInput` adapter at the CLI.
303
427
  *
304
428
  * - `plan`: ready-to-execute `MigrationPlan` with `targetId` already
@@ -314,8 +438,8 @@ interface AggregatePlannerInput<TFamilyId extends string, TTargetId extends stri
314
438
  /**
315
439
  * Per-edge metadata for the chain assembled by the graph-walk
316
440
  * strategy. Lets `migrate` surface a per-migration `applied[]`
317
- * entry (preserving the single-space `migrationsApplied` count
318
- * semantics) without re-walking the graph.
441
+ * entry (preserving the `migrationsApplied` count semantics) without
442
+ * re-walking the graph.
319
443
  *
320
444
  * `synth`-produced plans leave this absent — synthesised plans don't
321
445
  * have authored edges to surface.
@@ -327,7 +451,7 @@ interface AggregateMigrationEdgeRef {
327
451
  readonly to: string;
328
452
  readonly operationCount: number;
329
453
  }
330
- interface AggregatePerSpacePlan {
454
+ interface PerSpacePlan {
331
455
  readonly plan: MigrationPlan;
332
456
  readonly displayOps: readonly MigrationPlanOperation[];
333
457
  readonly destinationContract: Contract;
@@ -349,24 +473,24 @@ interface AggregatePerSpacePlan {
349
473
  */
350
474
  readonly pathDecision?: PathDecision;
351
475
  }
352
- interface AggregatePlannerSuccess {
353
- readonly perSpace: ReadonlyMap<string, AggregatePerSpacePlan>;
476
+ interface PlannerSuccess {
477
+ readonly perSpace: ReadonlyMap<string, PerSpacePlan>;
354
478
  /**
355
479
  * `applyOrder` is the order the runner must walk per-space inputs.
356
480
  * Mirrors the existing `concatenateSpaceApplyInputs` convention:
357
481
  * extensions alphabetically by `spaceId`, then the app. Tests assert
358
- * on `MultiSpaceRunnerFailure.failingSpace`, which is positional in
482
+ * on `MigrationRunnerFailure.failingSpace`, which is positional in
359
483
  * the runner's input array — preserving the literal ordering keeps
360
484
  * `failingSpace` attribution byte-for-byte.
361
485
  */
362
486
  readonly applyOrder: readonly string[];
363
487
  }
364
488
  /**
365
- * Discriminated failure variants for {@link planAggregate}. Each
489
+ * Discriminated failure variants for {@link planMigration}. Each
366
490
  * variant short-circuits the plan; per-member errors carry the
367
491
  * `spaceId` so the CLI can surface a precise envelope.
368
492
  */
369
- type AggregatePlannerError = {
493
+ type PlannerError = {
370
494
  readonly kind: 'extensionPathUnreachable';
371
495
  readonly spaceId: string;
372
496
  readonly target: string;
@@ -383,7 +507,7 @@ type AggregatePlannerError = {
383
507
  readonly spaceId: string;
384
508
  readonly detail: string;
385
509
  };
386
- type AggregatePlannerOutput = Result<AggregatePlannerSuccess, AggregatePlannerError>;
510
+ type PlannerOutput = Result<PlannerSuccess, PlannerError>;
387
511
  //#endregion
388
512
  //#region src/aggregate/planner.d.ts
389
513
  /**
@@ -394,7 +518,7 @@ type AggregatePlannerOutput = Result<AggregatePlannerSuccess, AggregatePlannerEr
394
518
  * 1. If `callerPolicy.ignoreGraphFor.has(member.spaceId)`:
395
519
  * - If `member.headRef.invariants` is empty → synth.
396
520
  * - Else → `policyConflict` (synth cannot satisfy authored invariants).
397
- * 2. Else if `member.migrations.graph` is non-empty AND graph-walk
521
+ * 2. Else if `member.graph()` is non-empty AND graph-walk
398
522
  * succeeds → graph-walk.
399
523
  * 3. Else if `member.headRef.invariants` is empty → synth.
400
524
  * 4. Else → graph-walk failure → `extensionPathUnreachable` /
@@ -403,12 +527,12 @@ type AggregatePlannerOutput = Result<AggregatePlannerSuccess, AggregatePlannerEr
403
527
  * Output `applyOrder` is `[...aggregate.extensions.map(spaceId), aggregate.app.spaceId]`
404
528
  * — extensions alphabetical, then app — matching today's
405
529
  * `concatenateSpaceApplyInputs` ordering. This preserves
406
- * `MultiSpaceRunnerFailure.failingSpace` attribution byte-for-byte.
530
+ * `MigrationRunnerFailure.failingSpace` attribution byte-for-byte.
407
531
  *
408
532
  * Every emitted `MigrationPlan` has `targetId = aggregate.targetId`.
409
533
  * No placeholder cast; no patch step.
410
534
  */
411
- declare function planAggregate<TFamilyId extends string, TTargetId extends string>(input: AggregatePlannerInput<TFamilyId, TTargetId>): Promise<AggregatePlannerOutput>;
535
+ declare function planMigration<TFamilyId extends string, TTargetId extends string>(input: PlannerInput<TFamilyId, TTargetId>): Promise<PlannerOutput>;
412
536
  //#endregion
413
537
  //#region src/aggregate/project-schema-to-space.d.ts
414
538
  /**
@@ -471,14 +595,14 @@ declare function projectSchemaToSpace(schema: unknown, member: ContractSpaceMemb
471
595
  /**
472
596
  * Outcome variants for the graph-walk strategy. Mirrors
473
597
  * {@link import('../../compute-extension-space-apply-path').ExtensionSpaceApplyPathOutcome}
474
- * but operates against the **already-hydrated** `member.migrations.graph`
598
+ * but operates against the member's lazily-reconstructed `graph()`
475
599
  * instead of re-reading from disk. The aggregate planner converts
476
- * these into {@link import('../planner-types').AggregatePlannerError}
600
+ * these into {@link import('../planner-types').PlannerError}
477
601
  * variants.
478
602
  */
479
603
  type GraphWalkOutcome = {
480
604
  readonly kind: 'ok';
481
- readonly result: AggregatePerSpacePlan;
605
+ readonly result: PerSpacePlan;
482
606
  } | {
483
607
  readonly kind: 'unreachable';
484
608
  } | {
@@ -516,12 +640,12 @@ declare function graphWalkStrategy(input: GraphWalkStrategyInputs): GraphWalkOut
516
640
  //#endregion
517
641
  //#region src/aggregate/verifier.d.ts
518
642
  /**
519
- * Caller policy for the aggregate verifier. Today's only knob is
643
+ * Caller policy for the verifier. Today's only knob is
520
644
  * `mode`: `strict` treats orphan elements (live tables not claimed by
521
645
  * any aggregate member) as errors; `lenient` treats them as
522
646
  * informational. Maps directly to `db verify --strict`.
523
647
  */
524
- interface AggregateVerifierInput<TSchemaResult> {
648
+ interface VerifierInput<TSchemaResult> {
525
649
  readonly aggregate: ContractSpaceAggregate;
526
650
  readonly markersBySpaceId: ReadonlyMap<string, ContractMarkerRecordLike | null>;
527
651
  readonly schemaIntrospection: unknown;
@@ -529,7 +653,7 @@ interface AggregateVerifierInput<TSchemaResult> {
529
653
  /**
530
654
  * Caller-supplied per-space schema verifier. The CLI wires this to
531
655
  * the family's `verifySqlSchema` (SQL) / equivalent (other
532
- * families). The aggregate verifier projects the schema to the
656
+ * families). The verifier projects the schema to the
533
657
  * member's slice via {@link projectSchemaToSpace} before invoking
534
658
  * the callback, so single-contract semantics are preserved.
535
659
  *
@@ -567,7 +691,7 @@ interface MarkerCheckSection {
567
691
  }
568
692
  /**
569
693
  * A live storage element (today: a top-level table) not claimed by any
570
- * member of the aggregate. The aggregate verifier always reports these;
694
+ * member of the aggregate. The verifier always reports these;
571
695
  * the caller decides what to do — `db verify --strict` treats them as
572
696
  * errors, the lenient default treats them as informational.
573
697
  *
@@ -587,15 +711,15 @@ interface SchemaCheckSection<TSchemaResult> {
587
711
  */
588
712
  readonly orphanElements: readonly OrphanElement[];
589
713
  }
590
- interface AggregateVerifierSuccess<TSchemaResult> {
714
+ interface VerifierSuccess<TSchemaResult> {
591
715
  readonly markerCheck: MarkerCheckSection;
592
716
  readonly schemaCheck: SchemaCheckSection<TSchemaResult>;
593
717
  }
594
- type AggregateVerifierError = {
718
+ type VerifierError = {
595
719
  readonly kind: 'introspectionFailure';
596
720
  readonly detail: string;
597
721
  };
598
- type AggregateVerifierOutput<TSchemaResult> = Result<AggregateVerifierSuccess<TSchemaResult>, AggregateVerifierError>;
722
+ type VerifierOutput<TSchemaResult> = Result<VerifierSuccess<TSchemaResult>, VerifierError>;
599
723
  /**
600
724
  * Verify a {@link ContractSpaceAggregate} against the live database
601
725
  * state. Bundles two checks:
@@ -618,7 +742,7 @@ type AggregateVerifierOutput<TSchemaResult> = Result<AggregateVerifierSuccess<TS
618
742
  * Pure synchronous function; no I/O. The caller (CLI) gathers
619
743
  * `markersBySpaceId` and `schemaIntrospection` ahead of the call.
620
744
  */
621
- declare function verifyAggregate<TSchemaResult>(input: AggregateVerifierInput<TSchemaResult>): AggregateVerifierOutput<TSchemaResult>;
745
+ declare function verifyMigration<TSchemaResult>(input: VerifierInput<TSchemaResult>): VerifierOutput<TSchemaResult>;
622
746
  //#endregion
623
- export { type AggregateCurrentDBState, type AggregateMigrationEdgeRef, type AggregatePerSpacePlan, type AggregatePlannerError, type AggregatePlannerInput, type AggregatePlannerOutput, type AggregatePlannerSuccess, type AggregateVerifierError, type AggregateVerifierInput, type AggregateVerifierOutput, type AggregateVerifierSuccess, type CallerPolicy, type ContractMarkerRecordLike, type ContractSpaceAggregate, type ContractSpaceMember, type DeclaredExtensionEntry, type GraphWalkOutcome, type GraphWalkStrategyInputs, type HydratedMigrationGraph, type LayoutViolation, type LoadAggregateError, type LoadAggregateInput, type LoadAggregateOutput, type MarkerCheckResult, type MarkerCheckSection, type OrphanElement, type SchemaCheckSection, graphWalkStrategy, loadContractSpaceAggregate, planAggregate, projectSchemaToSpace, verifyAggregate };
747
+ export { type AggregateCurrentDBState, type AggregateMigrationEdgeRef, type CallerPolicy, type ContractAtOptions, type ContractAtResult, type ContractMarkerRecordLike, type ContractSpaceAggregate, type ContractSpaceMember, type DeclaredExtensionEntry, type GraphWalkOutcome, type GraphWalkStrategyInputs, type IntegrityComputationInput, type IntegrityQueryOptions, type IntegritySpaceState, type IntegrityViolation, type LoadAggregateInput, type MarkerCheckResult, type MarkerCheckSection, type OrphanElement, type PerSpacePlan, type PlannerError, type PlannerInput, type PlannerOutput, type PlannerSuccess, type SchemaCheckSection, type VerifierError, type VerifierInput, type VerifierOutput, type VerifierSuccess, computeIntegrityViolations, createContractSpaceAggregate, createContractSpaceMember, graphWalkStrategy, loadContractSpaceAggregate, loadProblemToViolation, planMigration, projectSchemaToSpace, requireHeadRef, verifyMigration };
624
748
  //# sourceMappingURL=aggregate.d.mts.map