@prisma-next/migration-tools 0.11.0-dev.23 → 0.11.0-dev.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/exports/aggregate.d.mts +4 -4
  2. package/dist/exports/aggregate.mjs +2 -1
  3. package/dist/exports/aggregate.mjs.map +1 -1
  4. package/dist/exports/enumerate-migration-spaces.d.mts +53 -0
  5. package/dist/exports/enumerate-migration-spaces.d.mts.map +1 -0
  6. package/dist/exports/enumerate-migration-spaces.mjs +107 -0
  7. package/dist/exports/enumerate-migration-spaces.mjs.map +1 -0
  8. package/dist/exports/graph.d.mts +1 -1
  9. package/dist/exports/hash.d.mts +2 -2
  10. package/dist/exports/invariants.d.mts +1 -1
  11. package/dist/exports/io.d.mts +1 -1
  12. package/dist/exports/metadata.d.mts +1 -1
  13. package/dist/exports/migration-graph.d.mts +2 -2
  14. package/dist/exports/migration-list-types.d.mts +2 -0
  15. package/dist/exports/migration-list-types.mjs +1 -0
  16. package/dist/exports/migration.d.mts +1 -1
  17. package/dist/exports/package.d.mts +1 -1
  18. package/dist/exports/ref-resolution.d.mts +2 -2
  19. package/dist/exports/refs.d.mts +1 -1
  20. package/dist/exports/spaces.d.mts +27 -2
  21. package/dist/exports/spaces.d.mts.map +1 -1
  22. package/dist/exports/spaces.mjs +6 -4
  23. package/dist/exports/spaces.mjs.map +1 -1
  24. package/dist/{graph-BrLXqoUc.d.mts → graph-C7AJPGV5.d.mts} +1 -1
  25. package/dist/{graph-BrLXqoUc.d.mts.map → graph-C7AJPGV5.d.mts.map} +1 -1
  26. package/dist/{migration-graph-De0dUZoC.d.mts → migration-graph-ABYqVsmv.d.mts} +3 -3
  27. package/dist/{migration-graph-De0dUZoC.d.mts.map → migration-graph-ABYqVsmv.d.mts.map} +1 -1
  28. package/dist/migration-list-types-B-qimPet.d.mts +23 -0
  29. package/dist/migration-list-types-B-qimPet.d.mts.map +1 -0
  30. package/dist/{package-DZj8YvD0.d.mts → package-DIttKL7X.d.mts} +1 -1
  31. package/dist/{package-DZj8YvD0.d.mts.map → package-DIttKL7X.d.mts.map} +1 -1
  32. package/dist/read-contract-space-contract-CwwlqKl_.mjs +82 -0
  33. package/dist/read-contract-space-contract-CwwlqKl_.mjs.map +1 -0
  34. package/dist/{refs-CDaNerhT.d.mts → refs-BaygQaFD.d.mts} +1 -1
  35. package/dist/{refs-CDaNerhT.d.mts.map → refs-BaygQaFD.d.mts.map} +1 -1
  36. package/dist/{read-contract-space-contract-Cglige7P.mjs → verify-contract-spaces-DxXWxGR0.mjs} +31 -79
  37. package/dist/verify-contract-spaces-DxXWxGR0.mjs.map +1 -0
  38. package/package.json +14 -6
  39. package/src/emit-contract-space-artefacts.ts +4 -3
  40. package/src/enumerate-migration-spaces.ts +127 -0
  41. package/src/exports/enumerate-migration-spaces.ts +4 -0
  42. package/src/exports/migration-list-types.ts +5 -0
  43. package/src/exports/spaces.ts +3 -0
  44. package/src/migration-list-types.ts +21 -0
  45. package/src/read-contract-space-head-ref.ts +5 -2
  46. package/src/space-layout.ts +30 -0
  47. package/dist/read-contract-space-contract-Cglige7P.mjs.map +0 -1
  48. /package/dist/{metadata-BFX0xdz8.d.mts → metadata-B4Wy6zor.d.mts} +0 -0
@@ -1,4 +1,4 @@
1
- import { t as MigrationOps } from "../package-DZj8YvD0.mjs";
1
+ import { t as MigrationOps } from "../package-DIttKL7X.mjs";
2
2
  import { APP_SPACE_ID, ContractSpace, ContractSpaceHeadRef, ContractSpaceHeadRef as ContractSpaceHeadRef$1 } from "@prisma-next/framework-components/control";
3
3
  import { Contract } from "@prisma-next/contract/types";
4
4
 
@@ -521,6 +521,31 @@ declare function assertValidSpaceId(spaceId: string): asserts spaceId is ValidSp
521
521
  * relative shape and is symmetric with `pathe.join`.
522
522
  */
523
523
  declare function spaceMigrationDirectory(projectMigrationsDir: string, spaceId: string): string;
524
+ /**
525
+ * Per-space subdirectory name reserved for the ref store
526
+ * (`migrations/<space>/<SPACE_REFS_DIRNAME>/*.json`). Single source of
527
+ * truth: every helper that composes a per-space refs path imports this
528
+ * constant, and the enumerator uses it (via
529
+ * {@link RESERVED_SPACE_SUBDIR_NAMES}) to exclude reserved names from
530
+ * the contract-space candidate list.
531
+ */
532
+ declare const SPACE_REFS_DIRNAME = "refs";
533
+ /**
534
+ * Names reserved as per-space subdirectories of `migrations/<space>/`.
535
+ * Used by the enumerator to filter contract-space candidates so a
536
+ * reserved name (e.g. a top-level `migrations/refs/` left in the wrong
537
+ * place) is never enumerated as a phantom contract space. Currently
538
+ * holds `SPACE_REFS_DIRNAME`; extend if future per-space layouts add
539
+ * more reserved subdirectories.
540
+ */
541
+ declare const RESERVED_SPACE_SUBDIR_NAMES: ReadonlySet<string>;
542
+ /**
543
+ * Resolve the per-space refs directory for `spaceMigrationsDir`
544
+ * (typically the value returned by {@link spaceMigrationDirectory}).
545
+ * Composes the canonical {@link SPACE_REFS_DIRNAME} so callers do not
546
+ * hard-code the literal.
547
+ */
548
+ declare function spaceRefsDirectory(spaceMigrationsDir: string): string;
524
549
  //#endregion
525
- export { APP_SPACE_ID, type ComputeExtensionSpaceApplyPathInputs, type ContractSpaceArtefactInputs, type ContractSpaceHeadRecord, type ContractSpaceHeadRef, type DescriptorSelfConsistencyInputs, type DiskContractSpaceState, type ExtensionSpaceApplyPathOutcome, type SpaceApplyInput, type SpaceMarkerRecord, type SpacePlanInput, type SpacePlanOutput, type SpaceVerifierViolation, type ValidSpaceId, type VerifyContractSpacesInputs, type VerifyContractSpacesResult, assertDescriptorSelfConsistency, assertValidSpaceId, computeExtensionSpaceApplyPath, contractSpaceFromJson, emitContractSpaceArtefacts, gatherDiskContractSpaceState, isValidSpaceId, listContractSpaceDirectories, planAllSpaces, readContractSpaceContract, readContractSpaceHeadRef, spaceMigrationDirectory, verifyContractSpaces };
550
+ export { APP_SPACE_ID, type ComputeExtensionSpaceApplyPathInputs, type ContractSpaceArtefactInputs, type ContractSpaceHeadRecord, type ContractSpaceHeadRef, type DescriptorSelfConsistencyInputs, type DiskContractSpaceState, type ExtensionSpaceApplyPathOutcome, RESERVED_SPACE_SUBDIR_NAMES, SPACE_REFS_DIRNAME, type SpaceApplyInput, type SpaceMarkerRecord, type SpacePlanInput, type SpacePlanOutput, type SpaceVerifierViolation, type ValidSpaceId, type VerifyContractSpacesInputs, type VerifyContractSpacesResult, assertDescriptorSelfConsistency, assertValidSpaceId, computeExtensionSpaceApplyPath, contractSpaceFromJson, emitContractSpaceArtefacts, gatherDiskContractSpaceState, isValidSpaceId, listContractSpaceDirectories, planAllSpaces, readContractSpaceContract, readContractSpaceHeadRef, spaceMigrationDirectory, spaceRefsDirectory, verifyContractSpaces };
526
551
  //# sourceMappingURL=spaces.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spaces.d.mts","names":[],"sources":["../../src/assert-descriptor-self-consistency.ts","../../src/read-contract-space-head-ref.ts","../../src/compute-extension-space-apply-path.ts","../../src/concatenate-space-apply-inputs.ts","../../src/contract-space-from-json.ts","../../src/emit-contract-space-artefacts.ts","../../src/verify-contract-spaces.ts","../../src/gather-disk-contract-space-state.ts","../../src/plan-all-spaces.ts","../../src/read-contract-space-contract.ts","../../src/space-layout.ts"],"mappings":"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"}
1
+ {"version":3,"file":"spaces.d.mts","names":[],"sources":["../../src/assert-descriptor-self-consistency.ts","../../src/read-contract-space-head-ref.ts","../../src/compute-extension-space-apply-path.ts","../../src/concatenate-space-apply-inputs.ts","../../src/contract-space-from-json.ts","../../src/emit-contract-space-artefacts.ts","../../src/verify-contract-spaces.ts","../../src/gather-disk-contract-space-state.ts","../../src/plan-all-spaces.ts","../../src/read-contract-space-contract.ts","../../src/space-layout.ts"],"mappings":"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"}
@@ -2,7 +2,8 @@ import { r as errorDescriptorHeadHashMismatch, s as errorDuplicateSpaceId } from
2
2
  import { s as readMigrationsDir } from "../io-gHmDrSjQ.mjs";
3
3
  import "../constants-DWV9_o2Z.mjs";
4
4
  import { l as reconstructGraph, o as findPathWithDecision } from "../migration-graph-C2iNX8dk.mjs";
5
- import { a as APP_SPACE_ID, c as spaceMigrationDirectory, i as readContractSpaceHeadRef, n as listContractSpaceDirectories, o as assertValidSpaceId, r as verifyContractSpaces, s as isValidSpaceId, t as readContractSpaceContract } from "../read-contract-space-contract-Cglige7P.mjs";
5
+ import { a as SPACE_REFS_DIRNAME, c as spaceMigrationDirectory, i as RESERVED_SPACE_SUBDIR_NAMES, l as spaceRefsDirectory, n as verifyContractSpaces, o as assertValidSpaceId, r as APP_SPACE_ID, s as isValidSpaceId, t as listContractSpaceDirectories } from "../verify-contract-spaces-DxXWxGR0.mjs";
6
+ import { n as readContractSpaceHeadRef, t as readContractSpaceContract } from "../read-contract-space-contract-CwwlqKl_.mjs";
6
7
  import { join } from "pathe";
7
8
  import { mkdir, writeFile } from "node:fs/promises";
8
9
  import { canonicalizeJson } from "@prisma-next/framework-components/utils";
@@ -186,7 +187,8 @@ function contractSpaceFromJson(inputs) {
186
187
  async function emitContractSpaceArtefacts(projectMigrationsDir, spaceId, inputs) {
187
188
  assertValidSpaceId(spaceId);
188
189
  const dir = join(projectMigrationsDir, spaceId);
189
- await mkdir(join(dir, "refs"), { recursive: true });
190
+ const refsDir = spaceRefsDirectory(dir);
191
+ await mkdir(refsDir, { recursive: true });
190
192
  await writeFile(join(dir, "contract.json"), `${canonicalizeJson(inputs.contract)}\n`);
191
193
  await writeFile(join(dir, "contract.d.ts"), inputs.contractDts);
192
194
  const sortedInvariants = [...inputs.headRef.invariants].sort();
@@ -194,7 +196,7 @@ async function emitContractSpaceArtefacts(projectMigrationsDir, spaceId, inputs)
194
196
  hash: inputs.headRef.hash,
195
197
  invariants: sortedInvariants
196
198
  });
197
- await writeFile(join(dir, "refs", "head.json"), `${headJson}\n`);
199
+ await writeFile(join(refsDir, "head.json"), `${headJson}\n`);
198
200
  }
199
201
  //#endregion
200
202
  //#region src/gather-disk-contract-space-state.ts
@@ -275,6 +277,6 @@ function planAllSpaces(inputs, planSpace) {
275
277
  }));
276
278
  }
277
279
  //#endregion
278
- export { APP_SPACE_ID, assertDescriptorSelfConsistency, assertValidSpaceId, computeExtensionSpaceApplyPath, contractSpaceFromJson, emitContractSpaceArtefacts, gatherDiskContractSpaceState, isValidSpaceId, listContractSpaceDirectories, planAllSpaces, readContractSpaceContract, readContractSpaceHeadRef, spaceMigrationDirectory, verifyContractSpaces };
280
+ export { APP_SPACE_ID, RESERVED_SPACE_SUBDIR_NAMES, SPACE_REFS_DIRNAME, assertDescriptorSelfConsistency, assertValidSpaceId, computeExtensionSpaceApplyPath, contractSpaceFromJson, emitContractSpaceArtefacts, gatherDiskContractSpaceState, isValidSpaceId, listContractSpaceDirectories, planAllSpaces, readContractSpaceContract, readContractSpaceHeadRef, spaceMigrationDirectory, spaceRefsDirectory, verifyContractSpaces };
279
281
 
