{"version":3,"file":"cache-hints.mjs","sources":["../../../src/lib/api/cache-hints.ts"],"sourcesContent":["/**\n * @file Cache Hints\n * @description Cache hint extraction and management utilities for API responses.\n * Handles parsing of Cache-Control headers and generation of cache metadata.\n */\n\nimport type { CacheInfo, ResponseHeaders } from './types';\n\n/**\n * Cache hint extraction result\n */\nexport interface CacheHints {\n  /** Whether response is cacheable */\n  cacheable: boolean;\n  /** Time-to-live in seconds */\n  maxAge?: number;\n  /** Stale-while-revalidate window in seconds */\n  staleWhileRevalidate?: number;\n  /** Entity tag for validation */\n  etag?: string;\n  /** Last modified timestamp */\n  lastModified?: Date;\n  /** Whether must revalidate before serving stale */\n  mustRevalidate: boolean;\n  /** Whether response is private (user-specific) */\n  isPrivate: boolean;\n  /** Whether response should not be stored */\n  noStore: boolean;\n  /** Whether response should not be cached */\n  noCache: boolean;\n}\n\n/**\n * Extract cache hints from response headers\n *\n * @param headers - Response headers\n * @returns Parsed cache hints\n *\n * @example\n * ```typescript\n * const hints = extractCacheHints(response.headers);\n * if (hints.cacheable && hints.maxAge) {\n *   cache.set(key, data, hints.maxAge * 1000);\n * }\n * ```\n */\nexport function extractCacheHints(headers: ResponseHeaders): CacheHints {\n  const cacheControl = headers['cache-control'] ?? '';\n  const { etag, 'last-modified': lastModified } = headers;\n\n  // Parse Cache-Control directives\n  const directives = parseCacheControlHeader(cacheControl);\n\n  return {\n    cacheable: directives['no-store'] === undefined && directives['no-cache'] === undefined,\n    maxAge:\n      directives['max-age'] !== undefined &&\n      directives['max-age'] !== null &&\n      directives['max-age'] !== ''\n        ? parseInt(directives['max-age'], 10)\n        : undefined,\n    staleWhileRevalidate:\n      directives['stale-while-revalidate'] !== undefined &&\n      directives['stale-while-revalidate'] !== null &&\n      directives['stale-while-revalidate'] !== ''\n        ? parseInt(directives['stale-while-revalidate'], 10)\n        : undefined,\n    etag,\n    lastModified:\n      lastModified !== undefined && lastModified !== null && lastModified !== ''\n        ? new Date(lastModified)\n        : undefined,\n    mustRevalidate: 'must-revalidate' in directives,\n    isPrivate: 'private' in directives,\n    noStore: 'no-store' in directives,\n    noCache: 'no-cache' in directives,\n  };\n}\n\n/**\n * Parse Cache-Control header into directives\n */\nfunction parseCacheControlHeader(header: string): Record<string, string> {\n  const directives: Record<string, string> = {};\n\n  if (!header) return directives;\n\n  const parts = header.split(',').map((p) => p.trim());\n\n  for (const part of parts) {\n    const [key, value] = part.split('=').map((s) => s.trim());\n    if (key !== undefined && key !== null && key !== '')\n      directives[key.toLowerCase()] = value ?? 'true';\n  }\n\n  return directives;\n}\n\n/**\n * Build cache info from response\n */\nexport function buildCacheInfo(\n  headers: ResponseHeaders,\n  fromCache = false,\n  age?: number\n): CacheInfo {\n  const hints = extractCacheHints(headers);\n\n  return {\n    fromCache,\n    hitType: fromCache ? 'memory' : undefined,\n    age,\n    expiresAt:\n      hints.maxAge !== undefined && hints.maxAge > 0 ? Date.now() + hints.maxAge * 1000 : undefined,\n    etag: hints.etag,\n  };\n}\n\n/**\n * Generate Cache-Control header value\n *\n * @example\n * ```typescript\n * const cacheControl = generateCacheControl({\n *   maxAge: 3600,\n *   isPrivate: true,\n *   staleWhileRevalidate: 60,\n * });\n * // Returns: 'private, max-age=3600, stale-while-revalidate=60'\n * ```\n */\nexport function generateCacheControl(hints: Partial<CacheHints>): string {\n  const directives: string[] = [];\n\n  if (hints.noStore === true) {\n    return 'no-store';\n  }\n\n  if (hints.noCache === true) {\n    directives.push('no-cache');\n  }\n\n  if (hints.isPrivate === true) {\n    directives.push('private');\n  } else {\n    directives.push('public');\n  }\n\n  if (hints.maxAge !== undefined) {\n    directives.push(`max-age=${hints.maxAge}`);\n  }\n\n  if (hints.staleWhileRevalidate !== undefined) {\n    directives.push(`stale-while-revalidate=${hints.staleWhileRevalidate}`);\n  }\n\n  if (hints.mustRevalidate === true) {\n    directives.push('must-revalidate');\n  }\n\n  return directives.join(', ');\n}\n"],"names":["extractCacheHints","headers","cacheControl","etag","lastModified","directives","parseCacheControlHeader","header","parts","p","part","key","value","s","buildCacheInfo","fromCache","age","hints","generateCacheControl"],"mappings":"AA8CO,SAASA,EAAkBC,GAAsC;AACtE,QAAMC,IAAeD,EAAQ,eAAe,KAAK,IAC3C,EAAE,MAAAE,GAAM,iBAAiBC,EAAA,IAAiBH,GAG1CI,IAAaC,EAAwBJ,CAAY;AAEvD,SAAO;AAAA,IACL,WAAWG,EAAW,UAAU,MAAM,UAAaA,EAAW,UAAU,MAAM;AAAA,IAC9E,QACEA,EAAW,SAAS,MAAM,UAC1BA,EAAW,SAAS,MAAM,QAC1BA,EAAW,SAAS,MAAM,KACtB,SAASA,EAAW,SAAS,GAAG,EAAE,IAClC;AAAA,IACN,sBACEA,EAAW,wBAAwB,MAAM,UACzCA,EAAW,wBAAwB,MAAM,QACzCA,EAAW,wBAAwB,MAAM,KACrC,SAASA,EAAW,wBAAwB,GAAG,EAAE,IACjD;AAAA,IACN,MAAAF;AAAA,IACA,cACgCC,KAAiB,QAAQA,MAAiB,KACpE,IAAI,KAAKA,CAAY,IACrB;AAAA,IACN,gBAAgB,qBAAqBC;AAAA,IACrC,WAAW,aAAaA;AAAA,IACxB,SAAS,cAAcA;AAAA,IACvB,SAAS,cAAcA;AAAA,EAAA;AAE3B;AAKA,SAASC,EAAwBC,GAAwC;AACvE,QAAMF,IAAqC,CAAA;AAE3C,MAAI,CAACE,EAAQ,QAAOF;AAEpB,QAAMG,IAAQD,EAAO,MAAM,GAAG,EAAE,IAAI,CAACE,MAAMA,EAAE,MAAM;AAEnD,aAAWC,KAAQF,GAAO;AACxB,UAAM,CAACG,GAAKC,CAAK,IAAIF,EAAK,MAAM,GAAG,EAAE,IAAI,CAACG,MAAMA,EAAE,MAAM;AACxD,IAAyBF,KAAQ,QAAQA,MAAQ,OAC/CN,EAAWM,EAAI,aAAa,IAAIC,KAAS;AAAA,EAC7C;AAEA,SAAOP;AACT;AAKO,SAASS,EACdb,GACAc,IAAY,IACZC,GACW;AACX,QAAMC,IAAQjB,EAAkBC,CAAO;AAEvC,SAAO;AAAA,IACL,WAAAc;AAAA,IACA,SAASA,IAAY,WAAW;AAAA,IAChC,KAAAC;AAAA,IACA,WACEC,EAAM,WAAW,UAAaA,EAAM,SAAS,IAAI,KAAK,IAAA,IAAQA,EAAM,SAAS,MAAO;AAAA,IACtF,MAAMA,EAAM;AAAA,EAAA;AAEhB;AAeO,SAASC,EAAqBD,GAAoC;AACvE,QAAMZ,IAAuB,CAAA;AAE7B,SAAIY,EAAM,YAAY,KACb,cAGLA,EAAM,YAAY,MACpBZ,EAAW,KAAK,UAAU,GAGxBY,EAAM,cAAc,KACtBZ,EAAW,KAAK,SAAS,IAEzBA,EAAW,KAAK,QAAQ,GAGtBY,EAAM,WAAW,UACnBZ,EAAW,KAAK,WAAWY,EAAM,MAAM,EAAE,GAGvCA,EAAM,yBAAyB,UACjCZ,EAAW,KAAK,0BAA0BY,EAAM,oBAAoB,EAAE,GAGpEA,EAAM,mBAAmB,MAC3BZ,EAAW,KAAK,iBAAiB,GAG5BA,EAAW,KAAK,IAAI;AAC7B;"}