const METHOD_PREFIX = /^(?:GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\s+/i; function normalizeLink(link, baseUrl = "", verifiedRoutes = [], options = {}) { if (!link || typeof link.href !== "string") return null; const routeSet = verifiedRoutes instanceof Set ? verifiedRoutes : new Set(verifiedRoutes); const href = decodeHtmlEntities(link.href).trim().replace(METHOD_PREFIX, ""); const label = stripTags(String(link.label || routeLabel(href))).trim(); if (!href || !label) return null; if (href.startsWith("/")) { const pathname = routePath(href); if (!routeSet.has(pathname)) return null; return { href: absoluteInternalUrl(href, baseUrl), label, internal: true }; } let parsed; try { parsed = new URL(href); } catch { return null; } if (!["http:", "https:"].includes(parsed.protocol)) return null; const base = safeBaseUrl(baseUrl); if (base && parsed.origin === base.origin) { if (!routeSet.has(parsed.pathname)) return null; return { href: parsed.href, label, internal: true }; } if (options.allowExternal === false || link.external === false) return null; return { href: parsed.href, label, internal: false }; } function absoluteInternalUrl(route, baseUrl) { const base = safeBaseUrl(baseUrl); return base ? new URL(route, base).href : route; } function safeBaseUrl(value) { if (!value) return null; try { const parsed = new URL(value); return ["http:", "https:"].includes(parsed.protocol) ? parsed : null; } catch { return null; } } function routePath(value) { return String(value || "").split(/[?#]/, 1)[0]; } function routeLabel(route) { const pathname = routePath(String(route || "").replace(METHOD_PREFIX, "")); const segment = pathname.split("/").filter(Boolean).pop() || "Lumi page"; return segment.replace(/[-_]/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase()); } function stripTags(value) { return String(value || "").replace(/<[^>]*>/g, ""); } function decodeHtmlEntities(value) { return String(value || "") .replace(/&/gi, "&") .replace(/"/gi, "\"") .replace(/'|'/gi, "'") .replace(/</gi, "<") .replace(/>/gi, ">"); } module.exports = { METHOD_PREFIX, normalizeLink, absoluteInternalUrl, safeBaseUrl, routeLabel, stripTags };