@miragon/modeler-api 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +210 -20
- package/dist/chunk-E6KJB7QH.js +14 -0
- package/dist/chunk-E6KJB7QH.js.map +1 -0
- package/dist/conformance.cjs +552 -7
- package/dist/conformance.cjs.map +1 -1
- package/dist/conformance.d.cts +92 -12
- package/dist/conformance.d.ts +92 -12
- package/dist/conformance.js +548 -8
- package/dist/conformance.js.map +1 -1
- package/dist/index.cjs +11 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -7
- package/dist/index.d.ts +152 -7
- package/dist/index.js +5 -3
- package/package.json +1 -1
- package/dist/chunk-HWB2EYDZ.js +0 -7
- package/dist/chunk-HWB2EYDZ.js.map +0 -1
package/dist/conformance.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/conformance.ts"],"sourcesContent":["/**\n * The conformance test kit — every law of the CollaborativeModeler contract\n * as an executable case. A modeler repo supplies a small HARNESS (how to\n * create an instance, canonical sample texts, scripted user mutations) and\n * binds the cases to its own test runner:\n *\n * import { conformanceCases, registerConformance } from \"@miragon/modeler-api/conformance\";\n * registerConformance(harness, test); // node:test, vitest, jest — any (name, fn) runner\n *\n * Cases throw plain Errors on violation, so the kit has no runner or\n * assertion-library dependency. A case that CANNOT (fully) run because the\n * harness left an optional field undefined resolves with `{ skipped }` and\n * registerConformance reports it (console.warn by default) — a law the kit\n * did not verify is never a silent green.\n */\nimport { type CollaborativeModeler, MODELER_API_VERSION } from \"./index.js\";\n\nexport interface UserMutation {\n /** names the failing mutation in the kit's error message */\n name: string;\n run(modeler: CollaborativeModeler): Promise<void> | void;\n}\n\nexport interface ConformanceHarness {\n /**\n * a fresh EDITABLE modeler, attached to a host element that is IN the\n * document with a non-zero size (`attachedHost()` below) — diagram-js\n * engines compute viewboxes from the host's client size, so a detached\n * or 0×0 host yields NaN view state; they also need a real browser\n * (vitest browser mode / Playwright), jsdom has no getBBox\n */\n create(): Promise<CollaborativeModeler>;\n /** a fresh READ-ONLY instance (`meta.editable === false`) — the viewer\n * mount hosts use on read-only surfaces; absent = the package ships no\n * viewer and the viewer case is reported as skipped */\n createReadonly?(): Promise<CollaborativeModeler>;\n /**\n * CANONICAL sample documents of the notation (>= 2, distinct, at least one\n * rendering an element, ideally several covering config/axis content too)\n * — exactly what exportText emits. Obtain them by exporting a hand-authored\n * document and REVIEWING the bytes, not by pasting whatever the current\n * build prints: L2 only proves the serializer is a fixpoint of the texts\n * you vouch for\n */\n canonicalTexts: string[];\n /** importable but non-canonical inputs (hand-authored variants); the kit\n * verifies canonicalization is IDEMPOTENT on them, not byte-stable.\n * `undefined` = skipped; `[]` = the notation has no loose form */\n looseTexts?: string[];\n /**\n * NON-importable inputs (half-typed peer text, wrong notation) — the kit\n * verifies importText REJECTS, leaves no trace and stays usable (L7).\n * `undefined` = not provided (the L7 cases report a SKIP); `[]` = the\n * deliberate statement that this notation has no non-importable text\n * (the lenient line DSLs keep every line, so nothing rejects)\n */\n invalidTexts?: string[];\n /**\n * importable documents carrying content the modeler does NOT understand\n * (vendor extensions, unknown lines, unknown JSON keys), each with the\n * fragments that must come back from an import → export round-trip (L6).\n * `undefined` = skipped; `[]` = the notation has no foreign form\n */\n foreignTexts?: Array<{ text: string; mustSurvive: string[] }>;\n /**\n * a NON-default view state the modeler accepts — a panned/zoomed viewport\n * (e.g. a viewbox) that differs from what an import leaves; L5 cannot tell\n * a restore from a no-op without one. The kit never compares a readback\n * with this literal: it sets it, reads the modeler's own (possibly\n * normalised) state back, and requires THAT to survive a re-import and to\n * be settable itself. `undefined` = L5 is reported as skipped\n */\n viewState?: unknown;\n /** wait until deferred/debounced change events have been delivered —\n * MUST outlast the modeler's event debounce; defaults to one macrotask */\n settle?(): Promise<void>;\n /**\n * perform ONE user-visible atomic ELEMENT command through the modeler's\n * own commands: ADD an element — never rename or delete one (the elements\n * case treats it as an edit unrelated to the existing elements, and L4\n * builds undo history from it, so it must be undoable)\n */\n mutate(modeler: CollaborativeModeler): Promise<void> | void;\n /**\n * user-visible changes that do NOT run through an element command:\n * config/axis/title/style/size edits that live in the document. A notation\n * with such surfaces MUST list them — the kit cannot see what the canvas\n * offers, and a modeler that only forwards its command stack passes\n * `mutate` while a host loses exactly these edits. `undefined` = skipped;\n * `[]` = the notation has no such surface\n */\n configMutations?: UserMutation[];\n /** trigger the modeler's undo; `undefined` = the notation has no undo\n * (the undo cases are reported as skipped) */\n undo?(modeler: CollaborativeModeler): Promise<void> | void;\n /** trigger the modeler's redo; `undefined` with `undo` defined = the redo\n * half of the undo/redo case is reported as skipped */\n redo?(modeler: CollaborativeModeler): Promise<void> | void;\n}\n\nexport interface CaseOutcome {\n /** the case could not (fully) verify its law — the harness left the field\n * it needs undefined, or the modeler has no such optional surface; the\n * reason names which */\n skipped?: string;\n}\n\nexport interface ConformanceCase {\n name: string;\n run(harness: ConformanceHarness): Promise<CaseOutcome | void>;\n}\n\n/**\n * a host element of the given size, attached to the document — what\n * `create()` should mount into (browser only). The returned element sits\n * INSIDE a sized wrapper: the Miragon renderers restyle the container they\n * are given to 100% × 100%, which then resolves against the wrapper\n */\nexport function attachedHost(width = 800, height = 600): HTMLElement {\n const wrapper = document.createElement(\"div\");\n wrapper.style.cssText = `width:${width}px;height:${height}px;position:absolute;left:0;top:0;overflow:hidden`;\n const host = document.createElement(\"div\");\n host.style.cssText = \"width:100%;height:100%\";\n wrapper.append(host);\n document.body.append(wrapper);\n return host;\n}\n\n/**\n * apiVersion compatibility: same major — and, while the contract is 0.x,\n * the same minor too (pre-1.0 minors may break; bump-minor-pre-major)\n */\nexport function apiVersionsCompatible(a: string, b: string): boolean {\n const [aMajor = \"\", aMinor = \"\"] = a.split(\".\");\n const [bMajor = \"\", bMinor = \"\"] = b.split(\".\");\n if (aMajor !== bMajor) return false;\n return aMajor !== \"0\" || aMinor === bMinor;\n}\n\nfunction fail(message: string): never {\n throw new Error(`modeler-api conformance: ${message}`);\n}\n\nconst skip = (reason: string): CaseOutcome => ({ skipped: reason });\n\nfunction exportOrFail(modeler: CollaborativeModeler, when: string): string {\n try {\n return modeler.exportText();\n } catch (e) {\n fail(`exportText threw ${when}: ${String(e)}`);\n }\n}\n\nconst same = (a: unknown, b: unknown): boolean => JSON.stringify(a) === JSON.stringify(b);\n\n/** a view state with NaN/Infinity is what a detached or 0×0 host produces */\nfunction assertFinite(state: unknown, when: string): void {\n const json = JSON.stringify(state, (_k, v: unknown) =>\n typeof v === \"number\" && !Number.isFinite(v) ? \"__non_finite__\" : v,\n );\n if (json?.includes(\"__non_finite__\")) {\n fail(\n `getViewState() ${when} contains a non-finite number — the host has no size (mount into attachedHost())`,\n );\n }\n}\n\nconst defaultSettle = (): Promise<void> => new Promise((resolve) => setTimeout(resolve, 0));\n\n/** count invocations of onContentChanged around an action — settle-aware, so\n * debounced engines neither hide an echo nor fail a legitimate event; the\n * export seen INSIDE the last delivery is returned too (L3's ordering rule) */\nasync function changeEvents(\n harness: ConformanceHarness,\n modeler: CollaborativeModeler,\n action: () => Promise<void> | void,\n): Promise<{ calls: number; exportAtEmit: string | undefined }> {\n const settle = harness.settle ?? defaultSettle;\n let calls = 0;\n let exportAtEmit: string | undefined;\n const off = modeler.onContentChanged(() => {\n calls += 1;\n exportAtEmit = modeler.exportText();\n });\n try {\n await action();\n await settle();\n } finally {\n off();\n }\n return { calls, exportAtEmit };\n}\n\nconst secondText = (h: ConformanceHarness): string => h.canonicalTexts[1] ?? h.canonicalTexts[0] ?? \"\";\n\n/** the harness's user mutations: the element command first, then every config edit */\nconst mutationsOf = (h: ConformanceHarness): UserMutation[] => [\n { name: \"mutate (element command)\", run: (m) => h.mutate(m) },\n ...(h.configMutations ?? []),\n];\n\nconst sortedIds = (m: CollaborativeModeler): string[] | undefined =>\n m.elements ? [...m.elements.list().map((e) => e.id)].sort() : undefined;\n\n/**\n * import the first canonical text that renders at least one element (an\n * empty document has no fitted view — renderers leave the viewport alone),\n * and return it; without an elements surface the first text is used\n */\nasync function importContentText(h: ConformanceHarness, m: CollaborativeModeler): Promise<string> {\n for (const text of h.canonicalTexts) {\n await m.importText(text);\n if (!m.elements || m.elements.list().length > 0) return text;\n }\n fail(\"L5 needs a canonical text that renders at least one element — an empty document has no fitted view\");\n}\n\nexport function conformanceCases(): ConformanceCase[] {\n return [\n {\n name: \"meta declares the notation, editability and a compatible apiVersion\",\n async run(h) {\n const m = await h.create();\n try {\n if (!m.meta.notation) fail(\"meta.notation is empty\");\n if (m.meta.editable !== true)\n fail(\"create() must return an EDITABLE modeler (meta.editable === true)\");\n if (!apiVersionsCompatible(m.meta.apiVersion, MODELER_API_VERSION)) {\n fail(`apiVersion ${m.meta.apiVersion} is not compatible with ${MODELER_API_VERSION}`);\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L1: importText is silent (no change events, for canonical, loose and foreign inputs)\",\n async run(h) {\n const m = await h.create();\n try {\n const texts = [\n ...h.canonicalTexts,\n ...(h.looseTexts ?? []),\n ...(h.foreignTexts ?? []).map((f) => f.text),\n ];\n for (const text of texts) {\n const { calls } = await changeEvents(h, m, () => m.importText(text).then(() => undefined));\n if (calls > 0) fail(`importText fired onContentChanged ${calls}x`);\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L1: importText over EXISTING undo history is silent too\",\n async run(h) {\n // diagram-js' commandStack.clear() emits 'changed' whether or not the\n // stack is empty — but an engine that only emits when it has something\n // to clear would pass the fresh-instance case above\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n const { calls } = await changeEvents(h, m, () => m.importText(secondText(h)).then(() => undefined));\n if (calls > 0) fail(`importText over a non-empty undo history fired onContentChanged ${calls}x`);\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L2: canonical texts are byte fixpoints of import → export\",\n async run(h) {\n if (h.canonicalTexts.length === 0) fail(\"harness provides no canonicalTexts\");\n const m = await h.create();\n try {\n for (const text of h.canonicalTexts) {\n await m.importText(text);\n const out = m.exportText();\n if (out !== text)\n fail(`export after import differs from the canonical input:\\n--- in\\n${text}\\n--- out\\n${out}`);\n if (m.exportText() !== out)\n fail(\"exportText is not deterministic — two exports of one document differ\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L2: canonicalization of loose input is idempotent\",\n async run(h) {\n if (h.looseTexts === undefined)\n return skip(\"looseTexts is undefined — pass [] if the notation has no loose form\");\n if (h.looseTexts.length === 0) return;\n const m = await h.create();\n try {\n for (const text of h.looseTexts) {\n await m.importText(text);\n const canonical = m.exportText();\n await m.importText(canonical);\n const again = m.exportText();\n if (again !== canonical) fail(\"canonicalization is not idempotent on loose input\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L3: a scripted user mutation fires onContentChanged, and the export already reflects it inside the listener\",\n async run(h) {\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n const before = m.exportText();\n const { calls, exportAtEmit } = await changeEvents(h, m, () => h.mutate(m));\n if (calls === 0) fail(\"a user mutation fired no onContentChanged\");\n const after = m.exportText();\n if (after === before)\n fail(\"the harness mutation did not change the export — the case proves nothing\");\n if (exportAtEmit !== after) {\n fail(\n \"exportText() inside the onContentChanged listener did not return the post-change document (emit before apply)\",\n );\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L3: config/axis edits fire onContentChanged too, with the export already updated\",\n async run(h) {\n if (h.configMutations === undefined) {\n return skip(\n \"configMutations is undefined — pass [] if the notation has no config/axis/title surface\",\n );\n }\n for (const mutation of h.configMutations) {\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n const before = m.exportText();\n const { calls, exportAtEmit } = await changeEvents(h, m, () => mutation.run(m));\n if (calls === 0) fail(`config mutation \"${mutation.name}\" fired no onContentChanged`);\n const after = m.exportText();\n if (after === before) {\n fail(`config mutation \"${mutation.name}\" did not change the export — the case proves nothing`);\n }\n if (exportAtEmit !== after) {\n fail(\n `exportText() inside the listener of config mutation \"${mutation.name}\" did not return the post-change document`,\n );\n }\n } finally {\n m.destroy();\n }\n }\n },\n },\n {\n name: \"L3: unsubscribe stops delivery\",\n async run(h) {\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n let calls = 0;\n const off = m.onContentChanged(() => {\n calls += 1;\n });\n off();\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n if (calls > 0) fail(\"onContentChanged delivered after unsubscribe\");\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L3: undo and redo fire onContentChanged and restore the export\",\n async run(h) {\n if (!h.undo) return skip(\"undo is undefined — the notation declares no undo\");\n const undo = h.undo;\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n const before = m.exportText();\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n const mutated = m.exportText();\n const undone = await changeEvents(h, m, () => undo(m));\n if (undone.calls === 0)\n fail(\"undo fired no onContentChanged — the host would never export the undone state\");\n if (m.exportText() !== before)\n fail(\"undo of a single mutation did not restore the pre-mutation export\");\n if (undone.exportAtEmit !== before)\n fail(\"exportText() inside the undo listener did not return the undone document\");\n if (!h.redo)\n return skip(\n \"redo is undefined — the redo half of this case was not verified (pass redo to verify it)\",\n );\n const redo = h.redo;\n const redone = await changeEvents(h, m, () => redo(m));\n if (redone.calls === 0) fail(\"redo fired no onContentChanged\");\n if (m.exportText() !== mutated) fail(\"redo did not restore the mutated export\");\n if (redone.exportAtEmit !== mutated)\n fail(\"exportText() inside the redo listener did not return the redone document\");\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L4: undo directly after an import is a no-op\",\n async run(h) {\n if (!h.undo) return skip(\"undo is undefined — the notation declares no undo\");\n const first = h.canonicalTexts[0];\n const second = h.canonicalTexts[1];\n if (first === undefined || second === undefined || first === second) {\n fail(\"the L4 case needs TWO DISTINCT canonicalTexts — with one, stale undo is undetectable\");\n }\n const m = await h.create();\n try {\n await m.importText(first);\n await h.mutate(m); // real history exists…\n await h.mutate(m); // …more than one entry deep…\n // leg 1 — GUARANTEED id coincidence: re-import the mutated document\n // itself. A stale diagram-js stack whose revert removes the created\n // shape by id is a silent no-op on any other document and deletes a\n // re-imported element on this one — the tt drift as it really bites\n const mutated = m.exportText();\n await m.importText(mutated);\n const baselineSame = m.exportText();\n const idsSame = sortedIds(m);\n const { calls } = await changeEvents(h, m, () => h.undo!(m));\n if (calls > 0)\n fail(\n \"undo directly after importText fired onContentChanged — history housekeeping is not a change\",\n );\n if (m.exportText() !== baselineSame || (idsSame && !same(idsSame, sortedIds(m)))) {\n fail(\n \"undo after importText removed a re-imported element by id coincidence (stale history applied)\",\n );\n }\n // leg 2 — a DIFFERENT document replaces the doc; a snapshot-restoring\n // stale stack would resurrect the previous one\n await h.mutate(m);\n await m.importText(second);\n // baseline via the modeler's OWN serializer: this case isolates the\n // undo law — a non-canonical serializer must fail L2, not here\n const baseline = m.exportText();\n await h.undo(m);\n await h.undo(m); // drain a stale multi-entry history too\n if (m.exportText() !== baseline) {\n fail(\"undo after importText mutated the freshly imported document (stale history applied)\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L5: getViewState never throws before the first import\",\n async run(h) {\n const m = await h.create();\n try {\n let state: unknown;\n try {\n state = m.getViewState();\n } catch (e) {\n fail(`getViewState threw before the first import: ${String(e)}`);\n }\n if (state !== undefined) assertFinite(state, \"before the first import\");\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L5: view state round-trips across a re-import, an import fits the view — and none of it is document content\",\n async run(h) {\n if (h.viewState === undefined) {\n return skip(\n \"viewState is undefined — supply a panned/zoomed state, a no-op setViewState is otherwise undetectable\",\n );\n }\n const alt = h.viewState;\n const m = await h.create();\n try {\n const text = await importContentText(h, m);\n const doc = m.exportText();\n const fitted = m.getViewState();\n assertFinite(fitted, \"after the first import\");\n // the modeler's OWN readback is the oracle from here on — a\n // diagram-js viewbox comes back rounded and enriched (scale, inner,\n // outer), never byte-equal to the harness literal\n m.setViewState(alt);\n const applied = m.getViewState();\n assertFinite(applied, \"after setViewState\");\n if (same(applied, fitted)) {\n fail(\"setViewState(viewState) had no effect — supply a state that differs from the fitted view\");\n }\n await m.importText(text);\n if (!same(m.getViewState(), fitted)) {\n fail(\n \"an import must leave the view FITTED to the content (the same state the first import produced)\",\n );\n }\n // the host restores with what IT read back before the re-import —\n // the readback must be settable and yield itself again\n const { calls } = await changeEvents(h, m, () => m.setViewState(applied));\n if (calls > 0) fail(\"setViewState fired onContentChanged — view state is not document content\");\n if (m.exportText() !== doc)\n fail(\"setViewState changed the export — view state leaked into the document\");\n if (!same(m.getViewState(), applied)) {\n fail(\n \"view state did not survive re-import + restore (setting the modeler's own readback must yield that readback)\",\n );\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"elements (when present): stable ids across re-imports and unrelated edits, reveal and selection semantics\",\n async run(h) {\n const m = await h.create();\n try {\n const text = h.canonicalTexts[0] ?? \"\";\n await m.importText(text);\n const elements = m.elements;\n if (!elements)\n return skip(\"the modeler exposes no elements surface (optional) — nothing to verify\");\n const ids = elements.list().map((e) => e.id);\n if (new Set(ids).size !== ids.length) fail(\"element ids are not unique\");\n await m.importText(text);\n const again = elements.list().map((e) => e.id);\n if (!same([...ids].sort(), [...again].sort()))\n fail(\"element ids are not stable across re-imports of the same text\");\n if (elements.reveal(\"__modeler_api_no_such_id__\") !== false)\n fail(\"reveal(unknown id) must return false\");\n const firstId = ids[0];\n if (firstId !== undefined) {\n const deliveries: string[][] = [];\n const off = elements.onSelection((selected) => deliveries.push([...selected]));\n const { calls } = await changeEvents(h, m, () => {\n if (elements.reveal(firstId) !== true) fail(\"reveal(known id) must return true\");\n });\n if (calls > 0) fail(\"reveal fired onContentChanged — revealing is view behavior, not an edit\");\n if (!deliveries.some((d) => d.includes(firstId))) {\n fail(\n `reveal('${firstId}') delivered no selection containing the id — is it a selectable element (not a root or a label)?`,\n );\n }\n for (const d of deliveries) {\n for (const id of d) {\n if (!again.includes(id)) {\n fail(\n `onSelection reported an id that is not an element: '${id}' (label ids must resolve to their target)`,\n );\n }\n }\n }\n if (!elements.selection().includes(firstId)) fail(\"selection() does not contain the revealed id\");\n const seen = deliveries.length;\n off();\n // a DIFFERENT selection after the unsubscribe — re-revealing the\n // same id would be deduplicated by many engines and prove nothing\n const otherId = ids[1];\n if (otherId !== undefined) elements.reveal(otherId);\n else await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n if (deliveries.length !== seen) fail(\"onSelection delivered after unsubscribe\");\n }\n // an edit to ANOTHER element must not renumber existing ids: every id\n // still exists AND (where the notation has names) still names the\n // same element — a positional scheme keeps the id strings while\n // shifting what they point at\n const namesBefore = new Map(elements.list().map((e) => [e.id, e.name]));\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n const afterEdit = new Map(elements.list().map((e) => [e.id, e.name]));\n if (afterEdit.size <= namesBefore.size) {\n fail(\n \"the harness mutate must ADD an element (the elements case verifies ids under an unrelated edit)\",\n );\n }\n for (const [id, name] of namesBefore) {\n if (!afterEdit.has(id)) {\n fail(\n `element id '${id}' vanished after an unrelated edit — ids must survive edits to other elements`,\n );\n }\n if (name !== undefined && afterEdit.get(id) !== name) {\n fail(\n `element id '${id}' names a different element after an unrelated edit ('${name}' → '${String(afterEdit.get(id))}')`,\n );\n }\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L6: export is total after imports, user mutations and undo\",\n async run(h) {\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n for (const mutation of mutationsOf(h)) {\n await mutation.run(m);\n exportOrFail(m, `after ${mutation.name}`);\n }\n if (h.undo) {\n await h.undo(m);\n exportOrFail(m, \"after an undo\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L6: foreign content survives an import → export round-trip\",\n async run(h) {\n if (h.foreignTexts === undefined) {\n return skip(\"foreignTexts is undefined — pass [] if the notation has no foreign form\");\n }\n if (h.foreignTexts.length === 0) return;\n const m = await h.create();\n try {\n await foreignRoundTrips(h, m, \"\");\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L7: a rejected import leaves no trace\",\n async run(h) {\n if (h.invalidTexts === undefined) {\n return skip(\"invalidTexts is undefined — pass [] if the notation has no non-importable text\");\n }\n if (h.invalidTexts.length === 0) return;\n const m = await h.create();\n try {\n const text = h.canonicalTexts[0] ?? \"\";\n await m.importText(text);\n const beforeEdit = m.exportText();\n // with undo history on the stack: a rejected import must leave it\n // intact too (an engine that erases history BEFORE parsing does not)\n if (h.undo) {\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n }\n const doc = m.exportText();\n const ids = sortedIds(m);\n for (const bad of h.invalidTexts) {\n let rejected = false;\n const { calls } = await changeEvents(h, m, () =>\n m.importText(bad).then(\n () => undefined,\n () => {\n rejected = true;\n },\n ),\n );\n if (!rejected) fail(\"importText resolved for a non-importable input — it must REJECT\");\n if (calls > 0) fail(\"a rejected import fired onContentChanged\");\n if (m.exportText() !== doc)\n fail(\"a rejected import changed the document — the previous state must stay intact\");\n if (ids && !same(ids, sortedIds(m))) {\n fail(\n \"a rejected import changed the rendered elements — the canvas must keep the previous document\",\n );\n }\n }\n if (h.undo) {\n await h.undo(m);\n if (m.exportText() !== beforeEdit) {\n fail(\n \"a rejected import erased the undo history — the previous document's history must stay intact\",\n );\n }\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L7: the modeler stays usable after a rejected import\",\n async run(h) {\n if (h.invalidTexts === undefined) {\n return skip(\"invalidTexts is undefined — pass [] if the notation has no non-importable text\");\n }\n const bad = h.invalidTexts[0];\n if (bad === undefined) return;\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n await m.importText(bad).then(\n () => fail(\"importText resolved for a non-importable input — it must REJECT\"),\n () => undefined,\n );\n const next = secondText(h);\n await m\n .importText(next)\n .catch((e) => fail(`the next VALID import after a rejection did not render: ${String(e)}`));\n if (m.exportText() !== next)\n fail(\"the next valid import after a rejection did not replace the document\");\n const before = m.exportText();\n const { calls } = await changeEvents(h, m, () => h.mutate(m));\n if (calls === 0) fail(\"a user mutation after a rejected import fired no onContentChanged (wedged)\");\n if (exportOrFail(m, \"after a mutation that followed a rejected import\") === before) {\n fail(\"a user mutation after a rejected import did not apply (wedged)\");\n }\n if (h.undo) {\n await h.undo(m);\n exportOrFail(m, \"after an undo that followed a rejected import\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"viewer (when shipped): read-only, silent, canonical, fits, reveals, keeps foreign content, unshaken by a rejected import\",\n async run(h) {\n if (!h.createReadonly) return skip(\"createReadonly is undefined — the package ships no viewer mount\");\n const v = await h.createReadonly();\n try {\n if (v.meta.editable !== false)\n fail(\"createReadonly() must return a viewer (meta.editable === false)\");\n try {\n const fresh = v.getViewState();\n if (fresh !== undefined) assertFinite(fresh, \"on the viewer before the first import\");\n } catch (e) {\n fail(`the viewer's getViewState threw before the first import: ${String(e)}`);\n }\n let calls = 0;\n const off = v.onContentChanged(() => {\n calls += 1;\n });\n try {\n for (const text of h.canonicalTexts) {\n await v\n .importText(text)\n .catch((e) => fail(`the viewer rejected a canonical text: ${String(e)}`));\n if (exportOrFail(v, \"on the viewer\") !== text)\n fail(\"the viewer's export is not the canonical text\");\n }\n const doc = v.exportText();\n const ids = sortedIds(v);\n if (v.elements) {\n const firstId = v.elements.list()[0]?.id;\n if (firstId !== undefined) {\n const deliveries: string[][] = [];\n const offSel = v.elements.onSelection((s) => deliveries.push([...s]));\n if (v.elements.reveal(firstId) !== true)\n fail(\"the viewer's reveal(known id) must return true\");\n await (h.settle ?? defaultSettle)();\n if (!deliveries.some((d) => d.includes(firstId)))\n fail(\"the viewer's reveal delivered no selection containing the id\");\n if (!v.elements.selection().includes(firstId))\n fail(\"the viewer's selection() does not contain the revealed id\");\n offSel();\n }\n if (v.elements.reveal(\"__modeler_api_no_such_id__\") !== false)\n fail(\"the viewer's reveal(unknown id) must return false\");\n }\n for (const bad of h.invalidTexts ?? []) {\n await v.importText(bad).then(\n () => fail(\"the viewer resolved a non-importable input — it must REJECT\"),\n () => undefined,\n );\n if (v.exportText() !== doc) fail(\"a rejected import changed the viewer's document\");\n if (ids && !same(ids, sortedIds(v)))\n fail(\"a rejected import changed the viewer's rendered elements\");\n }\n await foreignRoundTrips(h, v, \"the viewer's \");\n if (h.viewState !== undefined) {\n const text = await importContentText(h, v);\n const fitted = v.getViewState();\n assertFinite(fitted, \"on the viewer after import\");\n v.setViewState(h.viewState);\n const applied = v.getViewState();\n if (same(applied, fitted)) fail(\"the viewer's setViewState had no effect\");\n await v.importText(text);\n if (!same(v.getViewState(), fitted))\n fail(\"the viewer's import must leave the view fitted (L5)\");\n v.setViewState(applied);\n if (!same(v.getViewState(), applied))\n fail(\"setting the viewer's own readback did not yield that readback\");\n }\n await (h.settle ?? defaultSettle)();\n if (calls > 0)\n fail(`the viewer fired onContentChanged ${calls}x — a viewer never changes content`);\n } finally {\n off();\n }\n } finally {\n v.destroy();\n v.destroy(); // must not throw — same as the editor\n }\n },\n },\n {\n name: \"destroy is safe to call twice\",\n async run(h) {\n const m = await h.create();\n m.destroy();\n m.destroy(); // must not throw\n },\n },\n ];\n}\n\n/** the L6 foreign-content round-trips, on an editor or a viewer */\nasync function foreignRoundTrips(h: ConformanceHarness, m: CollaborativeModeler, who: string): Promise<void> {\n for (const { text, mustSurvive } of h.foreignTexts ?? []) {\n if (mustSurvive.length === 0) fail(\"a foreignTexts entry must name at least one fragment in mustSurvive\");\n await m.importText(text); // warnings allowed — a throw is the violation\n const first = exportOrFail(m, `${who}after importing a document with foreign content`);\n for (const fragment of mustSurvive) {\n if (!first.includes(fragment))\n fail(`${who}foreign content was dropped on import → export: '${fragment}' is missing`);\n }\n await m.importText(first);\n const second = exportOrFail(m, `${who}after re-importing its own export of foreign content`);\n if (second !== first) fail(`${who}foreign content did not survive a second import → export round-trip`);\n }\n}\n\nexport interface RegisterOptions {\n /** how a SKIPPED case is reported — a law the kit could not (fully) verify\n * because the harness left its field undefined. Default: console.warn */\n onSkip?: (caseName: string, reason: string) => void;\n}\n\n/** bind every case to a (name, fn) test runner — node:test, vitest, jest */\nexport function registerConformance(\n harness: ConformanceHarness,\n test: (name: string, fn: () => Promise<void>) => unknown,\n options: RegisterOptions = {},\n): void {\n const onSkip =\n options.onSkip ??\n ((name: string, reason: string) => {\n console.warn(`modeler-api conformance: SKIPPED \"${name}\" — ${reason}`);\n });\n for (const conformanceCase of conformanceCases()) {\n test(`conformance: ${conformanceCase.name}`, async () => {\n const outcome = await conformanceCase.run(harness);\n if (outcome?.skipped) onSkip(conformanceCase.name, outcome.skipped);\n });\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/conformance.ts"],"sourcesContent":["/**\n * The conformance test kit — every law of the CollaborativeModeler contract\n * as an executable case. A modeler repo supplies a small HARNESS (how to\n * create an instance, canonical sample texts, scripted user mutations) and\n * binds the cases to its own test runner:\n *\n * import { conformanceCases, registerConformance } from \"@miragon/modeler-api/conformance\";\n * registerConformance(harness, test); // node:test, vitest, jest — any (name, fn) runner\n *\n * Cases throw plain Errors on violation, so the kit has no runner or\n * assertion-library dependency. A case that CANNOT (fully) run because the\n * harness left an optional field undefined resolves with `{ skipped }` and\n * registerConformance reports it (console.warn by default) — a law the kit\n * did not verify is never a silent green. A RECOMMENDATION the modeler does\n * not follow resolves with `{ advisories }`, reported the same way and never\n * a failure: a recommendation becomes a law only in a major release.\n */\nimport {\n apiVersionsCompatible,\n type CollaborativeModeler,\n type ElementBounds,\n type ElementsSurface,\n MODELER_API_VERSION,\n} from \"./index.js\";\n\nexport { apiVersionsCompatible };\n\nexport interface UserMutation {\n /** names the failing mutation in the kit's error message */\n name: string;\n run(modeler: CollaborativeModeler): Promise<void> | void;\n}\n\nexport interface ConformanceHarness {\n /**\n * a fresh EDITABLE modeler, attached to a host element that is IN the\n * document with a non-zero size (`attachedHost()` below) — diagram-js\n * engines compute viewboxes from the host's client size, so a detached\n * or 0×0 host yields NaN view state; they also need a real browser\n * (vitest browser mode / Playwright), jsdom has no getBBox. Keep\n * `fitOnImport` at its default: the L5 cases check the modeler's own fit\n */\n create(): Promise<CollaborativeModeler>;\n /** a fresh READ-ONLY instance (`meta.editable === false`) — the viewer\n * mount hosts use on read-only surfaces; absent = the package ships no\n * viewer and the viewer case is reported as skipped */\n createReadonly?(): Promise<CollaborativeModeler>;\n /**\n * CANONICAL sample documents of the notation (>= 2, distinct, at least one\n * rendering an element, ideally several covering config/axis content too)\n * — exactly what exportText emits. Obtain them by exporting a hand-authored\n * document and REVIEWING the bytes, not by pasting whatever the current\n * build prints: L2 only proves the serializer is a fixpoint of the texts\n * you vouch for\n */\n canonicalTexts: string[];\n /** importable but non-canonical inputs (hand-authored variants); the kit\n * verifies canonicalization is IDEMPOTENT on them, not byte-stable.\n * `undefined` = skipped; `[]` = the notation has no loose form */\n looseTexts?: string[];\n /**\n * NON-importable inputs (half-typed peer text, wrong notation) — the kit\n * verifies importText REJECTS, leaves no trace and stays usable (L7).\n * `undefined` = not provided (the L7 cases report a SKIP); `[]` = the\n * deliberate statement that this notation has no LISTED non-importable\n * text (the lenient line DSLs keep every line, so nothing rejects). The\n * probes below run either way, so a parser that does reject some input\n * is held to L7 on it\n */\n invalidTexts?: string[];\n /**\n * extra inputs for the L7 probe case, on top of the probes the kit derives\n * from every canonical text: its first half, its first third, the text\n * without its last character and the text followed by a NUL character.\n * Unlike `invalidTexts`, a probe need not reject: one that imports must\n * be silent (L1) with `exportText` total (L6), one that rejects must leave\n * no trace (L7). Add the damaged forms your parser is likely to meet (a\n * cut-off JSON object, an unclosed block). `undefined` and `[]` both mean\n * \"the derived probes only\" — the probe case never skips\n */\n probeTexts?: string[];\n /**\n * importable documents carrying content the modeler does NOT understand\n * (vendor extensions, unknown lines, unknown JSON keys), each with the\n * fragments that must come back from an import → export round-trip (L6).\n * `undefined` = skipped; `[]` = the notation has no foreign form\n */\n foreignTexts?: Array<{ text: string; mustSurvive: string[] }>;\n /**\n * a NON-default view state the modeler accepts — a panned/zoomed viewport\n * (e.g. a viewbox) that differs from what an import leaves; L5 cannot tell\n * a restore from a no-op without one. The kit never compares a readback\n * with this literal: it sets it, reads the modeler's own (possibly\n * normalised) state back, and requires THAT to survive a re-import and to\n * be settable itself. `undefined` = L5 is reported as skipped\n */\n viewState?: unknown;\n /** wait until deferred/debounced change events have been delivered —\n * MUST outlast the modeler's event debounce; defaults to one macrotask */\n settle?(): Promise<void>;\n /**\n * perform ONE user-visible atomic ELEMENT command through the modeler's\n * own commands: ADD an element — never rename or delete one (the elements\n * cases treat it as an edit unrelated to the existing elements and compare\n * the ids two instances allocate for it, and L4 builds undo history from\n * it, so it must be undoable). Let each call's element differ in content\n * from the previous one (a counter the harness shares across instances,\n * as the reference harness does): the collision case compares the ids two\n * fresh instances allocate for one new element each, and an id derived\n * from identical content (one that spells out the element's name)\n * collides by design. The kit also runs it once on a\n * fresh instance BEFORE the first import; a throw there is tolerated, and\n * so is a call that never settles (a mutate waiting for the change event\n * of a command the modeler ignores without a document): the kit stops\n * waiting after 2 s — what a command does without a document is the\n * modeler's choice\n */\n mutate(modeler: CollaborativeModeler): Promise<void> | void;\n /**\n * user-visible changes that do NOT run through an element command:\n * config/axis/title/style/size edits that live in the document. A notation\n * with such surfaces MUST list them — the kit cannot see what the canvas\n * offers, and a modeler that only forwards its command stack passes\n * `mutate` while a host loses exactly these edits. `undefined` = skipped;\n * `[]` = the notation has no such surface\n */\n configMutations?: UserMutation[];\n /** trigger the modeler's undo; `undefined` = the notation has no undo\n * (the undo cases are reported as skipped) */\n undo?(modeler: CollaborativeModeler): Promise<void> | void;\n /** trigger the modeler's redo; `undefined` with `undo` defined = the redo\n * half of the undo/redo case is reported as skipped */\n redo?(modeler: CollaborativeModeler): Promise<void> | void;\n /**\n * move ONE element read from `canonicalTexts[0]` (imported, then one\n * `mutate` added an element) through the modeler's own commands, the way\n * a user drags it, far enough that its box changes position after any\n * snapping, rounding or clamping — and return that element's id\n * (ElementInfo.id). The bounds case requires `elements.bounds` to report\n * the moved box. `undefined` = that half of the bounds case is reported as\n * skipped (only when the modeler has `bounds`)\n */\n moveElement?(modeler: CollaborativeModeler): Promise<string> | string;\n /**\n * open an INTERACTION on an element of `canonicalTexts[0]` (already\n * imported) the way a user does — its label editor, or a popup with an\n * input — and type input WITHOUT committing it (no Enter, no blur). The\n * kit then imports over it (L1) and changes the view over it (L5), and\n * neither may commit it. Type the same input on every call, one the\n * serializer writes verbatim: the L5 case looks for it in the export and\n * compares what a commit writes with and without a view change in\n * between. `undefined` = the L1 and L5 interaction cases are reported as\n * skipped\n */\n openInteraction?(modeler: CollaborativeModeler): Promise<void> | void;\n /**\n * the interaction open right now — the element it edits and its\n * uncommitted input — or `undefined` when none is open. The kit reads it\n * before and after a re-import of the same text to check the RECOMMENDED\n * keep-and-reopen (an advisory, never a failure). `undefined` = that half\n * of the L1 interaction case is reported as skipped\n */\n interactionState?(modeler: CollaborativeModeler): InteractionState | undefined;\n /**\n * commit the interaction `openInteraction` opened, the way a user does\n * (Enter, or a click on the canvas). The L5 interaction case commits once\n * without a view change, proving the typed input changes the document,\n * and once after a host's view change, checking the RECOMMENDED keep-open\n * (an advisory, never a failure). `undefined` = both halves are reported\n * as skipped\n */\n commitInteraction?(modeler: CollaborativeModeler): Promise<void> | void;\n}\n\n/** an interaction in progress, as `ConformanceHarness.interactionState` reports it */\nexport interface InteractionState {\n /** the stable id of the element the interaction edits (ElementInfo.id) */\n elementId: string;\n /** the uncommitted input, when the interaction has one */\n text?: string;\n}\n\nexport interface CaseOutcome {\n /** the case could not (fully) verify its law — the harness left the field\n * it needs undefined, or the modeler has no such optional surface; the\n * reason names which */\n skipped?: string;\n /**\n * RECOMMENDED behaviour the modeler does not show. The case passed — the\n * contract only recommends what is missing (a candidate for a law in the\n * next major) — and registerConformance reports each advice through\n * `onAdvisory`, never as a failure\n */\n advisories?: string[];\n}\n\nexport interface ConformanceCase {\n name: string;\n run(harness: ConformanceHarness): Promise<CaseOutcome | void>;\n}\n\n/**\n * a host element of the given size, attached to the document — what\n * `create()` should mount into (browser only). The returned element sits\n * INSIDE a sized wrapper: the Miragon renderers restyle the container they\n * are given to 100% × 100%, which then resolves against the wrapper\n */\nexport function attachedHost(width = 800, height = 600): HTMLElement {\n const wrapper = document.createElement(\"div\");\n wrapper.style.cssText = `width:${width}px;height:${height}px;position:absolute;left:0;top:0;overflow:hidden`;\n const host = document.createElement(\"div\");\n host.style.cssText = \"width:100%;height:100%\";\n wrapper.append(host);\n document.body.append(wrapper);\n return host;\n}\n\nfunction fail(message: string): never {\n throw new Error(`modeler-api conformance: ${message}`);\n}\n\nconst skip = (reason: string): CaseOutcome => ({ skipped: reason });\n\nfunction exportOrFail(modeler: CollaborativeModeler, when: string): string {\n try {\n return modeler.exportText();\n } catch (e) {\n fail(`exportText threw ${when}: ${String(e)}`);\n }\n}\n\nconst same = (a: unknown, b: unknown): boolean => JSON.stringify(a) === JSON.stringify(b);\n\ntype BoundedElements = ElementsSurface & { bounds(id: string): ElementBounds | undefined };\n\nconst hasBounds = (elements: ElementsSurface): elements is BoundedElements =>\n typeof elements.bounds === \"function\";\n\nconst isFiniteBox = (box: ElementBounds | undefined): box is ElementBounds =>\n box !== undefined &&\n [box.x, box.y, box.width, box.height].every((n) => typeof n === \"number\" && Number.isFinite(n)) &&\n box.width >= 0 &&\n box.height >= 0;\n\nconst describeBox = (box: ElementBounds | undefined): string =>\n box === undefined\n ? \"undefined\"\n : `{ x: ${box.x}, y: ${box.y}, width: ${box.width}, height: ${box.height} }`;\n\n/** every listed element's box, copied — a listed id without a finite box fails */\nfunction boxesOf(elements: BoundedElements, when: string): Map<string, ElementBounds> {\n const boxes = new Map<string, ElementBounds>();\n for (const { id } of elements.list()) {\n const box = elements.bounds(id);\n if (!isFiniteBox(box)) {\n fail(\n `elements.bounds('${id}') ${when} is ${describeBox(box)} — every listed id must give a finite box with a non-negative size`,\n );\n }\n boxes.set(id, { x: box.x, y: box.y, width: box.width, height: box.height });\n }\n return boxes;\n}\n\n/** a view state with NaN/Infinity is what a detached or 0×0 host produces */\nfunction assertFinite(state: unknown, when: string): void {\n const json = JSON.stringify(state, (_k, v: unknown) =>\n typeof v === \"number\" && !Number.isFinite(v) ? \"__non_finite__\" : v,\n );\n if (json?.includes(\"__non_finite__\")) {\n fail(\n `getViewState() ${when} contains a non-finite number — the host has no size (mount into attachedHost())`,\n );\n }\n}\n\nconst defaultSettle = (): Promise<void> => new Promise((resolve) => setTimeout(resolve, 0));\n\n/** how long the pre-import case waits for `mutate` on an instance without a document */\nconst PRE_IMPORT_CALL_BOUND_MS = 2000;\n\n/** run a harness call whose throw and whose hang are both tolerated: it gets\n * `ms` to settle, then the kit moves on (the call may keep running) */\nasync function toleratedCall(action: () => Promise<void> | void, ms: number): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const call = Promise.resolve()\n .then(action)\n .catch(() => undefined);\n const bound = new Promise<void>((resolve) => {\n timer = setTimeout(resolve, ms);\n });\n try {\n await Promise.race([call, bound]);\n } finally {\n clearTimeout(timer);\n }\n}\n\n/** count invocations of onContentChanged around an action — settle-aware, so\n * debounced engines neither hide an echo nor fail a legitimate event; the\n * export seen INSIDE the last delivery is returned too (L3's ordering rule) */\nasync function changeEvents(\n harness: ConformanceHarness,\n modeler: CollaborativeModeler,\n action: () => Promise<void> | void,\n): Promise<{ calls: number; exportAtEmit: string | undefined }> {\n const settle = harness.settle ?? defaultSettle;\n let calls = 0;\n let exportAtEmit: string | undefined;\n const off = modeler.onContentChanged(() => {\n calls += 1;\n exportAtEmit = modeler.exportText();\n });\n try {\n await action();\n await settle();\n } finally {\n off();\n }\n return { calls, exportAtEmit };\n}\n\n/** an id or a name reduced to its letters and digits, lower case — `component_new_component` and\n * `New Component` share `newcomponent` */\nconst lettersAndDigits = (value: string): string => value.toLowerCase().replace(/[^\\p{L}\\p{N}]+/gu, \"\");\n\nconst secondText = (h: ConformanceHarness): string => h.canonicalTexts[1] ?? h.canonicalTexts[0] ?? \"\";\n\n/** the harness's user mutations: the element command first, then every config edit */\nconst mutationsOf = (h: ConformanceHarness): UserMutation[] => [\n { name: \"mutate (element command)\", run: (m) => h.mutate(m) },\n ...(h.configMutations ?? []),\n];\n\nconst sortedIds = (m: CollaborativeModeler): string[] | undefined =>\n m.elements ? [...m.elements.list().map((e) => e.id)].sort() : undefined;\n\n/** element id → name; `undefined` without an elements surface */\nconst namesById = (m: CollaborativeModeler): Map<string, string | undefined> | undefined =>\n m.elements ? new Map(m.elements.list().map((e) => [e.id, e.name])) : undefined;\n\nasync function importOrFail(m: CollaborativeModeler, text: string, failure: string): Promise<void> {\n await m.importText(text).catch((e: unknown) => fail(`${failure}: ${String(e)}`));\n}\n\ninterface Probe {\n /** where the probe comes from, for the failure message */\n label: string;\n text: string;\n}\n\nconst describeProbe = (probe: Probe): string => {\n const excerpt = probe.text.length > 40 ? `${probe.text.slice(0, 40)}…` : probe.text;\n return `${probe.label} (${JSON.stringify(excerpt)})`;\n};\n\n/**\n * damaged forms of every canonical text — what a host re-imports while a\n * peer is still typing (cut mid-way, the last character missing) or after a\n * transport glitch (a trailing NUL) — plus the harness's own probeTexts.\n * Cuts fall on code points, never inside a surrogate pair; duplicates are\n * dropped so each input runs once\n */\nfunction probesOf(h: ConformanceHarness): Probe[] {\n const probes: Probe[] = [];\n h.canonicalTexts.forEach((text, index) => {\n const source = `canonicalTexts[${index}]`;\n const codePoints = Array.from(text);\n const cut = (length: number): string => codePoints.slice(0, length).join(\"\");\n probes.push(\n { label: `the first half of ${source}`, text: cut(Math.floor(codePoints.length / 2)) },\n { label: `the first third of ${source}`, text: cut(Math.floor(codePoints.length / 3)) },\n { label: `${source} without its last character`, text: cut(codePoints.length - 1) },\n { label: `${source} followed by a NUL character`, text: `${text}\\u0000` },\n );\n });\n (h.probeTexts ?? []).forEach((text, index) => probes.push({ label: `probeTexts[${index}]`, text }));\n const seen = new Set<string>();\n return probes.filter((probe) => {\n if (seen.has(probe.text)) return false;\n seen.add(probe.text);\n return true;\n });\n}\n\n/**\n * import the first canonical text that renders at least one element (an\n * empty document has no fitted view — renderers leave the viewport alone),\n * and return it; without an elements surface the first text is used\n */\nasync function importContentText(h: ConformanceHarness, m: CollaborativeModeler): Promise<string> {\n for (const text of h.canonicalTexts) {\n await m.importText(text);\n if (!m.elements || m.elements.list().length > 0) return text;\n }\n fail(\"L5 needs a canonical text that renders at least one element — an empty document has no fitted view\");\n}\n\nexport function conformanceCases(): ConformanceCase[] {\n return [\n {\n name: \"meta declares the notation, editability and a compatible apiVersion\",\n async run(h) {\n const m = await h.create();\n try {\n if (!m.meta.notation) fail(\"meta.notation is empty\");\n if (m.meta.editable !== true)\n fail(\"create() must return an EDITABLE modeler (meta.editable === true)\");\n if (!apiVersionsCompatible(m.meta.apiVersion, MODELER_API_VERSION)) {\n fail(`apiVersion ${m.meta.apiVersion} is not compatible with ${MODELER_API_VERSION}`);\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"calls before the first import never wedge the instance\",\n async run(h) {\n const m = await h.create();\n let control: CollaborativeModeler | undefined;\n try {\n // what these return without a document is the modeler's choice —\n // only the instance has to survive them\n try {\n m.exportText();\n } catch {\n // a throw before the first import is allowed\n }\n await toleratedCall(() => h.mutate(m), PRE_IMPORT_CALL_BOUND_MS);\n await (h.settle ?? defaultSettle)();\n control = await h.create();\n const texts = h.canonicalTexts.length > 0 ? h.canonicalTexts.slice(0, 2) : [\"\"];\n let last = \"\";\n for (const [index, text] of texts.entries()) {\n const source = `canonicalTexts[${index}]`;\n // the oracle is a fresh instance's export of the same text: this\n // case isolates the pre-import calls — a non-canonical serializer\n // fails L2, not here\n await control.importText(text);\n const expected = exportOrFail(control, `after importing ${source} into a fresh instance`);\n await importOrFail(\n m,\n text,\n `importText(${source}) rejected on an instance that ran exportText() and a user command before its first import (wedged)`,\n );\n last = exportOrFail(m, `after importing ${source} into an instance used before its first import`);\n if (last !== expected) {\n fail(\n `importText(${source}) on an instance that ran exportText() and a user command before its first import did not render that document:\\n--- expected (a fresh instance's export)\\n${expected}\\n--- got\\n${last}`,\n );\n }\n }\n const { calls } = await changeEvents(h, m, () => h.mutate(m));\n if (calls === 0) {\n fail(\n \"a user mutation fired no onContentChanged on an instance used before its first import (wedged)\",\n );\n }\n if (exportOrFail(m, \"after a user mutation on an instance used before its first import\") === last) {\n fail(\"a user mutation did not apply on an instance used before its first import (wedged)\");\n }\n } finally {\n m.destroy();\n control?.destroy();\n }\n },\n },\n {\n name: \"L1: importText is silent (no change events, for canonical, loose and foreign inputs)\",\n async run(h) {\n const m = await h.create();\n try {\n const texts = [\n ...h.canonicalTexts,\n ...(h.looseTexts ?? []),\n ...(h.foreignTexts ?? []).map((f) => f.text),\n ];\n for (const text of texts) {\n const { calls } = await changeEvents(h, m, () => m.importText(text).then(() => undefined));\n if (calls > 0) fail(`importText fired onContentChanged ${calls}x`);\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L1: importText over EXISTING undo history is silent too\",\n async run(h) {\n // diagram-js' commandStack.clear() emits 'changed' whether or not the\n // stack is empty — but an engine that only emits when it has something\n // to clear would pass the fresh-instance case above\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n const { calls } = await changeEvents(h, m, () => m.importText(secondText(h)).then(() => undefined));\n if (calls > 0) fail(`importText over a non-empty undo history fired onContentChanged ${calls}x`);\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L1: an interaction open during importText never commits\",\n async run(h) {\n if (!h.openInteraction) {\n return skip(\n \"openInteraction is undefined — an interaction open during an import (a label editor with typed input) was not verified\",\n );\n }\n const openInteraction = h.openInteraction;\n const first = h.canonicalTexts[0] ?? \"\";\n const second = h.canonicalTexts[1];\n const m = await h.create();\n try {\n // the oracles come from the modeler's OWN serializer: this case\n // isolates the interaction — a non-canonical serializer fails L2\n const legs: Array<{ text: string; expected: string; what: string }> = [];\n // a DIFFERENT document usually removes the edited element (a commit\n // would land on a removed element); the SAME document keeps it (a\n // commit would write the typed input into the re-imported document)\n if (second !== undefined) {\n await m.importText(second);\n const expected = exportOrFail(m, \"after importing canonicalTexts[1]\");\n legs.push({ text: second, expected, what: \"a different document (canonicalTexts[1])\" });\n }\n await m.importText(first);\n const firstExport = exportOrFail(m, \"after importing canonicalTexts[0]\");\n legs.push({\n text: first,\n expected: firstExport,\n what: \"the same document again (canonicalTexts[0])\",\n });\n let advice: string | undefined;\n for (const [index, leg] of legs.entries()) {\n await m.importText(first);\n await openInteraction(m);\n await (h.settle ?? defaultSettle)();\n const opened = h.interactionState?.(m);\n if (h.interactionState && opened === undefined) {\n fail(\"interactionState reports no interaction after openInteraction — the case proves nothing\");\n }\n const { calls } = await changeEvents(h, m, () => m.importText(leg.text).then(() => undefined));\n if (calls > 0) {\n fail(\n `importing ${leg.what} over an open interaction fired onContentChanged ${calls}x — the interaction committed during or because of the import (if every import emits, see the L1 cases first)`,\n );\n }\n const reopened = h.interactionState?.(m);\n if (exportOrFail(m, `after importing ${leg.what} over an open interaction`) !== leg.expected) {\n fail(\n `importing ${leg.what} over an open interaction did not leave that document — the interaction's input was applied to it`,\n );\n }\n if (h.undo) {\n await h.undo(m);\n await (h.settle ?? defaultSettle)();\n if (exportOrFail(m, `after an undo that followed importing ${leg.what}`) !== leg.expected) {\n fail(\n `undo after importing ${leg.what} over an open interaction changed the document — the interaction was committed onto the fresh undo history (L4)`,\n );\n }\n }\n const sameDocument = index === legs.length - 1;\n const kept = reopened?.elementId === opened?.elementId && reopened?.text === opened?.text;\n if (sameDocument && opened !== undefined && !kept) {\n const input = opened.text === undefined ? \"\" : ` with the input ${JSON.stringify(opened.text)}`;\n const after = reopened === undefined ? \"closed it\" : `left ${JSON.stringify(reopened)} open`;\n advice = `an interaction on '${opened.elementId}'${input} did not survive a re-import of the same document (the import ${after}) — RECOMMENDED: keep the typed input and reopen the interaction on the element with the same id, so a remote re-import does not cost the user what they were typing`;\n }\n }\n const unverified = [\n ...(second === undefined\n ? [\n \"canonicalTexts holds one text — importing a DIFFERENT document over an open interaction was not verified (add a second canonical text)\",\n ]\n : []),\n ...(h.interactionState\n ? []\n : [\n \"interactionState is undefined — the RECOMMENDED keep-and-reopen of an interaction across an import was not checked (pass interactionState to check it)\",\n ]),\n ];\n return {\n ...(unverified.length > 0 ? { skipped: unverified.join(\"; \") } : {}),\n ...(advice === undefined ? {} : { advisories: [advice] }),\n };\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L2: canonical texts are byte fixpoints of import → export\",\n async run(h) {\n if (h.canonicalTexts.length === 0) fail(\"harness provides no canonicalTexts\");\n const m = await h.create();\n try {\n for (const text of h.canonicalTexts) {\n await m.importText(text);\n const out = m.exportText();\n if (out !== text)\n fail(`export after import differs from the canonical input:\\n--- in\\n${text}\\n--- out\\n${out}`);\n if (m.exportText() !== out)\n fail(\"exportText is not deterministic — two exports of one document differ\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L2: canonicalization of loose input is idempotent\",\n async run(h) {\n if (h.looseTexts === undefined)\n return skip(\"looseTexts is undefined — pass [] if the notation has no loose form\");\n if (h.looseTexts.length === 0) return;\n const m = await h.create();\n try {\n for (const text of h.looseTexts) {\n await m.importText(text);\n const canonical = m.exportText();\n await m.importText(canonical);\n const again = m.exportText();\n if (again !== canonical) fail(\"canonicalization is not idempotent on loose input\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L3: a scripted user mutation fires onContentChanged, and the export already reflects it inside the listener\",\n async run(h) {\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n const before = m.exportText();\n const { calls, exportAtEmit } = await changeEvents(h, m, () => h.mutate(m));\n if (calls === 0) fail(\"a user mutation fired no onContentChanged\");\n const after = m.exportText();\n if (after === before)\n fail(\"the harness mutation did not change the export — the case proves nothing\");\n if (exportAtEmit !== after) {\n fail(\n \"exportText() inside the onContentChanged listener did not return the post-change document (emit before apply)\",\n );\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L3: config/axis edits fire onContentChanged too, with the export already updated\",\n async run(h) {\n if (h.configMutations === undefined) {\n return skip(\n \"configMutations is undefined — pass [] if the notation has no config/axis/title surface\",\n );\n }\n for (const mutation of h.configMutations) {\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n const before = m.exportText();\n const { calls, exportAtEmit } = await changeEvents(h, m, () => mutation.run(m));\n if (calls === 0) fail(`config mutation \"${mutation.name}\" fired no onContentChanged`);\n const after = m.exportText();\n if (after === before) {\n fail(`config mutation \"${mutation.name}\" did not change the export — the case proves nothing`);\n }\n if (exportAtEmit !== after) {\n fail(\n `exportText() inside the listener of config mutation \"${mutation.name}\" did not return the post-change document`,\n );\n }\n } finally {\n m.destroy();\n }\n }\n },\n },\n {\n name: \"L3: unsubscribe stops delivery\",\n async run(h) {\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n let calls = 0;\n const off = m.onContentChanged(() => {\n calls += 1;\n });\n off();\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n if (calls > 0) fail(\"onContentChanged delivered after unsubscribe\");\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L3: undo and redo fire onContentChanged and restore the export\",\n async run(h) {\n if (!h.undo) return skip(\"undo is undefined — the notation declares no undo\");\n const undo = h.undo;\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n const before = m.exportText();\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n const mutated = m.exportText();\n const undone = await changeEvents(h, m, () => undo(m));\n if (undone.calls === 0)\n fail(\"undo fired no onContentChanged — the host would never export the undone state\");\n if (m.exportText() !== before)\n fail(\"undo of a single mutation did not restore the pre-mutation export\");\n if (undone.exportAtEmit !== before)\n fail(\"exportText() inside the undo listener did not return the undone document\");\n if (!h.redo)\n return skip(\n \"redo is undefined — the redo half of this case was not verified (pass redo to verify it)\",\n );\n const redo = h.redo;\n const redone = await changeEvents(h, m, () => redo(m));\n if (redone.calls === 0) fail(\"redo fired no onContentChanged\");\n if (m.exportText() !== mutated) fail(\"redo did not restore the mutated export\");\n if (redone.exportAtEmit !== mutated)\n fail(\"exportText() inside the redo listener did not return the redone document\");\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L4: undo directly after an import is a no-op\",\n async run(h) {\n if (!h.undo) return skip(\"undo is undefined — the notation declares no undo\");\n const first = h.canonicalTexts[0];\n const second = h.canonicalTexts[1];\n if (first === undefined || second === undefined || first === second) {\n fail(\"the L4 case needs TWO DISTINCT canonicalTexts — with one, stale undo is undetectable\");\n }\n const m = await h.create();\n try {\n await m.importText(first);\n await h.mutate(m); // real history exists…\n await h.mutate(m); // …more than one entry deep…\n // leg 1 — GUARANTEED id coincidence: re-import the mutated document\n // itself. A stale diagram-js stack whose revert removes the created\n // shape by id is a silent no-op on any other document and deletes a\n // re-imported element on this one — the tt drift as it really bites\n const mutated = m.exportText();\n await m.importText(mutated);\n const baselineSame = m.exportText();\n const idsSame = sortedIds(m);\n const { calls } = await changeEvents(h, m, () => h.undo!(m));\n if (calls > 0)\n fail(\n \"undo directly after importText fired onContentChanged — history housekeeping is not a change\",\n );\n if (m.exportText() !== baselineSame || (idsSame && !same(idsSame, sortedIds(m)))) {\n fail(\n \"undo after importText removed a re-imported element by id coincidence (stale history applied)\",\n );\n }\n // leg 2 — a DIFFERENT document replaces the doc; a snapshot-restoring\n // stale stack would resurrect the previous one\n await h.mutate(m);\n await m.importText(second);\n // baseline via the modeler's OWN serializer: this case isolates the\n // undo law — a non-canonical serializer must fail L2, not here\n const baseline = m.exportText();\n await h.undo(m);\n await h.undo(m); // drain a stale multi-entry history too\n if (m.exportText() !== baseline) {\n fail(\"undo after importText mutated the freshly imported document (stale history applied)\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L5: getViewState never throws before the first import\",\n async run(h) {\n const m = await h.create();\n try {\n let state: unknown;\n try {\n state = m.getViewState();\n } catch (e) {\n fail(`getViewState threw before the first import: ${String(e)}`);\n }\n if (state !== undefined) assertFinite(state, \"before the first import\");\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L5: view state round-trips across a re-import, an import fits the view — and none of it is document content\",\n async run(h) {\n if (h.viewState === undefined) {\n return skip(\n \"viewState is undefined — supply a panned/zoomed state, a no-op setViewState is otherwise undetectable\",\n );\n }\n const alt = h.viewState;\n const m = await h.create();\n try {\n const text = await importContentText(h, m);\n const doc = m.exportText();\n const fitted = m.getViewState();\n assertFinite(fitted, \"after the first import\");\n // the modeler's OWN readback is the oracle from here on — a\n // diagram-js viewbox comes back rounded and enriched (scale, inner,\n // outer), never byte-equal to the harness literal\n m.setViewState(alt);\n const applied = m.getViewState();\n assertFinite(applied, \"after setViewState\");\n if (same(applied, fitted)) {\n fail(\"setViewState(viewState) had no effect — supply a state that differs from the fitted view\");\n }\n await m.importText(text);\n if (!same(m.getViewState(), fitted)) {\n fail(\n \"an import must leave the view FITTED to the content (the same state the first import produced)\",\n );\n }\n // the host restores with what IT read back before the re-import —\n // the readback must be settable and yield itself again\n const { calls } = await changeEvents(h, m, () => m.setViewState(applied));\n if (calls > 0) fail(\"setViewState fired onContentChanged — view state is not document content\");\n if (m.exportText() !== doc)\n fail(\"setViewState changed the export — view state leaked into the document\");\n if (!same(m.getViewState(), applied)) {\n fail(\n \"view state did not survive re-import + restore (setting the modeler's own readback must yield that readback)\",\n );\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L5: a host's view change never commits an open interaction\",\n async run(h) {\n if (!h.openInteraction) {\n return skip(\n \"openInteraction is undefined — a host's view change over an open interaction (a label editor with typed input) was not verified\",\n );\n }\n if (h.viewState === undefined) {\n return skip(\n \"viewState is undefined — a host's view change over an open interaction was not verified (supply a panned/zoomed state)\",\n );\n }\n const openInteraction = h.openInteraction;\n const commitInteraction = h.commitInteraction;\n const viewState = h.viewState;\n const settle = h.settle ?? defaultSettle;\n const first = h.canonicalTexts[0] ?? \"\";\n const m = await h.create();\n try {\n await m.importText(first);\n const doc = exportOrFail(m, \"after importing canonicalTexts[0]\");\n const open = async (): Promise<InteractionState | undefined> => {\n await openInteraction(m);\n await settle();\n const opened = h.interactionState?.(m);\n if (h.interactionState && opened === undefined) {\n fail(\"interactionState reports no interaction after openInteraction — the case proves nothing\");\n }\n return opened;\n };\n // the document a commit writes WITHOUT a view change: proof that the\n // typed input is real, and the oracle for the commit after one\n let committed: string | undefined;\n if (commitInteraction) {\n const typed = (await open())?.text;\n await commitInteraction(m);\n await settle();\n committed = exportOrFail(m, \"after committing the interaction\");\n if (committed === doc || (typed && !committed.includes(typed))) {\n fail(\n \"committing the interaction openInteraction opened did not write its typed input into the export — the case proves nothing (commitInteraction must commit the way a user does, openInteraction type input the serializer writes verbatim)\",\n );\n }\n await importOrFail(m, first, \"re-importing canonicalTexts[0] after the commit rejected\");\n }\n const opened = await open();\n const view = m.getViewState();\n const { calls } = await changeEvents(h, m, () => m.setViewState(viewState));\n if (calls > 0) {\n fail(\n `setViewState over an open interaction fired onContentChanged ${calls}x — the host's view change committed it (L5; if every setViewState emits, see the L5 round-trip case first)`,\n );\n }\n if (exportOrFail(m, \"after setViewState over an open interaction\") !== doc) {\n fail(\n \"setViewState over an open interaction changed the export — the host's view change committed it (L5)\",\n );\n }\n if (same(m.getViewState(), view)) {\n fail(\n \"setViewState(viewState) had no effect over an open interaction — the case proves nothing (supply a state that differs from the fitted view)\",\n );\n }\n const after = h.interactionState?.(m);\n let advice: string | undefined;\n if (\n opened !== undefined &&\n (after?.elementId !== opened.elementId || after?.text !== opened.text)\n ) {\n const input = opened.text === undefined ? \"\" : ` with the input ${JSON.stringify(opened.text)}`;\n const what = after === undefined ? \"closed it\" : `left ${JSON.stringify(after)} open`;\n advice = `an interaction on '${opened.elementId}'${input} did not survive a host's view change (setViewState ${what}) — RECOMMENDED: keep it open with its typed input, so the user's commit afterwards writes what it would have written without the view change`;\n } else if (commitInteraction && committed !== undefined) {\n await commitInteraction(m);\n await settle();\n const out = exportOrFail(m, \"after committing the interaction after a view change\");\n if (out !== committed) {\n advice = `committing an interaction after a host's view change wrote another document than committing it without one — RECOMMENDED: keep it open with its typed input across a view change (if openInteraction types different input per call, this advice is the harness's):\\n--- committed without a view change\\n${committed}\\n--- committed after setViewState\\n${out}`;\n }\n }\n return {\n ...(commitInteraction\n ? {}\n : {\n skipped:\n \"commitInteraction is undefined — that the typed input changes the document (without it the case may prove nothing) and that it still commits after the view change were not checked (pass commitInteraction to check both)\",\n }),\n ...(advice === undefined ? {} : { advisories: [advice] }),\n };\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"elements (when present): stable ids across re-imports and unrelated edits, reveal and selection semantics\",\n async run(h) {\n const m = await h.create();\n try {\n const text = h.canonicalTexts[0] ?? \"\";\n await m.importText(text);\n const elements = m.elements;\n if (!elements)\n return skip(\"the modeler exposes no elements surface (optional) — nothing to verify\");\n const ids = elements.list().map((e) => e.id);\n if (new Set(ids).size !== ids.length) fail(\"element ids are not unique\");\n await m.importText(text);\n const again = elements.list().map((e) => e.id);\n if (!same([...ids].sort(), [...again].sort()))\n fail(\"element ids are not stable across re-imports of the same text\");\n if (elements.reveal(\"__modeler_api_no_such_id__\") !== false)\n fail(\"reveal(unknown id) must return false\");\n const firstId = ids[0];\n if (firstId !== undefined) {\n const deliveries: string[][] = [];\n const off = elements.onSelection((selected) => deliveries.push([...selected]));\n const { calls } = await changeEvents(h, m, () => {\n if (elements.reveal(firstId) !== true) fail(\"reveal(known id) must return true\");\n });\n if (calls > 0) fail(\"reveal fired onContentChanged — revealing is view behavior, not an edit\");\n if (!deliveries.some((d) => d.includes(firstId))) {\n fail(\n `reveal('${firstId}') delivered no selection containing the id — is it a selectable element (not a root or a label)?`,\n );\n }\n for (const d of deliveries) {\n for (const id of d) {\n if (!again.includes(id)) {\n fail(\n `onSelection reported an id that is not an element: '${id}' (label ids must resolve to their target)`,\n );\n }\n }\n }\n if (!elements.selection().includes(firstId)) fail(\"selection() does not contain the revealed id\");\n const seen = deliveries.length;\n off();\n // a DIFFERENT selection after the unsubscribe — re-revealing the\n // same id would be deduplicated by many engines and prove nothing\n const otherId = ids[1];\n if (otherId !== undefined) elements.reveal(otherId);\n else await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n if (deliveries.length !== seen) fail(\"onSelection delivered after unsubscribe\");\n }\n // an edit to ANOTHER element must not renumber existing ids: every id\n // still exists AND (where the notation has names) still names the\n // same element — a positional scheme keeps the id strings while\n // shifting what they point at\n const namesBefore = new Map(elements.list().map((e) => [e.id, e.name]));\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n const afterEdit = new Map(elements.list().map((e) => [e.id, e.name]));\n if (afterEdit.size <= namesBefore.size) {\n fail(\n \"the harness mutate must ADD an element (the elements case verifies ids under an unrelated edit)\",\n );\n }\n for (const [id, name] of namesBefore) {\n if (!afterEdit.has(id)) {\n fail(\n `element id '${id}' vanished after an unrelated edit — ids must survive edits to other elements`,\n );\n }\n if (name !== undefined && afterEdit.get(id) !== name) {\n fail(\n `element id '${id}' names a different element after an unrelated edit ('${name}' → '${String(afterEdit.get(id))}')`,\n );\n }\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"elements (when present): ids and names survive export → import, in this and in a fresh instance\",\n async run(h) {\n const m = await h.create();\n const others: CollaborativeModeler[] = [];\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n const elements = m.elements;\n if (!elements)\n return skip(\"the modeler exposes no elements surface (optional) — nothing to verify\");\n // the element the session just created is the one an in-memory id\n // scheme gives away: its id was never read back from a text\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n const session = new Map(elements.list().map((e) => [e.id, e.name]));\n const exported = exportOrFail(m, \"after a user mutation\");\n const expectSessionIds = (target: CollaborativeModeler, where: string): void => {\n const after = namesById(target);\n if (!after) fail(`${where} exposes no elements surface — the editor does`);\n for (const [id, name] of session) {\n if (!after.has(id)) {\n fail(\n `element id '${id}' is gone after export → import ${where} — the id a session reports must be the element's id in the document (write it back)`,\n );\n }\n // an empty name and an absent one are the same across the\n // serializer: the contract fixes ids, not how a parser reports\n // an element without a label\n if (name !== undefined && (after.get(id) ?? \"\") !== name) {\n fail(\n `element id '${id}' names a different element after export → import ${where} ('${name}' → '${String(after.get(id))}')`,\n );\n }\n }\n };\n await importOrFail(m, exported, \"importText rejected the modeler's own export\");\n expectSessionIds(m, \"into the same instance\");\n const fresh = await h.create();\n others.push(fresh);\n await importOrFail(fresh, exported, \"a fresh instance rejected the modeler's own export\");\n expectSessionIds(fresh, \"into a fresh instance\");\n if (h.createReadonly) {\n const viewer = await h.createReadonly();\n others.push(viewer);\n await importOrFail(viewer, exported, \"the viewer rejected the editor's export\");\n if (viewer.elements) expectSessionIds(viewer, \"into the viewer\");\n }\n } finally {\n m.destroy();\n for (const other of others) other.destroy();\n }\n },\n },\n {\n name: \"elements (when present, recommended): ids created on two peers at the same time do not collide\",\n async run(h) {\n const text = h.canonicalTexts[0] ?? \"\";\n const peers: CollaborativeModeler[] = [];\n try {\n // two peers start from the same document and each creates one\n // element — what happens when two users add an element at once\n const created: Array<Map<string, string | undefined>> = [];\n for (let count = 0; count < 2; count += 1) {\n const peer = await h.create();\n peers.push(peer);\n await peer.importText(text);\n if (!peer.elements)\n return skip(\"the modeler exposes no elements surface (optional) — nothing to verify\");\n const before = new Set(peer.elements.list().map((e) => e.id));\n await h.mutate(peer);\n await (h.settle ?? defaultSettle)();\n const added = new Map(\n peer.elements\n .list()\n .filter((e) => !before.has(e.id))\n .map((e) => [e.id, e.name]),\n );\n if (added.size === 0) {\n fail(\n \"the harness mutate must ADD an element (the elements cases compare the ids two instances allocate)\",\n );\n }\n created.push(added);\n }\n const [\n onFirst = new Map<string, string | undefined>(),\n onSecond = new Map<string, string | undefined>(),\n ] = created;\n const shared = [...onFirst.keys()].filter((id) => onSecond.has(id));\n // an id derived from the content (OWM's component_<slug>) collides\n // when both peers create the same named element — by design, and the\n // merge reconciles identical content anyway; only an id allocated\n // regardless of the content is the recommendation's concern. An id\n // that spells out its element's name counts as derived from it\n const derivedFromContent = (id: string): boolean => {\n const name = onFirst.get(id) ?? \"\";\n if (name === \"\" || name !== (onSecond.get(id) ?? \"\")) return false;\n const spelled = lettersAndDigits(name);\n return spelled !== \"\" && lettersAndDigits(id).includes(spelled);\n };\n const allocated = shared.filter((id) => !derivedFromContent(id));\n const quoted = (ids: string[]): string => ids.map((id) => `'${id}'`).join(\", \");\n if (allocated.length > 0) {\n return {\n advisories: [\n `two instances that imported the same document and each created one element, not of the same name, allocated the same id (${quoted(allocated)}) — RECOMMENDED: allocate ids for new elements that do not collide across peers (random or peer-scoped, not a per-instance counter); once both edits merge, two elements share one id`,\n ],\n };\n }\n if (shared.length > 0) {\n return skip(\n `both instances created an element of the same name and allocated the same id, one derived from that name (${quoted(shared)}) — such ids collide for identical content by design, so the check is inconclusive: let mutate's elements differ in content (a counter the harness shares across instances)`,\n );\n }\n } finally {\n for (const peer of peers) peer.destroy();\n }\n },\n },\n {\n name: \"elements.bounds (when present): a finite box in diagram coordinates for every listed id, undefined for an unknown one, live after a new element and a move\",\n async run(h) {\n const text = h.canonicalTexts[0] ?? \"\";\n const settle = h.settle ?? defaultSettle;\n const m = await h.create();\n let viewer: CollaborativeModeler | undefined;\n try {\n await m.importText(text);\n const elements = m.elements;\n if (!elements)\n return skip(\"the modeler exposes no elements surface (optional) — nothing to verify\");\n if (!hasBounds(elements))\n return skip(\"the modeler exposes no elements.bounds (optional) — nothing to verify\");\n boxesOf(elements, \"after an import\");\n if (elements.bounds(\"__modeler_api_no_such_id__\") !== undefined)\n fail(\"elements.bounds(unknown id) must return undefined\");\n // what a box cached at the last import gives away: an element the\n // session created has none, a moved one keeps its old position\n const imported = new Set(elements.list().map((e) => e.id));\n await h.mutate(m);\n await settle();\n for (const { id } of elements.list().filter((e) => !imported.has(e.id))) {\n const box = elements.bounds(id);\n if (!isFiniteBox(box)) {\n fail(\n `elements.bounds('${id}') of an element created in the session is ${describeBox(box)} — bounds must read the live canvas, not the last import`,\n );\n }\n }\n const unverified: string[] = [];\n if (h.moveElement) {\n const before = boxesOf(elements, \"before a move\");\n const movedId = await h.moveElement(m);\n await settle();\n const was = before.get(movedId);\n if (was === undefined)\n fail(`moveElement returned '${movedId}', an id elements.list() did not report before the move`);\n const box = boxesOf(elements, \"after a move\").get(movedId);\n if (box === undefined)\n fail(`'${movedId}' is gone from elements.list() after moveElement moved it`);\n if (box.x === was.x && box.y === was.y) {\n fail(\n `elements.bounds('${movedId}') did not follow a move made through the modeler's own commands (still ${describeBox(was)}) — bounds must read the live canvas, not the last import (or moveElement did not move the element)`,\n );\n }\n } else {\n unverified.push(\"moveElement is undefined — that a box follows a move was not verified\");\n }\n if (h.viewState !== undefined) {\n const atRest = boxesOf(elements, \"before a view change\");\n m.setViewState(h.viewState);\n for (const [id, box] of boxesOf(elements, \"after setViewState\")) {\n if (!same(box, atRest.get(id))) {\n fail(\n `elements.bounds('${id}') changed with the view (${describeBox(atRest.get(id))} → ${describeBox(box)}) — boxes are in diagram coordinates, which zoom and scroll leave alone`,\n );\n }\n }\n } else {\n unverified.push(\n \"viewState is undefined — that boxes are independent of the view was not verified\",\n );\n }\n if (h.createReadonly) {\n viewer = await h.createReadonly();\n await importOrFail(viewer, text, \"the viewer rejected canonicalTexts[0]\");\n if (viewer.elements && hasBounds(viewer.elements)) {\n boxesOf(viewer.elements, \"on the viewer after an import\");\n if (viewer.elements.bounds(\"__modeler_api_no_such_id__\") !== undefined)\n fail(\"the viewer's elements.bounds(unknown id) must return undefined\");\n }\n }\n if (unverified.length > 0) return skip(unverified.join(\"; \"));\n } finally {\n m.destroy();\n viewer?.destroy();\n }\n },\n },\n {\n name: \"L6: export is total after imports, user mutations and undo\",\n async run(h) {\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n for (const mutation of mutationsOf(h)) {\n await mutation.run(m);\n exportOrFail(m, `after ${mutation.name}`);\n }\n if (h.undo) {\n await h.undo(m);\n exportOrFail(m, \"after an undo\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L6: foreign content survives an import → export round-trip\",\n async run(h) {\n if (h.foreignTexts === undefined) {\n return skip(\"foreignTexts is undefined — pass [] if the notation has no foreign form\");\n }\n if (h.foreignTexts.length === 0) return;\n const m = await h.create();\n try {\n await foreignRoundTrips(h, m, \"\");\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L7: a rejected import leaves no trace\",\n async run(h) {\n if (h.invalidTexts === undefined) {\n return skip(\"invalidTexts is undefined — pass [] if the notation has no non-importable text\");\n }\n if (h.invalidTexts.length === 0) return;\n const m = await h.create();\n try {\n const text = h.canonicalTexts[0] ?? \"\";\n await m.importText(text);\n const beforeEdit = m.exportText();\n // with undo history on the stack: a rejected import must leave it\n // intact too (an engine that erases history BEFORE parsing does not)\n if (h.undo) {\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n }\n const doc = m.exportText();\n const ids = sortedIds(m);\n for (const bad of h.invalidTexts) {\n let rejected = false;\n const { calls } = await changeEvents(h, m, () =>\n m.importText(bad).then(\n () => undefined,\n () => {\n rejected = true;\n },\n ),\n );\n if (!rejected) fail(\"importText resolved for a non-importable input — it must REJECT\");\n if (calls > 0) fail(\"a rejected import fired onContentChanged\");\n if (m.exportText() !== doc)\n fail(\"a rejected import changed the document — the previous state must stay intact\");\n if (ids && !same(ids, sortedIds(m))) {\n fail(\n \"a rejected import changed the rendered elements — the canvas must keep the previous document\",\n );\n }\n }\n if (h.undo) {\n await h.undo(m);\n if (m.exportText() !== beforeEdit) {\n fail(\n \"a rejected import erased the undo history — the previous document's history must stay intact\",\n );\n }\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L7: the modeler stays usable after a rejected import\",\n async run(h) {\n if (h.invalidTexts === undefined) {\n return skip(\"invalidTexts is undefined — pass [] if the notation has no non-importable text\");\n }\n const bad = h.invalidTexts[0];\n if (bad === undefined) return;\n const m = await h.create();\n try {\n await m.importText(h.canonicalTexts[0] ?? \"\");\n await m.importText(bad).then(\n () => fail(\"importText resolved for a non-importable input — it must REJECT\"),\n () => undefined,\n );\n const next = secondText(h);\n await m\n .importText(next)\n .catch((e) => fail(`the next VALID import after a rejection did not render: ${String(e)}`));\n if (m.exportText() !== next)\n fail(\"the next valid import after a rejection did not replace the document\");\n const before = m.exportText();\n const { calls } = await changeEvents(h, m, () => h.mutate(m));\n if (calls === 0) fail(\"a user mutation after a rejected import fired no onContentChanged (wedged)\");\n if (exportOrFail(m, \"after a mutation that followed a rejected import\") === before) {\n fail(\"a user mutation after a rejected import did not apply (wedged)\");\n }\n if (h.undo) {\n await h.undo(m);\n exportOrFail(m, \"after an undo that followed a rejected import\");\n }\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"L7: every rejection of a derived probe leaves no trace\",\n async run(h) {\n const base = h.canonicalTexts[0] ?? \"\";\n const m = await h.create();\n try {\n await m.importText(base);\n // the baseline comes from the modeler's OWN serializer: this case\n // isolates the rejection path — a non-canonical serializer fails L2\n const baseline = exportOrFail(m, \"after importing canonicalTexts[0]\");\n const baseIds = sortedIds(m);\n let previous: Probe | undefined;\n const reimportBase = async (): Promise<void> => {\n if (previous === undefined) return;\n const after = `after importing ${describeProbe(previous)}`;\n await importOrFail(m, base, `the next valid import ${after} did not render`);\n const doc = exportOrFail(m, `on the next valid import ${after}`);\n if (doc !== baseline || (baseIds && !same(baseIds, sortedIds(m)))) {\n fail(`the next valid import ${after} did not replace the document (wedged)`);\n }\n };\n const mutateAndExpectApplied = async (): Promise<string> => {\n await h.mutate(m);\n await (h.settle ?? defaultSettle)();\n const doc = exportOrFail(m, \"after a user mutation\");\n if (doc === baseline) {\n fail(\n previous === undefined\n ? \"the harness mutation did not change the export — the case proves nothing\"\n : `a user mutation after importing ${describeProbe(previous)} did not apply (wedged)`,\n );\n }\n return doc;\n };\n for (const probe of probesOf(h)) {\n await reimportBase();\n // a document WITH undo history: a rejection must keep both\n const doc = await mutateAndExpectApplied();\n const ids = sortedIds(m);\n const what = `importText(${describeProbe(probe)})`;\n let rejected = false;\n const { calls } = await changeEvents(h, m, () =>\n m.importText(probe.text).then(\n () => undefined,\n () => {\n rejected = true;\n },\n ),\n );\n if (rejected) {\n if (calls > 0) fail(`a rejected ${what} fired onContentChanged ${calls}x (L7)`);\n if (exportOrFail(m, `after a rejected ${what}`) !== doc) {\n fail(`a rejected ${what} changed the document — the previous state must stay intact (L7)`);\n }\n if (ids && !same(ids, sortedIds(m))) {\n fail(\n `a rejected ${what} changed the rendered elements — the canvas must keep the previous document (L7)`,\n );\n }\n if (h.undo) {\n await h.undo(m);\n if (exportOrFail(m, `after an undo that followed a rejected ${what}`) !== baseline) {\n fail(\n `a rejected ${what} erased the undo history — the previous document's history must stay intact (L7)`,\n );\n }\n }\n } else {\n if (calls > 0)\n fail(`${what} resolved and fired onContentChanged ${calls}x — an import is silent (L1)`);\n exportOrFail(m, `after ${what} resolved (L6)`);\n }\n previous = probe;\n }\n await reimportBase();\n await mutateAndExpectApplied();\n } finally {\n m.destroy();\n }\n },\n },\n {\n name: \"viewer (when shipped): read-only, silent, canonical, fits, reveals, keeps foreign content, unshaken by a rejected import\",\n async run(h) {\n if (!h.createReadonly) return skip(\"createReadonly is undefined — the package ships no viewer mount\");\n const v = await h.createReadonly();\n try {\n if (v.meta.editable !== false)\n fail(\"createReadonly() must return a viewer (meta.editable === false)\");\n try {\n const fresh = v.getViewState();\n if (fresh !== undefined) assertFinite(fresh, \"on the viewer before the first import\");\n } catch (e) {\n fail(`the viewer's getViewState threw before the first import: ${String(e)}`);\n }\n let calls = 0;\n const off = v.onContentChanged(() => {\n calls += 1;\n });\n try {\n for (const text of h.canonicalTexts) {\n await v\n .importText(text)\n .catch((e) => fail(`the viewer rejected a canonical text: ${String(e)}`));\n if (exportOrFail(v, \"on the viewer\") !== text)\n fail(\"the viewer's export is not the canonical text\");\n }\n const doc = v.exportText();\n const ids = sortedIds(v);\n if (v.elements) {\n const firstId = v.elements.list()[0]?.id;\n if (firstId !== undefined) {\n const deliveries: string[][] = [];\n const offSel = v.elements.onSelection((s) => deliveries.push([...s]));\n if (v.elements.reveal(firstId) !== true)\n fail(\"the viewer's reveal(known id) must return true\");\n await (h.settle ?? defaultSettle)();\n if (!deliveries.some((d) => d.includes(firstId)))\n fail(\"the viewer's reveal delivered no selection containing the id\");\n if (!v.elements.selection().includes(firstId))\n fail(\"the viewer's selection() does not contain the revealed id\");\n offSel();\n }\n if (v.elements.reveal(\"__modeler_api_no_such_id__\") !== false)\n fail(\"the viewer's reveal(unknown id) must return false\");\n }\n for (const bad of h.invalidTexts ?? []) {\n await v.importText(bad).then(\n () => fail(\"the viewer resolved a non-importable input — it must REJECT\"),\n () => undefined,\n );\n if (v.exportText() !== doc) fail(\"a rejected import changed the viewer's document\");\n if (ids && !same(ids, sortedIds(v)))\n fail(\"a rejected import changed the viewer's rendered elements\");\n }\n await foreignRoundTrips(h, v, \"the viewer's \");\n if (h.viewState !== undefined) {\n const text = await importContentText(h, v);\n const fitted = v.getViewState();\n assertFinite(fitted, \"on the viewer after import\");\n v.setViewState(h.viewState);\n const applied = v.getViewState();\n if (same(applied, fitted)) fail(\"the viewer's setViewState had no effect\");\n await v.importText(text);\n if (!same(v.getViewState(), fitted))\n fail(\"the viewer's import must leave the view fitted (L5)\");\n v.setViewState(applied);\n if (!same(v.getViewState(), applied))\n fail(\"setting the viewer's own readback did not yield that readback\");\n }\n await (h.settle ?? defaultSettle)();\n if (calls > 0)\n fail(`the viewer fired onContentChanged ${calls}x — a viewer never changes content`);\n } finally {\n off();\n }\n } finally {\n v.destroy();\n v.destroy(); // must not throw — same as the editor\n }\n },\n },\n {\n name: \"destroy is safe to call twice\",\n async run(h) {\n const m = await h.create();\n m.destroy();\n m.destroy(); // must not throw\n },\n },\n ];\n}\n\n/** the L6 foreign-content round-trips, on an editor or a viewer */\nasync function foreignRoundTrips(h: ConformanceHarness, m: CollaborativeModeler, who: string): Promise<void> {\n for (const { text, mustSurvive } of h.foreignTexts ?? []) {\n if (mustSurvive.length === 0) fail(\"a foreignTexts entry must name at least one fragment in mustSurvive\");\n await m.importText(text); // warnings allowed — a throw is the violation\n const first = exportOrFail(m, `${who}after importing a document with foreign content`);\n for (const fragment of mustSurvive) {\n if (!first.includes(fragment))\n fail(`${who}foreign content was dropped on import → export: '${fragment}' is missing`);\n }\n await m.importText(first);\n const second = exportOrFail(m, `${who}after re-importing its own export of foreign content`);\n if (second !== first) fail(`${who}foreign content did not survive a second import → export round-trip`);\n }\n}\n\nexport interface RegisterOptions {\n /** how a SKIPPED case is reported — a law the kit could not (fully) verify\n * because the harness left its field undefined. Default: console.warn */\n onSkip?: (caseName: string, reason: string) => void;\n /** how an ADVISORY is reported — a RECOMMENDED behaviour the modeler does\n * not show; the case still passes. Called once per advice. Default:\n * console.warn */\n onAdvisory?: (caseName: string, advice: string) => void;\n}\n\n/** bind every case to a (name, fn) test runner — node:test, vitest, jest */\nexport function registerConformance(\n harness: ConformanceHarness,\n test: (name: string, fn: () => Promise<void>) => unknown,\n options: RegisterOptions = {},\n): void {\n const onSkip =\n options.onSkip ??\n ((name: string, reason: string) => {\n console.warn(`modeler-api conformance: SKIPPED \"${name}\" — ${reason}`);\n });\n const onAdvisory =\n options.onAdvisory ??\n ((name: string, advice: string) => {\n console.warn(`modeler-api conformance: ADVISORY \"${name}\" — ${advice}`);\n });\n for (const conformanceCase of conformanceCases()) {\n test(`conformance: ${conformanceCase.name}`, async () => {\n const outcome = await conformanceCase.run(harness);\n if (outcome?.skipped) onSkip(conformanceCase.name, outcome.skipped);\n for (const advice of outcome?.advisories ?? []) onAdvisory(conformanceCase.name, advice);\n });\n }\n}\n"],"mappings":"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","names":["opened"]}
|
package/dist/index.cjs
CHANGED
|
@@ -20,12 +20,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
MODELER_API_VERSION: () => MODELER_API_VERSION
|
|
23
|
+
MODELER_API_VERSION: () => MODELER_API_VERSION,
|
|
24
|
+
apiVersionsCompatible: () => apiVersionsCompatible
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(index_exports);
|
|
26
|
-
var MODELER_API_VERSION = "1.
|
|
27
|
+
var MODELER_API_VERSION = "1.2.0";
|
|
28
|
+
function apiVersionsCompatible(a, b) {
|
|
29
|
+
const [aMajor = "", aMinor = ""] = a.split(".");
|
|
30
|
+
const [bMajor = "", bMinor = ""] = b.split(".");
|
|
31
|
+
if (aMajor !== bMajor) return false;
|
|
32
|
+
return aMajor !== "0" || aMinor === bMinor;
|
|
33
|
+
}
|
|
27
34
|
// Annotate the CommonJS export names for ESM import in node:
|
|
28
35
|
0 && (module.exports = {
|
|
29
|
-
MODELER_API_VERSION
|
|
36
|
+
MODELER_API_VERSION,
|
|
37
|
+
apiVersionsCompatible
|
|
30
38
|
});
|
|
31
39
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @miragon/modeler-api — ONE interface every Miragon modeler implements.\n *\n * The contract encodes, as REQUIREMENTS, the behavioral drift that had to be\n * discovered per modeler against engine internals before it existed:\n * an `importDSL` that emitted change events (rewriting hand-authored files on\n * open), an import that left the undo stack on stale removed shapes, a\n * serializer that was not a byte fixpoint. A host embedding a conforming\n * modeler needs no per-engine suppression tricks — the laws below hold.\n *\n * The conformance test kit (`@miragon/modeler-api/conformance`) turns each\n * law into an executable case the modeler repo runs in its own CI.\n */\n\n/** the semver of THIS contract — a modeler declares the version it implements\n * in `meta.apiVersion`; hosts check compatibility with `apiVersionsCompatible`\n * (same major; while 0.x, the same minor too). The constant IS the\n * package version: release-please rewrites the literal below on every\n * release (release-please-config.json `extra-files`), and the kit's own\n * test pins it to package.json exactly. Modelers pin the npm package; the\n * constant is what travels in `meta`. */\nexport const MODELER_API_VERSION = \"1.0.0\"; // x-release-please-version\n\nexport interface ModelerMeta {\n /** stable notation id, e.g. \"wardley\", \"team-topology\", \"event-storming\" */\n readonly notation: string;\n /** the MODELER_API_VERSION this modeler implements — compatible when the\n * major matches and, while the contract is 0.x, the minor too (pre-1.0\n * minors may break: bump-minor-pre-major) */\n readonly apiVersion: string;\n /**\n * true on an editing mount (the package's Modeler), false on a VIEWER\n * (its NavigatedViewer/Viewer): a viewer never fires `onContentChanged`,\n * accepts no user commands, and still imports, exports, reveals and\n * reports view state exactly like the editor — hosts mount it on\n * read-only surfaces (an MCP-App widget in read-only mode) without\n * per-package knowledge or a command-stack shim.\n */\n readonly editable: boolean;\n}\n\nexport interface ImportResult {\n /** non-fatal notes from the import (unknown lines kept, migrations run) */\n warnings: string[];\n}\n\nexport interface ElementInfo {\n /**\n * stable element id — identical across re-imports of the same text AND\n * untouched by edits to OTHER elements: hosts anchor todos, presence and\n * `?element=` deep links to it, so an id that renumbers when a line is\n * inserted above (positional edge ids) breaks every anchor below it. An\n * element without an id in the text gets one the serializer WRITES BACK.\n */\n id: string;\n /** human label, when the notation has one */\n name?: string;\n}\n\n/**\n * Optional element surface — what a host needs for anchored todos, `?element=`\n * deep links and the selection half of live presence. Absent = the notation\n * has no addressable element identity. Ids are ELEMENT ids throughout: a\n * shape that only labels another element (diagram-js external labels) is\n * reported and revealed as its target, never under its own label id.\n */\nexport interface ElementsSurface {\n /** every addressable element currently on the canvas */\n list(): ElementInfo[];\n /** select + scroll the element into view; false when the id is unknown.\n * Revealing is view behavior: it delivers a selection, never a change event */\n reveal(id: string): boolean;\n /** the current selection as stable element ids — synchronous, so a host\n * can read it at the moment of a click (a deep link carrying the\n * selection) without caching every delivery */\n selection(): string[];\n /** selection changes as STABLE element ids; returns the unsubscribe */\n onSelection(cb: (ids: string[]) => void): () => void;\n}\n\n/**\n * The collaborative-modeler contract. The LAWS (verified by the conformance\n * kit) are part of the interface:\n *\n * L1 SILENT IMPORT — `importText` never fires `onContentChanged` and erases\n * the undo history without emitting — also when history exists. Hosts\n * re-import remote states on every sync; an import echo would re-export\n * the canonical serialization over half-typed peer text.\n * L2 CANONICAL FIXPOINT — `exportText` is deterministic, and for any text t:\n * with c = exportText() after importText(t), importText(c) followed by\n * exportText() returns c byte-for-byte. Canonicalization is idempotent;\n * opening a file never rewrites it.\n * L3 ONE CHANGE EVENT — every user-visible atomic change (element commands\n * AND config/axis/title/style edits that live in the document, AND\n * undo/redo) fires `onContentChanged` at least once, and nothing else\n * does: not `importText`, not undo-history housekeeping, not view or\n * selection changes. When a listener runs, `exportText()` ALREADY\n * returns the post-change serialization — hosts export inside the\n * listener; an emit-before-apply would lose the last edit.\n * L4 DEFINED UNDO ACROSS IMPORTS — undo directly after an `importText` is a\n * no-op: it must never mutate or resurrect shapes from the pre-import\n * document (id coincidence included).\n * L5 VIEW-STATE ROUND-TRIP — `getViewState`/`setViewState` survive a\n * re-import: snapshot before, restore after, viewport unchanged. An\n * import leaves the view FITTED to the content (the defined post-import\n * view, the same for the same document and host — a host that wants to\n * keep the user's viewport across a remote re-import snapshots before\n * and restores after); before the first import `getViewState()` returns\n * `undefined` or a state `setViewState` accepts, and never throws. The\n * readback may be a NORMALISED form of what was set (a diagram-js\n * viewbox comes back rounded, with scale/inner/outer) — what must hold\n * is that setting a readback yields that readback again.\n * L6 EXPORT IS TOTAL AFTER IMPORT — after a successful import (and any\n * sequence of user commands and undos), `exportText` succeeds. Foreign or\n * auxiliary content the modeler does not understand is PRESERVED through\n * import → export (it comes back in the export, canonicalised like the\n * rest), never dropped and never a reason to throw.\n * L7 REJECTED IMPORTS LEAVE NO TRACE — `importText` on non-importable text\n * REJECTS (resolve-with-warnings means \"imported, with notes\"; rejection\n * means \"nothing imported\"): afterwards the previously imported document\n * is still rendered with its elements, `exportText` still returns it, no\n * change event has fired and the undo history is untouched.\n * The modeler stays USABLE: the next valid import renders, the next user\n * command applies. Hosts re-import half-typed peer text on every sync —\n * \"blank (or wedged) until healed\" is not an acceptable failure mode.\n *\n * View state is NEVER part of the document: `setViewState` fires no change\n * events and `exportText` is independent of it.\n */\nexport interface CollaborativeModeler {\n readonly meta: ModelerMeta;\n\n /** mount the canvas into a host element (once, before the first import).\n * The host must be attached to the document with a non-zero size — a\n * detached or 0×0 element gives diagram-js engines NaN viewboxes */\n attachTo(host: Element): void;\n\n /** parse + render `text` — SILENT per L1, the view fitted to the content\n * per L5; resolves when rendered, REJECTS when nothing was imported (L7) */\n importText(text: string): Promise<ImportResult>;\n\n /** the canonical serialization of the current document (L2, L6) */\n exportText(): string;\n\n /** subscribe to user-visible model changes (L3); returns the unsubscribe */\n onContentChanged(cb: () => void): () => void;\n\n /** opaque viewport snapshot (zoom/scroll) — JSON-serializable (L5) */\n getViewState(): unknown;\n setViewState(state: unknown): void;\n\n readonly elements?: ElementsSurface;\n\n /** tear down DOM + listeners; further calls on the instance are undefined\n * behavior, but a second destroy() must not throw */\n destroy(): void;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBO,IAAM,sBAAsB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @miragon/modeler-api — ONE interface every Miragon modeler implements.\n *\n * The contract encodes, as REQUIREMENTS, the behavioral drift that had to be\n * discovered per modeler against engine internals before it existed:\n * an `importDSL` that emitted change events (rewriting hand-authored files on\n * open), an import that left the undo stack on stale removed shapes, a\n * serializer that was not a byte fixpoint. A host embedding a conforming\n * modeler needs no per-engine suppression tricks — the laws below hold.\n *\n * The conformance test kit (`@miragon/modeler-api/conformance`) turns each\n * law into an executable case the modeler repo runs in its own CI.\n */\n\n/** the semver of THIS contract — a modeler declares the version it implements\n * in `meta.apiVersion`; hosts check compatibility with `apiVersionsCompatible`\n * (same major; while 0.x, the same minor too). The constant IS the\n * package version: release-please rewrites the literal below on every\n * release (release-please-config.json `extra-files`), and the kit's own\n * test pins it to package.json exactly. Modelers pin the npm package; the\n * constant is what travels in `meta`. */\nexport const MODELER_API_VERSION = \"1.2.0\"; // x-release-please-version\n\n/**\n * apiVersion compatibility: same major — and, while the contract is 0.x,\n * the same minor too (pre-1.0 minors may break; bump-minor-pre-major).\n * Hosts call it at bind time with `meta.apiVersion` and their own\n * MODELER_API_VERSION; it lives in the package root so that check never\n * pulls the conformance kit into a host bundle (`/conformance` re-exports it)\n */\nexport function apiVersionsCompatible(a: string, b: string): boolean {\n const [aMajor = \"\", aMinor = \"\"] = a.split(\".\");\n const [bMajor = \"\", bMinor = \"\"] = b.split(\".\");\n if (aMajor !== bMajor) return false;\n return aMajor !== \"0\" || aMinor === bMinor;\n}\n\nexport interface ModelerMeta {\n /** stable notation id, e.g. \"wardley\", \"team-topology\", \"event-storming\" */\n readonly notation: string;\n /** the MODELER_API_VERSION this modeler implements — compatible when the\n * major matches and, while the contract is 0.x, the minor too (pre-1.0\n * minors may break: bump-minor-pre-major) */\n readonly apiVersion: string;\n /**\n * true on an editing mount (the package's Modeler), false on a VIEWER\n * (its NavigatedViewer/Viewer): a viewer never fires `onContentChanged`,\n * accepts no user commands, and still imports, exports, reveals and\n * reports view state exactly like the editor — hosts mount it on\n * read-only surfaces (an MCP-App widget in read-only mode) without\n * per-package knowledge or a command-stack shim.\n */\n readonly editable: boolean;\n}\n\nexport interface ImportResult {\n /** non-fatal notes from the import (unknown lines kept, migrations run) */\n warnings: string[];\n}\n\nexport interface ElementInfo {\n /**\n * stable element id — identical across re-imports of the same text AND\n * untouched by edits to OTHER elements: hosts anchor todos, presence and\n * `?element=` deep links to it, so an id that renumbers when a line is\n * inserted above (positional edge ids) breaks every anchor below it. An\n * element without an id in the text gets one the serializer WRITES BACK.\n *\n * The id a session reports IS the element's id in the document: after\n * `exportText()` → `importText()` — in this instance or in any other one\n * (a peer, the viewer, the next session) — the element carries the same\n * id. An id that lives only in memory anchors nothing: the todo or deep\n * link a host stored against it points nowhere once the text travels.\n *\n * RECOMMENDED: when two peers create different elements at the same time,\n * their ids do not collide. An id allocated regardless of the content —\n * a per-instance counter (`event_1` on both peers) — does: once both edits\n * merge into one document, two elements share one id; random or\n * peer-scoped ids do not. An id derived from the content (OWM's\n * `component_<slug>`) collides only when both users create the same\n * content, which the merge has to reconcile anyway.\n */\n id: string;\n /** human label, when the notation has one */\n name?: string;\n}\n\n/** an element's box in diagram coordinates, as `ElementsSurface.bounds` reports it */\nexport interface ElementBounds {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/**\n * Optional element surface — what a host needs for anchored todos, `?element=`\n * deep links and the selection half of live presence. Absent = the notation\n * has no addressable element identity. Ids are ELEMENT ids throughout: a\n * shape that only labels another element (diagram-js external labels) is\n * reported and revealed as its target, never under its own label id.\n */\nexport interface ElementsSurface {\n /** every addressable element currently on the canvas */\n list(): ElementInfo[];\n /** select + scroll the element into view; false when the id is unknown.\n * Revealing is view behavior: it delivers a selection, never a change event */\n reveal(id: string): boolean;\n /** the current selection as stable element ids — synchronous, so a host\n * can read it at the moment of a click (a deep link carrying the\n * selection) without caching every delivery */\n selection(): string[];\n /** selection changes as STABLE element ids; returns the unsubscribe */\n onSelection(cb: (ids: string[]) => void): () => void;\n /**\n * the element's box in DIAGRAM coordinates — the canvas's own coordinate\n * space, which zoom and scroll leave alone (a host maps it to the screen\n * with the current view, or draws in a layer that moves with the canvas) —\n * or `undefined` for an id `list()` does not report. Optional (1.2): hosts\n * feature-detect it for presence outlines and todo markers instead of\n * reaching into the engine.\n *\n * It reads the LIVE canvas: an element created, moved, resized or renamed\n * in this session has its current box at once, not the one of the last\n * import — a host outlining a peer's selection would otherwise draw it\n * where the element was, or not at all, until the next import. The box is\n * the element's own shape (a connection: the box around its waypoints); an\n * external label is not part of it.\n */\n bounds?(id: string): ElementBounds | undefined;\n}\n\n/**\n * The collaborative-modeler contract. The LAWS (verified by the conformance\n * kit) are part of the interface:\n *\n * L1 SILENT IMPORT — `importText` never fires `onContentChanged` and erases\n * the undo history without emitting — also when history exists. Hosts\n * re-import remote states on every sync; an import echo would re-export\n * the canonical serialization over half-typed peer text.\n * An interaction in progress when an import runs (an open label editor,\n * a popup with typed input) never commits during or because of the\n * import: no change event comes from it, its input lands neither in the\n * imported document nor on its fresh undo history, and nothing is\n * applied to an element the import removed. RECOMMENDED: keep the typed\n * input and reopen the interaction on the element with the same id when\n * that element is still there after the import — a remote re-import\n * should not cost the user what they were typing.\n * L2 CANONICAL FIXPOINT — `exportText` is deterministic, and for any text t:\n * with c = exportText() after importText(t), importText(c) followed by\n * exportText() returns c byte-for-byte. Canonicalization is idempotent;\n * opening a file never rewrites it.\n * L3 ONE CHANGE EVENT — every user-visible atomic change (element commands\n * AND config/axis/title/style edits that live in the document, AND\n * undo/redo) fires `onContentChanged` at least once, and nothing else\n * does: not `importText`, not undo-history housekeeping, not view or\n * selection changes. When a listener runs, `exportText()` ALREADY\n * returns the post-change serialization — hosts export inside the\n * listener; an emit-before-apply would lose the last edit.\n * L4 DEFINED UNDO ACROSS IMPORTS — undo directly after an `importText` is a\n * no-op: it must never mutate or resurrect shapes from the pre-import\n * document (id coincidence included).\n * L5 VIEW-STATE ROUND-TRIP — `getViewState`/`setViewState` survive a\n * re-import: snapshot before, restore after, viewport unchanged. An\n * import leaves the view FITTED to the content (the defined post-import\n * view, the same for the same document and host — a host that wants to\n * keep the user's viewport across a remote re-import snapshots before\n * and restores after); before the first import `getViewState()` returns\n * `undefined` or a state `setViewState` accepts, and never throws. The\n * readback may be a NORMALISED form of what was set (a diagram-js\n * viewbox comes back rounded, with scale/inner/outer) — what must hold\n * is that setting a readback yields that readback again.\n * A view change the HOST makes — `setViewState`, or a fit its chrome\n * applies to the canvas — never commits an interaction in progress (an\n * open label editor, a popup with typed input): no change event, the\n * export unchanged. Hosts restore the viewport after every remote\n * re-import and refit when their chrome changes; a commit there writes\n * the half-typed input into the shared document for every peer.\n * RECOMMENDED: the interaction stays open with its typed input, so the\n * user's own commit afterwards writes what it would have written without\n * the view change.\n * L6 EXPORT IS TOTAL AFTER IMPORT — after a successful import (and any\n * sequence of user commands and undos), `exportText` succeeds. Foreign or\n * auxiliary content the modeler does not understand is PRESERVED through\n * import → export (it comes back in the export, canonicalised like the\n * rest), never dropped and never a reason to throw.\n * L7 REJECTED IMPORTS LEAVE NO TRACE — `importText` on non-importable text\n * REJECTS (resolve-with-warnings means \"imported, with notes\"; rejection\n * means \"nothing imported\"). This holds for EVERY rejection, whatever\n * the input (a truncated or half-typed text included): afterwards the\n * previously imported document is still rendered with its elements,\n * `exportText` still returns it, no change event has fired and the undo\n * history is untouched.\n * The modeler stays USABLE: the next valid import renders, the next user\n * command applies. Hosts re-import half-typed peer text on every sync —\n * \"blank (or wedged) until healed\" is not an acceptable failure mode.\n *\n * View state is NEVER part of the document: `setViewState` fires no change\n * events and `exportText` is independent of it.\n */\nexport interface CollaborativeModeler {\n readonly meta: ModelerMeta;\n\n /** mount the canvas into a host element (once, before the first import).\n * The host must be attached to the document with a non-zero size — a\n * detached or 0×0 element gives diagram-js engines NaN viewboxes.\n * Calls made between mounting and the first import (`exportText()`, a\n * user command) return whatever the modeler defines, or throw (only\n * `getViewState` never does, L5), but they never wedge the instance:\n * the first `importText` still behaves exactly as specified */\n attachTo(host: Element): void;\n\n /** parse + render `text` — SILENT per L1, the view fitted to the content\n * per L5; resolves when rendered, REJECTS when nothing was imported (L7).\n * This holds for the first import too, whatever was called before it — a\n * host may export or route a command before its first document arrives */\n importText(text: string): Promise<ImportResult>;\n\n /** the canonical serialization of the current document (L2, L6) */\n exportText(): string;\n\n /** subscribe to user-visible model changes (L3); returns the unsubscribe */\n onContentChanged(cb: () => void): () => void;\n\n /** opaque viewport snapshot (zoom/scroll) — JSON-serializable (L5) */\n getViewState(): unknown;\n setViewState(state: unknown): void;\n\n readonly elements?: ElementsSurface;\n\n /** tear down DOM + listeners; further calls on the instance are undefined\n * behavior, but a second destroy() must not throw */\n destroy(): void;\n}\n\n/** px the host's chrome covers along the edges of the modeler's container */\nexport interface FreeAreaInsets {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\n/** a point in diagram coordinates */\nexport interface DiagramPoint {\n x: number;\n y: number;\n}\n\n/**\n * The FREE AREA: the part of the canvas no host chrome covers (Miragon\n * guide §5.9). Keyboard creation places a new element in its centre (§5.5)\n * and a renderer that constrains panning keeps the content reachable around\n * it, so both agree with a host that fits into the same rectangle. A host\n * passes ONE of two forms, each a function the renderer reads whenever it\n * needs it (the chrome changes when an inspector or a legend opens):\n *\n * - `insets`: what the chrome covers, in px from the container's edges\n * (the modeler shell's `shell.fitInsets()`); the renderer computes the\n * centre itself, the way a fit does;\n * - `center`: the centre itself, in diagram coordinates, `undefined` while\n * there is none (a 0×0 container) — the shell's `viewport.freeRectCenter()`.\n *\n * A renderer accepts both. One that needs the rectangle itself (a pan\n * constraint) and is given only `center` treats the whole container as\n * free, as it does without the option.\n */\nexport type FreeArea =\n | { insets: () => FreeAreaInsets; center?: never }\n | { center: () => DiagramPoint | undefined; insets?: never };\n\n/**\n * The options a renderer's contract factory takes under THESE names once it\n * implements contract 1.2, so a host feeds every notation the same object\n * instead of one spelling per renderer. A renderer extends the type with its\n * own options (diagram-js `additionalModules`); the contract fixes names,\n * meaning and defaults. A viewer factory takes the same type and ignores\n * what has no meaning without editing (`undoRedoKeys`).\n *\n * Each renderer adopts them in its own release, as part of its move to 1.2:\n * `meta.apiVersion` follows the pinned package and no kit case checks the\n * options, so the version alone does not tell a host they are honoured.\n * Until a renderer ships them, a host passes the renderer's own names, or\n * both. Those names then stay accepted as DEPRECATED aliases for one minor\n * release of that renderer, then go: `freeRectInsets` (Wardley, Event\n * Storming, Context Maps) is `freeArea: { insets }`, `freeRectCenter` (Team\n * Topologies, Value Chain) is `freeArea: { center }`, and Wardley's\n * `keyboard: { undoRedo }` is `undoRedoKeys`. Given both, the aligned name\n * wins.\n */\nexport interface CollaborativeModelerOptions {\n /** where the host's chrome leaves the canvas free; default: the whole container */\n freeArea?: FreeArea | undefined;\n /**\n * fit the content into the view after every import — default true, the\n * fitted view L5 defines. A host whose chrome fits the content itself\n * (the modeler shell's viewport fits into the free area) passes false:\n * the post-import view is then the host's to set, and L5's fit is the\n * host's fit. The conformance kit runs with the default\n */\n fitOnImport?: boolean | undefined;\n /**\n * bind Cmd/Ctrl+Z, Cmd/Ctrl+Shift+Z and Cmd/Ctrl+Y to the canvas history —\n * default true. A host that owns the document's history (VS Code) passes\n * false, and the keys reach it unprevented; text fields keep their own\n * undo either way\n */\n undoRedoKeys?: boolean | undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBO,IAAM,sBAAsB;AAS5B,SAAS,sBAAsB,GAAW,GAAoB;AACnE,QAAM,CAAC,SAAS,IAAI,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG;AAC9C,QAAM,CAAC,SAAS,IAAI,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG;AAC9C,MAAI,WAAW,OAAQ,QAAO;AAC9B,SAAO,WAAW,OAAO,WAAW;AACtC;","names":[]}
|