280
282
  //# sourceMappingURL=spaces.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"spaces.mjs","names":[],"sources":["../../src/assert-descriptor-self-consistency.ts","../../src/compute-extension-space-apply-path.ts","../../src/contract-space-from-json.ts","../../src/emit-contract-space-artefacts.ts","../../src/gather-disk-contract-space-state.ts","../../src/plan-all-spaces.ts"],"sourcesContent":["import { computeStorageHash } from '@prisma-next/contract/hashing';\nimport { errorDescriptorHeadHashMismatch } from './errors';\n\nfunction stripNamespaceKinds(storage: Record<string, unknown>): Record<string, unknown> {\n const namespaces = storage['namespaces'] as Record<string, Record<string, unknown>> | undefined;\n if (!namespaces) return storage;\n const stripped: Record<string, Record<string, unknown>> = {};\n for (const [nsId, ns] of Object.entries(namespaces)) {\n if (ns && typeof ns === 'object') {\n const { kind: _kind, ...rest } = ns as Record<string, unknown>;\n stripped[nsId] = rest as Record<string, unknown>;\n } else {\n stripped[nsId] = ns;\n }\n }\n return { ...storage, namespaces: stripped };\n}\n\n/**\n * Inputs the helper needs to recompute the descriptor's storage hash and\n * compare it to the published `headRef.hash`. Kept structural so the SQL\n * family (and any future target family) can compose the check without\n * coupling to its own descriptor types.\n */\nexport interface DescriptorSelfConsistencyInputs {\n readonly extensionId: string;\n readonly target: string;\n readonly targetFamily: string;\n /**\n * Family-specific storage object. Typed as `unknown` so callers can\n * pass their own narrow storage shape (e.g. `SqlStorage`) without an\n * inline cast — the helper canonicalises through `JSON.stringify`\n * inside {@link computeStorageHash} and only requires a plain\n * record-shaped value at runtime.\n */\n readonly storage: unknown;\n readonly headRefHash: string;\n}\n\n/**\n * Assert that an extension descriptor is self-consistent: the\n * `headRef.hash` it publishes must match the canonical hash recomputed\n * from its `contractSpace.contractJson`.\n *\n * Recomputes via {@link computeStorageHash} — the same canonical-JSON\n * pipeline the descriptor's own emit pipeline produced the hash with —\n * over `(target, targetFamily, storage)`. Mismatch indicates the\n * extension author bumped `contractJson` without rerunning emit, leaving\n * the descriptor's `headRef.hash` stale; the consumer-side helpers\n * (drift detection, on-disk artefact emission, runner marker writes) all\n * trust `headRef.hash` as the canonical identity, so a stale value would\n * silently corrupt every downstream boundary.\n *\n * Synchronous, pure, no I/O. Throws\n * `MIGRATION.DESCRIPTOR_HEAD_HASH_MISMATCH` on failure with both the\n * recomputed and published hashes in `details` so callers can surface a\n * clear remediation hint without re-deriving them.\n */\nexport function assertDescriptorSelfConsistency(inputs: DescriptorSelfConsistencyInputs): void {\n // The published `storage.storageHash` is the *output* of the production\n // emit pipeline's `computeStorageHash` call, computed over a storage\n // object that did not yet carry `storageHash`. Recomputing against the\n // published storage as-is would feed the result back into its own input\n // and produce a different digest. Strip `storageHash` before\n // recomputing so the helper sees the same canonical shape the\n // descriptor's authoring pipeline saw.\n // The helper requires only a plain record-shaped storage value at\n // runtime; a single cast here keeps the public input type\n // family-agnostic (`unknown`) while still letting us strip the\n // descriptor-published `storageHash` before re-canonicalising.\n const storageRecord = inputs.storage as Record<string, unknown>;\n const { storageHash: _stripped, ...storageWithoutHash } = storageRecord;\n // Target serializers (e.g. PostgresContractSerializer) inject a `kind`\n // discriminator into each namespace entry when writing contract.json (e.g.\n // `\"postgres-unbound-schema\"`). The authoring pipeline computes\n // `storageHash` from IR class instances whose `kind` property is\n // non-enumerable, so `kind` is absent from the hash input. Strip `kind`\n // from namespace entries before recomputing so this check always operates\n // on the same canonical shape the authoring pipeline saw.\n const normalizedStorage = stripNamespaceKinds(storageWithoutHash);\n const recomputed = computeStorageHash({\n target: inputs.target,\n targetFamily: inputs.targetFamily,\n storage: normalizedStorage,\n });\n if (recomputed !== inputs.headRefHash) {\n throw errorDescriptorHeadHashMismatch({\n extensionId: inputs.extensionId,\n recomputedHash: recomputed,\n headRefHash: inputs.headRefHash,\n });\n }\n}\n","import { EMPTY_CONTRACT_HASH } from './constants';\nimport { readMigrationsDir } from './io';\nimport { findPathWithDecision, reconstructGraph } from './migration-graph';\nimport type { MigrationOps } from './package';\nimport {\n type ContractSpaceHeadRef,\n readContractSpaceHeadRef,\n} from './read-contract-space-head-ref';\nimport { spaceMigrationDirectory } from './space-layout';\n\n/**\n * Outcome of {@link computeExtensionSpaceApplyPath} — a discriminated union\n * mirroring {@link import('./migration-graph').FindPathOutcome} so callers\n * can map structural / invariant failures to their preferred CLI envelope\n * without re-running pathfinding.\n */\nexport type ExtensionSpaceApplyPathOutcome =\n | {\n readonly kind: 'ok';\n readonly contractSpaceHeadRef: ContractSpaceHeadRef;\n /**\n * Sorted, deduplicated invariant ids covered by the walked path.\n * Mirrors the on-disk `providedInvariants` summed across edges and\n * canonicalised — what the runner stamps on the marker after apply.\n */\n readonly providedInvariants: readonly string[];\n /**\n * Path operations in apply order. Empty when the marker is already\n * at the recorded head (no-op).\n */\n readonly pathOps: MigrationOps;\n /**\n * Migration directory names walked, in order. Mirrors `pathOps`'s\n * structure but at the package granularity — useful for surfacing\n * \"applied N migration(s)\" messages.\n */\n readonly walkedMigrationDirs: readonly string[];\n }\n | { readonly kind: 'unreachable'; readonly contractSpaceHeadRef: ContractSpaceHeadRef }\n | {\n readonly kind: 'unsatisfiable';\n readonly contractSpaceHeadRef: ContractSpaceHeadRef;\n readonly missing: readonly string[];\n readonly structuralPath: readonly { readonly dirName: string; readonly to: string }[];\n }\n | { readonly kind: 'contractSpaceHeadRefMissing' };\n\n/**\n * Inputs to {@link computeExtensionSpaceApplyPath}. The helper is\n * deliberately framework-neutral and consumes only on-disk state:\n *\n * - `projectMigrationsDir` is the project's top-level `migrations/` dir.\n * - `spaceId` selects the per-space subdirectory under it.\n * - `currentMarkerHash` / `currentMarkerInvariants` come from the live\n * marker row keyed by `space = <spaceId>`. `null` hash = no marker yet\n * (the pathfinder treats this as the empty-contract sentinel per ADR\n * 208).\n */\nexport interface ComputeExtensionSpaceApplyPathInputs {\n readonly projectMigrationsDir: string;\n readonly spaceId: string;\n readonly currentMarkerHash: string | null;\n readonly currentMarkerInvariants: readonly string[];\n}\n\n/**\n * Compute the apply path for an extension contract space — the shortest\n * sequence of on-disk migration packages that walks the live marker\n * forward to the on-disk head ref hash, covering every required\n * invariant.\n *\n * Reads only on-disk artefacts (`migrations/<spaceId>/refs/head.json`\n * and the per-space migration packages). **Does not import any\n * extension descriptor module** — `db init` / `db update` must remain\n * runnable without the descriptor source on disk.\n *\n * Behaviour:\n * - Returns `{ kind: 'ok', pathOps: [], … }` when the marker is already\n * at the recorded head and no required invariants are missing.\n * - Returns `{ kind: 'unreachable' }` when the marker hash is not\n * structurally connected to the recorded head in the graph.\n * - Returns `{ kind: 'unsatisfiable', missing, … }` when the marker is\n * reachable but no path covers the required invariants.\n * - Returns `{ kind: 'contractSpaceHeadRefMissing' }` when the per-space\n * `refs/head.json` is absent — the precheck verifier should already\n * have rejected this case, but the helper is defensive so callers can\n * surface a coherent error rather than throw.\n */\nexport async function computeExtensionSpaceApplyPath(\n inputs: ComputeExtensionSpaceApplyPathInputs,\n): Promise<ExtensionSpaceApplyPathOutcome> {\n const { projectMigrationsDir, spaceId, currentMarkerHash, currentMarkerInvariants } = inputs;\n\n const contractSpaceHeadRef = await readContractSpaceHeadRef(projectMigrationsDir, spaceId);\n if (contractSpaceHeadRef === null) {\n return { kind: 'contractSpaceHeadRefMissing' };\n }\n\n const spaceDir = spaceMigrationDirectory(projectMigrationsDir, spaceId);\n const packages = await readMigrationsDir(spaceDir);\n const graph = reconstructGraph(packages);\n\n // Live-marker layer encodes \"no prior state\" as EMPTY_CONTRACT_HASH;\n // mirror the `migrate` flow so a fresh-marker initial walk\n // hits the baseline migration whose `from` is EMPTY_CONTRACT_HASH.\n const fromHash = currentMarkerHash ?? EMPTY_CONTRACT_HASH;\n const required = new Set(\n contractSpaceHeadRef.invariants.filter((id) => !currentMarkerInvariants.includes(id)),\n );\n\n const outcome = findPathWithDecision(graph, fromHash, contractSpaceHeadRef.hash, { required });\n\n if (outcome.kind === 'unreachable') {\n return { kind: 'unreachable', contractSpaceHeadRef };\n }\n if (outcome.kind === 'unsatisfiable') {\n return {\n kind: 'unsatisfiable',\n contractSpaceHeadRef,\n missing: outcome.missing,\n structuralPath: outcome.structuralPath.map(({ dirName, to }) => ({ dirName, to })),\n };\n }\n\n const packagesByHash = new Map(packages.map((pkg) => [pkg.metadata.migrationHash, pkg]));\n\n const pathOps: MigrationOps[number][] = [];\n const walkedMigrationDirs: string[] = [];\n const providedInvariantsSet = new Set<string>();\n for (const edge of outcome.decision.selectedPath) {\n const pkg = packagesByHash.get(edge.migrationHash);\n if (!pkg) {\n // Path edges always come from the same `packages` array, so this\n // is only reachable when the graph is internally inconsistent —\n // surface it loudly rather than silently truncating the path.\n throw new Error(\n `Migration package missing for edge ${edge.migrationHash} in space \"${spaceId}\"`,\n );\n }\n walkedMigrationDirs.push(pkg.dirName);\n for (const op of pkg.ops) pathOps.push(op);\n for (const invariant of pkg.metadata.providedInvariants) providedInvariantsSet.add(invariant);\n }\n\n return {\n kind: 'ok',\n contractSpaceHeadRef,\n providedInvariants: [...providedInvariantsSet].sort(),\n pathOps,\n walkedMigrationDirs,\n };\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type {\n ContractSpace,\n ContractSpaceHeadRef,\n MigrationPackage,\n MigrationPlanOperation,\n} from '@prisma-next/framework-components/control';\nimport type { MigrationMetadata } from './metadata';\n\n/**\n * Materialise a typed {@link ContractSpace} from the JSON artefacts a\n * contract-space extension package emits to disk.\n *\n * Extension descriptors wire `contract.json`, per-migration\n * `migration.json` / `ops.json`, and `refs/head.json` to the framework's\n * typed surfaces. TypeScript widens JSON imports to a structural record\n * that does not preserve readonly modifiers or branded scalars (e.g.\n * `StorageHashBase<'sha256:...'>`), so authoring the descriptor inline\n * forces every wiring site to cast through `unknown`. This helper\n * encapsulates the single narrowing point: descriptor sources stay\n * cast-free, and the (necessary) coercion is colocated with the\n * documentation explaining why it is safe.\n *\n * Safety: the JSON files passed here are produced by the framework's own\n * emit pipeline (`prisma-next contract emit` and `MigrationCLI.run`)\n * and re-validated downstream by the runner / verifier. The descriptor\n * is a pass-through wiring layer — no descriptor consumer treats the\n * narrowed types as a stronger guarantee than \"these came from the\n * canonical emit pipeline\".\n *\n * The helper does not introspect or schema-validate the inputs; runtime\n * validation is the responsibility of `family.deserializeContract`\n * (codec-aware, invoked at control-stack construction) and the\n * per-migration `readMigrationPackage` reader used when loading\n * from disk. JSON-imported packages flow through the descriptor without\n * a disk read, so the equivalent runtime guarantee comes from the emit\n * pipeline that produced the JSON in the first place.\n */\nexport function contractSpaceFromJson<TContract extends Contract = Contract>(inputs: {\n readonly contractJson: unknown;\n readonly migrations: ReadonlyArray<{\n readonly dirName: string;\n readonly metadata: unknown;\n readonly ops: unknown;\n }>;\n readonly headRef: ContractSpaceHeadRef;\n}): ContractSpace<TContract> {\n // The narrowing happens once, here. Casting via `unknown` rather than a\n // direct cast preserves TS's structural soundness checks for the\n // inputs (they must be assignable to `unknown`, which is trivial); the\n // resulting type is the family-specific Contract / MigrationPackage\n // surface descriptors publish.\n const migrations: readonly MigrationPackage[] = inputs.migrations.map((m) => ({\n dirName: m.dirName,\n metadata: m.metadata as MigrationMetadata,\n ops: m.ops as readonly MigrationPlanOperation[],\n }));\n return {\n contractJson: inputs.contractJson as TContract,\n migrations,\n headRef: inputs.headRef,\n };\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { canonicalizeJson } from '@prisma-next/framework-components/utils';\nimport { join } from 'pathe';\nimport type { ContractSpaceHeadRef } from './read-contract-space-head-ref';\nimport { assertValidSpaceId } from './space-layout';\n\n/**\n * Inputs for {@link emitContractSpaceArtefacts}.\n *\n * - `contract` is the canonical contract value the framework just emitted\n * for the space; it is serialised through {@link canonicalizeJson}, so\n * it must be a JSON-compatible value (objects / arrays / primitives).\n * Typed as `unknown` rather than the SQL-family `Contract<SqlStorage>`\n * to keep `migration-tools` framework-neutral; SQL-family callers pass\n * their typed value through unchanged.\n *\n * - `contractDts` is the pre-rendered `.d.ts` text. Rendering happens in\n * the SQL family (which owns the codec / typemap input the renderer\n * needs), so this helper accepts the text verbatim and writes it out\n * without further transformation.\n *\n * - `headRef` is the head reference for the space.\n * `invariants` are sorted alphabetically before serialisation so two\n * callers passing the same set in different orders produce\n * byte-identical `refs/head.json`.\n */\nexport interface ContractSpaceArtefactInputs {\n readonly contract: unknown;\n readonly contractDts: string;\n readonly headRef: ContractSpaceHeadRef;\n}\n\n/**\n * Emit the per-space artefacts (`contract.json`, `contract.d.ts`,\n * `refs/head.json`) under `<projectMigrationsDir>/<spaceId>/`.\n *\n * Always-overwrite: the framework owns these files; running `migrate`\n * twice with the same inputs is a no-op observably (idempotent), but the\n * helper does not check pre-existing contents — re-emit always wins.\n *\n * Path layout matches the convention in\n * [`spaceMigrationDirectory`](./space-layout.ts). The space id is\n * validated against `[a-z][a-z0-9_-]{0,63}` via\n * {@link assertValidSpaceId} for filesystem-safety reasons; the helper\n * accepts every space uniformly (including the app space, default\n * `'app'`).\n *\n * The migrations directory and space subdirectory are created if they\n * do not yet exist (`mkdir { recursive: true }`).\n */\nexport async function emitContractSpaceArtefacts(\n projectMigrationsDir: string,\n spaceId: string,\n inputs: ContractSpaceArtefactInputs,\n): Promise<void> {\n assertValidSpaceId(spaceId);\n\n const dir = join(projectMigrationsDir, spaceId);\n await mkdir(join(dir, 'refs'), { recursive: true });\n\n await writeFile(join(dir, 'contract.json'), `${canonicalizeJson(inputs.contract)}\\n`);\n await writeFile(join(dir, 'contract.d.ts'), inputs.contractDts);\n\n const sortedInvariants = [...inputs.headRef.invariants].sort();\n const headJson = canonicalizeJson({\n hash: inputs.headRef.hash,\n invariants: sortedInvariants,\n });\n await writeFile(join(dir, 'refs', 'head.json'), `${headJson}\\n`);\n}\n","import { readContractSpaceHeadRef } from './read-contract-space-head-ref';\nimport { APP_SPACE_ID } from './space-layout';\nimport {\n type ContractSpaceHeadRecord,\n listContractSpaceDirectories,\n} from './verify-contract-spaces';\n\n/**\n * Disk-side inputs to {@link import('./verify-contract-spaces').verifyContractSpaces}\n * — gathered without touching the live database. The caller composes\n * this with the marker rows it reads from the runtime to invoke the\n * verifier.\n */\nexport interface DiskContractSpaceState {\n /** Contract-space directory names observed under `<projectMigrationsDir>/`. */\n readonly spaceDirsOnDisk: readonly string[];\n /** Head-ref `(hash, invariants)` per extension space. */\n readonly headRefsBySpace: ReadonlyMap<string, ContractSpaceHeadRecord>;\n}\n\n/**\n * Read the on-disk state the per-space verifier needs:\n *\n * - The list of contract-space directories under\n * `<projectMigrationsDir>/` (via\n * {@link import('./verify-contract-spaces').listContractSpaceDirectories}).\n * - The on-disk head ref `(hash, invariants)` for each declared extension space\n * (via {@link readContractSpaceHeadRef}; missing on-disk artefacts are simply\n * omitted — the verifier reports them as `declaredButUnmigrated`).\n *\n * Synchronous in spirit but async due to filesystem reads. Reads only\n * the user's repo. **Does not import any extension descriptor module.**\n *\n * Composition convention: pure target-agnostic primitive in\n * `1-framework`; the SQL family (and any future target family) wires\n * it into its `dbInit` / `verify` flows alongside its own marker-row\n * read before invoking `verifyContractSpaces`.\n */\nexport async function gatherDiskContractSpaceState(args: {\n readonly projectMigrationsDir: string;\n /**\n * Set of space ids the project declares: `'app'` plus each entry in\n * `extensionPacks` whose descriptor exposes a `contractSpace`. The\n * helper reads on-disk head data only for the extension members.\n */\n readonly loadedSpaceIds: ReadonlySet<string>;\n}): Promise<DiskContractSpaceState> {\n const { projectMigrationsDir, loadedSpaceIds } = args;\n\n const spaceDirsOnDisk = await listContractSpaceDirectories(projectMigrationsDir);\n\n const headRefsBySpace = new Map<string, ContractSpaceHeadRecord>();\n for (const spaceId of loadedSpaceIds) {\n if (spaceId === APP_SPACE_ID) continue;\n const head = await readContractSpaceHeadRef(projectMigrationsDir, spaceId);\n if (head !== null) {\n headRefsBySpace.set(spaceId, head);\n }\n }\n\n return { spaceDirsOnDisk, headRefsBySpace };\n}\n","import { errorDuplicateSpaceId } from './errors';\n\n/**\n * Per-space input for {@link planAllSpaces}. One entry per loaded\n * contract space (the application's `'app'` plus each extension that\n * exposes a `contractSpace`).\n *\n * - `priorContract` is `null` for a space that has never been emitted\n * (no `migrations/<space-id>/contract.json` on disk yet); otherwise it\n * is the canonical contract value emitted for that space.\n * - `newContract` is the canonical contract value the planner is about\n * to emit for that space — for app-space, the just-emitted root\n * `contract.json`; for an extension space, the descriptor's\n * `contractSpace.contractJson`.\n */\nexport interface SpacePlanInput<TContract> {\n readonly spaceId: string;\n readonly priorContract: TContract | null;\n readonly newContract: TContract;\n}\n\nexport interface SpacePlanOutput<TPackage> {\n readonly spaceId: string;\n readonly migrationPackages: readonly TPackage[];\n}\n\n/**\n * Iterate the per-space planner across a set of loaded contract spaces\n * and return a deterministic shape regardless of declaration order.\n *\n * Behaviour:\n *\n * - The output is sorted alphabetically by `spaceId`. Two callers\n * passing the same set of inputs in different orders observe\n * byte-identical outputs.\n * - The per-space planner (`planSpace`) is called exactly once per\n * input, in alphabetical-by-spaceId order. Its return value is\n * attached to the corresponding output entry verbatim.\n * - Duplicate `spaceId`s in the input array throw\n * `MIGRATION.DUPLICATE_SPACE_ID` before any `planSpace` call runs,\n * keeping the planner pure when the input is malformed.\n *\n * The signature is generic over `TContract` and `TPackage` because the\n * shape is framework-neutral (SQL family today, Mongo family\n * eventually). Callers wire in whatever contract value and migration\n * package shape their family already speaks.\n *\n * Synchronous: the underlying per-space planner (target's\n * `MigrationPlanner.plan(...)`) is synchronous; callers that need to\n * resolve async I/O (e.g. reading on-disk `contract.json` from disk)\n * resolve it before calling `planAllSpaces` and pass the materialised\n * inputs through.\n */\nexport function planAllSpaces<TContract, TPackage>(\n inputs: readonly SpacePlanInput<TContract>[],\n planSpace: (input: SpacePlanInput<TContract>) => readonly TPackage[],\n): readonly SpacePlanOutput<TPackage>[] {\n const seen = new Set<string>();\n for (const input of inputs) {\n if (seen.has(input.spaceId)) {\n throw errorDuplicateSpaceId(input.spaceId);\n }\n seen.add(input.spaceId);\n }\n\n const sorted = [...inputs].sort((a, b) => {\n if (a.spaceId < b.spaceId) return -1;\n if (a.spaceId > b.spaceId) return 1;\n return 0;\n });\n\n return sorted.map((input) => ({\n spaceId: input.spaceId,\n migrationPackages: planSpace(input),\n }));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"spaces.mjs","names":[],"sources":["../../src/assert-descriptor-self-consistency.ts","../../src/compute-extension-space-apply-path.ts","../../src/contract-space-from-json.ts","../../src/emit-contract-space-artefacts.ts","../../src/gather-disk-contract-space-state.ts","../../src/plan-all-spaces.ts"],"sourcesContent":["import { computeStorageHash } from '@prisma-next/contract/hashing';\nimport { errorDescriptorHeadHashMismatch } from './errors';\n\nfunction stripNamespaceKinds(storage: Record<string, unknown>): Record<string, unknown> {\n const namespaces = storage['namespaces'] as Record<string, Record<string, unknown>> | undefined;\n if (!namespaces) return storage;\n const stripped: Record<string, Record<string, unknown>> = {};\n for (const [nsId, ns] of Object.entries(namespaces)) {\n if (ns && typeof ns === 'object') {\n const { kind: _kind, ...rest } = ns as Record<string, unknown>;\n stripped[nsId] = rest as Record<string, unknown>;\n } else {\n stripped[nsId] = ns;\n }\n }\n return { ...storage, namespaces: stripped };\n}\n\n/**\n * Inputs the helper needs to recompute the descriptor's storage hash and\n * compare it to the published `headRef.hash`. Kept structural so the SQL\n * family (and any future target family) can compose the check without\n * coupling to its own descriptor types.\n */\nexport interface DescriptorSelfConsistencyInputs {\n readonly extensionId: string;\n readonly target: string;\n readonly targetFamily: string;\n /**\n * Family-specific storage object. Typed as `unknown` so callers can\n * pass their own narrow storage shape (e.g. `SqlStorage`) without an\n * inline cast — the helper canonicalises through `JSON.stringify`\n * inside {@link computeStorageHash} and only requires a plain\n * record-shaped value at runtime.\n */\n readonly storage: unknown;\n readonly headRefHash: string;\n}\n\n/**\n * Assert that an extension descriptor is self-consistent: the\n * `headRef.hash` it publishes must match the canonical hash recomputed\n * from its `contractSpace.contractJson`.\n *\n * Recomputes via {@link computeStorageHash} — the same canonical-JSON\n * pipeline the descriptor's own emit pipeline produced the hash with —\n * over `(target, targetFamily, storage)`. Mismatch indicates the\n * extension author bumped `contractJson` without rerunning emit, leaving\n * the descriptor's `headRef.hash` stale; the consumer-side helpers\n * (drift detection, on-disk artefact emission, runner marker writes) all\n * trust `headRef.hash` as the canonical identity, so a stale value would\n * silently corrupt every downstream boundary.\n *\n * Synchronous, pure, no I/O. Throws\n * `MIGRATION.DESCRIPTOR_HEAD_HASH_MISMATCH` on failure with both the\n * recomputed and published hashes in `details` so callers can surface a\n * clear remediation hint without re-deriving them.\n */\nexport function assertDescriptorSelfConsistency(inputs: DescriptorSelfConsistencyInputs): void {\n // The published `storage.storageHash` is the *output* of the production\n // emit pipeline's `computeStorageHash` call, computed over a storage\n // object that did not yet carry `storageHash`. Recomputing against the\n // published storage as-is would feed the result back into its own input\n // and produce a different digest. Strip `storageHash` before\n // recomputing so the helper sees the same canonical shape the\n // descriptor's authoring pipeline saw.\n // The helper requires only a plain record-shaped storage value at\n // runtime; a single cast here keeps the public input type\n // family-agnostic (`unknown`) while still letting us strip the\n // descriptor-published `storageHash` before re-canonicalising.\n const storageRecord = inputs.storage as Record<string, unknown>;\n const { storageHash: _stripped, ...storageWithoutHash } = storageRecord;\n // Target serializers (e.g. PostgresContractSerializer) inject a `kind`\n // discriminator into each namespace entry when writing contract.json (e.g.\n // `\"postgres-unbound-schema\"`). The authoring pipeline computes\n // `storageHash` from IR class instances whose `kind` property is\n // non-enumerable, so `kind` is absent from the hash input. Strip `kind`\n // from namespace entries before recomputing so this check always operates\n // on the same canonical shape the authoring pipeline saw.\n const normalizedStorage = stripNamespaceKinds(storageWithoutHash);\n const recomputed = computeStorageHash({\n target: inputs.target,\n targetFamily: inputs.targetFamily,\n storage: normalizedStorage,\n });\n if (recomputed !== inputs.headRefHash) {\n throw errorDescriptorHeadHashMismatch({\n extensionId: inputs.extensionId,\n recomputedHash: recomputed,\n headRefHash: inputs.headRefHash,\n });\n }\n}\n","import { EMPTY_CONTRACT_HASH } from './constants';\nimport { readMigrationsDir } from './io';\nimport { findPathWithDecision, reconstructGraph } from './migration-graph';\nimport type { MigrationOps } from './package';\nimport {\n type ContractSpaceHeadRef,\n readContractSpaceHeadRef,\n} from './read-contract-space-head-ref';\nimport { spaceMigrationDirectory } from './space-layout';\n\n/**\n * Outcome of {@link computeExtensionSpaceApplyPath} — a discriminated union\n * mirroring {@link import('./migration-graph').FindPathOutcome} so callers\n * can map structural / invariant failures to their preferred CLI envelope\n * without re-running pathfinding.\n */\nexport type ExtensionSpaceApplyPathOutcome =\n | {\n readonly kind: 'ok';\n readonly contractSpaceHeadRef: ContractSpaceHeadRef;\n /**\n * Sorted, deduplicated invariant ids covered by the walked path.\n * Mirrors the on-disk `providedInvariants` summed across edges and\n * canonicalised — what the runner stamps on the marker after apply.\n */\n readonly providedInvariants: readonly string[];\n /**\n * Path operations in apply order. Empty when the marker is already\n * at the recorded head (no-op).\n */\n readonly pathOps: MigrationOps;\n /**\n * Migration directory names walked, in order. Mirrors `pathOps`'s\n * structure but at the package granularity — useful for surfacing\n * \"applied N migration(s)\" messages.\n */\n readonly walkedMigrationDirs: readonly string[];\n }\n | { readonly kind: 'unreachable'; readonly contractSpaceHeadRef: ContractSpaceHeadRef }\n | {\n readonly kind: 'unsatisfiable';\n readonly contractSpaceHeadRef: ContractSpaceHeadRef;\n readonly missing: readonly string[];\n readonly structuralPath: readonly { readonly dirName: string; readonly to: string }[];\n }\n | { readonly kind: 'contractSpaceHeadRefMissing' };\n\n/**\n * Inputs to {@link computeExtensionSpaceApplyPath}. The helper is\n * deliberately framework-neutral and consumes only on-disk state:\n *\n * - `projectMigrationsDir` is the project's top-level `migrations/` dir.\n * - `spaceId` selects the per-space subdirectory under it.\n * - `currentMarkerHash` / `currentMarkerInvariants` come from the live\n * marker row keyed by `space = <spaceId>`. `null` hash = no marker yet\n * (the pathfinder treats this as the empty-contract sentinel per ADR\n * 208).\n */\nexport interface ComputeExtensionSpaceApplyPathInputs {\n readonly projectMigrationsDir: string;\n readonly spaceId: string;\n readonly currentMarkerHash: string | null;\n readonly currentMarkerInvariants: readonly string[];\n}\n\n/**\n * Compute the apply path for an extension contract space — the shortest\n * sequence of on-disk migration packages that walks the live marker\n * forward to the on-disk head ref hash, covering every required\n * invariant.\n *\n * Reads only on-disk artefacts (`migrations/<spaceId>/refs/head.json`\n * and the per-space migration packages). **Does not import any\n * extension descriptor module** — `db init` / `db update` must remain\n * runnable without the descriptor source on disk.\n *\n * Behaviour:\n * - Returns `{ kind: 'ok', pathOps: [], … }` when the marker is already\n * at the recorded head and no required invariants are missing.\n * - Returns `{ kind: 'unreachable' }` when the marker hash is not\n * structurally connected to the recorded head in the graph.\n * - Returns `{ kind: 'unsatisfiable', missing, … }` when the marker is\n * reachable but no path covers the required invariants.\n * - Returns `{ kind: 'contractSpaceHeadRefMissing' }` when the per-space\n * `refs/head.json` is absent — the precheck verifier should already\n * have rejected this case, but the helper is defensive so callers can\n * surface a coherent error rather than throw.\n */\nexport async function computeExtensionSpaceApplyPath(\n inputs: ComputeExtensionSpaceApplyPathInputs,\n): Promise<ExtensionSpaceApplyPathOutcome> {\n const { projectMigrationsDir, spaceId, currentMarkerHash, currentMarkerInvariants } = inputs;\n\n const contractSpaceHeadRef = await readContractSpaceHeadRef(projectMigrationsDir, spaceId);\n if (contractSpaceHeadRef === null) {\n return { kind: 'contractSpaceHeadRefMissing' };\n }\n\n const spaceDir = spaceMigrationDirectory(projectMigrationsDir, spaceId);\n const packages = await readMigrationsDir(spaceDir);\n const graph = reconstructGraph(packages);\n\n // Live-marker layer encodes \"no prior state\" as EMPTY_CONTRACT_HASH;\n // mirror the `migrate` flow so a fresh-marker initial walk\n // hits the baseline migration whose `from` is EMPTY_CONTRACT_HASH.\n const fromHash = currentMarkerHash ?? EMPTY_CONTRACT_HASH;\n const required = new Set(\n contractSpaceHeadRef.invariants.filter((id) => !currentMarkerInvariants.includes(id)),\n );\n\n const outcome = findPathWithDecision(graph, fromHash, contractSpaceHeadRef.hash, { required });\n\n if (outcome.kind === 'unreachable') {\n return { kind: 'unreachable', contractSpaceHeadRef };\n }\n if (outcome.kind === 'unsatisfiable') {\n return {\n kind: 'unsatisfiable',\n contractSpaceHeadRef,\n missing: outcome.missing,\n structuralPath: outcome.structuralPath.map(({ dirName, to }) => ({ dirName, to })),\n };\n }\n\n const packagesByHash = new Map(packages.map((pkg) => [pkg.metadata.migrationHash, pkg]));\n\n const pathOps: MigrationOps[number][] = [];\n const walkedMigrationDirs: string[] = [];\n const providedInvariantsSet = new Set<string>();\n for (const edge of outcome.decision.selectedPath) {\n const pkg = packagesByHash.get(edge.migrationHash);\n if (!pkg) {\n // Path edges always come from the same `packages` array, so this\n // is only reachable when the graph is internally inconsistent —\n // surface it loudly rather than silently truncating the path.\n throw new Error(\n `Migration package missing for edge ${edge.migrationHash} in space \"${spaceId}\"`,\n );\n }\n walkedMigrationDirs.push(pkg.dirName);\n for (const op of pkg.ops) pathOps.push(op);\n for (const invariant of pkg.metadata.providedInvariants) providedInvariantsSet.add(invariant);\n }\n\n return {\n kind: 'ok',\n contractSpaceHeadRef,\n providedInvariants: [...providedInvariantsSet].sort(),\n pathOps,\n walkedMigrationDirs,\n };\n}\n","import type { Contract } from '@prisma-next/contract/types';\nimport type {\n ContractSpace,\n ContractSpaceHeadRef,\n MigrationPackage,\n MigrationPlanOperation,\n} from '@prisma-next/framework-components/control';\nimport type { MigrationMetadata } from './metadata';\n\n/**\n * Materialise a typed {@link ContractSpace} from the JSON artefacts a\n * contract-space extension package emits to disk.\n *\n * Extension descriptors wire `contract.json`, per-migration\n * `migration.json` / `ops.json`, and `refs/head.json` to the framework's\n * typed surfaces. TypeScript widens JSON imports to a structural record\n * that does not preserve readonly modifiers or branded scalars (e.g.\n * `StorageHashBase<'sha256:...'>`), so authoring the descriptor inline\n * forces every wiring site to cast through `unknown`. This helper\n * encapsulates the single narrowing point: descriptor sources stay\n * cast-free, and the (necessary) coercion is colocated with the\n * documentation explaining why it is safe.\n *\n * Safety: the JSON files passed here are produced by the framework's own\n * emit pipeline (`prisma-next contract emit` and `MigrationCLI.run`)\n * and re-validated downstream by the runner / verifier. The descriptor\n * is a pass-through wiring layer — no descriptor consumer treats the\n * narrowed types as a stronger guarantee than \"these came from the\n * canonical emit pipeline\".\n *\n * The helper does not introspect or schema-validate the inputs; runtime\n * validation is the responsibility of `family.deserializeContract`\n * (codec-aware, invoked at control-stack construction) and the\n * per-migration `readMigrationPackage` reader used when loading\n * from disk. JSON-imported packages flow through the descriptor without\n * a disk read, so the equivalent runtime guarantee comes from the emit\n * pipeline that produced the JSON in the first place.\n */\nexport function contractSpaceFromJson<TContract extends Contract = Contract>(inputs: {\n readonly contractJson: unknown;\n readonly migrations: ReadonlyArray<{\n readonly dirName: string;\n readonly metadata: unknown;\n readonly ops: unknown;\n }>;\n readonly headRef: ContractSpaceHeadRef;\n}): ContractSpace<TContract> {\n // The narrowing happens once, here. Casting via `unknown` rather than a\n // direct cast preserves TS's structural soundness checks for the\n // inputs (they must be assignable to `unknown`, which is trivial); the\n // resulting type is the family-specific Contract / MigrationPackage\n // surface descriptors publish.\n const migrations: readonly MigrationPackage[] = inputs.migrations.map((m) => ({\n dirName: m.dirName,\n metadata: m.metadata as MigrationMetadata,\n ops: m.ops as readonly MigrationPlanOperation[],\n }));\n return {\n contractJson: inputs.contractJson as TContract,\n migrations,\n headRef: inputs.headRef,\n };\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { canonicalizeJson } from '@prisma-next/framework-components/utils';\nimport { join } from 'pathe';\nimport type { ContractSpaceHeadRef } from './read-contract-space-head-ref';\nimport { assertValidSpaceId, spaceRefsDirectory } from './space-layout';\n\n/**\n * Inputs for {@link emitContractSpaceArtefacts}.\n *\n * - `contract` is the canonical contract value the framework just emitted\n * for the space; it is serialised through {@link canonicalizeJson}, so\n * it must be a JSON-compatible value (objects / arrays / primitives).\n * Typed as `unknown` rather than the SQL-family `Contract<SqlStorage>`\n * to keep `migration-tools` framework-neutral; SQL-family callers pass\n * their typed value through unchanged.\n *\n * - `contractDts` is the pre-rendered `.d.ts` text. Rendering happens in\n * the SQL family (which owns the codec / typemap input the renderer\n * needs), so this helper accepts the text verbatim and writes it out\n * without further transformation.\n *\n * - `headRef` is the head reference for the space.\n * `invariants` are sorted alphabetically before serialisation so two\n * callers passing the same set in different orders produce\n * byte-identical `refs/head.json`.\n */\nexport interface ContractSpaceArtefactInputs {\n readonly contract: unknown;\n readonly contractDts: string;\n readonly headRef: ContractSpaceHeadRef;\n}\n\n/**\n * Emit the per-space artefacts (`contract.json`, `contract.d.ts`,\n * `refs/head.json`) under `<projectMigrationsDir>/<spaceId>/`.\n *\n * Always-overwrite: the framework owns these files; running `migrate`\n * twice with the same inputs is a no-op observably (idempotent), but the\n * helper does not check pre-existing contents — re-emit always wins.\n *\n * Path layout matches the convention in\n * [`spaceMigrationDirectory`](./space-layout.ts). The space id is\n * validated against `[a-z][a-z0-9_-]{0,63}` via\n * {@link assertValidSpaceId} for filesystem-safety reasons; the helper\n * accepts every space uniformly (including the app space, default\n * `'app'`).\n *\n * The migrations directory and space subdirectory are created if they\n * do not yet exist (`mkdir { recursive: true }`).\n */\nexport async function emitContractSpaceArtefacts(\n projectMigrationsDir: string,\n spaceId: string,\n inputs: ContractSpaceArtefactInputs,\n): Promise<void> {\n assertValidSpaceId(spaceId);\n\n const dir = join(projectMigrationsDir, spaceId);\n const refsDir = spaceRefsDirectory(dir);\n await mkdir(refsDir, { recursive: true });\n\n await writeFile(join(dir, 'contract.json'), `${canonicalizeJson(inputs.contract)}\\n`);\n await writeFile(join(dir, 'contract.d.ts'), inputs.contractDts);\n\n const sortedInvariants = [...inputs.headRef.invariants].sort();\n const headJson = canonicalizeJson({\n hash: inputs.headRef.hash,\n invariants: sortedInvariants,\n });\n await writeFile(join(refsDir, 'head.json'), `${headJson}\\n`);\n}\n","import { readContractSpaceHeadRef } from './read-contract-space-head-ref';\nimport { APP_SPACE_ID } from './space-layout';\nimport {\n type ContractSpaceHeadRecord,\n listContractSpaceDirectories,\n} from './verify-contract-spaces';\n\n/**\n * Disk-side inputs to {@link import('./verify-contract-spaces').verifyContractSpaces}\n * — gathered without touching the live database. The caller composes\n * this with the marker rows it reads from the runtime to invoke the\n * verifier.\n */\nexport interface DiskContractSpaceState {\n /** Contract-space directory names observed under `<projectMigrationsDir>/`. */\n readonly spaceDirsOnDisk: readonly string[];\n /** Head-ref `(hash, invariants)` per extension space. */\n readonly headRefsBySpace: ReadonlyMap<string, ContractSpaceHeadRecord>;\n}\n\n/**\n * Read the on-disk state the per-space verifier needs:\n *\n * - The list of contract-space directories under\n * `<projectMigrationsDir>/` (via\n * {@link import('./verify-contract-spaces').listContractSpaceDirectories}).\n * - The on-disk head ref `(hash, invariants)` for each declared extension space\n * (via {@link readContractSpaceHeadRef}; missing on-disk artefacts are simply\n * omitted — the verifier reports them as `declaredButUnmigrated`).\n *\n * Synchronous in spirit but async due to filesystem reads. Reads only\n * the user's repo. **Does not import any extension descriptor module.**\n *\n * Composition convention: pure target-agnostic primitive in\n * `1-framework`; the SQL family (and any future target family) wires\n * it into its `dbInit` / `verify` flows alongside its own marker-row\n * read before invoking `verifyContractSpaces`.\n */\nexport async function gatherDiskContractSpaceState(args: {\n readonly projectMigrationsDir: string;\n /**\n * Set of space ids the project declares: `'app'` plus each entry in\n * `extensionPacks` whose descriptor exposes a `contractSpace`. The\n * helper reads on-disk head data only for the extension members.\n */\n readonly loadedSpaceIds: ReadonlySet<string>;\n}): Promise<DiskContractSpaceState> {\n const { projectMigrationsDir, loadedSpaceIds } = args;\n\n const spaceDirsOnDisk = await listContractSpaceDirectories(projectMigrationsDir);\n\n const headRefsBySpace = new Map<string, ContractSpaceHeadRecord>();\n for (const spaceId of loadedSpaceIds) {\n if (spaceId === APP_SPACE_ID) continue;\n const head = await readContractSpaceHeadRef(projectMigrationsDir, spaceId);\n if (head !== null) {\n headRefsBySpace.set(spaceId, head);\n }\n }\n\n return { spaceDirsOnDisk, headRefsBySpace };\n}\n","import { errorDuplicateSpaceId } from './errors';\n\n/**\n * Per-space input for {@link planAllSpaces}. One entry per loaded\n * contract space (the application's `'app'` plus each extension that\n * exposes a `contractSpace`).\n *\n * - `priorContract` is `null` for a space that has never been emitted\n * (no `migrations/<space-id>/contract.json` on disk yet); otherwise it\n * is the canonical contract value emitted for that space.\n * - `newContract` is the canonical contract value the planner is about\n * to emit for that space — for app-space, the just-emitted root\n * `contract.json`; for an extension space, the descriptor's\n * `contractSpace.contractJson`.\n */\nexport interface SpacePlanInput<TContract> {\n readonly spaceId: string;\n readonly priorContract: TContract | null;\n readonly newContract: TContract;\n}\n\nexport interface SpacePlanOutput<TPackage> {\n readonly spaceId: string;\n readonly migrationPackages: readonly TPackage[];\n}\n\n/**\n * Iterate the per-space planner across a set of loaded contract spaces\n * and return a deterministic shape regardless of declaration order.\n *\n * Behaviour:\n *\n * - The output is sorted alphabetically by `spaceId`. Two callers\n * passing the same set of inputs in different orders observe\n * byte-identical outputs.\n * - The per-space planner (`planSpace`) is called exactly once per\n * input, in alphabetical-by-spaceId order. Its return value is\n * attached to the corresponding output entry verbatim.\n * - Duplicate `spaceId`s in the input array throw\n * `MIGRATION.DUPLICATE_SPACE_ID` before any `planSpace` call runs,\n * keeping the planner pure when the input is malformed.\n *\n * The signature is generic over `TContract` and `TPackage` because the\n * shape is framework-neutral (SQL family today, Mongo family\n * eventually). Callers wire in whatever contract value and migration\n * package shape their family already speaks.\n *\n * Synchronous: the underlying per-space planner (target's\n * `MigrationPlanner.plan(...)`) is synchronous; callers that need to\n * resolve async I/O (e.g. reading on-disk `contract.json` from disk)\n * resolve it before calling `planAllSpaces` and pass the materialised\n * inputs through.\n */\nexport function planAllSpaces<TContract, TPackage>(\n inputs: readonly SpacePlanInput<TContract>[],\n planSpace: (input: SpacePlanInput<TContract>) => readonly TPackage[],\n): readonly SpacePlanOutput<TPackage>[] {\n const seen = new Set<string>();\n for (const input of inputs) {\n if (seen.has(input.spaceId)) {\n throw errorDuplicateSpaceId(input.spaceId);\n }\n seen.add(input.spaceId);\n }\n\n const sorted = [...inputs].sort((a, b) => {\n if (a.spaceId < b.spaceId) return -1;\n if (a.spaceId > b.spaceId) return 1;\n return 0;\n });\n\n return sorted.map((input) => ({\n spaceId: input.spaceId,\n migrationPackages: planSpace(input),\n }));\n}\n"],"mappings":"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"}
