feat: add deterministic Diagram IR v2 authoring
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
import { run as build } from "../build/action.js";
|
||||
import { run as validate } from "./action.js";
|
||||
|
||||
test("validate accepts Draw.io generated with a standard XML declaration", () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "drawio-validate-test-"));
|
||||
const spec = join(dir, "spec.yaml");
|
||||
const diagram = join(dir, "diagram.drawio");
|
||||
try {
|
||||
writeFileSync(spec, "version: 1\nnodes:\n - { id: node, label: Node }\n", "utf8");
|
||||
build(spec, 0, diagram);
|
||||
const result = validate(diagram);
|
||||
assert.deepEqual(result.summary, { pages: 1, valid: true, invalidPages: 0 });
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
@@ -26,30 +26,9 @@
|
||||
* - Base64/deflate encoding errors in the <diagram> body
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { JSDOM } from "jsdom";
|
||||
import { inflateRaw } from "pako";
|
||||
import { loadGraphStates } from "../../services/maxgraph-loader/graph-loader.js";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function getAttr(tagStr: string, attr: string): string {
|
||||
const re = new RegExp(`\\b${attr}\\s*=\\s*(?:"([^"]*?)"|'([^']*?)')`, "i");
|
||||
const m = re.exec(tagStr);
|
||||
return m ? (m[1] ?? m[2] ?? "") : "";
|
||||
}
|
||||
|
||||
function decodeDiagramContent(content: string): string {
|
||||
try {
|
||||
const decoded = Buffer.from(content.trim(), "base64");
|
||||
const decompressed = inflateRaw(decoded);
|
||||
return decodeURIComponent(new TextDecoder("utf-8").decode(decompressed));
|
||||
} catch {
|
||||
return content; // already plain XML
|
||||
}
|
||||
}
|
||||
import { parseAllPages } from "../../services/drawio-parser/parser.js";
|
||||
|
||||
interface PageResult {
|
||||
pageIndex: number;
|
||||
@@ -82,29 +61,7 @@ function checkXmlWellFormed(xml: string): string | null {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
export function run(filePath: string): Record<string, unknown> {
|
||||
const raw = readFileSync(filePath, "utf-8").trim();
|
||||
|
||||
// Extract pages (support both mxfile wrapper and bare mxGraphModel)
|
||||
const pages: Array<{ name: string; xml: string }> = [];
|
||||
|
||||
if (/^<mxfile/i.test(raw)) {
|
||||
const re = /<diagram([^>]*)>([\s\S]*?)<\/diagram>/gi;
|
||||
let match: RegExpExecArray | null;
|
||||
let idx = 0;
|
||||
while ((match = re.exec(raw)) !== null) {
|
||||
const name = getAttr(match[1], "name") || `Page ${idx + 1}`;
|
||||
const xml = decodeDiagramContent(match[2].trim());
|
||||
pages.push({ name, xml });
|
||||
idx++;
|
||||
}
|
||||
if (pages.length === 0) {
|
||||
throw new Error("No <diagram> elements found in mxfile");
|
||||
}
|
||||
} else if (/^<mxGraphModel/i.test(raw)) {
|
||||
pages.push({ name: "Page 1", xml: raw });
|
||||
} else {
|
||||
throw new Error(`Unrecognised draw.io file format in ${filePath}`);
|
||||
}
|
||||
const pages = parseAllPages(filePath).map((page) => ({ name: page.pageName, xml: page.graphModelXml }));
|
||||
|
||||
const results: PageResult[] = [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user