repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/strata/diagram.ts
Raw/** A deliberately narrow Mermaid source profile; syntax errors remain renderer errors. */
export function validateDiagram(source: string): string {
if (typeof source !== "string" || !source.trim() || source.length > 12_000) {
throw new Error("diagram must contain 1 to 12000 characters");
}
// No configuration, HTML/entities, CSS shorthand, or URI-bearing features.
if (
/[\u0000-\u0008\u000b-\u001f]/u.test(source) ||
/%%\s*\{|^\s*---|:::/mu.test(source) ||
/<\/?[A-Za-z!][^>]*>|&(?:#\w+|[A-Za-z]+);/u.test(source) ||
/\b(?:https?|mailto|javascript|data):|\b(?:href|xlink:href)\s*=/iu.test(
source,
)
)
throw new Error(
"diagram directives, HTML, links, or styling are not allowed",
);
let type = "";
for (const rawLine of source.split("\n")) {
const line = rawLine.trim();
if (!line || line.startsWith("%%")) continue;
if (!type) {
const header =
/^(flowchart\s+(?:TB|TD|BT|RL|LR)|sequenceDiagram|stateDiagram-v2|classDiagram|erDiagram)\s*;?$/u.exec(
line,
);
if (!header) throw new Error("unsupported diagram type or header");
type = line.split(/\s/u)[0] ?? "";
continue;
}
for (const statement of line.split(";")) {
const trimmed = statement.trim();
if (
/^(?:click|links?|style|classDef|linkStyle|cssClass|callback)\b/iu.test(
trimmed,
) ||
(/^class\b/iu.test(trimmed) && type !== "classDiagram") ||
/@\s*\{/u.test(trimmed)
)
throw new Error("diagram links or styling are not allowed");
}
}
if (!type) throw new Error("diagram type is required");
return source;
}