{"version":3,"file":"fingerprint.mjs","names":[],"sources":["../src/fingerprint.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { BosConfig } from \"./types\";\n\n/**\n * The published pointer's version identity (atomic-deploys 08/12): a hash\n * over every slot's `pin` — cheap to compute (no version-manifest fetch), and\n * stable exactly when the deployed set is. Shared so the CLI and the host\n * compute the same identity for the same pointer.\n */\nexport function pointerFingerprint(config: BosConfig): string {\n  const parts: Array<string> = [];\n  const slot = (prefix: string, s: { pin?: unknown; integrity?: unknown } | undefined) => {\n    if (!s) return;\n    const pin = s.pin as { manifest?: unknown; integrity?: unknown } | undefined;\n    const pinPart =\n      pin && typeof pin.manifest === \"string\" && typeof pin.integrity === \"string\"\n        ? `${pin.manifest}:${pin.integrity}`\n        : \"\";\n    const directPart = typeof s.integrity === \"string\" ? s.integrity : \"\";\n    parts.push(`${prefix}:${pinPart}:${directPart}`);\n  };\n  for (const [key, entry] of Object.entries(config.app ?? {})) {\n    slot(`app.${key}`, entry);\n    slot(`app.${key}.ui`, (entry as { ui?: { pin?: unknown; integrity?: unknown } }).ui);\n  }\n  for (const [key, entry] of Object.entries(config.plugins ?? {})) {\n    if (typeof entry === \"string\") {\n      parts.push(`plugins.${key}:${entry}`);\n      continue;\n    }\n    slot(`plugins.${key}`, entry);\n    slot(`plugins.${key}.ui`, (entry as { ui?: { pin?: unknown; integrity?: unknown } }).ui);\n  }\n  return createHash(\"sha256\").update(parts.join(\"|\")).digest(\"hex\").slice(0, 16);\n}\n\n/** Per-slot pin ids for display (slot → versioned manifest filename). */\nexport function slotPins(config: BosConfig): Record<string, string> {\n  const pins: Record<string, string> = {};\n  const slot = (prefix: string, s: { pin?: unknown } | undefined) => {\n    if (!s) return;\n    const manifest = (s.pin as { manifest?: unknown } | undefined)?.manifest;\n    if (typeof manifest === \"string\") pins[prefix] = manifest;\n  };\n  for (const [key, entry] of Object.entries(config.app ?? {})) {\n    slot(`app.${key}`, entry);\n    slot(`app.${key}.ui`, (entry as { ui?: { pin?: unknown } }).ui);\n  }\n  for (const [key, entry] of Object.entries(config.plugins ?? {})) {\n    if (typeof entry === \"string\") continue;\n    slot(`plugins.${key}`, entry);\n    slot(`plugins.${key}.ui`, (entry as { ui?: { pin?: unknown } }).ui);\n  }\n  return pins;\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,mBAAmB,QAA2B;CAC5D,MAAM,QAAuB,CAAC;CAC9B,MAAM,QAAQ,QAAgB,MAA0D;EACtF,IAAI,CAAC,GAAG;EACR,MAAM,MAAM,EAAE;EACd,MAAM,UACJ,OAAO,OAAO,IAAI,aAAa,YAAY,OAAO,IAAI,cAAc,WAChE,GAAG,IAAI,SAAS,GAAG,IAAI,cACvB;EACN,MAAM,aAAa,OAAO,EAAE,cAAc,WAAW,EAAE,YAAY;EACnE,MAAM,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,YAAY;CACjD;CACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,OAAO,CAAC,CAAC,GAAG;EAC3D,KAAK,OAAO,OAAO,KAAK;EACxB,KAAK,OAAO,IAAI,MAAO,MAA0D,EAAE;CACrF;CACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,WAAW,CAAC,CAAC,GAAG;EAC/D,IAAI,OAAO,UAAU,UAAU;GAC7B,MAAM,KAAK,WAAW,IAAI,GAAG,OAAO;GACpC;EACF;EACA,KAAK,WAAW,OAAO,KAAK;EAC5B,KAAK,WAAW,IAAI,MAAO,MAA0D,EAAE;CACzF;CACA,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;AAC/E;;AAGA,SAAgB,SAAS,QAA2C;CAClE,MAAM,OAA+B,CAAC;CACtC,MAAM,QAAQ,QAAgB,MAAqC;EACjE,IAAI,CAAC,GAAG;EACR,MAAM,WAAY,EAAE,KAA4C;EAChE,IAAI,OAAO,aAAa,UAAU,KAAK,UAAU;CACnD;CACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,OAAO,CAAC,CAAC,GAAG;EAC3D,KAAK,OAAO,OAAO,KAAK;EACxB,KAAK,OAAO,IAAI,MAAO,MAAqC,EAAE;CAChE;CACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,WAAW,CAAC,CAAC,GAAG;EAC/D,IAAI,OAAO,UAAU,UAAU;EAC/B,KAAK,WAAW,OAAO,KAAK;EAC5B,KAAK,WAAW,IAAI,MAAO,MAAqC,EAAE;CACpE;CACA,OAAO;AACT"}