@prisma-next/migration-tools 0.5.0-dev.74 → 0.5.0-dev.75

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.
@@ -1,5 +1,6 @@
1
1
  import { n as OnDiskMigrationPackage } from "../package-BjiZ7KDy.mjs";
2
2
  import { n as MigrationGraph } from "../graph-HMWAldoR.mjs";
3
+ import { t as PathDecision } from "../migration-graph-DulOITvG.mjs";
3
4
  import { ControlFamilyInstance, MigrationOperationPolicy, MigrationPlan, MigrationPlanOperation, MigrationPlannerConflict, TargetMigrationsCapability } from "@prisma-next/framework-components/control";
4
5
  import { Result } from "@prisma-next/utils/result";
5
6
  import { Contract } from "@prisma-next/contract/types";
@@ -325,11 +326,43 @@ interface AggregatePlannerInput<TFamilyId extends string, TTargetId extends stri
325
326
  * - `strategy`: which strategy produced this plan (`'graph-walk'` or
326
327
  * `'synth'`). Surfaced for diagnostics; not consumed by the runner.
327
328
  */
329
+ /**
330
+ * Per-edge metadata for the chain assembled by the graph-walk
331
+ * strategy. Lets `migration apply` surface a per-migration `applied[]`
332
+ * entry (preserving the single-space `migrationsApplied` count
333
+ * semantics) without re-walking the graph.
334
+ *
335
+ * `synth`-produced plans leave this absent — synthesised plans don't
336
+ * have authored edges to surface.
337
+ */
338
+ interface AggregateMigrationEdgeRef {
339
+ readonly migrationHash: string;
340
+ readonly dirName: string;
341
+ readonly from: string;
342
+ readonly to: string;
343
+ readonly operationCount: number;
344
+ }
328
345
  interface AggregatePerSpacePlan {
329
346
  readonly plan: MigrationPlan;
330
347
  readonly displayOps: readonly MigrationPlanOperation[];
331
348
  readonly destinationContract: Contract;
332
349
  readonly strategy: 'graph-walk' | 'synth';
350
+ /**
351
+ * Per-edge breakdown of the chain. Populated by the graph-walk
352
+ * strategy; absent for synth-produced plans.
353
+ */
354
+ readonly migrationEdges?: readonly AggregateMigrationEdgeRef[];
355
+ /**
356
+ * Path decision data the strategy used to select the chain
357
+ * (alternative count, tie-break reasons, required/satisfied
358
+ * invariants, per-edge invariants). Populated by the graph-walk
359
+ * strategy; absent for synth-produced plans.
360
+ *
361
+ * `migration apply` surfaces this for the app member as
362
+ * `MigrationApplySuccess.pathDecision` (back-compat with single-
363
+ * space callers).
364
+ */
365
+ readonly pathDecision?: PathDecision;
333
366
  }