@@ -25,4 +25,4 @@ interface MigrationGraph {
25
25
  }
26
26
  //#endregion
27
27
  export { MigrationGraph as n, MigrationEdge as t };
28
- //# sourceMappingURL=graph-BrLXqoUc.d.mts.map
28
+ //# sourceMappingURL=graph-C7AJPGV5.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph-BrLXqoUc.d.mts","names":[],"sources":["../src/graph.ts"],"mappings":";;AAIA;;;UAAiB,aAAA;EAAA,SACN,IAAA;EAAA,SACA,EAAA;EAAA,SACA,aAAA;EAAA,SACA,OAAA;EAAA,SACA,SAAA;EAAA,SACA,MAAA;EAMA;;AAAU;AAGrB;;EAHW,SAAA,UAAA;AAAA;AAAA,UAGM,cAAA;EAAA,SACN,KAAA,EAAO,WAAA;EAAA,SACP,YAAA,EAAc,WAAA,kBAA6B,aAAA;EAAA,SAC3C,YAAA,EAAc,WAAA,kBAA6B,aAAA;EAAA,SAC3C,eAAA,EAAiB,WAAA,SAAoB,aAAA;AAAA"}
1
+ {"version":3,"file":"graph-C7AJPGV5.d.mts","names":[],"sources":["../src/graph.ts"],"mappings":";;AAIA;;;UAAiB,aAAA;EAAA,SACN,IAAA;EAAA,SACA,EAAA;EAAA,SACA,aAAA;EAAA,SACA,OAAA;EAAA,SACA,SAAA;EAAA,SACA,MAAA;EAMA;;AAAU;AAGrB;;EAHW,SAAA,UAAA;AAAA;AAAA,UAGM,cAAA;EAAA,SACN,KAAA,EAAO,WAAA;EAAA,SACP,YAAA,EAAc,WAAA,kBAA6B,aAAA;EAAA,SAC3C,YAAA,EAAc,WAAA,kBAA6B,aAAA;EAAA,SAC3C,eAAA,EAAiB,WAAA,SAAoB,aAAA;AAAA"}
@@ -1,5 +1,5 @@
1
- import { n as OnDiskMigrationPackage } from "./package-DZj8YvD0.mjs";
2
- import { n as MigrationGraph, t as MigrationEdge } from "./graph-BrLXqoUc.mjs";
1
+ import { n as OnDiskMigrationPackage } from "./package-DIttKL7X.mjs";
2
+ import { n as MigrationGraph, t as MigrationEdge } from "./graph-C7AJPGV5.mjs";
3
3
 
