{"version":3,"file":"index.cjs","sources":["../cacheability/src//main/index.ts"],"sourcesContent":["import { camelCase, isBoolean, isNumber, isPlainObject, isString, kebabCase } from 'lodash-es';\nimport {\n  type CacheControl,\n  type CacheHeaders,\n  type CacheabilityArgs as CacheabilityArguments,\n  type HeaderKeys,\n  type Metadata,\n  type ParsedCacheHeaders,\n} from './types.ts';\n\n/**\n * A utility class to parse, store and print http cache headers.\n */\nexport class Cacheability {\n  private static _headerKeys: HeaderKeys = ['cache-control', 'etag'];\n\n  private static _getDirectives(cacheControl: string): string[] {\n    return cacheControl.split(', ');\n  }\n\n  private static _parseCacheControl(cacheControl?: string): CacheControl {\n    const object: CacheControl = {};\n    if (!isString(cacheControl) || cacheControl.length === 0) return object;\n    const directives = Cacheability._getDirectives(cacheControl);\n\n    for (const directive of directives) {\n      if (directive.includes('=')) {\n        const [key, value] = directive.split('=');\n        object[camelCase(key)] = Number(value);\n        continue;\n      }\n\n      object[camelCase(directive)] = true;\n    }\n\n    return object;\n  }\n\n  private static _parseHeaders(headers: Headers | CacheHeaders): ParsedCacheHeaders {\n    let parsed: CacheHeaders = {};\n\n    if (headers instanceof Headers) {\n      for (const key of Cacheability._headerKeys) {\n        const headerValue = headers.get(key);\n\n        if (!headerValue) {\n          continue;\n        }\n\n        // camelCase returns a string type and we know the\n        // string is either 'cacheControl' or 'etag'.\n        // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n        const metadataKey = camelCase(key) as 'cacheControl' | 'etag';\n        parsed[metadataKey] = headerValue;\n      }\n    } else if (isPlainObject(headers)) {\n      parsed = headers;\n    }\n\n    return {\n      cacheControl: Cacheability._parseCacheControl(parsed.cacheControl),\n      etag: parsed.etag,\n    };\n  }\n\n  private static _setDefaultMetadata(): Metadata {\n    return {\n      cacheControl: {},\n      ttl: Number.POSITIVE_INFINITY,\n    };\n  }\n\n  private static _setMetadata({ cacheControl, etag }: ParsedCacheHeaders): Metadata {\n    return {\n      cacheControl,\n      etag,\n      ttl: Cacheability._setTTL(cacheControl),\n    };\n  }\n\n  private static _setTTL({ maxAge, noCache, noStore, sMaxage }: CacheControl): number {\n    if (noCache || noStore) return 0;\n    const sec = sMaxage ?? maxAge;\n    if (!isNumber(sec)) return Number.POSITIVE_INFINITY;\n    const ms = sec * 1000;\n    return Date.now() + ms;\n  }\n\n  private static _validateMetadata(metadata: Metadata): Metadata {\n    if (!isPlainObject(metadata)) return this._setDefaultMetadata();\n    const { cacheControl, etag, ttl } = metadata;\n\n    return {\n      cacheControl: isPlainObject(cacheControl) ? cacheControl : {},\n      etag: isString(etag) ? etag : undefined,\n      ttl: isNumber(ttl) ? ttl : Number.POSITIVE_INFINITY,\n    };\n  }\n\n  /**\n   * The property holds the Cacheability instance's parsed cache\n   * headers data, including cache control directives, etag, and\n   * a derived TTL timestamp.\n   */\n  public readonly metadata: Metadata;\n\n  constructor(arguments_: CacheabilityArguments = {}) {\n    const { cacheControl, headers, metadata } = arguments_;\n\n    if (cacheControl) {\n      this.metadata = Cacheability._setMetadata({\n        cacheControl: Cacheability._parseCacheControl(cacheControl),\n      });\n    } else if (headers) {\n      this.metadata = Cacheability._setMetadata(Cacheability._parseHeaders(headers));\n    } else if (metadata) {\n      this.metadata = Cacheability._validateMetadata(metadata);\n    } else {\n      this.metadata = Cacheability._setDefaultMetadata();\n    }\n  }\n\n  /**\n   * The method checks whether the TTL timestamp stored in the Cacheability\n   * instance is still valid, by comparing it to the current timestamp.\n   */\n  public checkTTL(): boolean {\n    return this.metadata.ttl > Date.now();\n  }\n\n  /**\n   * The method prints a cache-control header field value based on\n   * the Cacheability instance's metadata. The max-age and/or s-maxage\n   * are derived from the TTL stored in the metadata.\n   */\n  public printCacheControl(): string {\n    if (Object.values(this.metadata.cacheControl).length === 0) return '';\n    const cacheControl: CacheControl = { ...this.metadata.cacheControl };\n\n    if (cacheControl.sMaxage || cacheControl.maxAge) {\n      const maxAge = this.checkTTL() ? Math.round((this.metadata.ttl - Date.now()) / 1000) : 0;\n      if (cacheControl.sMaxage) cacheControl.sMaxage = maxAge;\n      if (cacheControl.maxAge) cacheControl.maxAge = maxAge;\n    }\n\n    const directives: string[] = [];\n\n    for (const key of Object.keys(cacheControl)) {\n      if (isBoolean(cacheControl[key])) {\n        directives.push(kebabCase(key));\n        continue;\n      }\n\n      directives.push(`${kebabCase(key)}=${String(cacheControl[key])}`);\n    }\n\n    return directives.join(', ');\n  }\n}\n"],"names":["Cacheability","_headerKeys","_getDirectives","cacheControl","split","_parseCacheControl","object","isString","length","directives","directive","includes","key","value","camelCase","Number","_parseHeaders","headers","parsed","Headers","headerValue","get","isPlainObject","etag","_setDefaultMetadata","ttl","POSITIVE_INFINITY","_setMetadata","_setTTL","maxAge","noCache","noStore","sMaxage","sec","isNumber","ms","Date","now","_validateMetadata","metadata","this","undefined","constructor","arguments_","checkTTL","printCacheControl","Object","values","Math","round","keys","isBoolean","push","kebabCase","String","join"],"mappings":"oFAaO,MAAMA,cACIC,YAA0B,CAAC,gBAAiB,QAE3D,qBAAeC,CAAeC,GAC5B,OAAOA,EAAaC,MAAM,KAC5B,CAEA,yBAAeC,CAAmBF,GAChC,MAAMG,EAAuB,CAAC,EAC9B,IAAKC,EAASJ,SAAAA,IAAyC,IAAxBA,EAAaK,OAAc,OAAOF,EACjE,MAAMG,EAAaT,EAAaE,eAAeC,GAE/C,IAAK,MAAMO,KAAaD,EACtB,GAAIC,EAAUC,SAAS,KAAvB,CACE,MAAOC,EAAKC,GAASH,EAAUN,MAAM,KACrCE,EAAOQ,EAAAA,UAAUF,IAAQG,OAAOF,EAElC,MAEAP,EAAOQ,EAAUJ,UAAAA,KAAc,EAGjC,OAAOJ,CACT,CAEA,oBAAeU,CAAcC,GAC3B,IAAIC,EAAuB,CAAC,EAE5B,GAAID,aAAmBE,QACrB,IAAK,MAAMP,KAAOZ,EAAaC,YAAa,CAC1C,MAAMmB,EAAcH,EAAQI,IAAIT,GAEhC,IAAKQ,EACH,SAOFF,EADoBJ,EAAUF,UAAAA,IACRQ,CACxB,MACSE,EAAAA,cAAcL,KACvBC,EAASD,GAGX,MAAO,CACLd,aAAcH,EAAaK,mBAAmBa,EAAOf,cACrDoB,KAAML,EAAOK,KAEjB,CAEA,0BAAeC,GACb,MAAO,CACLrB,aAAc,CAAC,EACfsB,IAAKV,OAAOW,kBAEhB,CAEA,mBAAeC,EAAaxB,aAAEA,EAAYoB,KAAEA,IAC1C,MAAO,CACLpB,eACAoB,OACAE,IAAKzB,EAAa4B,QAAQzB,GAE9B,CAEA,cAAeyB,EAAQC,OAAEA,EAAMC,QAAEA,EAAOC,QAAEA,EAAOC,QAAEA,IACjD,GAAIF,GAAWC,EAAS,OAAO,EAC/B,MAAME,EAAMD,GAAWH,EACvB,IAAKK,EAAAA,SAASD,GAAM,OAAOlB,OAAOW,kBAClC,MAAMS,EAAW,IAANF,EACX,OAAOG,KAAKC,MAAQF,CACtB,CAEA,wBAAeG,CAAkBC,GAC/B,IAAKjB,EAAAA,cAAciB,GAAW,OAAOC,KAAKhB,sBAC1C,MAAMrB,aAAEA,EAAYoB,KAAEA,EAAIE,IAAEA,GAAQc,EAEpC,MAAO,CACLpC,aAAcmB,EAAAA,cAAcnB,GAAgBA,EAAe,CAAC,EAC5DoB,KAAMhB,EAAAA,SAASgB,GAAQA,OAAOkB,EAC9BhB,IAAKS,EAAAA,SAAST,GAAOA,EAAMV,OAAOW,kBAEtC,CASAgB,WAAAA,CAAYC,EAAoC,IAC9C,MAAMxC,aAAEA,EAAYc,QAAEA,EAAOsB,SAAEA,GAAaI,EAG1CH,KAAKD,SADHpC,EACcH,EAAa2B,aAAa,CACxCxB,aAAcH,EAAaK,mBAAmBF,KAEvCc,EACOjB,EAAa2B,aAAa3B,EAAagB,cAAcC,IAC5DsB,EACOvC,EAAasC,kBAAkBC,GAE/BvC,EAAawB,qBAEjC,CAMA,QAAAoB,GACE,OAAOJ,KAAKD,SAASd,IAAMW,KAAKC,KAClC,CAOA,iBAAAQ,GACE,GAAyD,IAArDC,OAAOC,OAAOP,KAAKD,SAASpC,cAAcK,OAAc,MAAO,GACnE,MAAML,EAA6B,IAAKqC,KAAKD,SAASpC,cAEtD,GAAIA,EAAa6B,SAAW7B,EAAa0B,OAAQ,CAC/C,MAAMA,EAASW,KAAKI,WAAaI,KAAKC,OAAOT,KAAKD,SAASd,IAAMW,KAAKC,OAAS,KAAQ,EACnFlC,EAAa6B,UAAS7B,EAAa6B,QAAUH,GAC7C1B,EAAa0B,SAAQ1B,EAAa0B,OAASA,EACjD,CAEA,MAAMpB,EAAuB,GAE7B,IAAK,MAAMG,KAAOkC,OAAOI,KAAK/C,GACxBgD,YAAUhD,EAAaS,IACzBH,EAAW2C,KAAKC,YAAUzC,IAI5BH,EAAW2C,KAAK,GAAGC,EAAAA,UAAUzC,MAAQ0C,OAAOnD,EAAaS,OAG3D,OAAOH,EAAW8C,KAAK,KACzB"}