From 6e46ce6f9770c8573e766c93c6afa5a380f3eb98 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Mon, 9 Feb 2026 12:15:19 +0100 Subject: [PATCH 01/12] Improve Mermaid rendering in tabbed docs Render Mermaid only after tab activation to avoid hidden layout errors and speed first render. Add Jest coverage and a simple intro diagram example. Co-authored-by: GPT 5.2 Codex (Cursor) Co-authored-by: Cursor --- docs/syntax/diagrams.md | 107 +++++++ .../Assets/mermaid.test.ts | 119 +++++++ .../Assets/mermaid.ts | 290 +++++++++++------- 3 files changed, 401 insertions(+), 115 deletions(-) create mode 100644 src/Elastic.Documentation.Site/Assets/mermaid.test.ts diff --git a/docs/syntax/diagrams.md b/docs/syntax/diagrams.md index 7421ba9e8..6b6834620 100644 --- a/docs/syntax/diagrams.md +++ b/docs/syntax/diagrams.md @@ -2,6 +2,11 @@ Create diagrams using [Mermaid](https://mermaid.js.org/) with standard fenced code blocks. Diagrams are rendered client-side in the browser. +```mermaid +flowchart LR + Start --> End +``` + ## Basic usage Use a fenced code block with `mermaid` as the language: @@ -174,6 +179,108 @@ erDiagram ::::: +### Complex flowchart + +:::::{tab-set} + +::::{tab-item} Source +````markdown +```mermaid +graph TB + A["Painless Operators"] + + B["General"] + C["Numeric"] + D["Boolean"] + E["Reference"] + F["Array"] + + B1["Control expression flow and
value assignment"] + C1["Mathematical operations and
bit manipulation"] + D1["Boolean logic and
conditional evaluation"] + E1["Object interaction and
safe data access"] + F1["Array manipulation and
element access"] + + B2["Precedence ( )
Function Call ( )
Cast ( )
Conditional ? :
Elvis ?:
Assignment =
Compound Assignment $="] + C2["Post/Pre Increment ++
Post/Pre Decrement --
Unary +/-
Bitwise Not ~
Multiplication *
Division /
Remainder %
Addition +
Subtraction -
Shift <<, >>, >>>
Bitwise And &
Bitwise Xor ^
Bitwise Or |"] + D2["Boolean Not !
Comparison >, >=, <, <=
Instanceof instanceof
Equality ==, !=
Identity ===, !==
Boolean Xor ^
Boolean And &&
Boolean Or ||"] + E2["Method Call . ( )
Field Access .
Null Safe ?.
New Instance new ( )
String Concatenation +
List/Map Init [ ], [ : ]
List/Map Access [ ]"] + F2["Array Init [ ] { }
Array Access [ ]
Array Length .length
New Array new [ ]"] + + A --> B & C & D & E & F + B --> B1 + C --> C1 + D --> D1 + E --> E1 + F --> F1 + B1 --> B2 + C1 --> C2 + D1 --> D2 + E1 --> E2 + F1 --> F2 + + classDef rootNode fill:#0B64DD,stroke:#101C3F,stroke-width:2px,color:#fff + classDef categoryBox fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#343741 + classDef descBox fill:#48EFCF,stroke:#343741,stroke-width:2px,color:#343741 + classDef exampleBox fill:#f5f7fa,stroke:#343741,stroke-width:2px,color:#343741 + + class A rootNode + class B,C,D,E,F categoryBox + class B1,C1,D1,E1,F1 descBox + class B2,C2,D2,E2,F2 exampleBox +``` +```` +:::: + +::::{tab-item} Rendered +```mermaid +graph TB + A["Painless Operators"] + + B["General"] + C["Numeric"] + D["Boolean"] + E["Reference"] + F["Array"] + + B1["Control expression flow and
value assignment"] + C1["Mathematical operations and
bit manipulation"] + D1["Boolean logic and
conditional evaluation"] + E1["Object interaction and
safe data access"] + F1["Array manipulation and
element access"] + + B2["Precedence ( )
Function Call ( )
Cast ( )
Conditional ? :
Elvis ?:
Assignment =
Compound Assignment $="] + C2["Post/Pre Increment ++
Post/Pre Decrement --
Unary +/-
Bitwise Not ~
Multiplication *
Division /
Remainder %
Addition +
Subtraction -
Shift <<, >>, >>>
Bitwise And &
Bitwise Xor ^
Bitwise Or |"] + D2["Boolean Not !
Comparison >, >=, <, <=
Instanceof instanceof
Equality ==, !=
Identity ===, !==
Boolean Xor ^
Boolean And &&
Boolean Or ||"] + E2["Method Call . ( )
Field Access .
Null Safe ?.
New Instance new ( )
String Concatenation +
List/Map Init [ ], [ : ]
List/Map Access [ ]"] + F2["Array Init [ ] { }
Array Access [ ]
Array Length .length
New Array new [ ]"] + + A --> B & C & D & E & F + B --> B1 + C --> C1 + D --> D1 + E --> E1 + F --> F1 + B1 --> B2 + C1 --> C2 + D1 --> D2 + E1 --> E2 + F1 --> F2 + + classDef rootNode fill:#0B64DD,stroke:#101C3F,stroke-width:2px,color:#fff + classDef categoryBox fill:#e1f5fe,stroke:#01579b,stroke-width:2px,color:#343741 + classDef descBox fill:#48EFCF,stroke:#343741,stroke-width:2px,color:#343741 + classDef exampleBox fill:#f5f7fa,stroke:#343741,stroke-width:2px,color:#343741 + + class A rootNode + class B,C,D,E,F categoryBox + class B1,C1,D1,E1,F1 descBox + class B2,C2,D2,E2,F2 exampleBox +``` +:::: + +::::: + ## Interactive controls Mermaid diagrams include interactive controls that appear when you hover over the diagram: diff --git a/src/Elastic.Documentation.Site/Assets/mermaid.test.ts b/src/Elastic.Documentation.Site/Assets/mermaid.test.ts new file mode 100644 index 000000000..6db5fd9b0 --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/mermaid.test.ts @@ -0,0 +1,119 @@ +const createRect = () => ({ + width: 100, + height: 60, + top: 0, + left: 0, + bottom: 60, + right: 100, + x: 0, + y: 0, + toJSON: () => ({}), +}) + +const setVisibleRect = (element: HTMLElement) => { + Object.defineProperty(element, 'getBoundingClientRect', { + value: () => createRect(), + }) + Object.defineProperty(element, 'getClientRects', { + value: () => [createRect()], + }) +} + +class MockIntersectionObserver { + callback: IntersectionObserverCallback + observe = jest.fn() + unobserve = jest.fn() + disconnect = jest.fn() + + constructor(callback: IntersectionObserverCallback) { + this.callback = callback + } +} + +const setupMermaid = () => { + window.mermaid = { + initialize: jest.fn(), + render: jest.fn().mockResolvedValue({ svg: '' }), + } +} + +const setupScriptLoad = () => { + return jest + .spyOn(document.head, 'appendChild') + .mockImplementation((node) => { + if (node instanceof HTMLScriptElement && node.onload) { + node.onload(new Event('load')) + } + + return node + }) +} + +describe('initMermaid', () => { + beforeEach(() => { + jest.resetModules() + document.body.innerHTML = '' + global.IntersectionObserver = + MockIntersectionObserver as unknown as typeof IntersectionObserver + }) + + afterEach(() => { + jest.restoreAllMocks() + }) + + it('renders visible mermaid blocks immediately', async () => { + setupMermaid() + setupScriptLoad() + + const pre = document.createElement('pre') + pre.className = 'mermaid' + pre.textContent = 'flowchart LR\nA-->B' + setVisibleRect(pre) + document.body.appendChild(pre) + + const { initMermaid } = await import('./mermaid') + await initMermaid() + + expect(window.mermaid.render).toHaveBeenCalledTimes(1) + }) + + it('renders mermaid blocks when a tab is activated', async () => { + setupMermaid() + setupScriptLoad() + + const tabs = document.createElement('div') + tabs.className = 'tabs' + + const input = document.createElement('input') + input.className = 'tabs-input' + input.type = 'radio' + input.checked = false + + const label = document.createElement('label') + label.className = 'tabs-label' + label.textContent = 'Rendered' + + const panel = document.createElement('div') + panel.className = 'tabs-content' + + const pre = document.createElement('pre') + pre.className = 'mermaid' + pre.textContent = 'flowchart LR\nA-->B' + setVisibleRect(pre) + + panel.appendChild(pre) + tabs.append(input, label, panel) + document.body.appendChild(tabs) + + const { initMermaid } = await import('./mermaid') + await initMermaid() + + expect(window.mermaid.render).not.toHaveBeenCalled() + + input.checked = true + input.dispatchEvent(new Event('change', { bubbles: true })) + await new Promise((resolve) => setTimeout(resolve, 0)) + + expect(window.mermaid.render).toHaveBeenCalledTimes(1) + }) +}) diff --git a/src/Elastic.Documentation.Site/Assets/mermaid.ts b/src/Elastic.Documentation.Site/Assets/mermaid.ts index f2f7fa8f7..4fa6141f0 100644 --- a/src/Elastic.Documentation.Site/Assets/mermaid.ts +++ b/src/Elastic.Documentation.Site/Assets/mermaid.ts @@ -1,56 +1,23 @@ -// Beautiful Mermaid is loaded from local _static/ to avoid client-side CDN calls +// Mermaid is loaded from local _static/ to avoid client-side CDN calls // The file is copied from node_modules during build (see package.json copy:mermaid) -// Type declaration for beautiful-mermaid browser global +// Type declaration for mermaid UMD global declare global { interface Window { - __mermaid: { - renderMermaid: ( - code: string, - options?: { - bg?: string - fg?: string - font?: string - transparent?: boolean - line?: string - accent?: string - muted?: string - surface?: string - border?: string - } - ) => Promise + mermaid: { + initialize: (config: Record) => void + render: ( + id: string, + code: string + ) => Promise<{ svg: string }> } } } let mermaidLoaded = false let mermaidLoading: Promise | null = null - -// High-contrast theme configuration -// beautiful-mermaid generates CSS variables that don't resolve correctly in all contexts, -// so we resolve them to actual colors during post-processing -const colors = { - background: '#FFFFFF', - foreground: '#000000', - nodeFill: '#F5F5F5', - nodeStroke: '#000000', - line: '#000000', - innerStroke: '#333333', -} - -// Map CSS variables to resolved colors -const variableReplacements: Record = { - '--_text': colors.foreground, - '--_text-sec': colors.foreground, - '--_text-muted': colors.foreground, - '--_text-faint': colors.foreground, // "+ ", ": ", "(no attributes)" - '--_line': colors.line, - '--_arrow': colors.foreground, - '--_node-fill': colors.nodeFill, - '--_node-stroke': colors.nodeStroke, - '--_inner-stroke': colors.innerStroke, - '--bg': colors.background, -} +let mermaidDiagramIndex = 0 +let tabListenerAttached = false // Zoom configuration const ZOOM_MIN = 0.5 @@ -84,31 +51,6 @@ interface DiagramState { startY: number } -/** - * Resolve CSS variables to actual colors in the SVG output - */ -function resolveVariables(svg: string): string { - let result = svg - for (const [variable, color] of Object.entries(variableReplacements)) { - const pattern = new RegExp( - `(fill|stroke)="var\\(${variable.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\)"`, - 'g' - ) - result = result.replace(pattern, `$1="${color}"`) - } - return result -} - -/** - * Remove Google Fonts @import to avoid external network dependency - */ -function removeGoogleFonts(svg: string): string { - return svg.replace( - /@import url\('https:\/\/fonts\.googleapis\.com[^']*'\);\s*/g, - '' - ) -} - /** * Get the base path for _static/ assets by finding main.js script location */ @@ -129,8 +71,143 @@ function getStaticBasePath(): string { return '/_static/' } +async function waitForFonts(): Promise { + if (!document.fonts?.ready) { + return + } + + try { + await document.fonts.ready + } catch { + // Ignore font loading failures and continue rendering + } +} + +async function renderMermaidDiagram( + id: string, + content: string +): Promise { + const { svg } = await window.mermaid.render(id, content) + + if (!/viewBox=\"-8 -8 16 16\"/.test(svg)) { + return svg + } + + await waitForFonts() + await new Promise(requestAnimationFrame) + + const retry = await window.mermaid.render(id, content) + return retry.svg +} + +function getTabInputForElement( + element: HTMLElement +): HTMLInputElement | null { + const panel = element.closest('.tabs-content') + if (!panel) { + return null + } + + const label = panel.previousElementSibling + const input = label?.previousElementSibling + if (input instanceof HTMLInputElement && input.classList.contains('tabs-input')) { + return input + } + + return null +} + +function isElementVisible(element: HTMLElement): boolean { + const tabInput = getTabInputForElement(element) + if (tabInput && !tabInput.checked) { + return false + } + + const rect = element.getBoundingClientRect() + if (rect.width === 0 || rect.height === 0) { + return false + } + + return element.getClientRects().length > 0 +} + +function getTabPanelForInput(input: HTMLInputElement): HTMLElement | null { + const label = input.nextElementSibling + const panel = label?.nextElementSibling + if (panel instanceof HTMLElement && panel.classList.contains('tabs-content')) { + return panel + } + + return null +} + +function attachTabChangeListener(): void { + if (tabListenerAttached) { + return + } + + document.addEventListener('change', (event) => { + const target = event.target + if (!(target instanceof HTMLInputElement)) { + return + } + + if (!target.classList.contains('tabs-input') || !target.checked) { + return + } + + const panel = getTabPanelForInput(target) + if (!panel) { + return + } + + const mermaidNodes = panel.querySelectorAll( + 'pre.mermaid:not([data-mermaid-processed])' + ) + for (const node of mermaidNodes) { + void renderMermaidElement(node as HTMLElement) + } + }) + + tabListenerAttached = true +} + +async function renderMermaidElement(element: HTMLElement): Promise { + const content = element.textContent?.trim() + if (!content) { + return + } + + // Mark as processed to prevent double rendering + element.setAttribute('data-mermaid-processed', 'true') + + try { + const diagramId = `mermaid-diagram-${mermaidDiagramIndex++}` + const svg = await renderMermaidDiagram(diagramId, content) + + const container = document.createElement('div') + container.className = 'mermaid-container' + + const viewport = document.createElement('div') + viewport.className = 'mermaid-viewport' + + const rendered = document.createElement('div') + rendered.className = 'mermaid-rendered' + rendered.innerHTML = svg + + viewport.appendChild(rendered) + container.appendChild(viewport) + + setupControls(container, viewport, rendered, svg) + element.replaceWith(container) + } catch (err) { + console.warn('Mermaid rendering error for diagram:', err) + element.classList.add('mermaid-error') + } +} + /** - * Lazy-load Beautiful Mermaid from local _static/ only when diagrams exist on the page + * Lazy-load Mermaid from local _static/ only when diagrams exist on the page */ async function loadMermaid(): Promise { if (mermaidLoaded) return @@ -142,10 +219,15 @@ async function loadMermaid(): Promise { script.async = true script.onload = () => { mermaidLoaded = true + // Initialize with startOnLoad disabled since we render manually + window.mermaid.initialize({ + startOnLoad: false, + theme: 'default', + }) resolve() } script.onerror = () => - reject(new Error('Failed to load Beautiful Mermaid')) + reject(new Error('Failed to load Mermaid')) document.head.appendChild(script) }) @@ -518,52 +600,30 @@ export async function initMermaid() { } try { - // Lazy-load Beautiful Mermaid only when diagrams exist + // Lazy-load Mermaid only when diagrams exist await loadMermaid() - - // Render each diagram individually - for (let i = 0; i < mermaidElements.length; i++) { - const element = mermaidElements[i] - const content = element.textContent?.trim() - - if (!content) continue - - // Mark as processed to prevent double rendering - element.setAttribute('data-mermaid-processed', 'true') - - try { - // Render the diagram using Beautiful Mermaid - let svg = await window.__mermaid.renderMermaid(content) - - // Post-process the SVG - svg = resolveVariables(svg) - svg = removeGoogleFonts(svg) - - // Create container structure with controls - const container = document.createElement('div') - container.className = 'mermaid-container' - - const viewport = document.createElement('div') - viewport.className = 'mermaid-viewport' - - const rendered = document.createElement('div') - rendered.className = 'mermaid-rendered' - rendered.innerHTML = svg - - viewport.appendChild(rendered) - container.appendChild(viewport) - - // Set up interactive controls - setupControls(container, viewport, rendered, svg) - - // Replace the pre element with the new container - element.replaceWith(container) - } catch (err) { - console.warn('Mermaid rendering error for diagram:', err) - // Keep the original content as fallback - element.classList.add('mermaid-error') - } - } + await waitForFonts() + attachTabChangeListener() + + const observer = new IntersectionObserver((entries, instance) => { + for (const entry of entries) { + if (!entry.isIntersecting) continue + const target = entry.target as HTMLElement + instance.unobserve(target) + void renderMermaidElement(target) + } + }) + + // Render each diagram once it is visible + for (let i = 0; i < mermaidElements.length; i++) { + const element = mermaidElements[i] as HTMLElement + + if (isElementVisible(element)) { + await renderMermaidElement(element) + } else { + observer.observe(element) + } + } } catch (error) { console.warn('Mermaid initialization error:', error) } From 994a249a7e3fdaf0b26ac93256c11d322b3948ee Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Mon, 9 Feb 2026 12:16:17 +0100 Subject: [PATCH 02/12] Update Mermaid dependency Swap dependencies to use MermaidJS instead of Beautiful Mermaid. Co-authored-by: Cursor --- .../package-lock.json | 1237 ++++++++++++++++- src/Elastic.Documentation.Site/package.json | 4 +- 2 files changed, 1198 insertions(+), 43 deletions(-) diff --git a/src/Elastic.Documentation.Site/package-lock.json b/src/Elastic.Documentation.Site/package-lock.json index 914238edf..ecddc925b 100644 --- a/src/Elastic.Documentation.Site/package-lock.json +++ b/src/Elastic.Documentation.Site/package-lock.json @@ -29,7 +29,6 @@ "@r2wc/react-to-web-component": "2.1.0", "@tanstack/react-query": "^5.90.6", "@uidotdev/usehooks": "2.4.1", - "beautiful-mermaid": "^0.1.3", "dompurify": "3.3.1", "highlight.js": "11.11.1", "htmx-ext-head-support": "2.0.4", @@ -39,6 +38,7 @@ "katex": "^0.16.27", "lodash": "^4.17.21", "marked": "17.0.1", + "mermaid": "11.12.2", "select-dom": "9.5.0", "tailwindcss": "4.1.18", "tippy.js": "6.3.7", @@ -105,6 +105,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@asamuzakjp/css-color": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", @@ -155,6 +168,7 @@ "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -1919,6 +1933,63 @@ "dev": true, "license": "MIT" }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "license": "MIT" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/cst-dts-gen/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "license": "Apache-2.0" + }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", @@ -2007,6 +2078,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2030,28 +2102,11 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } }, - "node_modules/@dagrejs/dagre": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@dagrejs/dagre/-/dagre-1.1.8.tgz", - "integrity": "sha512-5SEDlndt4W/LaVzPYJW+bSmSEZc9EzTf8rJ20WCKvjS5EAZAN0b+x0Yww7VMT4R3Wootkg+X9bUfUxazYw6Blw==", - "license": "MIT", - "dependencies": { - "@dagrejs/graphlib": "2.2.4" - } - }, - "node_modules/@dagrejs/graphlib": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@dagrejs/graphlib/-/graphlib-2.2.4.tgz", - "integrity": "sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==", - "license": "MIT", - "engines": { - "node": ">17.0.0" - } - }, "node_modules/@elastic/datemath": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/@elastic/datemath/-/datemath-5.0.3.tgz", @@ -2358,6 +2413,7 @@ "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", + "peer": true, "dependencies": { "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.13.5", @@ -2380,6 +2436,7 @@ "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -3161,6 +3218,23 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "mlly": "^1.8.0" + } + }, "node_modules/@img/colour": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", @@ -4343,6 +4417,15 @@ "unist-util-is": "^3.0.0" } }, + "node_modules/@mermaid-js/parser": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz", + "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==", + "license": "MIT", + "dependencies": { + "langium": "3.3.1" + } + }, "node_modules/@microsoft/fetch-event-source": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz", @@ -4476,6 +4559,7 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=8.0.0" } @@ -6143,6 +6227,7 @@ "integrity": "sha512-b9ll4jaFYfXSv6NZAOJ2P0uuyT/Doel7ho2AHLSUz2thtcL6HEb2+qdV2f9wriVvbEoPAj9VuSOgNc0t0f5iMw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@mischnic/json-sourcemap": "^0.1.1", "@parcel/cache": "2.16.3", @@ -24019,8 +24104,7 @@ "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -24063,12 +24147,271 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, "node_modules/@types/hast": { "version": "2.3.10", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", @@ -24235,6 +24578,7 @@ "version": "18.3.23", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -24365,6 +24709,7 @@ "integrity": "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/types": "8.53.0", @@ -24893,6 +25238,7 @@ "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -25366,15 +25712,6 @@ } ] }, - "node_modules/beautiful-mermaid": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/beautiful-mermaid/-/beautiful-mermaid-0.1.3.tgz", - "integrity": "sha512-lVEHCnlVLtVRbO03T+D9kY5BZlkpvFU6F18LEu2N2VLB0eo5evG1FJWg3SvREErKY+zZ7j9f+cNsgtiOhYI2Nw==", - "license": "MIT", - "dependencies": { - "@dagrejs/dagre": "^1.1.8" - } - }, "node_modules/binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", @@ -25436,6 +25773,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -25643,6 +25981,38 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "license": "MIT", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/chevrotain/node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "license": "MIT" + }, "node_modules/chroma-js": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.6.0.tgz", @@ -25810,6 +26180,12 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "license": "MIT" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -25835,6 +26211,15 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", @@ -26002,6 +26387,516 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, + "node_modules/cytoscape": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz", + "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==", + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, "node_modules/data-urls": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", @@ -26016,6 +26911,12 @@ "node": ">=18" } }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -26071,6 +26972,15 @@ "node": ">=0.10.0" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -26140,8 +27050,7 @@ "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/dom-serializer": { "version": "2.0.0", @@ -26492,6 +27401,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -27169,6 +28079,12 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "license": "MIT" + }, "node_modules/harmony-reflect": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", @@ -27494,7 +28410,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -27651,6 +28566,15 @@ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", "integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==" }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/is-alphabetical": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", @@ -29072,6 +29996,7 @@ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -29186,6 +30111,11 @@ "json-buffer": "3.0.1" } }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -29195,6 +30125,28 @@ "node": ">=6" } }, + "node_modules/langium": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", + "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", + "license": "MIT", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "license": "MIT" + }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -29589,7 +30541,6 @@ "version": "4.17.23", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.debounce": { @@ -29652,7 +30603,6 @@ "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -29802,6 +30752,52 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "node_modules/mermaid": { + "version": "11.12.2", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.2.tgz", + "integrity": "sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==", + "license": "MIT", + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.1", + "@mermaid-js/parser": "^0.6.3", + "@types/d3": "^7.4.3", + "cytoscape": "^3.29.3", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.13", + "dayjs": "^1.11.18", + "dompurify": "^3.2.5", + "katex": "^0.16.22", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^16.2.1", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, + "node_modules/mermaid/node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/mermaid/node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -29887,6 +30883,18 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, "node_modules/module-details-from-path": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", @@ -29897,6 +30905,7 @@ "version": "2.30.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "peer": true, "engines": { "node": "*" } @@ -30260,6 +31269,12 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "license": "MIT" + }, "node_modules/parcel": { "version": "2.16.3", "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.16.3.tgz", @@ -30691,6 +31706,12 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -30756,6 +31777,12 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -30804,6 +31831,17 @@ "node": ">=8" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, "node_modules/playwright": { "version": "1.56.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", @@ -30868,6 +31906,22 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -30887,6 +31941,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -30934,6 +31989,7 @@ "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -31028,7 +32084,6 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -31043,7 +32098,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "peer": true, "engines": { "node": ">=10" }, @@ -31172,6 +32226,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -31194,6 +32249,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -31530,6 +32586,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "peer": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -31794,6 +32851,24 @@ "node": ">=8" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "license": "Unlicense" + }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, "node_modules/rrweb-cssom": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", @@ -31801,6 +32876,12 @@ "dev": true, "license": "MIT" }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -31842,7 +32923,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, "license": "MIT" }, "node_modules/sax": { @@ -32487,6 +33567,15 @@ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -32528,6 +33617,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -32671,11 +33761,19 @@ "typescript": ">=4.8.4" } }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "peer": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "node_modules/type-check": { "version": "0.4.0", @@ -32722,6 +33820,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -32804,6 +33903,12 @@ "node": "*" } }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "license": "MIT" + }, "node_modules/undici": { "version": "7.19.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.19.0.tgz", @@ -33229,6 +34334,55 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "license": "MIT" + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -33568,7 +34722,8 @@ "version": "0.15.1", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/zustand": { "version": "5.0.8", diff --git a/src/Elastic.Documentation.Site/package.json b/src/Elastic.Documentation.Site/package.json index 485816e2e..5ce83dccd 100644 --- a/src/Elastic.Documentation.Site/package.json +++ b/src/Elastic.Documentation.Site/package.json @@ -7,7 +7,7 @@ "scripts": { "watch": "parcel watch", "build": "parcel build && npm run copy:mermaid", - "copy:mermaid": "cp node_modules/beautiful-mermaid/dist/beautiful-mermaid.browser.global.js _static/mermaid.min.js", + "copy:mermaid": "cp node_modules/mermaid/dist/mermaid.min.js _static/mermaid.min.js", "test": "jest", "test:watch": "jest --watch", "test:coverage": "jest --coverage", @@ -115,7 +115,7 @@ "idb-keyval": "6.2.2", "katex": "^0.16.27", "lodash": "^4.17.21", - "beautiful-mermaid": "^0.1.3", + "mermaid": "11.12.2", "marked": "17.0.1", "select-dom": "9.5.0", "tailwindcss": "4.1.18", From 47596b5b43b9c579a4fabd5fab8d5bf4d602f699 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Mon, 9 Feb 2026 12:18:25 +0100 Subject: [PATCH 03/12] Potential fix for code scanning alert no. 53: DOM text reinterpreted as HTML Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- src/Elastic.Documentation.Site/Assets/mermaid.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Elastic.Documentation.Site/Assets/mermaid.ts b/src/Elastic.Documentation.Site/Assets/mermaid.ts index 4fa6141f0..1794d85b9 100644 --- a/src/Elastic.Documentation.Site/Assets/mermaid.ts +++ b/src/Elastic.Documentation.Site/Assets/mermaid.ts @@ -1,6 +1,8 @@ // Mermaid is loaded from local _static/ to avoid client-side CDN calls // The file is copied from node_modules during build (see package.json copy:mermaid) +import DOMPurify from 'dompurify' + // Type declaration for mermaid UMD global declare global { interface Window { @@ -83,6 +85,12 @@ async function waitForFonts(): Promise { } } +function sanitizeSvg(svg: string): string { + // Use DOMPurify to sanitize the SVG output from mermaid before inserting it into the DOM. + // Restrict to the SVG profile to avoid allowing unexpected HTML. + return DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true } }) as string +} + async function renderMermaidDiagram( id: string, content: string @@ -193,7 +201,7 @@ async function renderMermaidElement(element: HTMLElement): Promise { const rendered = document.createElement('div') rendered.className = 'mermaid-rendered' - rendered.innerHTML = svg + rendered.innerHTML = sanitizeSvg(svg) viewport.appendChild(rendered) container.appendChild(viewport) From 6d8581a7ca3dc0e958a668fab6539be319fe32eb Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Mon, 9 Feb 2026 12:22:08 +0100 Subject: [PATCH 04/12] Fix issue --- src/Elastic.Documentation.Site/Assets/mermaid.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Documentation.Site/Assets/mermaid.ts b/src/Elastic.Documentation.Site/Assets/mermaid.ts index 1794d85b9..68355ec1b 100644 --- a/src/Elastic.Documentation.Site/Assets/mermaid.ts +++ b/src/Elastic.Documentation.Site/Assets/mermaid.ts @@ -97,7 +97,7 @@ async function renderMermaidDiagram( ): Promise { const { svg } = await window.mermaid.render(id, content) - if (!/viewBox=\"-8 -8 16 16\"/.test(svg)) { + if (!/viewBox="-8 -8 16 16"/.test(svg)) { return svg } From be402347e4ee15b73f72ad1e5752740f5ff542bb Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Mon, 9 Feb 2026 12:27:32 +0100 Subject: [PATCH 05/12] Format fixes --- .../Assets/mermaid.test.ts | 172 ++++++------ .../Assets/mermaid.ts | 265 +++++++++--------- 2 files changed, 218 insertions(+), 219 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/mermaid.test.ts b/src/Elastic.Documentation.Site/Assets/mermaid.test.ts index 6db5fd9b0..1a69c21eb 100644 --- a/src/Elastic.Documentation.Site/Assets/mermaid.test.ts +++ b/src/Elastic.Documentation.Site/Assets/mermaid.test.ts @@ -1,119 +1,119 @@ const createRect = () => ({ - width: 100, - height: 60, - top: 0, - left: 0, - bottom: 60, - right: 100, - x: 0, - y: 0, - toJSON: () => ({}), + width: 100, + height: 60, + top: 0, + left: 0, + bottom: 60, + right: 100, + x: 0, + y: 0, + toJSON: () => ({}), }) const setVisibleRect = (element: HTMLElement) => { - Object.defineProperty(element, 'getBoundingClientRect', { - value: () => createRect(), - }) - Object.defineProperty(element, 'getClientRects', { - value: () => [createRect()], - }) + Object.defineProperty(element, 'getBoundingClientRect', { + value: () => createRect(), + }) + Object.defineProperty(element, 'getClientRects', { + value: () => [createRect()], + }) } class MockIntersectionObserver { - callback: IntersectionObserverCallback - observe = jest.fn() - unobserve = jest.fn() - disconnect = jest.fn() - - constructor(callback: IntersectionObserverCallback) { - this.callback = callback - } + callback: IntersectionObserverCallback + observe = jest.fn() + unobserve = jest.fn() + disconnect = jest.fn() + + constructor(callback: IntersectionObserverCallback) { + this.callback = callback + } } const setupMermaid = () => { - window.mermaid = { - initialize: jest.fn(), - render: jest.fn().mockResolvedValue({ svg: '' }), - } + window.mermaid = { + initialize: jest.fn(), + render: jest.fn().mockResolvedValue({ svg: '' }), + } } const setupScriptLoad = () => { - return jest - .spyOn(document.head, 'appendChild') - .mockImplementation((node) => { - if (node instanceof HTMLScriptElement && node.onload) { - node.onload(new Event('load')) - } - - return node - }) + return jest + .spyOn(document.head, 'appendChild') + .mockImplementation((node) => { + if (node instanceof HTMLScriptElement && node.onload) { + node.onload(new Event('load')) + } + + return node + }) } describe('initMermaid', () => { - beforeEach(() => { - jest.resetModules() - document.body.innerHTML = '' - global.IntersectionObserver = - MockIntersectionObserver as unknown as typeof IntersectionObserver - }) + beforeEach(() => { + jest.resetModules() + document.body.innerHTML = '' + global.IntersectionObserver = + MockIntersectionObserver as unknown as typeof IntersectionObserver + }) - afterEach(() => { - jest.restoreAllMocks() - }) + afterEach(() => { + jest.restoreAllMocks() + }) - it('renders visible mermaid blocks immediately', async () => { - setupMermaid() - setupScriptLoad() + it('renders visible mermaid blocks immediately', async () => { + setupMermaid() + setupScriptLoad() - const pre = document.createElement('pre') - pre.className = 'mermaid' - pre.textContent = 'flowchart LR\nA-->B' - setVisibleRect(pre) - document.body.appendChild(pre) + const pre = document.createElement('pre') + pre.className = 'mermaid' + pre.textContent = 'flowchart LR\nA-->B' + setVisibleRect(pre) + document.body.appendChild(pre) - const { initMermaid } = await import('./mermaid') - await initMermaid() + const { initMermaid } = await import('./mermaid') + await initMermaid() - expect(window.mermaid.render).toHaveBeenCalledTimes(1) - }) + expect(window.mermaid.render).toHaveBeenCalledTimes(1) + }) - it('renders mermaid blocks when a tab is activated', async () => { - setupMermaid() - setupScriptLoad() + it('renders mermaid blocks when a tab is activated', async () => { + setupMermaid() + setupScriptLoad() - const tabs = document.createElement('div') - tabs.className = 'tabs' + const tabs = document.createElement('div') + tabs.className = 'tabs' - const input = document.createElement('input') - input.className = 'tabs-input' - input.type = 'radio' - input.checked = false + const input = document.createElement('input') + input.className = 'tabs-input' + input.type = 'radio' + input.checked = false - const label = document.createElement('label') - label.className = 'tabs-label' - label.textContent = 'Rendered' + const label = document.createElement('label') + label.className = 'tabs-label' + label.textContent = 'Rendered' - const panel = document.createElement('div') - panel.className = 'tabs-content' + const panel = document.createElement('div') + panel.className = 'tabs-content' - const pre = document.createElement('pre') - pre.className = 'mermaid' - pre.textContent = 'flowchart LR\nA-->B' - setVisibleRect(pre) + const pre = document.createElement('pre') + pre.className = 'mermaid' + pre.textContent = 'flowchart LR\nA-->B' + setVisibleRect(pre) - panel.appendChild(pre) - tabs.append(input, label, panel) - document.body.appendChild(tabs) + panel.appendChild(pre) + tabs.append(input, label, panel) + document.body.appendChild(tabs) - const { initMermaid } = await import('./mermaid') - await initMermaid() + const { initMermaid } = await import('./mermaid') + await initMermaid() - expect(window.mermaid.render).not.toHaveBeenCalled() + expect(window.mermaid.render).not.toHaveBeenCalled() - input.checked = true - input.dispatchEvent(new Event('change', { bubbles: true })) - await new Promise((resolve) => setTimeout(resolve, 0)) + input.checked = true + input.dispatchEvent(new Event('change', { bubbles: true })) + await new Promise((resolve) => setTimeout(resolve, 0)) - expect(window.mermaid.render).toHaveBeenCalledTimes(1) - }) + expect(window.mermaid.render).toHaveBeenCalledTimes(1) + }) }) diff --git a/src/Elastic.Documentation.Site/Assets/mermaid.ts b/src/Elastic.Documentation.Site/Assets/mermaid.ts index 68355ec1b..47c768334 100644 --- a/src/Elastic.Documentation.Site/Assets/mermaid.ts +++ b/src/Elastic.Documentation.Site/Assets/mermaid.ts @@ -1,6 +1,5 @@ // Mermaid is loaded from local _static/ to avoid client-side CDN calls // The file is copied from node_modules during build (see package.json copy:mermaid) - import DOMPurify from 'dompurify' // Type declaration for mermaid UMD global @@ -8,10 +7,7 @@ declare global { interface Window { mermaid: { initialize: (config: Record) => void - render: ( - id: string, - code: string - ) => Promise<{ svg: string }> + render: (id: string, code: string) => Promise<{ svg: string }> } } } @@ -74,144 +70,148 @@ function getStaticBasePath(): string { } async function waitForFonts(): Promise { - if (!document.fonts?.ready) { - return - } - - try { - await document.fonts.ready - } catch { - // Ignore font loading failures and continue rendering - } + if (!document.fonts?.ready) { + return + } + + try { + await document.fonts.ready + } catch { + // Ignore font loading failures and continue rendering + } } function sanitizeSvg(svg: string): string { - // Use DOMPurify to sanitize the SVG output from mermaid before inserting it into the DOM. - // Restrict to the SVG profile to avoid allowing unexpected HTML. - return DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true } }) as string + // Use DOMPurify to sanitize the SVG output from mermaid before inserting it into the DOM. + // Restrict to the SVG profile to avoid allowing unexpected HTML. + return DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true } }) as string } async function renderMermaidDiagram( - id: string, - content: string + id: string, + content: string ): Promise { - const { svg } = await window.mermaid.render(id, content) + const { svg } = await window.mermaid.render(id, content) - if (!/viewBox="-8 -8 16 16"/.test(svg)) { - return svg - } + if (!/viewBox="-8 -8 16 16"/.test(svg)) { + return svg + } - await waitForFonts() - await new Promise(requestAnimationFrame) + await waitForFonts() + await new Promise(requestAnimationFrame) - const retry = await window.mermaid.render(id, content) - return retry.svg + const retry = await window.mermaid.render(id, content) + return retry.svg } -function getTabInputForElement( - element: HTMLElement -): HTMLInputElement | null { - const panel = element.closest('.tabs-content') - if (!panel) { - return null - } - - const label = panel.previousElementSibling - const input = label?.previousElementSibling - if (input instanceof HTMLInputElement && input.classList.contains('tabs-input')) { - return input - } - - return null +function getTabInputForElement(element: HTMLElement): HTMLInputElement | null { + const panel = element.closest('.tabs-content') + if (!panel) { + return null + } + + const label = panel.previousElementSibling + const input = label?.previousElementSibling + if ( + input instanceof HTMLInputElement && + input.classList.contains('tabs-input') + ) { + return input + } + + return null } function isElementVisible(element: HTMLElement): boolean { - const tabInput = getTabInputForElement(element) - if (tabInput && !tabInput.checked) { - return false - } + const tabInput = getTabInputForElement(element) + if (tabInput && !tabInput.checked) { + return false + } - const rect = element.getBoundingClientRect() - if (rect.width === 0 || rect.height === 0) { - return false - } + const rect = element.getBoundingClientRect() + if (rect.width === 0 || rect.height === 0) { + return false + } - return element.getClientRects().length > 0 + return element.getClientRects().length > 0 } function getTabPanelForInput(input: HTMLInputElement): HTMLElement | null { - const label = input.nextElementSibling - const panel = label?.nextElementSibling - if (panel instanceof HTMLElement && panel.classList.contains('tabs-content')) { - return panel - } + const label = input.nextElementSibling + const panel = label?.nextElementSibling + if ( + panel instanceof HTMLElement && + panel.classList.contains('tabs-content') + ) { + return panel + } - return null + return null } function attachTabChangeListener(): void { - if (tabListenerAttached) { - return - } - - document.addEventListener('change', (event) => { - const target = event.target - if (!(target instanceof HTMLInputElement)) { - return - } - - if (!target.classList.contains('tabs-input') || !target.checked) { - return - } - - const panel = getTabPanelForInput(target) - if (!panel) { - return - } - - const mermaidNodes = panel.querySelectorAll( - 'pre.mermaid:not([data-mermaid-processed])' - ) - for (const node of mermaidNodes) { - void renderMermaidElement(node as HTMLElement) - } - }) - - tabListenerAttached = true + if (tabListenerAttached) { + return + } + + document.addEventListener('change', (event) => { + const target = event.target + if (!(target instanceof HTMLInputElement)) { + return + } + + if (!target.classList.contains('tabs-input') || !target.checked) { + return + } + + const panel = getTabPanelForInput(target) + if (!panel) { + return + } + + const mermaidNodes = panel.querySelectorAll( + 'pre.mermaid:not([data-mermaid-processed])' + ) + for (const node of mermaidNodes) { + void renderMermaidElement(node as HTMLElement) + } + }) + + tabListenerAttached = true } async function renderMermaidElement(element: HTMLElement): Promise { - const content = element.textContent?.trim() - if (!content) { - return - } + const content = element.textContent?.trim() + if (!content) { + return + } - // Mark as processed to prevent double rendering - element.setAttribute('data-mermaid-processed', 'true') + // Mark as processed to prevent double rendering + element.setAttribute('data-mermaid-processed', 'true') - try { - const diagramId = `mermaid-diagram-${mermaidDiagramIndex++}` - const svg = await renderMermaidDiagram(diagramId, content) + try { + const diagramId = `mermaid-diagram-${mermaidDiagramIndex++}` + const svg = await renderMermaidDiagram(diagramId, content) - const container = document.createElement('div') - container.className = 'mermaid-container' + const container = document.createElement('div') + container.className = 'mermaid-container' - const viewport = document.createElement('div') - viewport.className = 'mermaid-viewport' + const viewport = document.createElement('div') + viewport.className = 'mermaid-viewport' - const rendered = document.createElement('div') - rendered.className = 'mermaid-rendered' - rendered.innerHTML = sanitizeSvg(svg) + const rendered = document.createElement('div') + rendered.className = 'mermaid-rendered' + rendered.innerHTML = sanitizeSvg(svg) - viewport.appendChild(rendered) - container.appendChild(viewport) + viewport.appendChild(rendered) + container.appendChild(viewport) - setupControls(container, viewport, rendered, svg) - element.replaceWith(container) - } catch (err) { - console.warn('Mermaid rendering error for diagram:', err) - element.classList.add('mermaid-error') - } + setupControls(container, viewport, rendered, svg) + element.replaceWith(container) + } catch (err) { + console.warn('Mermaid rendering error for diagram:', err) + element.classList.add('mermaid-error') + } } /** @@ -234,8 +234,7 @@ async function loadMermaid(): Promise { }) resolve() } - script.onerror = () => - reject(new Error('Failed to load Mermaid')) + script.onerror = () => reject(new Error('Failed to load Mermaid')) document.head.appendChild(script) }) @@ -610,28 +609,28 @@ export async function initMermaid() { try { // Lazy-load Mermaid only when diagrams exist await loadMermaid() - await waitForFonts() - attachTabChangeListener() - - const observer = new IntersectionObserver((entries, instance) => { - for (const entry of entries) { - if (!entry.isIntersecting) continue - const target = entry.target as HTMLElement - instance.unobserve(target) - void renderMermaidElement(target) - } - }) - - // Render each diagram once it is visible - for (let i = 0; i < mermaidElements.length; i++) { - const element = mermaidElements[i] as HTMLElement - - if (isElementVisible(element)) { - await renderMermaidElement(element) - } else { - observer.observe(element) - } - } + await waitForFonts() + attachTabChangeListener() + + const observer = new IntersectionObserver((entries, instance) => { + for (const entry of entries) { + if (!entry.isIntersecting) continue + const target = entry.target as HTMLElement + instance.unobserve(target) + void renderMermaidElement(target) + } + }) + + // Render each diagram once it is visible + for (let i = 0; i < mermaidElements.length; i++) { + const element = mermaidElements[i] as HTMLElement + + if (isElementVisible(element)) { + await renderMermaidElement(element) + } else { + observer.observe(element) + } + } } catch (error) { console.warn('Mermaid initialization error:', error) } From 4242d2bcd7823f767c2bfd1ee83a2ca61a4e7fb2 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri Benedetti Date: Mon, 9 Feb 2026 12:55:00 +0100 Subject: [PATCH 06/12] Fix Mermaid text labels stripped by DOMPurify DOMPurify's SVG profile strips elements that Mermaid uses for text labels, and combining svg+html profiles returns an empty string entirely. Replace DOMPurify with lightweight regex sanitisation that removes variants - Incomplete multi-character sanitization (#57): nested on* attrs - DOM text reinterpreted as HTML (#55): regex not robust enough Also route the fullscreen modal SVG through sanitizeSvg. Co-authored-by: Claude Opus 4 Co-authored-by: Cursor --- .../Assets/mermaid.ts | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Elastic.Documentation.Site/Assets/mermaid.ts b/src/Elastic.Documentation.Site/Assets/mermaid.ts index c545d92b3..216ca829e 100644 --- a/src/Elastic.Documentation.Site/Assets/mermaid.ts +++ b/src/Elastic.Documentation.Site/Assets/mermaid.ts @@ -84,13 +84,24 @@ function sanitizeSvg(svg: string): string { // Mermaid renders diagrams locally from author-supplied code blocks, so the SVG // is not untrusted user input. DOMPurify's SVG profile strips // elements that Mermaid uses for text labels, and combining svg+html profiles - // returns an empty string entirely. We therefore apply a lightweight sanitisation - // that removes ') + ) as Element + expect(node.querySelector('script')).toBeNull() + expect(node.querySelector('rect')).not.toBeNull() + }) + + it('removes on* event handler attributes', () => { + const node = sanitizeSvgNode( + wrapSvg('') + ) as Element + expect(node.querySelector('rect')?.hasAttribute('onclick')).toBe(false) + }) + + it('removes javascript: URLs from href', () => { + const node = sanitizeSvgNode( + wrapSvg('x') + ) as Element + expect(node.querySelector('a')?.hasAttribute('href')).toBe(false) + }) + + it('removes javascript: URLs with leading whitespace', () => { + const node = sanitizeSvgNode( + wrapSvg('x') + ) as Element + expect(node.querySelector('a')?.hasAttribute('href')).toBe(false) + }) + + it('preserves safe href values', () => { + const node = sanitizeSvgNode( + wrapSvg('x') + ) as Element + expect(node.querySelector('a')?.getAttribute('href')).toBe( + 'https://example.com' + ) + }) + + it('removes