4
4
  //#region src/migration-graph.d.ts
5
5
  declare function reconstructGraph(packages: readonly OnDiskMigrationPackage[]): MigrationGraph;
@@ -121,4 +121,4 @@ declare function detectCycles(graph: MigrationGraph): readonly string[][];
121
121
  declare function detectOrphans(graph: MigrationGraph): readonly MigrationEdge[];
122
122
  //#endregion
123
123
  export { findLeaf as a, findPathWithInvariants as c, findLatestMigration as i, findReachableLeaves as l, detectCycles as n, findPath as o, detectOrphans as r, findPathWithDecision as s, PathDecision as t, reconstructGraph as u };
124
- //# sourceMappingURL=migration-graph-De0dUZoC.d.mts.map
124
+ //# sourceMappingURL=migration-graph-ABYqVsmv.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"migration-graph-De0dUZoC.d.mts","names":[],"sources":["../src/migration-graph.ts"],"mappings":";;;;iBAsCgB,gBAAA,CAAiB,QAAA,WAAmB,sBAAA,KAA2B,cAAc;;AAA7F;;;;;;;iBAqFgB,QAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,oBACU,aAAa;AAzFoE;AAqF7F;;;;;;;;;;AAIyB;AA4CzB;;;;;;;AArI6F,iBAqI7E,sBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,QAAA,EAAU,WAAA,oBACA,aAAA;AAAA,UAsFK,YAAA;EAAA,SACN,YAAA,WAAuB,aAAa;EAAA,SACpC,QAAA;EAAA,SACA,MAAA;EAAA,SACA,gBAAA;EAAA,SACA,eAAA;EAAA,SACA,OAAA;EA5Fc;EAAA,SA8Fd,kBAAA;EARM;;;;;EAAA,SAcN,mBAAA;AAAA;;;;;;;;AAAmB;AAoB9B;;;;;;;;;KAAY,eAAA;EAAA,SACG,IAAA;EAAA,SAAqB,QAAA,EAAU,YAAA;AAAA;EAAA,SAC/B,IAAA;AAAA;EAAA,SAEA,IAAA;EAAA,SACA,cAAA,WAAyB,aAAa;EAAA,SACtC,OAAA;AAAA;;;;AAYkB;AAajC;;;UAfiB,2BAAA;EAAA,SACN,OAAA;EAAA,SACA,QAAA,GAAW,WAAW;AAAA;;;;;;;;;;AAkBf;iBALF,oBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,OAAA,GAAS,2BAAA,GACR,eAAA;;;;;iBAqLa,mBAAA,CAAoB,KAAA,EAAO,cAAc,EAAE,QAAA;;;AAAgB;AAkB3E;;;;AAA8C;iBAA9B,QAAA,CAAS,KAAqB,EAAd,cAAc;;;;;;iBAwC9B,mBAAA,CAAoB,KAAA,EAAO,cAAA,GAAiB,aAAa;AAAA,iBAQzD,YAAA,CAAa,KAAqB,EAAd,cAAc;AAAA,iBA8DlC,aAAA,CAAc,KAAA,EAAO,cAAA,YAA0B,aAAa"}
1
+ {"version":3,"file":"migration-graph-ABYqVsmv.d.mts","names":[],"sources":["../src/migration-graph.ts"],"mappings":";;;;iBAsCgB,gBAAA,CAAiB,QAAA,WAAmB,sBAAA,KAA2B,cAAc;;AAA7F;;;;;;;iBAqFgB,QAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,oBACU,aAAa;AAzFoE;AAqF7F;;;;;;;;;;AAIyB;AA4CzB;;;;;;;AArI6F,iBAqI7E,sBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,QAAA,EAAU,WAAA,oBACA,aAAA;AAAA,UAsFK,YAAA;EAAA,SACN,YAAA,WAAuB,aAAa;EAAA,SACpC,QAAA;EAAA,SACA,MAAA;EAAA,SACA,gBAAA;EAAA,SACA,eAAA;EAAA,SACA,OAAA;EA5Fc;EAAA,SA8Fd,kBAAA;EARM;;;;;EAAA,SAcN,mBAAA;AAAA;;;;;;;;AAAmB;AAoB9B;;;;;;;;;KAAY,eAAA;EAAA,SACG,IAAA;EAAA,SAAqB,QAAA,EAAU,YAAA;AAAA;EAAA,SAC/B,IAAA;AAAA;EAAA,SAEA,IAAA;EAAA,SACA,cAAA,WAAyB,aAAa;EAAA,SACtC,OAAA;AAAA;;;;AAYkB;AAajC;;;UAfiB,2BAAA;EAAA,SACN,OAAA;EAAA,SACA,QAAA,GAAW,WAAW;AAAA;;;;;;;;;;AAkBf;iBALF,oBAAA,CACd,KAAA,EAAO,cAAA,EACP,QAAA,UACA,MAAA,UACA,OAAA,GAAS,2BAAA,GACR,eAAA;;;;;iBAqLa,mBAAA,CAAoB,KAAA,EAAO,cAAc,EAAE,QAAA;;;AAAgB;AAkB3E;;;;AAA8C;iBAA9B,QAAA,CAAS,KAAqB,EAAd,cAAc;;;;;;iBAwC9B,mBAAA,CAAoB,KAAA,EAAO,cAAA,GAAiB,aAAa;AAAA,iBAQzD,YAAA,CAAa,KAAqB,EAAd,cAAc;AAAA,iBA8DlC,aAAA,CAAc,KAAA,EAAO,cAAA,YAA0B,aAAa"}
@@ -0,0 +1,23 @@
1
+ //#region src/migration-list-types.d.ts
2
+ interface MigrationListEntry {
3
+ readonly dirName: string;
4
+ readonly from: string | null;
5
+ readonly to: string;
6
+ readonly migrationHash: string;
7
+ readonly operationCount: number;
8
+ readonly createdAt: string;
9
+ readonly refs: readonly string[];
10
+ readonly providedInvariants: readonly string[];
11
+ }
12
+ interface MigrationSpaceListEntry {
13
+ readonly spaceId: string;
14
+ readonly migrations: readonly MigrationListEntry[];
15
+ }
16
+ interface MigrationListResult {
17
+ readonly ok: true;
18
+ readonly spaces: readonly MigrationSpaceListEntry[];
19
+ readonly summary: string;
20
+ }
21
+ //#endregion
22
+ export { MigrationListResult as n, MigrationSpaceListEntry as r, MigrationListEntry as t };
23
+ //# sourceMappingURL=migration-list-types-B-qimPet.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migration-list-types-B-qimPet.d.mts","names":[],"sources":["../src/migration-list-types.ts"],"mappings":";UAAiB,kBAAA;EAAA,SACN,OAAA;EAAA,SACA,IAAA;EAAA,SACA,EAAA;EAAA,SACA,aAAA;EAAA,SACA,cAAA;EAAA,SACA,SAAA;EAAA,SACA,IAAA;EAAA,SACA,kBAAA;AAAA;AAAA,UAGM,uBAAA;EAAA,SACN,OAAA;EAAA,SACA,UAAA,WAAqB,kBAAkB;AAAA;AAAA,UAGjC,mBAAA;EAAA,SACN,EAAA;EAAA,SACA,MAAA,WAAiB,uBAAuB;EAAA,SACxC,OAAA;AAAA"}
@@ -18,4 +18,4 @@ interface OnDiskMigrationPackage extends MigrationPackage {
18
18
  }
