@prisma-next/cli 0.14.0-dev.39 → 0.14.0-dev.40

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 (80) hide show
  1. package/dist/cli.mjs +8 -8
  2. package/dist/{client-VA66WgBx.mjs → client-CWTNqJdj.mjs} +68 -67
  3. package/dist/client-CWTNqJdj.mjs.map +1 -0
  4. package/dist/commands/contract-infer.mjs +1 -1
  5. package/dist/commands/db-init.mjs +2 -2
  6. package/dist/commands/db-schema.mjs +2 -2
  7. package/dist/commands/db-sign.mjs +2 -2
  8. package/dist/commands/db-update.mjs +2 -2
  9. package/dist/commands/db-verify.mjs +1 -1
  10. package/dist/commands/migrate.d.mts +1 -1
  11. package/dist/commands/migrate.d.mts.map +1 -1
  12. package/dist/commands/migrate.mjs +31 -31
  13. package/dist/commands/migrate.mjs.map +1 -1
  14. package/dist/commands/migration-check.d.mts +1 -1
  15. package/dist/commands/migration-check.mjs +1 -1
  16. package/dist/commands/migration-graph.d.mts +1 -1
  17. package/dist/commands/migration-graph.mjs +5 -5
  18. package/dist/commands/migration-graph.mjs.map +1 -1
  19. package/dist/commands/migration-list.d.mts +6 -6
  20. package/dist/commands/migration-list.d.mts.map +1 -1
  21. package/dist/commands/migration-list.mjs +1 -1
  22. package/dist/commands/migration-log.d.mts +1 -1
  23. package/dist/commands/migration-log.mjs +1 -1
  24. package/dist/commands/migration-plan.mjs +1 -1
  25. package/dist/commands/migration-show.mjs +1 -1
  26. package/dist/commands/migration-status.d.mts +1 -1
  27. package/dist/commands/migration-status.mjs +1 -1
  28. package/dist/commands/ref.d.mts +1 -1
  29. package/dist/{contract-infer-Bmq_szvX.mjs → contract-infer-B__56PGb.mjs} +2 -2
  30. package/dist/{contract-infer-Bmq_szvX.mjs.map → contract-infer-B__56PGb.mjs.map} +1 -1
  31. package/dist/{db-verify-r157atLp.mjs → db-verify-DlqHvkkA.mjs} +65 -48
  32. package/dist/db-verify-DlqHvkkA.mjs.map +1 -0
  33. package/dist/exports/control-api.d.mts +3 -3
  34. package/dist/exports/control-api.mjs +1 -1
  35. package/dist/extension-pack-inputs-1ySHqxKG.mjs.map +1 -1
  36. package/dist/{inspect-live-schema-DBF3Rx17.mjs → inspect-live-schema-Dd1Ft0dw.mjs} +2 -2
  37. package/dist/{inspect-live-schema-DBF3Rx17.mjs.map → inspect-live-schema-Dd1Ft0dw.mjs.map} +1 -1
  38. package/dist/{migration-check-CTcJfCC4.mjs → migration-check-UoHMvsP3.mjs} +6 -6
  39. package/dist/migration-check-UoHMvsP3.mjs.map +1 -0
  40. package/dist/{migration-command-scaffold-bxooJ8Jl.mjs → migration-command-scaffold-DW2BpjZW.mjs} +2 -2
  41. package/dist/{migration-command-scaffold-bxooJ8Jl.mjs.map → migration-command-scaffold-DW2BpjZW.mjs.map} +1 -1
  42. package/dist/{migration-list-DVHVmMrf.mjs → migration-list-BOzQzJK4.mjs} +12 -12
  43. package/dist/migration-list-BOzQzJK4.mjs.map +1 -0
  44. package/dist/{migration-log-CBYftWK1.mjs → migration-log-q50rqMiU.mjs} +2 -2
  45. package/dist/{migration-log-CBYftWK1.mjs.map → migration-log-q50rqMiU.mjs.map} +1 -1
  46. package/dist/{migration-plan-o2aqjnfd.mjs → migration-plan-BwJLPkwV.mjs} +19 -19
  47. package/dist/migration-plan-BwJLPkwV.mjs.map +1 -0
  48. package/dist/{migration-status-BLa0SheI.mjs → migration-status-27NzO2SC.mjs} +10 -10
  49. package/dist/migration-status-27NzO2SC.mjs.map +1 -0
  50. package/dist/{types-qETCCNbe.d.mts → types-CKgJmH02.d.mts} +25 -19
  51. package/dist/types-CKgJmH02.d.mts.map +1 -0
  52. package/dist/{verify-CLw3YRbl.mjs → verify-DC14_nPu.mjs} +23 -5
  53. package/dist/verify-DC14_nPu.mjs.map +1 -0
  54. package/package.json +21 -21
  55. package/src/commands/db-verify.ts +31 -19
  56. package/src/commands/migrate.ts +44 -41
  57. package/src/commands/migration-check.ts +5 -5
  58. package/src/commands/migration-graph.ts +4 -4
  59. package/src/commands/migration-list.ts +12 -12
  60. package/src/commands/migration-plan.ts +3 -3
  61. package/src/commands/migration-status.ts +9 -9
  62. package/src/control-api/operations/db-init.ts +2 -2
  63. package/src/control-api/operations/db-run.ts +7 -7
  64. package/src/control-api/operations/db-verify.ts +37 -33
  65. package/src/control-api/operations/migrate.ts +66 -66
  66. package/src/control-api/operations/run-migration.ts +2 -2
  67. package/src/control-api/types.ts +7 -7
  68. package/src/utils/combine-verify-results.ts +113 -0
  69. package/src/utils/extension-pack-inputs.ts +1 -1
  70. package/src/utils/formatters/verify.ts +36 -3
  71. package/src/utils/plan-resolution.ts +18 -18
  72. package/dist/client-VA66WgBx.mjs.map +0 -1
  73. package/dist/db-verify-r157atLp.mjs.map +0 -1
  74. package/dist/migration-check-CTcJfCC4.mjs.map +0 -1
  75. package/dist/migration-list-DVHVmMrf.mjs.map +0 -1
  76. package/dist/migration-plan-o2aqjnfd.mjs.map +0 -1
  77. package/dist/migration-status-BLa0SheI.mjs.map +0 -1
  78. package/dist/types-qETCCNbe.d.mts.map +0 -1
  79. package/dist/verify-CLw3YRbl.mjs.map +0 -1
  80. package/src/utils/combine-schema-results.ts +0 -87
