{"version":3,"file":"options.cjs","names":[],"sources":["../../src/utils/options.ts"],"sourcesContent":["import type { IncomingMessage } from \"node:http\";\nimport type { Etag } from \"./etag\";\n\ntype EtagMatchType =\n  | {\n      type: \"match\";\n      value: Etag[];\n    }\n  | {\n      type: \"wildcard\";\n    };\n\nclass EtagMatch {\n  private constructor(private readonly etagMatch: EtagMatchType) {}\n\n  static parse(etagMatch: string | undefined): EtagMatch | null {\n    if (etagMatch === undefined) return null;\n    if (etagMatch === \"*\") {\n      return new EtagMatch({ type: \"wildcard\" });\n    } else {\n      return new EtagMatch({\n        type: \"match\",\n        value: etagMatch.split(\",\").map((etag) => etag.trim() as Etag),\n      });\n    }\n  }\n\n  matches(etag: Etag): boolean {\n    switch (this.etagMatch.type) {\n      case \"match\":\n        return this.etagMatch.value.includes(etag);\n      case \"wildcard\":\n        return true;\n    }\n  }\n}\n\nclass DateMatch {\n  private constructor(private readonly mtime: Date) {}\n\n  static parse(dateMatch: string | undefined): DateMatch | null {\n    if (dateMatch === undefined) return null;\n    const time = Date.parse(dateMatch);\n    if (isNaN(time)) return null;\n    return new DateMatch(new Date(time));\n  }\n\n  matches(mtime: Date): boolean {\n    return this.mtime.getTime() / 1_000 === mtime.getTime() / 1_000;\n  }\n}\n\nexport class Options {\n  /**\n   * See <https://datatracker.ietf.org/doc/html/rfc9110#name-if-match>\n   *\n   * Examples:\n   *\n   * ```text\n   * If-Match: \"xyzzy\"\n   * If-Match: \"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"\n   * If-Match: *\n   * ```\n   */\n  private ifMatch: EtagMatch | null;\n\n  /**\n   * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.2>\n   *\n   * Examples:\n   *\n   * ```text\n   * If-None-Match: \"xyzzy\"\n   * If-None-Match: \"xyzzy\", \"r2d2xxxx\", \"c3piozzzz\"\n   * If-None-Match: *\n   * ```\n   */\n  private ifNoneMatch: EtagMatch | null;\n\n  /**\n   * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.3>\n   *\n   * Examples:\n   *\n   * ```text\n   * If-Modified-Since: Mon, 07 Jan 2002 19:43:36 GMT\n   * ```\n   */\n  private ifModifiedSince: DateMatch | null;\n\n  /**\n   * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.4>\n   *\n   * Examples:\n   *\n   * ```text\n   * If-Unmodified-Since: Mon, 07 Jan 2002 19:43:36 GMT\n   * ```\n   */\n  private ifUnmodifiedSince: DateMatch | null;\n\n  constructor(request: IncomingMessage) {\n    this.ifMatch = EtagMatch.parse(request.headers[\"if-match\"]);\n    this.ifNoneMatch = EtagMatch.parse(request.headers[\"if-none-match\"]);\n    this.ifModifiedSince = DateMatch.parse(request.headers[\"if-modified-since\"]);\n    this.ifUnmodifiedSince = DateMatch.parse(request.headers[\"if-unmodified-since\"]);\n  }\n\n  /**\n   * RFC 9110 section 13.1.1: If-Match precondition evaluation\n   * RFC 9110 section 13.1.4: If-Unmodified-Since precondition evaluation\n   * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.1>\n   * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.4>\n   */\n  preconditionFailed(etag: Etag, mtime: Date): boolean {\n    // If-Match precondition takes precedence\n    if (this.ifMatch !== null && !this.ifMatch.matches(etag)) {\n      return true;\n    }\n\n    // If-Unmodified-Since precondition (only if no If-Match header)\n    if (this.ifMatch === null && this.ifUnmodifiedSince !== null) {\n      return !this.ifUnmodifiedSince.matches(mtime);\n    }\n\n    return false;\n  }\n\n  /**\n   * RFC 9110 section 13.1.2: If-None-Match precondition evaluation\n   * RFC 9110 section 13.1.3: If-Modified-Since precondition evaluation\n   * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.2>\n   * See <https://datatracker.ietf.org/doc/html/rfc9110#section-13.1.3>\n   */\n  notModified(etag: Etag, mtime: Date): boolean {\n    // If-None-Match takes precedence over If-Modified-Since\n    if (this.ifNoneMatch !== null) {\n      return this.ifNoneMatch.matches(etag);\n    }\n\n    // If-Modified-Since (only if no If-None-Match header)\n    if (this.ifModifiedSince !== null) {\n      return this.ifModifiedSince.matches(mtime);\n    }\n\n    return false;\n  }\n}\n"],"mappings":";AAYA,IAAM,YAAN,MAAM,UAAU;CACd,YAAoB,WAA2C;AAA1B,OAAA,YAAA;;CAErC,OAAO,MAAM,WAAiD;AAC5D,MAAI,cAAc,KAAA,EAAW,QAAO;AACpC,MAAI,cAAc,IAChB,QAAO,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;MAE1C,QAAO,IAAI,UAAU;GACnB,MAAM;GACN,OAAO,UAAU,MAAM,IAAI,CAAC,KAAK,SAAS,KAAK,MAAM,CAAS;GAC/D,CAAC;;CAIN,QAAQ,MAAqB;AAC3B,UAAQ,KAAK,UAAU,MAAvB;GACE,KAAK,QACH,QAAO,KAAK,UAAU,MAAM,SAAS,KAAK;GAC5C,KAAK,WACH,QAAO;;;;AAKf,IAAM,YAAN,MAAM,UAAU;CACd,YAAoB,OAA8B;AAAb,OAAA,QAAA;;CAErC,OAAO,MAAM,WAAiD;AAC5D,MAAI,cAAc,KAAA,EAAW,QAAO;EACpC,MAAM,OAAO,KAAK,MAAM,UAAU;AAClC,MAAI,MAAM,KAAK,CAAE,QAAO;AACxB,SAAO,IAAI,UAAU,IAAI,KAAK,KAAK,CAAC;;CAGtC,QAAQ,OAAsB;AAC5B,SAAO,KAAK,MAAM,SAAS,GAAG,QAAU,MAAM,SAAS,GAAG;;;AAI9D,IAAa,UAAb,MAAqB;;;;;;;;;;;;CAYnB;;;;;;;;;;;;CAaA;;;;;;;;;;CAWA;;;;;;;;;;CAWA;CAEA,YAAY,SAA0B;AACpC,OAAK,UAAU,UAAU,MAAM,QAAQ,QAAQ,YAAY;AAC3D,OAAK,cAAc,UAAU,MAAM,QAAQ,QAAQ,iBAAiB;AACpE,OAAK,kBAAkB,UAAU,MAAM,QAAQ,QAAQ,qBAAqB;AAC5E,OAAK,oBAAoB,UAAU,MAAM,QAAQ,QAAQ,uBAAuB;;;;;;;;CASlF,mBAAmB,MAAY,OAAsB;AAEnD,MAAI,KAAK,YAAY,QAAQ,CAAC,KAAK,QAAQ,QAAQ,KAAK,CACtD,QAAO;AAIT,MAAI,KAAK,YAAY,QAAQ,KAAK,sBAAsB,KACtD,QAAO,CAAC,KAAK,kBAAkB,QAAQ,MAAM;AAG/C,SAAO;;;;;;;;CAST,YAAY,MAAY,OAAsB;AAE5C,MAAI,KAAK,gBAAgB,KACvB,QAAO,KAAK,YAAY,QAAQ,KAAK;AAIvC,MAAI,KAAK,oBAAoB,KAC3B,QAAO,KAAK,gBAAgB,QAAQ,MAAM;AAG5C,SAAO"}