{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/** Matches Cache-Control directives and their optional values (RFC 9111 §5.2) */\nconst HEADER_REGEXP = /([a-zA-Z][a-zA-Z_-]*)\\s*(?:=(?:\"([^\"]*)\"|([^ \\t\",;]*)))?/g\n\n/** RFC 9110 §5.6.2: token = 1*tchar */\nconst TOKEN_REGEXP = /^[!#$%&'*+\\-.^_`|~\\w]+$/\n\n/**\n * Represents the possible values of a `Cache-Control` HTTP header.\n *\n * All properties are optional, allowing partial objects to be passed to {@link format}.\n */\nexport interface CacheControlValue {\n  maxAge?: number | null\n  sharedMaxAge?: number | null\n  maxStale?: boolean | null\n  maxStaleDuration?: number | null\n  minFresh?: number | null\n  immutable?: boolean | null\n  mustRevalidate?: boolean | null\n  mustUnderstand?: boolean | null\n  noCache?: boolean | null\n  noCacheFields?: string[] | null\n  noStore?: boolean | null\n  noTransform?: boolean | null\n  onlyIfCached?: boolean | null\n  private?: boolean | null\n  privateFields?: string[] | null\n  proxyRevalidate?: boolean | null\n  public?: boolean | null\n  staleWhileRevalidate?: number | null\n  staleIfError?: number | null\n}\n\nconst STRINGS = {\n  maxAge: 'max-age',\n  sharedMaxAge: 's-maxage',\n  maxStale: 'max-stale',\n  minFresh: 'min-fresh',\n  immutable: 'immutable',\n  mustRevalidate: 'must-revalidate',\n  mustUnderstand: 'must-understand',\n  noCache: 'no-cache',\n  noStore: 'no-store',\n  noTransform: 'no-transform',\n  onlyIfCached: 'only-if-cached',\n  private: 'private',\n  proxyRevalidate: 'proxy-revalidate',\n  public: 'public',\n  staleWhileRevalidate: 'stale-while-revalidate',\n  staleIfError: 'stale-if-error',\n}\n\nfunction parseBooleanOnly(value: string | null) {\n  return value === null\n}\n\nfunction parseFieldNames(value: string): string[] | null {\n  const fields = value\n    .split(',')\n    .map((f) => f.trim())\n    .filter(Boolean)\n  return fields.length > 0 ? fields : null\n}\n\nfunction formatDuration(tokens: string[], name: string, value: number | null | undefined) {\n  if (typeof value === 'number') {\n    tokens.push(`${name}=${value}`)\n  }\n}\n\nfunction formatBoolean(tokens: string[], name: string, value: boolean | null | undefined) {\n  if (value) {\n    tokens.push(name)\n  }\n}\n\nfunction formatFieldNames(\n  tokens: string[],\n  name: string,\n  value: boolean | null | undefined,\n  fields: string[] | null | undefined,\n) {\n  if (value) {\n    const valid = fields?.map((f) => f.trim()).filter((f) => TOKEN_REGEXP.test(f))\n    if (valid && valid.length > 0) {\n      tokens.push(`${name}=\"${valid.join(', ')}\"`)\n    } else {\n      tokens.push(name)\n    }\n  }\n}\n\nfunction parseDuration(value: string | null) {\n  if (!value) {\n    return null\n  }\n\n  const duration: number = Number.parseInt(value, 10)\n\n  if (!Number.isFinite(duration) || duration < 0) {\n    return null\n  }\n\n  return duration\n}\n\n/**\n * A parsed `Cache-Control` HTTP header.\n *\n * Properties are initialized to `null` (unset). After calling {@link CacheControl.parse},\n * boolean directives that were absent in the header are set to `false`,\n * while duration directives that were absent remain `null`.\n */\nexport class CacheControl implements CacheControlValue {\n  maxAge: number | null = null\n  sharedMaxAge: number | null = null\n  maxStale: boolean | null = null\n  maxStaleDuration: number | null = null\n  minFresh: number | null = null\n  immutable: boolean | null = null\n  mustRevalidate: boolean | null = null\n  mustUnderstand: boolean | null = null\n  noCache: boolean | null = null\n  noCacheFields: string[] | null = null\n  noStore: boolean | null = null\n  noTransform: boolean | null = null\n  onlyIfCached: boolean | null = null\n  private: boolean | null = null\n  privateFields: string[] | null = null\n  proxyRevalidate: boolean | null = null\n  public: boolean | null = null\n  staleWhileRevalidate: number | null = null\n  staleIfError: number | null = null\n\n  parse(header: string | undefined): this {\n    if (!header || header.length === 0) {\n      return this\n    }\n\n    const values: Record<string, string | null> = {}\n\n    for (const match of header.matchAll(HEADER_REGEXP)) {\n      values[match[1].toLowerCase()] = match[2] ?? match[3] ?? null\n    }\n\n    this.maxAge = parseDuration(values[STRINGS.maxAge])\n    this.sharedMaxAge = parseDuration(values[STRINGS.sharedMaxAge])\n\n    this.maxStale = parseBooleanOnly(values[STRINGS.maxStale])\n    this.maxStaleDuration = parseDuration(values[STRINGS.maxStale])\n    if (this.maxStaleDuration !== null) {\n      this.maxStale = true\n    }\n\n    this.minFresh = parseDuration(values[STRINGS.minFresh])\n\n    // NOTE: RFC 8246 says immutable arguments MUST be ignored, meaning\n    // immutable=foo should still parse as true. We intentionally treat it\n    // as false for consistency with the other boolean directives.\n    this.immutable = parseBooleanOnly(values[STRINGS.immutable])\n    this.mustRevalidate = parseBooleanOnly(values[STRINGS.mustRevalidate])\n    this.mustUnderstand = parseBooleanOnly(values[STRINGS.mustUnderstand])\n\n    if (STRINGS.noCache in values) {\n      this.noCache = true\n      const v = values[STRINGS.noCache]\n      this.noCacheFields = v ? parseFieldNames(v) : null\n    } else {\n      this.noCache = false\n      this.noCacheFields = null\n    }\n\n    this.noStore = parseBooleanOnly(values[STRINGS.noStore])\n    this.noTransform = parseBooleanOnly(values[STRINGS.noTransform])\n    this.onlyIfCached = parseBooleanOnly(values[STRINGS.onlyIfCached])\n\n    if (STRINGS.private in values) {\n      this.private = true\n      const v = values[STRINGS.private]\n      this.privateFields = v ? parseFieldNames(v) : null\n    } else {\n      this.private = false\n      this.privateFields = null\n    }\n\n    this.proxyRevalidate = parseBooleanOnly(values[STRINGS.proxyRevalidate])\n    this.public = parseBooleanOnly(values[STRINGS.public])\n    this.staleWhileRevalidate = parseDuration(values[STRINGS.staleWhileRevalidate])\n    this.staleIfError = parseDuration(values[STRINGS.staleIfError])\n\n    return this\n  }\n\n  format(): string {\n    const tokens: string[] = []\n\n    formatDuration(tokens, STRINGS.maxAge, this.maxAge)\n    formatDuration(tokens, STRINGS.sharedMaxAge, this.sharedMaxAge)\n\n    if (this.maxStale) {\n      if (typeof this.maxStaleDuration === 'number') {\n        tokens.push(`${STRINGS.maxStale}=${this.maxStaleDuration}`)\n      } else {\n        tokens.push(STRINGS.maxStale)\n      }\n    }\n\n    formatDuration(tokens, STRINGS.minFresh, this.minFresh)\n    formatBoolean(tokens, STRINGS.immutable, this.immutable)\n    formatBoolean(tokens, STRINGS.mustRevalidate, this.mustRevalidate)\n    formatBoolean(tokens, STRINGS.mustUnderstand, this.mustUnderstand)\n    formatFieldNames(tokens, STRINGS.noCache, this.noCache, this.noCacheFields)\n    formatBoolean(tokens, STRINGS.noStore, this.noStore)\n    formatBoolean(tokens, STRINGS.noTransform, this.noTransform)\n    formatBoolean(tokens, STRINGS.onlyIfCached, this.onlyIfCached)\n    formatFieldNames(tokens, STRINGS.private, this.private, this.privateFields)\n    formatBoolean(tokens, STRINGS.proxyRevalidate, this.proxyRevalidate)\n    formatBoolean(tokens, STRINGS.public, this.public)\n    formatDuration(tokens, STRINGS.staleWhileRevalidate, this.staleWhileRevalidate)\n    formatDuration(tokens, STRINGS.staleIfError, this.staleIfError)\n\n    return tokens.join(', ')\n  }\n}\n\n/**\n * Parses a `Cache-Control` HTTP header value into a {@link CacheControl} instance.\n *\n * @param header - The raw header string (e.g. `\"public, max-age=31536000\"`).\n *   If `undefined` or empty, all properties remain `null` (unset).\n * @returns A {@link CacheControl} instance with the parsed directive values.\n */\nexport function parse(header?: string): CacheControl {\n  const cc = new CacheControl()\n  return cc.parse(header)\n}\n\n/**\n * Formats a {@link CacheControlValue} object into a `Cache-Control` HTTP header string.\n *\n * @param cc - An object (or {@link CacheControl} instance) with directive values.\n *   Only truthy booleans and non-null numbers are included in the output.\n * @returns The formatted header string (e.g. `\"public, max-age=31536000\"`),\n *   or an empty string if no directives are set.\n */\nexport function format(cc: CacheControlValue): string {\n  if (!(cc instanceof CacheControl)) {\n    return CacheControl.prototype.format.call(cc)\n  }\n\n  return cc.format()\n}\n"],"mappings":"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","names":[]}