core/analyzers/diagram.analyzer.ts
core/analyzers/diagram.analyzer.ts is a file in Bane's Lab Site. 86 lines of code and 26 definitions.
import {
DIAGRAM_EDGE_SELECTOR,
DIAGRAM_NODE_SELECTOR,
DIAGRAM_WALK_SEPARATOR,
EDGE_PART_MARK,
EDGE_PREFIX,
FLOWCHART_MARK,
NODE_COUNT_MARK,
} from "#configuration/constants/diagram.constants";
import type { WalkStep } from "#types/diagram.types";
const DIGEST_ALGORITHM = "SHA-256";
const HEX_RADIX = 16;
const HEX_WIDTH = 2;
const HEX_PAD = "0";
const hexOf = function hexOf(bytes: ArrayBuffer): string {
return [...new Uint8Array(bytes)].map((byte) => byte.toString(HEX_RADIX).padStart(HEX_WIDTH, HEX_PAD)).join("");
};
export const diagramDigest = async function diagramDigest(source: string): Promise<string> {
return hexOf(await crypto.subtle.digest(DIGEST_ALGORITHM, new TextEncoder().encode(source)));
};
export const nodeNameOf = function nodeNameOf(id: string): string | null {
const at = id.indexOf(FLOWCHART_MARK);
if (at === -1) {
return null;
}
const rest = id.slice(at + FLOWCHART_MARK.length);
const cut = rest.lastIndexOf(NODE_COUNT_MARK);
return cut === -1 ? rest : rest.slice(0, cut);
};
export const edgeEndsOf = function edgeEndsOf(
id: string,
names: ReadonlySet<string>,
): readonly [string, string] | null {
if (!id.startsWith(EDGE_PREFIX)) {
return null;
}
const body = id.slice(EDGE_PREFIX.length, id.lastIndexOf(EDGE_PART_MARK));
for (let at = body.indexOf(EDGE_PART_MARK); at !== -1; at = body.indexOf(EDGE_PART_MARK, at + 1)) {
const from = body.slice(0, at);
const to = body.slice(at + EDGE_PART_MARK.length);
if (names.has(from) && names.has(to)) {
return [from, to];
}
}
return null;
};
export const walkOrder = function walkOrder(
nodeIds: readonly string[],
edgeIds: readonly string[],
): readonly WalkStep[] {
const byName = new Map(
nodeIds.flatMap((id) => {
const name = nodeNameOf(id);
return name === null ? [] : [[name, id] as const];
}),
);
const names = new Set(byName.keys());
const steps: WalkStep[] = [];
const seen = new Set<string>();
const visit = function visit(name: string): void {
const id = byName.get(name);
if (id !== undefined && !seen.has(id)) {
seen.add(id);
steps.push({ id, kind: "node" });
}
};
for (const edge of edgeIds) {
const ends = edgeEndsOf(edge, names);
if (ends !== null) {
visit(ends[0]);
steps.push({ id: edge, kind: "edge" });
visit(ends[1]);
}
}
for (const name of names) {
visit(name);
}
return steps;
};
export const walkAttributeOf = function walkAttributeOf(svg: ParentNode): string {
const ids = function ids(selector: string): string[] {
return [...svg.querySelectorAll(selector)].map((element) => element.id);
};
const order = walkOrder(ids(DIAGRAM_NODE_SELECTOR), ids(DIAGRAM_EDGE_SELECTOR)).map((step) => step.id);
return order.join(DIAGRAM_WALK_SEPARATOR);
};