@prisma-next/migration-tools 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +4 -4
  2. package/dist/{errors-DGYwcwXs.mjs → errors-vFROOhCR.mjs} +46 -21
  3. package/dist/errors-vFROOhCR.mjs.map +1 -0
  4. package/dist/exports/aggregate.d.mts +328 -204
  5. package/dist/exports/aggregate.d.mts.map +1 -1
  6. package/dist/exports/aggregate.mjs +480 -243
  7. package/dist/exports/aggregate.mjs.map +1 -1
  8. package/dist/exports/errors.d.mts +2 -2
  9. package/dist/exports/errors.d.mts.map +1 -1
  10. package/dist/exports/errors.mjs +1 -1
  11. package/dist/exports/graph.d.mts +1 -1
  12. package/dist/exports/hash.d.mts +8 -9
  13. package/dist/exports/hash.d.mts.map +1 -1
  14. package/dist/exports/hash.mjs +1 -1
  15. package/dist/exports/invariants.d.mts +1 -1
  16. package/dist/exports/invariants.d.mts.map +1 -1
  17. package/dist/exports/invariants.mjs +1 -1
  18. package/dist/exports/io.d.mts +2 -83
  19. package/dist/exports/io.mjs +1 -1
  20. package/dist/exports/metadata.d.mts +2 -2
  21. package/dist/exports/migration-graph.d.mts +9 -2
  22. package/dist/exports/migration-graph.d.mts.map +1 -0
  23. package/dist/exports/migration-graph.mjs +3 -2
  24. package/dist/exports/migration-ts.d.mts.map +1 -1
  25. package/dist/exports/migration-ts.mjs.map +1 -1
  26. package/dist/exports/migration.d.mts +5 -6
  27. package/dist/exports/migration.d.mts.map +1 -1
  28. package/dist/exports/migration.mjs +14 -32
  29. package/dist/exports/migration.mjs.map +1 -1
  30. package/dist/exports/package.d.mts +1 -1
  31. package/dist/exports/ref-resolution.d.mts +2 -2
  32. package/dist/exports/ref-resolution.d.mts.map +1 -1
  33. package/dist/exports/ref-resolution.mjs +1 -1
  34. package/dist/exports/ref-resolution.mjs.map +1 -1
  35. package/dist/exports/refs.d.mts +15 -2
  36. package/dist/exports/refs.d.mts.map +1 -0
  37. package/dist/exports/refs.mjs +3 -2
  38. package/dist/exports/spaces.d.mts +31 -132
  39. package/dist/exports/spaces.d.mts.map +1 -1
  40. package/dist/exports/spaces.mjs +13 -9
  41. package/dist/exports/spaces.mjs.map +1 -1
  42. package/dist/{graph-BrLXqoUc.d.mts → graph-3dLMZp5l.d.mts} +1 -2
  43. package/dist/graph-3dLMZp5l.d.mts.map +1 -0
  44. package/dist/graph-membership-BV23F1IV.mjs +15 -0
  45. package/dist/graph-membership-BV23F1IV.mjs.map +1 -0
  46. package/dist/{hash-Cr4WIr4Z.mjs → hash--Y7vCpN3.mjs} +8 -9
  47. package/dist/hash--Y7vCpN3.mjs.map +1 -0
  48. package/dist/{invariants-0daYEzyo.mjs → invariants-C23nXy1c.mjs} +2 -2
  49. package/dist/{invariants-0daYEzyo.mjs.map → invariants-C23nXy1c.mjs.map} +1 -1
  50. package/dist/{io-BPLfzvZe.mjs → io-BGlPOt9b.mjs} +100 -13
  51. package/dist/io-BGlPOt9b.mjs.map +1 -0
  52. package/dist/io-BH4G3F-i.d.mts +124 -0
  53. package/dist/io-BH4G3F-i.d.mts.map +1 -0
  54. package/dist/metadata-Bp9X04gM.d.mts +2 -0
  55. package/dist/{migration-graph-nlS4TRpn.mjs → migration-graph-BMAqSfv9.mjs} +6 -26
  56. package/dist/migration-graph-BMAqSfv9.mjs.map +1 -0
  57. package/dist/{migration-graph-De0dUZoC.d.mts → migration-graph-CWEM2SLR.d.mts} +6 -6
  58. package/dist/migration-graph-CWEM2SLR.d.mts.map +1 -0
  59. package/dist/op-schema-D5qkXfEf.mjs.map +1 -1
  60. package/dist/{package-DZj8YvD0.d.mts → package-Ca-J_z_0.d.mts} +1 -1
  61. package/dist/package-Ca-J_z_0.d.mts.map +1 -0
  62. package/dist/{read-contract-space-contract-DRueB4Aa.mjs → read-contract-space-contract-TbeXuJXL.mjs} +32 -5
  63. package/dist/read-contract-space-contract-TbeXuJXL.mjs.map +1 -0
  64. package/dist/{refs-BDHo5l_g.mjs → refs-C-_WUrPw.mjs} +97 -4
  65. package/dist/refs-C-_WUrPw.mjs.map +1 -0
  66. package/dist/refs-C7wuYFqZ.d.mts +42 -0
  67. package/dist/refs-C7wuYFqZ.d.mts.map +1 -0
  68. package/dist/snapshot-Bazwo13S.mjs +137 -0
  69. package/dist/snapshot-Bazwo13S.mjs.map +1 -0
  70. package/dist/verify-contract-spaces-BdysZdQk.d.mts +132 -0
  71. package/dist/verify-contract-spaces-BdysZdQk.d.mts.map +1 -0
  72. package/package.json +18 -9
  73. package/src/aggregate/aggregate.ts +266 -0
  74. package/src/aggregate/check-integrity.ts +243 -0
  75. package/src/aggregate/loader.ts +161 -334
  76. package/src/aggregate/planner-types.ts +14 -14
  77. package/src/aggregate/planner.ts +20 -23
  78. package/src/aggregate/project-schema-to-space.ts +3 -8
  79. package/src/aggregate/strategies/graph-walk.ts +15 -10
  80. package/src/aggregate/strategies/synth.ts +4 -4
  81. package/src/aggregate/types.ts +81 -62
  82. package/src/aggregate/verifier.ts +23 -23
  83. package/src/assert-descriptor-self-consistency.ts +6 -0
  84. package/src/compute-extension-space-apply-path.ts +1 -1
  85. package/src/emit-contract-space-artefacts.ts +4 -3
  86. package/src/errors.ts +58 -2
  87. package/src/exports/aggregate.ts +29 -19
  88. package/src/exports/io.ts +2 -0
  89. package/src/exports/metadata.ts +1 -1
  90. package/src/exports/migration-graph.ts +1 -0
  91. package/src/exports/refs.ts +11 -0
  92. package/src/exports/spaces.ts +3 -0
  93. package/src/graph-membership.ts +17 -0
  94. package/src/graph.ts +0 -1
  95. package/src/hash.ts +7 -8
  96. package/src/integrity-violation.ts +114 -0
  97. package/src/io.ts +139 -14
  98. package/src/metadata.ts +1 -1
  99. package/src/migration-base.ts +10 -30
  100. package/src/migration-graph.ts +7 -35
  101. package/src/read-contract-space-head-ref.ts +5 -2
  102. package/src/refs/snapshot.ts +199 -0
  103. package/src/refs.ts +124 -1
  104. package/src/space-layout.ts +30 -0
  105. package/dist/errors-DGYwcwXs.mjs.map +0 -1
  106. package/dist/exports/io.d.mts.map +0 -1
  107. package/dist/graph-BrLXqoUc.d.mts.map +0 -1
  108. package/dist/hash-Cr4WIr4Z.mjs.map +0 -1
  109. package/dist/io-BPLfzvZe.mjs.map +0 -1
  110. package/dist/metadata-BFX0xdz8.d.mts +0 -2
  111. package/dist/migration-graph-De0dUZoC.d.mts.map +0 -1
  112. package/dist/migration-graph-nlS4TRpn.mjs.map +0 -1
  113. package/dist/package-DZj8YvD0.d.mts.map +0 -1
  114. package/dist/read-contract-space-contract-DRueB4Aa.mjs.map +0 -1
  115. package/dist/refs-BDHo5l_g.mjs.map +0 -1
  116. package/dist/refs-CDaNerhT.d.mts +0 -16
  117. package/dist/refs-CDaNerhT.d.mts.map +0 -1
  118. package/src/aggregate/extract-storage-element-names.ts +0 -75