19
19
  //#endregion
20
20
  export { OnDiskMigrationPackage as n, MigrationOps as t };
21
- //# sourceMappingURL=package-DZj8YvD0.d.mts.map
21
+ //# sourceMappingURL=package-DIttKL7X.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"package-DZj8YvD0.d.mts","names":[],"sources":["../src/package.ts"],"mappings":";;;KAKY,YAAA,YAAwB,sBAAsB;;AAA1D;;;;AAA0D;AAa1D;;;;AACkB;UADD,sBAAA,SAA+B,gBAAgB;EAAA,SACrD,OAAO;AAAA"}
1
+ {"version":3,"file":"package-DIttKL7X.d.mts","names":[],"sources":["../src/package.ts"],"mappings":";;;KAKY,YAAA,YAAwB,sBAAsB;;AAA1D;;;;AAA0D;AAa1D;;;;AACkB;UADD,sBAAA,SAA+B,gBAAgB;EAAA,SACrD,OAAO;AAAA"}
@@ -0,0 +1,82 @@
1
+ import { b as errorMissingFile, d as errorInvalidJson, m as errorInvalidRefFile } from "./errors-CoEN114u.mjs";
2
+ import { c as spaceMigrationDirectory, l as spaceRefsDirectory, o as assertValidSpaceId } from "./verify-contract-spaces-DxXWxGR0.mjs";
3
+ import { join } from "pathe";
4
+ import { readFile } from "node:fs/promises";
5
+ //#region src/read-contract-space-head-ref.ts
6
+ function hasErrnoCode$1(error, code) {
7
+ return error instanceof Error && error.code === code;
8
+ }
9
+ /**
10
+ * Read the head ref (`hash` + `invariants`) for a contract space from
11
+ * `<projectMigrationsDir>/<spaceId>/refs/head.json`.
12
+ *
13
+ * Returns `null` when the file does not exist (first emit). Surfaces
14
+ * `MIGRATION.INVALID_JSON` / `MIGRATION.INVALID_REF_FILE` on a corrupt
15
+ * `refs/head.json` so callers can distinguish "no head ref on disk"
16
+ * (returns `null`) from "head ref present but unreadable" (throws).
17
+ *
18
+ * Validates the space id against `[a-z][a-z0-9_-]{0,63}` for the same
19
+ * filesystem-safety reasons as the rest of the per-space helpers. The
20
+ * helper is uniform across the app and extension spaces.
21
+ */
22
+ async function readContractSpaceHeadRef(projectMigrationsDir, spaceId) {
23
+ assertValidSpaceId(spaceId);
24
+ const filePath = join(spaceRefsDirectory(spaceMigrationDirectory(projectMigrationsDir, spaceId)), "head.json");
25
+ let raw;
26
+ try {
27
+ raw = await readFile(filePath, "utf-8");
28
+ } catch (error) {
29
+ if (hasErrnoCode$1(error, "ENOENT")) return null;
30
+ throw error;
31
+ }
32
+ let parsed;
33
+ try {
34
+ parsed = JSON.parse(raw);
35
+ } catch (e) {
36
+ throw errorInvalidJson(filePath, e instanceof Error ? e.message : String(e));
37
+ }
38
+ if (typeof parsed !== "object" || parsed === null) throw errorInvalidRefFile(filePath, "expected an object");
39
+ const obj = parsed;
40
+ if (typeof obj.hash !== "string") throw errorInvalidRefFile(filePath, "expected an object with a string `hash` field");
41
+ if (!Array.isArray(obj.invariants) || obj.invariants.some((value) => typeof value !== "string")) throw errorInvalidRefFile(filePath, "expected an object with an `invariants` array of strings");
42
+ return {
43
+ hash: obj.hash,
44
+ invariants: obj.invariants
45
+ };
46
+ }
47
+ //#endregion
48
+ //#region src/read-contract-space-contract.ts
49
+ function hasErrnoCode(error, code) {
50
+ return error instanceof Error && error.code === code;
51
+ }
52
+ /**
53
+ * Read the on-disk contract value for a contract space
54
+ * (`<projectMigrationsDir>/<spaceId>/contract.json`). Returns the parsed
55
+ * JSON value as `unknown` — callers that need a typed contract validate
56
+ * via their family's `deserializeContract` to surface schema issues.
57
+ *
58
+ * Companion to {@link import('./read-contract-space-head-ref').readContractSpaceHeadRef}
59
+ * — same ENOENT-throws / corrupt-file-error semantics. Returns the
60
+ * canonical-JSON value the framework wrote during emit, so re-running
61
+ * this helper across machines / runs yields a byte-identical value.
62
+ */
63
+ async function readContractSpaceContract(projectMigrationsDir, spaceId) {
64
+ assertValidSpaceId(spaceId);
65
+ const filePath = join(projectMigrationsDir, spaceId, "contract.json");
66
+ let raw;
67
+ try {
68
+ raw = await readFile(filePath, "utf-8");
69
+ } catch (error) {
70
+ if (hasErrnoCode(error, "ENOENT")) throw errorMissingFile("contract.json", join(projectMigrationsDir, spaceId));
71
+ throw error;
72
+ }
73
+ try {
74
+ return JSON.parse(raw);
75
+ } catch (e) {
76
+ throw errorInvalidJson(filePath, e instanceof Error ? e.message : String(e));
77
+ }
78
+ }
79
+ //#endregion
80
+ export { readContractSpaceHeadRef as n, readContractSpaceContract as t };
81
+
82
+ //# sourceMappingURL=read-contract-space-contract-CwwlqKl_.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-contract-space-contract-CwwlqKl_.mjs","names":["hasErrnoCode"],"sources":["../src/read-contract-space-head-ref.ts","../src/read-contract-space-contract.ts"],"sourcesContent":["import { readFile } from 'node:fs/promises';\nimport type { ContractSpaceHeadRef } from '@prisma-next/framework-components/control';\nimport { join } from 'pathe';\nimport { errorInvalidJson, errorInvalidRefFile } from './errors';\nimport { assertValidSpaceId, spaceMigrationDirectory, spaceRefsDirectory } from './space-layout';\n\nexport type { ContractSpaceHeadRef };\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\n/**\n * Read the head ref (`hash` + `invariants`) for a contract space from\n * `<projectMigrationsDir>/<spaceId>/refs/head.json`.\n *\n * Returns `null` when the file does not exist (first emit). Surfaces\n * `MIGRATION.INVALID_JSON` / `MIGRATION.INVALID_REF_FILE` on a corrupt\n * `refs/head.json` so callers can distinguish \"no head ref on disk\"\n * (returns `null`) from \"head ref present but unreadable\" (throws).\n *\n * Validates the space id against `[a-z][a-z0-9_-]{0,63}` for the same\n * filesystem-safety reasons as the rest of the per-space helpers. The\n * helper is uniform across the app and extension spaces.\n */\nexport async function readContractSpaceHeadRef(\n projectMigrationsDir: string,\n spaceId: string,\n): Promise<ContractSpaceHeadRef | null> {\n assertValidSpaceId(spaceId);\n\n const filePath = join(\n spaceRefsDirectory(spaceMigrationDirectory(projectMigrationsDir, spaceId)),\n 'head.json',\n );\n\n let raw: string;\n try {\n raw = await readFile(filePath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return null;\n }\n throw error;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (e) {\n throw errorInvalidJson(filePath, e instanceof Error ? e.message : String(e));\n }\n\n if (typeof parsed !== 'object' || parsed === null) {\n throw errorInvalidRefFile(filePath, 'expected an object');\n }\n const obj = parsed as { hash?: unknown; invariants?: unknown };\n if (typeof obj.hash !== 'string') {\n throw errorInvalidRefFile(filePath, 'expected an object with a string `hash` field');\n }\n if (!Array.isArray(obj.invariants) || obj.invariants.some((value) => typeof value !== 'string')) {\n throw errorInvalidRefFile(filePath, 'expected an object with an `invariants` array of strings');\n }\n\n return { hash: obj.hash, invariants: obj.invariants as readonly string[] };\n}\n","import { readFile } from 'node:fs/promises';\nimport { join } from 'pathe';\nimport { errorInvalidJson, errorMissingFile } from './errors';\nimport { assertValidSpaceId } from './space-layout';\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\n/**\n * Read the on-disk contract value for a contract space\n * (`<projectMigrationsDir>/<spaceId>/contract.json`). Returns the parsed\n * JSON value as `unknown` — callers that need a typed contract validate\n * via their family's `deserializeContract` to surface schema issues.\n *\n * Companion to {@link import('./read-contract-space-head-ref').readContractSpaceHeadRef}\n * — same ENOENT-throws / corrupt-file-error semantics. Returns the\n * canonical-JSON value the framework wrote during emit, so re-running\n * this helper across machines / runs yields a byte-identical value.\n */\nexport async function readContractSpaceContract(\n projectMigrationsDir: string,\n spaceId: string,\n): Promise<unknown> {\n assertValidSpaceId(spaceId);\n\n const filePath = join(projectMigrationsDir, spaceId, 'contract.json');\n\n let raw: string;\n try {\n raw = await readFile(filePath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile('contract.json', join(projectMigrationsDir, spaceId));\n }\n throw error;\n }\n\n try {\n return JSON.parse(raw);\n } catch (e) {\n throw errorInvalidJson(filePath, e instanceof Error ? e.message : String(e));\n }\n}\n"],"mappings":";;;;;AAQA,SAASA,eAAa,OAAgB,MAAuB;CAC3D,OAAO,iBAAiB,SAAU,MAA4B,SAAS;AACzE;;;;;;;;;;;;;;AAeA,eAAsB,yBACpB,sBACA,SACsC;CACtC,mBAAmB,OAAO;CAE1B,MAAM,WAAW,KACf,mBAAmB,wBAAwB,sBAAsB,OAAO,CAAC,GACzE,WACF;CAEA,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,SAAS,UAAU,OAAO;CACxC,SAAS,OAAO;EACd,IAAIA,eAAa,OAAO,QAAQ,GAC9B,OAAO;EAET,MAAM;CACR;CAEA,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,GAAG;CACzB,SAAS,GAAG;EACV,MAAM,iBAAiB,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;CAC7E;CAEA,IAAI,OAAO,WAAW,YAAY,WAAW,MAC3C,MAAM,oBAAoB,UAAU,oBAAoB;CAE1D,MAAM,MAAM;CACZ,IAAI,OAAO,IAAI,SAAS,UACtB,MAAM,oBAAoB,UAAU,+CAA+C;CAErF,IAAI,CAAC,MAAM,QAAQ,IAAI,UAAU,KAAK,IAAI,WAAW,MAAM,UAAU,OAAO,UAAU,QAAQ,GAC5F,MAAM,oBAAoB,UAAU,0DAA0D;CAGhG,OAAO;EAAE,MAAM,IAAI;EAAM,YAAY,IAAI;CAAgC;AAC3E;;;AC5DA,SAAS,aAAa,OAAgB,MAAuB;CAC3D,OAAO,iBAAiB,SAAU,MAA4B,SAAS;AACzE;;;;;;;;;;;;AAaA,eAAsB,0BACpB,sBACA,SACkB;CAClB,mBAAmB,OAAO;CAE1B,MAAM,WAAW,KAAK,sBAAsB,SAAS,eAAe;CAEpE,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,SAAS,UAAU,OAAO;CACxC,SAAS,OAAO;EACd,IAAI,aAAa,OAAO,QAAQ,GAC9B,MAAM,iBAAiB,iBAAiB,KAAK,sBAAsB,OAAO,CAAC;EAE7E,MAAM;CACR;CAEA,IAAI;EACF,OAAO,KAAK,MAAM,GAAG;CACvB,SAAS,GAAG;EACV,MAAM,iBAAiB,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;CAC7E;AACF"}
@@ -13,4 +13,4 @@ declare function deleteRef(refsDir: string, name: string): Promise<void>;
13
13
  declare function resolveRef(refs: Refs, name: string): RefEntry;
