{"version":3,"file":"bundle-cache.cjs","names":["path","readFile","mkdir","createHash","writeFile","rename"],"sources":["../src/bundle-cache.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n/**\n * Bundle disk cache (ADR 0011 amendment — foreign-namespace resilience):\n * the child tier loads base workspaces from foreign origins; this cache\n * keeps the last-known-good bytes so an origin outage degrades to stale\n * serving instead of a hard failure. One cache root, two entrances: the\n * host's `/bundles/*` proxy handler (browser traffic) and the CLI fetch\n * adapter (boot-time outbound). Keyed exactly like the staged layout —\n * `<account>/<gateway>/<rest>` — but a separate root from BOS_BUNDLE_DIR:\n * cached bytes are a resilience artifact, never a deployment (plan 043 —\n * never point a runtime's BOS_BUNDLE_DIR at another runtime's layout).\n */\n\nexport function bundleCacheRoot(input?: { cacheDir?: string }): string {\n  return path.resolve(input?.cacheDir ?? process.env.BOS_BUNDLE_CACHE_DIR ?? joinDefault());\n}\n\nfunction joinDefault(): string {\n  return path.join(process.cwd(), \".bos\", \"bundle-cache\");\n}\n\nexport function bundleCachePath(url: string, input?: { cacheDir?: string }): string | null {\n  let parsed: URL;\n  try {\n    parsed = new URL(url);\n  } catch {\n    return null;\n  }\n  if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") return null;\n  const match = /^\\/bundles\\/([^/]+)\\/([^/]+)\\/(.+)$/.exec(parsed.pathname);\n  if (!match) return null;\n  const [, account, gateway, rest] = match;\n  if (!rest || rest.endsWith(\"/\")) return null;\n  let decoded: string;\n  try {\n    decoded = decodeURIComponent(rest);\n  } catch {\n    return null;\n  }\n  const root = path.resolve(bundleCacheRoot(input));\n  const filePath = path.resolve(root, account, gateway, decoded);\n  if (!filePath.startsWith(root + path.sep)) return null;\n  return filePath;\n}\n\nexport async function readBundleCache(\n  url: string,\n  input?: { cacheDir?: string },\n): Promise<Uint8Array | null> {\n  const filePath = bundleCachePath(url, input);\n  if (!filePath) return null;\n  try {\n    return new Uint8Array(await readFile(filePath));\n  } catch {\n    return null;\n  }\n}\n\nexport async function writeBundleCache(\n  url: string,\n  bytes: Uint8Array,\n  input?: { cacheDir?: string },\n): Promise<void> {\n  const filePath = bundleCachePath(url, input);\n  if (!filePath) return;\n  try {\n    await mkdir(path.dirname(filePath), { recursive: true });\n    const tmp = `${filePath}.${process.pid}.${createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 8)}.tmp`;\n    await writeFile(tmp, bytes);\n    await rename(tmp, filePath);\n  } catch {\n    // cache writes are best-effort — never fail the serving path\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAgBA,SAAgB,gBAAgB,OAAuC;CACrE,OAAOA,kBAAK,QAAQ,OAAO,YAAY,QAAQ,IAAI,wBAAwB,YAAY,CAAC;AAC1F;AAEA,SAAS,cAAsB;CAC7B,OAAOA,kBAAK,KAAK,QAAQ,IAAI,GAAG,QAAQ,cAAc;AACxD;AAEA,SAAgB,gBAAgB,KAAa,OAA8C;CACzF,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,OAAO;CACT;CACA,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SAAS,OAAO;CACxE,MAAM,QAAQ,sCAAsC,KAAK,OAAO,QAAQ;CACxE,IAAI,CAAC,OAAO,OAAO;CACnB,MAAM,GAAG,SAAS,SAAS,QAAQ;CACnC,IAAI,CAAC,QAAQ,KAAK,SAAS,GAAG,GAAG,OAAO;CACxC,IAAI;CACJ,IAAI;EACF,UAAU,mBAAmB,IAAI;CACnC,QAAQ;EACN,OAAO;CACT;CACA,MAAM,OAAOA,kBAAK,QAAQ,gBAAgB,KAAK,CAAC;CAChD,MAAM,WAAWA,kBAAK,QAAQ,MAAM,SAAS,SAAS,OAAO;CAC7D,IAAI,CAAC,SAAS,WAAW,OAAOA,kBAAK,GAAG,GAAG,OAAO;CAClD,OAAO;AACT;AAEA,eAAsB,gBACpB,KACA,OAC4B;CAC5B,MAAM,WAAW,gBAAgB,KAAK,KAAK;CAC3C,IAAI,CAAC,UAAU,OAAO;CACtB,IAAI;EACF,OAAO,IAAI,WAAW,UAAMC,2BAAS,QAAQ,CAAC;CAChD,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAsB,iBACpB,KACA,OACA,OACe;CACf,MAAM,WAAW,gBAAgB,KAAK,KAAK;CAC3C,IAAI,CAAC,UAAU;CACf,IAAI;EACF,UAAMC,wBAAMF,kBAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;EACvD,MAAM,MAAM,GAAG,SAAS,GAAG,QAAQ,IAAI,OAAGG,wBAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;EACvG,UAAMC,4BAAU,KAAK,KAAK;EAC1B,UAAMC,yBAAO,KAAK,QAAQ;CAC5B,QAAQ,CAER;AACF"}