@@ -1 +1 @@
1
- {"version":3,"file":"aggregate.mjs","names":[],"sources":["../../src/aggregate/extract-storage-element-names.ts","../../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":["/**\n * Extract the set of top-level storage element names a contract claims.\n *\n * Used by the aggregate loader's disjointness check and by\n * `projectSchemaToSpace`'s \"names owned by other members\" walk.\n *\n * **Stopgap — known layering violation.** This helper duck-types the\n * storage shape from framework-domain code that has no business naming\n * family-specific storage idioms. The framework lacks a typed primitive\n * for storage *topology* — the structural backbone of \"what named things\n * does this contract claim?\" independent of what those things are.\n *\n * Behavioural notes for the lifetime of this helper:\n *\n * - SQL contracts contribute table names from every\n * `storage.namespaces[namespaceId].tables` map.\n * - Mongo contracts contribute names from each namespace's `tables`\n * (or `collections`, depending on the per-target Namespace's slot\n * choice). Per-namespace `collections` may appear as a record or as\n * an array of `{ name }` entries; both are accepted defensively.\n * - Root-level `tables` / `collections` records (when present) are\n * also unioned. These root-level walks are belt-and-suspenders for a\n * defensive helper operating on `unknown`; no in-tree contract emits\n * the root shape post-namespace flip.\n * - Unrecognised shapes contribute nothing beyond the walks above.\n * - Record-shape detection excludes arrays so array-shaped values aren't\n * walked as records via numeric keys.\n * - Names that appear in multiple places are deduplicated by the returned\n * `Set`.\n */\nexport function extractStorageElementNames(contract: unknown): Set<string> {\n const names = new Set<string>();\n if (typeof contract !== 'object' || contract === null) return names;\n const storage = (contract as { readonly storage?: unknown }).storage;\n if (typeof storage !== 'object' || storage === null) return names;\n const storageObj = storage as {\n readonly namespaces?: unknown;\n readonly tables?: unknown;\n readonly collections?: unknown;\n };\n\n if (\n typeof storageObj.namespaces === 'object' &&\n storageObj.namespaces !== null &&\n !Array.isArray(storageObj.namespaces)\n ) {\n for (const ns of Object.values(storageObj.namespaces as Record<string, unknown>)) {\n if (typeof ns !== 'object' || ns === null) continue;\n const nsObj = ns as { readonly tables?: unknown; readonly collections?: unknown };\n addRecordKeys(nsObj.tables, names);\n if (Array.isArray(nsObj.collections)) {\n for (const entry of nsObj.collections) {\n if (typeof entry === 'object' && entry !== null) {\n const name = (entry as { readonly name?: unknown }).name;\n if (typeof name === 'string') names.add(name);\n }\n }\n } else {\n addRecordKeys(nsObj.collections, names);\n }\n }\n }\n\n addRecordKeys(storageObj.tables, names);\n addRecordKeys(storageObj.collections, names);\n return names;\n}\n\nfunction addRecordKeys(value: unknown, names: Set<string>): void {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n for (const name of Object.keys(value as Record<string, unknown>)) {\n names.add(name);\n }\n }\n}\n","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 { MigrationToolsError } from '../errors';\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 { extractStorageElementNames } from './extract-storage-element-names';\nimport type { ContractSpaceAggregate, ContractSpaceMember, HydratedMigrationGraph } from './types';\n\nfunction integrityDetail(error: unknown): string {\n if (MigrationToolsError.is(error)) {\n return error.why;\n }\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n}\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 *\n * Whether the descriptor declares a contract space is decided by whether\n * its corresponding `migrations/<id>/` directory exists on disk\n * (materialised by the seed phase before the loader runs); the loader\n * never reads the descriptor's `contractJson` itself. That makes the\n * aggregate's apply / verify paths byte-for-byte independent of the\n * descriptor module — `db verify` succeeds even if the descriptor's\n * `contractJson` is a throwing getter.\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}\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 deserializeContract: (contractJson: unknown) => Contract;\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: '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 * the app contract value the caller supplies.\n *\n * The loader is the **only** descriptor-import boundary at apply /\n * verify time, but it intentionally does **not** read the extension\n * descriptor's `contractJson` value. Each extension space's contract\n * is read from its on-disk `migrations/<id>/contract.json` mirror; the\n * descriptor's role is exhausted by the seed phase that wrote that\n * mirror in the first place. The loader composes existing\n * migration-tools primitives — layout precheck (via\n * {@link listContractSpaceDirectories}), integrity checks (via\n * {@link readMigrationsDir} / {@link readContractSpaceHeadRef} /\n * {@link readContractSpaceContract} / `deserializeContract`), and\n * disjointness — into a single typed value.\n *\n * Failure semantics: every failure variant in {@link LoadAggregateError}\n * short-circuits the load.\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 //\n // Every declared extension contributes an entry to the aggregate when\n // a corresponding `migrations/<id>/` directory exists on disk. The\n // loader treats the directory's presence as the membership signal —\n // the descriptor itself is not read — so codec-only extensions (no\n // on-disk dir) and contract-space extensions (dir present) are\n // distinguished structurally.\n const declaredSpaceIds = new Set(input.declaredExtensions.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.\n const loadedExtensions: LoadedExtensionState[] = [];\n for (const entry of [...input.declaredExtensions].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: integrityDetail(error),\n });\n }\n\n let spaceContract: Contract;\n try {\n spaceContract = input.deserializeContract(spaceContractRaw);\n } catch (error) {\n return notOk({\n kind: 'validationFailure',\n spaceId: entry.id,\n detail: integrityDetail(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 // 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: integrityDetail(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: integrityDetail(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 elements = extractStorageElementNames(member.contract);\n for (const elementName of elements) {\n const claimers = elementClaimedBy.get(elementName);\n if (claimers) claimers.push(member.spaceId);\n else elementClaimedBy.set(elementName, [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","import 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 * `migrate` to surface the user-supplied `--to <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,\n strategy: 'graph-walk',\n migrationEdges: edgeRefs,\n pathDecision: outcome.decision,\n },\n };\n}\n","import { extractStorageElementNames } from './extract-storage-element-names';\nimport type { ContractSpaceMember } from './types';\n\n/**\n * Project the **introspected live schema** to the slice claimed by a\n * single contract-space member.\n *\n * \"Schema\" here means the live introspected database state — the\n * planner / verifier sees this object as a `MongoSchemaIR` (Mongo) or\n * `SqlSchemaIR` (SQL). It is **not** a database schema in the SQL\n * `CREATE SCHEMA` sense, nor a contract-space namespace. The\n * function's job is to filter that introspected state down to the\n * elements claimed by one space, so a per-space verify pass doesn't\n * see another space's storage as \"extras\".\n *\n * Returns the same `schema` value with every top-level storage element\n * (table or collection) claimed by **other** members of the aggregate\n * removed. Elements not claimed by any member flow through unchanged —\n * the planner / verifier sees them as 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 elements claimed\n * by other members as \"extras\" and emit destructive ops to drop them.\n * - The aggregate verifier's **schemaCheck**: projects per member so the\n * single-contract verify only sees the slice claimed by the member it\n * is checking. Closes the architectural concern that a multi-member\n * deployment makes each member's elements look like extras to every\n * 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 * Two storage shapes are recognised today:\n *\n * - SQL families expose `storage.tables: Record<string, ...>` on\n * contracts and the introspected schema mirrors the same record shape.\n * Pruning iterates the record entries.\n * - Mongo exposes `storage.collections: Record<string, ...>` on\n * contracts; the introspected `MongoSchemaIR` exposes\n * `collections: ReadonlyArray<{name: string, ...}>`. Pruning iterates\n * the array on the schema side and the record's keys on the\n * other-member side.\n *\n * Schemas of unrecognised shape are returned unchanged. The function\n * never imports family classes (`SqlSchemaIR`, `MongoSchemaIR`); the\n * projected schema is a plain object — `{...schema, tables: pruned}` or\n * `{...schema, collections: pruned}` — that downstream consumers\n * duck-type. A future family with a different storage shape gets the\n * schema returned unchanged rather than blowing up the aggregate\n * planner.\n *\n * Record-shape detection guards against arrays (`!Array.isArray`) so\n * an unrecognised array-shaped value falls through unchanged rather\n * than being pruned by numeric keys.\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\n const ownedByOthers = collectOwnedNames(member, otherMembers);\n if (ownedByOthers.size === 0) return schema;\n\n const schemaObj = schema as { readonly tables?: unknown; readonly collections?: unknown };\n\n if (\n typeof schemaObj.tables === 'object' &&\n schemaObj.tables !== null &&\n !Array.isArray(schemaObj.tables)\n ) {\n return pruneRecord(schemaObj, 'tables', ownedByOthers);\n }\n\n if (Array.isArray(schemaObj.collections)) {\n return pruneCollectionsArray(schemaObj, ownedByOthers);\n }\n\n if (\n typeof schemaObj.collections === 'object' &&\n schemaObj.collections !== null &&\n !Array.isArray(schemaObj.collections)\n ) {\n return pruneRecord(schemaObj, 'collections', ownedByOthers);\n }\n\n return schema;\n}\n\n/**\n * Collect the set of storage element names claimed by other members.\n * Reuses the loader's `extractStorageElementNames` helper so the\n * tables/collections walk lives in exactly one place.\n */\nfunction collectOwnedNames(\n member: ContractSpaceMember,\n otherMembers: ReadonlyArray<ContractSpaceMember>,\n): Set<string> {\n const owned = new Set<string>();\n for (const other of otherMembers) {\n if (other.spaceId === member.spaceId) continue;\n for (const name of extractStorageElementNames(other.contract)) {\n owned.add(name);\n }\n }\n return owned;\n}\n\nfunction pruneRecord(\n schemaObj: { readonly tables?: unknown; readonly collections?: unknown },\n field: 'tables' | 'collections',\n ownedByOthers: ReadonlySet<string>,\n): unknown {\n const source = schemaObj[field] as Record<string, unknown>;\n let removed = false;\n const pruned: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(source)) {\n if (ownedByOthers.has(name)) {\n removed = true;\n } else {\n pruned[name] = value;\n }\n }\n if (!removed) return schemaObj;\n return { ...schemaObj, [field]: pruned };\n}\n\nfunction pruneCollectionsArray(\n schemaObj: { readonly collections?: unknown },\n ownedByOthers: ReadonlySet<string>,\n): unknown {\n const source = schemaObj.collections as ReadonlyArray<unknown>;\n let removed = false;\n const pruned: unknown[] = [];\n for (const entry of source) {\n if (typeof entry === 'object' && entry !== null) {\n const name = (entry as { readonly name?: unknown }).name;\n if (typeof name === 'string' && ownedByOthers.has(name)) {\n removed = true;\n continue;\n }\n }\n pruned.push(entry);\n }\n if (!removed) return schemaObj;\n return { ...schemaObj, collections: pruned };\n}\n","import 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,\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 AggregateMigrationEdgeRef,\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 { extractStorageElementNames } from './extract-storage-element-names';\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 for (const name of extractStorageElementNames(member.contract)) {\n claimedTables.add(name);\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":["detailOf"],"sources":["../../src/aggregate/aggregate.ts","../../src/aggregate/check-integrity.ts","../../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 { readFile } from 'node:fs/promises';\nimport type { Contract } from '@prisma-next/contract/types';\nimport { join } from 'pathe';\nimport {\n errorBundleNotFoundForGraphNode,\n errorContractDeserializationFailed,\n errorHashNotInGraph,\n errorInvalidJson,\n errorMissingFile,\n errorSnapshotMissing,\n MigrationToolsError,\n} from '../errors';\nimport type { MigrationGraph } from '../graph';\nimport { isGraphNode } from '../graph-membership';\nimport type { IntegrityQueryOptions, IntegrityViolation } from '../integrity-violation';\nimport { reconstructGraph } from '../migration-graph';\nimport type { OnDiskMigrationPackage } from '../package';\nimport type { Refs } from '../refs';\nimport { readRefSnapshot } from '../refs/snapshot';\nimport type { ContractSpaceHeadRecord } from '../verify-contract-spaces';\nimport type {\n ContractAtOptions,\n ContractAtResult,\n ContractSpaceAggregate,\n ContractSpaceMember,\n} from './types';\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\nfunction contractAtMemoKey(hash: string, refName: string | undefined): string {\n return `${hash}\\0${refName ?? ''}`;\n}\n\nfunction deserializeContractAtPath(\n filePath: string,\n contractJson: unknown,\n deserializeContract: (raw: unknown) => Contract,\n): Contract {\n try {\n return deserializeContract(contractJson);\n } catch (error) {\n if (MigrationToolsError.is(error)) {\n throw error;\n }\n const message = error instanceof Error ? error.message : String(error);\n throw errorContractDeserializationFailed(filePath, message);\n }\n}\n\nasync function readGraphNodeEndContract(\n packageDir: string,\n deserializeContract: (raw: unknown) => Contract,\n): Promise<{ contractJson: unknown; contractDts: string; contract: Contract }> {\n const jsonPath = join(packageDir, 'end-contract.json');\n const dtsPath = join(packageDir, 'end-contract.d.ts');\n\n let rawJson: string;\n try {\n rawJson = await readFile(jsonPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile('end-contract.json', packageDir);\n }\n throw error;\n }\n\n let contractJson: unknown;\n try {\n contractJson = JSON.parse(rawJson);\n } catch (error) {\n throw errorInvalidJson(jsonPath, error instanceof Error ? error.message : String(error));\n }\n\n let contractDts: string;\n try {\n contractDts = await readFile(dtsPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile('end-contract.d.ts', packageDir);\n }\n throw error;\n }\n\n const contract = deserializeContractAtPath(jsonPath, contractJson, deserializeContract);\n return { contractJson, contractDts, contract };\n}\n\nasync function resolveContractAt(args: {\n readonly hash: string;\n readonly opts: ContractAtOptions | undefined;\n readonly refsDir: string;\n readonly packages: readonly OnDiskMigrationPackage[];\n readonly graph: MigrationGraph;\n readonly deserializeContract: (raw: unknown) => Contract;\n}): Promise<ContractAtResult> {\n const { hash, opts, refsDir, packages, graph, deserializeContract } = args;\n const refName = opts?.refName;\n\n if (refName !== undefined) {\n const snapshot = await readRefSnapshot(refsDir, refName);\n if (snapshot) {\n const jsonPath = join(refsDir, `${refName}.contract.json`);\n return {\n hash,\n contractJson: snapshot.contract,\n contractDts: snapshot.contractDts,\n contract: deserializeContractAtPath(jsonPath, snapshot.contract, deserializeContract),\n provenance: 'snapshot',\n };\n }\n\n if (isGraphNode(hash, graph)) {\n return resolveGraphNodeContractAt({\n hash,\n packages,\n deserializeContract,\n explicitLabel: refName,\n });\n }\n\n throw errorSnapshotMissing(refName);\n }\n\n if (isGraphNode(hash, graph)) {\n return resolveGraphNodeContractAt({ hash, packages, deserializeContract });\n }\n\n throw errorHashNotInGraph(hash, graph);\n}\n\nasync function resolveGraphNodeContractAt(args: {\n readonly hash: string;\n readonly packages: readonly OnDiskMigrationPackage[];\n readonly deserializeContract: (raw: unknown) => Contract;\n readonly explicitLabel?: string;\n}): Promise<ContractAtResult> {\n const { hash, packages, deserializeContract, explicitLabel } = args;\n const matchingBundle = packages.find((pkg) => pkg.metadata.to === hash);\n if (!matchingBundle) {\n throw errorBundleNotFoundForGraphNode(hash, explicitLabel);\n }\n\n const { contractJson, contractDts, contract } = await readGraphNodeEndContract(\n matchingBundle.dirPath,\n deserializeContract,\n );\n return {\n hash,\n contractJson,\n contractDts,\n contract,\n provenance: 'graph-node',\n sourceDir: matchingBundle.dirPath,\n };\n}\n\n/**\n * Resolve a member's head ref, asserting it is present. The apply/verify\n * engine only runs after `checkIntegrity` has refused on `headRefMissing`,\n * so a member reaching the planner / verifier without a head ref is a\n * programming error (the integrity gate was skipped), not a user-facing\n * state. The app member's head ref is always synthesised, so this only\n * ever guards an ungated extension space.\n */\nexport function requireHeadRef(member: ContractSpaceMember): ContractSpaceHeadRecord {\n if (member.headRef === null) {\n throw new Error(\n `Contract space \"${member.spaceId}\" has no head ref; the integrity gate must refuse a missing head ref before planning or verifying.`,\n );\n }\n return member.headRef;\n}\n\n/**\n * Build a {@link ContractSpaceMember} with lazily-memoised `graph()`,\n * `contract()`, and `contractAt()` facets.\n *\n * `graph()` reconstructs the migration graph from `packages` on first\n * call and caches it. `contract()` calls `resolveContract` on first call\n * and caches the result; a throwing `resolveContract` (e.g. a missing or\n * undeserializable on-disk contract) re-throws on each call rather than\n * caching a value — `checkIntegrity` surfaces that as `contractUnreadable`.\n * `contractAt()` materializes the contract at an arbitrary graph node with\n * the same resolution order as plan-time ref resolution: ref snapshot first\n * (when `opts.refName` is set), else the matching package's `end-contract.*`.\n */\nexport function createContractSpaceMember(args: {\n readonly spaceId: string;\n readonly packages: readonly OnDiskMigrationPackage[];\n readonly refs: Refs;\n readonly headRef: ContractSpaceHeadRecord | null;\n readonly refsDir: string;\n readonly resolveContract: () => Contract;\n readonly deserializeContract: (raw: unknown) => Contract;\n}): ContractSpaceMember {\n const { spaceId, packages, refs, headRef, refsDir, resolveContract, deserializeContract } = args;\n let graphMemo: MigrationGraph | undefined;\n let contractMemo: Contract | undefined;\n const contractAtMemo = new Map<string, ContractAtResult>();\n\n function memberGraph(): MigrationGraph {\n graphMemo ??= reconstructGraph(packages);\n return graphMemo;\n }\n\n return {\n spaceId,\n packages,\n refs,\n headRef,\n graph: memberGraph,\n contract() {\n contractMemo ??= resolveContract();\n return contractMemo;\n },\n async contractAt(hash, opts) {\n const key = contractAtMemoKey(hash, opts?.refName);\n const cached = contractAtMemo.get(key);\n if (cached) {\n return cached;\n }\n\n const result = await resolveContractAt({\n hash,\n opts,\n refsDir,\n packages,\n graph: memberGraph(),\n deserializeContract,\n });\n contractAtMemo.set(key, result);\n return result;\n },\n };\n}\n\n/**\n * Assemble a {@link ContractSpaceAggregate} value from its members and a\n * `checkIntegrity` implementation. The query methods (`listSpaces` /\n * `hasSpace` / `space` / `spaces`) are derived here so every aggregate —\n * loader-built or test-built — shares one query surface: `app` first,\n * then `extensions` in the order supplied (the loader sorts them\n * lex-ascending by `spaceId`).\n */\nexport function createContractSpaceAggregate(args: {\n readonly targetId: string;\n readonly app: ContractSpaceMember;\n readonly extensions: readonly ContractSpaceMember[];\n readonly checkIntegrity: (opts?: IntegrityQueryOptions) => readonly IntegrityViolation[];\n}): ContractSpaceAggregate {\n const { targetId, app, extensions, checkIntegrity } = args;\n const ordered: readonly ContractSpaceMember[] = [app, ...extensions];\n const byId = new Map(ordered.map((m) => [m.spaceId, m]));\n return {\n targetId,\n app,\n extensions,\n listSpaces: () => ordered.map((m) => m.spaceId),\n hasSpace: (id) => byId.has(id),\n space: (id) => byId.get(id),\n spaces: () => ordered,\n checkIntegrity,\n };\n}\n","import { elementCoordinates } from '@prisma-next/framework-components/ir';\nimport { EMPTY_CONTRACT_HASH } from '../constants';\nimport { MigrationToolsError } from '../errors';\nimport type {\n DeclaredExtensionEntry,\n IntegrityQueryOptions,\n IntegrityViolation,\n} from '../integrity-violation';\nimport type { PackageLoadProblem } from '../io';\nimport type { OnDiskMigrationPackage } from '../package';\nimport type { RefLoadProblem } from '../refs';\nimport type { ContractSpaceMember } from './types';\n\n/**\n * One space's load-time facts that `checkIntegrity` judges: the loaded\n * member, the load-time problems `readMigrationsDir` surfaced for it, and\n * whether it is the app space (the app head ref is synthesised, so the\n * head-ref checks are skipped for it).\n */\nexport interface IntegritySpaceState {\n readonly member: ContractSpaceMember;\n readonly problems: readonly PackageLoadProblem[];\n /** Per-ref problems: a user ref `*.json` that exists but is unparseable. */\n readonly refProblems: readonly RefLoadProblem[];\n /**\n * The space's `refs/head.json` problem when it exists but is unparseable.\n * `null` means the head ref was read cleanly or is genuinely absent —\n * the absent case is judged `headRefMissing`, the corrupt case here is\n * judged `refUnreadable` (and suppresses `headRefMissing`).\n */\n readonly headRefProblem: RefLoadProblem | null;\n readonly isApp: boolean;\n}\n\nexport interface IntegrityComputationInput {\n readonly targetId: string;\n readonly spaces: readonly IntegritySpaceState[];\n}\n\n/**\n * Walk the loaded model and return **every** integrity violation — never\n * bailing at the first. Structurally-derivable violations (load-time\n * problems, self-edges, missing / unreachable head refs) are always\n * produced; layout-drift checks require `declaredExtensions`, and\n * contract / target / disjointness checks require `checkContracts`.\n */\nexport function computeIntegrityViolations(\n input: IntegrityComputationInput,\n opts?: IntegrityQueryOptions,\n): readonly IntegrityViolation[] {\n const violations: IntegrityViolation[] = [];\n\n for (const { member, problems, refProblems, headRefProblem, isApp } of input.spaces) {\n const { spaceId } = member;\n\n for (const problem of problems) {\n violations.push(loadProblemToViolation(spaceId, problem));\n }\n\n for (const refProblem of refProblems) {\n violations.push({\n kind: 'refUnreadable',\n spaceId,\n refName: refProblem.refName,\n detail: refProblem.detail,\n });\n }\n if (headRefProblem !== null) {\n violations.push({\n kind: 'refUnreadable',\n spaceId,\n refName: headRefProblem.refName,\n detail: headRefProblem.detail,\n });\n }\n\n for (const pkg of member.packages) {\n const from = pkg.metadata.from ?? EMPTY_CONTRACT_HASH;\n const isSelfEdge = from === pkg.metadata.to;\n const hasDataOp = pkg.ops.some((op) => op.operationClass === 'data');\n if (isSelfEdge && !hasDataOp) {\n violations.push({ kind: 'sameSourceAndTarget', spaceId, dirName: pkg.dirName, hash: from });\n }\n }\n\n violations.push(...duplicateMigrationHashViolations(spaceId, member.packages));\n\n // The app head ref is synthesised from the live contract, so it is\n // always present and reachable; only extension spaces read their head\n // ref from disk and can be missing or point outside the graph. A head\n // ref that exists but is unparseable is already surfaced above as\n // `refUnreadable`, so it is not also reported as `headRefMissing`.\n if (!isApp && headRefProblem === null) {\n if (member.headRef === null) {\n violations.push({ kind: 'headRefMissing', spaceId });\n } else if (!headRefPresentInGraph(member, member.headRef.hash)) {\n violations.push({ kind: 'headRefNotInGraph', spaceId, hash: member.headRef.hash });\n }\n }\n }\n\n if (opts?.declaredExtensions !== undefined) {\n violations.push(...layoutViolations(input.spaces, opts.declaredExtensions));\n }\n\n if (opts?.checkContracts === true) {\n violations.push(...contractViolations(input));\n }\n\n return violations;\n}\n\nexport function loadProblemToViolation(\n spaceId: string,\n problem: PackageLoadProblem,\n): IntegrityViolation {\n switch (problem.kind) {\n case 'hashMismatch':\n return {\n kind: 'hashMismatch',\n spaceId,\n dirName: problem.dirName,\n stored: problem.stored,\n computed: problem.computed,\n };\n case 'providedInvariantsMismatch':\n return { kind: 'providedInvariantsMismatch', spaceId, dirName: problem.dirName };\n case 'packageUnloadable':\n return {\n kind: 'packageUnloadable',\n spaceId,\n dirName: problem.dirName,\n detail: problem.detail,\n };\n }\n}\n\nfunction duplicateMigrationHashViolations(\n spaceId: string,\n packages: readonly OnDiskMigrationPackage[],\n): readonly IntegrityViolation[] {\n const dirNamesByHash = new Map<string, string[]>();\n for (const pkg of packages) {\n const hash = pkg.metadata.migrationHash;\n const dirNames = dirNamesByHash.get(hash);\n if (dirNames) dirNames.push(pkg.dirName);\n else dirNamesByHash.set(hash, [pkg.dirName]);\n }\n\n const out: IntegrityViolation[] = [];\n for (const [migrationHash, dirNames] of dirNamesByHash) {\n if (dirNames.length > 1) {\n out.push({\n kind: 'duplicateMigrationHash',\n spaceId,\n migrationHash,\n dirNames: [...dirNames].sort(),\n });\n }\n }\n return out;\n}\n\n/**\n * Whether a space's head-ref hash is present in its reconstructed graph.\n * An empty graph is reachable only by the empty-contract sentinel.\n */\nfunction headRefPresentInGraph(member: ContractSpaceMember, headHash: string): boolean {\n const graph = member.graph();\n if (graph.nodes.size === 0) {\n return headHash === EMPTY_CONTRACT_HASH;\n }\n return graph.nodes.has(headHash);\n}\n\nfunction layoutViolations(\n spaces: readonly IntegritySpaceState[],\n declaredExtensions: readonly DeclaredExtensionEntry[],\n): readonly IntegrityViolation[] {\n const out: IntegrityViolation[] = [];\n const extensionSpaceIds = new Set(spaces.filter((s) => !s.isApp).map((s) => s.member.spaceId));\n const declaredIds = new Set(declaredExtensions.map((d) => d.id));\n\n for (const id of [...extensionSpaceIds].sort()) {\n if (!declaredIds.has(id)) {\n out.push({ kind: 'orphanSpaceDir', spaceId: id });\n }\n }\n for (const id of [...declaredIds].sort()) {\n if (!extensionSpaceIds.has(id)) {\n out.push({ kind: 'declaredButUnmigrated', spaceId: id });\n }\n }\n return out;\n}\n\nfunction contractViolations(input: IntegrityComputationInput): readonly IntegrityViolation[] {\n const out: IntegrityViolation[] = [];\n const elementClaimedBy = new Map<string, string[]>();\n\n for (const { member } of input.spaces) {\n let contract: ReturnType<ContractSpaceMember['contract']>;\n try {\n contract = member.contract();\n } catch (error) {\n out.push({ kind: 'contractUnreadable', spaceId: member.spaceId, detail: detailOf(error) });\n continue;\n }\n\n if (contract.target !== input.targetId) {\n out.push({\n kind: 'targetMismatch',\n spaceId: member.spaceId,\n expected: input.targetId,\n actual: contract.target,\n });\n }\n\n for (const { entityName: elementName } of elementCoordinates(contract.storage)) {\n const claimers = elementClaimedBy.get(elementName);\n if (claimers) claimers.push(member.spaceId);\n else elementClaimedBy.set(elementName, [member.spaceId]);\n }\n }\n\n const disjointness: IntegrityViolation[] = [];\n for (const [element, claimedBy] of elementClaimedBy) {\n if (claimedBy.length > 1) {\n disjointness.push({ kind: 'disjointness', element, claimedBy: [...claimedBy].sort() });\n }\n }\n disjointness.sort((a, b) =>\n a.kind === 'disjointness' && b.kind === 'disjointness' ? a.element.localeCompare(b.element) : 0,\n );\n out.push(...disjointness);\n return out;\n}\n\nfunction detailOf(error: unknown): string {\n if (MigrationToolsError.is(error)) return error.why;\n if (error instanceof Error) return error.message;\n return String(error);\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport { MigrationToolsError } from '../errors';\nimport { readMigrationsDir } from '../io';\nimport { readContractSpaceContract } from '../read-contract-space-contract';\nimport { readContractSpaceHeadRef } from '../read-contract-space-head-ref';\nimport { HEAD_REF_NAME, type RefLoadProblem, readRefsTolerant } from '../refs';\nimport {\n APP_SPACE_ID,\n isValidSpaceId,\n RESERVED_SPACE_SUBDIR_NAMES,\n spaceMigrationDirectory,\n spaceRefsDirectory,\n} from '../space-layout';\nimport { listContractSpaceDirectories } from '../verify-contract-spaces';\nimport { createContractSpaceAggregate, createContractSpaceMember } from './aggregate';\nimport { computeIntegrityViolations, type IntegritySpaceState } from './check-integrity';\nimport type { ContractSpaceAggregate } from './types';\n\nexport type { DeclaredExtensionEntry } from '../integrity-violation';\n\n/**\n * Inputs for {@link loadContractSpaceAggregate}.\n *\n * Construction reads migration **state** from disk (`migrations/<space>/`\n * packages + refs + head refs). The app's *live* contract is not a disk\n * artefact — in Prisma Next it is always compiled from the project's\n * central contract, so the caller always has it and threads it in as\n * `appContract`. `deserializeContract` is held and called lazily only for\n * the on-disk extension contracts (`migrations/<ext>/contract.json`).\n */\nexport interface LoadAggregateInput {\n readonly migrationsDir: string;\n readonly deserializeContract: (raw: unknown) => Contract;\n readonly appContract: Contract;\n}\n\n/**\n * Build a tolerant, queryable {@link ContractSpaceAggregate} from on-disk\n * migration state plus the caller's live app contract.\n *\n * Building **never throws on disk content**: a hash- or\n * invariants-mismatched package is retained, an unparseable package is\n * omitted, a missing extension head ref leaves `headRef: null`, and an\n * unreadable on-disk contract defers its failure to `member.contract()`.\n * Every such problem is judged by {@link ContractSpaceAggregate.checkIntegrity}\n * rather than aborting the load. The only rejections are catastrophic I/O\n * (a `migrations/` that exists but is unreadable for reasons other than\n * absence).\n *\n * The app space's head ref is synthesised from the live contract's\n * storage hash (the app contract is authored independently of the\n * migration graph), and `app.contract()` returns the supplied contract.\n * Extension spaces read their contract, refs, and head ref from disk.\n */\nexport async function loadContractSpaceAggregate(\n input: LoadAggregateInput,\n): Promise<ContractSpaceAggregate> {\n const { migrationsDir, deserializeContract, appContract } = input;\n const targetId = appContract.target;\n\n const appState = await loadAppSpace(migrationsDir, appContract, deserializeContract);\n const extensionStates = await loadExtensionSpaces(migrationsDir, deserializeContract);\n\n const spaces: readonly IntegritySpaceState[] = [appState, ...extensionStates];\n\n return createContractSpaceAggregate({\n targetId,\n app: appState.member,\n extensions: extensionStates.map((state) => state.member),\n checkIntegrity: (opts) => computeIntegrityViolations({ targetId, spaces }, opts),\n });\n}\n\nasync function loadAppSpace(\n migrationsDir: string,\n appContract: Contract,\n deserializeContract: (raw: unknown) => Contract,\n): Promise<IntegritySpaceState> {\n const spaceDir = spaceMigrationDirectory(migrationsDir, APP_SPACE_ID);\n const { packages, problems } = await readMigrationsDir(spaceDir);\n const { refs, problems: refProblems } = await readRefsTolerant(spaceRefsDirectory(spaceDir));\n\n const member = createContractSpaceMember({\n spaceId: APP_SPACE_ID,\n packages,\n refs,\n headRef: { hash: appContract.storage.storageHash, invariants: [] },\n refsDir: spaceRefsDirectory(spaceDir),\n resolveContract: () => appContract,\n deserializeContract,\n });\n\n // The app head ref is synthesised from the live contract, so there is\n // no on-disk head.json to be missing or corrupt for it.\n return { member, problems, refProblems, headRefProblem: null, isApp: true };\n}\n\nasync function loadExtensionSpaces(\n migrationsDir: string,\n deserializeContract: (raw: unknown) => Contract,\n): Promise<readonly IntegritySpaceState[]> {\n const candidateDirs = await listContractSpaceDirectories(migrationsDir);\n const extensionIds = candidateDirs\n .filter((name) => name !== APP_SPACE_ID)\n .filter((name) => !RESERVED_SPACE_SUBDIR_NAMES.has(name))\n .filter(isValidSpaceId)\n .sort();\n\n const states: IntegritySpaceState[] = [];\n for (const spaceId of extensionIds) {\n states.push(await loadExtensionSpace(migrationsDir, spaceId, deserializeContract));\n }\n return states;\n}\n\nasync function loadExtensionSpace(\n migrationsDir: string,\n spaceId: string,\n deserializeContract: (raw: unknown) => Contract,\n): Promise<IntegritySpaceState> {\n const spaceDir = spaceMigrationDirectory(migrationsDir, spaceId);\n const { packages, problems } = await readMigrationsDir(spaceDir);\n const { refs, problems: refProblems } = await readRefsTolerant(spaceRefsDirectory(spaceDir));\n const { headRef, problem: headRefProblem } = await readHeadRefTolerant(migrationsDir, spaceId);\n const rawContract = await readRawContractDeferred(migrationsDir, spaceId);\n\n const member = createContractSpaceMember({\n spaceId,\n packages,\n refs,\n headRef,\n refsDir: spaceRefsDirectory(spaceDir),\n resolveContract: () => deserializeContract(rawContract()),\n deserializeContract,\n });\n\n return { member, problems, refProblems, headRefProblem, isApp: false };\n}\n\n/**\n * The result of resolving an extension's `refs/head.json`: the parsed\n * head ref (or `null` when the file is absent or corrupt) plus a problem\n * when the file exists but cannot be parsed.\n */\ninterface HeadRefReadResult {\n readonly headRef: Awaited<ReturnType<typeof readContractSpaceHeadRef>>;\n readonly problem: RefLoadProblem | null;\n}\n\n/**\n * Read an extension's head ref, distinguishing a *genuinely absent*\n * `head.json` (`headRef: null`, no problem — judged `headRefMissing`)\n * from one that *exists but cannot be parsed* (`headRef: null` plus a\n * problem — judged `refUnreadable`, not `headRefMissing`).\n * `readContractSpaceHeadRef` already returns `null` only for ENOENT and\n * throws for unparseable / schema-invalid content, so the throw is the\n * corruption signal. Construction never throws on disk content.\n */\nfunction isToleratedRefHeadReadError(error: unknown): boolean {\n if (MigrationToolsError.is(error)) return true;\n if (!(error instanceof Error)) return false;\n const code = (error as NodeJS.ErrnoException).code;\n return code === 'ENOENT' || code === 'EISDIR';\n}\n\nasync function readHeadRefTolerant(\n migrationsDir: string,\n spaceId: string,\n): Promise<HeadRefReadResult> {\n try {\n const headRef = await readContractSpaceHeadRef(migrationsDir, spaceId);\n return { headRef, problem: null };\n } catch (error) {\n if (!isToleratedRefHeadReadError(error)) {\n throw error;\n }\n return { headRef: null, problem: { refName: HEAD_REF_NAME, detail: detailOf(error) } };\n }\n}\n\nfunction detailOf(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\n/**\n * Read the raw on-disk contract eagerly (cheap I/O) but defer its\n * (throwing) failure to call time, so a missing or unparseable\n * `contract.json` becomes a `contract()` throw — surfaced as\n * `contractUnreadable` — rather than a construction failure.\n */\nasync function readRawContractDeferred(\n migrationsDir: string,\n spaceId: string,\n): Promise<() => unknown> {\n try {\n const raw = await readContractSpaceContract(migrationsDir, spaceId);\n return () => raw;\n } catch (error) {\n return () => {\n throw error;\n };\n }\n}\n","import type { MigrationPlan } from '@prisma-next/framework-components/control';\nimport { EMPTY_CONTRACT_HASH } from '../../constants';\nimport { findPathWithDecision } from '../../migration-graph';\nimport type { MigrationOps, OnDiskMigrationPackage } from '../../package';\nimport { requireHeadRef } from '../aggregate';\nimport type { ContractMarkerRecordLike } from '../marker-types';\nimport type { PerSpacePlan } 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 member's lazily-reconstructed `graph()`\n * instead of re-reading from disk. The aggregate planner converts\n * these into {@link import('../planner-types').PlannerError}\n * variants.\n */\nexport type GraphWalkOutcome =\n | { readonly kind: 'ok'; readonly result: PerSpacePlan }\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 * `migrate` to surface the user-supplied `--to <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 headRef = requireHeadRef(member);\n const graph = member.graph();\n const packagesByMigrationHash = new Map<string, OnDiskMigrationPackage>(\n member.packages.map((pkg) => [pkg.metadata.migrationHash, pkg]),\n );\n\n const fromHash = currentMarker?.storageHash ?? EMPTY_CONTRACT_HASH;\n const markerInvariants = new Set(currentMarker?.invariants ?? []);\n const required = new Set(headRef.invariants.filter((id) => !markerInvariants.has(id)));\n\n const outcome = findPathWithDecision(graph, fromHash, 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: 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(),\n strategy: 'graph-walk',\n migrationEdges: edgeRefs,\n pathDecision: outcome.decision,\n },\n };\n}\n","import { elementCoordinates } from '@prisma-next/framework-components/ir';\nimport type { ContractSpaceMember } from './types';\n\n/**\n * Project the **introspected live schema** to the slice claimed by a\n * single contract-space member.\n *\n * \"Schema\" here means the live introspected database state — the\n * planner / verifier sees this object as a `MongoSchemaIR` (Mongo) or\n * `SqlSchemaIR` (SQL). It is **not** a database schema in the SQL\n * `CREATE SCHEMA` sense, nor a contract-space namespace. The\n * function's job is to filter that introspected state down to the\n * elements claimed by one space, so a per-space verify pass doesn't\n * see another space's storage as \"extras\".\n *\n * Returns the same `schema` value with every top-level storage element\n * (table or collection) claimed by **other** members of the aggregate\n * removed. Elements not claimed by any member flow through unchanged —\n * the planner / verifier sees them as 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 elements claimed\n * by other members as \"extras\" and emit destructive ops to drop them.\n * - The aggregate verifier's **schemaCheck**: projects per member so the\n * single-contract verify only sees the slice claimed by the member it\n * is checking. Closes the architectural concern that a multi-member\n * deployment makes each member's elements look like extras to every\n * 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 * Two storage shapes are recognised today:\n *\n * - SQL families expose `storage.tables: Record<string, ...>` on\n * contracts and the introspected schema mirrors the same record shape.\n * Pruning iterates the record entries.\n * - Mongo exposes `storage.collections: Record<string, ...>` on\n * contracts; the introspected `MongoSchemaIR` exposes\n * `collections: ReadonlyArray<{name: string, ...}>`. Pruning iterates\n * the array on the schema side and the record's keys on the\n * other-member side.\n *\n * Schemas of unrecognised shape are returned unchanged. The function\n * never imports family classes (`SqlSchemaIR`, `MongoSchemaIR`); the\n * projected schema is a plain object — `{...schema, tables: pruned}` or\n * `{...schema, collections: pruned}` — that downstream consumers\n * duck-type. A future family with a different storage shape gets the\n * schema returned unchanged rather than blowing up the aggregate\n * planner.\n *\n * Record-shape detection guards against arrays (`!Array.isArray`) so\n * an unrecognised array-shaped value falls through unchanged rather\n * than being pruned by numeric keys.\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\n const ownedByOthers = collectOwnedNames(member, otherMembers);\n if (ownedByOthers.size === 0) return schema;\n\n const schemaObj = schema as { readonly tables?: unknown; readonly collections?: unknown };\n\n if (\n typeof schemaObj.tables === 'object' &&\n schemaObj.tables !== null &&\n !Array.isArray(schemaObj.tables)\n ) {\n return pruneRecord(schemaObj, 'tables', ownedByOthers);\n }\n\n if (Array.isArray(schemaObj.collections)) {\n return pruneCollectionsArray(schemaObj, ownedByOthers);\n }\n\n if (\n typeof schemaObj.collections === 'object' &&\n schemaObj.collections !== null &&\n !Array.isArray(schemaObj.collections)\n ) {\n return pruneRecord(schemaObj, 'collections', ownedByOthers);\n }\n\n return schema;\n}\n\nfunction collectOwnedNames(\n member: ContractSpaceMember,\n otherMembers: ReadonlyArray<ContractSpaceMember>,\n): Set<string> {\n const owned = new Set<string>();\n for (const other of otherMembers) {\n if (other.spaceId === member.spaceId) continue;\n for (const { entityName } of elementCoordinates(other.contract().storage)) {\n owned.add(entityName);\n }\n }\n return owned;\n}\n\nfunction pruneRecord(\n schemaObj: { readonly tables?: unknown; readonly collections?: unknown },\n field: 'tables' | 'collections',\n ownedByOthers: ReadonlySet<string>,\n): unknown {\n const source = schemaObj[field] as Record<string, unknown>;\n let removed = false;\n const pruned: Record<string, unknown> = {};\n for (const [name, value] of Object.entries(source)) {\n if (ownedByOthers.has(name)) {\n removed = true;\n } else {\n pruned[name] = value;\n }\n }\n if (!removed) return schemaObj;\n return { ...schemaObj, [field]: pruned };\n}\n\nfunction pruneCollectionsArray(\n schemaObj: { readonly collections?: unknown },\n ownedByOthers: ReadonlySet<string>,\n): unknown {\n const source = schemaObj.collections as ReadonlyArray<unknown>;\n let removed = false;\n const pruned: unknown[] = [];\n for (const entry of source) {\n if (typeof entry === 'object' && entry !== null) {\n const name = (entry as { readonly name?: unknown }).name;\n if (typeof name === 'string' && ownedByOthers.has(name)) {\n removed = true;\n continue;\n }\n }\n pruned.push(entry);\n }\n if (!removed) return schemaObj;\n return { ...schemaObj, collections: pruned };\n}\n","import 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 { PerSpacePlan } 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: PerSpacePlan }\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(),\n strategy: 'synth',\n },\n };\n}\n","import { notOk, ok } from '@prisma-next/utils/result';\nimport { requireHeadRef } from './aggregate';\nimport type { PerSpacePlan, PlannerError, PlannerInput, PlannerOutput } 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 AggregateMigrationEdgeRef,\n CallerPolicy,\n PerSpacePlan,\n PlannerError,\n PlannerInput,\n PlannerOutput,\n PlannerSuccess,\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.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 * `MigrationRunnerFailure.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 planMigration<TFamilyId extends string, TTargetId extends string>(\n input: PlannerInput<TFamilyId, TTargetId>,\n): Promise<PlannerOutput> {\n const { aggregate, currentDBState, callerPolicy } = input;\n const allMembers: ReadonlyArray<ContractSpaceMember> = [aggregate.app, ...aggregate.extensions];\n\n const perSpace = new Map<string, PerSpacePlan>();\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 const headRef = requireHeadRef(member);\n\n const ignoreGraph = callerPolicy.ignoreGraphFor.has(member.spaceId);\n const invariantsRequired = headRef.invariants.length > 0;\n\n if (ignoreGraph && invariantsRequired) {\n const conflict: PlannerError = {\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 (${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.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: 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: [...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 { elementCoordinates } from '@prisma-next/framework-components/ir';\nimport type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { requireHeadRef } from './aggregate';\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 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 VerifierInput<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 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 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 VerifierSuccess<TSchemaResult> {\n readonly markerCheck: MarkerCheckSection;\n readonly schemaCheck: SchemaCheckSection<TSchemaResult>;\n}\n\nexport type VerifierError = {\n readonly kind: 'introspectionFailure';\n readonly detail: string;\n};\n\nexport type VerifierOutput<TSchemaResult> = Result<VerifierSuccess<TSchemaResult>, VerifierError>;\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 verifyMigration<TSchemaResult>(\n input: VerifierInput<TSchemaResult>,\n): VerifierOutput<TSchemaResult> {\n try {\n return runVerifyMigration(input);\n } catch (error) {\n return notOk({\n kind: 'introspectionFailure',\n detail: error instanceof Error ? error.message : String(error),\n });\n }\n}\n\nfunction runVerifyMigration<TSchemaResult>(\n input: VerifierInput<TSchemaResult>,\n): VerifierOutput<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 const headRef = requireHeadRef(member);\n if (marker.storageHash !== headRef.hash) {\n markerPerSpace.set(member.spaceId, {\n kind: 'hashMismatch',\n markerHash: marker.storageHash,\n expected: headRef.hash,\n });\n continue;\n }\n const markerInvariants = new Set(marker.invariants);\n const missing = 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 contract = member.contract();\n for (const { entityName } of elementCoordinates(contract.storage)) {\n claimedTables.add(entityName);\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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"}
@@ -37,8 +37,8 @@ declare function errorDescriptorHeadHashMismatch(args: {
37
37
  /**
38
38
  * Wire-shape edge surfaced through the JSON envelope's
39
39
  * `meta.structuralPath` of `MIGRATION.NO_INVARIANT_PATH`. Slim by design —
40
- * authoring metadata (`createdAt`, `labels`) lives on `MigrationEdge` but
41
- * is intentionally dropped here so the envelope stays stable across
40
+ * authoring metadata (`createdAt`) lives on `MigrationEdge` but is
41
+ * intentionally dropped here so the envelope stays stable across
42
42
  * graph-internal refactors.
43
43
  *
44
44
  * Stability: any field added here is part of the public CLI JSON contract.
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.mts","names":[],"sources":["../../src/errors.ts"],"mappings":";;AAmCA;;;;;;;;;;;;;;;cAAa,mBAAA,SAA4B,KAAA;EAAA,SAC9B,IAAA;EAAA,SACA,QAAA;EAAA,SACA,GAAA;EAAA,SACA,GAAA;EAAA,SACA,OAAA,EAAS,MAAA;cAGhB,IAAA,UACA,OAAA,UACA,OAAA;IAAA,SACW,GAAA;IAAA,SACA,GAAA;IAAA,SACA,OAAA,GAAU,MAAA;EAAA;EAAA,OAWhB,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,mBAAA;AAAA;AAAA,iBA0BtB,gBAAA,CAAiB,QAAA,UAAkB,UAAA,WAAqB,mBAAA;AAAA,iBAwDxD,+BAAA,CAAgC,IAAA;EAAA,SACrC,WAAA;EAAA,SACA,cAAA;EAAA,SACA,WAAA;AAAA,IACP,mBAAA;;;;;;;;;;;AA0NJ;UAlCiB,6BAAA;EAAA,SACN,OAAA;EAAA,SACA,aAAA;EAAA,SACA,IAAA;EAAA,SACA,EAAA;EAAA,SACA,UAAA;AAAA;AAAA,iBAGK,oBAAA,CAAqB,IAAA;EAAA,SAC1B,OAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA;EAAA,SACA,cAAA,WAAyB,6BAAA;AAAA,IAChC,mBAAA;AAAA,iBAqBY,qBAAA,CAAsB,IAAA;EAAA,SAC3B,OAAA;EAAA,SACA,OAAA;EAAA,SACA,QAAA;AAAA,IACP,mBAAA"}
1
+ {"version":3,"file":"errors.d.mts","names":[],"sources":["../../src/errors.ts"],"mappings":";AAoCA;;;;;;;;;;;;;;;;AAAA,cAAa,mBAAA,SAA4B,KAAA;EAAA,SAC9B,IAAA;EAAA,SACA,QAAA;EAAA,SACA,GAAA;EAAA,SACA,GAAA;EAAA,SACA,OAAA,EAAS,MAAA;cAGhB,IAAA,UACA,OAAA,UACA,OAAA;IAAA,SACW,GAAA;IAAA,SACA,GAAA;IAAA,SACA,OAAA,GAAU,MAAA;EAAA;EAAA,OAWhB,EAAA,CAAG,KAAA,YAAiB,KAAA,IAAS,mBAAA;AAAA;AAAA,iBA0BtB,gBAAA,CAAiB,QAAA,UAAkB,UAAA,WAAqB,mBAAmB;AAAA,iBAwD3E,+BAAA,CAAgC,IAAA;EAAA,SACrC,WAAA;EAAA,SACA,cAAA;EAAA,SACA,WAAA;AAAA,IACP,mBAAmB;;;;;;;;;AAqMA;AAqBvB;;UAlCiB,6BAAA;EAAA,SACN,OAAA;EAAA,SACA,aAAA;EAAA,SACA,IAAA;EAAA,SACA,EAAA;EAAA,SACA,UAAA;AAAA;AAAA,iBAGK,oBAAA,CAAqB,IAAA;EAAA,SAC1B,OAAA;EAAA,SACA,QAAA;EAAA,SACA,OAAA;EAAA,SACA,cAAA,WAAyB,6BAAA;AAAA,IAChC,mBAAmB;AAAA,iBAqBP,qBAAA,CAAsB,IAAA;EAAA,SAC3B,OAAA;EAAA,SACA,OAAA;EAAA,SACA,QAAA;AAAA,IACP,mBAAmB"}
@@ -1,2 +1,2 @@
1
- import { T as errorUnknownInvariant, r as errorDescriptorHeadHashMismatch, t as MigrationToolsError, u as errorInvalidJson, x as errorNoInvariantPath } from "../errors-DGYwcwXs.mjs";
1
+ import { C as errorNoInvariantPath, D as errorUnknownInvariant, a as errorDescriptorHeadHashMismatch, f as errorInvalidJson, t as MigrationToolsError } from "../errors-vFROOhCR.mjs";
2
2
  export { MigrationToolsError, errorDescriptorHeadHashMismatch, errorInvalidJson, errorNoInvariantPath, errorUnknownInvariant };
@@ -1,2 +1,2 @@
1
- import { n as MigrationGraph, t as MigrationEdge } from "../graph-BrLXqoUc.mjs";
1
+ import { n as MigrationGraph, t as MigrationEdge } from "../graph-3dLMZp5l.mjs";
2
2
  export { type MigrationEdge, type MigrationGraph };
@@ -1,5 +1,5 @@
1
- import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-DZj8YvD0.mjs";
2
- import { n as MigrationMetadata } from "../metadata-BFX0xdz8.mjs";
1
+ import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-Ca-J_z_0.mjs";
2
+ import { t as MigrationMetadata } from "../metadata-Bp9X04gM.mjs";
3
3
 
4
4
  //#region src/hash.d.ts
5
5
  interface VerifyResult {
@@ -9,13 +9,12 @@ interface VerifyResult {
9
9
  readonly computedHash: string;
10
10
  }
11
11
  /**
12
- * Content-addressed migration hash over (metadata envelope sans hints,
13
- * ops). See ADR 199 — Storage-only migration identity for the
14
- * rationale: the storage-hash bookends (`from`, `to`) inside the
15
- * envelope anchor the contract identity by hash, and planner hints are
16
- * advisory and must not affect identity. The full contract IRs are not
17
- * part of the manifest they live in sibling `*-contract.json` files
18
- * authored alongside the migration, never inlined here.
12
+ * Content-addressed migration hash over (metadata envelope, ops). See
13
+ * ADR 199 — Storage-only migration identity for the rationale: the
14
+ * storage-hash bookends (`from`, `to`) inside the envelope anchor the
15
+ * contract identity by hash. The full contract IRs are not part of the
16
+ * manifest they live in sibling `*-contract.json` files authored
17
+ * alongside the migration, never inlined here.
19
18
  *
20
19
  * The integrity check is purely structural, not semantic. The function
21
20
  * canonicalizes its inputs via `sortKeys` (recursive) + `JSON.stringify`
@@ -1 +1 @@
1
- {"version":3,"file":"hash.d.mts","names":[],"sources":["../../src/hash.ts"],"mappings":";;;;UAKiB,YAAA;EAAA,SACN,EAAA;EAAA,SACA,MAAA;EAAA,SACA,UAAA;EAAA,SACA,YAAA;AAAA;;;;;;;AAmCX;;;;;;;;;;;;;;;;AAwBA;;;;;;iBAxBgB,oBAAA,CACd,QAAA,EAAU,IAAA,CAAK,iBAAA;EAAA,SAAiD,aAAA;AAAA,GAChE,GAAA,EAAK,YAAA;;;;;;;;;;iBAsBS,mBAAA,CAAoB,GAAA,EAAK,sBAAA,GAAyB,YAAA"}
1
+ {"version":3,"file":"hash.d.mts","names":[],"sources":["../../src/hash.ts"],"mappings":";;;;UAKiB,YAAA;EAAA,SACN,EAAA;EAAA,SACA,MAAA;EAAA,SACA,UAAA;EAAA,SACA,YAAA;AAAA;;;;;;AAAY;AAkCvB;;;;;;;;;;;;;;;AAEmB;AAsBnB;;;;;iBAxBgB,oBAAA,CACd,QAAA,EAAU,IAAA,CAAK,iBAAA;EAAA,SAAiD,aAAA;AAAA,GAChE,GAAA,EAAK,YAAA;AAsBuE;;;;;;;;;AAAA,iBAA9D,mBAAA,CAAoB,GAAA,EAAK,sBAAA,GAAyB,YAAY"}
@@ -1,2 +1,2 @@
1
- import { n as verifyMigrationHash, t as computeMigrationHash } from "../hash-Cr4WIr4Z.mjs";
1
+ import { n as verifyMigrationHash, t as computeMigrationHash } from "../hash--Y7vCpN3.mjs";
2
2
  export { computeMigrationHash, verifyMigrationHash };
@@ -1,4 +1,4 @@
1
- import { t as MigrationOps } from "../package-DZj8YvD0.mjs";
1
+ import { t as MigrationOps } from "../package-Ca-J_z_0.mjs";
2
2
 
3
3
  //#region src/invariants.d.ts
4
4
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"invariants.d.mts","names":[],"sources":["../../src/invariants.ts"],"mappings":";;;;;AAUA;;;;iBAAgB,mBAAA,CAAoB,WAAA;AA8BpC;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iBAAgB,wBAAA,CAAyB,GAAA,EAAK,YAAA"}
1
+ {"version":3,"file":"invariants.d.mts","names":[],"sources":["../../src/invariants.ts"],"mappings":";;;;;AAUA;;;;iBAAgB,mBAAA,CAAoB,WAAmB;AA8BvD;;;;AAA0D;;;;;;;;;;;;;;;;;;;;;AAA1D,iBAAgB,wBAAA,CAAyB,GAAiB,EAAZ,YAAY"}
@@ -1,2 +1,2 @@
1
- import { n as validateInvariantId, t as deriveProvidedInvariants } from "../invariants-0daYEzyo.mjs";
1
+ import { n as validateInvariantId, t as deriveProvidedInvariants } from "../invariants-C23nXy1c.mjs";
2
2
  export { deriveProvidedInvariants, validateInvariantId };
@@ -1,83 +1,2 @@
1
- import { n as OnDiskMigrationPackage, t as MigrationOps } from "../package-DZj8YvD0.mjs";
2
- import { MigrationMetadata, MigrationPackage } from "@prisma-next/framework-components/control";
3
-
4
- //#region src/io.d.ts
5
- declare function writeMigrationPackage(dir: string, metadata: MigrationMetadata, ops: MigrationOps): Promise<void>;
6
- /**
7
- * Materialise an in-memory {@link MigrationPackage} to a per-space
8
- * directory on disk.
9
- *
10
- * Writes two files under `<targetDir>/<pkg.dirName>/`:
11
- *
12
- * - `migration.json` — the manifest (pretty-printed, matches
13
- * {@link writeMigrationPackage}'s output for byte-for-byte parity with
14
- * app-space migrations).
15
- * - `ops.json` — the operation list (pretty-printed).
16
- *
17
- * Distinct verb from the lower-level {@link writeMigrationPackage}
18
- * (which takes constituent `(metadata, ops)`): callers reading
19
- * `materialise…` know they are persisting a struct-typed package.
20
- *
21
- * Overwrite-idempotent: the per-package directory is cleared before
22
- * each emit, so re-running against the same `targetDir` produces
23
- * byte-identical contents and never leaves stale files behind. The
24
- * lower-level {@link writeMigrationPackage} stays strict because the
25
- * CLI authoring path (`migration plan` / `migration new`) deliberately
26
- * refuses to clobber an existing authored migration; this helper is
27
- * the re-emit path that is supposed to converge on a single canonical
28
- * on-disk shape.
29
- *
30
- * The per-space head contract lives at
31
- * `<projectMigrationsDir>/<spaceId>/contract.json` (written by
32
- * {@link import('./emit-contract-space-artefacts').emitContractSpaceArtefacts}),
33
- * not inside the per-package directory. The runner reads only
34
- * `migration.json` + `ops.json` from each package.
35
- */
36
- declare function materialiseMigrationPackage(targetDir: string, pkg: MigrationPackage): Promise<void>;
37
- /**
38
- * Idempotent variant of {@link materialiseMigrationPackage}: writes the
39
- * package only if `<targetDir>/<pkg.dirName>/` does not already exist on
40
- * disk as a directory; returns `{ written: false }` when the package
41
- * directory is present (no rewrite, no comparison — by-existence skip).
42
- *
43
- * Concretely:
44
- * - existing directory → skip silently, return `{ written: false }`.
45
- * - missing path → write three files via {@link materialiseMigrationPackage},
46
- * return `{ written: true }`.
47
- * - path exists but is not a directory (file/symlink) → treated as
48
- * missing; {@link materialiseMigrationPackage} will attempt creation
49
- * and fail with an appropriate OS error.
50
- * - any other I/O error from `stat` → propagated unchanged.
51
- *
52
- * Used by the CLI's `runContractSpaceExtensionMigrationsPass` to
53
- * materialise extension migration packages into a project's
54
- * `migrations/<spaceId>/` directory, and by extension-package tests
55
- * that mirror the same idempotent-rematerialise property locally
56
- * without taking a CLI dependency.
57
- */
58
- declare function materialiseExtensionMigrationPackageIfMissing(targetDir: string, pkg: MigrationPackage): Promise<{
59
- readonly written: boolean;
60
- }>;
61
- /**
62
- * Copy a list of files into `destDir`, optionally renaming each one.
63
- *
64
- * The destination directory is created (with `recursive: true`) if it
65
- * does not already exist. Each source path is copied byte-for-byte into
66
- * `destDir/<destName>`; missing sources throw `ENOENT`. The helper is
67
- * intentionally generic: callers own the list of files (e.g. a contract
68
- * emitter's emitted output) and the naming convention (e.g. renaming
69
- * the destination contract to `end-contract.*` and the source contract
70
- * to `start-contract.*`).
71
- */
72
- declare function copyFilesWithRename(destDir: string, files: readonly {
73
- readonly sourcePath: string;
74
- readonly destName: string;
75
- }[]): Promise<void>;
76
- declare function writeMigrationMetadata(dir: string, metadata: MigrationMetadata): Promise<void>;
77
- declare function writeMigrationOps(dir: string, ops: MigrationOps): Promise<void>;
78
- declare function readMigrationPackage(dir: string): Promise<OnDiskMigrationPackage>;
79
- declare function readMigrationsDir(migrationsRoot: string): Promise<readonly OnDiskMigrationPackage[]>;
80
- declare function formatMigrationDirName(timestamp: Date, slug: string): string;
81
- //#endregion
82
- export { copyFilesWithRename, formatMigrationDirName, materialiseExtensionMigrationPackageIfMissing, materialiseMigrationPackage, readMigrationPackage, readMigrationsDir, writeMigrationMetadata, writeMigrationOps, writeMigrationPackage };
83
- //# sourceMappingURL=io.d.mts.map
1
+ import { a as materialiseExtensionMigrationPackageIfMissing, c as readMigrationsDir, d as writeMigrationPackage, i as formatMigrationDirName, l as writeMigrationMetadata, n as ReadMigrationsDirResult, o as materialiseMigrationPackage, r as copyFilesWithRename, s as readMigrationPackage, t as PackageLoadProblem, u as writeMigrationOps } from "../io-BH4G3F-i.mjs";
2
+ export { type PackageLoadProblem, type ReadMigrationsDirResult, copyFilesWithRename, formatMigrationDirName, materialiseExtensionMigrationPackageIfMissing, materialiseMigrationPackage, readMigrationPackage, readMigrationsDir, writeMigrationMetadata, writeMigrationOps, writeMigrationPackage };
@@ -1,2 +1,2 @@
1
- import { a as materialiseMigrationPackage, c as writeMigrationMetadata, i as materialiseExtensionMigrationPackageIfMissing, l as writeMigrationOps, n as copyFilesWithRename, o as readMigrationPackage, r as formatMigrationDirName, s as readMigrationsDir, u as writeMigrationPackage } from "../io-BPLfzvZe.mjs";
1
+ import { a as materialiseMigrationPackage, c as writeMigrationMetadata, i as materialiseExtensionMigrationPackageIfMissing, l as writeMigrationOps, n as copyFilesWithRename, o as readMigrationPackage, r as formatMigrationDirName, s as readMigrationsDir, u as writeMigrationPackage } from "../io-BGlPOt9b.mjs";
2
2
  export { copyFilesWithRename, formatMigrationDirName, materialiseExtensionMigrationPackageIfMissing, materialiseMigrationPackage, readMigrationPackage, readMigrationsDir, writeMigrationMetadata, writeMigrationOps, writeMigrationPackage };
@@ -1,2 +1,2 @@
1
- import { n as MigrationMetadata, t as MigrationHints } from "../metadata-BFX0xdz8.mjs";
2
- export { type MigrationHints, type MigrationMetadata };
1
+ import { t as MigrationMetadata } from "../metadata-Bp9X04gM.mjs";
2
+ export { type MigrationMetadata };
@@ -1,2 +1,9 @@
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-De0dUZoC.mjs";
2
- export { type PathDecision, detectCycles, detectOrphans, findLatestMigration, findLeaf, findPath, findPathWithDecision, findPathWithInvariants, findReachableLeaves, reconstructGraph };
1
+ import { n as MigrationGraph } from "../graph-3dLMZp5l.mjs";
2
+ 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-CWEM2SLR.mjs";
3
+
4
+ //#region src/graph-membership.d.ts
5
+ declare function isGraphNode(hash: string, graph: MigrationGraph): boolean;
6
+ declare function assertHashIsGraphNode(hash: string, graph: MigrationGraph): asserts hash is string;
7
+ //#endregion
8
+ export { type PathDecision, assertHashIsGraphNode, detectCycles, detectOrphans, findLatestMigration, findLeaf, findPath, findPathWithDecision, findPathWithInvariants, findReachableLeaves, isGraphNode, reconstructGraph };
9
+ //# sourceMappingURL=migration-graph.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-graph.d.mts","names":[],"sources":["../../src/graph-membership.ts"],"mappings":";;;;iBAIgB,WAAA,CAAY,IAAA,UAAc,KAAA,EAAO,cAAc;AAAA,iBAO/C,qBAAA,CAAsB,IAAA,UAAc,KAAA,EAAO,cAAc,WAAW,IAAA"}
@@ -1,2 +1,3 @@
1
- import { a as findPath, c as findReachableLeaves, i as findLeaf, l as reconstructGraph, n as detectOrphans, o as findPathWithDecision, r as findLatestMigration, s as findPathWithInvariants, t as detectCycles } from "../migration-graph-nlS4TRpn.mjs";
2
- export { detectCycles, detectOrphans, findLatestMigration, findLeaf, findPath, findPathWithDecision, findPathWithInvariants, findReachableLeaves, reconstructGraph };
1
+ import { n as isGraphNode, t as assertHashIsGraphNode } from "../graph-membership-BV23F1IV.mjs";
2
+ import { a as findPath, c as findReachableLeaves, i as findLeaf, l as reconstructGraph, n as detectOrphans, o as findPathWithDecision, r as findLatestMigration, s as findPathWithInvariants, t as detectCycles } from "../migration-graph-BMAqSfv9.mjs";
3
+ export { assertHashIsGraphNode, detectCycles, detectOrphans, findLatestMigration, findLeaf, findPath, findPathWithDecision, findPathWithInvariants, findReachableLeaves, isGraphNode, reconstructGraph };
@@ -1 +1 @@
1
- {"version":3,"file":"migration-ts.d.mts","names":[],"sources":["../../src/migration-ts.ts","../../src/runtime-detection.ts"],"mappings":";;AA8BA;;;;;;;;;AAsBA;;;;;;;;ACpDA;;;;;iBD8BsB,gBAAA,CAAiB,UAAA,UAAoB,OAAA,WAAkB,OAAA;;;;iBAsBvD,cAAA,CAAe,UAAA,WAAqB,OAAA;;;KCpD9C,eAAA;AAAA,iBAEI,qBAAA,CAAA,GAAyB,eAAA;AAAA,iBAMzB,cAAA,CAAe,OAAA,EAAS,eAAA"}
1
+ {"version":3,"file":"migration-ts.d.mts","names":[],"sources":["../../src/migration-ts.ts","../../src/runtime-detection.ts"],"mappings":";;AA8BA;;;;;;;;AAAoF;AAsBpF;;;;AAAiE;;;;ACpDjE;;;;AAA2B;iBD8BL,gBAAA,CAAiB,UAAA,UAAoB,OAAA,WAAkB,OAAO;;;;iBAsB9D,cAAA,CAAe,UAAA,WAAqB,OAAO;;;KCpDrD,eAAA;AAAA,iBAEI,qBAAA,CAAA,GAAyB,eAAe;AAAA,iBAMxC,cAAA,CAAe,OAAwB,EAAf,eAAe"}
@@ -1 +1 @@
1
- {"version":3,"file":"migration-ts.mjs","names":[],"sources":["../../src/migration-ts.ts","../../src/runtime-detection.ts"],"sourcesContent":["/**\n * Utilities for reading/writing `migration.ts` files.\n *\n * Rendering migration.ts source is the target's responsibility — the CLI\n * obtains source strings from a planner's `plan.renderTypeScript()`. The\n * helper here is limited to file I/O: writing the returned source with the\n * right executable bit and probing for existence.\n */\n\nimport { stat, writeFile } from 'node:fs/promises';\nimport { join } from 'pathe';\nimport { format } from 'prettier';\n\nconst MIGRATION_TS_FILE = 'migration.ts';\n\n/**\n * Writes a pre-rendered `migration.ts` source string to the given package\n * directory. If the source begins with a shebang, the file is written with\n * executable permissions (0o755) so it can be run directly via\n * `./migration.ts` — the rendered scaffold ends with\n * `MigrationCLI.run(import.meta.url, M)` from\n * `@prisma-next/cli/migration-cli` (re-exported by the postgres facade),\n * which guards on the entrypoint and serializes when the file is the main\n * module.\n *\n * The source is run through prettier before writing so migration renderers\n * can produce structurally-correct but loosely-indented source and rely on\n * a single canonical format on disk. Matches what `@prisma-next/emitter`\n * already does for generated `contract.d.ts`.\n */\nexport async function writeMigrationTs(packageDir: string, content: string): Promise<void> {\n const formatted = await formatMigrationTsSource(content);\n const isExecutable = formatted.startsWith('#!');\n await writeFile(\n join(packageDir, MIGRATION_TS_FILE),\n formatted,\n isExecutable ? { mode: 0o755 } : undefined,\n );\n}\n\nasync function formatMigrationTsSource(source: string): Promise<string> {\n return format(source, {\n parser: 'typescript',\n singleQuote: true,\n semi: true,\n printWidth: 100,\n });\n}\n\n/**\n * Checks whether a migration.ts file exists in the package directory.\n */\nexport async function hasMigrationTs(packageDir: string): Promise<boolean> {\n try {\n const s = await stat(join(packageDir, MIGRATION_TS_FILE));\n return s.isFile();\n } catch {\n return false;\n }\n}\n","export type ScaffoldRuntime = 'node' | 'bun' | 'deno';\n\nexport function detectScaffoldRuntime(): ScaffoldRuntime {\n if (typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined') return 'bun';\n if (typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined') return 'deno';\n return 'node';\n}\n\nexport function shebangLineFor(runtime: ScaffoldRuntime): string {\n switch (runtime) {\n case 'bun':\n return '#!/usr/bin/env -S bun';\n case 'deno':\n return '#!/usr/bin/env -S deno run -A';\n case 'node':\n return '#!/usr/bin/env -S node';\n }\n}\n"],"mappings":";;;;;;;;;;;;AAaA,MAAM,oBAAoB;;;;;;;;;;;;;;;;AAiB1B,eAAsB,iBAAiB,YAAoB,SAAgC;CACzF,MAAM,YAAY,MAAM,wBAAwB,QAAQ;CACxD,MAAM,eAAe,UAAU,WAAW,KAAK;CAC/C,MAAM,UACJ,KAAK,YAAY,kBAAkB,EACnC,WACA,eAAe,EAAE,MAAM,KAAO,GAAG,KAAA,EAClC;;AAGH,eAAe,wBAAwB,QAAiC;CACtE,OAAO,OAAO,QAAQ;EACpB,QAAQ;EACR,aAAa;EACb,MAAM;EACN,YAAY;EACb,CAAC;;;;;AAMJ,eAAsB,eAAe,YAAsC;CACzE,IAAI;EAEF,QAAO,MADS,KAAK,KAAK,YAAY,kBAAkB,CAAC,EAChD,QAAQ;SACX;EACN,OAAO;;;;;ACvDX,SAAgB,wBAAyC;CACvD,IAAI,OAAQ,WAAiC,QAAQ,aAAa,OAAO;CACzE,IAAI,OAAQ,WAAkC,SAAS,aAAa,OAAO;CAC3E,OAAO;;AAGT,SAAgB,eAAe,SAAkC;CAC/D,QAAQ,SAAR;EACE,KAAK,OACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,QACH,OAAO"}
1
+ {"version":3,"file":"migration-ts.mjs","names":[],"sources":["../../src/migration-ts.ts","../../src/runtime-detection.ts"],"sourcesContent":["/**\n * Utilities for reading/writing `migration.ts` files.\n *\n * Rendering migration.ts source is the target's responsibility — the CLI\n * obtains source strings from a planner's `plan.renderTypeScript()`. The\n * helper here is limited to file I/O: writing the returned source with the\n * right executable bit and probing for existence.\n */\n\nimport { stat, writeFile } from 'node:fs/promises';\nimport { join } from 'pathe';\nimport { format } from 'prettier';\n\nconst MIGRATION_TS_FILE = 'migration.ts';\n\n/**\n * Writes a pre-rendered `migration.ts` source string to the given package\n * directory. If the source begins with a shebang, the file is written with\n * executable permissions (0o755) so it can be run directly via\n * `./migration.ts` — the rendered scaffold ends with\n * `MigrationCLI.run(import.meta.url, M)` from\n * `@prisma-next/cli/migration-cli` (re-exported by the postgres facade),\n * which guards on the entrypoint and serializes when the file is the main\n * module.\n *\n * The source is run through prettier before writing so migration renderers\n * can produce structurally-correct but loosely-indented source and rely on\n * a single canonical format on disk. Matches what `@prisma-next/emitter`\n * already does for generated `contract.d.ts`.\n */\nexport async function writeMigrationTs(packageDir: string, content: string): Promise<void> {\n const formatted = await formatMigrationTsSource(content);\n const isExecutable = formatted.startsWith('#!');\n await writeFile(\n join(packageDir, MIGRATION_TS_FILE),\n formatted,\n isExecutable ? { mode: 0o755 } : undefined,\n );\n}\n\nasync function formatMigrationTsSource(source: string): Promise<string> {\n return format(source, {\n parser: 'typescript',\n singleQuote: true,\n semi: true,\n printWidth: 100,\n });\n}\n\n/**\n * Checks whether a migration.ts file exists in the package directory.\n */\nexport async function hasMigrationTs(packageDir: string): Promise<boolean> {\n try {\n const s = await stat(join(packageDir, MIGRATION_TS_FILE));\n return s.isFile();\n } catch {\n return false;\n }\n}\n","export type ScaffoldRuntime = 'node' | 'bun' | 'deno';\n\nexport function detectScaffoldRuntime(): ScaffoldRuntime {\n if (typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined') return 'bun';\n if (typeof (globalThis as { Deno?: unknown }).Deno !== 'undefined') return 'deno';\n return 'node';\n}\n\nexport function shebangLineFor(runtime: ScaffoldRuntime): string {\n switch (runtime) {\n case 'bun':\n return '#!/usr/bin/env -S bun';\n case 'deno':\n return '#!/usr/bin/env -S deno run -A';\n case 'node':\n return '#!/usr/bin/env -S node';\n }\n}\n"],"mappings":";;;;;;;;;;;;AAaA,MAAM,oBAAoB;;;;;;;;;;;;;;;;AAiB1B,eAAsB,iBAAiB,YAAoB,SAAgC;CACzF,MAAM,YAAY,MAAM,wBAAwB,OAAO;CACvD,MAAM,eAAe,UAAU,WAAW,IAAI;CAC9C,MAAM,UACJ,KAAK,YAAY,iBAAiB,GAClC,WACA,eAAe,EAAE,MAAM,IAAM,IAAI,KAAA,CACnC;AACF;AAEA,eAAe,wBAAwB,QAAiC;CACtE,OAAO,OAAO,QAAQ;EACpB,QAAQ;EACR,aAAa;EACb,MAAM;EACN,YAAY;CACd,CAAC;AACH;;;;AAKA,eAAsB,eAAe,YAAsC;CACzE,IAAI;EAEF,QAAO,MADS,KAAK,KAAK,YAAY,iBAAiB,CAAC,GAC/C,OAAO;CAClB,QAAQ;EACN,OAAO;CACT;AACF;;;ACzDA,SAAgB,wBAAyC;CACvD,IAAI,OAAQ,WAAiC,QAAQ,aAAa,OAAO;CACzE,IAAI,OAAQ,WAAkC,SAAS,aAAa,OAAO;CAC3E,OAAO;AACT;AAEA,SAAgB,eAAe,SAAkC;CAC/D,QAAQ,SAAR;EACE,KAAK,OACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,QACH,OAAO;CACX;AACF"}
@@ -1,11 +1,10 @@
1
- import { n as MigrationMetadata$1 } from "../metadata-BFX0xdz8.mjs";
1
+ import { t as MigrationMetadata$1 } from "../metadata-Bp9X04gM.mjs";
2
2
  import { ControlStack, MigrationPlan, MigrationPlanOperation } from "@prisma-next/framework-components/control";
3
3
 
4
4
  //#region src/migration-base.d.ts
5
5
  interface MigrationMeta {
6
6
  readonly from: string | null;
7
7
  readonly to: string;
8
- readonly labels?: readonly string[];
9
8
  }
10
9
  /**
11
10
  * Base class for migrations.
@@ -77,10 +76,10 @@ interface MigrationArtifacts {
77
76
  * Pure conversion from a `Migration` instance (plus the previously
78
77
  * scaffolded metadata, when one exists on disk) to the in-memory
79
78
  * artifacts that downstream tooling persists. Owns metadata validation,
80
- * metadata synthesis/preservation, hint normalization, and the
81
- * content-addressed `migrationHash` computation, but performs no file I/O
82
- * — callers handle reads (to source `existing`) and writes (to persist
83
- * `opsJson` / `metadataJson`).
79
+ * metadata synthesis/preservation, and the content-addressed
80
+ * `migrationHash` computation, but performs no file I/O — callers handle
81
+ * reads (to source `existing`) and writes (to persist `opsJson` /
82
+ * `metadataJson`).
84
83
  */
85
84
  declare function buildMigrationArtifacts(instance: Migration, existing: Partial<MigrationMetadata$1> | null): MigrationArtifacts;
86
85
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"migration.d.mts","names":[],"sources":["../../src/migration-base.ts"],"mappings":";;;;UAeiB,aAAA;EAAA,SACN,IAAA;EAAA,SACA,EAAA;EAAA,SACA,MAAA;AAAA;;;;;;;AAuBX;;;uBAAsB,SAAA,oBACD,sBAAA,GAAyB,sBAAA,mFAGjC,aAAA;EAAA,kBAEO,QAAA;EAWqB;;;;;;;;;EAAA,mBAApB,KAAA,EAAO,YAAA,CAAa,SAAA,EAAW,SAAA;cAEtC,KAAA,GAAQ,YAAA,CAAa,SAAA,EAAW,SAAA;EAlB5C;;;;;;EAAA,aA4Ba,UAAA,CAAA,YAAuB,UAAA;EAZjB;;;;;EAAA,SAmBV,QAAA,CAAA,GAAY,aAAA;EAAA,IAEjB,MAAA,CAAA;IAAA,SAAqB,WAAA;EAAA;EAAA,IAKrB,WAAA,CAAA;IAAA,SAA0B,WAAA;EAAA;AAAA;;;;;;;AAYhC;iBAAgB,kBAAA,CAAmB,aAAA;;;;AAsBnC;;;;;;;;UAAiB,kBAAA;EAAA,SACN,OAAA;EAAA,SACA,QAAA,EAAU,mBAAA;EAAA,SACV,YAAA;AAAA;;;;;;;;;;iBA8DK,uBAAA,CACd,QAAA,EAAU,SAAA,EACV,QAAA,EAAU,OAAA,CAAQ,mBAAA,WACjB,kBAAA"}
1
+ {"version":3,"file":"migration.d.mts","names":[],"sources":["../../src/migration-base.ts"],"mappings":";;;;UAeiB,aAAA;EAAA,SACN,IAAA;EAAA,SACA,EAAE;AAAA;;;AAAA;AAsBb;;;;;;uBAAsB,SAAA,oBACD,sBAAA,GAAyB,sBAAA,mFAGjC,aAAA;EAAA,kBAEO,QAAA;EAae;;;;;;;;;EAAA,mBAFd,KAAA,EAAO,YAAA,CAAa,SAAA,EAAW,SAAA;cAEtC,KAAA,GAAQ,YAAA,CAAa,SAAA,EAAW,SAAA;EAjB5C;;;;;;EAAA,aA2Ba,UAAA,CAAA,YAAuB,UAAA;EAZc;;;;;EAAA,SAmBzC,QAAA,CAAA,GAAY,aAAA;EAAA,IAEjB,MAAA,CAAA;IAAA,SAAqB,WAAA;EAAA;EAAA,IAKrB,WAAA,CAAA;IAAA,SAA0B,WAAA;EAAA;AAAA;;;AAAW;AAY3C;;;;iBAAgB,kBAAA,CAAmB,aAAqB;AAsBxD;;;;;;;;;AAGuB;AA4CvB;AA/CA,UAAiB,kBAAA;EAAA,SACN,OAAA;EAAA,SACA,QAAA,EAAU,mBAAiB;EAAA,SAC3B,YAAA;AAAA;;;;;;;;;;iBA4CK,uBAAA,CACd,QAAA,EAAU,SAAA,EACV,QAAA,EAAU,OAAA,CAAQ,mBAAA,WACjB,kBAAA"}