/** * UUID v4 * from https://gist.github.com/jed/982883 */ export function generateUUID(placeholder?: string): string { return placeholder ? // eslint-disable-next-line no-bitwise (parseInt(placeholder, 10) ^ ((Math.random() * 16) >> (parseInt(placeholder, 10) / 4))).toString(16) : `${1e7}-${1e3}-${4e3}-${8e3}-${1e11}`.replace(/[018]/g, generateUUID) } // Assuming input string is following the HTTP Cookie format defined in // https://www.ietf.org/rfc/rfc2616.txt and https://www.ietf.org/rfc/rfc6265.txt, we don't need to // be too strict with this regex. const COMMA_SEPARATED_KEY_VALUE = /(\S+?)\s*=\s*(.+?)(?:;|$)/g /** * Returns the value of the key with the given name * If there are multiple values with the same key, returns the first one */ export function findCommaSeparatedValue(rawString: string, name: string): string | undefined { COMMA_SEPARATED_KEY_VALUE.lastIndex = 0 while (true) { const match = COMMA_SEPARATED_KEY_VALUE.exec(rawString) if (match) { if (match[1] === name) { return match[2] } } else { break } } } /** * Returns a map of all the values with the given key * If there are multiple values with the same key, returns all the values */ export function findAllCommaSeparatedValues(rawString: string): Map { const result = new Map() COMMA_SEPARATED_KEY_VALUE.lastIndex = 0 while (true) { const match = COMMA_SEPARATED_KEY_VALUE.exec(rawString) if (match) { const key = match[1] const value = match[2] if (result.has(key)) { result.get(key)!.push(value) } else { result.set(key, [value]) } } else { break } } return result } /** * Returns a map of the values with the given key * ⚠️ If there are multiple values with the same key, returns the LAST one * * @deprecated use `findAllCommaSeparatedValues()` instead */ export function findCommaSeparatedValues(rawString: string): Map { const result = new Map() COMMA_SEPARATED_KEY_VALUE.lastIndex = 0 while (true) { const match = COMMA_SEPARATED_KEY_VALUE.exec(rawString) if (match) { result.set(match[1], match[2]) } else { break } } return result } export function safeTruncate(candidate: string, length: number, suffix = '') { const lastChar = candidate.charCodeAt(length - 1) const isLastCharSurrogatePair = lastChar >= 0xd800 && lastChar <= 0xdbff const correctedLength = isLastCharSurrogatePair ? length + 1 : length if (candidate.length <= correctedLength) { return candidate } return `${candidate.slice(0, correctedLength)}${suffix}` }