/** * Small cookie helpers for the presentational choices that ride in cookies * (colour scheme, whether the menu shows) so the server can render them on * the first pass, where localStorage would only reach the page after * hydration and paint one layout before snapping to the other. */ /** A year: the merchant should not have to re-pick every session. */ const PREFERENCE_COOKIE_MAX_AGE_SECONDS = 31536000; /** * Reads one cookie out of a raw Cookie header. * * @param {string | null} cookieHeader - The request's raw Cookie header * @param {string} name - The cookie's name * @returns {string | null} Its value, or null when absent */ export function readCookie( cookieHeader: string | null, name: string ): string | null { const match = new RegExp(`(?:^|;\\s*)${name}=([^;]*)(?:;|$)`).exec( cookieHeader ?? '' ); return match ? match[1] : null; } /** What a preference cookie carries besides its value. */ interface PreferenceCookieOptions { /** Add ``Secure``; omitted on plain HTTP, where the browser would drop it. */ secure?: boolean; } /** * The ``Set-Cookie`` value (or ``document.cookie`` assignment) that remembers * a preference for a year, site-wide, same-site. * * @param {string} name - The cookie's name * @param {string} value - The choice * @param {PreferenceCookieOptions} [options] - Whether to mark it Secure * @returns {string} The cookie string */ export function preferenceCookie( name: string, value: string, { secure = false }: PreferenceCookieOptions = {} ): string { return ( `${name}=${value};Path=/;Max-Age=${PREFERENCE_COOKIE_MAX_AGE_SECONDS};SameSite=Lax` + (secure ? ';Secure' : '') ); }