/** * Server-safe URL parameter helpers (no 'use client' directive) shared by server components (searchParams * parsing) and the client `useUrlState` hook in ./url-state.ts, which re-exports them. */ export type UrlPatch = Record; export function applyPatch(current: URLSearchParams, patch: UrlPatch): URLSearchParams { const p = new URLSearchParams(current.toString()); for (const [k, v] of Object.entries(patch)) { if (v === undefined || v === null || v === '' || v === false) p.delete(k); else p.set(k, v === true ? '1' : String(v)); } return p; } /** Parse "a,b,c" → ["a","b","c"] limited to slug-safe tokens. */ export function parseList(v: string | null | undefined, max = 8): string[] { if (!v) return []; const out: string[] = []; for (const part of v.split(',')) { const s = part.trim().toLowerCase(); if (s && /^[a-z0-9][a-z0-9-]*$/.test(s) && !out.includes(s)) out.push(s); if (out.length >= max) break; } return out; } export function parseYear(v: string | null | undefined): number | null { if (!v) return null; const n = Number(v); return Number.isInteger(n) && n >= 1750 && n <= 2100 ? n : null; } /** First string value of a Next `searchParams` entry. */ export function firstParam(v: string | string[] | undefined): string | null { return Array.isArray(v) ? (v[0] ?? null) : (v ?? null); }