Lumi/plugins/lumi_ai/backend/link_normalizer.js
2026-06-12 11:54:46 +02:00

79 lines
2.3 KiB
JavaScript

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(/&amp;/gi, "&")
.replace(/&quot;/gi, "\"")
.replace(/&#39;|&apos;/gi, "'")
.replace(/&lt;/gi, "<")
.replace(/&gt;/gi, ">");
}
module.exports = {
METHOD_PREFIX,
normalizeLink,
absoluteInternalUrl,
safeBaseUrl,
routeLabel,
stripTags
};