// src/utils/processMenuLinks.js
const { winstonLogger } = require("./logging/index.js");
const { evaluateRules } = require("../utils/evaluateRules.js");
/**
* Applies attribute promotion from a child to a parent.
*/
function promoteAttributes(parent, child) {
const promote = child.promote;
if (!promote) return;
let keys = [];
if (promote === "true" || promote === true) {
keys = Object.keys(child).filter((k) => k !== "submenu" && k !== "promote");
} else if (typeof promote === "string") {
keys = [promote];
} else if (Array.isArray(promote)) {
keys = promote;
}
keys.forEach((key) => {
parent[key] = child[key];
});
}
function normalizePath(item, currentPath) {
if (item.appendCurrentPath && typeof item.href === "string") {
const shouldAppend =
currentPath !== "/" && !item.href.endsWith(currentPath);
if (shouldAppend) item.href += currentPath;
}
if (item.html || item.frame || item.mermaid) {
item.href = `/docs/hexa/${item.html || item.frame || item.mermaid}`;
}
return item;
}
function handleSubmenuLogic(item, session, currentPath, activePolicy) {
const nextPolicy = activePolicy === "deny-children" ? "deny" : activePolicy;
const processedSub = processMenuLinks(
item.submenu,
session,
currentPath,
nextPolicy,
);
if (processedSub.length === 0) {
delete item.submenu;
return item;
}
const primaryChild = item.submenu.find((s) => s.promote);
const isPrimaryVisible = processedSub.some(
(s) => s.label === primaryChild?.label,
);
if (isPrimaryVisible && primaryChild?.promote) {
promoteAttributes(item, primaryChild);
// Remove the redundant link from dropdown if it was promoted to the parent
item.submenu = processedSub.filter((s) => s.label !== primaryChild.label);
} else {
item.submenu = processedSub;
}
if (item.submenu.length === 0) delete item.submenu;
return item;
}
/**
* Processes menu links with policy inheritance and parent-override logic.
* @param {Array} links - The menu items to filter.
* @param {Object} session - The { isAuthenticated, user, groups } object.
* @param {string} currentPath - The active URL path.
* @param {string} inheritedPolicy - The policy passed down from the parent (default "allow").
*/
function processMenuLinks(
links,
session,
currentPath,
inheritedPolicy = "allow",
) {
return (links || []).reduce((acc, link) => {
let item = { ...link };
const activePolicy = item.policy || inheritedPolicy;
if (item.submenu) {
item = handleSubmenuLogic(item, session, currentPath, activePolicy);
}
item = normalizePath(item, currentPath);
const finalPolicy = item.policy || inheritedPolicy;
const isVisible =
finalPolicy === "allow" ||
finalPolicy === "deny-children" ||
(finalPolicy === "deny" &&
session.isAuthenticated &&
evaluateRules(item.rules, session));
if (isVisible) acc.push(item);
return acc;
}, []);
}
module.exports = processMenuLinks;