import "server-only"; import { createHash, randomBytes } from "node:crypto"; export const CART_CAPABILITY_COOKIE = "salix_cart_v1"; export const CART_CAPABILITY_MAX_AGE_SECONDS = 60 * 60 * 24 * 30; const capabilityPattern = /^[A-Za-z0-9_-]{43}$/u; export function createCartCapability(): string { return randomBytes(32).toString("base64url"); } export function parseCartCapability(value: string | undefined): string | null { return value !== undefined && capabilityPattern.test(value) ? value : null; } export function hashCartCapability(capability: string): string { const parsed = parseCartCapability(capability); if (parsed === null) throw new TypeError("Invalid cart capability."); return createHash("sha256").update(parsed, "utf8").digest("hex"); } export function hashCommerceRequest(value: unknown): string { return createHash("sha256") .update(JSON.stringify(canonicalize(value)), "utf8") .digest("hex"); } function canonicalize(value: unknown): unknown { if (Array.isArray(value)) return value.map(canonicalize); if (value !== null && typeof value === "object") { return Object.fromEntries( Object.entries(value) .sort(([left], [right]) => left.localeCompare(right)) .map(([key, child]) => [key, canonicalize(child)]), ); } if ( value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string" ) { return value; } throw new TypeError("Commerce request contains a non-JSON value."); }