14
14
  //#endregion
15
15
  export { readRefs as a, validateRefValue as c, readRef as i, writeRef as l, Refs as n, resolveRef as o, deleteRef as r, validateRefName as s, RefEntry as t };
16
- //# sourceMappingURL=refs-CDaNerhT.d.mts.map
16
+ //# sourceMappingURL=refs-BaygQaFD.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"refs-CDaNerhT.d.mts","names":[],"sources":["../src/refs.ts"],"mappings":";UAUiB,QAAA;EAAA,SACN,IAAA;EAAA,SACA,UAAU;AAAA;AAAA,KAGT,IAAA,GAAO,QAAA,CAAS,MAAA,SAAe,QAAA;AAAA,iBAK3B,eAAA,CAAgB,IAAY;AAAA,iBAQ5B,gBAAA,CAAiB,KAAa;AAAA,iBAsBxB,OAAA,CAAQ,OAAA,UAAiB,IAAA,WAAe,OAAO,CAAC,QAAA;AAAA,iBAmChD,QAAA,CAAS,OAAA,WAAkB,OAAO,CAAC,IAAA;AAAA,iBAqDnC,QAAA,CAAS,OAAA,UAAiB,IAAA,UAAc,KAAA,EAAO,QAAA,GAAW,OAAO;AAAA,iBAoBjE,SAAA,CAAU,OAAA,UAAiB,IAAA,WAAe,OAAO;AAAA,iBAsCvD,UAAA,CAAW,IAAA,EAAM,IAAA,EAAM,IAAA,WAAe,QAAQ"}
1
+ {"version":3,"file":"refs-BaygQaFD.d.mts","names":[],"sources":["../src/refs.ts"],"mappings":";UAUiB,QAAA;EAAA,SACN,IAAA;EAAA,SACA,UAAU;AAAA;AAAA,KAGT,IAAA,GAAO,QAAA,CAAS,MAAA,SAAe,QAAA;AAAA,iBAK3B,eAAA,CAAgB,IAAY;AAAA,iBAQ5B,gBAAA,CAAiB,KAAa;AAAA,iBAsBxB,OAAA,CAAQ,OAAA,UAAiB,IAAA,WAAe,OAAO,CAAC,QAAA;AAAA,iBAmChD,QAAA,CAAS,OAAA,WAAkB,OAAO,CAAC,IAAA;AAAA,iBAqDnC,QAAA,CAAS,OAAA,UAAiB,IAAA,UAAc,KAAA,EAAO,QAAA,GAAW,OAAO;AAAA,iBAoBjE,SAAA,CAAU,OAAA,UAAiB,IAAA,WAAe,OAAO;AAAA,iBAsCvD,UAAA,CAAW,IAAA,EAAM,IAAA,EAAM,IAAA,WAAe,QAAQ"}
@@ -1,7 +1,7 @@
1
- import { b as errorMissingFile, d as errorInvalidJson, m as errorInvalidRefFile, v as errorInvalidSpaceId } from "./errors-CoEN114u.mjs";
1
+ import { v as errorInvalidSpaceId } from "./errors-CoEN114u.mjs";
2
2
  import { t as MANIFEST_FILE } from "./io-gHmDrSjQ.mjs";
