{"version":3,"file":"invariants-5wBqXSaJ.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,CAAC,CAAC,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"}