All files / src/helpers cookies.ts

100% Statements 51/51
100% Branches 15/15
100% Functions 4/4
100% Lines 51/51

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 1141x 1x   1x                 1x   1x   1x                         1x 1x 13x 13x   1x                         1x 1x 4x 1x 1x 1x 3x 3x 2x 1x 1x 4x   1x                         1x 1x 3x 1x 1x 1x 2x 2x 3x 1x 1x 1x 3x   1x                         1x 1x 3x 1x 1x 1x 2x 2x 3x 1x 1x 1x 3x  
import { Logger } from '@/utils/Logger';
import { getErrorMessage } from '@/helpers/exceptions';
 
/**
 * @category Cookies Helpers
 * @categoryDescription Guarded wrappers around the `chrome.cookies` API for
 * seeding cookies into the browser jar and reading them back. The `Cookie`
 * request header is on the fetch-forbidden list, so suppliers cannot set
 * cookies via `this.headers`; writing them into the jar (and fetching with
 * `credentials: "include"`) is the only reliable path.
 * @showCategories
 * @source
 */
 
const logger = new Logger('cookies');
 
/**
 * Reports whether the `chrome.cookies` API is usable in the current context.
 * It is `undefined` unless the extension has the "cookies" permission and the
 * host permission for the origin, so callers guard with this to degrade
 * gracefully (dev fallback, or when a user hasn't accepted the upgraded
 * permission set yet).
 * @category Cookies Helpers
 * @returns `true` when `chrome.cookies` is available, otherwise `false`
 * @example
 * ```typescript
 * isCookiesApiAvailable() // true inside the extension, false in unit tests
 * ```
 * @source
 */
export function isCookiesApiAvailable(): boolean {
  return typeof chrome !== 'undefined' && Boolean(chrome.cookies);
}
 
/**
 * Writes a cookie into the browser cookie jar so the storefront returns it on
 * subsequent requests made with `credentials: "include"`. No-ops with a
 * warning when the API is unavailable, and swallows write failures (logging
 * them) so a failed seed never aborts the surrounding query.
 * @category Cookies Helpers
 * @param details - The `chrome.cookies.set` details (url, name, value, etc.)
 * @returns A promise that resolves once the write has been attempted
 * @example
 * ```typescript
 * await setCookie({ url: "https://shop.es-drei.de", name: "currency", value: "2" });
 * ```
 * @source
 */
export async function setCookie(details: chrome.cookies.SetDetails): Promise<void> {
  if (!isCookiesApiAvailable()) {
    logger.warn('chrome.cookies unavailable; skipping cookie set', { details });
    return;
  }
  try {
    await chrome.cookies.set(details);
  } catch (error: unknown) {
    logger.warn(`Failed to set cookie: ${getErrorMessage(error)}`, { details, error });
  }
}
 
/**
 * Reads every cookie the jar holds for the given URL. Useful for verifying
 * that session cookies are being stored/updated across requests. Returns an
 * empty array when the API is unavailable or the read fails.
 * @category Cookies Helpers
 * @param url - The URL whose cookies should be read
 * @returns A promise resolving to the matching cookies (empty on failure)
 * @example
 * ```typescript
 * const cookies = await getCookies("https://shop.es-drei.de");
 * console.log(cookies.find((c) => c.name === "currency")?.value); // "2"
 * ```
 * @source
 */
export async function getCookies(url: string): Promise<chrome.cookies.Cookie[]> {
  if (!isCookiesApiAvailable()) {
    logger.warn('chrome.cookies unavailable; cannot read cookies', { url });
    return [];
  }
  try {
    return await chrome.cookies.getAll({ url });
  } catch (error: unknown) {
    logger.warn(`Failed to read cookies: ${getErrorMessage(error)}`, { url, error });
    return [];
  }
}
 
/**
 * Reads a single named cookie for the given URL. Returns `null` when the
 * cookie is absent, the API is unavailable, or the read fails.
 * @category Cookies Helpers
 * @param url - The URL the cookie belongs to
 * @param name - The cookie name to read
 * @returns A promise resolving to the cookie, or `null`
 * @example
 * ```typescript
 * const cookie = await getCookie("https://shop.es-drei.de", "currency");
 * console.log(cookie?.value); // "2"
 * ```
 * @source
 */
export async function getCookie(url: string, name: string): Promise<chrome.cookies.Cookie | null> {
  if (!isCookiesApiAvailable()) {
    logger.warn('chrome.cookies unavailable; cannot read cookie', { url, name });
    return null;
  }
  try {
    return await chrome.cookies.get({ url, name });
  } catch (error: unknown) {
    logger.warn(`Failed to read cookie: ${getErrorMessage(error)}`, { url, name, error });
    return null;
  }
}