3
3
  import { join } from "pathe";
4
- import { readFile, readdir, stat } from "node:fs/promises";
4
+ import { readdir, stat } from "node:fs/promises";
5
5
  import { APP_SPACE_ID } from "@prisma-next/framework-components/control";
6
6
  //#region src/space-layout.ts
7
7
  /**
@@ -32,52 +32,36 @@ function spaceMigrationDirectory(projectMigrationsDir, spaceId) {
32
32
  assertValidSpaceId(spaceId);
33
33
  return join(projectMigrationsDir, spaceId);
34
34
  }
35
- //#endregion
36
- //#region src/read-contract-space-head-ref.ts
37
- function hasErrnoCode$2(error, code) {
38
- return error instanceof Error && error.code === code;
39
- }
40
35
  /**
41
- * Read the head ref (`hash` + `invariants`) for a contract space from
42
- * `<projectMigrationsDir>/<spaceId>/refs/head.json`.
43
- *
44
- * Returns `null` when the file does not exist (first emit). Surfaces
45
- * `MIGRATION.INVALID_JSON` / `MIGRATION.INVALID_REF_FILE` on a corrupt
46
- * `refs/head.json` so callers can distinguish "no head ref on disk"
47
- * (returns `null`) from "head ref present but unreadable" (throws).
48
- *
49
- * Validates the space id against `[a-z][a-z0-9_-]{0,63}` for the same
50
- * filesystem-safety reasons as the rest of the per-space helpers. The
51
- * helper is uniform across the app and extension spaces.
36
+ * Per-space subdirectory name reserved for the ref store
37
+ * (`migrations/<space>/<SPACE_REFS_DIRNAME>/*.json`). Single source of
38
+ * truth: every helper that composes a per-space refs path imports this
39
+ * constant, and the enumerator uses it (via
40
+ * {@link RESERVED_SPACE_SUBDIR_NAMES}) to exclude reserved names from
41
+ * the contract-space candidate list.
52
42
  */