@@ -2,7 +2,10 @@ import { readFile } from 'node:fs/promises';
2
2
  import { loadConfig } from '@prisma-next/config-loader';
3
3
  import type { Contract } from '@prisma-next/contract/types';
4
4
  import { createControlStack } from '@prisma-next/framework-components/control';
5
- import { type ContractSpaceMember, requireHeadRef } from '@prisma-next/migration-tools/aggregate';
5
+ import {
6
+ type AggregateContractSpace,
7
+ requireHeadRef,
8
+ } from '@prisma-next/migration-tools/aggregate';
6
9
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
7
10
  import { errorUnknownInvariant, MigrationToolsError } from '@prisma-next/migration-tools/errors';
8
11
  import { findLatestMigration, isGraphNode } from '@prisma-next/migration-tools/migration-graph';
@@ -13,7 +16,7 @@ import { ifDefined } from '@prisma-next/utils/defined';
13
16
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
14
17
  import { Command } from 'commander';
15
18
  import { createControlClient } from '../control-api/client';
16
- import { planMemberPath } from '../control-api/operations/migrate';
19
+ import { planSpacePath } from '../control-api/operations/migrate';
17
20
  import type {
18
21
  MigrateFailure,
19
22
  MigratePathDecision,
@@ -149,7 +152,7 @@ export interface MigrateResult {
149
152
  * Computes the path through the SAME seam as `executeMigrate`:
150
153
  * - `readAllMarkers()` for the from-state (when no `--from` is given), preserving
151
154
  * the full marker including `invariants` (not just `storageHash`).
152
- * - `planMemberPath()` (shared with `executeMigrate`) for per-member path selection,
155
+ * - `planSpacePath()` (shared with `executeMigrate`) for per-space path selection,
153
156
  * which feeds `graphWalkStrategy()` with the same target hash, target invariants,
154
157
  * and current marker as the real apply path uses.
155
158
  *
@@ -203,7 +206,7 @@ async function executeMigrateShowCommand(
203
206
  const appGraph = aggregate.app.graph();
204
207
 
205
208
  // Resolve the --to target (defaults to the on-disk contract, same as migrate).
206
- // Also capture the ref's invariants so planMemberPath feeds graphWalkStrategy the
209
+ // Also capture the ref's invariants so planSpacePath feeds graphWalkStrategy the
207
210
  // same target invariants that real migrate would use (refInvariants ?? headRef.invariants).
208
211
  let targetHash: string = contractHash;
209
212
  let refInvariants: readonly string[] | undefined;
@@ -258,13 +261,13 @@ async function executeMigrateShowCommand(
258
261
  // - Explicit --from: parse it offline (no connection).
259
262
  // - Omitted: read the live DB marker via readAllMarkers() — the same source migrate uses.
260
263
  //
261
- // Full marker records (storageHash + invariants) are preserved so planMemberPath
264
+ // Full marker records (storageHash + invariants) are preserved so planSpacePath
262
265
  // can feed graphWalkStrategy the complete currentMarker — exactly as executeMigrate
263
266
  // does via familyInstance.readAllMarkers(). A stripped { storageHash, invariants: [] }
264
267
  // marker would produce a different `required` set and a different (incorrect) path.
265
268
  type LiveMarker = { readonly storageHash: string; readonly invariants: readonly string[] };
266
269
  const markerBySpace = new Map<string, LiveMarker | null>();
267
- const allMembers: ReadonlyArray<ContractSpaceMember> = [aggregate.app, ...aggregate.extensions];
270
+ const allSpaces: ReadonlyArray<AggregateContractSpace> = [aggregate.app, ...aggregate.extensions];
268
271
 
269
272
  if (hasExplicitFrom) {
270
273
  // @db with explicit --from requires a connection
@@ -301,7 +304,7 @@ async function executeMigrateShowCommand(
301
304
  } else {
302
305
  // Offline hypothetical: the --from ref only carries a hash (no live invariants).
303
306
  // Apply the from-hash marker to the APP space only. Extension spaces are left
304
- // absent from markerBySpace (treated as null / greenfield by planMemberPath),
307
+ // absent from markerBySpace (treated as null / greenfield by planSpacePath),
305
308
  // so they plan from their own marker → own head — exactly as executeMigrate does.
306
309
  const fromHash = fromResult.value.hash;
307
310
  const offlineMarker: LiveMarker | null =
@@ -334,9 +337,9 @@ async function executeMigrateShowCommand(
334
337
  const allMarkers = await client.readAllMarkers();
335
338
  // Store the full marker record (storageHash + invariants) per space.
336
339
  // This is the same data executeMigrate uses via familyInstance.readAllMarkers().
337
- for (const member of allMembers) {
338
- const marker = allMarkers.get(member.spaceId);
339
- markerBySpace.set(member.spaceId, marker ?? null);
340
+ for (const space of allSpaces) {
341
+ const marker = allMarkers.get(space.spaceId);
342
+ markerBySpace.set(space.spaceId, marker ?? null);
340
343
  }
341
344
  } catch (error) {
342
345
  if (CliStructuredError.is(error)) {
@@ -355,35 +358,35 @@ async function executeMigrateShowCommand(
355
358
  }
356
359
  }
357
360
 
358
- // Walk the path via planMemberPath — the same helper executeMigrate uses.
359
- // planMemberPath feeds graphWalkStrategy identical inputs (targetHash, targetInvariants,
361
+ // Walk the path via planSpacePath — the same helper executeMigrate uses.
362
+ // planSpacePath feeds graphWalkStrategy identical inputs (targetHash, targetInvariants,
360
363
  // currentMarker with full invariants), so the preview path is always the path migrate runs.
361
364
  //
362
365
  // Canonical schedule order: extensions alphabetically first, then app — mirroring the
363
366
  // runner's `applyOrder` in operations/migrate.ts so the "Will run, in order:" list
364
367
  // reflects the actual execution sequence (extensions install first, app last).
365
- const canonicalOrderMembers: ReadonlyArray<ContractSpaceMember> = [
368
+ const canonicalOrderSpaces: ReadonlyArray<AggregateContractSpace> = [
366
369
  ...aggregate.extensions,
367
370
  aggregate.app,
368
371
  ];
369
372
  const orderedMigrations: MigrateShowMigration[] = [];
370
- for (const member of canonicalOrderMembers) {
371
- const isAppMember = member.spaceId === aggregate.app.spaceId;
372
- const headRef = requireHeadRef(member);
373
- const memberTargetHash = isAppMember ? targetHash : headRef.hash;
374
- const memberRefInvariants = isAppMember ? refInvariants : undefined;
375
- const liveMarker = markerBySpace.get(member.spaceId) ?? null;
376
-
377
- const outcome = planMemberPath({
378
- member,
373
+ for (const space of canonicalOrderSpaces) {
374
+ const isAppSpace = space.spaceId === aggregate.app.spaceId;
375
+ const headRef = requireHeadRef(space);
376
+ const spaceTargetHash = isAppSpace ? targetHash : headRef.hash;
377
+ const spaceRefInvariants = isAppSpace ? refInvariants : undefined;
378
+ const liveMarker = markerBySpace.get(space.spaceId) ?? null;
379
+
380
+ const outcome = planSpacePath({
381
+ space,
379
382
  aggregate,
380
- targetHash: memberTargetHash,
381
- refInvariants: memberRefInvariants,
383
+ targetHash: spaceTargetHash,
384
+ refInvariants: spaceRefInvariants,
382
385
  liveMarker,
383
386
  });
384
387
 
385
388
  if (outcome.kind === 'at-head') {
386
- // Empty-graph member already at target — nothing to run for this space.
389
+ // Empty-graph space already at target — nothing to run for this space.
387
390
  continue;
388
391
  }
389
392
  if (outcome.kind === 'never-planned') {
@@ -417,7 +420,7 @@ async function executeMigrateShowCommand(
417
420
 
418
421
  for (const edge of outcome.plan.migrationEdges) {
419
422
  orderedMigrations.push({
420
- spaceId: member.spaceId,
423
+ spaceId: space.spaceId,
421
424
  dirName: edge.dirName,
422
425
  migrationHash: edge.migrationHash,
423
426
  from: edge.from,
@@ -445,12 +448,12 @@ async function executeMigrateShowCommand(
445
448
  // before rendering. This ensures the name column, hash column, and ops column
446
449
  // start at the same horizontal offset across every space section AND the
447
450
  // "Will run, in order:" list below.
448
- const memberLayouts = allMembers.map((member) => {
449
- const isApp = member.spaceId === aggregate.app.spaceId;
450
- const memberGraph = member.graph();
451
- const rowModel = buildMigrationGraphRows(memberGraph, isApp ? { contractHash } : {});
451
+ const spaceLayouts = allSpaces.map((space) => {
452
+ const isApp = space.spaceId === aggregate.app.spaceId;
453
+ const spaceGraph = space.graph();
454
+ const rowModel = buildMigrationGraphRows(spaceGraph, isApp ? { contractHash } : {});
452
455
  const edgeAnnotations = new Map<string, MigrationEdgeAnnotation>();
453
- for (const edge of memberGraph.migrationByHash.values()) {
456
+ for (const edge of spaceGraph.migrationByHash.values()) {
454
457
  edgeAnnotations.set(edge.migrationHash, {
455
458
  pathHighlight: onPathHashes.has(edge.migrationHash) ? 'on-path' : 'off-path',
456
459
  });
@@ -459,17 +462,17 @@ async function executeMigrateShowCommand(
459
462
  // green/continuous; off-path lanes dim. Rows, gutter, and labels all come
460
463
  // from this one grid.
461
464
  const grid = buildGrid(rowModel, {}, highlightFromEdgeAnnotations(edgeAnnotations));
462
- return { member, isApp, memberGraph, rowModel, grid, edgeAnnotations };
465
+ return { space, isApp, spaceGraph, rowModel, grid, edgeAnnotations };
463
466
  });
464
467
 
465
468
  // Global max across all space grids so every section's labels share columns.
466
469
  const globalLabelColumn =
467
- memberLayouts.length > 1
468
- ? Math.max(...memberLayouts.map(({ grid }) => computeLabelColumn(grid, 'unicode')))
470
+ spaceLayouts.length > 1
471
+ ? Math.max(...spaceLayouts.map(({ grid }) => computeLabelColumn(grid, 'unicode')))
469
472
  : undefined;
470
473
  const globalMaxDirNameWidthFromLayouts =
471
- memberLayouts.length > 1
472
- ? Math.max(...memberLayouts.map(({ rowModel }) => computeMaxDirNameWidth(rowModel)))
474
+ spaceLayouts.length > 1
475
+ ? Math.max(...spaceLayouts.map(({ rowModel }) => computeMaxDirNameWidth(rowModel)))
473
476
  : undefined;
474
477
  // The run-list name column width must be at least as wide as the global tree dirName
475
478
  // width so that tree sections and the list align at the hash column.
@@ -485,10 +488,10 @@ async function executeMigrateShowCommand(
485
488
  runListLeftPad = globalLabelColumn;
486
489
 
487
490
  // Render each space section with globally computed widths.
488
- const showSpaceHeadings = allMembers.length > 1;
491
+ const showSpaceHeadings = allSpaces.length > 1;
489
492
  const sections: string[] = [];
490
- for (const { member, isApp, rowModel, grid, edgeAnnotations } of memberLayouts) {
491
- const liveMarker = markerBySpace.get(member.spaceId) ?? null;
493
+ for (const { space, isApp, rowModel, grid, edgeAnnotations } of spaceLayouts) {
494
+ const liveMarker = markerBySpace.get(space.spaceId) ?? null;
492
495
  const liveMarkerHash = liveMarker?.storageHash ?? EMPTY_CONTRACT_HASH;
493
496
  const tree = renderMigrationGraphCommand({
494
497
  grid,
@@ -496,7 +499,7 @@ async function executeMigrateShowCommand(
496
499
  contractHash,
497
500
  isAppSpace: isApp,
498
501
  ...(needsLiveMarker ? { dbHash: liveMarkerHash } : {}),
499
- refsByHash: listRefsByContractHash(member),
502
+ refsByHash: listRefsByContractHash(space),
500
503
  edgeAnnotationsByHash: edgeAnnotations,
501
504
  colorize,
502
505
  glyphMode: 'unicode',
@@ -505,7 +508,7 @@ async function executeMigrateShowCommand(
505
508
  });
506
509
  if (tree.length === 0) continue;
507
510
  if (showSpaceHeadings) {
508
- sections.push(`${member.spaceId}:\n${indentMigrationGraphTreeBlock(tree, ' ')}`);
511
+ sections.push(`${space.spaceId}:\n${indentMigrationGraphTreeBlock(tree, ' ')}`);
509
512
  } else {
510
513
  sections.push(tree);
511
514
  }
@@ -156,16 +156,16 @@ export async function enumerateCheckSpaces(
156
156
  candidateDirs.filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name)).filter(isValidSpaceId),
157
157
  );
158
158
  const spaces: CheckSpace[] = [];
159
- for (const member of aggregate.spaces()) {
160
- const spaceId = member.spaceId;
159
+ for (const space of aggregate.spaces()) {
160
+ const spaceId = space.spaceId;
161
161
  if (!isValidSpaceId(spaceId)) continue;
162
162
  if (!onDiskSpaceIds.has(spaceId)) continue;
163
163
  const migrationsDir = spaceMigrationDirectory(projectMigrationsDir, spaceId);
164
164
  spaces.push({
165
165
  spaceId,
166
- packages: member.packages,
167
- refs: member.refs,
168
- graph: member.graph(),
166
+ packages: space.packages,
167
+ refs: space.refs,
168
+ graph: space.graph(),
169
169
  migrationsDir,
170
170
  refsDir: spaceRefsDirectory(migrationsDir),
171
171
  });
@@ -154,13 +154,13 @@ export async function executeMigrationGraphCommand(
154
154
  const treeSections: MigrationGraphTreeSection[] = [];
155
155
  const spaces: MigrationSpaceGraphEntry[] = [];
156
156
  for (const spaceEntry of scopedSpaces) {
157
- const member = aggregate.space(spaceEntry.space);
158
- if (member === undefined) {
157
+ const space = aggregate.space(spaceEntry.space);
158
+ if (space === undefined) {
159
159
  continue;
160
160
  }
161
- const graph = member.graph();
161
+ const graph = space.graph();
162
162
  const isAppSpace = spaceEntry.space === aggregate.app.spaceId;
163
- const refsByHash = listRefsByContractHash(member);
163
+ const refsByHash = listRefsByContractHash(space);
164
164
  const tree =
165
165
  spaceEntry.migrations.length === 0
166
166
  ? ''
@@ -1,7 +1,7 @@
1
1
  import { loadConfig } from '@prisma-next/config-loader';
2
2
  import type {
3
+ AggregateContractSpace,
3
4
  ContractSpaceAggregate,
4
- ContractSpaceMember,
5
5
  } from '@prisma-next/migration-tools/aggregate';
6
6
  import type { MigrationGraph } from '@prisma-next/migration-tools/graph';
7
7
  import { HEAD_REF_NAME, refsByContractHash } from '@prisma-next/migration-tools/refs';
@@ -59,18 +59,18 @@ function compareDirNamesDescending(a: MigrationListEntry, b: MigrationListEntry)
59
59
 
60
60
  /**
61
61
  * Ref names decorating a space's destination contract hashes. The
62
- * tolerant `member.refs` deliberately omits the structural `head.json`;
62
+ * tolerant `space.refs` deliberately omits the structural `head.json`;
63
63
  * for extension spaces the old enumerator surfaced it as a `head`
64
- * decoration on the tip migration, so fold `member.headRef` back in to
64
+ * decoration on the tip migration, so fold `space.headRef` back in to
65
65
  * keep that output. The app space synthesises its head, so it carries
66
66
  * no on-disk `head` ref to restore.
67
67
  */
68
68
  export function listRefsByContractHash(
69
- member: ContractSpaceMember,
69
+ space: AggregateContractSpace,
70
70
  ): ReadonlyMap<string, readonly string[]> {
71
- const byHash = new Map(refsByContractHash(member.refs));
72
- if (member.spaceId !== APP_SPACE_ID && member.headRef !== null) {
73
- const hash = member.headRef.hash;
71
+ const byHash = new Map(refsByContractHash(space.refs));
72
+ if (space.spaceId !== APP_SPACE_ID && space.headRef !== null) {
73
+ const hash = space.headRef.hash;
74
74
  const bucket = byHash.get(hash) ?? [];
75
75
  if (!bucket.includes(HEAD_REF_NAME)) {
76
76
  byHash.set(hash, [...bucket, HEAD_REF_NAME].sort());
@@ -92,7 +92,7 @@ async function orderedOnDiskSpaceIds(projectMigrationsDir: string): Promise<read
92
92
  * {@link MigrationSpaceListEntry} rows `migration list` displays.
93
93
  *
94
94
  * Space membership matches the on-disk contract-space directories (not the
95
- * aggregate's always-present synthesized app member when `migrations/app/`
95
+ * aggregate's always-present synthesized app space when `migrations/app/`
96
96
  * is absent); package and ref data come from `aggregate.space(id)`.
97
97
  */
98
98
  export async function migrationSpaceListEntriesFromAggregate(
@@ -103,12 +103,12 @@ export async function migrationSpaceListEntriesFromAggregate(
103
103
  const spaces: MigrationSpaceListEntry[] = [];
104
104
 
105
105
  for (const spaceId of spaceIds) {
106
- const member = aggregate.space(spaceId);
107
- if (member === undefined) {
106
+ const space = aggregate.space(spaceId);
107
+ if (space === undefined) {
108
108
  continue;
109
109
  }
110
- const refsByHash = listRefsByContractHash(member);
111
- const migrations: MigrationListEntry[] = member.packages
110
+ const refsByHash = listRefsByContractHash(space);
111
+ const migrations: MigrationListEntry[] = space.packages
112
112
  .map((pkg) => ({
113
113
  name: pkg.dirName,
114
114
  hash: pkg.metadata.migrationHash,
@@ -321,11 +321,11 @@ async function executeMigrationPlanCommand(
321
321
  if (!tolerantAggregateResult.ok) {
322
322
  return notOk(tolerantAggregateResult.failure);
323
323
  }
324
- const resolutionMember = tolerantAggregateResult.value.app;
324
+ const resolutionSpace = tolerantAggregateResult.value.app;
325
325
 
326
326
  const resolutionResult = await resolveFromForPlan({
327
327
  optionsFrom: options.from,
328
- member: resolutionMember,
328
+ space: resolutionSpace,
329
329
  });
330
330
 
331
331
  if (!resolutionResult.ok) {
@@ -365,7 +365,7 @@ async function executeMigrationPlanCommand(
365
365
  // change.
366
366
  if (options.to !== undefined) {
367
367
  const toResolution = await resolveToForPlan(options.to, {
368
- member: resolutionMember,
368
+ space: resolutionSpace,
369
369
  });
370
370
  if (!toResolution.ok) {
371
371
  return notOk(toResolution.failure);
@@ -1,8 +1,8 @@
1
1
  import { loadConfig } from '@prisma-next/config-loader';
2
2
  import type { LedgerEntryRecord } from '@prisma-next/contract/types';
3
3
  import type {
4
+ AggregateContractSpace,
4
5
  ContractMarkerRecordLike,
5
- ContractSpaceMember,
6
6
  } from '@prisma-next/migration-tools/aggregate';
7
7
  import { EMPTY_CONTRACT_HASH } from '@prisma-next/migration-tools/constants';
8
8
  import {
@@ -130,7 +130,7 @@ function buildStatusMigrations(
130
130
  }
131
131
 
132
132
  function renderSpaceTree(args: {
133
- readonly member: ContractSpaceMember;
133
+ readonly space: AggregateContractSpace;
134
134
  readonly liveContractHash: string;
135
135
  readonly migrations: readonly MigrationListEntry[];
136
136
  readonly markerHash: string | undefined;
@@ -142,7 +142,7 @@ function renderSpaceTree(args: {
142
142
  readonly globalMaxEdgeTreePrefixWidth?: number;
143
143
  readonly globalMaxDirNameWidth?: number;
144
144
  }): string {
145
- const graph = args.member.graph();
145
+ const graph = args.space.graph();
146
146
  if (graph.nodes.size === 0) {
147
147
  return '';
148
148
  }
@@ -150,7 +150,7 @@ function renderSpaceTree(args: {
150
150
  graph,
151
151
  migrations: args.migrations,
152
152
  liveContractHash: args.liveContractHash,
153
- refsByHash: listRefsByContractHash(args.member),
153
+ refsByHash: listRefsByContractHash(args.space),
154
154
  statusOverlayByHash: args.statusOverlay,
155
155
  colorize: args.colorize,
156
156
  glyphMode: args.glyphMode,
@@ -493,12 +493,12 @@ export async function executeMigrationStatusCommand(
493
493
  globalLayoutInputs.length > 0 ? computeGlobalMaxDirNameWidth(globalLayoutInputs) : undefined;
494
494
 
495
495
  for (const spaceEntry of scopedSpaces) {
496
- const member = aggregate.space(spaceEntry.space);
497
- if (member === undefined) {
496
+ const space = aggregate.space(spaceEntry.space);
497
+ if (space === undefined) {
498
498
  continue;
499
499
  }
500
- const graph = member.graph();
501
- const spaceContractHash = member.contract().storage.storageHash;
500
+ const graph = space.graph();
501
+ const spaceContractHash = space.contract().storage.storageHash;
502
502
  const targetHash = resolveTarget(spaceContractHash, activeRefHash);
503
503
  if (spaceEntry.space === aggregate.app.spaceId) {
504
504
  headlineTargetHash = targetHash;
@@ -547,7 +547,7 @@ export async function executeMigrationStatusCommand(
547
547
  });
548
548
  const isAppSpace = spaceEntry.space === aggregate.app.spaceId;
549
549
  const tree = renderSpaceTree({
550
- member,
550
+ space,
551
551
  liveContractHash: contractHash,
552
552
  migrations: spaceEntry.migrations,
553
553
  markerHash,
@@ -20,7 +20,7 @@ import { executeRun } from './db-run';
20
20
  * {@link import('@prisma-next/migration-tools/aggregate').loadContractSpaceAggregate}
21
21
  * from the supplied descriptor set + on-disk on-disk artefacts.
22
22
  * 2. The aggregate planner runs with `callerPolicy.ignoreGraphFor`
23
- * locked to the app member — synth strategy for the app, graph-walk
23
+ * locked to the app space — synth strategy for the app, graph-walk
24
24
  * for every extension.
25
25
  * 3. The runner's `execute` applies the per-space plans
26
26
  * inside one outer transaction.
@@ -53,7 +53,7 @@ export interface ExecuteDbInitOptions<TFamilyId extends string, TTargetId extend
53
53
  /**
54
54
  * Declared extension descriptors. Defaults to an empty list, which
55
55
  * routes through the same loader → planner → runner pipeline with no
56
- * extension members in the aggregate.
56
+ * extension spaces in the aggregate.
57
57
  */
58
58
  readonly extensionPacks?: ReadonlyArray<ControlExtensionDescriptor<TFamilyId, TTargetId>>;
59
59
  /** Optional progress callback for observing operation progress */
@@ -98,9 +98,9 @@ export interface ExecuteRunOptions<TFamilyId extends string, TTargetId extends s
98
98
  * 2. **Read DB state**: marker rows (`familyInstance.readAllMarkers`)
99
99
  * + introspected schema (`familyInstance.introspect`).
100
100
  * 3. **Plan**: {@link planMigration} chooses graph-walk vs synth per
101
- * member according to `callerPolicy.ignoreGraphFor`. The app member
101
+ * space according to `callerPolicy.ignoreGraphFor`. The app space
102
102
  * is forced through synth (today's daily-driver behaviour); every
103
- * extension member walks its on-disk graph.
103
+ * extension space walks its on-disk graph.
104
104
  * 4. **Apply** (when `mode === 'apply'`): every per-space `MigrationPlan`
105
105
  * feeds into the runner's `execute` — one outer
106
106
  * transaction across every space; failure on any space rolls back
@@ -200,7 +200,7 @@ export async function executeRun<TFamilyId extends string, TTargetId extends str
200
200
  const appResolution = orderedResolutions.find((r) => r.spaceId === aggregate.app.spaceId);
201
201
  if (!appResolution) {
202
202
  throw new Error(
203
- 'Aggregate planner returned no plan for the app member — the planner is supposed to always emit one.',
203
+ 'Aggregate planner returned no plan for the app space — the planner is supposed to always emit one.',
204
204
  );
205
205
  }
206
206
  const appPlan = appResolution.entry.plan;
@@ -280,14 +280,14 @@ function aggregatePlannerWarnings(
280
280
 
281
281
  /**
282
282
  * Compare the live `_prisma_marker` rows against the aggregate's
283
- * declared members. Any marker row whose `space` is not a member of
283
+ * declared contract spaces. Any marker row whose `space` is not a space of
284
284
  * the aggregate is an "orphan" — typically a marker left behind by
285
285
  * an extension that was removed from `extensionPacks` without first
286
286
  * cleaning up its on-disk migrations / database tables.
287
287
  *
288
288
  * Returns a {@link CliStructuredError} envelope (code `5002`,
289
289
  * `kind: 'orphanMarker'`) for the first orphan it finds, or `null`
290
- * when every marker row maps to a declared member. Mirrors the M2
290
+ * when every marker row maps to a declared contract space. Mirrors the M2
291
291
  * `runContractSpaceVerifierMarkerCheck` envelope so downstream
292
292
  * tooling (integration tests, JSON consumers) keeps asserting on the
293
293
  * same shape.
@@ -296,13 +296,13 @@ function detectOrphanMarkers(
296
296
  aggregate: ContractSpaceAggregate,
297
297
  markerRows: ReadonlyMap<string, unknown>,
298
298
  ): CliStructuredError | null {
299
- const memberSpaceIds = new Set<string>([
299
+ const aggregateSpaceIds = new Set<string>([
300
300
  aggregate.app.spaceId,
301
301
  ...aggregate.extensions.map((m) => m.spaceId),
302
302
  ]);
303
303
  const orphans: string[] = [];
304
304
  for (const [spaceId, row] of markerRows) {
305
- if (row !== null && row !== undefined && !memberSpaceIds.has(spaceId)) {
305
+ if (row !== null && row !== undefined && !aggregateSpaceIds.has(spaceId)) {
306
306
  orphans.push(spaceId);
307
307
  }
308
308
  }
@@ -7,13 +7,13 @@ import type {
7
7
  VerifyDatabaseSchemaResult,
8
8
  } from '@prisma-next/framework-components/control';
9
9
  import {
10
- type ContractSpaceMember,
10
+ type AggregateContractSpace,
11
11
  collectAggregateNamespaces,
12
12
  requireHeadRef,
13
13
  type VerifierOutput,
14
14
  verifyMigration,
15
15
  } from '@prisma-next/migration-tools/aggregate';
16
- import { blindCast, castAs } from '@prisma-next/utils/casts';
16
+ import { castAs } from '@prisma-next/utils/casts';
17
17
  import { notOk, ok, type Result } from '@prisma-next/utils/result';
18
18
  import { CliStructuredError } from '../../utils/cli-errors';
19
19
  import {
@@ -37,9 +37,9 @@ const SPAN_IDS = {
37
37
  *
38
38
  * Loader → verifier pipeline. The loader (sole descriptor-import
39
39
  * boundary) builds a {@link import('@prisma-next/migration-tools/aggregate').ContractSpaceAggregate};
40
- * the aggregate verifier bundles `markerCheck` + per-space pre-projected
41
- * `schemaCheck`. `mode: 'strict' | 'lenient'` maps directly to the user
42
- * facing `--strict` flag.
40
+ * the aggregate verifier bundles `markerCheck` + per-space `schemaCheck`
41
+ * (each contract space verified against the full schema; extras stripped to its own view).
42
+ * `mode: 'strict' | 'lenient'` maps directly to the user facing `--strict` flag.
43
43
  */
44
44
  export interface ExecuteDbVerifyOptions<TFamilyId extends string, TTargetId extends string> {
45
45
  readonly driver: ControlDriverInstance<TFamilyId, TTargetId>;
@@ -63,13 +63,19 @@ export interface ExecuteDbVerifyOptions<TFamilyId extends string, TTargetId exte
63
63
  * emitted, so downstream tooling and integration tests assert on the
64
64
  * same shape).
65
65
  *
66
- * On success, the per-space schema results are returned for the CLI to
66
+ * On success, the per-space verify results are returned for the CLI to
67
67
  * render. When `skipSchema` is true (`--marker-only`), the schema map
68
68
  * is empty.
69
69
  */
70
70
  export interface ExecuteDbVerifySuccess {
71
71
  readonly schemaResults: ReadonlyMap<string, VerifyDatabaseSchemaResult>;
72
- readonly memberOrder: readonly string[];
72
+ /**
73
+ * Live element names no contract space declares, deduplicated and reported
74
+ * once for the whole database (never per space). Strict mode fails on a
75
+ * non-empty list; lenient mode surfaces it informationally.
76
+ */
77
+ readonly unclaimed: readonly string[];
78
+ readonly spaceOrder: readonly string[];
73
79
  readonly appSpaceId: string;
74
80
  }
75
81
 
@@ -85,11 +91,11 @@ export type ExecuteDbVerifyResult = Result<ExecuteDbVerifySuccess, CliStructured
85
91
  * structured CLI error.
86
92
  * 2. **Read DB state**: marker rows + (when `skipSchema` is `false`)
87
93
  * schema introspection.
88
- * 3. **Verify**: {@link verifyMigration} returns per-space
89
- * `markerCheck` + per-space pre-projected `schemaCheck` (closes F23).
90
- * Marker mismatches map to `CliStructuredError` (code `5002`) so
91
- * callers (CLI command) can render and exit. Schema results are
92
- * returned to the caller verbatim.
94
+ * 3. **Verify**: {@link verifyMigration} returns per-space `markerCheck` +
95
+ * per-space `schemaCheck` (each contract space verified against the full schema,
96
+ * then scoped to its own contract space). Marker mismatches map to
97
+ * `CliStructuredError` (code `5002`) so callers (CLI command) can render
98
+ * and exit. Verify results are returned to the caller verbatim.
93
99
  */
94
100
  export async function executeDbVerify<TFamilyId extends string, TTargetId extends string>(
95
101
  options: ExecuteDbVerifyOptions<TFamilyId, TTargetId>,
@@ -115,7 +121,7 @@ export async function executeDbVerify<TFamilyId extends string, TTargetId extend
115
121
  markersBySpaceId,
116
122
  schemaIntrospection,
117
123
  mode: options.mode,
118
- verifySchemaForMember: createPerMemberVerifier(options),
124
+ verifySchemaForSpace: createPerSpaceVerifier(options),
119
125
  });
120
126
  return finaliseVerifyResult({ verifyResult, aggregate, skipMarker, onProgress });
121
127
  }
@@ -166,30 +172,27 @@ async function runIntrospection<TFamilyId extends string, TTargetId extends stri
166
172
  }
167
173
 
168
174
  /**
169
- * Build the per-member schema callback handed to the aggregate verifier.
175
+ * Build the per-space schema callback handed to the aggregate verifier.
170
176
  * When `skipSchema` is true the callback short-circuits with a synthetic
171
177
  * `ok` result so the verifier still runs the (cheap) schemaCheck loop
172
178
  * without invoking the family's verification path.
173
179
  */
174
- export function createPerMemberVerifier<TFamilyId extends string, TTargetId extends string>(
180
+ export function createPerSpaceVerifier<TFamilyId extends string, TTargetId extends string>(
175
181
  options: ExecuteDbVerifyOptions<TFamilyId, TTargetId>,
176
182
  ): (
177
- projectedSchema: unknown,
178
- member: ContractSpaceMember,
183
+ schema: unknown,
184
+ space: AggregateContractSpace,
179
185
  verifyMode: 'strict' | 'lenient',
180
186
  ) => VerifyDatabaseSchemaResult {
181
187
  const { skipSchema, familyInstance, frameworkComponents } = options;
182
- return (projectedSchema, member, verifyMode) => {
183
- if (skipSchema) return buildSkippedSchemaResult(member);
188
+ return (schema, space, verifyMode) => {
189
+ if (skipSchema) return buildSkippedSchemaResult(space);
184
190
  return familyInstance.verifySchema({
185
- contract: member.contract(),
186
- // The family's `TSchemaIR` is opaque to migration-tools; the
187
- // aggregate verifier passes through whatever we hand it. The
188
- // family expects its own IR shape on the way back.
189
- schema: blindCast<
190
- never,
191
- 'family TSchemaIR is opaque to migration-tools; projectedSchema is passed straight through'
192
- >(projectedSchema),
191
+ contract: space.contract(),
192
+ // `familyInstance` is `ControlFamilyInstance<_, unknown>`, so `verifySchema`
193
+ // takes its `TSchemaIR` as `unknown` the introspected schema passes
194
+ // straight through; the family narrows to its own IR node internally.
195
+ schema,
193
196
  strict: verifyMode === 'strict',
194
197
  frameworkComponents,
195
198
  });
@@ -223,7 +226,7 @@ function emitVerifySpan(
223
226
  * by the CLI's `--schema-only` mode.
224
227
  */
225
228
  function finaliseVerifyResult(args: {
226
- verifyResult: VerifierOutput<VerifyDatabaseSchemaResult>;
229
+ verifyResult: VerifierOutput;
227
230
  aggregate: {
228
231
  readonly app: { readonly spaceId: string };
229
232
  readonly extensions: ReadonlyArray<{ readonly spaceId: string }>;
@@ -253,14 +256,15 @@ function finaliseVerifyResult(args: {
253
256
  emitVerifySpan(onProgress, 'spanEndOk');
254
257
  return ok({
255
258
  schemaResults: verifyResult.value.schemaCheck.perSpace,
256
- memberOrder: [aggregate.app.spaceId, ...aggregate.extensions.map((e) => e.spaceId)],
259
+ unclaimed: verifyResult.value.schemaCheck.unclaimed,
260
+ spaceOrder: [aggregate.app.spaceId, ...aggregate.extensions.map((e) => e.spaceId)],
257
261
  appSpaceId: aggregate.app.spaceId,
258
262
  });
259
263
  }
260
264
 
261
- function buildSkippedSchemaResult(member: ContractSpaceMember): VerifyDatabaseSchemaResult {
262
- const contract = member.contract();
263
- const headRef = requireHeadRef(member);
265
+ function buildSkippedSchemaResult(space: AggregateContractSpace): VerifyDatabaseSchemaResult {
266
+ const contract = space.contract();
267
+ const headRef = requireHeadRef(space);
264
268
  const profileHash = castAs<{ profileHash?: string }>(contract).profileHash;
265
269
  return {
266
270
  ok: true,
@@ -276,7 +280,7 @@ function buildSkippedSchemaResult(member: ContractSpaceMember): VerifyDatabaseSc
276
280
  root: {
277
281
  status: 'pass',
278
282
  kind: 'skipped',
279
- name: member.spaceId,
283
+ name: space.spaceId,
280
284
  contractPath: '',
281
285
  code: 'SKIPPED',
282
286
  message: 'Schema verification skipped',