export type RecursiveUrlState = { serverId: string | null; q: string; route: string | null; table: string | null; error: string | null } export type RecursiveUrlPatch = Partial<{ serverId: string | null; q: string | null; route: string | null; table: string | null }> export function readRecursiveUrl(params: URLSearchParams): RecursiveUrlState { const result: RecursiveUrlState = { serverId: null, q: "", route: null, table: null, error: null } for (const [key, limit] of [["server", 64], ["q", 256], ["route", 128], ["table", 256]] as const) { const values = params.getAll(key) if (values.length > 1 || values.some(value => value.length > limit)) return { ...result, error: "Некорректный контекст маршрутов в ссылке" } } const server = params.get("server"), q = params.get("q")?.trim() ?? "" if (server !== null && !/^[1-9]\d*$/.test(server)) return { ...result, error: "Некорректное устройство в ссылке" } const route = params.get("route")?.trim() ?? null, table = params.get("table")?.trim() ?? null if ((route !== null || table !== null) && (!route || !table)) return { ...result, error: "В ссылке маршрута нужны адрес и таблица" } return { serverId: server, q, route, table, error: null } } export function patchRecursiveUrl(params: URLSearchParams, patch: RecursiveUrlPatch): URLSearchParams { const next = new URLSearchParams(params) for (const [field, value] of Object.entries(patch)) { const key = field === "serverId" ? "server" : field if (value === null || value.trim() === "") next.delete(key); else next.set(key, value.trim()) } return next }