{"version":3,"sources":["../../src/utils/video-bite-id.ts"],"names":[],"mappings":";;;AAiBO,IAAM,UAAA,GAAa;AAGnB,SAAS,cAAA,GAAyB;AACvC,EAAA,OAAO,MAAA,CAAO,YAAW,CAAE,OAAA,CAAQ,MAAM,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AACzD;AAOO,SAAS,cAAyC,KAAA,EAAuD;AAC9G,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAY;AAC7B,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,CAAI,CAAA,IAAA,KAAQ;AAC5B,IAAA,MAAM,KAAA,GAAQ,OAAO,IAAA,CAAK,EAAA,KAAO,YAAY,UAAA,CAAW,IAAA,CAAK,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,IAAA,CAAK,GAAA,CAAI,KAAK,EAAE,CAAA;AAC1F,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,IAAA,CAAK,GAAA,CAAI,KAAK,EAAY,CAAA;AAC1B,MAAA,OAAO,IAAA;AAAA,IACT;AACA,IAAA,OAAA,GAAU,IAAA;AACV,IAAA,IAAI,KAAK,cAAA,EAAe;AACxB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,OAAQ,cAAA,EAAe;AACzC,IAAA,IAAA,CAAK,IAAI,EAAE,CAAA;AACX,IAAA,OAAO,EAAE,GAAG,IAAA,EAAM,EAAA,EAAG;AAAA,EACvB,CAAC,CAAA;AACD,EAAA,OAAO,OAAA,GAAU,EAAE,KAAA,EAAO,GAAA,EAAK,SAAQ,GAAI,EAAE,OAAqB,OAAA,EAAQ;AAC5E;AAiBO,SAAS,gBAAA,CAAiB,UAAwB,QAAA,EAAiC;AACxF,EAAA,IAAI,QAAA,CAAS,MAAM,QAAA,CAAS,EAAA,IAAM,SAAS,EAAA,KAAO,QAAA,CAAS,IAAI,OAAO,IAAA;AACtE,EAAA,IAAI,QAAA,CAAS,GAAA,KAAQ,QAAA,CAAS,GAAA,EAAK,OAAO,IAAA;AAC1C,EAAA,IAAI,SAAS,UAAA,IAAc,QAAA,CAAS,GAAA,KAAQ,QAAA,CAAS,YAAY,OAAO,IAAA;AACxE,EAAA,OAAO,KAAA;AACT","file":"video-bite-id.cjs","sourcesContent":["// Video-bite element identity — SSOT for the stable per-element `id` on\n// `video_bites` / `teasers` jsonb elements (see types/video-processing.ts\n// `VideoTeaser.id`) and for the ONE element-matching predicate shared by the\n// hub's bite writers (`saveEntityVideoBitesResult` merge + the\n// `reconcileIncomingBites` clobber guard).\n//\n// Isomorphic by design: runs in the admin editor (browser) and in the hub's\n// Mux reconciliation sweep (Node). Zero dependencies.\n\n/** Shape every matcher/stamper here operates on — the identity-relevant subset of VideoTeaser. */\nexport interface BiteIdentity {\n  id?: string\n  url: string\n  source_url?: string\n}\n\n/** 8 lowercase hex chars. Collision-safe within one array (n≤~100 → p≈1e-6); identity is always scoped to (table, row, column). */\nexport const BITE_ID_RE = /^[0-9a-f]{8}$/\n\n/** Generate a new 8-hex bite element id. */\nexport function generateBiteId(): string {\n  return crypto.randomUUID().replace(/-/g, '').slice(0, 8)\n}\n\n/**\n * Stamp missing ids and re-stamp in-array duplicates (copy/pasted bites).\n * Returns the SAME array reference when nothing changed so callers can cheaply\n * skip persistence (`changed === false` ⇒ no write needed).\n */\nexport function ensureBiteIds<T extends { id?: string }>(bites: readonly T[]): { bites: T[]; changed: boolean } {\n  const seen = new Set<string>()\n  let changed = false\n  const out = bites.map(bite => {\n    const valid = typeof bite.id === 'string' && BITE_ID_RE.test(bite.id) && !seen.has(bite.id)\n    if (valid) {\n      seen.add(bite.id as string)\n      return bite\n    }\n    changed = true\n    let id = generateBiteId()\n    while (seen.has(id)) id = generateBiteId()\n    seen.add(id)\n    return { ...bite, id }\n  })\n  return changed ? { bites: out, changed } : { bites: bites as T[], changed }\n}\n\n/**\n * The ONE element-identity predicate. Matching legs (any-of, NOT\n * short-circuit-on-id-inequality — two sides can carry DIFFERENT ids for\n * the same clip, e.g. a redelivered Vizard ingest regenerates ids):\n *   1. `id` equality — when both sides carry one and they match.\n *   2. `incoming.url === existing.url` — plain url (pre-flip stale sessions,\n *      plain re-saves).\n *   3. `incoming.url === existing.source_url` — the incoming side still holds\n *      the original storage URL of an element the Mux pipeline already flipped\n *      to HLS (redelivered ingests, stale editor arrays).\n *\n * Callers that match a whole incoming array against an existing array MUST\n * consume each existing element at most once (multiset matching) — duplicate\n * incoming urls must not all bind to one element and clone its id.\n */\nexport function matchBiteElement(existing: BiteIdentity, incoming: BiteIdentity): boolean {\n  if (existing.id && incoming.id && existing.id === incoming.id) return true\n  if (incoming.url === existing.url) return true\n  if (existing.source_url && incoming.url === existing.source_url) return true\n  return false\n}\n"]}