File
Blob: test/util/d1Bookmark.ts
| 1 | import { |
| 2 | D1_BOOKMARK_COOKIE_MAX_AGE_SECONDS, |
| 3 | D1_BOOKMARK_COOKIE_NAME, |
| 4 | D1_BOOKMARK_HEADER, |
| 5 | } from "@/worker/routes/d1Bookmark"; |
| 6 | |
| 7 | export { D1_BOOKMARK_HEADER, D1_BOOKMARK_COOKIE_NAME, D1_BOOKMARK_COOKIE_MAX_AGE_SECONDS }; |
| 8 | export const D1_BOOKMARK_COOKIE_HEADER_NAME = `__Host-${D1_BOOKMARK_COOKIE_NAME}`; |
| 9 | |
| 10 | // Parses `Set-Cookie` headers and pulls out the bookmark cookie. Tests |
| 11 | // drive the worker through `workerExports.default.fetch`, which surfaces |
| 12 | // a single combined `Set-Cookie` header even when the worker appended |
| 13 | // several cookies; we split on the conventional comma-newline pattern |
| 14 | // and look for our entry by name. Attribute values are the raw strings |
| 15 | // the worker emitted (after URL-decoding the cookie value itself). |
| 16 | export type ParsedBookmarkCookie = { |
| 17 | value: string; |
| 18 | attributes: Record<string, string | true>; |
| 19 | }; |
| 20 | |
| 21 | export function parseBookmarkCookie(setCookieHeader: string | null): ParsedBookmarkCookie | null { |
| 22 | if (!setCookieHeader) return null; |
| 23 | // Multiple cookies in a combined header are separated by `, ` in |
| 24 | // workerd's surface; entries can themselves contain commas inside |
| 25 | // `Expires=...`, but the bookmark cookie only carries `Max-Age`, so |
| 26 | // splitting on `,` followed by a space is safe here. |
| 27 | const entries = setCookieHeader.split(/, (?=[^ ]+=)/); |
| 28 | for (const entry of entries) { |
| 29 | const [head, ...rest] = entry.split(";"); |
| 30 | if (!head) continue; |
| 31 | const eq = head.indexOf("="); |
| 32 | if (eq === -1) continue; |
| 33 | const name = head.slice(0, eq).trim(); |
| 34 | if (name !== D1_BOOKMARK_COOKIE_HEADER_NAME) continue; |
| 35 | const rawValue = head.slice(eq + 1).trim(); |
| 36 | const attributes: Record<string, string | true> = {}; |
| 37 | for (const attr of rest) { |
| 38 | const trimmed = attr.trim(); |
| 39 | if (!trimmed) continue; |
| 40 | const aeq = trimmed.indexOf("="); |
| 41 | if (aeq === -1) { |
| 42 | attributes[trimmed] = true; |
| 43 | } else { |
| 44 | attributes[trimmed.slice(0, aeq)] = trimmed.slice(aeq + 1); |
| 45 | } |
| 46 | } |
| 47 | return { value: decodeURIComponent(rawValue), attributes }; |
| 48 | } |
| 49 | return null; |
| 50 | } |