All files / src/worker cookies.ts

92.85% Statements 39/42
81.81% Branches 18/22
100% Functions 7/7
92.85% Lines 39/42

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143                        20x 20x                 20x                                             26x 26x   26x 20x     6x                           26x 26x   26x 2x     24x   24x 52x 52x       52x   52x           52x     52x 52x     52x     24x             52x 52x             52x 52x             104x 104x   104x 132x 132x 104x       104x 104x 104x 103x   1x     104x    
/**
 * @fileoverview
 *
 * Implements utility functions for working with cookies.
 * Based on "cookie" library: https://github.com/jshttp/cookie
 * */
 
/**
 * Represents a cookie with a name and value.
 */
class Cookie {
  constructor(
    public readonly name: string,
    public readonly value: string
  ) {}
 
  /**
   * Converts the cookie's name and value into a string formatted as a valid cookie.
   *
   * @return {string} A string representation of the cookie in "name=value" format.
   */
  toCookieString(): string {
    return `${this.name}=${this.value}`
  }
}
 
/**
 * Finds and extracts a specific cookie from a cookie header string.
 *
 * Searches for a cookie with the specified name in the provided cookie string
 * and returns the complete cookie key-value pair (name=value) if found.
 *
 * @param cookies - The cookie header string containing one or more cookies separated by semicolons
 * @param name - The name of the cookie to search for
 * @returns The complete cookie string (name=value) if found, undefined otherwise
 *
 * @example
 * findCookie('sessionId=abc123; _iidt=xyz789; theme=dark', '_iidt')
 * // returns '_iidt=xyz789'
 *
 * @example
 * findCookie('sessionId=abc123; theme=dark', 'nonexistent')
 * // returns undefined
 */
export function findCookie(cookies: string, name: string): string | undefined {
  const parsedCookies = parseCookies(cookies)
  const value = parsedCookies.get(name)
 
  if (typeof value === 'string') {
    return new Cookie(name, value).toCookieString()
  }
 
  return undefined
}
/**
 * Cookies object.
 */
export type Cookies = Map<string, string | undefined>
 
/**
 * Parse a `Cookie` header.
 *
 * Parse the given cookie header string into an object
 * The object has the various cookies as keys(names) => values
 */
export function parseCookies(str: string): Cookies {
  const obj: Cookies = new Map()
  const len = str.length
  // RFC 6265 sec 4.1.1, RFC 2616 2.2 defines a cookie name consists of one char minimum, plus '='.
  if (len < 2) {
    return obj
  }
 
  let index = 0
 
  do {
    const eqIdx = eqIndex(str, index, len)
    Iif (eqIdx === -1) {
      break
    } // No more cookie pairs.
 
    const endIdx = endIndex(str, index, len)
 
    Iif (eqIdx > endIdx) {
      // backtrack on prior semicolon
      index = str.lastIndexOf(';', eqIdx - 1) + 1
      continue
    }
 
    const key = valueSlice(str, index, eqIdx)
 
    // only assign once
    Eif (obj.get(key) === undefined) {
      obj.set(key, valueSlice(str, eqIdx + 1, endIdx))
    }
 
    index = endIdx + 1
  } while (index < len)
 
  return obj
}
 
/**
 * Find the `;` character between `min` and `len` in str.
 */
function endIndex(str: string, min: number, len: number) {
  const index = str.indexOf(';', min)
  return index === -1 ? len : index
}
 
/**
 * Find the `=` character between `min` and `max` in str.
 */
function eqIndex(str: string, min: number, max: number) {
  const index = str.indexOf('=', min)
  return index < max ? index : -1
}
 
/**
 * Slice out a value between startPod to max.
 */
function valueSlice(str: string, min: number, max: number) {
  let start = min
  let end = max
 
  do {
    const code = str.charCodeAt(start)
    if (code !== 0x20 /*   */ && code !== 0x09 /* \t */) {
      break
    }
  } while (++start < end)
 
  while (end > start) {
    const code = str.charCodeAt(end - 1)
    if (code !== 0x20 /*   */ && code !== 0x09 /* \t */) {
      break
    }
    end--
  }
 
  return str.slice(start, end)
}