334
367
  interface AggregatePlannerSuccess {
335
368
  readonly perSpace: ReadonlyMap<string, AggregatePerSpacePlan>;
@@ -424,6 +457,53 @@ declare function planAggregate<TFamilyId extends string, TTargetId extends strin
424
457
  */
425
458
  declare function projectSchemaToSpace(schema: unknown, member: ContractSpaceMember, otherMembers: ReadonlyArray<ContractSpaceMember>): unknown;
426
459
  //#endregion
460
+ //#region src/aggregate/strategies/graph-walk.d.ts
461
+ /**
462
+ * Outcome variants for the graph-walk strategy. Mirrors
463
+ * {@link import('../../compute-extension-space-apply-path').ExtensionSpaceApplyPathOutcome}
464
+ * but operates against the **already-hydrated** `member.migrations.graph`
465
+ * instead of re-reading from disk. The aggregate planner converts
466
+ * these into {@link import('../planner-types').AggregatePlannerError}
467
+ * variants.
468
+ */
469
+ type GraphWalkOutcome = {
470
+ readonly kind: 'ok';
471
+ readonly result: AggregatePerSpacePlan;
472
+ } | {
473
+ readonly kind: 'unreachable';
474
+ } | {
475
+ readonly kind: 'unsatisfiable';
476
+ readonly missing: readonly string[];
477
+ };
478
+ interface GraphWalkStrategyInputs {
479
+ readonly aggregateTargetId: string;
480
+ readonly member: ContractSpaceMember;
481
+ readonly currentMarker: ContractMarkerRecordLike | null;
482
+ /**
483
+ * Optional ref name to decorate the resulting `PathDecision`. Used by
484
+ * `migration apply` to surface the user-supplied `--ref <name>` in
485
+ * structured-progress events and invariant-path error envelopes. The
486
+ * strategy itself does not interpret it.
487
+ */
488
+ readonly refName?: string;
489
+ }
490
+ /**
491
+ * Walk a member's hydrated migration graph from the live marker to
492
+ * `member.headRef.hash`, covering every required invariant.
493
+ *
494
+ * Pure synchronous function — no I/O. The aggregate's loader has
495
+ * already integrity-checked every package and reconstructed the graph;
496
+ * this strategy just looks up ops by `migrationHash` and assembles a
497
+ * `MigrationPlan` with `targetId` set from the aggregate (no
498
+ * placeholder cast).
499
+ *
500
+ * Required invariants are computed as `headRef.invariants \ marker.invariants`
501
+ * — the marker already declares some invariants satisfied; the path
502
+ * only needs to provide the remainder. Mirrors today's
503
+ * `computeExtensionSpaceApplyPath` semantics.
504
+ */
505
+ declare function graphWalkStrategy(input: GraphWalkStrategyInputs): GraphWalkOutcome;
506
+ //#endregion
427
507
  //#region src/aggregate/verifier.d.ts
428
508
  /**
429
509
  * Caller policy for the aggregate verifier. Today's only knob is
@@ -530,5 +610,5 @@ type AggregateVerifierOutput<TSchemaResult> = Result<AggregateVerifierSuccess<TS
530
610
  */
531
611
  declare function verifyAggregate<TSchemaResult>(input: AggregateVerifierInput<TSchemaResult>): AggregateVerifierOutput<TSchemaResult>;
532
612
  //#endregion
533
- export { type AggregateContractHasher, type AggregateCurrentDBState, 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 HydratedMigrationGraph, type LayoutViolation, type LoadAggregateError, type LoadAggregateInput, type LoadAggregateOutput, type MarkerCheckResult, type MarkerCheckSection, type OrphanElement, type SchemaCheckSection, loadContractSpaceAggregate, planAggregate, projectSchemaToSpace, verifyAggregate };
613
+ export { type AggregateContractHasher, type AggregateCurrentDBState, 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 };
534
614
  //# sourceMappingURL=aggregate.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.d.mts","names":[],"sources":["../../src/aggregate/types.ts","../../src/aggregate/loader.ts","../../src/aggregate/marker-types.ts","../../src/aggregate/planner-types.ts","../../src/aggregate/planner.ts","../../src/aggregate/project-schema-to-space.ts","../../src/aggregate/verifier.ts"],"mappings":"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"}
1
+ {"version":3,"file":"aggregate.d.mts","names":[],"sources":["../../src/aggregate/types.ts","../../src/aggregate/loader.ts","../../src/aggregate/marker-types.ts","../../src/aggregate/planner-types.ts","../../src/aggregate/planner.ts","../../src/aggregate/project-schema-to-space.ts","../../src/aggregate/strategies/graph-walk.ts","../../src/aggregate/verifier.ts"],"mappings":"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"}
@@ -226,12 +226,15 @@ function extractTableNames(contract) {
226
226
  * `computeExtensionSpaceApplyPath` semantics.
227
227
  */
228
228
  function graphWalkStrategy(input) {
229
- const { aggregateTargetId, member, currentMarker } = input;
229
+ const { aggregateTargetId, member, currentMarker, refName } = input;
230
230
  const { graph, packagesByMigrationHash } = member.migrations;
231
231
  const fromHash = currentMarker?.storageHash ?? "sha256:empty";
232
232
  const markerInvariants = new Set(currentMarker?.invariants ?? []);
233
233
  const required = new Set(member.headRef.invariants.filter((id) => !markerInvariants.has(id)));
234
- const outcome = findPathWithDecision(graph, fromHash, member.headRef.hash, { required });
234
+ const outcome = findPathWithDecision(graph, fromHash, member.headRef.hash, {
235
+ required,
236
+ ...refName !== void 0 ? { refName } : {}
237
+ });
235
238
  if (outcome.kind === "unreachable") return { kind: "unreachable" };
236
239
  if (outcome.kind === "unsatisfiable") return {
237
240
  kind: "unsatisfiable",
@@ -239,11 +242,19 @@ function graphWalkStrategy(input) {
239
242
  };
240
243
  const pathOps = [];
241
244
  const providedInvariantsSet = /* @__PURE__ */ new Set();
245
+ const edgeRefs = [];
242
246
  for (const edge of outcome.decision.selectedPath) {
243
247
  const pkg = packagesByMigrationHash.get(edge.migrationHash);
244
248
  if (!pkg) throw new Error(`Migration package missing for edge ${edge.migrationHash} in space "${member.spaceId}". The hydrated migration graph and packagesByMigrationHash map are out of sync — this should be unreachable; report.`);
245
249
  for (const op of pkg.ops) pathOps.push(op);
246
250
  for (const invariant of pkg.metadata.providedInvariants) providedInvariantsSet.add(invariant);
251
+ edgeRefs.push({
252
+ migrationHash: edge.migrationHash,
253
+ dirName: edge.dirName,
254
+ from: edge.from,
255
+ to: edge.to,
256
+ operationCount: pkg.ops.length
257
+ });
247
258
  }
248
259
  return {
249
260
  kind: "ok",
@@ -258,7 +269,9 @@ function graphWalkStrategy(input) {
258
269
  },
259
270
  displayOps: pathOps,
260
271
  destinationContract: member.contract,
261
- strategy: "graph-walk"
272
+ strategy: "graph-walk",
273
+ migrationEdges: edgeRefs,
274
+ pathDecision: outcome.decision
262
275
  }
263
276
  };
264
277
  }
@@ -593,6 +606,6 @@ function detectOrphanElements(schemaIntrospection, members) {
593
606
  return orphans;
594
607
  }
595
608
  //#endregion
596
- export { loadContractSpaceAggregate, planAggregate, projectSchemaToSpace, verifyAggregate };
609
+ export { graphWalkStrategy, loadContractSpaceAggregate, planAggregate, projectSchemaToSpace, verifyAggregate };
597
610
 
598
611
  //# sourceMappingURL=aggregate.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.mjs","names":[],"sources":["../../src/aggregate/loader.ts","../../src/aggregate/strategies/graph-walk.ts","../../src/aggregate/project-schema-to-space.ts","../../src/aggregate/strategies/synth.ts","../../src/aggregate/planner.ts","../../src/aggregate/verifier.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { EMPTY_CONTRACT_HASH } from '../constants';\nimport { detectSpaceContractDrift } from '../detect-space-contract-drift';\nimport { readMigrationsDir } from '../io';\nimport { reconstructGraph } from '../migration-graph';\nimport type { OnDiskMigrationPackage } from '../package';\nimport { readContractSpaceContract } from '../read-contract-space-contract';\nimport { readContractSpaceHeadRef } from '../read-contract-space-head-ref';\nimport { APP_SPACE_ID, spaceMigrationDirectory } from '../space-layout';\nimport { listContractSpaceDirectories } from '../verify-contract-spaces';\nimport type { ContractSpaceAggregate, ContractSpaceMember, HydratedMigrationGraph } from './types';\n\n/**\n * Hash function used by drift detection. Defaults to a canonical-JSON +\n * SHA-256 pipeline that mirrors the framework's contract-hash convention,\n * but the loader accepts a callback so SQL-family callers can pass their\n * `coreHash` / `storageHash` derivation through unchanged.\n *\n * The contract value passed in is the framework-neutral `unknown` form;\n * callers that have already validated typed contracts can simply hand\n * the validated value back through.\n */\nexport type AggregateContractHasher = (contract: unknown) => string;\n\n/**\n * Single declared extension entry the loader needs from `Config.extensionPacks`.\n *\n * Only the subset of fields the loader operates on:\n *\n * - `id` — the space id (also the directory name under `migrations/`).\n * - `targetId` — the configured `Config.adapter.targetId` value the\n * declaring extension declared. The loader rejects mismatches against\n * the aggregate's `targetId` with `targetMismatch`.\n * - `contractSpace` — present iff the descriptor declares a contract\n * space (extensions can ship without one and remain runtime-only /\n * codec-only). Drift detection compares the descriptor's\n * `contractJson` hash against the on-disk on-disk head hash; the loader\n * rejects drift fatally.\n *\n * Typed structurally so the migration-tools layer stays framework-neutral.\n */\nexport interface DeclaredExtensionEntry {\n readonly id: string;\n readonly targetId: string;\n readonly contractSpace?: {\n readonly contractJson: unknown;\n };\n}\n\n/**\n * Inputs for {@link loadContractSpaceAggregate}.\n *\n * The loader is the **sole** descriptor-import boundary in the M2.5\n * pipeline: callers gather the descriptor data (already-validated app\n * contract, declared extension entries) and pass it through. Once the\n * loader returns, no descriptor module is imported again for this\n * aggregate's lifetime.\n */\nexport interface LoadAggregateInput {\n readonly targetId: string;\n readonly migrationsDir: string;\n readonly appContract: Contract;\n readonly declaredExtensions: ReadonlyArray<DeclaredExtensionEntry>;\n readonly validateContract: (contractJson: unknown) => Contract;\n readonly hashContract: AggregateContractHasher;\n /**\n * Hydrated migration graph for the **app member**.\n *\n * The framework-neutral migration-tools layer doesn't know how to read\n * the user's authored `migrations/` directory (the app member's\n * migration-package layout is family-aware: ops.json shape, manifest\n * keys, etc.). Callers — the SQL family today — read the user's\n * `migrations/` and hand the resulting `OnDiskMigrationPackage[]` through.\n *\n * Passing `[]` is valid (greenfield project, no authored migrations).\n * Equivalent to `migrations/` not existing or being empty.\n */\n readonly appMigrationPackages: ReadonlyArray<OnDiskMigrationPackage>;\n}\n\n/**\n * Discriminated failure variants the loader emits.\n *\n * Every variant short-circuits at first hit; the loader does not keep\n * collecting after the first violation in any phase except for layout\n * (where every layout offence is bundled into one `layoutViolation`).\n */\nexport type LoadAggregateError =\n | { readonly kind: 'layoutViolation'; readonly violations: readonly LayoutViolation[] }\n | { readonly kind: 'integrityFailure'; readonly spaceId: string; readonly detail: string }\n | { readonly kind: 'validationFailure'; readonly spaceId: string; readonly detail: string }\n | {\n readonly kind: 'driftViolation';\n readonly spaceId: string;\n readonly priorHeadHash: string;\n readonly liveHash: string;\n }\n | {\n readonly kind: 'disjointnessViolation';\n readonly element: string;\n readonly claimedBy: readonly string[];\n }\n | {\n readonly kind: 'targetMismatch';\n readonly spaceId: string;\n readonly expected: string;\n readonly actual: string;\n };\n\n/**\n * Single layout violation; bundled into a `layoutViolation` error so\n * users see every layout offence at once rather than fixing them one\n * at a time across re-runs.\n *\n * - `declaredButUnmigrated`: extension declared in `extensionPacks` with\n * a `contractSpace` but no contract-space dir on disk. Remediation:\n * `prisma-next migrate`.\n * - `orphanSpaceDir`: contract-space dir under `migrations/` for an extension\n * not in `extensionPacks`. Remediation: remove the directory, or\n * re-add the extension to `extensionPacks`.\n */\nexport type LayoutViolation =\n | { readonly kind: 'declaredButUnmigrated'; readonly spaceId: string }\n | { readonly kind: 'orphanSpaceDir'; readonly spaceId: string };\n\nexport type LoadAggregateOutput = Result<\n { readonly aggregate: ContractSpaceAggregate },\n LoadAggregateError\n>;\n\ninterface LoadedExtensionState {\n readonly entry: DeclaredExtensionEntry;\n readonly contract: Contract;\n readonly headRefHash: string;\n readonly headRefInvariants: readonly string[];\n readonly migrations: HydratedMigrationGraph;\n}\n\n/**\n * Hydrate a {@link ContractSpaceAggregate} from on-disk state and\n * caller-provided descriptor data.\n *\n * This is the **only** descriptor-import boundary in the post-M2.5\n * pipeline: callers read `extensionPacks` from `Config`, validate the\n * app contract, and pass everything through. The loader composes\n * existing migration-tools primitives — layout precheck (via\n * {@link listContractSpaceDirectories}), integrity checks (via\n * {@link readMigrationsDir} / {@link readContractSpaceHeadRef} /\n * {@link readContractSpaceContract} / `validateContract`), drift detection\n * (via {@link detectSpaceContractDrift}), and disjointness — into a\n * single typed value.\n *\n * Failure semantics: every failure variant in {@link LoadAggregateError}\n * short-circuits the load. Drift is fatal (M2.5 spec § Loader, step 5).\n */\nexport async function loadContractSpaceAggregate(\n input: LoadAggregateInput,\n): Promise<LoadAggregateOutput> {\n // 1. Validate target consistency on the app contract.\n const appContractTarget = input.appContract.target;\n if (appContractTarget !== input.targetId) {\n return notOk({\n kind: 'targetMismatch',\n spaceId: APP_SPACE_ID,\n expected: input.targetId,\n actual: appContractTarget,\n });\n }\n\n for (const entry of input.declaredExtensions) {\n if (entry.targetId !== input.targetId) {\n return notOk({\n kind: 'targetMismatch',\n spaceId: entry.id,\n expected: input.targetId,\n actual: entry.targetId,\n });\n }\n }\n\n // 2. Layout precheck: bundle every layout offence at once.\n const declaredWithSpace = input.declaredExtensions.filter((e) => e.contractSpace !== undefined);\n const declaredSpaceIds = new Set(declaredWithSpace.map((e) => e.id));\n const allDirs = await listContractSpaceDirectories(input.migrationsDir);\n // The app member is implicitly declared (it is always part of the\n // aggregate); its `migrations/<APP_SPACE_ID>/` directory may exist or\n // not (greenfield projects start with neither). Filter it out of the\n // orphan / declared-but-unmigrated checks so the layout precheck is\n // about extensions only.\n const extensionDirsOnDisk = allDirs.filter((d) => d !== APP_SPACE_ID);\n const spaceDirSet = new Set(extensionDirsOnDisk);\n\n const layoutViolations: LayoutViolation[] = [];\n for (const dir of extensionDirsOnDisk) {\n if (!declaredSpaceIds.has(dir)) {\n layoutViolations.push({ kind: 'orphanSpaceDir', spaceId: dir });\n }\n }\n for (const id of [...declaredSpaceIds].sort()) {\n if (!spaceDirSet.has(id)) {\n layoutViolations.push({ kind: 'declaredButUnmigrated', spaceId: id });\n }\n }\n if (layoutViolations.length > 0) {\n return notOk({ kind: 'layoutViolation', violations: layoutViolations });\n }\n\n // 3-5. Per-extension: read + validate + integrity-check + drift.\n const loadedExtensions: LoadedExtensionState[] = [];\n for (const entry of [...declaredWithSpace].sort((a, b) => a.id.localeCompare(b.id))) {\n const headRef = await readContractSpaceHeadRef(input.migrationsDir, entry.id);\n if (headRef === null) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: `Head ref \\`refs/head.json\\` is missing for extension space \"${entry.id}\".`,\n });\n }\n\n let spaceContractRaw: unknown;\n try {\n spaceContractRaw = await readContractSpaceContract(input.migrationsDir, entry.id);\n } catch (error) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n let spaceContract: Contract;\n try {\n spaceContract = input.validateContract(spaceContractRaw);\n } catch (error) {\n return notOk({\n kind: 'validationFailure',\n spaceId: entry.id,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n if (spaceContract.target !== input.targetId) {\n return notOk({\n kind: 'targetMismatch',\n spaceId: entry.id,\n expected: input.targetId,\n actual: spaceContract.target,\n });\n }\n\n // Drift: compare descriptor's live `contractJson` to on-disk\n // `refs/head.json.hash`.\n if (entry.contractSpace) {\n const liveHash = input.hashContract(entry.contractSpace.contractJson);\n const drift = detectSpaceContractDrift(entry.id, {\n descriptorHash: liveHash,\n priorHeadHash: headRef.hash,\n });\n if (drift.kind === 'drift') {\n return notOk({\n kind: 'driftViolation',\n spaceId: entry.id,\n priorHeadHash: drift.priorHeadHash ?? '',\n liveHash: drift.descriptorHash,\n });\n }\n }\n\n // Read + integrity-check the migration packages. `readMigrationsDir`\n // re-derives `providedInvariants` and verifies migrationHash for\n // every package.\n let packages: readonly OnDiskMigrationPackage[];\n try {\n packages = await readMigrationsDir(spaceMigrationDirectory(input.migrationsDir, entry.id));\n } catch (error) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n let graph: ReturnType<typeof reconstructGraph>;\n try {\n graph = reconstructGraph(packages);\n } catch (error) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n // The on-disk head ref must be reachable in the graph. Empty graphs\n // are tolerated only when the head ref points at the empty-contract\n // sentinel (a never-emitted extension space; not a typical scenario\n // because the layout precheck would have flagged the missing\n // dir, but defensible).\n if (graph.nodes.size === 0) {\n if (headRef.hash !== EMPTY_CONTRACT_HASH) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: `Head ref \"${headRef.hash}\" is not present in the (empty) on-disk migration graph.`,\n });\n }\n } else if (!graph.nodes.has(headRef.hash)) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: `Head ref \"${headRef.hash}\" is not present in the on-disk migration graph.`,\n });\n }\n\n const packagesByMigrationHash = new Map<string, OnDiskMigrationPackage>(\n packages.map((p) => [p.metadata.migrationHash, p]),\n );\n\n loadedExtensions.push({\n entry,\n contract: spaceContract,\n headRefHash: headRef.hash,\n headRefInvariants: [...headRef.invariants].sort(),\n migrations: { graph, packagesByMigrationHash },\n });\n }\n\n // 6. Build app member with hydrated graph from caller-supplied packages.\n let appGraph: ReturnType<typeof reconstructGraph>;\n try {\n appGraph = reconstructGraph(input.appMigrationPackages);\n } catch (error) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: APP_SPACE_ID,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n const appPackagesByMigrationHash = new Map<string, OnDiskMigrationPackage>(\n input.appMigrationPackages.map((p) => [p.metadata.migrationHash, p]),\n );\n\n const appMember: ContractSpaceMember = {\n spaceId: APP_SPACE_ID,\n contract: input.appContract,\n headRef: {\n hash: input.appContract.storage.storageHash,\n invariants: [],\n },\n migrations: {\n graph: appGraph,\n packagesByMigrationHash: appPackagesByMigrationHash,\n },\n };\n\n const extensionMembers: ContractSpaceMember[] = loadedExtensions.map((s) => ({\n spaceId: s.entry.id,\n contract: s.contract,\n headRef: {\n hash: s.headRefHash,\n invariants: s.headRefInvariants,\n },\n migrations: s.migrations,\n }));\n\n // 7. Disjointness: no two members claim the same storage element.\n const elementClaimedBy = new Map<string, string[]>();\n for (const member of [appMember, ...extensionMembers]) {\n const tables = extractTableNames(member.contract);\n for (const tableName of tables) {\n const claimers = elementClaimedBy.get(tableName);\n if (claimers) claimers.push(member.spaceId);\n else elementClaimedBy.set(tableName, [member.spaceId]);\n }\n }\n for (const [element, claimedBy] of elementClaimedBy) {\n if (claimedBy.length > 1) {\n return notOk({\n kind: 'disjointnessViolation',\n element,\n claimedBy: [...claimedBy].sort(),\n });\n }\n }\n\n return ok({\n aggregate: {\n targetId: input.targetId,\n app: appMember,\n extensions: extensionMembers,\n },\n });\n}\n\n/**\n * Extract the set of top-level storage table names from a contract.\n * Duck-typed: returns `[]` if the contract's storage shape doesn't\n * match the canonical `storage.tables: Record<string, ...>` form. A\n * future family with a different storage shape gets disjointness\n * effectively disabled (not enforced) rather than a hard failure.\n */\nfunction extractTableNames(contract: Contract): readonly string[] {\n const storage = (contract as { readonly storage?: unknown }).storage;\n if (typeof storage !== 'object' || storage === null) return [];\n const tables = (storage as { readonly tables?: unknown }).tables;\n if (typeof tables !== 'object' || tables === null) return [];\n return Object.keys(tables as Record<string, unknown>);\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { MigrationPlan } from '@prisma-next/framework-components/control';\nimport { EMPTY_CONTRACT_HASH } from '../../constants';\nimport { findPathWithDecision } from '../../migration-graph';\nimport type { MigrationOps } from '../../package';\nimport type { ContractMarkerRecordLike } from '../marker-types';\nimport type { AggregatePerSpacePlan } from '../planner-types';\nimport type { ContractSpaceMember } from '../types';\n\n/**\n * Outcome variants for the graph-walk strategy. Mirrors\n * {@link import('../../compute-extension-space-apply-path').ExtensionSpaceApplyPathOutcome}\n * but operates against the **already-hydrated** `member.migrations.graph`\n * instead of re-reading from disk. The aggregate planner converts\n * these into {@link import('../planner-types').AggregatePlannerError}\n * variants.\n */\nexport type GraphWalkOutcome =\n | { readonly kind: 'ok'; readonly result: AggregatePerSpacePlan }\n | { readonly kind: 'unreachable' }\n | { readonly kind: 'unsatisfiable'; readonly missing: readonly string[] };\n\nexport interface GraphWalkStrategyInputs {\n readonly aggregateTargetId: string;\n readonly member: ContractSpaceMember;\n readonly currentMarker: ContractMarkerRecordLike | null;\n}\n\n/**\n * Walk a member's hydrated migration graph from the live marker to\n * `member.headRef.hash`, covering every required invariant.\n *\n * Pure synchronous function — no I/O. The aggregate's loader has\n * already integrity-checked every package and reconstructed the graph;\n * this strategy just looks up ops by `migrationHash` and assembles a\n * `MigrationPlan` with `targetId` set from the aggregate (no\n * placeholder cast).\n *\n * Required invariants are computed as `headRef.invariants \\ marker.invariants`\n * — the marker already declares some invariants satisfied; the path\n * only needs to provide the remainder. Mirrors today's\n * `computeExtensionSpaceApplyPath` semantics.\n */\nexport function graphWalkStrategy(input: GraphWalkStrategyInputs): GraphWalkOutcome {\n const { aggregateTargetId, member, currentMarker } = input;\n const { graph, packagesByMigrationHash } = member.migrations;\n\n const fromHash = currentMarker?.storageHash ?? EMPTY_CONTRACT_HASH;\n const markerInvariants = new Set(currentMarker?.invariants ?? []);\n const required = new Set(member.headRef.invariants.filter((id) => !markerInvariants.has(id)));\n\n const outcome = findPathWithDecision(graph, fromHash, member.headRef.hash, { required });\n\n if (outcome.kind === 'unreachable') {\n return { kind: 'unreachable' };\n }\n if (outcome.kind === 'unsatisfiable') {\n return { kind: 'unsatisfiable', missing: outcome.missing };\n }\n\n const pathOps: MigrationOps[number][] = [];\n const providedInvariantsSet = new Set<string>();\n for (const edge of outcome.decision.selectedPath) {\n const pkg = packagesByMigrationHash.get(edge.migrationHash);\n if (!pkg) {\n throw new Error(\n `Migration package missing for edge ${edge.migrationHash} in space \"${member.spaceId}\". The hydrated migration graph and packagesByMigrationHash map are out of sync — this should be unreachable; report.`,\n );\n }\n for (const op of pkg.ops) pathOps.push(op);\n for (const invariant of pkg.metadata.providedInvariants) providedInvariantsSet.add(invariant);\n }\n\n const plan: MigrationPlan = {\n targetId: aggregateTargetId,\n spaceId: member.spaceId,\n origin: currentMarker === null ? null : { storageHash: currentMarker.storageHash },\n destination: { storageHash: member.headRef.hash },\n operations: pathOps,\n providedInvariants: [...providedInvariantsSet].sort(),\n };\n\n return {\n kind: 'ok',\n result: {\n plan,\n displayOps: pathOps,\n destinationContract: member.contract as Contract,\n strategy: 'graph-walk',\n },\n };\n}\n","import type { ContractSpaceMember } from './types';\n\n/**\n * Project the introspected live schema to the slice claimed by a single\n * contract-space member.\n *\n * Returns the same `schema` value with every top-level table claimed by\n * **other** members of the aggregate removed. Tables not claimed by any\n * member flow through unchanged — the planner / verifier sees them as\n * orphans (extras in strict mode).\n *\n * Used by:\n *\n * - The aggregate planner's **synth strategy**: when synthesising a\n * plan against a member's contract, the live schema must be projected\n * to that member's slice so the planner doesn't treat tables claimed\n * by other members as \"extras\" and emit destructive ops to drop\n * them.\n * - The aggregate verifier's **schemaCheck**: projects per member so the\n * single-contract `verifySqlSchema` only sees the slice claimed by\n * the member it is checking. Closes the F23 architectural concern\n * (multi-member deployments where each member's tables look like\n * extras to every other member's verify pass).\n *\n * **Duck-typing semantics**: the helper operates on `unknown` for the\n * schema and falls through structurally if the shape doesn't match.\n * Every family today exposes `storage.tables: Record<string, ...>` and\n * the introspected schema mirrors the same shape; a future family with\n * a different storage shape gets the schema returned unchanged rather\n * than blowing up the aggregate planner.\n */\nexport function projectSchemaToSpace(\n schema: unknown,\n member: ContractSpaceMember,\n otherMembers: ReadonlyArray<ContractSpaceMember>,\n): unknown {\n if (typeof schema !== 'object' || schema === null) return schema;\n const schemaObj = schema as { readonly tables?: unknown };\n if (typeof schemaObj.tables !== 'object' || schemaObj.tables === null) return schema;\n const schemaTables = schemaObj.tables as Record<string, unknown>;\n\n const ownedByOthers = new Set<string>();\n for (const other of otherMembers) {\n if (other.spaceId === member.spaceId) continue;\n const storage = (other.contract as { readonly storage?: unknown }).storage;\n if (typeof storage !== 'object' || storage === null) continue;\n const tables = (storage as { readonly tables?: unknown }).tables;\n if (typeof tables !== 'object' || tables === null) continue;\n for (const tableName of Object.keys(tables as Record<string, unknown>)) {\n ownedByOthers.add(tableName);\n }\n }\n\n if (ownedByOthers.size === 0) return schema;\n\n const prunedTables: Record<string, unknown> = {};\n for (const [name, table] of Object.entries(schemaTables)) {\n if (!ownedByOthers.has(name)) {\n prunedTables[name] = table;\n }\n }\n\n return { ...schemaObj, tables: prunedTables };\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlFamilyInstance,\n MigrationOperationPolicy,\n MigrationPlan,\n MigrationPlannerConflict,\n MigrationPlannerResult,\n TargetMigrationsCapability,\n} from '@prisma-next/framework-components/control';\nimport type { AggregatePerSpacePlan } from '../planner-types';\nimport { projectSchemaToSpace } from '../project-schema-to-space';\nimport type { ContractSpaceMember } from '../types';\n\nexport interface SynthStrategyInputs<TFamilyId extends string, TTargetId extends string> {\n readonly aggregateTargetId: string;\n readonly member: ContractSpaceMember;\n readonly otherMembers: ReadonlyArray<ContractSpaceMember>;\n readonly schemaIntrospection: unknown;\n readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;\n readonly migrations: TargetMigrationsCapability<\n TFamilyId,\n TTargetId,\n ControlFamilyInstance<TFamilyId, unknown>\n >;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;\n readonly operationPolicy: MigrationOperationPolicy;\n}\n\nexport type SynthStrategyOutcome =\n | { readonly kind: 'ok'; readonly result: AggregatePerSpacePlan }\n | { readonly kind: 'failure'; readonly conflicts: readonly MigrationPlannerConflict[] };\n\n/**\n * The {@link MigrationPlanner.plan} interface is declared as synchronous,\n * but historical and test fixture call sites have always invoked it\n * with `await` (see prior `db-apply-per-space.ts`). Tolerating a\n * Promise here keeps existing test mocks working without changing the\n * declared family SPI.\n */\ntype MaybeAsyncPlannerResult = MigrationPlannerResult | Promise<MigrationPlannerResult>;\n\n/**\n * Synthesise a migration plan for a single member by projecting the\n * live schema down to that member's claimed slice and delegating to\n * the family's `createPlanner(...).plan(...)`.\n *\n * Pre-projection (via {@link projectSchemaToSpace}) closes the F23\n * concern: without it, the family's planner sees other members'\n * tables as \"extras\" and emits destructive ops to drop them. With it,\n * the planner only sees the slice this member claims.\n *\n * The synthesised plan's `targetId` is set from `aggregateTargetId`\n * (the aggregate's ambient target). The family's planner does not\n * stamp `targetId` on the produced plan; the aggregate planner is\n * the single point that knows the target.\n *\n * Used by:\n *\n * - The app member by default (CLI policy\n * `ignoreGraphFor: { app.spaceId }`).\n * - Any extension member whose `headRef.invariants` is empty (the\n * strategy selector falls back to synth when graph-walk isn't\n * required).\n */\nexport async function synthStrategy<TFamilyId extends string, TTargetId extends string>(\n input: SynthStrategyInputs<TFamilyId, TTargetId>,\n): Promise<SynthStrategyOutcome> {\n const projectedSchema = projectSchemaToSpace(\n input.schemaIntrospection,\n input.member,\n input.otherMembers,\n );\n\n const planner = input.migrations.createPlanner(input.familyInstance);\n const plannerResult: MigrationPlannerResult = await (planner.plan({\n contract: input.member.contract,\n schema: projectedSchema,\n policy: input.operationPolicy,\n fromContract: null,\n frameworkComponents: input.frameworkComponents,\n spaceId: input.member.spaceId,\n }) as MaybeAsyncPlannerResult);\n\n if (plannerResult.kind === 'failure') {\n return { kind: 'failure', conflicts: plannerResult.conflicts };\n }\n\n const synthedPlan = plannerResult.plan;\n // The family planner returns a class-instance-shaped plan whose\n // `destination` / `operations` are accessors on the prototype, often\n // backed by private fields. A naive spread (`{ ...synthedPlan }`)\n // would lose those accessors and produce a plan with\n // `destination: undefined`; rebinding the prototype on a plain\n // object would break private-field access. We instead wrap the plan\n // in a Proxy that forwards every read except `targetId`, which is\n // stamped from the aggregate's ambient target. This preserves the\n // planner's class semantics while keeping the aggregate the single\n // source of truth for `targetId`.\n const plan: MigrationPlan = new Proxy(synthedPlan, {\n get(target, prop) {\n if (prop === 'targetId') return input.aggregateTargetId;\n // Forward `this` as the original target so prototype-bound\n // private fields (#destination, #operations, …) resolve.\n return Reflect.get(target, prop, target);\n },\n has(target, prop) {\n if (prop === 'targetId') return true;\n return Reflect.has(target, prop);\n },\n });\n\n return {\n kind: 'ok',\n result: {\n plan,\n displayOps: synthedPlan.operations,\n destinationContract: input.member.contract as Contract,\n strategy: 'synth',\n },\n };\n}\n","import { notOk, ok } from '@prisma-next/utils/result';\nimport type {\n AggregatePerSpacePlan,\n AggregatePlannerError,\n AggregatePlannerInput,\n AggregatePlannerOutput,\n} from './planner-types';\nimport { graphWalkStrategy } from './strategies/graph-walk';\nimport { synthStrategy } from './strategies/synth';\nimport type { ContractSpaceMember } from './types';\n\nexport type {\n AggregateCurrentDBState,\n AggregatePerSpacePlan,\n AggregatePlannerError,\n AggregatePlannerInput,\n AggregatePlannerOutput,\n AggregatePlannerSuccess,\n CallerPolicy,\n} from './planner-types';\n\n/**\n * Plan a migration across every member of a {@link ContractSpaceAggregate}.\n *\n * Strategy selection per member, in order; first match wins:\n *\n * 1. If `callerPolicy.ignoreGraphFor.has(member.spaceId)`:\n * - If `member.headRef.invariants` is empty → synth.\n * - Else → `policyConflict` (synth cannot satisfy authored invariants).\n * 2. Else if `member.migrations.graph` is non-empty AND graph-walk\n * succeeds → graph-walk.\n * 3. Else if `member.headRef.invariants` is empty → synth.\n * 4. Else → graph-walk failure → `extensionPathUnreachable` /\n * `extensionPathUnsatisfiable`.\n *\n * Output `applyOrder` is `[...aggregate.extensions.map(spaceId), aggregate.app.spaceId]`\n * — extensions alphabetical, then app — matching today's\n * `concatenateSpaceApplyInputs` ordering. This preserves\n * `MultiSpaceRunnerFailure.failingSpace` attribution byte-for-byte.\n *\n * Every emitted `MigrationPlan` has `targetId = aggregate.targetId`.\n * No placeholder cast; no patch step.\n */\nexport async function planAggregate<TFamilyId extends string, TTargetId extends string>(\n input: AggregatePlannerInput<TFamilyId, TTargetId>,\n): Promise<AggregatePlannerOutput> {\n const { aggregate, currentDBState, callerPolicy } = input;\n const allMembers: ReadonlyArray<ContractSpaceMember> = [aggregate.app, ...aggregate.extensions];\n\n const perSpace = new Map<string, AggregatePerSpacePlan>();\n\n // Iterate in apply order so a per-member error short-circuits the\n // walk in the same order the runner would walk inputs.\n const orderedMembers: ReadonlyArray<ContractSpaceMember> = [\n ...aggregate.extensions,\n aggregate.app,\n ];\n\n for (const member of orderedMembers) {\n const otherMembers = allMembers.filter((m) => m.spaceId !== member.spaceId);\n const currentMarker = currentDBState.markersBySpaceId.get(member.spaceId) ?? null;\n\n const ignoreGraph = callerPolicy.ignoreGraphFor.has(member.spaceId);\n const invariantsRequired = member.headRef.invariants.length > 0;\n\n if (ignoreGraph && invariantsRequired) {\n const conflict: AggregatePlannerError = {\n kind: 'policyConflict',\n spaceId: member.spaceId,\n detail: `\\`callerPolicy.ignoreGraphFor\\` requested for space \"${member.spaceId}\", but the member declares non-empty head-ref invariants (${member.headRef.invariants.join(', ')}). Synthesising a plan from the contract IR cannot satisfy authored invariants — the graph must be walked. Either remove \"${member.spaceId}\" from \\`ignoreGraphFor\\` or amend the on-disk head ref to declare zero invariants.`,\n };\n return notOk(conflict);\n }\n\n if (ignoreGraph) {\n const synthOutcome = await synthStrategy({\n aggregateTargetId: aggregate.targetId,\n member,\n otherMembers,\n schemaIntrospection: currentDBState.schemaIntrospection,\n familyInstance: input.familyInstance,\n migrations: input.migrations,\n frameworkComponents: input.frameworkComponents,\n operationPolicy: input.operationPolicy,\n });\n if (synthOutcome.kind === 'failure') {\n return notOk({\n kind: 'appSynthFailure',\n spaceId: member.spaceId,\n conflicts: synthOutcome.conflicts,\n });\n }\n perSpace.set(member.spaceId, synthOutcome.result);\n continue;\n }\n\n // Try graph-walk first when the graph has nodes; fall back to synth\n // when the graph is empty AND no invariants are required.\n if (member.migrations.graph.nodes.size > 0) {\n const walked = graphWalkStrategy({\n aggregateTargetId: aggregate.targetId,\n member,\n currentMarker,\n });\n if (walked.kind === 'ok') {\n perSpace.set(member.spaceId, walked.result);\n continue;\n }\n if (walked.kind === 'unreachable') {\n return notOk({\n kind: 'extensionPathUnreachable',\n spaceId: member.spaceId,\n target: member.headRef.hash,\n });\n }\n // unsatisfiable — surface\n return notOk({\n kind: 'extensionPathUnsatisfiable',\n spaceId: member.spaceId,\n missingInvariants: walked.missing,\n });\n }\n\n // Empty graph: synth is the only option, and it can only satisfy\n // empty-invariant members.\n if (invariantsRequired) {\n return notOk({\n kind: 'extensionPathUnsatisfiable',\n spaceId: member.spaceId,\n missingInvariants: [...member.headRef.invariants].sort(),\n });\n }\n\n const synthOutcome = await synthStrategy({\n aggregateTargetId: aggregate.targetId,\n member,\n otherMembers,\n schemaIntrospection: currentDBState.schemaIntrospection,\n familyInstance: input.familyInstance,\n migrations: input.migrations,\n frameworkComponents: input.frameworkComponents,\n operationPolicy: input.operationPolicy,\n });\n if (synthOutcome.kind === 'failure') {\n return notOk({\n kind: 'appSynthFailure',\n spaceId: member.spaceId,\n conflicts: synthOutcome.conflicts,\n });\n }\n perSpace.set(member.spaceId, synthOutcome.result);\n }\n\n return ok({\n perSpace,\n applyOrder: [...aggregate.extensions.map((m) => m.spaceId), aggregate.app.spaceId],\n });\n}\n","import type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport type { ContractMarkerRecordLike } from './marker-types';\nimport { projectSchemaToSpace } from './project-schema-to-space';\nimport type { ContractSpaceAggregate, ContractSpaceMember } from './types';\n\n/**\n * Caller policy for the aggregate verifier. Today's only knob is\n * `mode`: `strict` treats orphan elements (live tables not claimed by\n * any aggregate member) as errors; `lenient` treats them as\n * informational. Maps directly to `db verify --strict`.\n */\nexport interface AggregateVerifierInput<TSchemaResult> {\n readonly aggregate: ContractSpaceAggregate;\n readonly markersBySpaceId: ReadonlyMap<string, ContractMarkerRecordLike | null>;\n readonly schemaIntrospection: unknown;\n readonly mode: 'strict' | 'lenient';\n /**\n * Caller-supplied per-space schema verifier. The CLI wires this to\n * the family's `verifySqlSchema` (SQL) / equivalent (other\n * families). The aggregate verifier projects the schema to the\n * member's slice via {@link projectSchemaToSpace} before invoking\n * the callback, so single-contract semantics are preserved.\n *\n * Typed structurally with a generic `TSchemaResult` so the\n * migration-tools layer doesn't depend on the SQL family's\n * `VerifySqlSchemaResult`. CLI callers pass the family's type\n * through unchanged.\n */\n readonly verifySchemaForMember: (\n projectedSchema: unknown,\n member: ContractSpaceMember,\n mode: 'strict' | 'lenient',\n ) => TSchemaResult;\n}\n\n/**\n * Marker-check result per member. Mirrors the four cases the\n * `verifyContractSpaces` primitive surfaces today, plus an `'absent'`\n * case for greenfield spaces (no marker row written yet — `db init`\n * not run).\n */\nexport type MarkerCheckResult =\n | { readonly kind: 'ok' }\n | { readonly kind: 'absent' }\n | {\n readonly kind: 'hashMismatch';\n readonly markerHash: string;\n readonly expected: string;\n }\n | { readonly kind: 'missingInvariants'; readonly missing: readonly string[] };\n\nexport interface MarkerCheckSection {\n readonly perSpace: ReadonlyMap<string, MarkerCheckResult>;\n readonly orphanMarkers: readonly {\n readonly spaceId: string;\n readonly row: ContractMarkerRecordLike;\n }[];\n}\n\n/**\n * A live storage element (today: a top-level table) not claimed by any\n * member of the aggregate. The aggregate verifier always reports these;\n * the caller decides what to do — `db verify --strict` treats them as\n * errors, the lenient default treats them as informational.\n *\n * Today only `kind: 'table'` exists. The discriminated shape leaves\n * room for orphan columns / indexes / sequences in the future without\n * breaking the type contract.\n */\nexport type OrphanElement = { readonly kind: 'table'; readonly name: string };\n\nexport interface SchemaCheckSection<TSchemaResult> {\n readonly perSpace: ReadonlyMap<string, TSchemaResult>;\n /**\n * Live elements present in the introspected schema that are not\n * claimed by **any** aggregate member. Sorted alphabetically by name.\n */\n readonly orphanElements: readonly OrphanElement[];\n}\n\nexport interface AggregateVerifierSuccess<TSchemaResult> {\n readonly markerCheck: MarkerCheckSection;\n readonly schemaCheck: SchemaCheckSection<TSchemaResult>;\n}\n\nexport type AggregateVerifierError = {\n readonly kind: 'introspectionFailure';\n readonly detail: string;\n};\n\nexport type AggregateVerifierOutput<TSchemaResult> = Result<\n AggregateVerifierSuccess<TSchemaResult>,\n AggregateVerifierError\n>;\n\n/**\n * Verify a {@link ContractSpaceAggregate} against the live database\n * state. Bundles two checks:\n *\n * - `markerCheck` per member: compare the live marker row against the\n * member's `headRef.hash` + `headRef.invariants`. Absence is a\n * distinct kind, not an error (callers — `db verify` strict vs\n * `db init` precondition — choose how to interpret it).\n * - `schemaCheck` per member: project the live schema to the slice\n * the member claims via {@link projectSchemaToSpace}, then delegate\n * to the caller-supplied `verifySchemaForMember`. The pre-projection\n * means the family's single-contract verifier no longer sees other\n * members' tables as `extras`, so a multi-member deployment never\n * surfaces cross-member tables as orphaned schema elements.\n *\n * `markerCheck.orphanMarkers` lists every marker row whose `space` is\n * not a member of the aggregate. `db verify` callers reject orphans;\n * future tooling may not.\n *\n * Pure synchronous function; no I/O. The caller (CLI) gathers\n * `markersBySpaceId` and `schemaIntrospection` ahead of the call.\n */\nexport function verifyAggregate<TSchemaResult>(\n input: AggregateVerifierInput<TSchemaResult>,\n): AggregateVerifierOutput<TSchemaResult> {\n try {\n return runVerifyAggregate(input);\n } catch (error) {\n return notOk({\n kind: 'introspectionFailure',\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\nfunction runVerifyAggregate<TSchemaResult>(\n input: AggregateVerifierInput<TSchemaResult>,\n): AggregateVerifierOutput<TSchemaResult> {\n const { aggregate, markersBySpaceId, schemaIntrospection, mode, verifySchemaForMember } = input;\n const allMembers: ReadonlyArray<ContractSpaceMember> = [aggregate.app, ...aggregate.extensions];\n const memberSpaceIds = new Set(allMembers.map((m) => m.spaceId));\n\n // Marker check per member.\n const markerPerSpace = new Map<string, MarkerCheckResult>();\n for (const member of allMembers) {\n const marker = markersBySpaceId.get(member.spaceId) ?? null;\n if (marker === null) {\n markerPerSpace.set(member.spaceId, { kind: 'absent' });\n continue;\n }\n if (marker.storageHash !== member.headRef.hash) {\n markerPerSpace.set(member.spaceId, {\n kind: 'hashMismatch',\n markerHash: marker.storageHash,\n expected: member.headRef.hash,\n });\n continue;\n }\n const markerInvariants = new Set(marker.invariants);\n const missing = member.headRef.invariants.filter((id) => !markerInvariants.has(id));\n if (missing.length > 0) {\n markerPerSpace.set(member.spaceId, {\n kind: 'missingInvariants',\n missing: [...missing].sort(),\n });\n continue;\n }\n markerPerSpace.set(member.spaceId, { kind: 'ok' });\n }\n\n // Orphan markers: entries in markersBySpaceId whose spaceId is not a\n // member of the aggregate.\n const orphanMarkers: { spaceId: string; row: ContractMarkerRecordLike }[] = [];\n for (const [spaceId, row] of markersBySpaceId) {\n if (row !== null && !memberSpaceIds.has(spaceId)) {\n orphanMarkers.push({ spaceId, row });\n }\n }\n orphanMarkers.sort((a, b) => a.spaceId.localeCompare(b.spaceId));\n\n // Schema check per member (with per-space pre-projection).\n const schemaPerSpace = new Map<string, TSchemaResult>();\n for (const member of allMembers) {\n const others = allMembers.filter((m) => m.spaceId !== member.spaceId);\n const projected = projectSchemaToSpace(schemaIntrospection, member, others);\n schemaPerSpace.set(member.spaceId, verifySchemaForMember(projected, member, mode));\n }\n\n return ok({\n markerCheck: {\n perSpace: markerPerSpace,\n orphanMarkers,\n },\n schemaCheck: {\n perSpace: schemaPerSpace,\n orphanElements: detectOrphanElements(schemaIntrospection, allMembers),\n },\n });\n}\n\n/**\n * Live tables not claimed by any aggregate member. Duck-typed against\n * the introspected schema's `tables` map; schemas whose shape doesn't\n * match return an empty list (consistent with\n * {@link projectSchemaToSpace}'s fall-through).\n */\nfunction detectOrphanElements(\n schemaIntrospection: unknown,\n members: ReadonlyArray<ContractSpaceMember>,\n): readonly OrphanElement[] {\n if (typeof schemaIntrospection !== 'object' || schemaIntrospection === null) return [];\n const liveTables = (schemaIntrospection as { readonly tables?: unknown }).tables;\n if (typeof liveTables !== 'object' || liveTables === null) return [];\n\n const claimedTables = new Set<string>();\n for (const member of members) {\n const storage = (member.contract as { readonly storage?: unknown }).storage;\n if (typeof storage !== 'object' || storage === null) continue;\n const tables = (storage as { readonly tables?: unknown }).tables;\n if (typeof tables !== 'object' || tables === null) continue;\n for (const tableName of Object.keys(tables as Record<string, unknown>)) {\n claimedTables.add(tableName);\n }\n }\n\n const orphans: OrphanElement[] = [];\n for (const tableName of Object.keys(liveTables as Record<string, unknown>)) {\n if (!claimedTables.has(tableName)) {\n orphans.push({ kind: 'table', name: tableName });\n }\n }\n orphans.sort((a, b) => a.name.localeCompare(b.name));\n return orphans;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"aggregate.mjs","names":[],"sources":["../../src/aggregate/loader.ts","../../src/aggregate/strategies/graph-walk.ts","../../src/aggregate/project-schema-to-space.ts","../../src/aggregate/strategies/synth.ts","../../src/aggregate/planner.ts","../../src/aggregate/verifier.ts"],"sourcesContent":["import type { Contract } from '@prisma-next/contract/types';\nimport { notOk, ok, type Result } from '@prisma-next/utils/result';\nimport { EMPTY_CONTRACT_HASH } from '../constants';\nimport { detectSpaceContractDrift } from '../detect-space-contract-drift';\nimport { readMigrationsDir } from '../io';\nimport { reconstructGraph } from '../migration-graph';\nimport type { OnDiskMigrationPackage } from '../package';\nimport { readContractSpaceContract } from '../read-contract-space-contract';\nimport { readContractSpaceHeadRef } from '../read-contract-space-head-ref';\nimport { APP_SPACE_ID, spaceMigrationDirectory } from '../space-layout';\nimport { listContractSpaceDirectories } from '../verify-contract-spaces';\nimport type { ContractSpaceAggregate, ContractSpaceMember, HydratedMigrationGraph } from './types';\n\n/**\n * Hash function used by drift detection. Defaults to a canonical-JSON +\n * SHA-256 pipeline that mirrors the framework's contract-hash convention,\n * but the loader accepts a callback so SQL-family callers can pass their\n * `coreHash` / `storageHash` derivation through unchanged.\n *\n * The contract value passed in is the framework-neutral `unknown` form;\n * callers that have already validated typed contracts can simply hand\n * the validated value back through.\n */\nexport type AggregateContractHasher = (contract: unknown) => string;\n\n/**\n * Single declared extension entry the loader needs from `Config.extensionPacks`.\n *\n * Only the subset of fields the loader operates on:\n *\n * - `id` — the space id (also the directory name under `migrations/`).\n * - `targetId` — the configured `Config.adapter.targetId` value the\n * declaring extension declared. The loader rejects mismatches against\n * the aggregate's `targetId` with `targetMismatch`.\n * - `contractSpace` — present iff the descriptor declares a contract\n * space (extensions can ship without one and remain runtime-only /\n * codec-only). Drift detection compares the descriptor's\n * `contractJson` hash against the on-disk on-disk head hash; the loader\n * rejects drift fatally.\n *\n * Typed structurally so the migration-tools layer stays framework-neutral.\n */\nexport interface DeclaredExtensionEntry {\n readonly id: string;\n readonly targetId: string;\n readonly contractSpace?: {\n readonly contractJson: unknown;\n };\n}\n\n/**\n * Inputs for {@link loadContractSpaceAggregate}.\n *\n * The loader is the **sole** descriptor-import boundary in the M2.5\n * pipeline: callers gather the descriptor data (already-validated app\n * contract, declared extension entries) and pass it through. Once the\n * loader returns, no descriptor module is imported again for this\n * aggregate's lifetime.\n */\nexport interface LoadAggregateInput {\n readonly targetId: string;\n readonly migrationsDir: string;\n readonly appContract: Contract;\n readonly declaredExtensions: ReadonlyArray<DeclaredExtensionEntry>;\n readonly validateContract: (contractJson: unknown) => Contract;\n readonly hashContract: AggregateContractHasher;\n /**\n * Hydrated migration graph for the **app member**.\n *\n * The framework-neutral migration-tools layer doesn't know how to read\n * the user's authored `migrations/` directory (the app member's\n * migration-package layout is family-aware: ops.json shape, manifest\n * keys, etc.). Callers — the SQL family today — read the user's\n * `migrations/` and hand the resulting `OnDiskMigrationPackage[]` through.\n *\n * Passing `[]` is valid (greenfield project, no authored migrations).\n * Equivalent to `migrations/` not existing or being empty.\n */\n readonly appMigrationPackages: ReadonlyArray<OnDiskMigrationPackage>;\n}\n\n/**\n * Discriminated failure variants the loader emits.\n *\n * Every variant short-circuits at first hit; the loader does not keep\n * collecting after the first violation in any phase except for layout\n * (where every layout offence is bundled into one `layoutViolation`).\n */\nexport type LoadAggregateError =\n | { readonly kind: 'layoutViolation'; readonly violations: readonly LayoutViolation[] }\n | { readonly kind: 'integrityFailure'; readonly spaceId: string; readonly detail: string }\n | { readonly kind: 'validationFailure'; readonly spaceId: string; readonly detail: string }\n | {\n readonly kind: 'driftViolation';\n readonly spaceId: string;\n readonly priorHeadHash: string;\n readonly liveHash: string;\n }\n | {\n readonly kind: 'disjointnessViolation';\n readonly element: string;\n readonly claimedBy: readonly string[];\n }\n | {\n readonly kind: 'targetMismatch';\n readonly spaceId: string;\n readonly expected: string;\n readonly actual: string;\n };\n\n/**\n * Single layout violation; bundled into a `layoutViolation` error so\n * users see every layout offence at once rather than fixing them one\n * at a time across re-runs.\n *\n * - `declaredButUnmigrated`: extension declared in `extensionPacks` with\n * a `contractSpace` but no contract-space dir on disk. Remediation:\n * `prisma-next migrate`.\n * - `orphanSpaceDir`: contract-space dir under `migrations/` for an extension\n * not in `extensionPacks`. Remediation: remove the directory, or\n * re-add the extension to `extensionPacks`.\n */\nexport type LayoutViolation =\n | { readonly kind: 'declaredButUnmigrated'; readonly spaceId: string }\n | { readonly kind: 'orphanSpaceDir'; readonly spaceId: string };\n\nexport type LoadAggregateOutput = Result<\n { readonly aggregate: ContractSpaceAggregate },\n LoadAggregateError\n>;\n\ninterface LoadedExtensionState {\n readonly entry: DeclaredExtensionEntry;\n readonly contract: Contract;\n readonly headRefHash: string;\n readonly headRefInvariants: readonly string[];\n readonly migrations: HydratedMigrationGraph;\n}\n\n/**\n * Hydrate a {@link ContractSpaceAggregate} from on-disk state and\n * caller-provided descriptor data.\n *\n * This is the **only** descriptor-import boundary in the post-M2.5\n * pipeline: callers read `extensionPacks` from `Config`, validate the\n * app contract, and pass everything through. The loader composes\n * existing migration-tools primitives — layout precheck (via\n * {@link listContractSpaceDirectories}), integrity checks (via\n * {@link readMigrationsDir} / {@link readContractSpaceHeadRef} /\n * {@link readContractSpaceContract} / `validateContract`), drift detection\n * (via {@link detectSpaceContractDrift}), and disjointness — into a\n * single typed value.\n *\n * Failure semantics: every failure variant in {@link LoadAggregateError}\n * short-circuits the load. Drift is fatal (M2.5 spec § Loader, step 5).\n */\nexport async function loadContractSpaceAggregate(\n input: LoadAggregateInput,\n): Promise<LoadAggregateOutput> {\n // 1. Validate target consistency on the app contract.\n const appContractTarget = input.appContract.target;\n if (appContractTarget !== input.targetId) {\n return notOk({\n kind: 'targetMismatch',\n spaceId: APP_SPACE_ID,\n expected: input.targetId,\n actual: appContractTarget,\n });\n }\n\n for (const entry of input.declaredExtensions) {\n if (entry.targetId !== input.targetId) {\n return notOk({\n kind: 'targetMismatch',\n spaceId: entry.id,\n expected: input.targetId,\n actual: entry.targetId,\n });\n }\n }\n\n // 2. Layout precheck: bundle every layout offence at once.\n const declaredWithSpace = input.declaredExtensions.filter((e) => e.contractSpace !== undefined);\n const declaredSpaceIds = new Set(declaredWithSpace.map((e) => e.id));\n const allDirs = await listContractSpaceDirectories(input.migrationsDir);\n // The app member is implicitly declared (it is always part of the\n // aggregate); its `migrations/<APP_SPACE_ID>/` directory may exist or\n // not (greenfield projects start with neither). Filter it out of the\n // orphan / declared-but-unmigrated checks so the layout precheck is\n // about extensions only.\n const extensionDirsOnDisk = allDirs.filter((d) => d !== APP_SPACE_ID);\n const spaceDirSet = new Set(extensionDirsOnDisk);\n\n const layoutViolations: LayoutViolation[] = [];\n for (const dir of extensionDirsOnDisk) {\n if (!declaredSpaceIds.has(dir)) {\n layoutViolations.push({ kind: 'orphanSpaceDir', spaceId: dir });\n }\n }\n for (const id of [...declaredSpaceIds].sort()) {\n if (!spaceDirSet.has(id)) {\n layoutViolations.push({ kind: 'declaredButUnmigrated', spaceId: id });\n }\n }\n if (layoutViolations.length > 0) {\n return notOk({ kind: 'layoutViolation', violations: layoutViolations });\n }\n\n // 3-5. Per-extension: read + validate + integrity-check + drift.\n const loadedExtensions: LoadedExtensionState[] = [];\n for (const entry of [...declaredWithSpace].sort((a, b) => a.id.localeCompare(b.id))) {\n const headRef = await readContractSpaceHeadRef(input.migrationsDir, entry.id);\n if (headRef === null) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: `Head ref \\`refs/head.json\\` is missing for extension space \"${entry.id}\".`,\n });\n }\n\n let spaceContractRaw: unknown;\n try {\n spaceContractRaw = await readContractSpaceContract(input.migrationsDir, entry.id);\n } catch (error) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n let spaceContract: Contract;\n try {\n spaceContract = input.validateContract(spaceContractRaw);\n } catch (error) {\n return notOk({\n kind: 'validationFailure',\n spaceId: entry.id,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n if (spaceContract.target !== input.targetId) {\n return notOk({\n kind: 'targetMismatch',\n spaceId: entry.id,\n expected: input.targetId,\n actual: spaceContract.target,\n });\n }\n\n // Drift: compare descriptor's live `contractJson` to on-disk\n // `refs/head.json.hash`.\n if (entry.contractSpace) {\n const liveHash = input.hashContract(entry.contractSpace.contractJson);\n const drift = detectSpaceContractDrift(entry.id, {\n descriptorHash: liveHash,\n priorHeadHash: headRef.hash,\n });\n if (drift.kind === 'drift') {\n return notOk({\n kind: 'driftViolation',\n spaceId: entry.id,\n priorHeadHash: drift.priorHeadHash ?? '',\n liveHash: drift.descriptorHash,\n });\n }\n }\n\n // Read + integrity-check the migration packages. `readMigrationsDir`\n // re-derives `providedInvariants` and verifies migrationHash for\n // every package.\n let packages: readonly OnDiskMigrationPackage[];\n try {\n packages = await readMigrationsDir(spaceMigrationDirectory(input.migrationsDir, entry.id));\n } catch (error) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n let graph: ReturnType<typeof reconstructGraph>;\n try {\n graph = reconstructGraph(packages);\n } catch (error) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n\n // The on-disk head ref must be reachable in the graph. Empty graphs\n // are tolerated only when the head ref points at the empty-contract\n // sentinel (a never-emitted extension space; not a typical scenario\n // because the layout precheck would have flagged the missing\n // dir, but defensible).\n if (graph.nodes.size === 0) {\n if (headRef.hash !== EMPTY_CONTRACT_HASH) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: `Head ref \"${headRef.hash}\" is not present in the (empty) on-disk migration graph.`,\n });\n }\n } else if (!graph.nodes.has(headRef.hash)) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: entry.id,\n detail: `Head ref \"${headRef.hash}\" is not present in the on-disk migration graph.`,\n });\n }\n\n const packagesByMigrationHash = new Map<string, OnDiskMigrationPackage>(\n packages.map((p) => [p.metadata.migrationHash, p]),\n );\n\n loadedExtensions.push({\n entry,\n contract: spaceContract,\n headRefHash: headRef.hash,\n headRefInvariants: [...headRef.invariants].sort(),\n migrations: { graph, packagesByMigrationHash },\n });\n }\n\n // 6. Build app member with hydrated graph from caller-supplied packages.\n let appGraph: ReturnType<typeof reconstructGraph>;\n try {\n appGraph = reconstructGraph(input.appMigrationPackages);\n } catch (error) {\n return notOk({\n kind: 'integrityFailure',\n spaceId: APP_SPACE_ID,\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n const appPackagesByMigrationHash = new Map<string, OnDiskMigrationPackage>(\n input.appMigrationPackages.map((p) => [p.metadata.migrationHash, p]),\n );\n\n const appMember: ContractSpaceMember = {\n spaceId: APP_SPACE_ID,\n contract: input.appContract,\n headRef: {\n hash: input.appContract.storage.storageHash,\n invariants: [],\n },\n migrations: {\n graph: appGraph,\n packagesByMigrationHash: appPackagesByMigrationHash,\n },\n };\n\n const extensionMembers: ContractSpaceMember[] = loadedExtensions.map((s) => ({\n spaceId: s.entry.id,\n contract: s.contract,\n headRef: {\n hash: s.headRefHash,\n invariants: s.headRefInvariants,\n },\n migrations: s.migrations,\n }));\n\n // 7. Disjointness: no two members claim the same storage element.\n const elementClaimedBy = new Map<string, string[]>();\n for (const member of [appMember, ...extensionMembers]) {\n const tables = extractTableNames(member.contract);\n for (const tableName of tables) {\n const claimers = elementClaimedBy.get(tableName);\n if (claimers) claimers.push(member.spaceId);\n else elementClaimedBy.set(tableName, [member.spaceId]);\n }\n }\n for (const [element, claimedBy] of elementClaimedBy) {\n if (claimedBy.length > 1) {\n return notOk({\n kind: 'disjointnessViolation',\n element,\n claimedBy: [...claimedBy].sort(),\n });\n }\n }\n\n return ok({\n aggregate: {\n targetId: input.targetId,\n app: appMember,\n extensions: extensionMembers,\n },\n });\n}\n\n/**\n * Extract the set of top-level storage table names from a contract.\n * Duck-typed: returns `[]` if the contract's storage shape doesn't\n * match the canonical `storage.tables: Record<string, ...>` form. A\n * future family with a different storage shape gets disjointness\n * effectively disabled (not enforced) rather than a hard failure.\n */\nfunction extractTableNames(contract: Contract): readonly string[] {\n const storage = (contract as { readonly storage?: unknown }).storage;\n if (typeof storage !== 'object' || storage === null) return [];\n const tables = (storage as { readonly tables?: unknown }).tables;\n if (typeof tables !== 'object' || tables === null) return [];\n return Object.keys(tables as Record<string, unknown>);\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { MigrationPlan } from '@prisma-next/framework-components/control';\nimport { EMPTY_CONTRACT_HASH } from '../../constants';\nimport { findPathWithDecision } from '../../migration-graph';\nimport type { MigrationOps } from '../../package';\nimport type { ContractMarkerRecordLike } from '../marker-types';\nimport type { AggregatePerSpacePlan } from '../planner-types';\nimport type { ContractSpaceMember } from '../types';\n\n/**\n * Outcome variants for the graph-walk strategy. Mirrors\n * {@link import('../../compute-extension-space-apply-path').ExtensionSpaceApplyPathOutcome}\n * but operates against the **already-hydrated** `member.migrations.graph`\n * instead of re-reading from disk. The aggregate planner converts\n * these into {@link import('../planner-types').AggregatePlannerError}\n * variants.\n */\nexport type GraphWalkOutcome =\n | { readonly kind: 'ok'; readonly result: AggregatePerSpacePlan }\n | { readonly kind: 'unreachable' }\n | { readonly kind: 'unsatisfiable'; readonly missing: readonly string[] };\n\nexport interface GraphWalkStrategyInputs {\n readonly aggregateTargetId: string;\n readonly member: ContractSpaceMember;\n readonly currentMarker: ContractMarkerRecordLike | null;\n /**\n * Optional ref name to decorate the resulting `PathDecision`. Used by\n * `migration apply` to surface the user-supplied `--ref <name>` in\n * structured-progress events and invariant-path error envelopes. The\n * strategy itself does not interpret it.\n */\n readonly refName?: string;\n}\n\n/**\n * Walk a member's hydrated migration graph from the live marker to\n * `member.headRef.hash`, covering every required invariant.\n *\n * Pure synchronous function — no I/O. The aggregate's loader has\n * already integrity-checked every package and reconstructed the graph;\n * this strategy just looks up ops by `migrationHash` and assembles a\n * `MigrationPlan` with `targetId` set from the aggregate (no\n * placeholder cast).\n *\n * Required invariants are computed as `headRef.invariants \\ marker.invariants`\n * — the marker already declares some invariants satisfied; the path\n * only needs to provide the remainder. Mirrors today's\n * `computeExtensionSpaceApplyPath` semantics.\n */\nexport function graphWalkStrategy(input: GraphWalkStrategyInputs): GraphWalkOutcome {\n const { aggregateTargetId, member, currentMarker, refName } = input;\n const { graph, packagesByMigrationHash } = member.migrations;\n\n const fromHash = currentMarker?.storageHash ?? EMPTY_CONTRACT_HASH;\n const markerInvariants = new Set(currentMarker?.invariants ?? []);\n const required = new Set(member.headRef.invariants.filter((id) => !markerInvariants.has(id)));\n\n const outcome = findPathWithDecision(graph, fromHash, member.headRef.hash, {\n required,\n ...(refName !== undefined ? { refName } : {}),\n });\n\n if (outcome.kind === 'unreachable') {\n return { kind: 'unreachable' };\n }\n if (outcome.kind === 'unsatisfiable') {\n return { kind: 'unsatisfiable', missing: outcome.missing };\n }\n\n const pathOps: MigrationOps[number][] = [];\n const providedInvariantsSet = new Set<string>();\n const edgeRefs: Array<{\n migrationHash: string;\n dirName: string;\n from: string;\n to: string;\n operationCount: number;\n }> = [];\n for (const edge of outcome.decision.selectedPath) {\n const pkg = packagesByMigrationHash.get(edge.migrationHash);\n if (!pkg) {\n throw new Error(\n `Migration package missing for edge ${edge.migrationHash} in space \"${member.spaceId}\". The hydrated migration graph and packagesByMigrationHash map are out of sync — this should be unreachable; report.`,\n );\n }\n for (const op of pkg.ops) pathOps.push(op);\n for (const invariant of pkg.metadata.providedInvariants) providedInvariantsSet.add(invariant);\n edgeRefs.push({\n migrationHash: edge.migrationHash,\n dirName: edge.dirName,\n from: edge.from,\n to: edge.to,\n operationCount: pkg.ops.length,\n });\n }\n\n const plan: MigrationPlan = {\n targetId: aggregateTargetId,\n spaceId: member.spaceId,\n origin: currentMarker === null ? null : { storageHash: currentMarker.storageHash },\n destination: { storageHash: member.headRef.hash },\n operations: pathOps,\n providedInvariants: [...providedInvariantsSet].sort(),\n };\n\n return {\n kind: 'ok',\n result: {\n plan,\n displayOps: pathOps,\n destinationContract: member.contract as Contract,\n strategy: 'graph-walk',\n migrationEdges: edgeRefs,\n pathDecision: outcome.decision,\n },\n };\n}\n","import type { ContractSpaceMember } from './types';\n\n/**\n * Project the introspected live schema to the slice claimed by a single\n * contract-space member.\n *\n * Returns the same `schema` value with every top-level table claimed by\n * **other** members of the aggregate removed. Tables not claimed by any\n * member flow through unchanged — the planner / verifier sees them as\n * orphans (extras in strict mode).\n *\n * Used by:\n *\n * - The aggregate planner's **synth strategy**: when synthesising a\n * plan against a member's contract, the live schema must be projected\n * to that member's slice so the planner doesn't treat tables claimed\n * by other members as \"extras\" and emit destructive ops to drop\n * them.\n * - The aggregate verifier's **schemaCheck**: projects per member so the\n * single-contract `verifySqlSchema` only sees the slice claimed by\n * the member it is checking. Closes the F23 architectural concern\n * (multi-member deployments where each member's tables look like\n * extras to every other member's verify pass).\n *\n * **Duck-typing semantics**: the helper operates on `unknown` for the\n * schema and falls through structurally if the shape doesn't match.\n * Every family today exposes `storage.tables: Record<string, ...>` and\n * the introspected schema mirrors the same shape; a future family with\n * a different storage shape gets the schema returned unchanged rather\n * than blowing up the aggregate planner.\n */\nexport function projectSchemaToSpace(\n schema: unknown,\n member: ContractSpaceMember,\n otherMembers: ReadonlyArray<ContractSpaceMember>,\n): unknown {\n if (typeof schema !== 'object' || schema === null) return schema;\n const schemaObj = schema as { readonly tables?: unknown };\n if (typeof schemaObj.tables !== 'object' || schemaObj.tables === null) return schema;\n const schemaTables = schemaObj.tables as Record<string, unknown>;\n\n const ownedByOthers = new Set<string>();\n for (const other of otherMembers) {\n if (other.spaceId === member.spaceId) continue;\n const storage = (other.contract as { readonly storage?: unknown }).storage;\n if (typeof storage !== 'object' || storage === null) continue;\n const tables = (storage as { readonly tables?: unknown }).tables;\n if (typeof tables !== 'object' || tables === null) continue;\n for (const tableName of Object.keys(tables as Record<string, unknown>)) {\n ownedByOthers.add(tableName);\n }\n }\n\n if (ownedByOthers.size === 0) return schema;\n\n const prunedTables: Record<string, unknown> = {};\n for (const [name, table] of Object.entries(schemaTables)) {\n if (!ownedByOthers.has(name)) {\n prunedTables[name] = table;\n }\n }\n\n return { ...schemaObj, tables: prunedTables };\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type { TargetBoundComponentDescriptor } from '@prisma-next/framework-components/components';\nimport type {\n ControlFamilyInstance,\n MigrationOperationPolicy,\n MigrationPlan,\n MigrationPlannerConflict,\n MigrationPlannerResult,\n TargetMigrationsCapability,\n} from '@prisma-next/framework-components/control';\nimport type { AggregatePerSpacePlan } from '../planner-types';\nimport { projectSchemaToSpace } from '../project-schema-to-space';\nimport type { ContractSpaceMember } from '../types';\n\nexport interface SynthStrategyInputs<TFamilyId extends string, TTargetId extends string> {\n readonly aggregateTargetId: string;\n readonly member: ContractSpaceMember;\n readonly otherMembers: ReadonlyArray<ContractSpaceMember>;\n readonly schemaIntrospection: unknown;\n readonly familyInstance: ControlFamilyInstance<TFamilyId, unknown>;\n readonly migrations: TargetMigrationsCapability<\n TFamilyId,\n TTargetId,\n ControlFamilyInstance<TFamilyId, unknown>\n >;\n readonly frameworkComponents: ReadonlyArray<TargetBoundComponentDescriptor<TFamilyId, TTargetId>>;\n readonly operationPolicy: MigrationOperationPolicy;\n}\n\nexport type SynthStrategyOutcome =\n | { readonly kind: 'ok'; readonly result: AggregatePerSpacePlan }\n | { readonly kind: 'failure'; readonly conflicts: readonly MigrationPlannerConflict[] };\n\n/**\n * The {@link MigrationPlanner.plan} interface is declared as synchronous,\n * but historical and test fixture call sites have always invoked it\n * with `await` (see prior `db-apply-per-space.ts`). Tolerating a\n * Promise here keeps existing test mocks working without changing the\n * declared family SPI.\n */\ntype MaybeAsyncPlannerResult = MigrationPlannerResult | Promise<MigrationPlannerResult>;\n\n/**\n * Synthesise a migration plan for a single member by projecting the\n * live schema down to that member's claimed slice and delegating to\n * the family's `createPlanner(...).plan(...)`.\n *\n * Pre-projection (via {@link projectSchemaToSpace}) closes the F23\n * concern: without it, the family's planner sees other members'\n * tables as \"extras\" and emits destructive ops to drop them. With it,\n * the planner only sees the slice this member claims.\n *\n * The synthesised plan's `targetId` is set from `aggregateTargetId`\n * (the aggregate's ambient target). The family's planner does not\n * stamp `targetId` on the produced plan; the aggregate planner is\n * the single point that knows the target.\n *\n * Used by:\n *\n * - The app member by default (CLI policy\n * `ignoreGraphFor: { app.spaceId }`).\n * - Any extension member whose `headRef.invariants` is empty (the\n * strategy selector falls back to synth when graph-walk isn't\n * required).\n */\nexport async function synthStrategy<TFamilyId extends string, TTargetId extends string>(\n input: SynthStrategyInputs<TFamilyId, TTargetId>,\n): Promise<SynthStrategyOutcome> {\n const projectedSchema = projectSchemaToSpace(\n input.schemaIntrospection,\n input.member,\n input.otherMembers,\n );\n\n const planner = input.migrations.createPlanner(input.familyInstance);\n const plannerResult: MigrationPlannerResult = await (planner.plan({\n contract: input.member.contract,\n schema: projectedSchema,\n policy: input.operationPolicy,\n fromContract: null,\n frameworkComponents: input.frameworkComponents,\n spaceId: input.member.spaceId,\n }) as MaybeAsyncPlannerResult);\n\n if (plannerResult.kind === 'failure') {\n return { kind: 'failure', conflicts: plannerResult.conflicts };\n }\n\n const synthedPlan = plannerResult.plan;\n // The family planner returns a class-instance-shaped plan whose\n // `destination` / `operations` are accessors on the prototype, often\n // backed by private fields. A naive spread (`{ ...synthedPlan }`)\n // would lose those accessors and produce a plan with\n // `destination: undefined`; rebinding the prototype on a plain\n // object would break private-field access. We instead wrap the plan\n // in a Proxy that forwards every read except `targetId`, which is\n // stamped from the aggregate's ambient target. This preserves the\n // planner's class semantics while keeping the aggregate the single\n // source of truth for `targetId`.\n const plan: MigrationPlan = new Proxy(synthedPlan, {\n get(target, prop) {\n if (prop === 'targetId') return input.aggregateTargetId;\n // Forward `this` as the original target so prototype-bound\n // private fields (#destination, #operations, …) resolve.\n return Reflect.get(target, prop, target);\n },\n has(target, prop) {\n if (prop === 'targetId') return true;\n return Reflect.has(target, prop);\n },\n });\n\n return {\n kind: 'ok',\n result: {\n plan,\n displayOps: synthedPlan.operations,\n destinationContract: input.member.contract as Contract,\n strategy: 'synth',\n },\n };\n}\n","import { notOk, ok } from '@prisma-next/utils/result';\nimport type {\n AggregatePerSpacePlan,\n AggregatePlannerError,\n AggregatePlannerInput,\n AggregatePlannerOutput,\n} from './planner-types';\nimport { graphWalkStrategy } from './strategies/graph-walk';\nimport { synthStrategy } from './strategies/synth';\nimport type { ContractSpaceMember } from './types';\n\nexport type {\n AggregateCurrentDBState,\n AggregatePerSpacePlan,\n AggregatePlannerError,\n AggregatePlannerInput,\n AggregatePlannerOutput,\n AggregatePlannerSuccess,\n CallerPolicy,\n} from './planner-types';\n\n/**\n * Plan a migration across every member of a {@link ContractSpaceAggregate}.\n *\n * Strategy selection per member, in order; first match wins:\n *\n * 1. If `callerPolicy.ignoreGraphFor.has(member.spaceId)`:\n * - If `member.headRef.invariants` is empty → synth.\n * - Else → `policyConflict` (synth cannot satisfy authored invariants).\n * 2. Else if `member.migrations.graph` is non-empty AND graph-walk\n * succeeds → graph-walk.\n * 3. Else if `member.headRef.invariants` is empty → synth.\n * 4. Else → graph-walk failure → `extensionPathUnreachable` /\n * `extensionPathUnsatisfiable`.\n *\n * Output `applyOrder` is `[...aggregate.extensions.map(spaceId), aggregate.app.spaceId]`\n * — extensions alphabetical, then app — matching today's\n * `concatenateSpaceApplyInputs` ordering. This preserves\n * `MultiSpaceRunnerFailure.failingSpace` attribution byte-for-byte.\n *\n * Every emitted `MigrationPlan` has `targetId = aggregate.targetId`.\n * No placeholder cast; no patch step.\n */\nexport async function planAggregate<TFamilyId extends string, TTargetId extends string>(\n input: AggregatePlannerInput<TFamilyId, TTargetId>,\n): Promise<AggregatePlannerOutput> {\n const { aggregate, currentDBState, callerPolicy } = input;\n const allMembers: ReadonlyArray<ContractSpaceMember> = [aggregate.app, ...aggregate.extensions];\n\n const perSpace = new Map<string, AggregatePerSpacePlan>();\n\n // Iterate in apply order so a per-member error short-circuits the\n // walk in the same order the runner would walk inputs.\n const orderedMembers: ReadonlyArray<ContractSpaceMember> = [\n ...aggregate.extensions,\n aggregate.app,\n ];\n\n for (const member of orderedMembers) {\n const otherMembers = allMembers.filter((m) => m.spaceId !== member.spaceId);\n const currentMarker = currentDBState.markersBySpaceId.get(member.spaceId) ?? null;\n\n const ignoreGraph = callerPolicy.ignoreGraphFor.has(member.spaceId);\n const invariantsRequired = member.headRef.invariants.length > 0;\n\n if (ignoreGraph && invariantsRequired) {\n const conflict: AggregatePlannerError = {\n kind: 'policyConflict',\n spaceId: member.spaceId,\n detail: `\\`callerPolicy.ignoreGraphFor\\` requested for space \"${member.spaceId}\", but the member declares non-empty head-ref invariants (${member.headRef.invariants.join(', ')}). Synthesising a plan from the contract IR cannot satisfy authored invariants — the graph must be walked. Either remove \"${member.spaceId}\" from \\`ignoreGraphFor\\` or amend the on-disk head ref to declare zero invariants.`,\n };\n return notOk(conflict);\n }\n\n if (ignoreGraph) {\n const synthOutcome = await synthStrategy({\n aggregateTargetId: aggregate.targetId,\n member,\n otherMembers,\n schemaIntrospection: currentDBState.schemaIntrospection,\n familyInstance: input.familyInstance,\n migrations: input.migrations,\n frameworkComponents: input.frameworkComponents,\n operationPolicy: input.operationPolicy,\n });\n if (synthOutcome.kind === 'failure') {\n return notOk({\n kind: 'appSynthFailure',\n spaceId: member.spaceId,\n conflicts: synthOutcome.conflicts,\n });\n }\n perSpace.set(member.spaceId, synthOutcome.result);\n continue;\n }\n\n // Try graph-walk first when the graph has nodes; fall back to synth\n // when the graph is empty AND no invariants are required.\n if (member.migrations.graph.nodes.size > 0) {\n const walked = graphWalkStrategy({\n aggregateTargetId: aggregate.targetId,\n member,\n currentMarker,\n });\n if (walked.kind === 'ok') {\n perSpace.set(member.spaceId, walked.result);\n continue;\n }\n if (walked.kind === 'unreachable') {\n return notOk({\n kind: 'extensionPathUnreachable',\n spaceId: member.spaceId,\n target: member.headRef.hash,\n });\n }\n // unsatisfiable — surface\n return notOk({\n kind: 'extensionPathUnsatisfiable',\n spaceId: member.spaceId,\n missingInvariants: walked.missing,\n });\n }\n\n // Empty graph: synth is the only option, and it can only satisfy\n // empty-invariant members.\n if (invariantsRequired) {\n return notOk({\n kind: 'extensionPathUnsatisfiable',\n spaceId: member.spaceId,\n missingInvariants: [...member.headRef.invariants].sort(),\n });\n }\n\n const synthOutcome = await synthStrategy({\n aggregateTargetId: aggregate.targetId,\n member,\n otherMembers,\n schemaIntrospection: currentDBState.schemaIntrospection,\n familyInstance: input.familyInstance,\n migrations: input.migrations,\n frameworkComponents: input.frameworkComponents,\n operationPolicy: input.operationPolicy,\n });\n if (synthOutcome.kind === 'failure') {\n return notOk({\n kind: 'appSynthFailure',\n spaceId: member.spaceId,\n conflicts: synthOutcome.conflicts,\n });\n }\n perSpace.set(member.spaceId, synthOutcome.result);\n }\n\n return ok({\n perSpace,\n applyOrder: [...aggregate.extensions.map((m) => m.spaceId), aggregate.app.spaceId],\n });\n}\n","import type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport type { ContractMarkerRecordLike } from './marker-types';\nimport { projectSchemaToSpace } from './project-schema-to-space';\nimport type { ContractSpaceAggregate, ContractSpaceMember } from './types';\n\n/**\n * Caller policy for the aggregate verifier. Today's only knob is\n * `mode`: `strict` treats orphan elements (live tables not claimed by\n * any aggregate member) as errors; `lenient` treats them as\n * informational. Maps directly to `db verify --strict`.\n */\nexport interface AggregateVerifierInput<TSchemaResult> {\n readonly aggregate: ContractSpaceAggregate;\n readonly markersBySpaceId: ReadonlyMap<string, ContractMarkerRecordLike | null>;\n readonly schemaIntrospection: unknown;\n readonly mode: 'strict' | 'lenient';\n /**\n * Caller-supplied per-space schema verifier. The CLI wires this to\n * the family's `verifySqlSchema` (SQL) / equivalent (other\n * families). The aggregate verifier projects the schema to the\n * member's slice via {@link projectSchemaToSpace} before invoking\n * the callback, so single-contract semantics are preserved.\n *\n * Typed structurally with a generic `TSchemaResult` so the\n * migration-tools layer doesn't depend on the SQL family's\n * `VerifySqlSchemaResult`. CLI callers pass the family's type\n * through unchanged.\n */\n readonly verifySchemaForMember: (\n projectedSchema: unknown,\n member: ContractSpaceMember,\n mode: 'strict' | 'lenient',\n ) => TSchemaResult;\n}\n\n/**\n * Marker-check result per member. Mirrors the four cases the\n * `verifyContractSpaces` primitive surfaces today, plus an `'absent'`\n * case for greenfield spaces (no marker row written yet — `db init`\n * not run).\n */\nexport type MarkerCheckResult =\n | { readonly kind: 'ok' }\n | { readonly kind: 'absent' }\n | {\n readonly kind: 'hashMismatch';\n readonly markerHash: string;\n readonly expected: string;\n }\n | { readonly kind: 'missingInvariants'; readonly missing: readonly string[] };\n\nexport interface MarkerCheckSection {\n readonly perSpace: ReadonlyMap<string, MarkerCheckResult>;\n readonly orphanMarkers: readonly {\n readonly spaceId: string;\n readonly row: ContractMarkerRecordLike;\n }[];\n}\n\n/**\n * A live storage element (today: a top-level table) not claimed by any\n * member of the aggregate. The aggregate verifier always reports these;\n * the caller decides what to do — `db verify --strict` treats them as\n * errors, the lenient default treats them as informational.\n *\n * Today only `kind: 'table'` exists. The discriminated shape leaves\n * room for orphan columns / indexes / sequences in the future without\n * breaking the type contract.\n */\nexport type OrphanElement = { readonly kind: 'table'; readonly name: string };\n\nexport interface SchemaCheckSection<TSchemaResult> {\n readonly perSpace: ReadonlyMap<string, TSchemaResult>;\n /**\n * Live elements present in the introspected schema that are not\n * claimed by **any** aggregate member. Sorted alphabetically by name.\n */\n readonly orphanElements: readonly OrphanElement[];\n}\n\nexport interface AggregateVerifierSuccess<TSchemaResult> {\n readonly markerCheck: MarkerCheckSection;\n readonly schemaCheck: SchemaCheckSection<TSchemaResult>;\n}\n\nexport type AggregateVerifierError = {\n readonly kind: 'introspectionFailure';\n readonly detail: string;\n};\n\nexport type AggregateVerifierOutput<TSchemaResult> = Result<\n AggregateVerifierSuccess<TSchemaResult>,\n AggregateVerifierError\n>;\n\n/**\n * Verify a {@link ContractSpaceAggregate} against the live database\n * state. Bundles two checks:\n *\n * - `markerCheck` per member: compare the live marker row against the\n * member's `headRef.hash` + `headRef.invariants`. Absence is a\n * distinct kind, not an error (callers — `db verify` strict vs\n * `db init` precondition — choose how to interpret it).\n * - `schemaCheck` per member: project the live schema to the slice\n * the member claims via {@link projectSchemaToSpace}, then delegate\n * to the caller-supplied `verifySchemaForMember`. The pre-projection\n * means the family's single-contract verifier no longer sees other\n * members' tables as `extras`, so a multi-member deployment never\n * surfaces cross-member tables as orphaned schema elements.\n *\n * `markerCheck.orphanMarkers` lists every marker row whose `space` is\n * not a member of the aggregate. `db verify` callers reject orphans;\n * future tooling may not.\n *\n * Pure synchronous function; no I/O. The caller (CLI) gathers\n * `markersBySpaceId` and `schemaIntrospection` ahead of the call.\n */\nexport function verifyAggregate<TSchemaResult>(\n input: AggregateVerifierInput<TSchemaResult>,\n): AggregateVerifierOutput<TSchemaResult> {\n try {\n return runVerifyAggregate(input);\n } catch (error) {\n return notOk({\n kind: 'introspectionFailure',\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\nfunction runVerifyAggregate<TSchemaResult>(\n input: AggregateVerifierInput<TSchemaResult>,\n): AggregateVerifierOutput<TSchemaResult> {\n const { aggregate, markersBySpaceId, schemaIntrospection, mode, verifySchemaForMember } = input;\n const allMembers: ReadonlyArray<ContractSpaceMember> = [aggregate.app, ...aggregate.extensions];\n const memberSpaceIds = new Set(allMembers.map((m) => m.spaceId));\n\n // Marker check per member.\n const markerPerSpace = new Map<string, MarkerCheckResult>();\n for (const member of allMembers) {\n const marker = markersBySpaceId.get(member.spaceId) ?? null;\n if (marker === null) {\n markerPerSpace.set(member.spaceId, { kind: 'absent' });\n continue;\n }\n if (marker.storageHash !== member.headRef.hash) {\n markerPerSpace.set(member.spaceId, {\n kind: 'hashMismatch',\n markerHash: marker.storageHash,\n expected: member.headRef.hash,\n });\n continue;\n }\n const markerInvariants = new Set(marker.invariants);\n const missing = member.headRef.invariants.filter((id) => !markerInvariants.has(id));\n if (missing.length > 0) {\n markerPerSpace.set(member.spaceId, {\n kind: 'missingInvariants',\n missing: [...missing].sort(),\n });\n continue;\n }\n markerPerSpace.set(member.spaceId, { kind: 'ok' });\n }\n\n // Orphan markers: entries in markersBySpaceId whose spaceId is not a\n // member of the aggregate.\n const orphanMarkers: { spaceId: string; row: ContractMarkerRecordLike }[] = [];\n for (const [spaceId, row] of markersBySpaceId) {\n if (row !== null && !memberSpaceIds.has(spaceId)) {\n orphanMarkers.push({ spaceId, row });\n }\n }\n orphanMarkers.sort((a, b) => a.spaceId.localeCompare(b.spaceId));\n\n // Schema check per member (with per-space pre-projection).\n const schemaPerSpace = new Map<string, TSchemaResult>();\n for (const member of allMembers) {\n const others = allMembers.filter((m) => m.spaceId !== member.spaceId);\n const projected = projectSchemaToSpace(schemaIntrospection, member, others);\n schemaPerSpace.set(member.spaceId, verifySchemaForMember(projected, member, mode));\n }\n\n return ok({\n markerCheck: {\n perSpace: markerPerSpace,\n orphanMarkers,\n },\n schemaCheck: {\n perSpace: schemaPerSpace,\n orphanElements: detectOrphanElements(schemaIntrospection, allMembers),\n },\n });\n}\n\n/**\n * Live tables not claimed by any aggregate member. Duck-typed against\n * the introspected schema's `tables` map; schemas whose shape doesn't\n * match return an empty list (consistent with\n * {@link projectSchemaToSpace}'s fall-through).\n */\nfunction detectOrphanElements(\n schemaIntrospection: unknown,\n members: ReadonlyArray<ContractSpaceMember>,\n): readonly OrphanElement[] {\n if (typeof schemaIntrospection !== 'object' || schemaIntrospection === null) return [];\n const liveTables = (schemaIntrospection as { readonly tables?: unknown }).tables;\n if (typeof liveTables !== 'object' || liveTables === null) return [];\n\n const claimedTables = new Set<string>();\n for (const member of members) {\n const storage = (member.contract as { readonly storage?: unknown }).storage;\n if (typeof storage !== 'object' || storage === null) continue;\n const tables = (storage as { readonly tables?: unknown }).tables;\n if (typeof tables !== 'object' || tables === null) continue;\n for (const tableName of Object.keys(tables as Record<string, unknown>)) {\n claimedTables.add(tableName);\n }\n }\n\n const orphans: OrphanElement[] = [];\n for (const tableName of Object.keys(liveTables as Record<string, unknown>)) {\n if (!claimedTables.has(tableName)) {\n orphans.push({ kind: 'table', name: tableName });\n }\n }\n orphans.sort((a, b) => a.name.localeCompare(b.name));\n return orphans;\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-BjiZ7KDy.mjs";
2
- import { n as MigrationMetadata } from "../metadata-BnLFiI6B.mjs";
2
+ import { n as MigrationMetadata } from "../metadata-CFvm3ayn.mjs";
3
3
 
4
4
  //#region src/hash.d.ts
5
5
  interface VerifyResult {
@@ -1,2 +1,2 @@
1
- import { n as MigrationMetadata, t as MigrationHints } from "../metadata-BnLFiI6B.mjs";
1
+ import { n as MigrationMetadata, t as MigrationHints } from "../metadata-CFvm3ayn.mjs";
2
2
  export { type MigrationHints, type MigrationMetadata };
@@ -1,124 +1,2 @@
1
- import { n as OnDiskMigrationPackage } from "../package-BjiZ7KDy.mjs";
2
- import { n as MigrationGraph, t as MigrationEdge } from "../graph-HMWAldoR.mjs";
3
-
4
- //#region src/migration-graph.d.ts
5
- declare function reconstructGraph(packages: readonly OnDiskMigrationPackage[]): MigrationGraph;
6
- /**
7
- * Find the shortest path from `fromHash` to `toHash` using BFS over the
8
- * contract-hash graph. Returns the ordered list of edges, or null if no path
9
- * exists. Returns an empty array when `fromHash === toHash` (no-op).
10
- *
11
- * Neighbor ordering is deterministic via the tie-break sort key:
12
- * label priority → createdAt → to → migrationHash.
13
- */
14
- declare function findPath(graph: MigrationGraph, fromHash: string, toHash: string): readonly MigrationEdge[] | null;
15
- /**
16
- * Find the shortest path from `fromHash` to `toHash` whose edges collectively
17
- * cover every invariant in `required`. Returns `null` when no such path exists
18
- * (either `fromHash`→`toHash` is structurally unreachable, or every reachable
19
- * path leaves at least one required invariant uncovered). When `required` is
20
- * empty, delegates to `findPath` so the result is byte-identical for that case.
21
- *
22
- * Algorithm: BFS over `(node, coveredSubset)` states with state-level dedup.
23
- * The covered subset is a `Set<string>` of invariant ids; the state's dedup
24
- * key is `${node}\0${[...covered].sort().join('\0')}`. State keys distinguish
25
- * distinct `(node, covered)` tuples regardless of node-name length because
26
- * `\0` cannot appear in any invariant id (validation rejects whitespace and
27
- * control chars at authoring time).
28
- *
29
- * Neighbour ordering when `required ≠ ∅`: edges covering ≥1 still-needed
30
- * invariant come first, with `labelPriority → createdAt → to → migrationHash`
31
- * as the secondary key. The heuristic steers BFS toward the satisfying path;
32
- * correctness (shortest, deterministic) does not depend on it.
33
- */
34
- declare function findPathWithInvariants(graph: MigrationGraph, fromHash: string, toHash: string, required: ReadonlySet<string>): readonly MigrationEdge[] | null;
35
- interface PathDecision {
36
- readonly selectedPath: readonly MigrationEdge[];
37
- readonly fromHash: string;
38
- readonly toHash: string;
39
- readonly alternativeCount: number;
40
- readonly tieBreakReasons: readonly string[];
41
- readonly refName?: string;
42
- /** The caller-supplied required invariant set, sorted ascending. */
43
- readonly requiredInvariants: readonly string[];
44
- /**
45
- * The subset of `requiredInvariants` actually covered by edges on
46
- * `selectedPath`. Always a subset of `requiredInvariants` (when the path
47
- * is satisfying, equal to it); always derived from `selectedPath`.
48
- */
49
- readonly satisfiedInvariants: readonly string[];
50
- }
51
- /**
52
- * Outcome of {@link findPathWithDecision}. The pathfinder distinguishes
53
- * three cases up front so callers don't re-derive structural reachability:
54
- *
55
- * - `ok` — a path covering `required` exists; `decision` carries the
56
- * selection metadata and per-edge invariants.
57
- * - `unreachable` — `from`→`to` has no structural path. Mapped by callers
58
- * to the existing no-path / `NO_TARGET` diagnostic.
59
- * - `unsatisfiable` — `from`→`to` is structurally reachable but no path
60
- * covers every required invariant. `structuralPath` is the
61
- * `findPath(graph, from, to)` result, included so callers don't have to
62
- * recompute it when raising `MIGRATION.NO_INVARIANT_PATH`. `missing` is
63
- * the subset of `required` that the structural path does *not* cover —
64
- * correctly accounts for partial coverage when some required invariants
65
- * are met by the fallback path. Only emitted when `required` is
66
- * non-empty.
67
- */
68
- type FindPathOutcome = {
69
- readonly kind: 'ok';
70
- readonly decision: PathDecision;
71
- } | {
72
- readonly kind: 'unreachable';
73
- } | {
74
- readonly kind: 'unsatisfiable';
75
- readonly structuralPath: readonly MigrationEdge[];
76
- readonly missing: readonly string[];
77
- };
78
- /**
79
- * Routing context for {@link findPathWithDecision}. Both fields are optional;
80
- * `refName` is only used to decorate the resulting `PathDecision` for the
81
- * JSON envelope, and `required` defaults to an empty set (purely structural
82
- * routing). They are passed via a single options object so the call sites
83
- * cannot silently swap two adjacent string parameters.
84
- */
85
- interface FindPathWithDecisionOptions {
86
- readonly refName?: string;
87
- readonly required?: ReadonlySet<string>;
88
- }
89
- /**
90
- * Find the shortest path from `fromHash` to `toHash` and return structured
91
- * path-decision metadata for machine-readable output. When `required` is
92
- * non-empty, the returned path is the shortest one whose edges collectively
93
- * cover every required invariant.
94
- *
95
- * The discriminated return type tells the caller *why* a path could not be
96
- * found, so the CLI can pick the right structured error without re-running
97
- * a structural BFS.
98
- */
99
- declare function findPathWithDecision(graph: MigrationGraph, fromHash: string, toHash: string, options?: FindPathWithDecisionOptions): FindPathOutcome;
100
- /**
101
- * Find all branch tips (nodes with no outgoing edges) reachable from
102
- * `fromHash` via forward edges.
103
- */
104
- declare function findReachableLeaves(graph: MigrationGraph, fromHash: string): readonly string[];
105
- /**
106
- * Find the target contract hash of the migration graph reachable from
107
- * EMPTY_CONTRACT_HASH. Returns `null` for a graph that has no target
108
- * state (either empty, or containing only the root with no outgoing
109
- * edges). Throws NO_INITIAL_MIGRATION if the graph has nodes but none
110
- * originate from the empty hash, and AMBIGUOUS_TARGET if multiple
111
- * branch tips exist.
112
- */
113
- declare function findLeaf(graph: MigrationGraph): string | null;
114
- /**
115
- * Find the latest migration entry by traversing from EMPTY_CONTRACT_HASH
116
- * to the single target. Returns null for an empty graph.
117
- * Throws AMBIGUOUS_TARGET if the graph has multiple branch tips.
118
- */
119
- declare function findLatestMigration(graph: MigrationGraph): MigrationEdge | null;
120
- declare function detectCycles(graph: MigrationGraph): readonly string[][];
121
- declare function detectOrphans(graph: MigrationGraph): readonly MigrationEdge[];
122
- //#endregion
123
- export { type PathDecision, detectCycles, detectOrphans, findLatestMigration, findLeaf, findPath, findPathWithDecision, findPathWithInvariants, findReachableLeaves, reconstructGraph };
124
- //# sourceMappingURL=migration-graph.d.mts.map
1
+ import { a as findLeaf, c as findPathWithInvariants, i as findLatestMigration, l as findReachableLeaves, n as detectCycles, o as findPath, r as detectOrphans, s as findPathWithDecision, t as PathDecision, u as reconstructGraph } from "../migration-graph-DulOITvG.mjs";
2
+ export { type PathDecision, detectCycles, detectOrphans, findLatestMigration, findLeaf, findPath, findPathWithDecision, findPathWithInvariants, findReachableLeaves, reconstructGraph };
@@ -1,4 +1,4 @@
1
- import { n as MigrationMetadata$1 } from "../metadata-BnLFiI6B.mjs";
1
+ import { n as MigrationMetadata$1 } from "../metadata-CFvm3ayn.mjs";
2
2
  import { ControlStack, MigrationPlan, MigrationPlanOperation } from "@prisma-next/framework-components/control";
3
3
 
4
4
  //#region src/migration-base.d.ts
@@ -0,0 +1,124 @@
1
+ import { n as OnDiskMigrationPackage } from "./package-BjiZ7KDy.mjs";
2
+ import { n as MigrationGraph, t as MigrationEdge } from "./graph-HMWAldoR.mjs";
3
+
4
+ //#region src/migration-graph.d.ts
5
+ declare function reconstructGraph(packages: readonly OnDiskMigrationPackage[]): MigrationGraph;
6
+ /**
7
+ * Find the shortest path from `fromHash` to `toHash` using BFS over the
8
+ * contract-hash graph. Returns the ordered list of edges, or null if no path
9
+ * exists. Returns an empty array when `fromHash === toHash` (no-op).
10
+ *
11
+ * Neighbor ordering is deterministic via the tie-break sort key:
12
+ * label priority → createdAt → to → migrationHash.
13
+ */
14
+ declare function findPath(graph: MigrationGraph, fromHash: string, toHash: string): readonly MigrationEdge[] | null;
15
+ /**
16
+ * Find the shortest path from `fromHash` to `toHash` whose edges collectively
17
+ * cover every invariant in `required`. Returns `null` when no such path exists
18
+ * (either `fromHash`→`toHash` is structurally unreachable, or every reachable
19
+ * path leaves at least one required invariant uncovered). When `required` is
20
+ * empty, delegates to `findPath` so the result is byte-identical for that case.
21
+ *
22
+ * Algorithm: BFS over `(node, coveredSubset)` states with state-level dedup.
23
+ * The covered subset is a `Set<string>` of invariant ids; the state's dedup
24
+ * key is `${node}\0${[...covered].sort().join('\0')}`. State keys distinguish
25
+ * distinct `(node, covered)` tuples regardless of node-name length because
26
+ * `\0` cannot appear in any invariant id (validation rejects whitespace and
27
+ * control chars at authoring time).
28
+ *
29
+ * Neighbour ordering when `required ≠ ∅`: edges covering ≥1 still-needed
30
+ * invariant come first, with `labelPriority → createdAt → to → migrationHash`
31
+ * as the secondary key. The heuristic steers BFS toward the satisfying path;
32
+ * correctness (shortest, deterministic) does not depend on it.
33
+ */
34
+ declare function findPathWithInvariants(graph: MigrationGraph, fromHash: string, toHash: string, required: ReadonlySet<string>): readonly MigrationEdge[] | null;
35
+ interface PathDecision {
36
+ readonly selectedPath: readonly MigrationEdge[];
37
+ readonly fromHash: string;
38
+ readonly toHash: string;
39
+ readonly alternativeCount: number;
40
+ readonly tieBreakReasons: readonly string[];
41
+ readonly refName?: string;
42
+ /** The caller-supplied required invariant set, sorted ascending. */
43
+ readonly requiredInvariants: readonly string[];
44
+ /**
45
+ * The subset of `requiredInvariants` actually covered by edges on
46
+ * `selectedPath`. Always a subset of `requiredInvariants` (when the path
47
+ * is satisfying, equal to it); always derived from `selectedPath`.
48
+ */
49
+ readonly satisfiedInvariants: readonly string[];
50
+ }
51
+ /**
52
+ * Outcome of {@link findPathWithDecision}. The pathfinder distinguishes
53
+ * three cases up front so callers don't re-derive structural reachability:
54
+ *
55
+ * - `ok` — a path covering `required` exists; `decision` carries the
56
+ * selection metadata and per-edge invariants.
57
+ * - `unreachable` — `from`→`to` has no structural path. Mapped by callers
58
+ * to the existing no-path / `NO_TARGET` diagnostic.
59
+ * - `unsatisfiable` — `from`→`to` is structurally reachable but no path
60
+ * covers every required invariant. `structuralPath` is the
61
+ * `findPath(graph, from, to)` result, included so callers don't have to
62
+ * recompute it when raising `MIGRATION.NO_INVARIANT_PATH`. `missing` is
63
+ * the subset of `required` that the structural path does *not* cover —
64
+ * correctly accounts for partial coverage when some required invariants
65
+ * are met by the fallback path. Only emitted when `required` is
66
+ * non-empty.
67
+ */
68
+ type FindPathOutcome = {
69
+ readonly kind: 'ok';
70
+ readonly decision: PathDecision;
71
+ } | {
72
+ readonly kind: 'unreachable';
73
+ } | {
74
+ readonly kind: 'unsatisfiable';
75
+ readonly structuralPath: readonly MigrationEdge[];
76
+ readonly missing: readonly string[];
77
+ };
78
+ /**
79
+ * Routing context for {@link findPathWithDecision}. Both fields are optional;
80
+ * `refName` is only used to decorate the resulting `PathDecision` for the
81
+ * JSON envelope, and `required` defaults to an empty set (purely structural
82
+ * routing). They are passed via a single options object so the call sites
83
+ * cannot silently swap two adjacent string parameters.
84
+ */
85
+ interface FindPathWithDecisionOptions {
86
+ readonly refName?: string;
87
+ readonly required?: ReadonlySet<string>;
88
+ }
89
+ /**
90
+ * Find the shortest path from `fromHash` to `toHash` and return structured
91
+ * path-decision metadata for machine-readable output. When `required` is
92
+ * non-empty, the returned path is the shortest one whose edges collectively
93
+ * cover every required invariant.
94
+ *
95
+ * The discriminated return type tells the caller *why* a path could not be
96
+ * found, so the CLI can pick the right structured error without re-running
97
+ * a structural BFS.
98
+ */
99
+ declare function findPathWithDecision(graph: MigrationGraph, fromHash: string, toHash: string, options?: FindPathWithDecisionOptions): FindPathOutcome;
100
+ /**
101
+ * Find all branch tips (nodes with no outgoing edges) reachable from
102
+ * `fromHash` via forward edges.
103
+ */
104
+ declare function findReachableLeaves(graph: MigrationGraph, fromHash: string): readonly string[];
105
+ /**
106
+ * Find the target contract hash of the migration graph reachable from
107
+ * EMPTY_CONTRACT_HASH. Returns `null` for a graph that has no target
108
+ * state (either empty, or containing only the root with no outgoing
109
+ * edges). Throws NO_INITIAL_MIGRATION if the graph has nodes but none
110
+ * originate from the empty hash, and AMBIGUOUS_TARGET if multiple
111
+ * branch tips exist.
112
+ */
113
+ declare function findLeaf(graph: MigrationGraph): string | null;
114
+ /**
115
+ * Find the latest migration entry by traversing from EMPTY_CONTRACT_HASH
116
+ * to the single target. Returns null for an empty graph.
117
+ * Throws AMBIGUOUS_TARGET if the graph has multiple branch tips.
118
+ */
119
+ declare function findLatestMigration(graph: MigrationGraph): MigrationEdge | null;
120
+ declare function detectCycles(graph: MigrationGraph): readonly string[][];
121
+ declare function detectOrphans(graph: MigrationGraph): readonly MigrationEdge[];
122
+ //#endregion
123
+ export { findLeaf as a, findPathWithInvariants as c, findLatestMigration as i, findReachableLeaves as l, detectCycles as n, findPath as o, detectOrphans as r, findPathWithDecision as s, PathDecision as t, reconstructGraph as u };
124
+ //# sourceMappingURL=migration-graph-DulOITvG.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-graph-DulOITvG.d.mts","names":[],"sources":["../src/migration-graph.ts"],"mappings":";;;;iBAsCgB,gBAAA,CAAiB,QAAA,WAAmB,sBAAA,KAA2B,cAAA;;AAA/E;;;;;;;iBAqFgB,QAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,oBACU,aAAA;;AAJZ;;;;;;;;;;;AAgDA;;;;;;;iBAAgB,sBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,QAAA,EAAU,WAAA,oBACA,aAAA;AAAA,UAsFK,YAAA;EAAA,SACN,YAAA,WAAuB,aAAA;EAAA,SACvB,QAAA;EAAA,SACA,MAAA;EAAA,SACA,gBAAA;EAAA,SACA,eAAA;EAAA,SACA,OAAA;EA5Fc;EAAA,SA8Fd,kBAAA;EARM;;;;;EAAA,SAcN,mBAAA;AAAA;;;;;;;;;AAoBX;;;;;;;;;KAAY,eAAA;EAAA,SACG,IAAA;EAAA,SAAqB,QAAA,EAAU,YAAA;AAAA;EAAA,SAC/B,IAAA;AAAA;EAAA,SAEA,IAAA;EAAA,SACA,cAAA,WAAyB,aAAA;EAAA,SACzB,OAAA;AAAA;;;;;AAyBf;;;UAfiB,2BAAA;EAAA,SACN,OAAA;EAAA,SACA,QAAA,GAAW,WAAA;AAAA;;;;;;;;;;;iBAaN,oBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,OAAA,GAAS,2BAAA,GACR,eAAA;;;;;iBAqLa,mBAAA,CAAoB,KAAA,EAAO,cAAA,EAAgB,QAAA;;;;AAkB3D;;;;;iBAAgB,QAAA,CAAS,KAAA,EAAO,cAAA;;;;;;iBAwChB,mBAAA,CAAoB,KAAA,EAAO,cAAA,GAAiB,aAAA;AAAA,iBAQ5C,YAAA,CAAa,KAAA,EAAO,cAAA;AAAA,iBA8DpB,aAAA,CAAc,KAAA,EAAO,cAAA,YAA0B,aAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma-next/migration-tools",
3
- "version": "0.5.0-dev.74",
3
+ "version": "0.5.0-dev.75",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -9,9 +9,9 @@
9
9
  "arktype": "^2.1.29",
10
10
  "pathe": "^2.0.3",
11
11
  "prettier": "^3.8.3",
12
- "@prisma-next/contract": "0.5.0-dev.74",
13
- "@prisma-next/framework-components": "0.5.0-dev.74",
14
- "@prisma-next/utils": "0.5.0-dev.74"
12
+ "@prisma-next/contract": "0.5.0-dev.75",
13
+ "@prisma-next/utils": "0.5.0-dev.75",
14
+ "@prisma-next/framework-components": "0.5.0-dev.75"
15
15
  },
16
16
  "devDependencies": {
17
17
  "tsdown": "0.22.0",
@@ -9,6 +9,7 @@ import type {
9
9
  TargetMigrationsCapability,
10
10
  } from '@prisma-next/framework-components/control';
11
11
  import type { Result } from '@prisma-next/utils/result';
12
+ import type { PathDecision } from '../migration-graph';
12
13
  import type { ContractMarkerRecordLike } from './marker-types';
13
14
  import type { ContractSpaceAggregate } from './types';
14
15
 
@@ -95,11 +96,44 @@ export interface AggregatePlannerInput<TFamilyId extends string, TTargetId exten
95
96
  * - `strategy`: which strategy produced this plan (`'graph-walk'` or
96
97
  * `'synth'`). Surfaced for diagnostics; not consumed by the runner.
97
98
  */
99
+ /**
100
+ * Per-edge metadata for the chain assembled by the graph-walk
101
+ * strategy. Lets `migration apply` surface a per-migration `applied[]`
102
+ * entry (preserving the single-space `migrationsApplied` count
103
+ * semantics) without re-walking the graph.
104
+ *
105
+ * `synth`-produced plans leave this absent — synthesised plans don't
106
+ * have authored edges to surface.
107
+ */
108
+ export interface AggregateMigrationEdgeRef {
109
+ readonly migrationHash: string;
110
+ readonly dirName: string;
111
+ readonly from: string;
112
+ readonly to: string;
113
+ readonly operationCount: number;
114
+ }
115
+
98
116
  export interface AggregatePerSpacePlan {
99
117
  readonly plan: MigrationPlan;
100
118
  readonly displayOps: readonly MigrationPlanOperation[];
101
119
  readonly destinationContract: Contract;
102
120
  readonly strategy: 'graph-walk' | 'synth';
121
+ /**
122
+ * Per-edge breakdown of the chain. Populated by the graph-walk
123
+ * strategy; absent for synth-produced plans.
124
+ */
125
+ readonly migrationEdges?: readonly AggregateMigrationEdgeRef[];
126
+ /**
127
+ * Path decision data the strategy used to select the chain
128
+ * (alternative count, tie-break reasons, required/satisfied
129
+ * invariants, per-edge invariants). Populated by the graph-walk
130
+ * strategy; absent for synth-produced plans.
131
+ *
132
+ * `migration apply` surfaces this for the app member as
133
+ * `MigrationApplySuccess.pathDecision` (back-compat with single-
134
+ * space callers).
135
+ */
136
+ readonly pathDecision?: PathDecision;
103
137
  }
104
138
 
105
139
  export interface AggregatePlannerSuccess {
@@ -24,6 +24,13 @@ export interface GraphWalkStrategyInputs {
24
24
  readonly aggregateTargetId: string;
25
25
  readonly member: ContractSpaceMember;
26
26
  readonly currentMarker: ContractMarkerRecordLike | null;
27
+ /**
28
+ * Optional ref name to decorate the resulting `PathDecision`. Used by
29
+ * `migration apply` to surface the user-supplied `--ref <name>` in
30
+ * structured-progress events and invariant-path error envelopes. The
31
+ * strategy itself does not interpret it.
32
+ */
33
+ readonly refName?: string;
27
34
  }
28
35
 
29
36
  /**
@@ -42,14 +49,17 @@ export interface GraphWalkStrategyInputs {
42
49
  * `computeExtensionSpaceApplyPath` semantics.
43
50
  */
44
51
  export function graphWalkStrategy(input: GraphWalkStrategyInputs): GraphWalkOutcome {
45
- const { aggregateTargetId, member, currentMarker } = input;
52
+ const { aggregateTargetId, member, currentMarker, refName } = input;
46
53
  const { graph, packagesByMigrationHash } = member.migrations;
47
54
 
48
55
  const fromHash = currentMarker?.storageHash ?? EMPTY_CONTRACT_HASH;
49
56
  const markerInvariants = new Set(currentMarker?.invariants ?? []);
50
57
  const required = new Set(member.headRef.invariants.filter((id) => !markerInvariants.has(id)));
51
58
 
52
- const outcome = findPathWithDecision(graph, fromHash, member.headRef.hash, { required });
59
+ const outcome = findPathWithDecision(graph, fromHash, member.headRef.hash, {
60
+ required,
61
+ ...(refName !== undefined ? { refName } : {}),
62
+ });
53
63
 
54
64
  if (outcome.kind === 'unreachable') {
55
65
  return { kind: 'unreachable' };
@@ -60,6 +70,13 @@ export function graphWalkStrategy(input: GraphWalkStrategyInputs): GraphWalkOutc
60
70
 
61
71
  const pathOps: MigrationOps[number][] = [];
62
72
  const providedInvariantsSet = new Set<string>();
73
+ const edgeRefs: Array<{
74
+ migrationHash: string;
75
+ dirName: string;
76
+ from: string;
77
+ to: string;
78
+ operationCount: number;
79
+ }> = [];
63
80
  for (const edge of outcome.decision.selectedPath) {
64
81
  const pkg = packagesByMigrationHash.get(edge.migrationHash);
65
82
  if (!pkg) {
@@ -69,6 +86,13 @@ export function graphWalkStrategy(input: GraphWalkStrategyInputs): GraphWalkOutc
69
86
  }
70
87
  for (const op of pkg.ops) pathOps.push(op);
71
88
  for (const invariant of pkg.metadata.providedInvariants) providedInvariantsSet.add(invariant);
89
+ edgeRefs.push({
90
+ migrationHash: edge.migrationHash,
91
+ dirName: edge.dirName,
92
+ from: edge.from,
93
+ to: edge.to,
94
+ operationCount: pkg.ops.length,
95
+ });
72
96
  }
73
97
 
74
98
  const plan: MigrationPlan = {
@@ -87,6 +111,8 @@ export function graphWalkStrategy(input: GraphWalkStrategyInputs): GraphWalkOutc
87
111
  displayOps: pathOps,
88
112
  destinationContract: member.contract as Contract,
89
113
  strategy: 'graph-walk',
114
+ migrationEdges: edgeRefs,
115
+ pathDecision: outcome.decision,
90
116
  },
91
117
  };
92
118
  }
@@ -19,6 +19,11 @@ export {
19
19
  planAggregate,
20
20
  } from '../aggregate/planner';
21
21
  export { projectSchemaToSpace } from '../aggregate/project-schema-to-space';
22
+ export {
23
+ type GraphWalkOutcome,
24
+ type GraphWalkStrategyInputs,
25
+ graphWalkStrategy,
26
+ } from '../aggregate/strategies/graph-walk';
22
27
  export type {
23
28
  ContractSpaceAggregate,
24
29
  ContractSpaceMember,
@@ -1 +0,0 @@
1
- {"version":3,"file":"migration-graph.d.mts","names":[],"sources":["../../src/migration-graph.ts"],"mappings":";;;;iBAsCgB,gBAAA,CAAiB,QAAA,WAAmB,sBAAA,KAA2B,cAAA;;AAA/E;;;;;;;iBAqFgB,QAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,oBACU,aAAA;;AAJZ;;;;;;;;;;;AAgDA;;;;;;;iBAAgB,sBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,QAAA,EAAU,WAAA,oBACA,aAAA;AAAA,UAsFK,YAAA;EAAA,SACN,YAAA,WAAuB,aAAA;EAAA,SACvB,QAAA;EAAA,SACA,MAAA;EAAA,SACA,gBAAA;EAAA,SACA,eAAA;EAAA,SACA,OAAA;EA5Fc;EAAA,SA8Fd,kBAAA;EARM;;;;;EAAA,SAcN,mBAAA;AAAA;;;;;;;;;AAoBX;;;;;;;;;KAAY,eAAA;EAAA,SACG,IAAA;EAAA,SAAqB,QAAA,EAAU,YAAA;AAAA;EAAA,SAC/B,IAAA;AAAA;EAAA,SAEA,IAAA;EAAA,SACA,cAAA,WAAyB,aAAA;EAAA,SACzB,OAAA;AAAA;;;;;AAyBf;;;UAfiB,2BAAA;EAAA,SACN,OAAA;EAAA,SACA,QAAA,GAAW,WAAA;AAAA;;;;;;;;;;;iBAaN,oBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,OAAA,GAAS,2BAAA,GACR,eAAA;;;;;iBAqLa,mBAAA,CAAoB,KAAA,EAAO,cAAA,EAAgB,QAAA;;;;AAkB3D;;;;;iBAAgB,QAAA,CAAS,KAAA,EAAO,cAAA;;;;;;iBAwChB,mBAAA,CAAoB,KAAA,EAAO,cAAA,GAAiB,aAAA;AAAA,iBAQ5C,YAAA,CAAa,KAAA,EAAO,cAAA;AAAA,iBA8DpB,aAAA,CAAc,KAAA,EAAO,cAAA,YAA0B,aAAA"}