{"version":3,"file":"index.mjs","sources":["../../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 = this._getDirectives(cacheControl);\n\n    for (const directive of directives) {\n      if (directive.includes('=')) {\n        const [key, value] = directive.split('=', 2);\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 this._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: this._parseCacheControl(parsed.cacheControl),\n      etag: parsed.etag,\n    };\n  }\n\n  private static _setDefaultMetadata(): Metadata {\n    return {\n      cacheControl: {},\n      ttl: Infinity,\n    };\n  }\n\n  private static _setMetadata({ cacheControl, etag }: ParsedCacheHeaders): Metadata {\n    return {\n      cacheControl,\n      etag,\n      ttl: this._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 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 : 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, value] of Object.entries(cacheControl)) {\n      if (isBoolean(value)) {\n        directives.push(kebabCase(key));\n        continue;\n      }\n\n      directives.push(`${kebabCase(key)}=${String(value)}`);\n    }\n\n    return directives.join(', ');\n  }\n}\n"],"names":["Cacheability","_headerKeys","_getDirectives","cacheControl","split","_parseCacheControl","object","isString","length","directives","this","directive","includes","key","value","camelCase","Number","_parseHeaders","headers","parsed","Headers","headerValue","get","isPlainObject","etag","_setDefaultMetadata","ttl","Infinity","_setMetadata","_setTTL","maxAge","noCache","noStore","sMaxage","sec","isNumber","ms","Date","now","_validateMetadata","metadata","undefined","constructor","arguments_","checkTTL","printCacheControl","Object","values","Math","round","entries","isBoolean","push","kebabCase","String","join"],"mappings":"mHAaO,MAAMA,cACIC,YAA0B,CAAC,gBAAiB,QAE3D,qBAAeC,CAAeC,GAC5B,OAAOA,EAAaC,MAAM,KAC5B,CAEA,yBAAeC,CAAmBF,GAChC,MAAMG,EAAuB,CAAA,EAC7B,IAAKC,EAASJ,IAAyC,IAAxBA,EAAaK,OAAc,OAAOF,EACjE,MAAMG,EAAaC,KAAKR,eAAeC,GAEvC,IAAK,MAAMQ,KAAaF,EAAY,CAClC,GAAIE,EAAUC,SAAS,KAAM,CAC3B,MAAOC,EAAKC,GAASH,EAAUP,MAAM,IAAK,GAC1CE,EAAOS,EAAUF,IAAQG,OAAOF,GAChC,QACF,CAEAR,EAAOS,EAAUJ,KAAc,CACjC,CAEA,OAAOL,CACT,CAEA,oBAAeW,CAAcC,GAC3B,IAAIC,EAAuB,CAAA,EAE3B,GAAID,aAAmBE,QACrB,IAAK,MAAMP,KAAOH,KAAKT,YAAa,CAClC,MAAMoB,EAAcH,EAAQI,IAAIT,GAEhC,IAAKQ,EACH,SAOFF,EADoBJ,EAAUF,IACRQ,CACxB,MACSE,EAAcL,KACvBC,EAASD,GAGX,MAAO,CACLf,aAAcO,KAAKL,mBAAmBc,EAAOhB,cAC7CqB,KAAML,EAAOK,KAEjB,CAEA,0BAAeC,GACb,MAAO,CACLtB,aAAc,CAAA,EACduB,IAAKC,IAET,CAEA,mBAAeC,EAAazB,aAAEA,EAAYqB,KAAEA,IAC1C,MAAO,CACLrB,eACAqB,OACAE,IAAKhB,KAAKmB,QAAQ1B,GAEtB,CAEA,cAAe0B,EAAQC,OAAEA,EAAMC,QAAEA,EAAOC,QAAEA,EAAOC,QAAEA,IACjD,GAAIF,GAAWC,EAAS,OAAO,EAC/B,MAAME,EAAMD,GAAWH,EACvB,IAAKK,EAASD,GAAM,OAAOP,IAC3B,MAAMS,EAAW,IAANF,EACX,OAAOG,KAAKC,MAAQF,CACtB,CAEA,wBAAeG,CAAkBC,GAC/B,IAAKjB,EAAciB,GAAW,OAAO9B,KAAKe,sBAC1C,MAAMtB,aAAEA,EAAYqB,KAAEA,EAAIE,IAAEA,GAAQc,EAEpC,MAAO,CACLrC,aAAcoB,EAAcpB,GAAgBA,EAAe,CAAA,EAC3DqB,KAAMjB,EAASiB,GAAQA,OAAOiB,EAC9Bf,IAAKS,EAAST,GAAOA,EAAMC,IAE/B,CASA,WAAAe,CAAYC,EAAoC,IAC9C,MAAMxC,aAAEA,EAAYe,QAAEA,EAAOsB,SAAEA,GAAaG,EAG1CjC,KAAK8B,SADHrC,EACcH,EAAa4B,aAAa,CACxCzB,aAAcH,EAAaK,mBAAmBF,KAEvCe,EACOlB,EAAa4B,aAAa5B,EAAaiB,cAAcC,IAC5DsB,EACOxC,EAAauC,kBAAkBC,GAE/BxC,EAAayB,qBAEjC,CAMA,QAAAmB,GACE,OAAOlC,KAAK8B,SAASd,IAAMW,KAAKC,KAClC,CAOA,iBAAAO,GACE,GAAyD,IAArDC,OAAOC,OAAOrC,KAAK8B,SAASrC,cAAcK,OAAc,MAAO,GACnE,MAAML,EAA6B,IAAKO,KAAK8B,SAASrC,cAEtD,GAAIA,EAAa8B,SAAW9B,EAAa2B,OAAQ,CAC/C,MAAMA,EAASpB,KAAKkC,WAAaI,KAAKC,OAAOvC,KAAK8B,SAASd,IAAMW,KAAKC,OAAS,KAAQ,EACnFnC,EAAa8B,UAAS9B,EAAa8B,QAAUH,GAC7C3B,EAAa2B,SAAQ3B,EAAa2B,OAASA,EACjD,CAEA,MAAMrB,EAAuB,GAE7B,IAAK,MAAOI,EAAKC,KAAUgC,OAAOI,QAAQ/C,GACpCgD,EAAUrC,GACZL,EAAW2C,KAAKC,EAAUxC,IAI5BJ,EAAW2C,KAAK,GAAGC,EAAUxC,MAAQyC,OAAOxC,MAG9C,OAAOL,EAAW8C,KAAK,KACzB"}