53
- async function readContractSpaceHeadRef(projectMigrationsDir, spaceId) {
54
- assertValidSpaceId(spaceId);
55
- const filePath = join(projectMigrationsDir, spaceId, "refs", "head.json");
56
- let raw;
57
- try {
58
- raw = await readFile(filePath, "utf-8");
59
- } catch (error) {
60
- if (hasErrnoCode$2(error, "ENOENT")) return null;
61
- throw error;
62
- }
63
- let parsed;
64
- try {
65
- parsed = JSON.parse(raw);
66
- } catch (e) {
67
- throw errorInvalidJson(filePath, e instanceof Error ? e.message : String(e));
68
- }
69
- if (typeof parsed !== "object" || parsed === null) throw errorInvalidRefFile(filePath, "expected an object");
70
- const obj = parsed;
71
- if (typeof obj.hash !== "string") throw errorInvalidRefFile(filePath, "expected an object with a string `hash` field");
72
- if (!Array.isArray(obj.invariants) || obj.invariants.some((value) => typeof value !== "string")) throw errorInvalidRefFile(filePath, "expected an object with an `invariants` array of strings");
73
- return {
74
- hash: obj.hash,
75
- invariants: obj.invariants
76
- };
43
+ const SPACE_REFS_DIRNAME = "refs";
44
+ /**
45
+ * Names reserved as per-space subdirectories of `migrations/<space>/`.
46
+ * Used by the enumerator to filter contract-space candidates so a
47
+ * reserved name (e.g. a top-level `migrations/refs/` left in the wrong
48
+ * place) is never enumerated as a phantom contract space. Currently
49
+ * holds `SPACE_REFS_DIRNAME`; extend if future per-space layouts add
50
+ * more reserved subdirectories.
51
+ */
52
+ const RESERVED_SPACE_SUBDIR_NAMES = new Set([SPACE_REFS_DIRNAME]);
53
+ /**
54
+ * Resolve the per-space refs directory for `spaceMigrationsDir`
55
+ * (typically the value returned by {@link spaceMigrationDirectory}).
56
+ * Composes the canonical {@link SPACE_REFS_DIRNAME} so callers do not
57
+ * hard-code the literal.
58
+ */
59
+ function spaceRefsDirectory(spaceMigrationsDir) {
60
+ return join(spaceMigrationsDir, SPACE_REFS_DIRNAME);
77
61
  }
78
62
  //#endregion
79
63
  //#region src/verify-contract-spaces.ts
80
- function hasErrnoCode$1(error, code) {
64
+ function hasErrnoCode(error, code) {
81
65
  return error instanceof Error && error.code === code;
82
66
  }
83
67
  /**
@@ -104,7 +88,7 @@ async function listContractSpaceDirectories(projectMigrationsDir) {
104
88
  isDirectory: d.isDirectory()
105
89
  }));
106
90
  } catch (error) {
107
- if (hasErrnoCode$1(error, "ENOENT")) return [];
91
+ if (hasErrnoCode(error, "ENOENT")) return [];
108
92
  throw error;
109
93
  }
110
94
  const namedCandidates = entries.filter((e) => e.isDirectory).map((e) => e.name).filter((name) => !name.startsWith(".")).sort();
@@ -116,7 +100,7 @@ async function listContractSpaceDirectories(projectMigrationsDir) {
116
100
  isMigrationDir: true
117
101
  };
118
102
  } catch (error) {
119
- if (hasErrnoCode$1(error, "ENOENT")) return {
103
+ if (hasErrnoCode(error, "ENOENT")) return {
120
104
  name,
121
105
  isMigrationDir: false
122
106
  };
@@ -222,38 +206,6 @@ function verifyContractSpaces(inputs) {
222
206
  };
223
207
  }
224
208
  //#endregion
225
- //#region src/read-contract-space-contract.ts
226
- function hasErrnoCode(error, code) {
227
- return error instanceof Error && error.code === code;
228
- }
229
- /**
230
- * Read the on-disk contract value for a contract space
231
- * (`<projectMigrationsDir>/<spaceId>/contract.json`). Returns the parsed
232
- * JSON value as `unknown` — callers that need a typed contract validate
233
- * via their family's `deserializeContract` to surface schema issues.
234
- *
235
- * Companion to {@link import('./read-contract-space-head-ref').readContractSpaceHeadRef}
236
- * — same ENOENT-throws / corrupt-file-error semantics. Returns the
237
- * canonical-JSON value the framework wrote during emit, so re-running
238
- * this helper across machines / runs yields a byte-identical value.
239
- */
240
- async function readContractSpaceContract(projectMigrationsDir, spaceId) {
241
- assertValidSpaceId(spaceId);
242
- const filePath = join(projectMigrationsDir, spaceId, "contract.json");
243
- let raw;
244
- try {
245
- raw = await readFile(filePath, "utf-8");
246
- } catch (error) {
247
- if (hasErrnoCode(error, "ENOENT")) throw errorMissingFile("contract.json", join(projectMigrationsDir, spaceId));
248
- throw error;
249
- }
250
- try {
251
- return JSON.parse(raw);
252
- } catch (e) {
253
- throw errorInvalidJson(filePath, e instanceof Error ? e.message : String(e));
254
- }
255
- }
256
- //#endregion
257
- export { APP_SPACE_ID as a, spaceMigrationDirectory as c, readContractSpaceHeadRef as i, listContractSpaceDirectories as n, assertValidSpaceId as o, verifyContractSpaces as r, isValidSpaceId as s, readContractSpaceContract as t };
209
+ export { SPACE_REFS_DIRNAME as a, spaceMigrationDirectory as c, RESERVED_SPACE_SUBDIR_NAMES as i, spaceRefsDirectory as l, verifyContractSpaces as n, assertValidSpaceId as o, APP_SPACE_ID as r, isValidSpaceId as s, listContractSpaceDirectories as t };
258
210
 
259
- //# sourceMappingURL=read-contract-space-contract-Cglige7P.mjs.map
211
+ //# sourceMappingURL=verify-contract-spaces-DxXWxGR0.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-contract-spaces-DxXWxGR0.mjs","names":[],"sources":["../src/space-layout.ts","../src/verify-contract-spaces.ts"],"sourcesContent":["import { APP_SPACE_ID } from '@prisma-next/framework-components/control';\nimport { join } from 'pathe';\nimport { errorInvalidSpaceId } from './errors';\n\nexport { APP_SPACE_ID };\n\n/**\n * Branded string carrying a compile-time guarantee that the value has\n * been validated by {@link assertValidSpaceId}. Downstream filesystem\n * helpers (e.g. {@link spaceMigrationDirectory}) accept this type to\n * make \"validated\" tracking visible at the type level rather than\n * relying purely on a runtime check.\n */\nexport type ValidSpaceId = string & { readonly __brand: 'ValidSpaceId' };\n\n/**\n * Pattern a contract-space identifier must match. The constraint is\n * filesystem-friendly: lowercase letters / digits / hyphen / underscore,\n * starts with a letter, max 64 characters.\n */\nconst SPACE_ID_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;\n\nexport function isValidSpaceId(spaceId: string): spaceId is ValidSpaceId {\n return SPACE_ID_PATTERN.test(spaceId);\n}\n\nexport function assertValidSpaceId(spaceId: string): asserts spaceId is ValidSpaceId {\n if (!isValidSpaceId(spaceId)) {\n throw errorInvalidSpaceId(spaceId);\n }\n}\n\n/**\n * Resolve the migrations subdirectory for a given contract space.\n *\n * Every contract space — including the app space (default `'app'`) —\n * lands under `<projectMigrationsDir>/<spaceId>/`. The space id is\n * validated against {@link SPACE_ID_PATTERN} because it becomes a\n * filesystem directory name verbatim.\n *\n * `projectMigrationsDir` is the project's top-level `migrations/`\n * directory; the helper does not assume anything about its absolute /\n * relative shape and is symmetric with `pathe.join`.\n */\nexport function spaceMigrationDirectory(projectMigrationsDir: string, spaceId: string): string {\n assertValidSpaceId(spaceId);\n return join(projectMigrationsDir, spaceId);\n}\n\n/**\n * Per-space subdirectory name reserved for the ref store\n * (`migrations/<space>/<SPACE_REFS_DIRNAME>/*.json`). Single source of\n * truth: every helper that composes a per-space refs path imports this\n * constant, and the enumerator uses it (via\n * {@link RESERVED_SPACE_SUBDIR_NAMES}) to exclude reserved names from\n * the contract-space candidate list.\n */\nexport const SPACE_REFS_DIRNAME = 'refs';\n\n/**\n * Names reserved as per-space subdirectories of `migrations/<space>/`.\n * Used by the enumerator to filter contract-space candidates so a\n * reserved name (e.g. a top-level `migrations/refs/` left in the wrong\n * place) is never enumerated as a phantom contract space. Currently\n * holds `SPACE_REFS_DIRNAME`; extend if future per-space layouts add\n * more reserved subdirectories.\n */\nexport const RESERVED_SPACE_SUBDIR_NAMES: ReadonlySet<string> = new Set([SPACE_REFS_DIRNAME]);\n\n/**\n * Resolve the per-space refs directory for `spaceMigrationsDir`\n * (typically the value returned by {@link spaceMigrationDirectory}).\n * Composes the canonical {@link SPACE_REFS_DIRNAME} so callers do not\n * hard-code the literal.\n */\nexport function spaceRefsDirectory(spaceMigrationsDir: string): string {\n return join(spaceMigrationsDir, SPACE_REFS_DIRNAME);\n}\n","import { readdir, stat } from 'node:fs/promises';\nimport { join } from 'pathe';\nimport { MANIFEST_FILE } from './io';\nimport { APP_SPACE_ID } from './space-layout';\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\n/**\n * List the per-space subdirectories under\n * `<projectRoot>/migrations/`. Returns space-id directory names (sorted\n * alphabetically) — i.e. any non-dot-prefixed subdirectory whose root\n * does **not** contain a `migration.json` manifest. The manifest is the\n * structural marker of a user-authored migration directory (see\n * `readMigrationsDir` in `./io`); directory names themselves belong to\n * the user and are not part of the contract.\n *\n * Returns `[]` if the migrations directory does not exist (greenfield\n * project).\n *\n * Reads only the user's repo. **No descriptor import.** The caller\n * (verifier) feeds the result into {@link verifyContractSpaces} alongside\n * the loaded-space set and the marker rows.\n */\nexport async function listContractSpaceDirectories(\n projectMigrationsDir: string,\n): Promise<readonly string[]> {\n let entries: { readonly name: string; readonly isDirectory: boolean }[];\n try {\n const dirents = await readdir(projectMigrationsDir, { withFileTypes: true });\n entries = dirents.map((d) => ({ name: d.name, isDirectory: d.isDirectory() }));\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return [];\n }\n throw error;\n }\n\n const namedCandidates = entries\n .filter((e) => e.isDirectory)\n .map((e) => e.name)\n .filter((name) => !name.startsWith('.'))\n .sort();\n\n const manifestChecks = await Promise.all(\n namedCandidates.map(async (name) => {\n try {\n await stat(join(projectMigrationsDir, name, MANIFEST_FILE));\n return { name, isMigrationDir: true };\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return { name, isMigrationDir: false };\n }\n throw error;\n }\n }),\n );\n\n return manifestChecks.filter((c) => !c.isMigrationDir).map((c) => c.name);\n}\n\n/**\n * On-disk head value (`(hash, invariants)`) for one contract space.\n * The verifier compares this against the marker row for the same space\n * to detect drift between the user-emitted artefacts and the live DB\n * marker.\n */\nexport interface ContractSpaceHeadRecord {\n readonly hash: string;\n readonly invariants: readonly string[];\n}\n\n/**\n * Marker row read from `prisma_contract.marker` (one per `space`).\n * Caller resolves these via the family runtime's marker reader before\n * invoking {@link verifyContractSpaces}.\n */\nexport interface SpaceMarkerRecord {\n readonly hash: string;\n readonly invariants: readonly string[];\n}\n\nexport interface VerifyContractSpacesInputs {\n /**\n * Set of contract spaces the project declares: `'app'` plus each\n * extension space in `extensionPacks`. The caller's discovery path\n * never reads the extension descriptor module — it walks the\n * `extensionPacks` configuration in `prisma-next.config.ts` for the\n * space ids.\n */\n readonly loadedSpaces: ReadonlySet<string>;\n\n /**\n * Per-space subdirectories observed under\n * `<projectRoot>/migrations/`. Resolved via\n * {@link listContractSpaceDirectories}.\n */\n readonly spaceDirsOnDisk: readonly string[];\n\n /**\n * Head ref per space, keyed by space id. Caller reads\n * `<projectRoot>/migrations/<space-id>/contract.json` and\n * `<projectRoot>/migrations/<space-id>/refs/head.json` to construct\n * this map. Spaces with no contract-space dir on disk simply omit a\n * map entry.\n */\n readonly headRefsBySpace: ReadonlyMap<string, ContractSpaceHeadRecord>;\n\n /**\n * Marker rows keyed by `space`. Caller reads them from the\n * `prisma_contract.marker` table.\n */\n readonly markerRowsBySpace: ReadonlyMap<string, SpaceMarkerRecord>;\n}\n\nexport type SpaceVerifierViolation =\n | {\n readonly kind: 'declaredButUnmigrated';\n readonly spaceId: string;\n readonly remediation: string;\n }\n | {\n readonly kind: 'orphanMarker';\n readonly spaceId: string;\n readonly remediation: string;\n }\n | {\n readonly kind: 'orphanSpaceDir';\n readonly spaceId: string;\n readonly remediation: string;\n }\n | {\n readonly kind: 'hashMismatch';\n readonly spaceId: string;\n readonly priorHeadHash: string;\n readonly markerHash: string;\n readonly remediation: string;\n }\n | {\n readonly kind: 'invariantsMismatch';\n readonly spaceId: string;\n readonly onDiskInvariants: readonly string[];\n readonly markerInvariants: readonly string[];\n readonly remediation: string;\n };\n\nexport type VerifyContractSpacesResult =\n | { readonly ok: true }\n | { readonly ok: false; readonly violations: readonly SpaceVerifierViolation[] };\n\n/**\n * Pure structural verifier for the per-space mechanism. Aggregates the\n * three orphan / missing checks plus per-space hash and invariant\n * comparison.\n *\n * Algorithm:\n *\n * - For every extension space declared in `loadedSpaces` (`'app'`\n * excluded — the per-space verifier is scoped to extension members;\n * the app is verified through the aggregate path):\n * - If no contract-space dir on disk → `declaredButUnmigrated`.\n * - Else if `markerRowsBySpace` lacks an entry → no violation here;\n * the live-DB compare done outside this helper is where the\n * absence shows up.\n * - Else compare marker hash / invariants vs. on-disk head hash /\n * invariants → `hashMismatch` / `invariantsMismatch` on drift.\n * - For every contract-space dir on disk that is not in `loadedSpaces` →\n * `orphanSpaceDir`.\n * - For every marker row whose `space` is not in `loadedSpaces` →\n * `orphanMarker`. The app-space marker is always loaded (`'app'` is\n * in `loadedSpaces` by definition).\n *\n * Output is deterministic: violations are sorted first by `kind`\n * (`declaredButUnmigrated` → `orphanMarker` → `orphanSpaceDir` →\n * `hashMismatch` → `invariantsMismatch`) then by `spaceId`. Two callers\n * passing equivalent inputs see byte-identical violation lists.\n *\n * Synchronous, pure, no I/O. **Does not import the extension descriptor**\n * (the inputs are pre-resolved by the caller); the verifier reads only\n * the user repo, not `node_modules`.\n */\nexport function verifyContractSpaces(\n inputs: VerifyContractSpacesInputs,\n): VerifyContractSpacesResult {\n const violations: SpaceVerifierViolation[] = [];\n\n for (const spaceId of [...inputs.loadedSpaces].sort()) {\n if (spaceId === APP_SPACE_ID) continue;\n\n if (!inputs.spaceDirsOnDisk.includes(spaceId)) {\n violations.push({\n kind: 'declaredButUnmigrated',\n spaceId,\n remediation: `Extension '${spaceId}' is declared in extensionPacks but has not been emitted; run \\`prisma-next migrate\\`.`,\n });\n continue;\n }\n\n const head = inputs.headRefsBySpace.get(spaceId);\n const marker = inputs.markerRowsBySpace.get(spaceId);\n if (!head || !marker) {\n continue;\n }\n\n if (head.hash !== marker.hash) {\n violations.push({\n kind: 'hashMismatch',\n spaceId,\n priorHeadHash: head.hash,\n markerHash: marker.hash,\n remediation: `Marker row for space '${spaceId}' is keyed at ${marker.hash}, but the on-disk ${join('migrations', spaceId, 'contract.json')} resolves to ${head.hash}. Run \\`prisma-next db update\\` to advance the database, or \\`prisma-next migrate\\` if the descriptor was bumped without re-emitting.`,\n });\n continue;\n }\n\n const onDiskInvariants = [...head.invariants].sort();\n const markerInvariants = new Set(marker.invariants);\n const missing = onDiskInvariants.filter((id) => !markerInvariants.has(id));\n if (missing.length > 0) {\n violations.push({\n kind: 'invariantsMismatch',\n spaceId,\n onDiskInvariants,\n markerInvariants: [...marker.invariants].sort(),\n remediation: `Marker row for space '${spaceId}' is missing invariants [${missing.map((s) => JSON.stringify(s)).join(', ')}]. Run \\`prisma-next db update\\` to apply the corresponding data-transform migrations.`,\n });\n }\n }\n\n for (const dir of [...inputs.spaceDirsOnDisk].sort()) {\n if (!inputs.loadedSpaces.has(dir)) {\n violations.push({\n kind: 'orphanSpaceDir',\n spaceId: dir,\n remediation: `Orphan contract-space directory \\`${join('migrations', dir)}/\\` for an extension not in extensionPacks; remove the directory or re-add the extension.`,\n });\n }\n }\n\n for (const space of [...inputs.markerRowsBySpace.keys()].sort()) {\n if (!inputs.loadedSpaces.has(space)) {\n violations.push({\n kind: 'orphanMarker',\n spaceId: space,\n remediation: `Orphan marker row for space '${space}' (no longer in extensionPacks); remediation: manually delete the row from \\`prisma_contract.marker\\`.`,\n });\n }\n }\n\n if (violations.length === 0) {\n return { ok: true };\n }\n\n const kindOrder: Record<SpaceVerifierViolation['kind'], number> = {\n declaredButUnmigrated: 0,\n orphanMarker: 1,\n orphanSpaceDir: 2,\n hashMismatch: 3,\n invariantsMismatch: 4,\n };\n\n violations.sort((a, b) => {\n const k = kindOrder[a.kind] - kindOrder[b.kind];\n if (k !== 0) return k;\n if (a.spaceId < b.spaceId) return -1;\n if (a.spaceId > b.spaceId) return 1;\n return 0;\n });\n\n return { ok: false, violations };\n}\n"],"mappings":"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"}