export const ospfTabs = ["interfaces", "neighbors", "routes", "bfd"] as const export type OspfTab = typeof ospfTabs[number] export function readOspfUrl(params: URLSearchParams) { const server = params.get("server") ?? "all" const search = params.get("q") ?? "" const node = params.get("node") const rawTab = params.get("tab") ?? "interfaces" let error: string | null = null if (["server", "tab", "q", "node"].some(key => params.getAll(key).length > 1)) error = "В ссылке повторяется контекст OSPF." else if (!/^[a-zA-Z0-9_.:-]{1,64}$/.test(server)) error = "В ссылке указан некорректный сервер OSPF." else if (!(ospfTabs as readonly string[]).includes(rawTab)) error = "В ссылке указана неизвестная вкладка OSPF." else if (search.length > 256) error = "Поиск OSPF в ссылке слишком длинный." else if (node !== null && !/^[a-zA-Z0-9_.:-]{1,128}$/.test(node)) error = "В ссылке указан некорректный узел OSPF." return { server, search, node, tab: (ospfTabs as readonly string[]).includes(rawTab) ? rawTab as OspfTab : "interfaces" as OspfTab, error } } export function patchOspfUrl(params: URLSearchParams, changes: Record) { const next = new URLSearchParams(params) for (const [key,value] of Object.entries(changes)) { if (value === null || value === "") next.delete(key); else next.set(key,value) } return "/ospf" + (next.size ? "?" + next.toString() : "") }