@prisma-next/migration-tools 0.11.0-dev.12 → 0.11.0-dev.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"ref-resolution.d.mts","names":[],"sources":["../../src/refs/types.ts","../../src/refs/contract-ref.ts","../../src/refs/migration-ref.ts"],"mappings":";;;;;;UAIiB,oBAAA;EAAA,SACN,KAAA,EAAO,cAAA;EAAA,SACP,IAAA,EAAM,IAAA;AAAA;AAAA,KAGL,qBAAA;EAAA,SACG,IAAA;EAAA,SAAuB,KAAA;AAAA;EAAA,SACvB,IAAA;EAAA,SAAsB,OAAA;AAAA;EAAA,SACtB,IAAA;EAAA,SAA+B,OAAA;AAAA;EAAA,SAC/B,IAAA;EAAA,SAAiC,OAAA;AAAA;;UAG/B,WAAA;EAAA,SACN,IAAA;EAAA,SACA,UAAA,EAAY,qBAAA;AAAA;AAAA,KAGX,sBAAA;EAAA,SACG,IAAA;EAAA,SAA2B,OAAA;AAAA;EAAA,SAC3B,IAAA;EAAA,SAAuB,KAAA;AAAA;;UAGrB,YAAA;EAAA,SACN,OAAA;EAAA,SACA,aAAA;EAAA,SACA,IAAA;EAAA,SACA,EAAA;EAAA,SACA,UAAA,EAAY,sBAAA;AAAA;AAAA,UAGN,qBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,OAAA;AAAA;AAAA,UAGM,sBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA;AAAA;AAAA,UAGM,yBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,eAAA;EAAA,SACA,OAAA;EAAA,SACA,GAAA;AAAA;AAAA,UAGM,0BAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,MAAA;AAAA;AAAA,KAGC,kBAAA,GACR,qBAAA,GACA,sBAAA,GACA,yBAAA,GACA,0BAAA;AAAA,iBAiBY,iBAAA,CACd,KAAA,EAAO,cAAA,EACP,OAAA,WACC,aAAA;;;;;;AAjFH;;;;;;;;iBCkBgB,gBAAA,CACd,KAAA,UACA,GAAA,EAAK,oBAAA,GACJ,MAAA,CAAO,WAAA,EAAa,kBAAA;;;;;;ADrBvB;;;;;;;;;iBEcgB,iBAAA,CACd,KAAA,UACA,GAAA,EAAK,oBAAA,GACJ,MAAA,CAAO,YAAA,EAAc,kBAAA"}
1
+ {"version":3,"file":"ref-resolution.d.mts","names":[],"sources":["../../src/refs/types.ts","../../src/refs/contract-ref.ts","../../src/refs/migration-ref.ts"],"mappings":";;;;;;UAIiB,oBAAA;EAAA,SACN,KAAA,EAAO,cAAA;EAAA,SACP,IAAA,EAAM,IAAI;AAAA;AAAA,KAGT,qBAAA;EAAA,SACG,IAAA;EAAA,SAAuB,KAAA;AAAA;EAAA,SACvB,IAAA;EAAA,SAAsB,OAAA;AAAA;EAAA,SACtB,IAAA;EAAA,SAA+B,OAAA;AAAA;EAAA,SAC/B,IAAA;EAAA,SAAiC,OAAA;AAAA;;UAG/B,WAAA;EAAA,SACN,IAAA;EAAA,SACA,UAAA,EAAY,qBAAqB;AAAA;AAAA,KAGhC,sBAAA;EAAA,SACG,IAAA;EAAA,SAA2B,OAAA;AAAA;EAAA,SAC3B,IAAA;EAAA,SAAuB,KAAA;AAAA;;UAGrB,YAAA;EAAA,SACN,OAAA;EAAA,SACA,aAAA;EAAA,SACA,IAAA;EAAA,SACA,EAAA;EAAA,SACA,UAAA,EAAY,sBAAsB;AAAA;AAAA,UAG5B,qBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,OAAA;AAAA;AAAA,UAGM,sBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,UAAA;EAAA,SACA,OAAA;AAAA;AAAA,UAGM,yBAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,eAAA;EAAA,SACA,OAAA;EAAA,SACA,GAAA;AAAA;AAAA,UAGM,0BAAA;EAAA,SACN,IAAA;EAAA,SACA,KAAA;EAAA,SACA,MAAA;AAAA;AAAA,KAGC,kBAAA,GACR,qBAAA,GACA,sBAAA,GACA,yBAAA,GACA,0BAAA;AAAA,iBAiBY,iBAAA,CACd,KAAA,EAAO,cAAA,EACP,OAAA,WACC,aAAa;;;;;;AAjFhB;;;;;;;;iBCkBgB,gBAAA,CACd,KAAA,UACA,GAAA,EAAK,oBAAA,GACJ,MAAA,CAAO,WAAA,EAAa,kBAAA;;;;;;ADrBvB;;;;;;;;;iBEcgB,iBAAA,CACd,KAAA,UACA,GAAA,EAAK,oBAAA,GACJ,MAAA,CAAO,YAAA,EAAc,kBAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"ref-resolution.mjs","names":[],"sources":["../../src/refs/types.ts","../../src/refs/contract-ref.ts","../../src/refs/migration-ref.ts"],"sourcesContent":["import type { MigrationEdge, MigrationGraph } from '../graph';\nimport type { Refs } from '../refs';\n\n/** Context required to resolve a contract or migration reference. */\nexport interface RefResolutionContext {\n readonly graph: MigrationGraph;\n readonly refs: Refs;\n}\n\nexport type ContractRefProvenance =\n | { readonly kind: 'hash'; readonly input: string }\n | { readonly kind: 'ref'; readonly refName: string }\n | { readonly kind: 'migration-to'; readonly dirName: string }\n | { readonly kind: 'migration-from'; readonly dirName: string };\n\n/** A resolved contract reference: the target hash and how it was derived. */\nexport interface ContractRef {\n readonly hash: string;\n readonly provenance: ContractRefProvenance;\n}\n\nexport type MigrationRefProvenance =\n | { readonly kind: 'dir-name'; readonly dirName: string }\n | { readonly kind: 'hash'; readonly input: string };\n\n/** A resolved migration reference. */\nexport interface MigrationRef {\n readonly dirName: string;\n readonly migrationHash: string;\n readonly from: string;\n readonly to: string;\n readonly provenance: MigrationRefProvenance;\n}\n\nexport interface RefResolutionNotFound {\n readonly kind: 'not-found';\n readonly input: string;\n readonly grammar: 'contract' | 'migration';\n}\n\nexport interface RefResolutionAmbiguous {\n readonly kind: 'ambiguous';\n readonly input: string;\n readonly candidates: readonly string[];\n readonly grammar: 'contract' | 'migration';\n}\n\nexport interface RefResolutionWrongGrammar {\n readonly kind: 'wrong-grammar';\n readonly input: string;\n readonly expectedGrammar: 'contract' | 'migration';\n readonly message: string;\n readonly fix: string;\n}\n\nexport interface RefResolutionInvalidFormat {\n readonly kind: 'invalid-format';\n readonly input: string;\n readonly reason: string;\n}\n\nexport type RefResolutionError =\n | RefResolutionNotFound\n | RefResolutionAmbiguous\n | RefResolutionWrongGrammar\n | RefResolutionInvalidFormat;\n\nconst FULL_HASH_PATTERN = /^sha256:([0-9a-f]{64}|empty)$/;\nconst HEX_PREFIX_PATTERN = /^(sha256:)?[0-9a-f]{6,}$/;\n\nexport function isFullHash(input: string): boolean {\n return FULL_HASH_PATTERN.test(input);\n}\n\nexport function isHexPrefix(input: string): boolean {\n return HEX_PREFIX_PATTERN.test(input);\n}\n\nexport function normalizeHashPrefix(input: string): string {\n return input.startsWith('sha256:') ? input : `sha256:${input}`;\n}\n\nexport function findEdgeByDirName(\n graph: MigrationGraph,\n dirName: string,\n): MigrationEdge | undefined {\n for (const edges of graph.forwardChain.values()) {\n for (const edge of edges) {\n if (edge.dirName === dirName) return edge;\n }\n }\n return undefined;\n}\n","import type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { validateRefName } from '../refs';\nimport type {\n ContractRef,\n ContractRefProvenance,\n RefResolutionContext,\n RefResolutionError,\n} from './types';\nimport { findEdgeByDirName, isFullHash, isHexPrefix, normalizeHashPrefix } from './types';\n\n/**\n * Resolve a user-supplied string to a contract hash using the unified\n * contract-reference grammar.\n *\n * Accepted forms:\n * - Full storage hash (`sha256:<64 hex>` or `sha256:empty`)\n * - Hex prefix (6+ hex chars, must uniquely identify one contract)\n * - Ref name (looked up in the refs index)\n * - Migration directory name (resolves to the migration's `to`-contract)\n * - `<dir>^` (resolves to the migration's `from`-contract)\n */\nexport function parseContractRef(\n input: string,\n ctx: RefResolutionContext,\n): Result<ContractRef, RefResolutionError> {\n if (!input) {\n return notOk({ kind: 'invalid-format', input, reason: 'Reference cannot be empty' });\n }\n\n if (isFullHash(input)) {\n if (ctx.graph.nodes.has(input)) {\n return ok({ hash: input, provenance: { kind: 'hash', input } });\n }\n return notOk({ kind: 'not-found', input, grammar: 'contract' });\n }\n\n if (input.endsWith('^')) {\n const dirName = input.slice(0, -1);\n if (!dirName) {\n return notOk({ kind: 'invalid-format', input, reason: 'Missing directory name before ^' });\n }\n const edge = findEdgeByDirName(ctx.graph, dirName);\n if (edge) {\n return ok({ hash: edge.from, provenance: { kind: 'migration-from', dirName } });\n }\n return notOk({ kind: 'not-found', input, grammar: 'contract' });\n }\n\n type Candidate = { hash: string; provenance: ContractRefProvenance; label: string };\n const candidates: Candidate[] = [];\n\n if (validateRefName(input) && Object.hasOwn(ctx.refs, input)) {\n const ref = ctx.refs[input];\n if (ref) {\n candidates.push({\n hash: ref.hash,\n provenance: { kind: 'ref', refName: input },\n label: `ref \"${input}\"`,\n });\n }\n }\n\n const edge = findEdgeByDirName(ctx.graph, input);\n if (edge) {\n candidates.push({\n hash: edge.to,\n provenance: { kind: 'migration-to', dirName: input },\n label: `migration directory \"${input}\"`,\n });\n }\n\n if (isHexPrefix(input)) {\n const prefix = normalizeHashPrefix(input);\n const matches = [...ctx.graph.nodes].filter((n) => n.startsWith(prefix));\n const [firstMatch] = matches;\n if (matches.length === 1 && firstMatch !== undefined) {\n candidates.push({\n hash: firstMatch,\n provenance: { kind: 'hash', input },\n label: `hash prefix \"${input}\"`,\n });\n } else if (matches.length > 1) {\n return notOk({ kind: 'ambiguous', input, candidates: matches, grammar: 'contract' });\n }\n }\n\n const [firstCandidate] = candidates;\n if (candidates.length === 1 && firstCandidate !== undefined) {\n return ok({ hash: firstCandidate.hash, provenance: firstCandidate.provenance });\n }\n\n if (candidates.length > 1) {\n return notOk({\n kind: 'ambiguous',\n input,\n candidates: candidates.map((c) => c.label),\n grammar: 'contract',\n });\n }\n\n return notOk({ kind: 'not-found', input, grammar: 'contract' });\n}\n","import type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { validateRefName } from '../refs';\nimport type { MigrationRef, RefResolutionContext, RefResolutionError } from './types';\nimport { findEdgeByDirName, isFullHash, isHexPrefix, normalizeHashPrefix } from './types';\n\n/**\n * Resolve a user-supplied string to a migration using the migration-reference\n * grammar.\n *\n * Accepted forms:\n * - Migration directory name (e.g. `20260101-add-users`)\n * - Migration hash (full or 6+ hex prefix)\n *\n * Wrong-grammar diagnostics are produced when the input matches a\n * contract-grammar form (ref name, `<dir>^`, contract-only hash) so the\n * user gets a targeted hint rather than a generic \"not found\".\n */\nexport function parseMigrationRef(\n input: string,\n ctx: RefResolutionContext,\n): Result<MigrationRef, RefResolutionError> {\n if (!input) {\n return notOk({ kind: 'invalid-format', input, reason: 'Reference cannot be empty' });\n }\n\n if (input.endsWith('^')) {\n return notOk({\n kind: 'wrong-grammar',\n input,\n expectedGrammar: 'migration',\n message: '`^` syntax addresses contracts, not migrations',\n fix: 'Pass the migration directory name without `^`, or use a contract-accepting flag like `--to` or `--from`.',\n });\n }\n\n if (validateRefName(input) && Object.hasOwn(ctx.refs, input)) {\n return notOk({\n kind: 'wrong-grammar',\n input,\n expectedGrammar: 'migration',\n message: `\"${input}\" is a ref name, not a migration`,\n fix: 'Refs point at contracts, not migrations. Use a migration directory name or migration hash.',\n });\n }\n\n const edge = findEdgeByDirName(ctx.graph, input);\n if (edge) {\n return ok({\n dirName: edge.dirName,\n migrationHash: edge.migrationHash,\n from: edge.from,\n to: edge.to,\n provenance: { kind: 'dir-name', dirName: input },\n });\n }\n\n if (isFullHash(input)) {\n const migEdge = ctx.graph.migrationByHash.get(input);\n if (migEdge) {\n return ok({\n dirName: migEdge.dirName,\n migrationHash: migEdge.migrationHash,\n from: migEdge.from,\n to: migEdge.to,\n provenance: { kind: 'hash', input },\n });\n }\n if (ctx.graph.nodes.has(input)) {\n return notOk({\n kind: 'wrong-grammar',\n input,\n expectedGrammar: 'migration',\n message: 'Hash matched a contract but not a migration',\n fix: 'Use a contract-accepting flag like `--to` or `--from` to reference contracts by hash. Pass `migration show <dir>` for a specific migration.',\n });\n }\n return notOk({ kind: 'not-found', input, grammar: 'migration' });\n }\n\n if (isHexPrefix(input)) {\n const prefix = normalizeHashPrefix(input);\n const migMatches = [...ctx.graph.migrationByHash.entries()].filter(([hash]) =>\n hash.startsWith(prefix),\n );\n\n const [firstMigMatch] = migMatches;\n if (migMatches.length === 1 && firstMigMatch !== undefined) {\n const [, matchedEdge] = firstMigMatch;\n return ok({\n dirName: matchedEdge.dirName,\n migrationHash: matchedEdge.migrationHash,\n from: matchedEdge.from,\n to: matchedEdge.to,\n provenance: { kind: 'hash', input },\n });\n }\n\n if (migMatches.length > 1) {\n return notOk({\n kind: 'ambiguous',\n input,\n candidates: migMatches.map(([hash]) => hash),\n grammar: 'migration',\n });\n }\n\n const contractMatches = [...ctx.graph.nodes].filter((n) => n.startsWith(prefix));\n if (contractMatches.length > 0) {\n return notOk({\n kind: 'wrong-grammar',\n input,\n expectedGrammar: 'migration',\n message: 'Hash matched a contract but not a migration',\n fix: 'Use a contract-accepting flag like `--to` or `--from` to reference contracts by hash. Pass `migration show <dir>` for a specific migration.',\n });\n }\n }\n\n return notOk({ kind: 'not-found', input, grammar: 'migration' });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ref-resolution.mjs","names":[],"sources":["../../src/refs/types.ts","../../src/refs/contract-ref.ts","../../src/refs/migration-ref.ts"],"sourcesContent":["import type { MigrationEdge, MigrationGraph } from '../graph';\nimport type { Refs } from '../refs';\n\n/** Context required to resolve a contract or migration reference. */\nexport interface RefResolutionContext {\n readonly graph: MigrationGraph;\n readonly refs: Refs;\n}\n\nexport type ContractRefProvenance =\n | { readonly kind: 'hash'; readonly input: string }\n | { readonly kind: 'ref'; readonly refName: string }\n | { readonly kind: 'migration-to'; readonly dirName: string }\n | { readonly kind: 'migration-from'; readonly dirName: string };\n\n/** A resolved contract reference: the target hash and how it was derived. */\nexport interface ContractRef {\n readonly hash: string;\n readonly provenance: ContractRefProvenance;\n}\n\nexport type MigrationRefProvenance =\n | { readonly kind: 'dir-name'; readonly dirName: string }\n | { readonly kind: 'hash'; readonly input: string };\n\n/** A resolved migration reference. */\nexport interface MigrationRef {\n readonly dirName: string;\n readonly migrationHash: string;\n readonly from: string;\n readonly to: string;\n readonly provenance: MigrationRefProvenance;\n}\n\nexport interface RefResolutionNotFound {\n readonly kind: 'not-found';\n readonly input: string;\n readonly grammar: 'contract' | 'migration';\n}\n\nexport interface RefResolutionAmbiguous {\n readonly kind: 'ambiguous';\n readonly input: string;\n readonly candidates: readonly string[];\n readonly grammar: 'contract' | 'migration';\n}\n\nexport interface RefResolutionWrongGrammar {\n readonly kind: 'wrong-grammar';\n readonly input: string;\n readonly expectedGrammar: 'contract' | 'migration';\n readonly message: string;\n readonly fix: string;\n}\n\nexport interface RefResolutionInvalidFormat {\n readonly kind: 'invalid-format';\n readonly input: string;\n readonly reason: string;\n}\n\nexport type RefResolutionError =\n | RefResolutionNotFound\n | RefResolutionAmbiguous\n | RefResolutionWrongGrammar\n | RefResolutionInvalidFormat;\n\nconst FULL_HASH_PATTERN = /^sha256:([0-9a-f]{64}|empty)$/;\nconst HEX_PREFIX_PATTERN = /^(sha256:)?[0-9a-f]{6,}$/;\n\nexport function isFullHash(input: string): boolean {\n return FULL_HASH_PATTERN.test(input);\n}\n\nexport function isHexPrefix(input: string): boolean {\n return HEX_PREFIX_PATTERN.test(input);\n}\n\nexport function normalizeHashPrefix(input: string): string {\n return input.startsWith('sha256:') ? input : `sha256:${input}`;\n}\n\nexport function findEdgeByDirName(\n graph: MigrationGraph,\n dirName: string,\n): MigrationEdge | undefined {\n for (const edges of graph.forwardChain.values()) {\n for (const edge of edges) {\n if (edge.dirName === dirName) return edge;\n }\n }\n return undefined;\n}\n","import type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { validateRefName } from '../refs';\nimport type {\n ContractRef,\n ContractRefProvenance,\n RefResolutionContext,\n RefResolutionError,\n} from './types';\nimport { findEdgeByDirName, isFullHash, isHexPrefix, normalizeHashPrefix } from './types';\n\n/**\n * Resolve a user-supplied string to a contract hash using the unified\n * contract-reference grammar.\n *\n * Accepted forms:\n * - Full storage hash (`sha256:<64 hex>` or `sha256:empty`)\n * - Hex prefix (6+ hex chars, must uniquely identify one contract)\n * - Ref name (looked up in the refs index)\n * - Migration directory name (resolves to the migration's `to`-contract)\n * - `<dir>^` (resolves to the migration's `from`-contract)\n */\nexport function parseContractRef(\n input: string,\n ctx: RefResolutionContext,\n): Result<ContractRef, RefResolutionError> {\n if (!input) {\n return notOk({ kind: 'invalid-format', input, reason: 'Reference cannot be empty' });\n }\n\n if (isFullHash(input)) {\n if (ctx.graph.nodes.has(input)) {\n return ok({ hash: input, provenance: { kind: 'hash', input } });\n }\n return notOk({ kind: 'not-found', input, grammar: 'contract' });\n }\n\n if (input.endsWith('^')) {\n const dirName = input.slice(0, -1);\n if (!dirName) {\n return notOk({ kind: 'invalid-format', input, reason: 'Missing directory name before ^' });\n }\n const edge = findEdgeByDirName(ctx.graph, dirName);\n if (edge) {\n return ok({ hash: edge.from, provenance: { kind: 'migration-from', dirName } });\n }\n return notOk({ kind: 'not-found', input, grammar: 'contract' });\n }\n\n type Candidate = { hash: string; provenance: ContractRefProvenance; label: string };\n const candidates: Candidate[] = [];\n\n if (validateRefName(input) && Object.hasOwn(ctx.refs, input)) {\n const ref = ctx.refs[input];\n if (ref) {\n candidates.push({\n hash: ref.hash,\n provenance: { kind: 'ref', refName: input },\n label: `ref \"${input}\"`,\n });\n }\n }\n\n const edge = findEdgeByDirName(ctx.graph, input);\n if (edge) {\n candidates.push({\n hash: edge.to,\n provenance: { kind: 'migration-to', dirName: input },\n label: `migration directory \"${input}\"`,\n });\n }\n\n if (isHexPrefix(input)) {\n const prefix = normalizeHashPrefix(input);\n const matches = [...ctx.graph.nodes].filter((n) => n.startsWith(prefix));\n const [firstMatch] = matches;\n if (matches.length === 1 && firstMatch !== undefined) {\n candidates.push({\n hash: firstMatch,\n provenance: { kind: 'hash', input },\n label: `hash prefix \"${input}\"`,\n });\n } else if (matches.length > 1) {\n return notOk({ kind: 'ambiguous', input, candidates: matches, grammar: 'contract' });\n }\n }\n\n const [firstCandidate] = candidates;\n if (candidates.length === 1 && firstCandidate !== undefined) {\n return ok({ hash: firstCandidate.hash, provenance: firstCandidate.provenance });\n }\n\n if (candidates.length > 1) {\n return notOk({\n kind: 'ambiguous',\n input,\n candidates: candidates.map((c) => c.label),\n grammar: 'contract',\n });\n }\n\n return notOk({ kind: 'not-found', input, grammar: 'contract' });\n}\n","import type { Result } from '@prisma-next/utils/result';\nimport { notOk, ok } from '@prisma-next/utils/result';\nimport { validateRefName } from '../refs';\nimport type { MigrationRef, RefResolutionContext, RefResolutionError } from './types';\nimport { findEdgeByDirName, isFullHash, isHexPrefix, normalizeHashPrefix } from './types';\n\n/**\n * Resolve a user-supplied string to a migration using the migration-reference\n * grammar.\n *\n * Accepted forms:\n * - Migration directory name (e.g. `20260101-add-users`)\n * - Migration hash (full or 6+ hex prefix)\n *\n * Wrong-grammar diagnostics are produced when the input matches a\n * contract-grammar form (ref name, `<dir>^`, contract-only hash) so the\n * user gets a targeted hint rather than a generic \"not found\".\n */\nexport function parseMigrationRef(\n input: string,\n ctx: RefResolutionContext,\n): Result<MigrationRef, RefResolutionError> {\n if (!input) {\n return notOk({ kind: 'invalid-format', input, reason: 'Reference cannot be empty' });\n }\n\n if (input.endsWith('^')) {\n return notOk({\n kind: 'wrong-grammar',\n input,\n expectedGrammar: 'migration',\n message: '`^` syntax addresses contracts, not migrations',\n fix: 'Pass the migration directory name without `^`, or use a contract-accepting flag like `--to` or `--from`.',\n });\n }\n\n if (validateRefName(input) && Object.hasOwn(ctx.refs, input)) {\n return notOk({\n kind: 'wrong-grammar',\n input,\n expectedGrammar: 'migration',\n message: `\"${input}\" is a ref name, not a migration`,\n fix: 'Refs point at contracts, not migrations. Use a migration directory name or migration hash.',\n });\n }\n\n const edge = findEdgeByDirName(ctx.graph, input);\n if (edge) {\n return ok({\n dirName: edge.dirName,\n migrationHash: edge.migrationHash,\n from: edge.from,\n to: edge.to,\n provenance: { kind: 'dir-name', dirName: input },\n });\n }\n\n if (isFullHash(input)) {\n const migEdge = ctx.graph.migrationByHash.get(input);\n if (migEdge) {\n return ok({\n dirName: migEdge.dirName,\n migrationHash: migEdge.migrationHash,\n from: migEdge.from,\n to: migEdge.to,\n provenance: { kind: 'hash', input },\n });\n }\n if (ctx.graph.nodes.has(input)) {\n return notOk({\n kind: 'wrong-grammar',\n input,\n expectedGrammar: 'migration',\n message: 'Hash matched a contract but not a migration',\n fix: 'Use a contract-accepting flag like `--to` or `--from` to reference contracts by hash. Pass `migration show <dir>` for a specific migration.',\n });\n }\n return notOk({ kind: 'not-found', input, grammar: 'migration' });\n }\n\n if (isHexPrefix(input)) {\n const prefix = normalizeHashPrefix(input);\n const migMatches = [...ctx.graph.migrationByHash.entries()].filter(([hash]) =>\n hash.startsWith(prefix),\n );\n\n const [firstMigMatch] = migMatches;\n if (migMatches.length === 1 && firstMigMatch !== undefined) {\n const [, matchedEdge] = firstMigMatch;\n return ok({\n dirName: matchedEdge.dirName,\n migrationHash: matchedEdge.migrationHash,\n from: matchedEdge.from,\n to: matchedEdge.to,\n provenance: { kind: 'hash', input },\n });\n }\n\n if (migMatches.length > 1) {\n return notOk({\n kind: 'ambiguous',\n input,\n candidates: migMatches.map(([hash]) => hash),\n grammar: 'migration',\n });\n }\n\n const contractMatches = [...ctx.graph.nodes].filter((n) => n.startsWith(prefix));\n if (contractMatches.length > 0) {\n return notOk({\n kind: 'wrong-grammar',\n input,\n expectedGrammar: 'migration',\n message: 'Hash matched a contract but not a migration',\n fix: 'Use a contract-accepting flag like `--to` or `--from` to reference contracts by hash. Pass `migration show <dir>` for a specific migration.',\n });\n }\n }\n\n return notOk({ kind: 'not-found', input, grammar: 'migration' });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"refs.d.mts","names":[],"sources":["../../src/refs/snapshot.ts"],"mappings":";;;UAQiB,UAAA;EAAA,SACN,QAAA;EAAA,SACA,WAAA;AAAA;AAAA,iBAgEW,gBAAA,CACpB,OAAA,UACA,IAAA,UACA,QAAA,EAAU,UAAA,GACT,OAAA;AAAA,iBA4BmB,eAAA,CAAgB,OAAA,UAAiB,IAAA,WAAe,OAAA,CAAQ,UAAA;AAAA,iBAiCxD,iBAAA,CAAkB,OAAA,UAAiB,IAAA,WAAe,OAAA;AAAA,iBASlD,cAAA,CACpB,OAAA,UACA,IAAA,UACA,KAAA,EAAO,QAAA,EACP,QAAA,EAAU,UAAA,GACT,OAAA;AAAA,iBA4BmB,eAAA,CAAgB,OAAA,UAAiB,IAAA,WAAe,OAAA"}
1
+ {"version":3,"file":"refs.d.mts","names":[],"sources":["../../src/refs/snapshot.ts"],"mappings":";;;UAQiB,UAAA;EAAA,SACN,QAAA;EAAA,SACA,WAAW;AAAA;AAAA,iBAgEA,gBAAA,CACpB,OAAA,UACA,IAAA,UACA,QAAA,EAAU,UAAA,GACT,OAAO;AAAA,iBA4BY,eAAA,CAAgB,OAAA,UAAiB,IAAA,WAAe,OAAO,CAAC,UAAA;AAAA,iBAiCxD,iBAAA,CAAkB,OAAA,UAAiB,IAAA,WAAe,OAAO;AAAA,iBASzD,cAAA,CACpB,OAAA,UACA,IAAA,UACA,KAAA,EAAO,QAAA,EACP,QAAA,EAAU,UAAA,GACT,OAAA;AAAA,iBA4BmB,eAAA,CAAgB,OAAA,UAAiB,IAAA,WAAe,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"refs.mjs","names":[],"sources":["../../src/refs/snapshot.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport { access, mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';\nimport { canonicalizeJson } from '@prisma-next/framework-components/utils';\nimport { type } from 'arktype';\nimport { basename, dirname, join } from 'pathe';\nimport { errorInvalidRefFile, errorInvalidRefName, MigrationToolsError } from '../errors';\nimport { deleteRef, type RefEntry, validateRefName, writeRef } from '../refs';\n\nexport interface ContractIR {\n readonly contract: unknown;\n readonly contractDts: string;\n}\n\nconst ContractIrSchema = type({\n targetFamily: 'string',\n target: 'string',\n profileHash: 'string',\n storage: type({\n storageHash: 'string',\n }),\n models: 'object',\n});\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\nfunction snapshotJsonPath(refsDir: string, name: string): string {\n return join(refsDir, `${name}.contract.json`);\n}\n\nfunction snapshotDtsPath(refsDir: string, name: string): string {\n return join(refsDir, `${name}.contract.d.ts`);\n}\n\nfunction tmpPathFor(finalPath: string): string {\n const dir = dirname(finalPath);\n const fileName = basename(finalPath);\n return join(dir, `.${fileName}.${Date.now()}-${randomBytes(4).toString('hex')}.tmp`);\n}\n\nasync function atomicWriteFile(finalPath: string, content: string): Promise<void> {\n const dir = dirname(finalPath);\n await mkdir(dir, { recursive: true });\n const tmpPath = tmpPathFor(finalPath);\n await writeFile(tmpPath, content);\n await rename(tmpPath, finalPath);\n}\n\nasync function unlinkIfExists(filePath: string): Promise<void> {\n try {\n await unlink(filePath);\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nfunction parseContractSnapshotJson(filePath: string, raw: string): unknown {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw errorInvalidRefFile(filePath, 'Failed to parse as JSON');\n }\n\n const result = ContractIrSchema(parsed);\n if (result instanceof type.errors) {\n throw errorInvalidRefFile(filePath, result.summary);\n }\n\n return result;\n}\n\nexport async function writeRefSnapshot(\n refsDir: string,\n name: string,\n snapshot: ContractIR,\n): Promise<void> {\n if (!validateRefName(name)) {\n throw errorInvalidRefName(name);\n }\n\n const jsonPath = snapshotJsonPath(refsDir, name);\n const dtsPath = snapshotDtsPath(refsDir, name);\n const jsonContent = `${canonicalizeJson(snapshot.contract)}\\n`;\n const dtsContent = snapshot.contractDts.endsWith('\\n')\n ? snapshot.contractDts\n : `${snapshot.contractDts}\\n`;\n\n try {\n await atomicWriteFile(jsonPath, jsonContent);\n } catch (error) {\n await unlinkIfExists(jsonPath);\n throw error;\n }\n\n try {\n await atomicWriteFile(dtsPath, dtsContent);\n } catch (error) {\n await unlinkIfExists(jsonPath);\n await unlinkIfExists(dtsPath);\n throw error;\n }\n}\n\nexport async function readRefSnapshot(refsDir: string, name: string): Promise<ContractIR | null> {\n if (!validateRefName(name)) {\n throw errorInvalidRefName(name);\n }\n\n const jsonPath = snapshotJsonPath(refsDir, name);\n const dtsPath = snapshotDtsPath(refsDir, name);\n\n let raw: string;\n try {\n raw = await readFile(jsonPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return null;\n }\n throw error;\n }\n\n const contract = parseContractSnapshotJson(jsonPath, raw);\n\n let contractDts: string;\n try {\n contractDts = await readFile(dtsPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorInvalidRefFile(dtsPath, 'Missing paired contract.d.ts snapshot file');\n }\n throw error;\n }\n\n return { contract, contractDts };\n}\n\nexport async function deleteRefSnapshot(refsDir: string, name: string): Promise<void> {\n if (!validateRefName(name)) {\n throw errorInvalidRefName(name);\n }\n\n await unlinkIfExists(snapshotJsonPath(refsDir, name));\n await unlinkIfExists(snapshotDtsPath(refsDir, name));\n}\n\nexport async function writeRefPaired(\n refsDir: string,\n name: string,\n entry: RefEntry,\n snapshot: ContractIR,\n): Promise<void> {\n await writeRefSnapshot(refsDir, name, snapshot);\n try {\n await writeRef(refsDir, name, entry);\n } catch (writeError) {\n try {\n await deleteRefSnapshot(refsDir, name);\n } catch {\n // Rollback failure is secondary; preserve the original writeRef error.\n }\n throw writeError;\n }\n}\n\nfunction isUnknownRefError(error: unknown): boolean {\n return MigrationToolsError.is(error) && error.code === 'MIGRATION.UNKNOWN_REF';\n}\n\nasync function snapshotFilesExist(refsDir: string, name: string): Promise<boolean> {\n if (!validateRefName(name)) {\n throw errorInvalidRefName(name);\n }\n\n const paths = [snapshotJsonPath(refsDir, name), snapshotDtsPath(refsDir, name)];\n const checks = await Promise.allSettled(paths.map((filePath) => access(filePath)));\n return checks.some((result) => result.status === 'fulfilled');\n}\n\nexport async function deleteRefPaired(refsDir: string, name: string): Promise<void> {\n if (await snapshotFilesExist(refsDir, name)) {\n try {\n await deleteRef(refsDir, name);\n } catch (error) {\n if (!isUnknownRefError(error)) {\n throw error;\n }\n }\n await deleteRefSnapshot(refsDir, name);\n return;\n }\n\n await deleteRef(refsDir, name);\n await deleteRefSnapshot(refsDir, name);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"refs.mjs","names":[],"sources":["../../src/refs/snapshot.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport { access, mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';\nimport { canonicalizeJson } from '@prisma-next/framework-components/utils';\nimport { type } from 'arktype';\nimport { basename, dirname, join } from 'pathe';\nimport { errorInvalidRefFile, errorInvalidRefName, MigrationToolsError } from '../errors';\nimport { deleteRef, type RefEntry, validateRefName, writeRef } from '../refs';\n\nexport interface ContractIR {\n readonly contract: unknown;\n readonly contractDts: string;\n}\n\nconst ContractIrSchema = type({\n targetFamily: 'string',\n target: 'string',\n profileHash: 'string',\n storage: type({\n storageHash: 'string',\n }),\n models: 'object',\n});\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\nfunction snapshotJsonPath(refsDir: string, name: string): string {\n return join(refsDir, `${name}.contract.json`);\n}\n\nfunction snapshotDtsPath(refsDir: string, name: string): string {\n return join(refsDir, `${name}.contract.d.ts`);\n}\n\nfunction tmpPathFor(finalPath: string): string {\n const dir = dirname(finalPath);\n const fileName = basename(finalPath);\n return join(dir, `.${fileName}.${Date.now()}-${randomBytes(4).toString('hex')}.tmp`);\n}\n\nasync function atomicWriteFile(finalPath: string, content: string): Promise<void> {\n const dir = dirname(finalPath);\n await mkdir(dir, { recursive: true });\n const tmpPath = tmpPathFor(finalPath);\n await writeFile(tmpPath, content);\n await rename(tmpPath, finalPath);\n}\n\nasync function unlinkIfExists(filePath: string): Promise<void> {\n try {\n await unlink(filePath);\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) return;\n throw error;\n }\n}\n\nfunction parseContractSnapshotJson(filePath: string, raw: string): unknown {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw errorInvalidRefFile(filePath, 'Failed to parse as JSON');\n }\n\n const result = ContractIrSchema(parsed);\n if (result instanceof type.errors) {\n throw errorInvalidRefFile(filePath, result.summary);\n }\n\n return result;\n}\n\nexport async function writeRefSnapshot(\n refsDir: string,\n name: string,\n snapshot: ContractIR,\n): Promise<void> {\n if (!validateRefName(name)) {\n throw errorInvalidRefName(name);\n }\n\n const jsonPath = snapshotJsonPath(refsDir, name);\n const dtsPath = snapshotDtsPath(refsDir, name);\n const jsonContent = `${canonicalizeJson(snapshot.contract)}\\n`;\n const dtsContent = snapshot.contractDts.endsWith('\\n')\n ? snapshot.contractDts\n : `${snapshot.contractDts}\\n`;\n\n try {\n await atomicWriteFile(jsonPath, jsonContent);\n } catch (error) {\n await unlinkIfExists(jsonPath);\n throw error;\n }\n\n try {\n await atomicWriteFile(dtsPath, dtsContent);\n } catch (error) {\n await unlinkIfExists(jsonPath);\n await unlinkIfExists(dtsPath);\n throw error;\n }\n}\n\nexport async function readRefSnapshot(refsDir: string, name: string): Promise<ContractIR | null> {\n if (!validateRefName(name)) {\n throw errorInvalidRefName(name);\n }\n\n const jsonPath = snapshotJsonPath(refsDir, name);\n const dtsPath = snapshotDtsPath(refsDir, name);\n\n let raw: string;\n try {\n raw = await readFile(jsonPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return null;\n }\n throw error;\n }\n\n const contract = parseContractSnapshotJson(jsonPath, raw);\n\n let contractDts: string;\n try {\n contractDts = await readFile(dtsPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorInvalidRefFile(dtsPath, 'Missing paired contract.d.ts snapshot file');\n }\n throw error;\n }\n\n return { contract, contractDts };\n}\n\nexport async function deleteRefSnapshot(refsDir: string, name: string): Promise<void> {\n if (!validateRefName(name)) {\n throw errorInvalidRefName(name);\n }\n\n await unlinkIfExists(snapshotJsonPath(refsDir, name));\n await unlinkIfExists(snapshotDtsPath(refsDir, name));\n}\n\nexport async function writeRefPaired(\n refsDir: string,\n name: string,\n entry: RefEntry,\n snapshot: ContractIR,\n): Promise<void> {\n await writeRefSnapshot(refsDir, name, snapshot);\n try {\n await writeRef(refsDir, name, entry);\n } catch (writeError) {\n try {\n await deleteRefSnapshot(refsDir, name);\n } catch {\n // Rollback failure is secondary; preserve the original writeRef error.\n }\n throw writeError;\n }\n}\n\nfunction isUnknownRefError(error: unknown): boolean {\n return MigrationToolsError.is(error) && error.code === 'MIGRATION.UNKNOWN_REF';\n}\n\nasync function snapshotFilesExist(refsDir: string, name: string): Promise<boolean> {\n if (!validateRefName(name)) {\n throw errorInvalidRefName(name);\n }\n\n const paths = [snapshotJsonPath(refsDir, name), snapshotDtsPath(refsDir, name)];\n const checks = await Promise.allSettled(paths.map((filePath) => access(filePath)));\n return checks.some((result) => result.status === 'fulfilled');\n}\n\nexport async function deleteRefPaired(refsDir: string, name: string): Promise<void> {\n if (await snapshotFilesExist(refsDir, name)) {\n try {\n await deleteRef(refsDir, name);\n } catch (error) {\n if (!isUnknownRefError(error)) {\n throw error;\n }\n }\n await deleteRefSnapshot(refsDir, name);\n return;\n }\n\n await deleteRef(refsDir, name);\n await deleteRefSnapshot(refsDir, name);\n}\n"],"mappings":"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"}
@@ -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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"}
@@ -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":";;;;;;;;;;AAGA,SAAS,oBAAoB,SAA2D;CACtF,MAAM,aAAa,QAAQ;CAC3B,IAAI,CAAC,YAAY,OAAO;CACxB,MAAM,WAAoD,EAAE;CAC5D,KAAK,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,WAAW,EACjD,IAAI,MAAM,OAAO,OAAO,UAAU;EAChC,MAAM,EAAE,MAAM,OAAO,GAAG,SAAS;EACjC,SAAS,QAAQ;QAEjB,SAAS,QAAQ;CAGrB,OAAO;EAAE,GAAG;EAAS,YAAY;EAAU;;;;;;;;;;;;;;;;;;;;;AA2C7C,SAAgB,gCAAgC,QAA+C;CAa7F,MAAM,EAAE,aAAa,WAAW,GAAG,uBADb,OAAO;CAS7B,MAAM,oBAAoB,oBAAoB,mBAAmB;CACjE,MAAM,aAAa,mBAAmB;EACpC,QAAQ,OAAO;EACf,cAAc,OAAO;EACrB,SAAS;EACV,CAAC;CACF,IAAI,eAAe,OAAO,aACxB,MAAM,gCAAgC;EACpC,aAAa,OAAO;EACpB,gBAAgB;EAChB,aAAa,OAAO;EACrB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;ACFN,eAAsB,+BACpB,QACyC;CACzC,MAAM,EAAE,sBAAsB,SAAS,mBAAmB,4BAA4B;CAEtF,MAAM,uBAAuB,MAAM,yBAAyB,sBAAsB,QAAQ;CAC1F,IAAI,yBAAyB,MAC3B,OAAO,EAAE,MAAM,+BAA+B;CAIhD,MAAM,WAAW,MAAM,kBADN,wBAAwB,sBAAsB,QACd,CAAC;CAClD,MAAM,QAAQ,iBAAiB,SAAS;CAKxC,MAAM,WAAW,qBAAA;CACjB,MAAM,WAAW,IAAI,IACnB,qBAAqB,WAAW,QAAQ,OAAO,CAAC,wBAAwB,SAAS,GAAG,CAAC,CACtF;CAED,MAAM,UAAU,qBAAqB,OAAO,UAAU,qBAAqB,MAAM,EAAE,UAAU,CAAC;CAE9F,IAAI,QAAQ,SAAS,eACnB,OAAO;EAAE,MAAM;EAAe;EAAsB;CAEtD,IAAI,QAAQ,SAAS,iBACnB,OAAO;EACL,MAAM;EACN;EACA,SAAS,QAAQ;EACjB,gBAAgB,QAAQ,eAAe,KAAK,EAAE,SAAS,UAAU;GAAE;GAAS;GAAI,EAAE;EACnF;CAGH,MAAM,iBAAiB,IAAI,IAAI,SAAS,KAAK,QAAQ,CAAC,IAAI,SAAS,eAAe,IAAI,CAAC,CAAC;CAExF,MAAM,UAAkC,EAAE;CAC1C,MAAM,sBAAgC,EAAE;CACxC,MAAM,wCAAwB,IAAI,KAAa;CAC/C,KAAK,MAAM,QAAQ,QAAQ,SAAS,cAAc;EAChD,MAAM,MAAM,eAAe,IAAI,KAAK,cAAc;EAClD,IAAI,CAAC,KAIH,MAAM,IAAI,MACR,sCAAsC,KAAK,cAAc,aAAa,QAAQ,GAC/E;EAEH,oBAAoB,KAAK,IAAI,QAAQ;EACrC,KAAK,MAAM,MAAM,IAAI,KAAK,QAAQ,KAAK,GAAG;EAC1C,KAAK,MAAM,aAAa,IAAI,SAAS,oBAAoB,sBAAsB,IAAI,UAAU;;CAG/F,OAAO;EACL,MAAM;EACN;EACA,oBAAoB,CAAC,GAAG,sBAAsB,CAAC,MAAM;EACrD;EACA;EACD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChHH,SAAgB,sBAA6D,QAQhD;CAM3B,MAAM,aAA0C,OAAO,WAAW,KAAK,OAAO;EAC5E,SAAS,EAAE;EACX,UAAU,EAAE;EACZ,KAAK,EAAE;EACR,EAAE;CACH,OAAO;EACL,cAAc,OAAO;EACrB;EACA,SAAS,OAAO;EACjB;;;;;;;;;;;;;;;;;;;;;;ACXH,eAAsB,2BACpB,sBACA,SACA,QACe;CACf,mBAAmB,QAAQ;CAE3B,MAAM,MAAM,KAAK,sBAAsB,QAAQ;CAC/C,MAAM,MAAM,KAAK,KAAK,OAAO,EAAE,EAAE,WAAW,MAAM,CAAC;CAEnD,MAAM,UAAU,KAAK,KAAK,gBAAgB,EAAE,GAAG,iBAAiB,OAAO,SAAS,CAAC,IAAI;CACrF,MAAM,UAAU,KAAK,KAAK,gBAAgB,EAAE,OAAO,YAAY;CAE/D,MAAM,mBAAmB,CAAC,GAAG,OAAO,QAAQ,WAAW,CAAC,MAAM;CAC9D,MAAM,WAAW,iBAAiB;EAChC,MAAM,OAAO,QAAQ;EACrB,YAAY;EACb,CAAC;CACF,MAAM,UAAU,KAAK,KAAK,QAAQ,YAAY,EAAE,GAAG,SAAS,IAAI;;;;;;;;;;;;;;;;;;;;;;AC9BlE,eAAsB,6BAA6B,MAQf;CAClC,MAAM,EAAE,sBAAsB,mBAAmB;CAEjD,MAAM,kBAAkB,MAAM,6BAA6B,qBAAqB;CAEhF,MAAM,kCAAkB,IAAI,KAAsC;CAClE,KAAK,MAAM,WAAW,gBAAgB;EACpC,IAAI,YAAY,cAAc;EAC9B,MAAM,OAAO,MAAM,yBAAyB,sBAAsB,QAAQ;EAC1E,IAAI,SAAS,MACX,gBAAgB,IAAI,SAAS,KAAK;;CAItC,OAAO;EAAE;EAAiB;EAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACP7C,SAAgB,cACd,QACA,WACsC;CACtC,MAAM,uBAAO,IAAI,KAAa;CAC9B,KAAK,MAAM,SAAS,QAAQ;EAC1B,IAAI,KAAK,IAAI,MAAM,QAAQ,EACzB,MAAM,sBAAsB,MAAM,QAAQ;EAE5C,KAAK,IAAI,MAAM,QAAQ;;CASzB,OANe,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM;EACxC,IAAI,EAAE,UAAU,EAAE,SAAS,OAAO;EAClC,IAAI,EAAE,UAAU,EAAE,SAAS,OAAO;EAClC,OAAO;GAGI,CAAC,KAAK,WAAW;EAC5B,SAAS,MAAM;EACf,mBAAmB,UAAU,MAAM;EACpC,EAAE"}
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 +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;;;AAGX;;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-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 +1 @@
1
- {"version":3,"file":"hash-Cr4WIr4Z.mjs","names":[],"sources":["../src/hash.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { canonicalizeJson } from '@prisma-next/framework-components/utils';\nimport type { MigrationMetadata } from './metadata';\nimport type { MigrationOps, OnDiskMigrationPackage } from './package';\n\nexport interface VerifyResult {\n readonly ok: boolean;\n readonly reason?: 'mismatch';\n readonly storedHash: string;\n readonly computedHash: string;\n}\n\nfunction sha256Hex(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Content-addressed migration hash over (metadata envelope sans hints,\n * ops). See ADR 199 — Storage-only migration identity for the\n * rationale: the storage-hash bookends (`from`, `to`) inside the\n * envelope anchor the contract identity by hash, and planner hints are\n * advisory and must not affect identity. The full contract IRs are not\n * part of the manifest — they live in sibling `*-contract.json` files\n * authored alongside the migration, never inlined here.\n *\n * The integrity check is purely structural, not semantic. The function\n * canonicalizes its inputs via `sortKeys` (recursive) + `JSON.stringify`\n * and hashes the result. Target-specific operation payloads (`step.sql`,\n * Mongo's pipeline AST, …) are hashed verbatim — no per-target\n * normalization is required, because what's being verified is \"do the\n * on-disk bytes still produce their recorded hash\", not \"do two\n * semantically-equivalent migrations hash the same\". The latter is an\n * emit-drift concern (ADR 192 step 2).\n *\n * The symmetry across write and read holds because `JSON.parse(\n * JSON.stringify(x))` round-trips JSON-safe values losslessly and\n * `sortKeys` is idempotent and deterministic — write-time and read-time\n * canonicalization produce the same canonical bytes regardless of\n * source-side key ordering or whitespace.\n *\n * The `migrationHash` field on the metadata is stripped before hashing\n * so the function can be used both at write time (when no hash exists\n * yet) and at verify time (rehashing an already-attested record).\n */\nexport function computeMigrationHash(\n metadata: Omit<MigrationMetadata, 'migrationHash'> & { readonly migrationHash?: string },\n ops: MigrationOps,\n): string {\n const { migrationHash: _migrationHash, hints: _hints, ...strippedMeta } = metadata;\n\n const canonicalMetadata = canonicalizeJson(strippedMeta);\n const canonicalOps = canonicalizeJson(ops);\n\n const partHashes = [canonicalMetadata, canonicalOps].map(sha256Hex);\n const hash = sha256Hex(canonicalizeJson(partHashes));\n\n return `sha256:${hash}`;\n}\n\n/**\n * Re-hash an in-memory migration package and compare against the stored\n * `migrationHash`. See `computeMigrationHash` for the canonicalization rules.\n *\n * Returns `{ ok: true }` when the package is internally consistent, or\n * `{ ok: false, reason: 'mismatch', storedHash, computedHash }` when it is\n * not — typically a sign of FS corruption, partial writes, or a post-emit\n * hand edit.\n */\nexport function verifyMigrationHash(pkg: OnDiskMigrationPackage): VerifyResult {\n const computed = computeMigrationHash(pkg.metadata, pkg.ops);\n\n if (pkg.metadata.migrationHash === computed) {\n return {\n ok: true,\n storedHash: pkg.metadata.migrationHash,\n computedHash: computed,\n };\n }\n\n return {\n ok: false,\n reason: 'mismatch',\n storedHash: pkg.metadata.migrationHash,\n computedHash: computed,\n };\n}\n"],"mappings":";;;AAYA,SAAS,UAAU,OAAuB;CACxC,OAAO,WAAW,SAAS,CAAC,OAAO,MAAM,CAAC,OAAO,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BzD,SAAgB,qBACd,UACA,KACQ;CACR,MAAM,EAAE,eAAe,gBAAgB,OAAO,QAAQ,GAAG,iBAAiB;CAQ1E,OAAO,UAFM,UAAU,iBADJ,CAHO,iBAAiB,aAGN,EAFhB,iBAAiB,IAEa,CAAC,CAAC,IAAI,UACP,CAAC,CAE9B;;;;;;;;;;;AAYvB,SAAgB,oBAAoB,KAA2C;CAC7E,MAAM,WAAW,qBAAqB,IAAI,UAAU,IAAI,IAAI;CAE5D,IAAI,IAAI,SAAS,kBAAkB,UACjC,OAAO;EACL,IAAI;EACJ,YAAY,IAAI,SAAS;EACzB,cAAc;EACf;CAGH,OAAO;EACL,IAAI;EACJ,QAAQ;EACR,YAAY,IAAI,SAAS;EACzB,cAAc;EACf"}
1
+ {"version":3,"file":"hash-Cr4WIr4Z.mjs","names":[],"sources":["../src/hash.ts"],"sourcesContent":["import { createHash } from 'node:crypto';\nimport { canonicalizeJson } from '@prisma-next/framework-components/utils';\nimport type { MigrationMetadata } from './metadata';\nimport type { MigrationOps, OnDiskMigrationPackage } from './package';\n\nexport interface VerifyResult {\n readonly ok: boolean;\n readonly reason?: 'mismatch';\n readonly storedHash: string;\n readonly computedHash: string;\n}\n\nfunction sha256Hex(input: string): string {\n return createHash('sha256').update(input).digest('hex');\n}\n\n/**\n * Content-addressed migration hash over (metadata envelope sans hints,\n * ops). See ADR 199 — Storage-only migration identity for the\n * rationale: the storage-hash bookends (`from`, `to`) inside the\n * envelope anchor the contract identity by hash, and planner hints are\n * advisory and must not affect identity. The full contract IRs are not\n * part of the manifest — they live in sibling `*-contract.json` files\n * authored alongside the migration, never inlined here.\n *\n * The integrity check is purely structural, not semantic. The function\n * canonicalizes its inputs via `sortKeys` (recursive) + `JSON.stringify`\n * and hashes the result. Target-specific operation payloads (`step.sql`,\n * Mongo's pipeline AST, …) are hashed verbatim — no per-target\n * normalization is required, because what's being verified is \"do the\n * on-disk bytes still produce their recorded hash\", not \"do two\n * semantically-equivalent migrations hash the same\". The latter is an\n * emit-drift concern (ADR 192 step 2).\n *\n * The symmetry across write and read holds because `JSON.parse(\n * JSON.stringify(x))` round-trips JSON-safe values losslessly and\n * `sortKeys` is idempotent and deterministic — write-time and read-time\n * canonicalization produce the same canonical bytes regardless of\n * source-side key ordering or whitespace.\n *\n * The `migrationHash` field on the metadata is stripped before hashing\n * so the function can be used both at write time (when no hash exists\n * yet) and at verify time (rehashing an already-attested record).\n */\nexport function computeMigrationHash(\n metadata: Omit<MigrationMetadata, 'migrationHash'> & { readonly migrationHash?: string },\n ops: MigrationOps,\n): string {\n const { migrationHash: _migrationHash, hints: _hints, ...strippedMeta } = metadata;\n\n const canonicalMetadata = canonicalizeJson(strippedMeta);\n const canonicalOps = canonicalizeJson(ops);\n\n const partHashes = [canonicalMetadata, canonicalOps].map(sha256Hex);\n const hash = sha256Hex(canonicalizeJson(partHashes));\n\n return `sha256:${hash}`;\n}\n\n/**\n * Re-hash an in-memory migration package and compare against the stored\n * `migrationHash`. See `computeMigrationHash` for the canonicalization rules.\n *\n * Returns `{ ok: true }` when the package is internally consistent, or\n * `{ ok: false, reason: 'mismatch', storedHash, computedHash }` when it is\n * not — typically a sign of FS corruption, partial writes, or a post-emit\n * hand edit.\n */\nexport function verifyMigrationHash(pkg: OnDiskMigrationPackage): VerifyResult {\n const computed = computeMigrationHash(pkg.metadata, pkg.ops);\n\n if (pkg.metadata.migrationHash === computed) {\n return {\n ok: true,\n storedHash: pkg.metadata.migrationHash,\n computedHash: computed,\n };\n }\n\n return {\n ok: false,\n reason: 'mismatch',\n storedHash: pkg.metadata.migrationHash,\n computedHash: computed,\n };\n}\n"],"mappings":";;;AAYA,SAAS,UAAU,OAAuB;CACxC,OAAO,WAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO,KAAK;AACxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,SAAgB,qBACd,UACA,KACQ;CACR,MAAM,EAAE,eAAe,gBAAgB,OAAO,QAAQ,GAAG,iBAAiB;CAQ1E,OAAO,UAFM,UAAU,iBADJ,CAHO,iBAAiB,YAGP,GAFf,iBAAiB,GAEY,CAAC,EAAE,IAAI,SACR,CAAC,CAE9B;AACtB;;;;;;;;;;AAWA,SAAgB,oBAAoB,KAA2C;CAC7E,MAAM,WAAW,qBAAqB,IAAI,UAAU,IAAI,GAAG;CAE3D,IAAI,IAAI,SAAS,kBAAkB,UACjC,OAAO;EACL,IAAI;EACJ,YAAY,IAAI,SAAS;EACzB,cAAc;CAChB;CAGF,OAAO;EACL,IAAI;EACJ,QAAQ;EACR,YAAY,IAAI,SAAS;EACzB,cAAc;CAChB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"invariants-lbJddL-S.mjs","names":[],"sources":["../src/invariants.ts"],"sourcesContent":["import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';\nimport { errorDuplicateInvariantInEdge, errorInvalidInvariantId } from './errors';\nimport type { MigrationOps } from './package';\n\n/**\n * Hygiene check for `invariantId`. Rejects empty values plus any\n * whitespace or control character (including Unicode whitespace like\n * NBSP and em space, which are visually identical to ASCII space and\n * routinely sneak in via paste).\n */\nexport function validateInvariantId(invariantId: string): boolean {\n if (invariantId.length === 0) return false;\n return !/[\\p{Cc}\\p{White_Space}]/u.test(invariantId);\n}\n\n/**\n * Walk a migration's operations and produce its `providedInvariants`\n * aggregate: the sorted, deduplicated list of `invariantId`s declared\n * by ops in the migration. Ops without an `invariantId` are skipped.\n *\n * Both `data`-class ops (data-transforms, e.g. backfills) and\n * `additive`-class opaque DDL (e.g. cipherstash's vendored EQL bundle\n * via `installEqlBundleOp`) may declare invariantIds: the\n * `operationClass` axis classifies *policy gating* (which kinds of ops\n * a `db init` / `db update` policy permits), while `invariantId`\n * classifies *marker bookkeeping* (which named bundles of work a\n * future regeneration knows to skip). The two concerns are\n * intentionally orthogonal — an extension can ship additive\n * non-IR-derivable DDL (the only way the planner can know the bundle\n * is already applied is via the invariantId on the marker) without\n * needing to mis-classify it as `data`-class.\n *\n * Throws `MIGRATION.INVALID_INVARIANT_ID` on a malformed id and\n * `MIGRATION.DUPLICATE_INVARIANT_IN_EDGE` on duplicates.\n *\n * @see docs/architecture docs/adrs/ADR 212 - Contract spaces.md\n * — extension migrations carry `invariantId`s on additive ops; e.g.\n * cipherstash's `installEqlBundle` and structural `create-*` ops are\n * additive-class but carry `cipherstash:*` invariantIds.\n */\nexport function deriveProvidedInvariants(ops: MigrationOps): readonly string[] {\n const seen = new Set<string>();\n for (const op of ops) {\n const invariantId = readInvariantId(op);\n if (invariantId === undefined) continue;\n if (!validateInvariantId(invariantId)) {\n throw errorInvalidInvariantId(invariantId);\n }\n if (seen.has(invariantId)) {\n throw errorDuplicateInvariantInEdge(invariantId);\n }\n seen.add(invariantId);\n }\n return [...seen].sort();\n}\n\nfunction readInvariantId(op: MigrationPlanOperation): string | undefined {\n if (!Object.hasOwn(op, 'invariantId')) return undefined;\n const candidate = (op as { invariantId?: unknown }).invariantId;\n return typeof candidate === 'string' ? candidate : undefined;\n}\n"],"mappings":";;;;;;;;AAUA,SAAgB,oBAAoB,aAA8B;CAChE,IAAI,YAAY,WAAW,GAAG,OAAO;CACrC,OAAO,CAAC,2BAA2B,KAAK,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BtD,SAAgB,yBAAyB,KAAsC;CAC7E,MAAM,uBAAO,IAAI,KAAa;CAC9B,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,cAAc,gBAAgB,GAAG;EACvC,IAAI,gBAAgB,KAAA,GAAW;EAC/B,IAAI,CAAC,oBAAoB,YAAY,EACnC,MAAM,wBAAwB,YAAY;EAE5C,IAAI,KAAK,IAAI,YAAY,EACvB,MAAM,8BAA8B,YAAY;EAElD,KAAK,IAAI,YAAY;;CAEvB,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM;;AAGzB,SAAS,gBAAgB,IAAgD;CACvE,IAAI,CAAC,OAAO,OAAO,IAAI,cAAc,EAAE,OAAO,KAAA;CAC9C,MAAM,YAAa,GAAiC;CACpD,OAAO,OAAO,cAAc,WAAW,YAAY,KAAA"}
1
+ {"version":3,"file":"invariants-lbJddL-S.mjs","names":[],"sources":["../src/invariants.ts"],"sourcesContent":["import type { MigrationPlanOperation } from '@prisma-next/framework-components/control';\nimport { errorDuplicateInvariantInEdge, errorInvalidInvariantId } from './errors';\nimport type { MigrationOps } from './package';\n\n/**\n * Hygiene check for `invariantId`. Rejects empty values plus any\n * whitespace or control character (including Unicode whitespace like\n * NBSP and em space, which are visually identical to ASCII space and\n * routinely sneak in via paste).\n */\nexport function validateInvariantId(invariantId: string): boolean {\n if (invariantId.length === 0) return false;\n return !/[\\p{Cc}\\p{White_Space}]/u.test(invariantId);\n}\n\n/**\n * Walk a migration's operations and produce its `providedInvariants`\n * aggregate: the sorted, deduplicated list of `invariantId`s declared\n * by ops in the migration. Ops without an `invariantId` are skipped.\n *\n * Both `data`-class ops (data-transforms, e.g. backfills) and\n * `additive`-class opaque DDL (e.g. cipherstash's vendored EQL bundle\n * via `installEqlBundleOp`) may declare invariantIds: the\n * `operationClass` axis classifies *policy gating* (which kinds of ops\n * a `db init` / `db update` policy permits), while `invariantId`\n * classifies *marker bookkeeping* (which named bundles of work a\n * future regeneration knows to skip). The two concerns are\n * intentionally orthogonal — an extension can ship additive\n * non-IR-derivable DDL (the only way the planner can know the bundle\n * is already applied is via the invariantId on the marker) without\n * needing to mis-classify it as `data`-class.\n *\n * Throws `MIGRATION.INVALID_INVARIANT_ID` on a malformed id and\n * `MIGRATION.DUPLICATE_INVARIANT_IN_EDGE` on duplicates.\n *\n * @see docs/architecture docs/adrs/ADR 212 - Contract spaces.md\n * — extension migrations carry `invariantId`s on additive ops; e.g.\n * cipherstash's `installEqlBundle` and structural `create-*` ops are\n * additive-class but carry `cipherstash:*` invariantIds.\n */\nexport function deriveProvidedInvariants(ops: MigrationOps): readonly string[] {\n const seen = new Set<string>();\n for (const op of ops) {\n const invariantId = readInvariantId(op);\n if (invariantId === undefined) continue;\n if (!validateInvariantId(invariantId)) {\n throw errorInvalidInvariantId(invariantId);\n }\n if (seen.has(invariantId)) {\n throw errorDuplicateInvariantInEdge(invariantId);\n }\n seen.add(invariantId);\n }\n return [...seen].sort();\n}\n\nfunction readInvariantId(op: MigrationPlanOperation): string | undefined {\n if (!Object.hasOwn(op, 'invariantId')) return undefined;\n const candidate = (op as { invariantId?: unknown }).invariantId;\n return typeof candidate === 'string' ? candidate : undefined;\n}\n"],"mappings":";;;;;;;;AAUA,SAAgB,oBAAoB,aAA8B;CAChE,IAAI,YAAY,WAAW,GAAG,OAAO;CACrC,OAAO,CAAC,2BAA2B,KAAK,WAAW;AACrD;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAAgB,yBAAyB,KAAsC;CAC7E,MAAM,uBAAO,IAAI,IAAY;CAC7B,KAAK,MAAM,MAAM,KAAK;EACpB,MAAM,cAAc,gBAAgB,EAAE;EACtC,IAAI,gBAAgB,KAAA,GAAW;EAC/B,IAAI,CAAC,oBAAoB,WAAW,GAClC,MAAM,wBAAwB,WAAW;EAE3C,IAAI,KAAK,IAAI,WAAW,GACtB,MAAM,8BAA8B,WAAW;EAEjD,KAAK,IAAI,WAAW;CACtB;CACA,OAAO,CAAC,GAAG,IAAI,EAAE,KAAK;AACxB;AAEA,SAAS,gBAAgB,IAAgD;CACvE,IAAI,CAAC,OAAO,OAAO,IAAI,aAAa,GAAG,OAAO,KAAA;CAC9C,MAAM,YAAa,GAAiC;CACpD,OAAO,OAAO,cAAc,WAAW,YAAY,KAAA;AACrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"io-gHmDrSjQ.mjs","names":[],"sources":["../src/io.ts"],"sourcesContent":["import { copyFile, mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';\nimport type {\n MigrationMetadata,\n MigrationPackage,\n} from '@prisma-next/framework-components/control';\nimport { type } from 'arktype';\nimport { basename, dirname, join, resolve } from 'pathe';\nimport {\n errorDirectoryExists,\n errorInvalidDestName,\n errorInvalidJson,\n errorInvalidManifest,\n errorInvalidSlug,\n errorMigrationHashMismatch,\n errorMissingFile,\n errorProvidedInvariantsMismatch,\n} from './errors';\nimport { verifyMigrationHash } from './hash';\nimport { deriveProvidedInvariants } from './invariants';\nimport { MigrationOpsSchema } from './op-schema';\nimport type { MigrationOps, OnDiskMigrationPackage } from './package';\n\nexport const MANIFEST_FILE = 'migration.json';\nconst OPS_FILE = 'ops.json';\nconst MAX_SLUG_LENGTH = 64;\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\nconst MigrationHintsSchema = type({\n used: 'string[]',\n applied: 'string[]',\n plannerVersion: 'string',\n});\n\nconst MigrationMetadataSchema = type({\n '+': 'reject',\n from: 'string > 0 | null',\n to: 'string',\n migrationHash: 'string',\n hints: MigrationHintsSchema,\n labels: 'string[]',\n providedInvariants: 'string[]',\n createdAt: 'string',\n});\n\nexport async function writeMigrationPackage(\n dir: string,\n metadata: MigrationMetadata,\n ops: MigrationOps,\n): Promise<void> {\n await mkdir(dirname(dir), { recursive: true });\n\n try {\n await mkdir(dir);\n } catch (error) {\n if (hasErrnoCode(error, 'EEXIST')) {\n throw errorDirectoryExists(dir);\n }\n throw error;\n }\n\n await writeFile(join(dir, MANIFEST_FILE), JSON.stringify(metadata, null, 2), {\n flag: 'wx',\n });\n await writeFile(join(dir, OPS_FILE), JSON.stringify(ops, null, 2), { flag: 'wx' });\n}\n\n/**\n * Materialise an in-memory {@link MigrationPackage} to a per-space\n * directory on disk.\n *\n * Writes two files under `<targetDir>/<pkg.dirName>/`:\n *\n * - `migration.json` — the manifest (pretty-printed, matches\n * {@link writeMigrationPackage}'s output for byte-for-byte parity with\n * app-space migrations).\n * - `ops.json` — the operation list (pretty-printed).\n *\n * Distinct verb from the lower-level {@link writeMigrationPackage}\n * (which takes constituent `(metadata, ops)`): callers reading\n * `materialise…` know they are persisting a struct-typed package.\n *\n * Overwrite-idempotent: the per-package directory is cleared before\n * each emit, so re-running against the same `targetDir` produces\n * byte-identical contents and never leaves stale files behind. The\n * lower-level {@link writeMigrationPackage} stays strict because the\n * CLI authoring path (`migration plan` / `migration new`) deliberately\n * refuses to clobber an existing authored migration; this helper is\n * the re-emit path that is supposed to converge on a single canonical\n * on-disk shape.\n *\n * The per-space head contract lives at\n * `<projectMigrationsDir>/<spaceId>/contract.json` (written by\n * {@link import('./emit-contract-space-artefacts').emitContractSpaceArtefacts}),\n * not inside the per-package directory. The runner reads only\n * `migration.json` + `ops.json` from each package.\n */\nexport async function materialiseMigrationPackage(\n targetDir: string,\n pkg: MigrationPackage,\n): Promise<void> {\n const dir = join(targetDir, pkg.dirName);\n await rm(dir, { recursive: true, force: true });\n await writeMigrationPackage(dir, pkg.metadata, pkg.ops);\n}\n\n/**\n * Idempotent variant of {@link materialiseMigrationPackage}: writes the\n * package only if `<targetDir>/<pkg.dirName>/` does not already exist on\n * disk as a directory; returns `{ written: false }` when the package\n * directory is present (no rewrite, no comparison — by-existence skip).\n *\n * Concretely:\n * - existing directory → skip silently, return `{ written: false }`.\n * - missing path → write three files via {@link materialiseMigrationPackage},\n * return `{ written: true }`.\n * - path exists but is not a directory (file/symlink) → treated as\n * missing; {@link materialiseMigrationPackage} will attempt creation\n * and fail with an appropriate OS error.\n * - any other I/O error from `stat` → propagated unchanged.\n *\n * Used by the CLI's `runContractSpaceExtensionMigrationsPass` to\n * materialise extension migration packages into a project's\n * `migrations/<spaceId>/` directory, and by extension-package tests\n * that mirror the same idempotent-rematerialise property locally\n * without taking a CLI dependency.\n */\nexport async function materialiseExtensionMigrationPackageIfMissing(\n targetDir: string,\n pkg: MigrationPackage,\n): Promise<{ readonly written: boolean }> {\n const pkgDir = join(targetDir, pkg.dirName);\n if (await directoryExists(pkgDir)) {\n return { written: false };\n }\n await materialiseMigrationPackage(targetDir, pkg);\n return { written: true };\n}\n\nasync function directoryExists(p: string): Promise<boolean> {\n try {\n return (await stat(p)).isDirectory();\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) return false;\n throw error;\n }\n}\n\n/**\n * Copy a list of files into `destDir`, optionally renaming each one.\n *\n * The destination directory is created (with `recursive: true`) if it\n * does not already exist. Each source path is copied byte-for-byte into\n * `destDir/<destName>`; missing sources throw `ENOENT`. The helper is\n * intentionally generic: callers own the list of files (e.g. a contract\n * emitter's emitted output) and the naming convention (e.g. renaming\n * the destination contract to `end-contract.*` and the source contract\n * to `start-contract.*`).\n */\nexport async function copyFilesWithRename(\n destDir: string,\n files: readonly { readonly sourcePath: string; readonly destName: string }[],\n): Promise<void> {\n await mkdir(destDir, { recursive: true });\n for (const file of files) {\n if (basename(file.destName) !== file.destName) {\n throw errorInvalidDestName(file.destName);\n }\n await copyFile(file.sourcePath, join(destDir, file.destName));\n }\n}\n\nexport async function writeMigrationMetadata(\n dir: string,\n metadata: MigrationMetadata,\n): Promise<void> {\n await writeFile(join(dir, MANIFEST_FILE), `${JSON.stringify(metadata, null, 2)}\\n`);\n}\n\nexport async function writeMigrationOps(dir: string, ops: MigrationOps): Promise<void> {\n await writeFile(join(dir, OPS_FILE), `${JSON.stringify(ops, null, 2)}\\n`);\n}\n\nexport async function readMigrationPackage(dir: string): Promise<OnDiskMigrationPackage> {\n const absoluteDir = resolve(dir);\n const manifestPath = join(absoluteDir, MANIFEST_FILE);\n const opsPath = join(absoluteDir, OPS_FILE);\n\n let manifestRaw: string;\n try {\n manifestRaw = await readFile(manifestPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile(MANIFEST_FILE, absoluteDir);\n }\n throw error;\n }\n\n let opsRaw: string;\n try {\n opsRaw = await readFile(opsPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile(OPS_FILE, absoluteDir);\n }\n throw error;\n }\n\n let metadata: MigrationMetadata;\n try {\n metadata = JSON.parse(manifestRaw);\n } catch (e) {\n throw errorInvalidJson(manifestPath, e instanceof Error ? e.message : String(e));\n }\n\n let ops: MigrationOps;\n try {\n ops = JSON.parse(opsRaw);\n } catch (e) {\n throw errorInvalidJson(opsPath, e instanceof Error ? e.message : String(e));\n }\n\n validateMetadata(metadata, manifestPath);\n validateOps(ops, opsPath);\n\n // Re-derive before the hash check so format/duplicate diagnostics\n // fire with their dedicated codes rather than as a generic hash mismatch.\n const derivedInvariants = deriveProvidedInvariants(ops);\n if (!arraysEqual(metadata.providedInvariants, derivedInvariants)) {\n throw errorProvidedInvariantsMismatch(\n manifestPath,\n metadata.providedInvariants,\n derivedInvariants,\n );\n }\n\n const pkg: OnDiskMigrationPackage = {\n dirName: basename(absoluteDir),\n dirPath: absoluteDir,\n metadata,\n ops,\n };\n\n const verification = verifyMigrationHash(pkg);\n if (!verification.ok) {\n throw errorMigrationHashMismatch(\n absoluteDir,\n verification.storedHash,\n verification.computedHash,\n );\n }\n\n return pkg;\n}\n\nfunction arraysEqual(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\nfunction validateMetadata(\n metadata: unknown,\n filePath: string,\n): asserts metadata is MigrationMetadata {\n const result = MigrationMetadataSchema(metadata);\n if (result instanceof type.errors) {\n throw errorInvalidManifest(filePath, result.summary);\n }\n}\n\nfunction validateOps(ops: unknown, filePath: string): asserts ops is MigrationOps {\n const result = MigrationOpsSchema(ops);\n if (result instanceof type.errors) {\n throw errorInvalidManifest(filePath, result.summary);\n }\n}\n\nexport async function readMigrationsDir(\n migrationsRoot: string,\n): Promise<readonly OnDiskMigrationPackage[]> {\n let entries: string[];\n try {\n entries = await readdir(migrationsRoot);\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return [];\n }\n throw error;\n }\n\n const packages: OnDiskMigrationPackage[] = [];\n\n for (const entry of entries.sort()) {\n const entryPath = join(migrationsRoot, entry);\n const entryStat = await stat(entryPath);\n if (!entryStat.isDirectory()) continue;\n\n const manifestPath = join(entryPath, MANIFEST_FILE);\n try {\n await stat(manifestPath);\n } catch {\n continue; // skip non-migration directories\n }\n\n packages.push(await readMigrationPackage(entryPath));\n }\n\n return packages;\n}\n\nexport function formatMigrationDirName(timestamp: Date, slug: string): string {\n const sanitized = slug\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '_')\n .replace(/_+/g, '_')\n .replace(/^_|_$/g, '');\n\n if (sanitized.length === 0) {\n throw errorInvalidSlug(slug);\n }\n\n const truncated = sanitized.slice(0, MAX_SLUG_LENGTH);\n\n const y = timestamp.getUTCFullYear();\n const mo = String(timestamp.getUTCMonth() + 1).padStart(2, '0');\n const d = String(timestamp.getUTCDate()).padStart(2, '0');\n const h = String(timestamp.getUTCHours()).padStart(2, '0');\n const mi = String(timestamp.getUTCMinutes()).padStart(2, '0');\n\n return `${y}${mo}${d}T${h}${mi}_${truncated}`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"io-gHmDrSjQ.mjs","names":[],"sources":["../src/io.ts"],"sourcesContent":["import { copyFile, mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';\nimport type {\n MigrationMetadata,\n MigrationPackage,\n} from '@prisma-next/framework-components/control';\nimport { type } from 'arktype';\nimport { basename, dirname, join, resolve } from 'pathe';\nimport {\n errorDirectoryExists,\n errorInvalidDestName,\n errorInvalidJson,\n errorInvalidManifest,\n errorInvalidSlug,\n errorMigrationHashMismatch,\n errorMissingFile,\n errorProvidedInvariantsMismatch,\n} from './errors';\nimport { verifyMigrationHash } from './hash';\nimport { deriveProvidedInvariants } from './invariants';\nimport { MigrationOpsSchema } from './op-schema';\nimport type { MigrationOps, OnDiskMigrationPackage } from './package';\n\nexport const MANIFEST_FILE = 'migration.json';\nconst OPS_FILE = 'ops.json';\nconst MAX_SLUG_LENGTH = 64;\n\nfunction hasErrnoCode(error: unknown, code: string): boolean {\n return error instanceof Error && (error as { code?: string }).code === code;\n}\n\nconst MigrationHintsSchema = type({\n used: 'string[]',\n applied: 'string[]',\n plannerVersion: 'string',\n});\n\nconst MigrationMetadataSchema = type({\n '+': 'reject',\n from: 'string > 0 | null',\n to: 'string',\n migrationHash: 'string',\n hints: MigrationHintsSchema,\n labels: 'string[]',\n providedInvariants: 'string[]',\n createdAt: 'string',\n});\n\nexport async function writeMigrationPackage(\n dir: string,\n metadata: MigrationMetadata,\n ops: MigrationOps,\n): Promise<void> {\n await mkdir(dirname(dir), { recursive: true });\n\n try {\n await mkdir(dir);\n } catch (error) {\n if (hasErrnoCode(error, 'EEXIST')) {\n throw errorDirectoryExists(dir);\n }\n throw error;\n }\n\n await writeFile(join(dir, MANIFEST_FILE), JSON.stringify(metadata, null, 2), {\n flag: 'wx',\n });\n await writeFile(join(dir, OPS_FILE), JSON.stringify(ops, null, 2), { flag: 'wx' });\n}\n\n/**\n * Materialise an in-memory {@link MigrationPackage} to a per-space\n * directory on disk.\n *\n * Writes two files under `<targetDir>/<pkg.dirName>/`:\n *\n * - `migration.json` — the manifest (pretty-printed, matches\n * {@link writeMigrationPackage}'s output for byte-for-byte parity with\n * app-space migrations).\n * - `ops.json` — the operation list (pretty-printed).\n *\n * Distinct verb from the lower-level {@link writeMigrationPackage}\n * (which takes constituent `(metadata, ops)`): callers reading\n * `materialise…` know they are persisting a struct-typed package.\n *\n * Overwrite-idempotent: the per-package directory is cleared before\n * each emit, so re-running against the same `targetDir` produces\n * byte-identical contents and never leaves stale files behind. The\n * lower-level {@link writeMigrationPackage} stays strict because the\n * CLI authoring path (`migration plan` / `migration new`) deliberately\n * refuses to clobber an existing authored migration; this helper is\n * the re-emit path that is supposed to converge on a single canonical\n * on-disk shape.\n *\n * The per-space head contract lives at\n * `<projectMigrationsDir>/<spaceId>/contract.json` (written by\n * {@link import('./emit-contract-space-artefacts').emitContractSpaceArtefacts}),\n * not inside the per-package directory. The runner reads only\n * `migration.json` + `ops.json` from each package.\n */\nexport async function materialiseMigrationPackage(\n targetDir: string,\n pkg: MigrationPackage,\n): Promise<void> {\n const dir = join(targetDir, pkg.dirName);\n await rm(dir, { recursive: true, force: true });\n await writeMigrationPackage(dir, pkg.metadata, pkg.ops);\n}\n\n/**\n * Idempotent variant of {@link materialiseMigrationPackage}: writes the\n * package only if `<targetDir>/<pkg.dirName>/` does not already exist on\n * disk as a directory; returns `{ written: false }` when the package\n * directory is present (no rewrite, no comparison — by-existence skip).\n *\n * Concretely:\n * - existing directory → skip silently, return `{ written: false }`.\n * - missing path → write three files via {@link materialiseMigrationPackage},\n * return `{ written: true }`.\n * - path exists but is not a directory (file/symlink) → treated as\n * missing; {@link materialiseMigrationPackage} will attempt creation\n * and fail with an appropriate OS error.\n * - any other I/O error from `stat` → propagated unchanged.\n *\n * Used by the CLI's `runContractSpaceExtensionMigrationsPass` to\n * materialise extension migration packages into a project's\n * `migrations/<spaceId>/` directory, and by extension-package tests\n * that mirror the same idempotent-rematerialise property locally\n * without taking a CLI dependency.\n */\nexport async function materialiseExtensionMigrationPackageIfMissing(\n targetDir: string,\n pkg: MigrationPackage,\n): Promise<{ readonly written: boolean }> {\n const pkgDir = join(targetDir, pkg.dirName);\n if (await directoryExists(pkgDir)) {\n return { written: false };\n }\n await materialiseMigrationPackage(targetDir, pkg);\n return { written: true };\n}\n\nasync function directoryExists(p: string): Promise<boolean> {\n try {\n return (await stat(p)).isDirectory();\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) return false;\n throw error;\n }\n}\n\n/**\n * Copy a list of files into `destDir`, optionally renaming each one.\n *\n * The destination directory is created (with `recursive: true`) if it\n * does not already exist. Each source path is copied byte-for-byte into\n * `destDir/<destName>`; missing sources throw `ENOENT`. The helper is\n * intentionally generic: callers own the list of files (e.g. a contract\n * emitter's emitted output) and the naming convention (e.g. renaming\n * the destination contract to `end-contract.*` and the source contract\n * to `start-contract.*`).\n */\nexport async function copyFilesWithRename(\n destDir: string,\n files: readonly { readonly sourcePath: string; readonly destName: string }[],\n): Promise<void> {\n await mkdir(destDir, { recursive: true });\n for (const file of files) {\n if (basename(file.destName) !== file.destName) {\n throw errorInvalidDestName(file.destName);\n }\n await copyFile(file.sourcePath, join(destDir, file.destName));\n }\n}\n\nexport async function writeMigrationMetadata(\n dir: string,\n metadata: MigrationMetadata,\n): Promise<void> {\n await writeFile(join(dir, MANIFEST_FILE), `${JSON.stringify(metadata, null, 2)}\\n`);\n}\n\nexport async function writeMigrationOps(dir: string, ops: MigrationOps): Promise<void> {\n await writeFile(join(dir, OPS_FILE), `${JSON.stringify(ops, null, 2)}\\n`);\n}\n\nexport async function readMigrationPackage(dir: string): Promise<OnDiskMigrationPackage> {\n const absoluteDir = resolve(dir);\n const manifestPath = join(absoluteDir, MANIFEST_FILE);\n const opsPath = join(absoluteDir, OPS_FILE);\n\n let manifestRaw: string;\n try {\n manifestRaw = await readFile(manifestPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile(MANIFEST_FILE, absoluteDir);\n }\n throw error;\n }\n\n let opsRaw: string;\n try {\n opsRaw = await readFile(opsPath, 'utf-8');\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n throw errorMissingFile(OPS_FILE, absoluteDir);\n }\n throw error;\n }\n\n let metadata: MigrationMetadata;\n try {\n metadata = JSON.parse(manifestRaw);\n } catch (e) {\n throw errorInvalidJson(manifestPath, e instanceof Error ? e.message : String(e));\n }\n\n let ops: MigrationOps;\n try {\n ops = JSON.parse(opsRaw);\n } catch (e) {\n throw errorInvalidJson(opsPath, e instanceof Error ? e.message : String(e));\n }\n\n validateMetadata(metadata, manifestPath);\n validateOps(ops, opsPath);\n\n // Re-derive before the hash check so format/duplicate diagnostics\n // fire with their dedicated codes rather than as a generic hash mismatch.\n const derivedInvariants = deriveProvidedInvariants(ops);\n if (!arraysEqual(metadata.providedInvariants, derivedInvariants)) {\n throw errorProvidedInvariantsMismatch(\n manifestPath,\n metadata.providedInvariants,\n derivedInvariants,\n );\n }\n\n const pkg: OnDiskMigrationPackage = {\n dirName: basename(absoluteDir),\n dirPath: absoluteDir,\n metadata,\n ops,\n };\n\n const verification = verifyMigrationHash(pkg);\n if (!verification.ok) {\n throw errorMigrationHashMismatch(\n absoluteDir,\n verification.storedHash,\n verification.computedHash,\n );\n }\n\n return pkg;\n}\n\nfunction arraysEqual(a: readonly string[], b: readonly string[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\nfunction validateMetadata(\n metadata: unknown,\n filePath: string,\n): asserts metadata is MigrationMetadata {\n const result = MigrationMetadataSchema(metadata);\n if (result instanceof type.errors) {\n throw errorInvalidManifest(filePath, result.summary);\n }\n}\n\nfunction validateOps(ops: unknown, filePath: string): asserts ops is MigrationOps {\n const result = MigrationOpsSchema(ops);\n if (result instanceof type.errors) {\n throw errorInvalidManifest(filePath, result.summary);\n }\n}\n\nexport async function readMigrationsDir(\n migrationsRoot: string,\n): Promise<readonly OnDiskMigrationPackage[]> {\n let entries: string[];\n try {\n entries = await readdir(migrationsRoot);\n } catch (error) {\n if (hasErrnoCode(error, 'ENOENT')) {\n return [];\n }\n throw error;\n }\n\n const packages: OnDiskMigrationPackage[] = [];\n\n for (const entry of entries.sort()) {\n const entryPath = join(migrationsRoot, entry);\n const entryStat = await stat(entryPath);\n if (!entryStat.isDirectory()) continue;\n\n const manifestPath = join(entryPath, MANIFEST_FILE);\n try {\n await stat(manifestPath);\n } catch {\n continue; // skip non-migration directories\n }\n\n packages.push(await readMigrationPackage(entryPath));\n }\n\n return packages;\n}\n\nexport function formatMigrationDirName(timestamp: Date, slug: string): string {\n const sanitized = slug\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '_')\n .replace(/_+/g, '_')\n .replace(/^_|_$/g, '');\n\n if (sanitized.length === 0) {\n throw errorInvalidSlug(slug);\n }\n\n const truncated = sanitized.slice(0, MAX_SLUG_LENGTH);\n\n const y = timestamp.getUTCFullYear();\n const mo = String(timestamp.getUTCMonth() + 1).padStart(2, '0');\n const d = String(timestamp.getUTCDate()).padStart(2, '0');\n const h = String(timestamp.getUTCHours()).padStart(2, '0');\n const mi = String(timestamp.getUTCMinutes()).padStart(2, '0');\n\n return `${y}${mo}${d}T${h}${mi}_${truncated}`;\n}\n"],"mappings":"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"}