From 38eed7c9880c6d4081155608942d6c9dfab6b783 Mon Sep 17 00:00:00 2001 From: Steph <35910788+huynhicode@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:54:46 -0700 Subject: [PATCH 001/230] product hunt promo material updates --- docs/ecosystem/mermaid-chart.md | 2 +- .../.vitepress/components/ProductHuntBadge.vue | 14 -------------- .../mermaid/src/docs/.vitepress/theme/index.ts | 3 --- .../mermaid/src/docs/ecosystem/mermaid-chart.md | 2 +- 4 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 packages/mermaid/src/docs/.vitepress/components/ProductHuntBadge.vue diff --git a/docs/ecosystem/mermaid-chart.md b/docs/ecosystem/mermaid-chart.md index 32bf3376f..1348219c1 100644 --- a/docs/ecosystem/mermaid-chart.md +++ b/docs/ecosystem/mermaid-chart.md @@ -12,7 +12,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
-Mermaid Whiteboard - Drag & Drop your Nodes with Mermaid's new Whiteboard! | Product Hunt +Mermaid Chart - A smarter way to create diagrams | Product Hunt ## About diff --git a/packages/mermaid/src/docs/.vitepress/components/ProductHuntBadge.vue b/packages/mermaid/src/docs/.vitepress/components/ProductHuntBadge.vue deleted file mode 100644 index 0f246db71..000000000 --- a/packages/mermaid/src/docs/.vitepress/components/ProductHuntBadge.vue +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/packages/mermaid/src/docs/.vitepress/theme/index.ts b/packages/mermaid/src/docs/.vitepress/theme/index.ts index 05f15ea6a..3ec200937 100644 --- a/packages/mermaid/src/docs/.vitepress/theme/index.ts +++ b/packages/mermaid/src/docs/.vitepress/theme/index.ts @@ -9,8 +9,6 @@ import Contributors from '../components/Contributors.vue'; import HomePage from '../components/HomePage.vue'; // @ts-ignore Type not available import TopBar from '../components/TopBar.vue'; -// @ts-ignore Type not available -import ProductHuntBadge from '../components/ProductHuntBadge.vue'; import { getRedirect } from './redirect.js'; // @ts-ignore Type not available import 'uno.css'; @@ -25,7 +23,6 @@ export default { return h(Theme.Layout, null, { // Keeping this as comment as it took a lot of time to figure out how to add a component to the top bar. 'home-hero-before': () => h(TopBar), - 'home-hero-info-before': () => h(ProductHuntBadge), 'home-features-after': () => h(HomePage), 'doc-before': () => h(TopBar), }); diff --git a/packages/mermaid/src/docs/ecosystem/mermaid-chart.md b/packages/mermaid/src/docs/ecosystem/mermaid-chart.md index 83695dab7..77a7020b7 100644 --- a/packages/mermaid/src/docs/ecosystem/mermaid-chart.md +++ b/packages/mermaid/src/docs/ecosystem/mermaid-chart.md @@ -6,7 +6,7 @@ Try the Ultimate AI, Mermaid, and Visual Diagramming Suite by creating an accoun
-Mermaid Whiteboard - Drag & Drop your Nodes with Mermaid's new Whiteboard! | Product Hunt +Mermaid Chart - A smarter way to create diagrams | Product Hunt ## About From 6df1fa1a0324190f987281f370f1a53aea3eabe7 Mon Sep 17 00:00:00 2001 From: Steph <35910788+huynhicode@users.noreply.github.com> Date: Mon, 21 Oct 2024 23:26:17 -0700 Subject: [PATCH 002/230] update taglines --- .../src/docs/.vitepress/components/TopBar.vue | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/mermaid/src/docs/.vitepress/components/TopBar.vue b/packages/mermaid/src/docs/.vitepress/components/TopBar.vue index 5aa515575..b36fdd2d0 100644 --- a/packages/mermaid/src/docs/.vitepress/components/TopBar.vue +++ b/packages/mermaid/src/docs/.vitepress/components/TopBar.vue @@ -7,6 +7,10 @@ interface Taglines { } const taglines: Taglines[] = [ + { + label: 'Explore the Mermaid Whiteboard from the creators of Mermaid', + url: 'https://docs.mermaidchart.com/guides/whiteboard?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=whiteboard', + }, { label: 'Use the Visual Editor in Mermaid Chart to design and build diagrams', url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=visual_editor', @@ -16,8 +20,8 @@ const taglines: Taglines[] = [ url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=teams', }, { - label: 'Skip the rough draft with Mermaid AI in Mermaid Chart', - url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=mermaid_ai', + label: 'Replace ChatGPT Pro, Mermaid.live, and LucidChart with Mermaid Pro', + url: 'https://www.mermaidchart.com/play?utm_source=mermaid_live_editor&utm_medium=banner_ad&utm_campaign=AIbundle', }, ]; @@ -25,7 +29,7 @@ let index = ref(Math.floor(Math.random() * taglines.length)); onMounted(() => { setInterval(() => { index.value = (index.value + 1) % taglines.length; - }, 60_000); + }, 40_000); }); @@ -41,7 +45,7 @@ onMounted(() => { > {{ taglines[index].label }} From d752240efce248e36c79441448619ad4defbf97e Mon Sep 17 00:00:00 2001 From: Michael Baudino Date: Mon, 28 Oct 2024 14:49:34 +0100 Subject: [PATCH 003/230] Fix a configuration example in gantt.md According to the [config schema docs](https://mermaid.js.org/config/schema-docs/config-defs-gantt-diagram-config.html#tickinterval-constraints), Gantt's `tickInterval` configuration must match the following regular expression, which does **not** allow any space: ```regexp /^([1-9][0-9]*)(millisecond|second|minute|hour|day|week|month)$/ ``` --- packages/mermaid/src/docs/syntax/gantt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mermaid/src/docs/syntax/gantt.md b/packages/mermaid/src/docs/syntax/gantt.md index 01a9f041d..eab35d09f 100644 --- a/packages/mermaid/src/docs/syntax/gantt.md +++ b/packages/mermaid/src/docs/syntax/gantt.md @@ -390,7 +390,7 @@ mermaid.ganttConfig = { sectionFontSize: 24, // Font size for sections numberSectionStyles: 1, // The number of alternating section styles axisFormat: '%d/%m', // Date/time format of the axis - tickInterval: '1 week', // Axis ticks + tickInterval: '1week', // Axis ticks topAxis: true, // When this flag is set, date labels will be added to the top of the chart displayMode: 'compact', // Turns compact mode on weekday: 'sunday', // On which day a week-based interval should start From 8cb1c681661edcd242ac7d61c900603ea32236a7 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 13:59:06 +0000 Subject: [PATCH 004/230] [autofix.ci] apply automated fixes --- docs/syntax/gantt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/syntax/gantt.md b/docs/syntax/gantt.md index cdaf0c2ac..ff6be97aa 100644 --- a/docs/syntax/gantt.md +++ b/docs/syntax/gantt.md @@ -500,7 +500,7 @@ mermaid.ganttConfig = { sectionFontSize: 24, // Font size for sections numberSectionStyles: 1, // The number of alternating section styles axisFormat: '%d/%m', // Date/time format of the axis - tickInterval: '1 week', // Axis ticks + tickInterval: '1week', // Axis ticks topAxis: true, // When this flag is set, date labels will be added to the top of the chart displayMode: 'compact', // Turns compact mode on weekday: 'sunday', // On which day a week-based interval should start From 054f9291502c3b0e966dcf4ace0b5358150ecd6c Mon Sep 17 00:00:00 2001 From: Alois Klink Date: Tue, 29 Oct 2024 21:12:11 +0900 Subject: [PATCH 005/230] refactor: remove unused param from `labelHelper()` Fixes: 7401cb8f6aec0c7dccae820824eb2bb3e6a3e12d --- .../rendering-elements/shapes/util.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts index 6a9db8c22..e69e36e38 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts @@ -10,8 +10,7 @@ import type { D3Selection, Point } from '../../../types.js'; export const labelHelper = async ( parent: D3Selection, node: Node, - _classes?: string, - _shapeSvg?: D3Selection + _classes?: string ) => { let cssClasses; const useHtmlLabels = node.useHtmlLabels || evaluate(getConfig()?.flowchart?.htmlLabels); @@ -22,12 +21,10 @@ export const labelHelper = async ( } // Add outer g element - const shapeSvg = _shapeSvg - ? _shapeSvg - : parent - .insert('g') - .attr('class', cssClasses) - .attr('id', node.domId || node.id); + const shapeSvg = parent + .insert('g') + .attr('class', cssClasses) + .attr('id', node.domId || node.id); // Create the label and insert it after the rect const labelEl = shapeSvg From f33ebfaf7a135834affbc5e114014028c82c99ef Mon Sep 17 00:00:00 2001 From: Alois Klink Date: Tue, 29 Oct 2024 21:13:10 +0900 Subject: [PATCH 006/230] refactor: fix broken generic on `insertLabel()` Fixes: 7401cb8f6aec0c7dccae820824eb2bb3e6a3e12d --- .../src/rendering-util/rendering-elements/shapes/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts index e69e36e38..d18394f68 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/util.ts @@ -116,7 +116,7 @@ export const labelHelper = async ( labelEl.insert('rect', ':first-child'); return { shapeSvg, bbox, halfPadding, label: labelEl }; }; -export const insertLabel = async ( +export const insertLabel = async ( parent: D3Selection, label: string, options: { From 9168d5d1f4e3947e0c91ed20453f460c07d41f93 Mon Sep 17 00:00:00 2001 From: Alois Klink Date: Tue, 29 Oct 2024 21:13:57 +0900 Subject: [PATCH 007/230] refactor(types): fix some type errors in kanbanItem Fixes: 7401cb8f6aec0c7dccae820824eb2bb3e6a3e12d --- .../rendering-elements/shapes/kanbanItem.ts | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts index 61dc3f85d..4dc6324a7 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts @@ -1,10 +1,10 @@ import { labelHelper, insertLabel, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import type { SVG } from '../../../diagram-api/types.js'; import type { Node, KanbanNode, ShapeRenderOptions } from '../../types.js'; import { createRoundedRectPathD } from './roundedRectPath.js'; import { userNodeOverrides, styles2String } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; +import type { D3Selection } from '../../../types.js'; const colorFromPriority = (priority: KanbanNode['priority']) => { switch (priority) { @@ -18,7 +18,11 @@ const colorFromPriority = (priority: KanbanNode['priority']) => { return 'lightblue'; } }; -export const kanbanItem = async (parent: SVG, node: Node, { config }: ShapeRenderOptions) => { +export const kanbanItem = async ( + parent: D3Selection, + node: Node, + { config }: ShapeRenderOptions +) => { const unknownNode = node as unknown; const kanbanNode = unknownNode as KanbanNode; const { labelStyles, nodeStyles } = styles2String(kanbanNode); @@ -41,7 +45,7 @@ export const kanbanItem = async (parent: SVG, node: Node, { config }: ShapeRende if (kanbanNode.ticket && config?.kanban?.ticketBaseUrl) { ticketUrl = config?.kanban?.ticketBaseUrl.replace('#TICKET#', kanbanNode.ticket); link = shapeSvg - .insert('svg:a', ':first-child') + .insert('svg:a', ':first-child') .attr('class', 'kanban-ticket-link') .attr('xlink:href', ticketUrl) .attr('target', '_blank'); @@ -56,11 +60,16 @@ export const kanbanItem = async (parent: SVG, node: Node, { config }: ShapeRende padding: kanbanNode.padding, centerLabel: false, }; - const { label: labelEl, bbox: bbox2 } = await insertLabel( - link ? link : shapeSvg, - kanbanNode.ticket || '', - options - ); + let labelEl, bbox2; + if (link) { + ({ label: labelEl, bbox: bbox2 } = await insertLabel(link, kanbanNode.ticket || '', options)); + } else { + ({ label: labelEl, bbox: bbox2 } = await insertLabel( + shapeSvg, + kanbanNode.ticket || '', + options + )); + } const { label: labelElAssigned, bbox: bboxAssigned } = await insertLabel( shapeSvg, kanbanNode.assigned || '', From dffaf9fa8ff523fa4d30b9c73c54175cfe36d423 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Tue, 29 Oct 2024 13:41:20 +0100 Subject: [PATCH 008/230] fix for kanban section shape --- packages/mermaid/src/diagrams/kanban/kanbanDb.ts | 3 ++- .../mermaid/src/rendering-util/rendering-elements/clusters.js | 2 +- .../mermaid/src/rendering-util/rendering-elements/shapes.ts | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/mermaid/src/diagrams/kanban/kanbanDb.ts b/packages/mermaid/src/diagrams/kanban/kanbanDb.ts index e6f57ef79..f27799f1e 100644 --- a/packages/mermaid/src/diagrams/kanban/kanbanDb.ts +++ b/packages/mermaid/src/diagrams/kanban/kanbanDb.ts @@ -129,7 +129,8 @@ const addNode = (level: number, id: string, descr: string, type: number, shapeDa throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`); } - if (doc?.shape) { + // if shape is defined in the yaml data, use it if it is a valid shape kanbanItem or kanbanSection + if (doc?.shape && (doc.shape === 'kanbanItem' || doc.shape === 'kanbanSection')) { node.shape = doc?.shape; } if (doc?.label) { diff --git a/packages/mermaid/src/rendering-util/rendering-elements/clusters.js b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js index 3bd9c9dc7..12ba15f39 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/clusters.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js @@ -280,7 +280,7 @@ const roundedWithTitle = async (parent, node) => { return { cluster: shapeSvg, labelBBox: bbox }; }; -const kanbanSection = async (parent, node) => { +export const kanbanSection = async (parent, node) => { log.info('Creating subgraph rect for ', node.id, node); const siteConfig = getConfig(); const { themeVariables, handDrawnSeed } = siteConfig; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts index 4f6459d85..0dd22b113 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts @@ -59,6 +59,7 @@ import { waveRectangle } from './shapes/waveRectangle.js'; import { windowPane } from './shapes/windowPane.js'; import { classBox } from './shapes/classBox.js'; import { kanbanItem } from './shapes/kanbanItem.js'; +import { kanbanSection } from './clusters.js'; type ShapeHandler = ( parent: D3Selection, @@ -478,6 +479,7 @@ const generateShapeMap = () => { iconRounded, imageSquare, kanbanItem, + kanbanSection, anchor, } as const; From ef30283fe90481b1c9d45a5bcec18362ee2377f6 Mon Sep 17 00:00:00 2001 From: Alois Klink Date: Tue, 29 Oct 2024 21:39:34 +0900 Subject: [PATCH 009/230] refactor: fix more type errors in kanbanItem Fixes: 7401cb8f6aec0c7dccae820824eb2bb3e6a3e12d --- .../rendering-elements/shapes/kanbanItem.ts | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts index 4dc6324a7..d19ada7c4 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts @@ -6,12 +6,14 @@ import { userNodeOverrides, styles2String } from './handDrawnShapeStyles.js'; import rough from 'roughjs'; import type { D3Selection } from '../../../types.js'; -const colorFromPriority = (priority: KanbanNode['priority']) => { +const colorFromPriority = (priority: NonNullable) => { switch (priority) { case 'Very High': return 'red'; case 'High': return 'orange'; + case 'Medium': + return null; // no stroke case 'Low': return 'blue'; case 'Very Low': @@ -20,11 +22,9 @@ const colorFromPriority = (priority: KanbanNode['priority']) => { }; export const kanbanItem = async ( parent: D3Selection, - node: Node, + kanbanNode: Node | Omit, { config }: ShapeRenderOptions ) => { - const unknownNode = node as unknown; - const kanbanNode = unknownNode as KanbanNode; const { labelStyles, nodeStyles } = styles2String(kanbanNode); kanbanNode.labelStyle = labelStyles; @@ -42,7 +42,7 @@ export const kanbanItem = async ( let ticketUrl = ''; let link; - if (kanbanNode.ticket && config?.kanban?.ticketBaseUrl) { + if ('ticket' in kanbanNode && kanbanNode.ticket && config?.kanban?.ticketBaseUrl) { ticketUrl = config?.kanban?.ticketBaseUrl.replace('#TICKET#', kanbanNode.ticket); link = shapeSvg .insert('svg:a', ':first-child') @@ -62,17 +62,21 @@ export const kanbanItem = async ( }; let labelEl, bbox2; if (link) { - ({ label: labelEl, bbox: bbox2 } = await insertLabel(link, kanbanNode.ticket || '', options)); + ({ label: labelEl, bbox: bbox2 } = await insertLabel( + link, + ('ticket' in kanbanNode && kanbanNode.ticket) || '', + options + )); } else { ({ label: labelEl, bbox: bbox2 } = await insertLabel( shapeSvg, - kanbanNode.ticket || '', + ('ticket' in kanbanNode && kanbanNode.ticket) || '', options )); } const { label: labelElAssigned, bbox: bboxAssigned } = await insertLabel( shapeSvg, - kanbanNode.assigned || '', + ('assigned' in kanbanNode && kanbanNode.assigned) || '', options ); kanbanNode.width = orgWidth; @@ -129,7 +133,9 @@ export const kanbanItem = async ( .attr('y', y) .attr('width', totalWidth) .attr('height', totalHeight); - if (kanbanNode.priority) { + + const priority = 'priority' in kanbanNode && kanbanNode.priority; + if (priority) { const line = shapeSvg.append('line', ':first-child'); const lineX = x + 2; @@ -142,7 +148,7 @@ export const kanbanItem = async ( .attr('y2', y2) .attr('stroke-width', '4') - .attr('stroke', colorFromPriority(kanbanNode.priority)); + .attr('stroke', colorFromPriority(priority)); } } From 0197c089169cfe28a476f73c2b216dc537bbf818 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Tue, 29 Oct 2024 14:04:57 +0100 Subject: [PATCH 010/230] revert adding kanban section to shape --- packages/mermaid/src/diagrams/kanban/kanbanDb.ts | 4 ++-- .../mermaid/src/rendering-util/rendering-elements/clusters.js | 2 +- .../mermaid/src/rendering-util/rendering-elements/shapes.ts | 2 -- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/mermaid/src/diagrams/kanban/kanbanDb.ts b/packages/mermaid/src/diagrams/kanban/kanbanDb.ts index f27799f1e..5d6fc68f9 100644 --- a/packages/mermaid/src/diagrams/kanban/kanbanDb.ts +++ b/packages/mermaid/src/diagrams/kanban/kanbanDb.ts @@ -129,8 +129,8 @@ const addNode = (level: number, id: string, descr: string, type: number, shapeDa throw new Error(`No such shape: ${doc.shape}. Shape names should be lowercase.`); } - // if shape is defined in the yaml data, use it if it is a valid shape kanbanItem or kanbanSection - if (doc?.shape && (doc.shape === 'kanbanItem' || doc.shape === 'kanbanSection')) { + // if shape is defined in the yaml data, use it if it is a valid shape kanbanItem + if (doc?.shape && doc.shape === 'kanbanItem') { node.shape = doc?.shape; } if (doc?.label) { diff --git a/packages/mermaid/src/rendering-util/rendering-elements/clusters.js b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js index 12ba15f39..3bd9c9dc7 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/clusters.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/clusters.js @@ -280,7 +280,7 @@ const roundedWithTitle = async (parent, node) => { return { cluster: shapeSvg, labelBBox: bbox }; }; -export const kanbanSection = async (parent, node) => { +const kanbanSection = async (parent, node) => { log.info('Creating subgraph rect for ', node.id, node); const siteConfig = getConfig(); const { themeVariables, handDrawnSeed } = siteConfig; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts index 0dd22b113..4f6459d85 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes.ts @@ -59,7 +59,6 @@ import { waveRectangle } from './shapes/waveRectangle.js'; import { windowPane } from './shapes/windowPane.js'; import { classBox } from './shapes/classBox.js'; import { kanbanItem } from './shapes/kanbanItem.js'; -import { kanbanSection } from './clusters.js'; type ShapeHandler = ( parent: D3Selection, @@ -479,7 +478,6 @@ const generateShapeMap = () => { iconRounded, imageSquare, kanbanItem, - kanbanSection, anchor, } as const; From 34e8946c585ac039e0c2b02947d110df1d295c25 Mon Sep 17 00:00:00 2001 From: Alois Klink Date: Tue, 29 Oct 2024 22:17:24 +0900 Subject: [PATCH 011/230] refactor(types): fix kanbanItem circular types Exclude the `node.shape` type from `kanbanItem()`, as otherwise it causes a circular dependency in the types. --- .../rendering-elements/shapes/kanbanItem.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts index d19ada7c4..34ba7873d 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/kanbanItem.ts @@ -20,11 +20,12 @@ const colorFromPriority = (priority: NonNullable) => { return 'lightblue'; } }; -export const kanbanItem = async ( +export async function kanbanItem( parent: D3Selection, - kanbanNode: Node | Omit, + // Omit the 'shape' prop since otherwise, it causes a TypeScript circular dependency error + kanbanNode: Omit | Omit, { config }: ShapeRenderOptions -) => { +) { const { labelStyles, nodeStyles } = styles2String(kanbanNode); kanbanNode.labelStyle = labelStyles; @@ -160,4 +161,4 @@ export const kanbanItem = async ( }; return shapeSvg; -}; +} From 124d2f72dd20e3441aeaa5cf1cb64947d95a3590 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Tue, 29 Oct 2024 14:51:55 +0100 Subject: [PATCH 012/230] Fix for icon borders --- packages/mermaid/src/diagrams/kanban/kanbanDb.ts | 1 + .../rendering-elements/shapes/iconCircle.ts | 6 ++++-- .../rendering-elements/shapes/iconRounded.ts | 8 +++++--- .../rendering-elements/shapes/iconSquare.ts | 9 ++++++--- packages/mermaid/src/rendering-util/types.ts | 2 ++ 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/mermaid/src/diagrams/kanban/kanbanDb.ts b/packages/mermaid/src/diagrams/kanban/kanbanDb.ts index e6f57ef79..260b1d32a 100644 --- a/packages/mermaid/src/diagrams/kanban/kanbanDb.ts +++ b/packages/mermaid/src/diagrams/kanban/kanbanDb.ts @@ -84,6 +84,7 @@ const getData = function () { shape: 'kanbanItem', level: item.level, rx: 5, + ry: 5, cssStyles: ['text-align: left'], } satisfies KanbanNode; _nodes.push(childNode); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts index 313e5c7af..785a3d219 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconCircle.ts @@ -26,16 +26,18 @@ export async function iconCircle( const topLabel = node.pos === 't'; - const { nodeBorder } = themeVariables; + const { nodeBorder, mainBkg } = themeVariables; const { stylesMap } = compileStyles(node); // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); - const options = userNodeOverrides(node, { stroke: 'transparent' }); + const options = userNodeOverrides(node, {}); if (node.look !== 'handDrawn') { options.roughness = 0; options.fillStyle = 'solid'; } + const stroke = stylesMap.get('stroke'); + options.stroke = stroke ? stylesMap.get('stroke') : mainBkg; const iconElem = shapeSvg.append('g'); if (node.icon) { diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts index ab778de71..cfb13c35c 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconRounded.ts @@ -30,7 +30,7 @@ export async function iconRounded( const height = iconSize + halfPadding * 2; const width = iconSize + halfPadding * 2; - const { nodeBorder } = themeVariables; + const { nodeBorder, mainBkg } = themeVariables; const { stylesMap } = compileStyles(node); const x = -width / 2; @@ -40,12 +40,14 @@ export async function iconRounded( // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); - const options = userNodeOverrides(node, { stroke: 'transparent' }); + const options = userNodeOverrides(node, {}); if (node.look !== 'handDrawn') { options.roughness = 0; options.fillStyle = 'solid'; } + const stroke = stylesMap.get('stroke'); + options.stroke = stroke ? stylesMap.get('stroke') : mainBkg; const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 5), options); @@ -58,7 +60,7 @@ export async function iconRounded( stroke: 'none', }); - const iconShape = shapeSvg.insert(() => iconNode, ':first-child'); + const iconShape = shapeSvg.insert(() => iconNode, ':first-child').attr('class', 'icon-shape2'); const outerShape = shapeSvg.insert(() => outerNode); if (node.icon) { diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts index 8cbccb74d..97d5da1d1 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/iconSquare.ts @@ -3,6 +3,7 @@ import { log } from '../../../logger.js'; import { getIconSVG } from '../../icons.js'; import type { Node, ShapeRenderOptions } from '../../types.js'; import intersect from '../intersect/index.js'; +import { createRoundedRectPathD } from './roundedRectPath.js'; import { compileStyles, styles2String, userNodeOverrides } from './handDrawnShapeStyles.js'; import { labelHelper, updateNodeBounds } from './util.js'; import type { D3Selection } from '../../../types.js'; @@ -29,7 +30,7 @@ export async function iconSquare( const height = iconSize + halfPadding * 2; const width = iconSize + halfPadding * 2; - const { nodeBorder } = themeVariables; + const { nodeBorder, mainBkg } = themeVariables; const { stylesMap } = compileStyles(node); const x = -width / 2; @@ -39,14 +40,16 @@ export async function iconSquare( // @ts-expect-error -- Passing a D3.Selection seems to work for some reason const rc = rough.svg(shapeSvg); - const options = userNodeOverrides(node, { stroke: 'transparent' }); + const options = userNodeOverrides(node, {}); if (node.look !== 'handDrawn') { options.roughness = 0; options.fillStyle = 'solid'; } + const stroke = stylesMap.get('stroke'); + options.stroke = stroke ? stylesMap.get('stroke') : mainBkg; - const iconNode = rc.rectangle(x, y, width, height, options); + const iconNode = rc.path(createRoundedRectPathD(x, y, width, height, 0.1), options); const outerWidth = Math.max(width, bbox.width); const outerHeight = height + bbox.height + labelPadding; diff --git a/packages/mermaid/src/rendering-util/types.ts b/packages/mermaid/src/rendering-util/types.ts index e49218f71..f8e9c31b5 100644 --- a/packages/mermaid/src/rendering-util/types.ts +++ b/packages/mermaid/src/rendering-util/types.ts @@ -161,4 +161,6 @@ export interface KanbanNode extends Node { assigned?: string; icon?: string; level: number; + rx: number; + ry: number; } From 73bbde8476ec76e3c46eac2320d04cd7e195c278 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Tue, 29 Oct 2024 16:21:56 +0100 Subject: [PATCH 013/230] Update of the background-fix --- cypress/platform/knsv2.html | 101 +++++++++++++++++- .../rendering-elements/shapes/iconCircle.ts | 4 +- .../rendering-elements/shapes/iconRounded.ts | 4 +- .../rendering-elements/shapes/iconSquare.ts | 4 +- 4 files changed, 105 insertions(+), 8 deletions(-) diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 1de071283..66fc0f2d3 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -10,6 +10,10 @@ href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css" rel="stylesheet" /> + +
+flowchart LR
+nA[Default] --> A@{ icon: 'fa:bell', form: 'rounded' }
+
+    
+
+flowchart LR
+nA[Style] --> A@{ icon: 'fa:bell', form: 'rounded' }
+style A fill:#f9f,stroke:#333,stroke-width:4px
+    
+
+flowchart LR
+nA[Class] --> A@{ icon: 'fa:bell', form: 'rounded' }
+A:::AClass
+classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
+    
+
+flowchart LR
+  nA[Class] --> A@{ icon: 'logos:aws', form: 'rounded' }
+
+    
+
+flowchart LR
+nA[Default] --> A@{ icon: 'fa:bell', form: 'square' }
+
+    
+
+flowchart LR
+nA[Style] --> A@{ icon: 'fa:bell', form: 'square' }
+style A fill:#f9f,stroke:#333,stroke-width:4px
+    
+
+flowchart LR
+nA[Class] --> A@{ icon: 'fa:bell', form: 'square' }
+A:::AClass
+classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
+    
+
+flowchart LR
+  nA[Class] --> A@{ icon: 'logos:aws', form: 'square' }
+
+    
+
+flowchart LR
+nA[Default] --> A@{ icon: 'fa:bell', form: 'circle' }
+
+    
+
+flowchart LR
+nA[Style] --> A@{ icon: 'fa:bell', form: 'circle' }
+style A fill:#f9f,stroke:#333,stroke-width:4px
+    
+
+flowchart LR
+nA[Class] --> A@{ icon: 'fa:bell', form: 'circle' }
+A:::AClass
+classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
+    
+
+flowchart LR
+  nA[Class] --> A@{ icon: 'logos:aws', form: 'circle' }
+  A:::AClass
+  classDef AClass fill:#f9f,stroke:#333,stroke-width:4px
+    
+
+flowchart LR
+  nA[Style] --> A@{ icon: 'logos:aws', form: 'circle' }
+  style A fill:#f9f,stroke:#333,stroke-width:4px
+    
 kanban
   id2[In progress]
     docs[Create Blog about the new diagram]@{ priority: 'Very Low', ticket: MC-2037, assigned: 'knsv' }
     
-
+    
 ---
 config:
   kanban:
@@ -118,6 +191,30 @@ kanban
     
 

From fdb8ae5b5357d1e2a1940d628917be99dc447c1f Mon Sep 17 00:00:00 2001
From: saurabhg772244 
Date: Mon, 27 Jan 2025 16:16:43 +0530
Subject: [PATCH 150/230] Set custom font family for legend in user diagrams.

---
 packages/mermaid/src/diagrams/user-journey/styles.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/packages/mermaid/src/diagrams/user-journey/styles.js b/packages/mermaid/src/diagrams/user-journey/styles.js
index 9cdbcd12e..391f64b87 100644
--- a/packages/mermaid/src/diagrams/user-journey/styles.js
+++ b/packages/mermaid/src/diagrams/user-journey/styles.js
@@ -13,6 +13,8 @@ const getStyles = (options) =>
 
   .legend {
     fill: ${options.textColor};
+    font-family: ${options.fontFamily};
+    font-size: ${options.textSize};
   }
 
   .label text {

From 5e9c887385466a35a74295d94f25065bcee9f14d Mon Sep 17 00:00:00 2001
From: saurabhg772244 
Date: Mon, 27 Jan 2025 16:30:35 +0530
Subject: [PATCH 151/230] Fixed unit tests.

---
 packages/mermaid/src/diagrams/user-journey/styles.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/mermaid/src/diagrams/user-journey/styles.js b/packages/mermaid/src/diagrams/user-journey/styles.js
index 391f64b87..a0528294f 100644
--- a/packages/mermaid/src/diagrams/user-journey/styles.js
+++ b/packages/mermaid/src/diagrams/user-journey/styles.js
@@ -14,7 +14,6 @@ const getStyles = (options) =>
   .legend {
     fill: ${options.textColor};
     font-family: ${options.fontFamily};
-    font-size: ${options.textSize};
   }
 
   .label text {

From 47d4d56fa61f29252e5eb2fc475ad7c531a922fa Mon Sep 17 00:00:00 2001
From: yari-dewalt 
Date: Mon, 27 Jan 2025 07:14:10 -0800
Subject: [PATCH 152/230] Remove -unified from renderer file name

---
 packages/mermaid/src/diagrams/requirement/requirementDiagram.ts | 2 +-
 .../{requirementRenderer-unified.ts => requirementRenderer.ts}  | 0
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename packages/mermaid/src/diagrams/requirement/{requirementRenderer-unified.ts => requirementRenderer.ts} (100%)

diff --git a/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts b/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts
index 5fb4c3fc7..619f5b052 100644
--- a/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts
+++ b/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts
@@ -3,7 +3,7 @@ import type { DiagramDefinition } from '../../diagram-api/types.js';
 import parser from './parser/requirementDiagram.jison';
 import db from './requirementDb.js';
 import styles from './styles.js';
-import renderer from './requirementRenderer-unified.js';
+import renderer from './requirementRenderer.js';
 
 export const diagram: DiagramDefinition = {
   parser,
diff --git a/packages/mermaid/src/diagrams/requirement/requirementRenderer-unified.ts b/packages/mermaid/src/diagrams/requirement/requirementRenderer.ts
similarity index 100%
rename from packages/mermaid/src/diagrams/requirement/requirementRenderer-unified.ts
rename to packages/mermaid/src/diagrams/requirement/requirementRenderer.ts

From 97788df7e3a30f91ae3a3251fe30188d70732a7a Mon Sep 17 00:00:00 2001
From: yari-dewalt 
Date: Mon, 27 Jan 2025 08:21:52 -0800
Subject: [PATCH 153/230] Change variable casing

---
 .../rendering-elements/shapes/requirementBox.ts  | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/requirementBox.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/requirementBox.ts
index 3e0a310e6..da28f5db2 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/requirementBox.ts
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/requirementBox.ts
@@ -19,8 +19,8 @@ export async function requirementBox(
   const requirementNode = node as unknown as Requirement;
   const elementNode = node as unknown as Element;
   const config = getConfig().requirement;
-  const PADDING = 20;
-  const GAP = 20;
+  const padding = 20;
+  const gap = 20;
   const isRequirementNode = 'verifyMethod' in node;
   const classes = getNodeClasses(node);
 
@@ -49,7 +49,7 @@ export async function requirementBox(
     accumulativeHeight,
     node.labelStyle
   );
-  accumulativeHeight += nameHeight + GAP;
+  accumulativeHeight += nameHeight + gap;
 
   // Requirement
   if (isRequirementNode) {
@@ -99,7 +99,7 @@ export async function requirementBox(
   }
 
   const totalWidth = Math.max(
-    (shapeSvg.node()?.getBBox().width ?? 200) + PADDING,
+    (shapeSvg.node()?.getBBox().width ?? 200) + padding,
     config?.rect_min_width ?? 200
   );
   const totalHeight = totalWidth;
@@ -141,22 +141,22 @@ export async function requirementBox(
     }
 
     const newTranslateY = translateY - totalHeight / 2;
-    let newTranslateX = x + PADDING / 2;
+    let newTranslateX = x + padding / 2;
 
     // Keep type and name labels centered.
     if (i === 0 || i === 1) {
       newTranslateX = translateX;
     }
     // Set the updated transform attribute
-    text.attr('transform', `translate(${newTranslateX}, ${newTranslateY + PADDING})`);
+    text.attr('transform', `translate(${newTranslateX}, ${newTranslateY + padding})`);
   });
 
   // Insert divider line
   const roughLine = rc.line(
     x,
-    y + typeHeight + nameHeight + GAP,
+    y + typeHeight + nameHeight + gap,
     x + totalWidth,
-    y + typeHeight + nameHeight + GAP,
+    y + typeHeight + nameHeight + gap,
     options
   );
   const dividerLine = shapeSvg.insert(() => roughLine);

From 9609aced140eabda0ea800afff511217ba7dacfc Mon Sep 17 00:00:00 2001
From: yari-dewalt 
Date: Mon, 27 Jan 2025 09:21:34 -0800
Subject: [PATCH 154/230] Update requirementDb to class to encapsulate data

---
 .../src/diagrams/requirement/requirementDb.ts | 580 +++++++++---------
 .../requirement/requirementDiagram.ts         |   6 +-
 2 files changed, 298 insertions(+), 288 deletions(-)

diff --git a/packages/mermaid/src/diagrams/requirement/requirementDb.ts b/packages/mermaid/src/diagrams/requirement/requirementDb.ts
index 76787fd8f..972c5e44e 100644
--- a/packages/mermaid/src/diagrams/requirement/requirementDb.ts
+++ b/packages/mermaid/src/diagrams/requirement/requirementDb.ts
@@ -1,4 +1,5 @@
 import { getConfig } from '../../diagram-api/diagramAPI.js';
+import type { DiagramDB } from '../../diagram-api/types.js';
 import { log } from '../../logger.js';
 import type { Node, Edge } from '../../rendering-util/types.js';
 
@@ -22,320 +23,327 @@ import type {
   VerifyType,
 } from './types.js';
 
-const RequirementType = {
-  REQUIREMENT: 'Requirement',
-  FUNCTIONAL_REQUIREMENT: 'Functional Requirement',
-  INTERFACE_REQUIREMENT: 'Interface Requirement',
-  PERFORMANCE_REQUIREMENT: 'Performance Requirement',
-  PHYSICAL_REQUIREMENT: 'Physical Requirement',
-  DESIGN_CONSTRAINT: 'Design Constraint',
-};
+export class RequirementDB implements DiagramDB {
+  private relations: Relation[] = [];
+  private latestRequirement: Requirement = this.getInitialRequirement();
+  private requirements = new Map();
+  private latestElement: Element = this.getInitialElement();
+  private elements = new Map();
+  private classes = new Map();
+  private direction = 'TB';
 
-const RiskLevel = {
-  LOW_RISK: 'Low',
-  MED_RISK: 'Medium',
-  HIGH_RISK: 'High',
-};
+  private RequirementType = {
+    REQUIREMENT: 'Requirement',
+    FUNCTIONAL_REQUIREMENT: 'Functional Requirement',
+    INTERFACE_REQUIREMENT: 'Interface Requirement',
+    PERFORMANCE_REQUIREMENT: 'Performance Requirement',
+    PHYSICAL_REQUIREMENT: 'Physical Requirement',
+    DESIGN_CONSTRAINT: 'Design Constraint',
+  };
 
-const VerifyType = {
-  VERIFY_ANALYSIS: 'Analysis',
-  VERIFY_DEMONSTRATION: 'Demonstration',
-  VERIFY_INSPECTION: 'Inspection',
-  VERIFY_TEST: 'Test',
-};
+  private RiskLevel = {
+    LOW_RISK: 'Low',
+    MED_RISK: 'Medium',
+    HIGH_RISK: 'High',
+  };
 
-const Relationships = {
-  CONTAINS: 'contains',
-  COPIES: 'copies',
-  DERIVES: 'derives',
-  SATISFIES: 'satisfies',
-  VERIFIES: 'verifies',
-  REFINES: 'refines',
-  TRACES: 'traces',
-};
+  private VerifyType = {
+    VERIFY_ANALYSIS: 'Analysis',
+    VERIFY_DEMONSTRATION: 'Demonstration',
+    VERIFY_INSPECTION: 'Inspection',
+    VERIFY_TEST: 'Test',
+  };
 
-let direction = 'TB';
-const getDirection = () => direction;
-const setDirection = (dir: string) => {
-  direction = dir;
-};
+  private Relationships = {
+    CONTAINS: 'contains',
+    COPIES: 'copies',
+    DERIVES: 'derives',
+    SATISFIES: 'satisfies',
+    VERIFIES: 'verifies',
+    REFINES: 'refines',
+    TRACES: 'traces',
+  };
 
-const getInitialRequirement = (): Requirement => ({
-  requirementId: '',
-  text: '',
-  risk: '' as RiskLevel,
-  verifyMethod: '' as VerifyType,
-  name: '',
-  type: '' as RequirementType,
-  cssStyles: [],
-  classes: ['default'],
-});
+  constructor() {
+    this.clear();
 
-const getInitialElement = (): Element => ({
-  name: '',
-  type: '',
-  docRef: '',
-  cssStyles: [],
-  classes: ['default'],
-});
+    // Needed for JISON since it only supports direct properties
+    this.setDirection = this.setDirection.bind(this);
+    this.addRequirement = this.addRequirement.bind(this);
+    this.setNewReqId = this.setNewReqId.bind(this);
+    this.setNewReqRisk = this.setNewReqRisk.bind(this);
+    this.setNewReqText = this.setNewReqText.bind(this);
+    this.setNewReqVerifyMethod = this.setNewReqVerifyMethod.bind(this);
+    this.addElement = this.addElement.bind(this);
+    this.setNewElementType = this.setNewElementType.bind(this);
+    this.setNewElementDocRef = this.setNewElementDocRef.bind(this);
+    this.addRelationship = this.addRelationship.bind(this);
+    this.setCssStyle = this.setCssStyle.bind(this);
+    this.setClass = this.setClass.bind(this);
+    this.defineClass = this.defineClass.bind(this);
+    this.setAccTitle = this.setAccTitle.bind(this);
+    this.setAccDescription = this.setAccDescription.bind(this);
+  }
 
-// Update initial declarations
-let relations: Relation[] = [];
-let latestRequirement: Requirement = getInitialRequirement();
-let requirements = new Map();
-let latestElement: Element = getInitialElement();
-let elements = new Map();
-let classes = new Map();
+  public getDirection() {
+    return this.direction;
+  }
+  public setDirection(dir: string) {
+    this.direction = dir;
+  }
 
-// Add reset functions
-const resetLatestRequirement = () => {
-  latestRequirement = getInitialRequirement();
-};
+  private resetLatestRequirement() {
+    this.latestRequirement = this.getInitialRequirement();
+  }
 
-const resetLatestElement = () => {
-  latestElement = getInitialElement();
-};
+  private resetLatestElement() {
+    this.latestElement = this.getInitialElement();
+  }
 
-const addRequirement = (name: string, type: RequirementType) => {
-  if (!requirements.has(name)) {
-    requirements.set(name, {
-      name,
-      type,
-      requirementId: latestRequirement.requirementId,
-      text: latestRequirement.text,
-      risk: latestRequirement.risk,
-      verifyMethod: latestRequirement.verifyMethod,
+  private getInitialRequirement(): Requirement {
+    return {
+      requirementId: '',
+      text: '',
+      risk: '' as RiskLevel,
+      verifyMethod: '' as VerifyType,
+      name: '',
+      type: '' as RequirementType,
       cssStyles: [],
       classes: ['default'],
-    });
+    };
   }
-  resetLatestRequirement();
 
-  return requirements.get(name);
-};
-
-const getRequirements = () => requirements;
-
-const setNewReqId = (id: string) => {
-  if (latestRequirement !== undefined) {
-    latestRequirement.requirementId = id;
-  }
-};
-
-const setNewReqText = (text: string) => {
-  if (latestRequirement !== undefined) {
-    latestRequirement.text = text;
-  }
-};
-
-const setNewReqRisk = (risk: RiskLevel) => {
-  if (latestRequirement !== undefined) {
-    latestRequirement.risk = risk;
-  }
-};
-
-const setNewReqVerifyMethod = (verifyMethod: VerifyType) => {
-  if (latestRequirement !== undefined) {
-    latestRequirement.verifyMethod = verifyMethod;
-  }
-};
-
-const addElement = (name: string) => {
-  if (!elements.has(name)) {
-    elements.set(name, {
-      name,
-      type: latestElement.type,
-      docRef: latestElement.docRef,
+  private getInitialElement(): Element {
+    return {
+      name: '',
+      type: '',
+      docRef: '',
       cssStyles: [],
       classes: ['default'],
-    });
-    log.info('Added new element: ', name);
+    };
   }
-  resetLatestElement();
 
-  return elements.get(name);
-};
-
-const getElements = () => elements;
-
-const setNewElementType = (type: string) => {
-  if (latestElement !== undefined) {
-    latestElement.type = type;
-  }
-};
-
-const setNewElementDocRef = (docRef: string) => {
-  if (latestElement !== undefined) {
-    latestElement.docRef = docRef;
-  }
-};
-
-const addRelationship = (type: RelationshipType, src: string, dst: string) => {
-  relations.push({
-    type,
-    src,
-    dst,
-  });
-};
-
-const getRelationships = () => relations;
-
-const clear = () => {
-  relations = [];
-  resetLatestRequirement();
-  requirements = new Map();
-  resetLatestElement();
-  elements = new Map();
-  classes = new Map();
-  commonClear();
-};
-
-export const setCssStyle = function (ids: string[], styles: string[]) {
-  for (const id of ids) {
-    const node = requirements.get(id) ?? elements.get(id);
-    if (!styles || !node) {
-      return;
-    }
-    for (const s of styles) {
-      if (s.includes(',')) {
-        node.cssStyles.push(...s.split(','));
-      } else {
-        node.cssStyles.push(s);
-      }
-    }
-  }
-};
-
-export const setClass = function (ids: string[], classNames: string[]) {
-  for (const id of ids) {
-    const node = requirements.get(id) ?? elements.get(id);
-    if (node) {
-      for (const _class of classNames) {
-        node.classes.push(_class);
-        const styles = classes.get(_class)?.styles;
-        if (styles) {
-          node.cssStyles.push(...styles);
-        }
-      }
-    }
-  }
-};
-
-export const defineClass = function (ids: string[], style: string[]) {
-  for (const id of ids) {
-    let styleClass = classes.get(id);
-    if (styleClass === undefined) {
-      styleClass = { id, styles: [], textStyles: [] };
-      classes.set(id, styleClass);
-    }
-
-    if (style) {
-      style.forEach(function (s) {
-        if (/color/.exec(s)) {
-          const newStyle = s.replace('fill', 'bgFill'); // .replace('color', 'fill');
-          styleClass.textStyles.push(newStyle);
-        }
-        styleClass.styles.push(s);
+  public addRequirement(name: string, type: RequirementType) {
+    if (!this.requirements.has(name)) {
+      this.requirements.set(name, {
+        name,
+        type,
+        requirementId: this.latestRequirement.requirementId,
+        text: this.latestRequirement.text,
+        risk: this.latestRequirement.risk,
+        verifyMethod: this.latestRequirement.verifyMethod,
+        cssStyles: [],
+        classes: ['default'],
       });
     }
+    this.resetLatestRequirement();
 
-    requirements.forEach((value) => {
-      if (value.classes.includes(id)) {
-        value.cssStyles.push(...style.flatMap((s) => s.split(',')));
-      }
-    });
-    elements.forEach((value) => {
-      if (value.classes.includes(id)) {
-        value.cssStyles.push(...style.flatMap((s) => s.split(',')));
-      }
+    return this.requirements.get(name);
+  }
+
+  public getRequirements() {
+    return this.requirements;
+  }
+
+  public setNewReqId(id: string) {
+    if (this.latestRequirement !== undefined) {
+      this.latestRequirement.requirementId = id;
+    }
+  }
+
+  public setNewReqText(text: string) {
+    if (this.latestRequirement !== undefined) {
+      this.latestRequirement.text = text;
+    }
+  }
+
+  public setNewReqRisk(risk: RiskLevel) {
+    if (this.latestRequirement !== undefined) {
+      this.latestRequirement.risk = risk;
+    }
+  }
+
+  public setNewReqVerifyMethod(verifyMethod: VerifyType) {
+    if (this.latestRequirement !== undefined) {
+      this.latestRequirement.verifyMethod = verifyMethod;
+    }
+  }
+
+  public addElement(name: string) {
+    if (!this.elements.has(name)) {
+      this.elements.set(name, {
+        name,
+        type: this.latestElement.type,
+        docRef: this.latestElement.docRef,
+        cssStyles: [],
+        classes: ['default'],
+      });
+      log.info('Added new element: ', name);
+    }
+    this.resetLatestElement();
+
+    return this.elements.get(name);
+  }
+
+  public getElements() {
+    return this.elements;
+  }
+
+  public setNewElementType(type: string) {
+    if (this.latestElement !== undefined) {
+      this.latestElement.type = type;
+    }
+  }
+
+  public setNewElementDocRef(docRef: string) {
+    if (this.latestElement !== undefined) {
+      this.latestElement.docRef = docRef;
+    }
+  }
+
+  public addRelationship(type: RelationshipType, src: string, dst: string) {
+    this.relations.push({
+      type,
+      src,
+      dst,
     });
   }
-};
 
-export const getClasses = () => {
-  return classes;
-};
-
-const getData = () => {
-  const config = getConfig();
-  const nodes: Node[] = [];
-  const edges: Edge[] = [];
-  for (const requirement of requirements.values()) {
-    const node = requirement as unknown as Node;
-    node.id = requirement.name;
-    node.cssStyles = requirement.cssStyles;
-    node.cssClasses = requirement.classes.join(' ');
-    node.shape = 'requirementBox';
-    node.look = config.look;
-    nodes.push(node);
+  public getRelationships() {
+    return this.relations;
   }
 
-  for (const element of elements.values()) {
-    const node = element as unknown as Node;
-    node.shape = 'requirementBox';
-    node.look = config.look;
-    node.id = element.name;
-    node.cssStyles = element.cssStyles;
-    node.cssClasses = element.classes.join(' ');
-
-    nodes.push(node);
+  public clear() {
+    this.relations = [];
+    this.resetLatestRequirement();
+    this.requirements = new Map();
+    this.resetLatestElement();
+    this.elements = new Map();
+    this.classes = new Map();
+    commonClear();
   }
 
-  for (const relation of relations) {
-    let counter = 0;
-    const isContains = relation.type === Relationships.CONTAINS;
-    const edge: Edge = {
-      id: `${relation.src}-${relation.dst}-${counter}`,
-      start: requirements.get(relation.src)?.name ?? elements.get(relation.src)?.name,
-      end: requirements.get(relation.dst)?.name ?? elements.get(relation.dst)?.name,
-      label: `<<${relation.type}>>`,
-      classes: 'relationshipLine',
-      style: ['fill:none', isContains ? '' : 'stroke-dasharray: 10,7'],
-      labelpos: 'c',
-      thickness: 'normal',
-      type: 'normal',
-      pattern: isContains ? 'normal' : 'dashed',
-      arrowTypeEnd: isContains ? 'requirement_contains' : 'requirement_arrow',
-      look: config.look,
-    };
-
-    edges.push(edge);
-    counter++;
+  public setCssStyle(ids: string[], styles: string[]) {
+    for (const id of ids) {
+      const node = this.requirements.get(id) ?? this.elements.get(id);
+      if (!styles || !node) {
+        return;
+      }
+      for (const s of styles) {
+        if (s.includes(',')) {
+          node.cssStyles.push(...s.split(','));
+        } else {
+          node.cssStyles.push(s);
+        }
+      }
+    }
   }
 
-  return { nodes, edges, other: {}, config, direction: getDirection() };
-};
+  public setClass(ids: string[], classNames: string[]) {
+    for (const id of ids) {
+      const node = this.requirements.get(id) ?? this.elements.get(id);
+      if (node) {
+        for (const _class of classNames) {
+          node.classes.push(_class);
+          const styles = this.classes.get(_class)?.styles;
+          if (styles) {
+            node.cssStyles.push(...styles);
+          }
+        }
+      }
+    }
+  }
 
-export default {
-  Relationships,
-  RequirementType,
-  RiskLevel,
-  VerifyType,
-  getConfig: () => getConfig().requirement,
-  addRequirement,
-  getRequirements,
-  setNewReqId,
-  setNewReqText,
-  setNewReqRisk,
-  setNewReqVerifyMethod,
-  setAccTitle,
-  getAccTitle,
-  setAccDescription,
-  getAccDescription,
-  setDiagramTitle,
-  getDiagramTitle,
-  getDirection,
-  setDirection,
-  addElement,
-  getElements,
-  setNewElementType,
-  setNewElementDocRef,
-  addRelationship,
-  getRelationships,
-  clear,
-  setCssStyle,
-  setClass,
-  defineClass,
-  getClasses,
-  getData,
-};
+  public defineClass(ids: string[], style: string[]) {
+    for (const id of ids) {
+      let styleClass = this.classes.get(id);
+      if (styleClass === undefined) {
+        styleClass = { id, styles: [], textStyles: [] };
+        this.classes.set(id, styleClass);
+      }
+
+      if (style) {
+        style.forEach(function (s) {
+          if (/color/.exec(s)) {
+            const newStyle = s.replace('fill', 'bgFill'); // .replace('color', 'fill');
+            styleClass.textStyles.push(newStyle);
+          }
+          styleClass.styles.push(s);
+        });
+      }
+
+      this.requirements.forEach((value) => {
+        if (value.classes.includes(id)) {
+          value.cssStyles.push(...style.flatMap((s) => s.split(',')));
+        }
+      });
+      this.elements.forEach((value) => {
+        if (value.classes.includes(id)) {
+          value.cssStyles.push(...style.flatMap((s) => s.split(',')));
+        }
+      });
+    }
+  }
+
+  public getClasses() {
+    return this.classes;
+  }
+
+  public getData() {
+    const config = getConfig();
+    const nodes: Node[] = [];
+    const edges: Edge[] = [];
+    for (const requirement of this.requirements.values()) {
+      const node = requirement as unknown as Node;
+      node.id = requirement.name;
+      node.cssStyles = requirement.cssStyles;
+      node.cssClasses = requirement.classes.join(' ');
+      node.shape = 'requirementBox';
+      node.look = config.look;
+      nodes.push(node);
+    }
+
+    for (const element of this.elements.values()) {
+      const node = element as unknown as Node;
+      node.shape = 'requirementBox';
+      node.look = config.look;
+      node.id = element.name;
+      node.cssStyles = element.cssStyles;
+      node.cssClasses = element.classes.join(' ');
+
+      nodes.push(node);
+    }
+
+    for (const relation of this.relations) {
+      let counter = 0;
+      const isContains = relation.type === this.Relationships.CONTAINS;
+      const edge: Edge = {
+        id: `${relation.src}-${relation.dst}-${counter}`,
+        start: this.requirements.get(relation.src)?.name ?? this.elements.get(relation.src)?.name,
+        end: this.requirements.get(relation.dst)?.name ?? this.elements.get(relation.dst)?.name,
+        label: `<<${relation.type}>>`,
+        classes: 'relationshipLine',
+        style: ['fill:none', isContains ? '' : 'stroke-dasharray: 10,7'],
+        labelpos: 'c',
+        thickness: 'normal',
+        type: 'normal',
+        pattern: isContains ? 'normal' : 'dashed',
+        arrowTypeEnd: isContains ? 'requirement_contains' : 'requirement_arrow',
+        look: config.look,
+      };
+
+      edges.push(edge);
+      counter++;
+    }
+
+    return { nodes, edges, other: {}, config, direction: this.getDirection() };
+  }
+
+  public setAccTitle = setAccTitle;
+  public getAccTitle = getAccTitle;
+  public setAccDescription = setAccDescription;
+  public getAccDescription = getAccDescription;
+  public setDiagramTitle = setDiagramTitle;
+  public getDiagramTitle = getDiagramTitle;
+  public getConfig = () => getConfig().class;
+}
diff --git a/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts b/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts
index 619f5b052..246d91197 100644
--- a/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts
+++ b/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts
@@ -1,13 +1,15 @@
 import type { DiagramDefinition } from '../../diagram-api/types.js';
 // @ts-ignore: JISON doesn't support types
 import parser from './parser/requirementDiagram.jison';
-import db from './requirementDb.js';
+import { RequirementDB } from './requirementDb.js';
 import styles from './styles.js';
 import renderer from './requirementRenderer.js';
 
 export const diagram: DiagramDefinition = {
   parser,
-  db,
+  get db() {
+    return new RequirementDB();
+  },
   renderer,
   styles,
 };

From 523286bbcba2129d4ab89da98a3d30ecf5887b69 Mon Sep 17 00:00:00 2001
From: yari-dewalt 
Date: Mon, 27 Jan 2025 09:29:51 -0800
Subject: [PATCH 155/230] Update test file to work with new Db

---
 .../mermaid/src/diagrams/requirement/requirementDb.spec.ts | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/packages/mermaid/src/diagrams/requirement/requirementDb.spec.ts b/packages/mermaid/src/diagrams/requirement/requirementDb.spec.ts
index da6684ca1..715d4d053 100644
--- a/packages/mermaid/src/diagrams/requirement/requirementDb.spec.ts
+++ b/packages/mermaid/src/diagrams/requirement/requirementDb.spec.ts
@@ -1,8 +1,9 @@
-import requirementDb from './requirementDb.js';
+import { RequirementDB } from './requirementDb.js';
 import { describe, it, expect } from 'vitest';
-import type { RelationshipType } from './types.js';
+import type { Relation, RelationshipType } from './types.js';
 
 describe('requirementDb', () => {
+  const requirementDb = new RequirementDB();
   beforeEach(() => {
     requirementDb.clear();
   });
@@ -23,7 +24,7 @@ describe('requirementDb', () => {
     requirementDb.addRelationship('contains' as RelationshipType, 'src', 'dst');
     const relationships = requirementDb.getRelationships();
     const relationship = relationships.find(
-      (r) => r.type === 'contains' && r.src === 'src' && r.dst === 'dst'
+      (r: Relation) => r.type === 'contains' && r.src === 'src' && r.dst === 'dst'
     );
     expect(relationship).toBeDefined();
   });

From a3f35f63677c03f813ce92bd2369fc5dfc3fda88 Mon Sep 17 00:00:00 2001
From: yari-dewalt 
Date: Mon, 27 Jan 2025 09:40:09 -0800
Subject: [PATCH 156/230] Fix parser test file

---
 .../src/diagrams/requirement/parser/requirementDiagram.spec.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/packages/mermaid/src/diagrams/requirement/parser/requirementDiagram.spec.js b/packages/mermaid/src/diagrams/requirement/parser/requirementDiagram.spec.js
index 743543f28..5875e143d 100644
--- a/packages/mermaid/src/diagrams/requirement/parser/requirementDiagram.spec.js
+++ b/packages/mermaid/src/diagrams/requirement/parser/requirementDiagram.spec.js
@@ -1,5 +1,5 @@
 import { setConfig } from '../../../config.js';
-import requirementDb from '../requirementDb.js';
+import { RequirementDB } from '../requirementDb.js';
 import reqDiagram from './requirementDiagram.jison';
 
 setConfig({
@@ -7,6 +7,7 @@ setConfig({
 });
 
 describe('when parsing requirement diagram it...', function () {
+  const requirementDb = new RequirementDB();
   beforeEach(function () {
     reqDiagram.parser.yy = requirementDb;
     reqDiagram.parser.yy.clear();

From b53cf0a1fb40f39d132ce5bf61c522d4dde209c2 Mon Sep 17 00:00:00 2001
From: yari-dewalt 
Date: Mon, 27 Jan 2025 10:33:45 -0800
Subject: [PATCH 157/230] Fix getConfig to return correct config

---
 packages/mermaid/src/diagrams/requirement/requirementDb.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/mermaid/src/diagrams/requirement/requirementDb.ts b/packages/mermaid/src/diagrams/requirement/requirementDb.ts
index 972c5e44e..b7c628040 100644
--- a/packages/mermaid/src/diagrams/requirement/requirementDb.ts
+++ b/packages/mermaid/src/diagrams/requirement/requirementDb.ts
@@ -345,5 +345,5 @@ export class RequirementDB implements DiagramDB {
   public getAccDescription = getAccDescription;
   public setDiagramTitle = setDiagramTitle;
   public getDiagramTitle = getDiagramTitle;
-  public getConfig = () => getConfig().class;
+  public getConfig = () => getConfig().requirement;
 }

From f00507449b668aee3e37b5038f79b8480a70dfc7 Mon Sep 17 00:00:00 2001
From: yari-dewalt 
Date: Tue, 28 Jan 2025 07:51:06 -0800
Subject: [PATCH 158/230] Remove unnecessary default export

---
 .../mermaid/src/diagrams/requirement/requirementDiagram.ts    | 2 +-
 .../mermaid/src/diagrams/requirement/requirementRenderer.ts   | 4 ----
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts b/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts
index 246d91197..5ad89949a 100644
--- a/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts
+++ b/packages/mermaid/src/diagrams/requirement/requirementDiagram.ts
@@ -3,7 +3,7 @@ import type { DiagramDefinition } from '../../diagram-api/types.js';
 import parser from './parser/requirementDiagram.jison';
 import { RequirementDB } from './requirementDb.js';
 import styles from './styles.js';
-import renderer from './requirementRenderer.js';
+import * as renderer from './requirementRenderer.js';
 
 export const diagram: DiagramDefinition = {
   parser,
diff --git a/packages/mermaid/src/diagrams/requirement/requirementRenderer.ts b/packages/mermaid/src/diagrams/requirement/requirementRenderer.ts
index 7e190f2c9..de18a08f3 100644
--- a/packages/mermaid/src/diagrams/requirement/requirementRenderer.ts
+++ b/packages/mermaid/src/diagrams/requirement/requirementRenderer.ts
@@ -34,7 +34,3 @@ export const draw = async function (text: string, id: string, _version: string,
 
   setupViewPortForSVG(svg, padding, 'requirementDiagram', conf?.useMaxWidth ?? true);
 };
-
-export default {
-  draw,
-};

From d0768cbc37d4fa5ce447382486e2054722f63420 Mon Sep 17 00:00:00 2001
From: yari-dewalt 
Date: Tue, 28 Jan 2025 08:10:07 -0800
Subject: [PATCH 159/230] Update shape

---
 .../shapes/requirementBox.ts                  | 35 ++++++++++---------
 1 file changed, 18 insertions(+), 17 deletions(-)

diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/requirementBox.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/requirementBox.ts
index da28f5db2..a41e1483a 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/requirementBox.ts
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/requirementBox.ts
@@ -18,7 +18,6 @@ export async function requirementBox(
   node.labelStyle = labelStyles;
   const requirementNode = node as unknown as Requirement;
   const elementNode = node as unknown as Element;
-  const config = getConfig().requirement;
   const padding = 20;
   const gap = 20;
   const isRequirementNode = 'verifyMethod' in node;
@@ -47,7 +46,7 @@ export async function requirementBox(
     shapeSvg,
     requirementNode.name,
     accumulativeHeight,
-    node.labelStyle
+    node.labelStyle + '; font-weight: bold;'
   );
   accumulativeHeight += nameHeight + gap;
 
@@ -98,11 +97,8 @@ export async function requirementBox(
     );
   }
 
-  const totalWidth = Math.max(
-    (shapeSvg.node()?.getBBox().width ?? 200) + padding,
-    config?.rect_min_width ?? 200
-  );
-  const totalHeight = totalWidth;
+  const totalWidth = (shapeSvg.node()?.getBBox().width ?? 200) + padding;
+  const totalHeight = (shapeSvg.node()?.getBBox().height ?? 200) + padding;
   const x = -totalWidth / 2;
   const y = -totalHeight / 2;
 
@@ -151,16 +147,18 @@ export async function requirementBox(
     text.attr('transform', `translate(${newTranslateX}, ${newTranslateY + padding})`);
   });
 
-  // Insert divider line
-  const roughLine = rc.line(
-    x,
-    y + typeHeight + nameHeight + gap,
-    x + totalWidth,
-    y + typeHeight + nameHeight + gap,
-    options
-  );
-  const dividerLine = shapeSvg.insert(() => roughLine);
-  dividerLine.attr('style', nodeStyles);
+  // Insert divider line if there is body text
+  if (accumulativeHeight > typeHeight + nameHeight + gap) {
+    const roughLine = rc.line(
+      x,
+      y + typeHeight + nameHeight + gap,
+      x + totalWidth,
+      y + typeHeight + nameHeight + gap,
+      options
+    );
+    const dividerLine = shapeSvg.insert(() => roughLine);
+    dividerLine.attr('style', nodeStyles);
+  }
 
   updateNodeBounds(node, rect);
 
@@ -201,6 +199,9 @@ async function addText(
     const textChild = text.children[0];
     for (const child of textChild.children) {
       child.textContent = child.textContent.replaceAll('>', '>').replaceAll('<', '<');
+      if (style) {
+        child.setAttribute('style', style);
+      }
     }
     // Get the bounding box after the text update
     bbox = text.getBBox();

From d155e414a0c4d463102032296283dfd056b187b6 Mon Sep 17 00:00:00 2001
From: homersimpsons 
Date: Sat, 1 Feb 2025 11:20:06 +0100
Subject: [PATCH 160/230] Add git graph preview in README.md

---
 README.md | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/README.md b/README.md
index 280725af7..047f8254c 100644
--- a/README.md
+++ b/README.md
@@ -257,6 +257,34 @@ pie
 
 ### Git graph [experimental - live editor]
 
+```
+gitGraph
+  commit
+  commit
+  branch develop
+  checkout develop
+  commit
+  commit
+  checkout main
+  merge develop
+  commit
+  commit
+```
+
+```mermaid
+gitGraph
+  commit
+  commit
+  branch develop
+  checkout develop
+  commit
+  commit
+  checkout main
+  merge develop
+  commit
+  commit
+```
+
 ### Bar chart (using gantt chart) [docs - live editor]
 
 ```

From a0e5408850f3bbfec0ad6219f45d7becae80dd84 Mon Sep 17 00:00:00 2001
From: saurabhg772244 
Date: Tue, 4 Feb 2025 16:41:54 +0530
Subject: [PATCH 161/230] convert stateDb to class, added test case.

---
 .../state/parser/state-parser.spec.js         |    4 +-
 .../diagrams/state/parser/state-style.spec.js |    4 +-
 packages/mermaid/src/diagrams/state/shapes.js |   12 +-
 .../mermaid/src/diagrams/state/stateDb.js     | 1131 +++++++++--------
 .../src/diagrams/state/stateDb.spec.js        |   30 +-
 .../diagrams/state/stateDiagram-v2.spec.js    |    4 +-
 .../src/diagrams/state/stateDiagram-v2.ts     |    6 +-
 .../src/diagrams/state/stateDiagram.spec.js   |    4 +-
 .../src/diagrams/state/stateDiagram.ts        |    6 +-
 packages/mermaid/src/mermaidAPI.spec.ts       |   26 +
 10 files changed, 669 insertions(+), 558 deletions(-)

diff --git a/packages/mermaid/src/diagrams/state/parser/state-parser.spec.js b/packages/mermaid/src/diagrams/state/parser/state-parser.spec.js
index 9fa8acab8..bb5345996 100644
--- a/packages/mermaid/src/diagrams/state/parser/state-parser.spec.js
+++ b/packages/mermaid/src/diagrams/state/parser/state-parser.spec.js
@@ -1,4 +1,4 @@
-import stateDb from '../stateDb.js';
+import { StateDB } from '../stateDb.js';
 import stateDiagram from './stateDiagram.jison';
 import { setConfig } from '../../../config.js';
 
@@ -7,7 +7,9 @@ setConfig({
 });
 
 describe('state parser can parse...', () => {
+  let stateDb;
   beforeEach(function () {
+    stateDb = new StateDB();
     stateDiagram.parser.yy = stateDb;
     stateDiagram.parser.yy.clear();
   });
diff --git a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js
index fed63c444..c37bed3c7 100644
--- a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js
+++ b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js
@@ -1,4 +1,4 @@
-import stateDb from '../stateDb.js';
+import { StateDB } from '../stateDb.js';
 import stateDiagram from './stateDiagram.jison';
 import { setConfig } from '../../../config.js';
 
@@ -7,7 +7,9 @@ setConfig({
 });
 
 describe('ClassDefs and classes when parsing a State diagram', () => {
+  let stateDb;
   beforeEach(function () {
+    stateDb = new StateDB();
     stateDiagram.parser.yy = stateDb;
     stateDiagram.parser.yy.clear();
   });
diff --git a/packages/mermaid/src/diagrams/state/shapes.js b/packages/mermaid/src/diagrams/state/shapes.js
index f0ab4136b..b18b4ca0e 100644
--- a/packages/mermaid/src/diagrams/state/shapes.js
+++ b/packages/mermaid/src/diagrams/state/shapes.js
@@ -1,6 +1,6 @@
 import { line, curveBasis } from 'd3';
 import idCache from './id-cache.js';
-import stateDb from './stateDb.js';
+import { StateDB } from './stateDb.js';
 import utils from '../../utils.js';
 import common from '../common/common.js';
 import { getConfig } from '../../diagram-api/diagramAPI.js';
@@ -414,13 +414,13 @@ let edgeCount = 0;
 export const drawEdge = function (elem, path, relation) {
   const getRelationType = function (type) {
     switch (type) {
-      case stateDb.relationType.AGGREGATION:
+      case StateDB.relationType.AGGREGATION:
         return 'aggregation';
-      case stateDb.relationType.EXTENSION:
+      case StateDB.relationType.EXTENSION:
         return 'extension';
-      case stateDb.relationType.COMPOSITION:
+      case StateDB.relationType.COMPOSITION:
         return 'composition';
-      case stateDb.relationType.DEPENDENCY:
+      case StateDB.relationType.DEPENDENCY:
         return 'dependency';
     }
   };
@@ -459,7 +459,7 @@ export const drawEdge = function (elem, path, relation) {
 
   svgPath.attr(
     'marker-end',
-    'url(' + url + '#' + getRelationType(stateDb.relationType.DEPENDENCY) + 'End' + ')'
+    'url(' + url + '#' + getRelationType(StateDB.relationType.DEPENDENCY) + 'End' + ')'
   );
 
   if (relation.title !== undefined) {
diff --git a/packages/mermaid/src/diagrams/state/stateDb.js b/packages/mermaid/src/diagrams/state/stateDb.js
index 1f12425e6..2da0719ca 100644
--- a/packages/mermaid/src/diagrams/state/stateDb.js
+++ b/packages/mermaid/src/diagrams/state/stateDb.js
@@ -1,28 +1,28 @@
+import { getConfig } from '../../diagram-api/diagramAPI.js';
 import { log } from '../../logger.js';
 import { generateId } from '../../utils.js';
 import common from '../common/common.js';
-import { getConfig } from '../../diagram-api/diagramAPI.js';
 import {
-  setAccTitle,
-  getAccTitle,
-  getAccDescription,
-  setAccDescription,
   clear as commonClear,
-  setDiagramTitle,
+  getAccDescription,
+  getAccTitle,
   getDiagramTitle,
+  setAccDescription,
+  setAccTitle,
+  setDiagramTitle,
 } from '../common/commonDb.js';
 import { dataFetcher, reset as resetDataFetching } from './dataFetcher.js';
 import { getDir } from './stateRenderer-v3-unified.js';
 
 import {
   DEFAULT_DIAGRAM_DIRECTION,
-  STMT_STATE,
-  STMT_RELATION,
-  STMT_CLASSDEF,
-  STMT_STYLEDEF,
-  STMT_APPLYCLASS,
   DEFAULT_STATE_TYPE,
   DIVIDER_TYPE,
+  STMT_APPLYCLASS,
+  STMT_CLASSDEF,
+  STMT_RELATION,
+  STMT_STATE,
+  STMT_STYLEDEF,
 } from './stateCommon.js';
 
 const START_NODE = '[*]';
@@ -46,15 +46,6 @@ function newClassesList() {
   return new Map();
 }
 
-let nodes = [];
-let edges = [];
-
-let direction = DEFAULT_DIAGRAM_DIRECTION;
-let rootDoc = [];
-let classes = newClassesList(); // style classes defined by a classDef
-
-// --------------------------------------
-
 const newDoc = () => {
   return {
     /** @type {{ id1: string, id2: string, relationTitle: string }[]} */
@@ -63,564 +54,622 @@ const newDoc = () => {
     documents: {},
   };
 };
-let documents = {
-  root: newDoc(),
-};
-
-let currentDocument = documents.root;
-let startEndCount = 0;
-let dividerCnt = 0;
-
-export const lineType = {
-  LINE: 0,
-  DOTTED_LINE: 1,
-};
-
-export const relationType = {
-  AGGREGATION: 0,
-  EXTENSION: 1,
-  COMPOSITION: 2,
-  DEPENDENCY: 3,
-};
 
 const clone = (o) => JSON.parse(JSON.stringify(o));
 
-const setRootDoc = (o) => {
-  log.info('Setting root doc', o);
-  // rootDoc = { id: 'root', doc: o };
-  rootDoc = o;
-};
+export class StateDB {
+  constructor() {
+    this.clear();
 
-const getRootDoc = () => rootDoc;
-
-const docTranslator = (parent, node, first) => {
-  if (node.stmt === STMT_RELATION) {
-    docTranslator(parent, node.state1, true);
-    docTranslator(parent, node.state2, false);
-  } else {
-    if (node.stmt === STMT_STATE) {
-      if (node.id === '[*]') {
-        node.id = first ? parent.id + '_start' : parent.id + '_end';
-        node.start = first;
-      } else {
-        // This is just a plain state, not a start or end
-        node.id = node.id.trim();
-      }
-    }
-
-    if (node.doc) {
-      const doc = [];
-      // Check for concurrency
-      let currentDoc = [];
-      let i;
-      for (i = 0; i < node.doc.length; i++) {
-        if (node.doc[i].type === DIVIDER_TYPE) {
-          // debugger;
-          const newNode = clone(node.doc[i]);
-          newNode.doc = clone(currentDoc);
-          doc.push(newNode);
-          currentDoc = [];
-        } else {
-          currentDoc.push(node.doc[i]);
-        }
-      }
-
-      // If any divider was encountered
-      if (doc.length > 0 && currentDoc.length > 0) {
-        const newNode = {
-          stmt: STMT_STATE,
-          id: generateId(),
-          type: 'divider',
-          doc: clone(currentDoc),
-        };
-        doc.push(clone(newNode));
-        node.doc = doc;
-      }
-
-      node.doc.forEach((docNode) => docTranslator(node, docNode, true));
-    }
-  }
-};
-const getRootDocV2 = () => {
-  docTranslator({ id: 'root' }, { id: 'root', doc: rootDoc }, true);
-  return { id: 'root', doc: rootDoc };
-  // Here
-};
-
-/**
- * Convert all of the statements (stmts) that were parsed into states and relationships.
- * This is done because a state diagram may have nested sections,
- * where each section is a 'document' and has its own set of statements.
- * Ex: the section within a fork has its own statements, and incoming and outgoing statements
- * refer to the fork as a whole (document).
- * See the parser grammar:  the definition of a document is a document then a 'line', where a line can be a statement.
- * This will push the statement into the list of statements for the current document.
- *
- * @param _doc
- */
-const extract = (_doc) => {
-  // const res = { states: [], relations: [] };
-  let doc;
-  if (_doc.doc) {
-    doc = _doc.doc;
-  } else {
-    doc = _doc;
-  }
-  // let doc = root.doc;
-  // if (!doc) {
-  //   doc = root;
-  // }
-  log.info(doc);
-  clear(true);
-
-  log.info('Extract initial document:', doc);
-
-  doc.forEach((item) => {
-    log.warn('Statement', item.stmt);
-    switch (item.stmt) {
-      case STMT_STATE:
-        addState(
-          item.id.trim(),
-          item.type,
-          item.doc,
-          item.description,
-          item.note,
-          item.classes,
-          item.styles,
-          item.textStyles
-        );
-        break;
-      case STMT_RELATION:
-        addRelation(item.state1, item.state2, item.description);
-        break;
-      case STMT_CLASSDEF:
-        addStyleClass(item.id.trim(), item.classes);
-        break;
-      case STMT_STYLEDEF:
-        {
-          const ids = item.id.trim().split(',');
-          const styles = item.styleClass.split(',');
-          ids.forEach((id) => {
-            let foundState = getState(id);
-            if (foundState === undefined) {
-              const trimmedId = id.trim();
-              addState(trimmedId);
-              foundState = getState(trimmedId);
-            }
-            foundState.styles = styles.map((s) => s.replace(/;/g, '')?.trim());
-          });
-        }
-        break;
-      case STMT_APPLYCLASS:
-        setCssClass(item.id.trim(), item.styleClass);
-        break;
-    }
-  });
-
-  const diagramStates = getStates();
-  const config = getConfig();
-  const look = config.look;
-  resetDataFetching();
-  dataFetcher(undefined, getRootDocV2(), diagramStates, nodes, edges, true, look, classes);
-  nodes.forEach((node) => {
-    if (Array.isArray(node.label)) {
-      // add the rest as description
-      node.description = node.label.slice(1);
-      if (node.isGroup && node.description.length > 0) {
-        throw new Error(
-          'Group nodes can only have label. Remove the additional description for node [' +
-            node.id +
-            ']'
-        );
-      }
-      // add first description as label
-      node.label = node.label[0];
-    }
-  });
-};
-
-/**
- * Function called by parser when a node definition has been found.
- *
- * @param {null | string} id
- * @param {null | string} type
- * @param {null | string} doc
- * @param {null | string | string[]} descr - description for the state. Can be a string or a list or strings
- * @param {null | string} note
- * @param {null | string | string[]} classes - class styles to apply to this state. Can be a string (1 style) or an array of styles. If it's just 1 class, convert it to an array of that 1 class.
- * @param {null | string | string[]} styles - styles to apply to this state. Can be a string (1 style) or an array of styles. If it's just 1 style, convert it to an array of that 1 style.
- * @param {null | string | string[]} textStyles - text styles to apply to this state. Can be a string (1 text test) or an array of text styles. If it's just 1 text style, convert it to an array of that 1 text style.
- */
-export const addState = function (
-  id,
-  type = DEFAULT_STATE_TYPE,
-  doc = null,
-  descr = null,
-  note = null,
-  classes = null,
-  styles = null,
-  textStyles = null
-) {
-  const trimmedId = id?.trim();
-  // add the state if needed
-  if (!currentDocument.states.has(trimmedId)) {
-    log.info('Adding state ', trimmedId, descr);
-    currentDocument.states.set(trimmedId, {
-      id: trimmedId,
-      descriptions: [],
-      type,
-      doc,
-      note,
-      classes: [],
-      styles: [],
-      textStyles: [],
-    });
-  } else {
-    if (!currentDocument.states.get(trimmedId).doc) {
-      currentDocument.states.get(trimmedId).doc = doc;
-    }
-    if (!currentDocument.states.get(trimmedId).type) {
-      currentDocument.states.get(trimmedId).type = type;
-    }
+    // Needed for JISON since it only supports direct properties
+    this.setRootDoc = this.setRootDoc.bind(this);
+    this.getDividerId = this.getDividerId.bind(this);
+    this.setDirection = this.setDirection.bind(this);
+    this.trimColon = this.trimColon.bind(this);
   }
 
-  if (descr) {
-    log.info('Setting state description', trimmedId, descr);
-    if (typeof descr === 'string') {
-      addDescription(trimmedId, descr.trim());
-    }
-
-    if (typeof descr === 'object') {
-      descr.forEach((des) => addDescription(trimmedId, des.trim()));
-    }
-  }
-
-  if (note) {
-    const doc2 = currentDocument.states.get(trimmedId);
-    doc2.note = note;
-    doc2.note.text = common.sanitizeText(doc2.note.text, getConfig());
-  }
-
-  if (classes) {
-    log.info('Setting state classes', trimmedId, classes);
-    const classesList = typeof classes === 'string' ? [classes] : classes;
-    classesList.forEach((cssClass) => setCssClass(trimmedId, cssClass.trim()));
-  }
-
-  if (styles) {
-    log.info('Setting state styles', trimmedId, styles);
-    const stylesList = typeof styles === 'string' ? [styles] : styles;
-    stylesList.forEach((style) => setStyle(trimmedId, style.trim()));
-  }
-
-  if (textStyles) {
-    log.info('Setting state styles', trimmedId, styles);
-    const textStylesList = typeof textStyles === 'string' ? [textStyles] : textStyles;
-    textStylesList.forEach((textStyle) => setTextStyle(trimmedId, textStyle.trim()));
-  }
-};
-
-export const clear = function (saveCommon) {
+  /**
+   * @private
+   * @type {Array}
+   */
   nodes = [];
+  /**
+   * @private
+   * @type {Array}
+   */
   edges = [];
+
+  /**
+   * @private
+   * @type {string}
+   */
+  direction = DEFAULT_DIAGRAM_DIRECTION;
+  /**
+   * @private
+   * @type {Array}
+   */
+  rootDoc = [];
+  /**
+   * @private
+   * @type {Map}
+   */
+  classes = newClassesList(); // style classes defined by a classDef
+
+  /**
+   * @private
+   * @type {Object}
+   */
   documents = {
     root: newDoc(),
   };
-  currentDocument = documents.root;
 
-  // number of start and end nodes; used to construct ids
+  /**
+   * @private
+   * @type {Object}
+   */
+  currentDocument = this.documents.root;
+  /**
+   * @private
+   * @type {number}
+   */
   startEndCount = 0;
-  classes = newClassesList();
-  if (!saveCommon) {
-    commonClear();
+  /**
+   * @private
+   * @type {number}
+   */
+  dividerCnt = 0;
+
+  static relationType = {
+    AGGREGATION: 0,
+    EXTENSION: 1,
+    COMPOSITION: 2,
+    DEPENDENCY: 3,
+  };
+
+  setRootDoc(o) {
+    log.info('Setting root doc', o);
+    // rootDoc = { id: 'root', doc: o };
+    this.rootDoc = o;
   }
-};
 
-export const getState = function (id) {
-  return currentDocument.states.get(id);
-};
-
-export const getStates = function () {
-  return currentDocument.states;
-};
-export const logDocuments = function () {
-  log.info('Documents = ', documents);
-};
-export const getRelations = function () {
-  return currentDocument.relations;
-};
-
-/**
- * If the id is a start node ( [*] ), then return a new id constructed from
- * the start node name and the current start node count.
- * else return the given id
- *
- * @param {string} id
- * @returns {string} - the id (original or constructed)
- */
-function startIdIfNeeded(id = '') {
-  let fixedId = id;
-  if (id === START_NODE) {
-    startEndCount++;
-    fixedId = `${START_TYPE}${startEndCount}`;
+  getRootDoc() {
+    return this.rootDoc;
   }
-  return fixedId;
-}
 
-/**
- * If the id is a start node ( [*] ), then return the start type ('start')
- * else return the given type
- *
- * @param {string} id
- * @param {string} type
- * @returns {string} - the type that should be used
- */
-function startTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
-  return id === START_NODE ? START_TYPE : type;
-}
+  /**
+   * @private
+   * @param {Object} parent
+   * @param {Object} node
+   * @param {boolean} first
+   */
+  docTranslator(parent, node, first) {
+    if (node.stmt === STMT_RELATION) {
+      this.docTranslator(parent, node.state1, true);
+      this.docTranslator(parent, node.state2, false);
+    } else {
+      if (node.stmt === STMT_STATE) {
+        if (node.id === '[*]') {
+          node.id = first ? parent.id + '_start' : parent.id + '_end';
+          node.start = first;
+        } else {
+          // This is just a plain state, not a start or end
+          node.id = node.id.trim();
+        }
+      }
 
-/**
- * If the id is an end node ( [*] ), then return a new id constructed from
- * the end node name and the current start_end node count.
- * else return the given id
- *
- * @param {string} id
- * @returns {string} - the id (original or constructed)
- */
-function endIdIfNeeded(id = '') {
-  let fixedId = id;
-  if (id === END_NODE) {
-    startEndCount++;
-    fixedId = `${END_TYPE}${startEndCount}`;
+      if (node.doc) {
+        const doc = [];
+        // Check for concurrency
+        let currentDoc = [];
+        let i;
+        for (i = 0; i < node.doc.length; i++) {
+          if (node.doc[i].type === DIVIDER_TYPE) {
+            const newNode = clone(node.doc[i]);
+            newNode.doc = clone(currentDoc);
+            doc.push(newNode);
+            currentDoc = [];
+          } else {
+            currentDoc.push(node.doc[i]);
+          }
+        }
+
+        // If any divider was encountered
+        if (doc.length > 0 && currentDoc.length > 0) {
+          const newNode = {
+            stmt: STMT_STATE,
+            id: generateId(),
+            type: 'divider',
+            doc: clone(currentDoc),
+          };
+          doc.push(clone(newNode));
+          node.doc = doc;
+        }
+
+        node.doc.forEach((docNode) => this.docTranslator(node, docNode, true));
+      }
+    }
+  }
+  getRootDocV2() {
+    this.docTranslator({ id: 'root' }, { id: 'root', doc: this.rootDoc }, true);
+    return { id: 'root', doc: this.rootDoc };
+    // Here
   }
-  return fixedId;
-}
 
-/**
- * If the id is an end node ( [*] ), then return the end type
- * else return the given type
- *
- * @param {string} id
- * @param {string} type
- * @returns {string} - the type that should be used
- */
-function endTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
-  return id === END_NODE ? END_TYPE : type;
-}
+  /**
+   * Convert all of the statements (stmts) that were parsed into states and relationships.
+   * This is done because a state diagram may have nested sections,
+   * where each section is a 'document' and has its own set of statements.
+   * Ex: the section within a fork has its own statements, and incoming and outgoing statements
+   * refer to the fork as a whole (document).
+   * See the parser grammar:  the definition of a document is a document then a 'line', where a line can be a statement.
+   * This will push the statement into the list of statements for the current document.
+   *
+   * @param _doc
+   */
+  extract(_doc) {
+    // const res = { states: [], relations: [] };
+    let doc;
+    if (_doc.doc) {
+      doc = _doc.doc;
+    } else {
+      doc = _doc;
+    }
+    // let doc = root.doc;
+    // if (!doc) {
+    //   doc = root;
+    // }
+    log.info(doc);
+    this.clear(true);
 
-/**
- *
- * @param item1
- * @param item2
- * @param relationTitle
- */
-export function addRelationObjs(item1, item2, relationTitle) {
-  let id1 = startIdIfNeeded(item1.id.trim());
-  let type1 = startTypeIfNeeded(item1.id.trim(), item1.type);
-  let id2 = startIdIfNeeded(item2.id.trim());
-  let type2 = startTypeIfNeeded(item2.id.trim(), item2.type);
+    log.info('Extract initial document:', doc);
 
+    doc.forEach((item) => {
+      log.warn('Statement', item.stmt);
+      switch (item.stmt) {
+        case STMT_STATE:
+          this.addState(
+            item.id.trim(),
+            item.type,
+            item.doc,
+            item.description,
+            item.note,
+            item.classes,
+            item.styles,
+            item.textStyles
+          );
+          break;
+        case STMT_RELATION:
+          this.addRelation(item.state1, item.state2, item.description);
+          break;
+        case STMT_CLASSDEF:
+          this.addStyleClass(item.id.trim(), item.classes);
+          break;
+        case STMT_STYLEDEF:
+          {
+            const ids = item.id.trim().split(',');
+            const styles = item.styleClass.split(',');
+            ids.forEach((id) => {
+              let foundState = this.getState(id);
+              if (foundState === undefined) {
+                const trimmedId = id.trim();
+                this.addState(trimmedId);
+                foundState = this.getState(trimmedId);
+              }
+              foundState.styles = styles.map((s) => s.replace(/;/g, '')?.trim());
+            });
+          }
+          break;
+        case STMT_APPLYCLASS:
+          this.setCssClass(item.id.trim(), item.styleClass);
+          break;
+      }
+    });
+
+    const diagramStates = this.getStates();
+    const config = getConfig();
+    const look = config.look;
+
+    resetDataFetching();
+    dataFetcher(
+      undefined,
+      this.getRootDocV2(),
+      diagramStates,
+      this.nodes,
+      this.edges,
+      true,
+      look,
+      this.classes
+    );
+    this.nodes.forEach((node) => {
+      if (Array.isArray(node.label)) {
+        // add the rest as description
+        node.description = node.label.slice(1);
+        if (node.isGroup && node.description.length > 0) {
+          throw new Error(
+            'Group nodes can only have label. Remove the additional description for node [' +
+              node.id +
+              ']'
+          );
+        }
+        // add first description as label
+        node.label = node.label[0];
+      }
+    });
+  }
+
+  /**
+   * Function called by parser when a node definition has been found.
+   *
+   * @param {null | string} id
+   * @param {null | string} type
+   * @param {null | string} doc
+   * @param {null | string | string[]} descr - description for the state. Can be a string or a list or strings
+   * @param {null | string} note
+   * @param {null | string | string[]} classes - class styles to apply to this state. Can be a string (1 style) or an array of styles. If it's just 1 class, convert it to an array of that 1 class.
+   * @param {null | string | string[]} styles - styles to apply to this state. Can be a string (1 style) or an array of styles. If it's just 1 style, convert it to an array of that 1 style.
+   * @param {null | string | string[]} textStyles - text styles to apply to this state. Can be a string (1 text test) or an array of text styles. If it's just 1 text style, convert it to an array of that 1 text style.
+   */
   addState(
-    id1,
-    type1,
-    item1.doc,
-    item1.description,
-    item1.note,
-    item1.classes,
-    item1.styles,
-    item1.textStyles
-  );
-  addState(
-    id2,
-    type2,
-    item2.doc,
-    item2.description,
-    item2.note,
-    item2.classes,
-    item2.styles,
-    item2.textStyles
-  );
+    id,
+    type = DEFAULT_STATE_TYPE,
+    doc = null,
+    descr = null,
+    note = null,
+    classes = null,
+    styles = null,
+    textStyles = null
+  ) {
+    const trimmedId = id?.trim();
+    // add the state if needed
+    if (!this.currentDocument.states.has(trimmedId)) {
+      log.info('Adding state ', trimmedId, descr);
+      this.currentDocument.states.set(trimmedId, {
+        id: trimmedId,
+        descriptions: [],
+        type,
+        doc,
+        note,
+        classes: [],
+        styles: [],
+        textStyles: [],
+      });
+    } else {
+      if (!this.currentDocument.states.get(trimmedId).doc) {
+        this.currentDocument.states.get(trimmedId).doc = doc;
+      }
+      if (!this.currentDocument.states.get(trimmedId).type) {
+        this.currentDocument.states.get(trimmedId).type = type;
+      }
+    }
 
-  currentDocument.relations.push({
-    id1,
-    id2,
-    relationTitle: common.sanitizeText(relationTitle, getConfig()),
-  });
-}
+    if (descr) {
+      log.info('Setting state description', trimmedId, descr);
+      if (typeof descr === 'string') {
+        this.addDescription(trimmedId, descr.trim());
+      }
 
-/**
- * Add a relation between two items.  The items may be full objects or just the string id of a state.
- *
- * @param {string | object} item1
- * @param {string | object} item2
- * @param {string} title
- */
-export const addRelation = function (item1, item2, title) {
-  if (typeof item1 === 'object') {
-    addRelationObjs(item1, item2, title);
-  } else {
-    const id1 = startIdIfNeeded(item1.trim());
-    const type1 = startTypeIfNeeded(item1);
-    const id2 = endIdIfNeeded(item2.trim());
-    const type2 = endTypeIfNeeded(item2);
+      if (typeof descr === 'object') {
+        descr.forEach((des) => this.addDescription(trimmedId, des.trim()));
+      }
+    }
 
-    addState(id1, type1);
-    addState(id2, type2);
-    currentDocument.relations.push({
+    if (note) {
+      const doc2 = this.currentDocument.states.get(trimmedId);
+      doc2.note = note;
+      doc2.note.text = common.sanitizeText(doc2.note.text, getConfig());
+    }
+
+    if (classes) {
+      log.info('Setting state classes', trimmedId, classes);
+      const classesList = typeof classes === 'string' ? [classes] : classes;
+      classesList.forEach((cssClass) => this.setCssClass(trimmedId, cssClass.trim()));
+    }
+
+    if (styles) {
+      log.info('Setting state styles', trimmedId, styles);
+      const stylesList = typeof styles === 'string' ? [styles] : styles;
+      stylesList.forEach((style) => this.setStyle(trimmedId, style.trim()));
+    }
+
+    if (textStyles) {
+      log.info('Setting state styles', trimmedId, styles);
+      const textStylesList = typeof textStyles === 'string' ? [textStyles] : textStyles;
+      textStylesList.forEach((textStyle) => this.setTextStyle(trimmedId, textStyle.trim()));
+    }
+  }
+
+  clear(saveCommon) {
+    this.nodes = [];
+    this.edges = [];
+    this.documents = {
+      root: newDoc(),
+    };
+    this.currentDocument = this.documents.root;
+
+    // number of start and end nodes; used to construct ids
+    this.startEndCount = 0;
+    this.classes = newClassesList();
+    if (!saveCommon) {
+      commonClear();
+    }
+  }
+
+  getState(id) {
+    return this.currentDocument.states.get(id);
+  }
+  getStates() {
+    return this.currentDocument.states;
+  }
+  logDocuments() {
+    log.info('Documents = ', this.documents);
+  }
+  getRelations() {
+    return this.currentDocument.relations;
+  }
+
+  /**
+   * If the id is a start node ( [*] ), then return a new id constructed from
+   * the start node name and the current start node count.
+   * else return the given id
+   *
+   * @param {string} id
+   * @returns {string} - the id (original or constructed)
+   * @private
+   */
+  startIdIfNeeded(id = '') {
+    let fixedId = id;
+    if (id === START_NODE) {
+      this.startEndCount++;
+      fixedId = `${START_TYPE}${this.startEndCount}`;
+    }
+    return fixedId;
+  }
+
+  /**
+   * If the id is a start node ( [*] ), then return the start type ('start')
+   * else return the given type
+   *
+   * @param {string} id
+   * @param {string} type
+   * @returns {string} - the type that should be used
+   * @private
+   */
+  startTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
+    return id === START_NODE ? START_TYPE : type;
+  }
+
+  /**
+   * If the id is an end node ( [*] ), then return a new id constructed from
+   * the end node name and the current start_end node count.
+   * else return the given id
+   *
+   * @param {string} id
+   * @returns {string} - the id (original or constructed)
+   * @private
+   */
+  endIdIfNeeded(id = '') {
+    let fixedId = id;
+    if (id === END_NODE) {
+      this.startEndCount++;
+      fixedId = `${END_TYPE}${this.startEndCount}`;
+    }
+    return fixedId;
+  }
+
+  /**
+   * If the id is an end node ( [*] ), then return the end type
+   * else return the given type
+   *
+   * @param {string} id
+   * @param {string} type
+   * @returns {string} - the type that should be used
+   * @private
+   */
+  endTypeIfNeeded(id = '', type = DEFAULT_STATE_TYPE) {
+    return id === END_NODE ? END_TYPE : type;
+  }
+
+  /**
+   *
+   * @param item1
+   * @param item2
+   * @param relationTitle
+   */
+  addRelationObjs(item1, item2, relationTitle) {
+    let id1 = this.startIdIfNeeded(item1.id.trim());
+    let type1 = this.startTypeIfNeeded(item1.id.trim(), item1.type);
+    let id2 = this.startIdIfNeeded(item2.id.trim());
+    let type2 = this.startTypeIfNeeded(item2.id.trim(), item2.type);
+
+    this.addState(
+      id1,
+      type1,
+      item1.doc,
+      item1.description,
+      item1.note,
+      item1.classes,
+      item1.styles,
+      item1.textStyles
+    );
+    this.addState(
+      id2,
+      type2,
+      item2.doc,
+      item2.description,
+      item2.note,
+      item2.classes,
+      item2.styles,
+      item2.textStyles
+    );
+
+    this.currentDocument.relations.push({
       id1,
       id2,
-      title: common.sanitizeText(title, getConfig()),
+      relationTitle: common.sanitizeText(relationTitle, getConfig()),
     });
   }
-};
 
-export const addDescription = function (id, descr) {
-  const theState = currentDocument.states.get(id);
-  const _descr = descr.startsWith(':') ? descr.replace(':', '').trim() : descr;
-  theState.descriptions.push(common.sanitizeText(_descr, getConfig()));
-};
+  /**
+   * Add a relation between two items.  The items may be full objects or just the string id of a state.
+   *
+   * @param {string | object} item1
+   * @param {string | object} item2
+   * @param {string} title
+   */
+  addRelation(item1, item2, title) {
+    if (typeof item1 === 'object') {
+      this.addRelationObjs(item1, item2, title);
+    } else {
+      const id1 = this.startIdIfNeeded(item1.trim());
+      const type1 = this.startTypeIfNeeded(item1);
+      const id2 = this.endIdIfNeeded(item2.trim());
+      const type2 = this.endTypeIfNeeded(item2);
 
-export const cleanupLabel = function (label) {
-  if (label.substring(0, 1) === ':') {
-    return label.substr(2).trim();
-  } else {
-    return label.trim();
-  }
-};
-
-const getDividerId = () => {
-  dividerCnt++;
-  return 'divider-id-' + dividerCnt;
-};
-
-/**
- * Called when the parser comes across a (style) class definition
- * @example classDef my-style fill:#f96;
- *
- * @param {string} id - the id of this (style) class
- * @param  {string | null} styleAttributes - the string with 1 or more style attributes (each separated by a comma)
- */
-export const addStyleClass = function (id, styleAttributes = '') {
-  // create a new style class object with this id
-  if (!classes.has(id)) {
-    classes.set(id, { id: id, styles: [], textStyles: [] }); // This is a classDef
-  }
-  const foundClass = classes.get(id);
-  if (styleAttributes !== undefined && styleAttributes !== null) {
-    styleAttributes.split(STYLECLASS_SEP).forEach((attrib) => {
-      // remove any trailing ;
-      const fixedAttrib = attrib.replace(/([^;]*);/, '$1').trim();
-
-      // replace some style keywords
-      if (RegExp(COLOR_KEYWORD).exec(attrib)) {
-        const newStyle1 = fixedAttrib.replace(FILL_KEYWORD, BG_FILL);
-        const newStyle2 = newStyle1.replace(COLOR_KEYWORD, FILL_KEYWORD);
-        foundClass.textStyles.push(newStyle2);
-      }
-      foundClass.styles.push(fixedAttrib);
-    });
-  }
-};
-
-/**
- * Return all of the style classes
- * @returns {{} | any | classes}
- */
-export const getClasses = function () {
-  return classes;
-};
-
-/**
- * Add a (style) class or css class to a state with the given id.
- * If the state isn't already in the list of known states, add it.
- * Might be called by parser when a style class or CSS class should be applied to a state
- *
- * @param {string | string[]} itemIds The id or a list of ids of the item(s) to apply the css class to
- * @param {string} cssClassName CSS class name
- */
-export const setCssClass = function (itemIds, cssClassName) {
-  itemIds.split(',').forEach(function (id) {
-    let foundState = getState(id);
-    if (foundState === undefined) {
-      const trimmedId = id.trim();
-      addState(trimmedId);
-      foundState = getState(trimmedId);
+      this.addState(id1, type1);
+      this.addState(id2, type2);
+      this.currentDocument.relations.push({
+        id1,
+        id2,
+        title: common.sanitizeText(title, getConfig()),
+      });
     }
-    foundState.classes.push(cssClassName);
-  });
-};
-
-/**
- * Add a style to a state with the given id.
- * @example style stateId fill:#f9f,stroke:#333,stroke-width:4px
- *   where 'style' is the keyword
- *   stateId is the id of a state
- *   the rest of the string is the styleText (all of the attributes to be applied to the state)
- *
- * @param itemId The id of item to apply the style to
- * @param styleText - the text of the attributes for the style
- */
-export const setStyle = function (itemId, styleText) {
-  const item = getState(itemId);
-  if (item !== undefined) {
-    item.styles.push(styleText);
   }
-};
 
-/**
- * Add a text style to a state with the given id
- *
- * @param itemId The id of item to apply the css class to
- * @param cssClassName CSS class name
- */
-export const setTextStyle = function (itemId, cssClassName) {
-  const item = getState(itemId);
-  if (item !== undefined) {
-    item.textStyles.push(cssClassName);
+  addDescription(id, descr) {
+    const theState = this.currentDocument.states.get(id);
+    const _descr = descr.startsWith(':') ? descr.replace(':', '').trim() : descr;
+    theState.descriptions.push(common.sanitizeText(_descr, getConfig()));
   }
-};
 
-const getDirection = () => direction;
-const setDirection = (dir) => {
-  direction = dir;
-};
+  cleanupLabel(label) {
+    if (label.substring(0, 1) === ':') {
+      return label.substr(2).trim();
+    } else {
+      return label.trim();
+    }
+  }
 
-const trimColon = (str) => (str && str[0] === ':' ? str.substr(1).trim() : str.trim());
+  getDividerId() {
+    this.dividerCnt++;
+    return 'divider-id-' + this.dividerCnt;
+  }
 
-export const getData = () => {
-  const config = getConfig();
-  return { nodes, edges, other: {}, config, direction: getDir(getRootDocV2()) };
-};
+  /**
+   * Called when the parser comes across a (style) class definition
+   * @example classDef my-style fill:#f96;
+   *
+   * @param {string} id - the id of this (style) class
+   * @param  {string | null} styleAttributes - the string with 1 or more style attributes (each separated by a comma)
+   */
+  addStyleClass(id, styleAttributes = '') {
+    // create a new style class object with this id
+    if (!this.classes.has(id)) {
+      this.classes.set(id, { id: id, styles: [], textStyles: [] }); // This is a classDef
+    }
+    const foundClass = this.classes.get(id);
+    if (styleAttributes !== undefined && styleAttributes !== null) {
+      styleAttributes.split(STYLECLASS_SEP).forEach((attrib) => {
+        // remove any trailing ;
+        const fixedAttrib = attrib.replace(/([^;]*);/, '$1').trim();
 
-export default {
-  getConfig: () => getConfig().state,
-  getData,
-  addState,
-  clear,
-  getState,
-  getStates,
-  getRelations,
-  getClasses,
-  getDirection,
-  addRelation,
-  getDividerId,
-  setDirection,
-  cleanupLabel,
-  lineType,
-  relationType,
-  logDocuments,
-  getRootDoc,
-  setRootDoc,
-  getRootDocV2,
-  extract,
-  trimColon,
-  getAccTitle,
-  setAccTitle,
-  getAccDescription,
-  setAccDescription,
-  addStyleClass,
-  setCssClass,
-  addDescription,
-  setDiagramTitle,
-  getDiagramTitle,
-};
+        // replace some style keywords
+        if (RegExp(COLOR_KEYWORD).exec(attrib)) {
+          const newStyle1 = fixedAttrib.replace(FILL_KEYWORD, BG_FILL);
+          const newStyle2 = newStyle1.replace(COLOR_KEYWORD, FILL_KEYWORD);
+          foundClass.textStyles.push(newStyle2);
+        }
+        foundClass.styles.push(fixedAttrib);
+      });
+    }
+  }
+
+  /**
+   * Return all of the style classes
+   * @returns {{} | any | classes}
+   */
+  getClasses() {
+    return this.classes;
+  }
+
+  /**
+   * Add a (style) class or css class to a state with the given id.
+   * If the state isn't already in the list of known states, add it.
+   * Might be called by parser when a style class or CSS class should be applied to a state
+   *
+   * @param {string | string[]} itemIds The id or a list of ids of the item(s) to apply the css class to
+   * @param {string} cssClassName CSS class name
+   */
+  setCssClass(itemIds, cssClassName) {
+    itemIds.split(',').forEach((id) => {
+      let foundState = this.getState(id);
+      if (foundState === undefined) {
+        const trimmedId = id.trim();
+        this.addState(trimmedId);
+        foundState = this.getState(trimmedId);
+      }
+      foundState.classes.push(cssClassName);
+    });
+  }
+
+  /**
+   * Add a style to a state with the given id.
+   * @example style stateId fill:#f9f,stroke:#333,stroke-width:4px
+   *   where 'style' is the keyword
+   *   stateId is the id of a state
+   *   the rest of the string is the styleText (all of the attributes to be applied to the state)
+   *
+   * @param itemId The id of item to apply the style to
+   * @param styleText - the text of the attributes for the style
+   */
+  setStyle(itemId, styleText) {
+    const item = this.getState(itemId);
+    if (item !== undefined) {
+      item.styles.push(styleText);
+    }
+  }
+
+  /**
+   * Add a text style to a state with the given id
+   *
+   * @param itemId The id of item to apply the css class to
+   * @param cssClassName CSS class name
+   */
+  setTextStyle(itemId, cssClassName) {
+    const item = this.getState(itemId);
+    if (item !== undefined) {
+      item.textStyles.push(cssClassName);
+    }
+  }
+
+  getDirection() {
+    return this.direction;
+  }
+  setDirection(dir) {
+    this.direction = dir;
+  }
+
+  trimColon(str) {
+    return str && str[0] === ':' ? str.substr(1).trim() : str.trim();
+  }
+
+  getData() {
+    const config = getConfig();
+    return {
+      nodes: this.nodes,
+      edges: this.edges,
+      other: {},
+      config,
+      direction: getDir(this.getRootDocV2()),
+    };
+  }
+
+  getConfig() {
+    return getConfig().state;
+  }
+  getAccTitle = getAccTitle;
+  setAccTitle = setAccTitle;
+  getAccDescription = getAccDescription;
+  setAccDescription = setAccDescription;
+  setDiagramTitle = setDiagramTitle;
+  getDiagramTitle = getDiagramTitle;
+}
diff --git a/packages/mermaid/src/diagrams/state/stateDb.spec.js b/packages/mermaid/src/diagrams/state/stateDb.spec.js
index ff0581200..73f1a4be9 100644
--- a/packages/mermaid/src/diagrams/state/stateDb.spec.js
+++ b/packages/mermaid/src/diagrams/state/stateDb.spec.js
@@ -1,8 +1,9 @@
-import stateDb from './stateDb.js';
+import { StateDB } from './stateDb.js';
 
 describe('State Diagram stateDb', () => {
+  let stateDb;
   beforeEach(() => {
-    stateDb.clear();
+    stateDb = new StateDB();
   });
 
   describe('addStyleClass', () => {
@@ -20,8 +21,9 @@ describe('State Diagram stateDb', () => {
   });
 
   describe('addDescription to a state', () => {
+    let stateDb;
     beforeEach(() => {
-      stateDb.clear();
+      stateDb = new StateDB();
       stateDb.addState('state1');
     });
 
@@ -73,3 +75,25 @@ describe('State Diagram stateDb', () => {
     });
   });
 });
+
+describe('state db class', () => {
+  let stateDb;
+  beforeEach(() => {
+    stateDb = new StateDB();
+  });
+  // This is to ensure that functions used in state JISON are exposed as function from StateDb
+  it('should have functions used in flow JISON as own property', () => {
+    const functionsUsedInParser = [
+      'setRootDoc',
+      'trimColon',
+      'getDividerId',
+      'setAccTitle',
+      'setAccDescription',
+      'setDirection',
+    ];
+
+    for (const fun of functionsUsedInParser) {
+      expect(Object.hasOwn(stateDb, fun)).toBe(true);
+    }
+  });
+});
diff --git a/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js b/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js
index 53063f41a..d1edc5b40 100644
--- a/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js
+++ b/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js
@@ -1,11 +1,13 @@
 import { parser } from './parser/stateDiagram.jison';
-import stateDb from './stateDb.js';
+import { StateDB } from './stateDb.js';
 import stateDiagram from './parser/stateDiagram.jison';
 
 describe('state diagram V2, ', function () {
   // TODO - these examples should be put into ./parser/stateDiagram.spec.js
   describe('when parsing an info graph it', function () {
+    let stateDb;
     beforeEach(function () {
+      stateDb = new StateDB();
       parser.yy = stateDb;
       stateDiagram.parser.yy = stateDb;
       stateDiagram.parser.yy.clear();
diff --git a/packages/mermaid/src/diagrams/state/stateDiagram-v2.ts b/packages/mermaid/src/diagrams/state/stateDiagram-v2.ts
index 8fd98e930..f7bc716c6 100644
--- a/packages/mermaid/src/diagrams/state/stateDiagram-v2.ts
+++ b/packages/mermaid/src/diagrams/state/stateDiagram-v2.ts
@@ -1,13 +1,15 @@
 import type { DiagramDefinition } from '../../diagram-api/types.js';
 // @ts-ignore: JISON doesn't support types
 import parser from './parser/stateDiagram.jison';
-import db from './stateDb.js';
+import { StateDB } from './stateDb.js';
 import styles from './styles.js';
 import renderer from './stateRenderer-v3-unified.js';
 
 export const diagram: DiagramDefinition = {
   parser,
-  db,
+  get db() {
+    return new StateDB();
+  },
   renderer,
   styles,
   init: (cnf) => {
diff --git a/packages/mermaid/src/diagrams/state/stateDiagram.spec.js b/packages/mermaid/src/diagrams/state/stateDiagram.spec.js
index 7fcf4d0a6..8175ef041 100644
--- a/packages/mermaid/src/diagrams/state/stateDiagram.spec.js
+++ b/packages/mermaid/src/diagrams/state/stateDiagram.spec.js
@@ -1,9 +1,11 @@
 import { parser } from './parser/stateDiagram.jison';
-import stateDb from './stateDb.js';
+import { StateDB } from './stateDb.js';
 
 describe('state diagram, ', function () {
   describe('when parsing an info graph it', function () {
+    let stateDb;
     beforeEach(function () {
+      stateDb = new StateDB();
       parser.yy = stateDb;
     });
 
diff --git a/packages/mermaid/src/diagrams/state/stateDiagram.ts b/packages/mermaid/src/diagrams/state/stateDiagram.ts
index bd8383287..a6f9d7c63 100644
--- a/packages/mermaid/src/diagrams/state/stateDiagram.ts
+++ b/packages/mermaid/src/diagrams/state/stateDiagram.ts
@@ -1,13 +1,15 @@
 import type { DiagramDefinition } from '../../diagram-api/types.js';
 // @ts-ignore: JISON doesn't support types
 import parser from './parser/stateDiagram.jison';
-import db from './stateDb.js';
+import { StateDB } from './stateDb.js';
 import styles from './styles.js';
 import renderer from './stateRenderer.js';
 
 export const diagram: DiagramDefinition = {
   parser,
-  db,
+  get db() {
+    return new StateDB();
+  },
   renderer,
   styles,
   init: (cnf) => {
diff --git a/packages/mermaid/src/mermaidAPI.spec.ts b/packages/mermaid/src/mermaidAPI.spec.ts
index e51d419d3..e1b13dbd7 100644
--- a/packages/mermaid/src/mermaidAPI.spec.ts
+++ b/packages/mermaid/src/mermaidAPI.spec.ts
@@ -71,6 +71,7 @@ import { decodeEntities, encodeEntities } from './utils.js';
 import { toBase64 } from './utils/base64.js';
 import { ClassDB } from './diagrams/class/classDb.js';
 import { FlowDB } from './diagrams/flowchart/flowDb.js';
+import { StateDB } from './diagrams/state/stateDb.js';
 
 /**
  * @see https://vitest.dev/guide/mocking.html Mock part of a module
@@ -836,6 +837,31 @@ graph TD;A--x|text including URL space|B;`)
     });
 
     it('should not modify db when rendering different diagrams', async () => {
+      const stateDiagram1 = await mermaidAPI.getDiagramFromText(
+        `stateDiagram
+            direction LR
+            [*] --> Still
+            Still --> [*]
+            Still --> Moving
+            Moving --> Still
+            Moving --> Crash
+            Crash --> [*]`
+      );
+      const stateDiagram2 = await mermaidAPI.getDiagramFromText(
+        `stateDiagram
+          direction TB
+          [*] --> Still
+          Still --> [*]
+          Still --> Moving
+          Moving --> Still
+          Moving --> Crash
+          Crash --> [*]`
+      );
+      expect(stateDiagram1.db).not.toBe(stateDiagram2.db);
+      assert(stateDiagram1.db instanceof StateDB);
+      assert(stateDiagram2.db instanceof StateDB);
+      expect(stateDiagram1.db.getDirection()).not.toEqual(stateDiagram2.db.getDirection());
+
       const flowDiagram1 = await mermaidAPI.getDiagramFromText(
         `flowchart LR
       A -- text --> B -- text2 --> C`

From 56d66cdabcbce7dafa2ba3cc7044f4d6c1cf2d73 Mon Sep 17 00:00:00 2001
From: saurabhg772244 
Date: Tue, 4 Feb 2025 16:45:45 +0530
Subject: [PATCH 162/230] Fix issue where data was not being set in the db
 after parsing.

---
 packages/mermaid/src/diagrams/state/stateDb.js                  | 1 +
 packages/mermaid/src/diagrams/state/stateRenderer-v3-unified.ts | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/mermaid/src/diagrams/state/stateDb.js b/packages/mermaid/src/diagrams/state/stateDb.js
index 2da0719ca..50785587e 100644
--- a/packages/mermaid/src/diagrams/state/stateDb.js
+++ b/packages/mermaid/src/diagrams/state/stateDb.js
@@ -130,6 +130,7 @@ export class StateDB {
     log.info('Setting root doc', o);
     // rootDoc = { id: 'root', doc: o };
     this.rootDoc = o;
+    this.extract(o);
   }
 
   getRootDoc() {
diff --git a/packages/mermaid/src/diagrams/state/stateRenderer-v3-unified.ts b/packages/mermaid/src/diagrams/state/stateRenderer-v3-unified.ts
index 109417c03..2998c8173 100644
--- a/packages/mermaid/src/diagrams/state/stateRenderer-v3-unified.ts
+++ b/packages/mermaid/src/diagrams/state/stateRenderer-v3-unified.ts
@@ -36,7 +36,6 @@ export const getClasses = function (
   text: string,
   diagramObj: any
 ): Map {
-  diagramObj.db.extract(diagramObj.db.getRootDocV2());
   return diagramObj.db.getClasses();
 };
 

From 9cad3c7aea3bbbc61495b23225ccff76d312783f Mon Sep 17 00:00:00 2001
From: saurabhg772244 
Date: Tue, 4 Feb 2025 17:09:09 +0530
Subject: [PATCH 163/230] added changeset

---
 .changeset/grumpy-cheetahs-deliver.md | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 .changeset/grumpy-cheetahs-deliver.md

diff --git a/.changeset/grumpy-cheetahs-deliver.md b/.changeset/grumpy-cheetahs-deliver.md
new file mode 100644
index 000000000..4213083f2
--- /dev/null
+++ b/.changeset/grumpy-cheetahs-deliver.md
@@ -0,0 +1,5 @@
+---
+'mermaid': patch
+---
+
+`mermaidAPI.getDiagramFromText()` now returns a new different db for each state diagram

From 4dbaa2b5d6d3ffaa0dbbc7e78b4f3888d0850db8 Mon Sep 17 00:00:00 2001
From: saurabhg772244 
Date: Tue, 4 Feb 2025 19:18:46 +0530
Subject: [PATCH 164/230] code refactor

---
 .../diagrams/state/parser/state-parser.spec.js   |  8 --------
 .../diagrams/state/parser/state-style.spec.js    | 16 +---------------
 packages/mermaid/src/diagrams/state/stateDb.js   |  2 +-
 .../src/diagrams/state/stateDiagram-v2.spec.js   |  5 +----
 4 files changed, 3 insertions(+), 28 deletions(-)

diff --git a/packages/mermaid/src/diagrams/state/parser/state-parser.spec.js b/packages/mermaid/src/diagrams/state/parser/state-parser.spec.js
index bb5345996..1ef6912d9 100644
--- a/packages/mermaid/src/diagrams/state/parser/state-parser.spec.js
+++ b/packages/mermaid/src/diagrams/state/parser/state-parser.spec.js
@@ -20,7 +20,6 @@ describe('state parser can parse...', () => {
         const diagramText = `stateDiagram-v2
         state "Small State 1" as namedState1`;
         stateDiagram.parser.parse(diagramText);
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const states = stateDiagram.parser.yy.getStates();
         expect(states.get('namedState1')).not.toBeUndefined();
@@ -33,7 +32,6 @@ describe('state parser can parse...', () => {
         const diagramText = `stateDiagram-v2
         namedState1 : Small State 1`;
         stateDiagram.parser.parse(diagramText);
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const states = stateDiagram.parser.yy.getStates();
         expect(states.get('namedState1')).not.toBeUndefined();
@@ -44,7 +42,6 @@ describe('state parser can parse...', () => {
         const diagramText = `stateDiagram-v2
         namedState1:Small State 1`;
         stateDiagram.parser.parse(diagramText);
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const states = stateDiagram.parser.yy.getStates();
         expect(states.get('namedState1')).not.toBeUndefined();
@@ -62,7 +59,6 @@ describe('state parser can parse...', () => {
       state assemblies
       `;
       stateDiagram.parser.parse(diagramText);
-      stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
       const states = stateDiagram.parser.yy.getStates();
       expect(states.get('assemble')).not.toBeUndefined();
       expect(states.get('assemblies')).not.toBeUndefined();
@@ -73,7 +69,6 @@ describe('state parser can parse...', () => {
       state "as" as as
       `;
       stateDiagram.parser.parse(diagramText);
-      stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
       const states = stateDiagram.parser.yy.getStates();
       expect(states.get('as')).not.toBeUndefined();
       expect(states.get('as').descriptions.join(' ')).toEqual('as');
@@ -98,7 +93,6 @@ describe('state parser can parse...', () => {
         namedState2 --> bigState2: should point to \\nbigState2 container`;
 
       stateDiagram.parser.parse(diagramText);
-      stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
       const states = stateDiagram.parser.yy.getStates();
       expect(states.get('namedState1')).not.toBeUndefined();
@@ -122,7 +116,6 @@ describe('state parser can parse...', () => {
             inner1 --> inner2
         }`;
       stateDiagram.parser.parse(diagramText);
-      stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
       const states = stateDiagram.parser.yy.getStates();
       expect(states.get('bigState1')).not.toBeUndefined();
@@ -139,7 +132,6 @@ describe('state parser can parse...', () => {
 stateDiagram-v2
 [*] --> ${prop}
 ${prop} --> [*]`);
-      stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
       const states = stateDiagram.parser.yy.getStates();
       expect(states.get(prop)).not.toBeUndefined();
     });
diff --git a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js
index c37bed3c7..3f0f6ab00 100644
--- a/packages/mermaid/src/diagrams/state/parser/state-style.spec.js
+++ b/packages/mermaid/src/diagrams/state/parser/state-style.spec.js
@@ -1,6 +1,6 @@
+import { setConfig } from '../../../config.js';
 import { StateDB } from '../stateDb.js';
 import stateDiagram from './stateDiagram.jison';
-import { setConfig } from '../../../config.js';
 
 setConfig({
   securityLevel: 'strict',
@@ -18,7 +18,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
     describe('defining (classDef)', () => {
       it('has "classDef" as a keyword, an id, and can set a css style attribute', function () {
         stateDiagram.parser.parse('stateDiagram-v2\n classDef exampleClass background:#bbb;');
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const styleClasses = stateDb.getClasses();
         expect(styleClasses.get('exampleClass').styles.length).toEqual(1);
@@ -29,7 +28,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
         stateDiagram.parser.parse(
           'stateDiagram-v2\n classDef exampleClass background:#bbb, font-weight:bold, font-style:italic;'
         );
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const styleClasses = stateDb.getClasses();
         expect(styleClasses.get('exampleClass').styles.length).toEqual(3);
@@ -43,7 +41,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
         stateDiagram.parser.parse(
           'stateDiagram-v2\n classDef exampleStyleClass background:#bbb,border:1.5px solid red;'
         );
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const classes = stateDiagram.parser.yy.getClasses();
         expect(classes.get('exampleStyleClass').styles.length).toBe(2);
@@ -55,7 +52,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
         stateDiagram.parser.parse(
           'stateDiagram-v2\n classDef exampleStyleClass background:  #bbb,border:1.5px solid red;'
         );
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const classes = stateDiagram.parser.yy.getClasses();
         expect(classes.get('exampleStyleClass').styles.length).toBe(2);
@@ -67,7 +63,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
         stateDiagram.parser.parse(
           'stateDiagram-v2\n classDef __proto__ background:#bbb,border:1.5px solid red;\n classDef constructor background:#bbb,border:1.5px solid red;'
         );
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
         const classes = stateDiagram.parser.yy.getClasses();
         expect(classes.get('__proto__').styles.length).toBe(2);
         expect(classes.get('constructor').styles.length).toBe(2);
@@ -83,7 +78,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
         diagram += 'class a exampleStyleClass';
 
         stateDiagram.parser.parse(diagram);
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const classes = stateDb.getClasses();
         expect(classes.get('exampleStyleClass').styles.length).toEqual(2);
@@ -104,7 +98,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
         diagram += 'class a_a exampleStyleClass';
 
         stateDiagram.parser.parse(diagram);
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const classes = stateDiagram.parser.yy.getClasses();
         expect(classes.get('exampleStyleClass').styles.length).toBe(2);
@@ -124,7 +117,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
           diagram += 'a --> b:::exampleStyleClass' + '\n';
 
           stateDiagram.parser.parse(diagram);
-          stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
           const states = stateDiagram.parser.yy.getStates();
           const classes = stateDiagram.parser.yy.getClasses();
@@ -163,7 +155,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
           diagram += 'class a,b exampleStyleClass';
 
           stateDiagram.parser.parse(diagram);
-          stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
           let classes = stateDiagram.parser.yy.getClasses();
           let states = stateDiagram.parser.yy.getStates();
 
@@ -182,7 +173,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
           diagram += 'class a,b,c, d, e exampleStyleClass';
 
           stateDiagram.parser.parse(diagram);
-          stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
           const classes = stateDiagram.parser.yy.getClasses();
           const states = stateDiagram.parser.yy.getStates();
 
@@ -210,7 +200,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
           diagram += '}\n';
 
           stateDiagram.parser.parse(diagram);
-          stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
           const states = stateDiagram.parser.yy.getStates();
 
@@ -226,7 +215,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
         stateDiagram.parser.parse(`stateDiagram-v2
         id1
         style id1 background:#bbb`);
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
         const data4Layout = stateDiagram.parser.yy.getData();
 
         expect(data4Layout.nodes[0].cssStyles).toEqual(['background:#bbb']);
@@ -236,7 +224,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
         id1
         id2
         style id1,id2 background:#bbb`);
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
         const data4Layout = stateDiagram.parser.yy.getData();
 
         expect(data4Layout.nodes[0].cssStyles).toEqual(['background:#bbb']);
@@ -249,7 +236,6 @@ describe('ClassDefs and classes when parsing a State diagram', () => {
         id2
         style id1,id2 background:#bbb, font-weight:bold, font-style:italic;`);
 
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
         const data4Layout = stateDiagram.parser.yy.getData();
 
         expect(data4Layout.nodes[0].cssStyles).toEqual([
diff --git a/packages/mermaid/src/diagrams/state/stateDb.js b/packages/mermaid/src/diagrams/state/stateDb.js
index 50785587e..cc44659eb 100644
--- a/packages/mermaid/src/diagrams/state/stateDb.js
+++ b/packages/mermaid/src/diagrams/state/stateDb.js
@@ -204,7 +204,7 @@ export class StateDB {
    * refer to the fork as a whole (document).
    * See the parser grammar:  the definition of a document is a document then a 'line', where a line can be a statement.
    * This will push the statement into the list of statements for the current document.
-   *
+   * @private
    * @param _doc
    */
   extract(_doc) {
diff --git a/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js b/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js
index d1edc5b40..ce20879c3 100644
--- a/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js
+++ b/packages/mermaid/src/diagrams/state/stateDiagram-v2.spec.js
@@ -1,6 +1,5 @@
-import { parser } from './parser/stateDiagram.jison';
+import stateDiagram, { parser } from './parser/stateDiagram.jison';
 import { StateDB } from './stateDb.js';
-import stateDiagram from './parser/stateDiagram.jison';
 
 describe('state diagram V2, ', function () {
   // TODO - these examples should be put into ./parser/stateDiagram.spec.js
@@ -129,7 +128,6 @@ describe('state diagram V2, ', function () {
         `;
 
         stateDiagram.parser.parse(diagram);
-        stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
         const rels = stateDb.getRelations();
         const rel_1_2 = rels.find((rel) => rel.id1 === 'State1' && rel.id2 === 'State2');
@@ -404,7 +402,6 @@ describe('state diagram V2, ', function () {
         `;
 
       stateDiagram.parser.parse(diagram);
-      stateDiagram.parser.yy.extract(stateDiagram.parser.yy.getRootDocV2());
 
       const states = stateDb.getStates();
       expect(states.get('Active').doc[0].id).toEqual('Idle');

From 4a19740aea90a20447500e1dfab7a3dae8a26e4f Mon Sep 17 00:00:00 2001
From: "Radmila M." 
Date: Mon, 10 Feb 2025 13:24:38 +0300
Subject: [PATCH 165/230] Update tutorials.md

---
 .../mermaid/src/docs/ecosystem/tutorials.md   | 35 +++++++++++++------
 1 file changed, 24 insertions(+), 11 deletions(-)

diff --git a/packages/mermaid/src/docs/ecosystem/tutorials.md b/packages/mermaid/src/docs/ecosystem/tutorials.md
index 7258361bf..82c272a4e 100644
--- a/packages/mermaid/src/docs/ecosystem/tutorials.md
+++ b/packages/mermaid/src/docs/ecosystem/tutorials.md
@@ -42,16 +42,7 @@ https://codepen.io/Ryuno-Ki/pen/LNxwgR
 
 ## Mermaid in open source docs
 
-[K8s.io Diagram Guide](https://kubernetes.io/docs/contribute/style/diagram-guide/)
-
-[K8s.dev blog: Improve your documentation with Mermaid.js diagrams](https://www.kubernetes.dev/blog/2021/12/01/improve-your-documentation-with-mermaid.js-diagrams/)
-
-## Jupyter Integration with mermaid-js
-
-Here's an example of Python integration with mermaid-js which uses the mermaid.ink service, that displays the graph in a Jupyter notebook.
-
-```python
-import base64
+[K8s.io Diagram Guide](https://kubernetes.io/docs/contribute/style/diagram-guide/)import base64
 from IPython.display import Image, display
 import matplotlib.pyplot as plt
 
@@ -61,6 +52,28 @@ def mm(graph):
     base64_string = base64_bytes.decode("ascii")
     display(Image(url="https://mermaid.ink/img/" + base64_string))
 
+[K8s.dev blog: Improve your documentation with Mermaid.js diagrams](https://www.kubernetes.dev/blog/2021/12/01/improve-your-documentation-with-mermaid.js-diagrams/)
+
+## Jupyter / Python Integration with mermaid-js
+
+Here's an example of Python integration with mermaid-js which uses the mermaid.ink service, that displays the graph in a Jupyter notebook and save it as *.png* image with the stated resolution (in this example, `dpi=1200`).
+
+```python
+import base64
+import io, requests
+from IPython.display import Image, display
+from PIL import Image as im
+import matplotlib.pyplot as plt
+
+def mm(graph):
+    graphbytes = graph.encode("utf8")
+    base64_bytes = base64.urlsafe_b64encode(graphbytes)
+    base64_string = base64_bytes.decode("ascii")
+    img = im.open(io.BytesIO(requests.get('https://mermaid.ink/img/' + base64_string).content))
+    plt.imshow(img)
+    plt.axis('off') # allow to hide axis
+    plt.savefig('image.png', dpi=1200)
+
 mm("""
 graph LR;
     A--> B & C & D;
@@ -73,6 +86,6 @@ graph LR;
 
 **Output**
 
-![Example graph of the Python integration](img/python-mermaid-integration.png)
+![Example graph of the Python integration](img/python-mermaid-integration-updated.png)
 
 

From a79c0f4c002198faee2eb585dab2461d34c8a174 Mon Sep 17 00:00:00 2001
From: "Radmila M." 
Date: Mon, 10 Feb 2025 14:01:09 +0300
Subject: [PATCH 166/230] Update tutorials.md

---
 .../mermaid/src/docs/ecosystem/tutorials.md   | 22 ++++++-------------
 1 file changed, 7 insertions(+), 15 deletions(-)

diff --git a/packages/mermaid/src/docs/ecosystem/tutorials.md b/packages/mermaid/src/docs/ecosystem/tutorials.md
index 82c272a4e..146ffb282 100644
--- a/packages/mermaid/src/docs/ecosystem/tutorials.md
+++ b/packages/mermaid/src/docs/ecosystem/tutorials.md
@@ -42,15 +42,7 @@ https://codepen.io/Ryuno-Ki/pen/LNxwgR
 
 ## Mermaid in open source docs
 
-[K8s.io Diagram Guide](https://kubernetes.io/docs/contribute/style/diagram-guide/)import base64
-from IPython.display import Image, display
-import matplotlib.pyplot as plt
-
-def mm(graph):
-    graphbytes = graph.encode("utf8")
-    base64_bytes = base64.urlsafe_b64encode(graphbytes)
-    base64_string = base64_bytes.decode("ascii")
-    display(Image(url="https://mermaid.ink/img/" + base64_string))
+[K8s.io Diagram Guide](https://kubernetes.io/docs/contribute/style/diagram-guide/)
 
 [K8s.dev blog: Improve your documentation with Mermaid.js diagrams](https://www.kubernetes.dev/blog/2021/12/01/improve-your-documentation-with-mermaid.js-diagrams/)
 
@@ -76,16 +68,16 @@ def mm(graph):
 
 mm("""
 graph LR;
-    A--> B & C & D;
-    B--> A & E;
-    C--> A & E;
-    D--> A & E;
-    E--> B & C & D;
+    A--> B & C & D
+    B--> A & E
+    C--> A & E
+    D--> A & E
+    E--> B & C & D
 """)
 ```
 
 **Output**
 
-![Example graph of the Python integration](img/python-mermaid-integration-updated.png)
+![Example graph of the Python integration](img/python-mermaid-integration.png)
 
 

From d07f85e6ac4cbd4a75ddfad395884678d5f8c2f8 Mon Sep 17 00:00:00 2001
From: "Radmila M." 
Date: Mon, 10 Feb 2025 14:05:55 +0300
Subject: [PATCH 167/230] Add files via upload

---
 .../img/python-mermaid-integration-updated.png | Bin 0 -> 261810 bytes
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 packages/mermaid/src/docs/ecosystem/img/python-mermaid-integration-updated.png

diff --git a/packages/mermaid/src/docs/ecosystem/img/python-mermaid-integration-updated.png b/packages/mermaid/src/docs/ecosystem/img/python-mermaid-integration-updated.png
new file mode 100644
index 0000000000000000000000000000000000000000..37ad58420671462588b204632c0bdcbc3d2aa85c
GIT binary patch
literal 261810
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
QGIAIFEykNOH##2uA7wj78vp
Date: Wed, 12 Feb 2025 15:54:51 +0530
Subject: [PATCH 168/230] Refactored arrow function to regular function and
 assigned unique ID to messages during parsing.

---
 .../src/diagrams/sequence/sequenceDb.ts       | 300 +++++++++++-------
 .../src/diagrams/sequence/sequenceDiagram.ts  |  19 +-
 .../src/diagrams/sequence/sequenceRenderer.ts |   1 -
 .../mermaid/src/diagrams/sequence/types.ts    |   1 +
 4 files changed, 190 insertions(+), 131 deletions(-)

diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts
index e71177b1f..ba5d464c5 100644
--- a/packages/mermaid/src/diagrams/sequence/sequenceDb.ts
+++ b/packages/mermaid/src/diagrams/sequence/sequenceDb.ts
@@ -45,7 +45,63 @@ export class SequenceDB implements DiagramDB {
     lastDestroyed: undefined,
   }));
 
-  public addBox = (data: { text: string; color: string; wrap: boolean }) => {
+  constructor() {
+    // Needed for JISON since it only supports direct properties
+    this.apply = this.apply.bind(this);
+    this.parseBoxData = this.parseBoxData.bind(this);
+    this.parseMessage = this.parseMessage.bind(this);
+    this.setWrap(getConfig().wrap);
+
+    this.clear();
+
+    this.LINETYPE = {
+      SOLID: 0,
+      DOTTED: 1,
+      NOTE: 2,
+      SOLID_CROSS: 3,
+      DOTTED_CROSS: 4,
+      SOLID_OPEN: 5,
+      DOTTED_OPEN: 6,
+      LOOP_START: 10,
+      LOOP_END: 11,
+      ALT_START: 12,
+      ALT_ELSE: 13,
+      ALT_END: 14,
+      OPT_START: 15,
+      OPT_END: 16,
+      ACTIVE_START: 17,
+      ACTIVE_END: 18,
+      PAR_START: 19,
+      PAR_AND: 20,
+      PAR_END: 21,
+      RECT_START: 22,
+      RECT_END: 23,
+      SOLID_POINT: 24,
+      DOTTED_POINT: 25,
+      AUTONUMBER: 26,
+      CRITICAL_START: 27,
+      CRITICAL_OPTION: 28,
+      CRITICAL_END: 29,
+      BREAK_START: 30,
+      BREAK_END: 31,
+      PAR_OVER_START: 32,
+      BIDIRECTIONAL_SOLID: 33,
+      BIDIRECTIONAL_DOTTED: 34,
+    };
+
+    this.ARROWTYPE = {
+      FILLED: 0,
+      OPEN: 1,
+    };
+
+    this.PLACEMENT = {
+      LEFTOF: 0,
+      RIGHTOF: 1,
+      OVER: 2,
+    };
+  }
+
+  public addBox(data: { text: string; color: string; wrap: boolean }) {
     this.state.records.boxes.push({
       name: data.text,
       wrap: data.wrap ?? this.autoWrap(),
@@ -53,14 +109,14 @@ export class SequenceDB implements DiagramDB {
       actorKeys: [],
     });
     this.state.records.currentBox = this.state.records.boxes.slice(-1)[0];
-  };
+  }
 
-  public addActor = (
+  public addActor(
     id: string,
     name: string,
     description: { text: string; wrap?: boolean | null; type: string },
     type: string
-  ) => {
+  ) {
     let assignedBox = this.state.records.currentBox;
     const old = this.state.records.actors.get(id);
     if (old) {
@@ -112,9 +168,9 @@ export class SequenceDB implements DiagramDB {
       this.state.records.currentBox.actorKeys.push(id);
     }
     this.state.records.prevActor = id;
-  };
+  }
 
-  private readonly activationCount = (part: string) => {
+  private activationCount(part: string) {
     let i;
     let count = 0;
     if (!part) {
@@ -135,30 +191,31 @@ export class SequenceDB implements DiagramDB {
       }
     }
     return count;
-  };
+  }
 
-  public addMessage = (
+  public addMessage(
     idFrom: Message['from'],
     idTo: Message['to'],
     message: { text: string; wrap?: boolean },
     answer: Message['answer']
-  ) => {
+  ) {
     this.state.records.messages.push({
+      id: this.state.records.messages.length.toString(),
       from: idFrom,
       to: idTo,
       message: message.text,
       wrap: message.wrap ?? this.autoWrap(),
       answer: answer,
     });
-  };
+  }
 
-  public addSignal = (
+  public addSignal(
     idFrom?: Message['from'],
     idTo?: Message['to'],
     message?: { text: string; wrap: boolean },
     messageType?: number,
     activate = false
-  ) => {
+  ) {
     if (messageType === this.LINETYPE.ACTIVE_END) {
       const cnt = this.activationCount(idFrom ?? '');
       if (cnt < 1) {
@@ -177,6 +234,7 @@ export class SequenceDB implements DiagramDB {
       }
     }
     this.state.records.messages.push({
+      id: this.state.records.messages.length.toString(),
       from: idFrom,
       to: idTo,
       message: message?.text ?? '',
@@ -185,52 +243,54 @@ export class SequenceDB implements DiagramDB {
       activate,
     });
     return true;
-  };
+  }
 
-  public hasAtLeastOneBox = () => {
+  public hasAtLeastOneBox() {
     return this.state.records.boxes.length > 0;
-  };
+  }
 
-  public hasAtLeastOneBoxWithTitle = () => {
+  public hasAtLeastOneBoxWithTitle() {
     return this.state.records.boxes.some((b) => b.name);
-  };
+  }
 
-  public getMessages = () => {
+  public getMessages() {
     return this.state.records.messages;
-  };
+  }
 
-  public getBoxes = () => {
+  public getBoxes() {
     return this.state.records.boxes;
-  };
-  public getActors = () => {
+  }
+  public getActors() {
     return this.state.records.actors;
-  };
-  public getCreatedActors = () => {
+  }
+  public getCreatedActors() {
     return this.state.records.createdActors;
-  };
-  public getDestroyedActors = () => {
+  }
+  public getDestroyedActors() {
     return this.state.records.destroyedActors;
-  };
-  public getActor = (id: string) => {
+  }
+  public getActor(id: string) {
     // TODO: do we ever use this function in a way that it might return undefined?
     return this.state.records.actors.get(id)!;
-  };
-  public getActorKeys = () => {
+  }
+  public getActorKeys() {
     return [...this.state.records.actors.keys()];
-  };
-  public enableSequenceNumbers = () => {
+  }
+  public enableSequenceNumbers() {
     this.state.records.sequenceNumbersEnabled = true;
-  };
-  public disableSequenceNumbers = () => {
+  }
+  public disableSequenceNumbers() {
     this.state.records.sequenceNumbersEnabled = false;
-  };
-  public showSequenceNumbers = () => this.state.records.sequenceNumbersEnabled;
+  }
+  public showSequenceNumbers() {
+    return this.state.records.sequenceNumbersEnabled;
+  }
 
-  public setWrap = (wrapSetting?: boolean) => {
+  public setWrap(wrapSetting?: boolean) {
     this.state.records.wrapEnabled = wrapSetting;
-  };
+  }
 
-  private readonly extractWrap = (text?: string): { cleanedText?: string; wrap?: boolean } => {
+  private extractWrap(text?: string): { cleanedText?: string; wrap?: boolean } {
     if (text === undefined) {
       return {};
     }
@@ -239,23 +299,23 @@ export class SequenceDB implements DiagramDB {
       /^:?wrap:/.exec(text) !== null ? true : /^:?nowrap:/.exec(text) !== null ? false : undefined;
     const cleanedText = (wrap === undefined ? text : text.replace(/^:?(?:no)?wrap:/, '')).trim();
     return { cleanedText, wrap };
-  };
+  }
 
-  public autoWrap = () => {
+  public autoWrap() {
     // if setWrap has been called, use that value, otherwise use the value from the config
     // TODO: refactor, always use the config value let setWrap update the config value
     if (this.state.records.wrapEnabled !== undefined) {
       return this.state.records.wrapEnabled;
     }
     return getConfig().sequence?.wrap ?? false;
-  };
+  }
 
-  public clear = () => {
+  public clear() {
     this.state.reset();
     commonClear();
-  };
+  }
 
-  public parseMessage = (str: string) => {
+  public parseMessage(str: string) {
     const trimmedStr = str.trim();
     const { wrap, cleanedText } = this.extractWrap(trimmedStr);
     const message = {
@@ -264,12 +324,12 @@ export class SequenceDB implements DiagramDB {
     };
     log.debug(`parseMessage: ${JSON.stringify(message)}`);
     return message;
-  };
+  }
 
   // We expect the box statement to be color first then description
   // The color can be rgb,rgba,hsl,hsla, or css code names  #hex codes are not supported for now because of the way the char # is handled
   // We extract first segment as color, the rest of the line is considered as text
-  public parseBoxData = (str: string) => {
+  public parseBoxData(str: string) {
     const match = /^((?:rgba?|hsla?)\s*\(.*\)|\w*)(.*)$/.exec(str);
     let color = match?.[1] ? match[1].trim() : 'transparent';
     let title = match?.[2] ? match[2].trim() : undefined;
@@ -294,59 +354,59 @@ export class SequenceDB implements DiagramDB {
       color,
       wrap,
     };
+  }
+
+  public LINETYPE: {
+    SOLID: 0;
+    DOTTED: 1;
+    NOTE: 2;
+    SOLID_CROSS: 3;
+    DOTTED_CROSS: 4;
+    SOLID_OPEN: 5;
+    DOTTED_OPEN: 6;
+    LOOP_START: 10;
+    LOOP_END: 11;
+    ALT_START: 12;
+    ALT_ELSE: 13;
+    ALT_END: 14;
+    OPT_START: 15;
+    OPT_END: 16;
+    ACTIVE_START: 17;
+    ACTIVE_END: 18;
+    PAR_START: 19;
+    PAR_AND: 20;
+    PAR_END: 21;
+    RECT_START: 22;
+    RECT_END: 23;
+    SOLID_POINT: 24;
+    DOTTED_POINT: 25;
+    AUTONUMBER: 26;
+    CRITICAL_START: 27;
+    CRITICAL_OPTION: 28;
+    CRITICAL_END: 29;
+    BREAK_START: 30;
+    BREAK_END: 31;
+    PAR_OVER_START: 32;
+    BIDIRECTIONAL_SOLID: 33;
+    BIDIRECTIONAL_DOTTED: 34;
   };
 
-  public LINETYPE = {
-    SOLID: 0,
-    DOTTED: 1,
-    NOTE: 2,
-    SOLID_CROSS: 3,
-    DOTTED_CROSS: 4,
-    SOLID_OPEN: 5,
-    DOTTED_OPEN: 6,
-    LOOP_START: 10,
-    LOOP_END: 11,
-    ALT_START: 12,
-    ALT_ELSE: 13,
-    ALT_END: 14,
-    OPT_START: 15,
-    OPT_END: 16,
-    ACTIVE_START: 17,
-    ACTIVE_END: 18,
-    PAR_START: 19,
-    PAR_AND: 20,
-    PAR_END: 21,
-    RECT_START: 22,
-    RECT_END: 23,
-    SOLID_POINT: 24,
-    DOTTED_POINT: 25,
-    AUTONUMBER: 26,
-    CRITICAL_START: 27,
-    CRITICAL_OPTION: 28,
-    CRITICAL_END: 29,
-    BREAK_START: 30,
-    BREAK_END: 31,
-    PAR_OVER_START: 32,
-    BIDIRECTIONAL_SOLID: 33,
-    BIDIRECTIONAL_DOTTED: 34,
+  public ARROWTYPE: {
+    FILLED: 0;
+    OPEN: 1;
   };
 
-  public ARROWTYPE = {
-    FILLED: 0,
-    OPEN: 1,
+  public PLACEMENT: {
+    LEFTOF: 0;
+    RIGHTOF: 1;
+    OVER: 2;
   };
 
-  public PLACEMENT = {
-    LEFTOF: 0,
-    RIGHTOF: 1,
-    OVER: 2,
-  };
-
-  public addNote = (
+  public addNote(
     actor: { actor: string },
     placement: Message['placement'],
     message: { text: string; wrap?: boolean }
-  ) => {
+  ) {
     const note: Note = {
       actor: actor,
       placement: placement,
@@ -359,6 +419,7 @@ export class SequenceDB implements DiagramDB {
     const actors = [].concat(actor, actor);
     this.state.records.notes.push(note);
     this.state.records.messages.push({
+      id: this.state.records.messages.length.toString(),
       from: actors[0],
       to: actors[1],
       message: message.text,
@@ -366,9 +427,9 @@ export class SequenceDB implements DiagramDB {
       type: this.LINETYPE.NOTE,
       placement: placement,
     });
-  };
+  }
 
-  public addLinks = (actorId: string, text: { text: string }) => {
+  public addLinks(actorId: string, text: { text: string }) {
     // find the actor
     const actor = this.getActor(actorId);
     // JSON.parse the text
@@ -382,9 +443,9 @@ export class SequenceDB implements DiagramDB {
     } catch (e) {
       log.error('error while parsing actor link text', e);
     }
-  };
+  }
 
-  public addALink = (actorId: string, text: { text: string }) => {
+  public addALink(actorId: string, text: { text: string }) {
     // find the actor
     const actor = this.getActor(actorId);
     try {
@@ -402,13 +463,9 @@ export class SequenceDB implements DiagramDB {
     } catch (e) {
       log.error('error while parsing actor link text', e);
     }
-  };
+  }
 
-  /**
-   * @param actor - the actor to add the links to
-   * @param links - the links to add to the actor
-   */
-  private readonly insertLinks = (actor: Actor, links: Record) => {
+  private insertLinks(actor: Actor, links: Record) {
     if (actor.links == null) {
       actor.links = links;
     } else {
@@ -416,9 +473,9 @@ export class SequenceDB implements DiagramDB {
         actor.links[key] = links[key];
       }
     }
-  };
+  }
 
-  public addProperties = (actorId: string, text: { text: string }) => {
+  public addProperties(actorId: string, text: { text: string }) {
     // find the actor
     const actor = this.getActor(actorId);
     // JSON.parse the text
@@ -430,13 +487,9 @@ export class SequenceDB implements DiagramDB {
     } catch (e) {
       log.error('error while parsing actor properties text', e);
     }
-  };
+  }
 
-  /**
-   * @param actor - the actor to add the properties to
-   * @param properties - the properties to add to the actor's properties
-   */
-  private readonly insertProperties = (actor: Actor, properties: Record) => {
+  private insertProperties(actor: Actor, properties: Record) {
     if (actor.properties == null) {
       actor.properties = properties;
     } else {
@@ -444,13 +497,13 @@ export class SequenceDB implements DiagramDB {
         actor.properties[key] = properties[key];
       }
     }
-  };
+  }
 
-  private readonly boxEnd = () => {
+  private boxEnd() {
     this.state.records.currentBox = undefined;
-  };
+  }
 
-  public addDetails = (actorId: string, text: { text: string }) => {
+  public addDetails(actorId: string, text: { text: string }) {
     // find the actor
     const actor = this.getActor(actorId);
     const elem = document.getElementById(text.text)!;
@@ -470,18 +523,18 @@ export class SequenceDB implements DiagramDB {
     } catch (e) {
       log.error('error while parsing actor details text', e);
     }
-  };
+  }
 
-  public getActorProperty = (actor: Actor, key: string) => {
+  public getActorProperty(actor: Actor, key: string) {
     if (actor?.properties !== undefined) {
       return actor.properties[key];
     }
 
     return undefined;
-  };
+  }
 
   // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-redundant-type-constituents
-  public apply = (param: any | AddMessageParams | AddMessageParams[]) => {
+  public apply(param: any | AddMessageParams | AddMessageParams[]) {
     if (Array.isArray(param)) {
       param.forEach((item) => {
         this.apply(item);
@@ -490,6 +543,7 @@ export class SequenceDB implements DiagramDB {
       switch (param.type) {
         case 'sequenceIndex':
           this.state.records.messages.push({
+            id: this.state.records.messages.length.toString(),
             from: undefined,
             to: undefined,
             message: {
@@ -628,13 +682,15 @@ export class SequenceDB implements DiagramDB {
           break;
       }
     }
-  };
+  }
 
-  public getAccTitle = getAccTitle;
-  public getDiagramTitle = getDiagramTitle;
-  public setDiagramTitle = setDiagramTitle;
-  public getConfig = () => getConfig().sequence;
   public setAccTitle = setAccTitle;
   public setAccDescription = setAccDescription;
+  public setDiagramTitle = setDiagramTitle;
+  public getAccTitle = getAccTitle;
   public getAccDescription = getAccDescription;
+  public getDiagramTitle = getDiagramTitle;
+  public getConfig() {
+    return getConfig().sequence;
+  }
 }
diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.ts b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.ts
index 80ccda5f9..f2b701712 100644
--- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.ts
+++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.ts
@@ -3,21 +3,24 @@ import type { DiagramDefinition } from '../../diagram-api/types.js';
 import parser from './parser/sequenceDiagram.jison';
 import { SequenceDB } from './sequenceDb.js';
 import styles from './styles.js';
+import { setConfig } from '../../diagram-api/diagramAPI.js';
 import renderer from './sequenceRenderer.js';
-
-let db: SequenceDB;
+import type { MermaidConfig } from '../../config.type.js';
 
 export const diagram: DiagramDefinition = {
   parser,
   get db() {
-    if (!db) {
-      db = new SequenceDB();
-    }
-    return db;
+    return new SequenceDB();
   },
   renderer,
   styles,
-  init: ({ wrap }) => {
-    db.setWrap(wrap);
+  init: (cnf: MermaidConfig) => {
+    if (!cnf.sequence) {
+      cnf.sequence = {};
+    }
+    if (cnf.wrap) {
+      cnf.sequence.wrap = cnf.wrap;
+      setConfig({ sequence: { wrap: cnf.wrap } });
+    }
   },
 };
diff --git a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts
index 951d84b86..389171d3c 100644
--- a/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts
+++ b/packages/mermaid/src/diagrams/sequence/sequenceRenderer.ts
@@ -1538,7 +1538,6 @@ const calculateLoopBounds = async function (messages, actors, _maxWidthPerActor,
   let current, noteModel, msgModel;
 
   for (const msg of messages) {
-    msg.id = utils.random({ length: 10 });
     switch (msg.type) {
       case diagObj.db.LINETYPE.LOOP_START:
       case diagObj.db.LINETYPE.ALT_START:
diff --git a/packages/mermaid/src/diagrams/sequence/types.ts b/packages/mermaid/src/diagrams/sequence/types.ts
index 10c1c8ed3..7cf2ead9c 100644
--- a/packages/mermaid/src/diagrams/sequence/types.ts
+++ b/packages/mermaid/src/diagrams/sequence/types.ts
@@ -20,6 +20,7 @@ export interface Actor {
 }
 
 export interface Message {
+  id: string;
   from?: string;
   to?: string;
   message:

From f454865b9773ce3843e42f3da929a0a907bfa1af Mon Sep 17 00:00:00 2001
From: saurabhg772244 
Date: Wed, 12 Feb 2025 16:00:14 +0530
Subject: [PATCH 169/230] Updated unit tests to reflect new DB instance per
 parse.

---
 .../diagrams/sequence/sequenceDiagram.spec.js | 795 ++++++++----------
 1 file changed, 364 insertions(+), 431 deletions(-)

diff --git a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js
index 474668edf..7fbf4f4e3 100644
--- a/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js
+++ b/packages/mermaid/src/diagrams/sequence/sequenceDiagram.spec.js
@@ -98,14 +98,15 @@ let diagram;
 describe('more than one sequence diagram', () => {
   it('should not have duplicated messages', async () => {
     const diagram1 = await Diagram.fromText(`
-         sequenceDiagram
-         Alice->Bob:Hello Bob, how are you?
+        sequenceDiagram
+        Alice->Bob:Hello Bob, how are you?
         Bob-->Alice: I am good thanks!`);
     expect(diagram1.db.getMessages()).toMatchInlineSnapshot(`
       [
         {
           "activate": false,
           "from": "Alice",
+          "id": "0",
           "message": "Hello Bob, how are you?",
           "to": "Bob",
           "type": 5,
@@ -114,6 +115,7 @@ describe('more than one sequence diagram', () => {
         {
           "activate": false,
           "from": "Bob",
+          "id": "1",
           "message": "I am good thanks!",
           "to": "Alice",
           "type": 6,
@@ -131,6 +133,7 @@ describe('more than one sequence diagram', () => {
         {
           "activate": false,
           "from": "Alice",
+          "id": "0",
           "message": "Hello Bob, how are you?",
           "to": "Bob",
           "type": 5,
@@ -139,6 +142,7 @@ describe('more than one sequence diagram', () => {
         {
           "activate": false,
           "from": "Bob",
+          "id": "1",
           "message": "I am good thanks!",
           "to": "Alice",
           "type": 6,
@@ -158,6 +162,7 @@ describe('more than one sequence diagram', () => {
         {
           "activate": false,
           "from": "Alice",
+          "id": "0",
           "message": "Hello John, how are you?",
           "to": "John",
           "type": 5,
@@ -166,6 +171,7 @@ describe('more than one sequence diagram', () => {
         {
           "activate": false,
           "from": "John",
+          "id": "1",
           "message": "I am good thanks!",
           "to": "Alice",
           "type": 6,
@@ -221,27 +227,27 @@ Alice->Bob:Hello Bob, how are you?
 Note right of Bob: Bob thinks
 Bob-->Alice: I am good thanks!`;
 
-    await mermaidAPI.parse(str);
-    await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); // needs to be rendered for the correct value of visibility auto numbers
-    expect(diagram.db.showSequenceNumbers()).toBe(true);
+    const diagram1 = await Diagram.fromText(str);
+
+    await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); // needs to be rendered for the correct value of visibility auto numbers
+    expect(diagram1.db.showSequenceNumbers()).toBe(true);
   });
 
   it('should handle a sequenceDiagram definition with a title:', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 title: Diagram Title
 Alice->Bob:Hello Bob, how are you?
 Note right of Bob: Bob thinks
-Bob-->Alice: I am good thanks!`;
+Bob-->Alice: I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     actors.get('Bob').description = 'Bob';
 
-    expect(diagram.db.getAccDescription()).toBe('');
-    const messages = diagram.db.getMessages();
-    const title = diagram.db.getDiagramTitle();
+    expect(diagram1.db.getAccDescription()).toBe('');
+    const messages = diagram1.db.getMessages();
+    const title = diagram1.db.getDiagramTitle();
 
     expect(messages.length).toBe(3);
     expect(messages[0].from).toBe('Alice');
@@ -250,21 +256,20 @@ Bob-->Alice: I am good thanks!`;
   });
 
   it('should handle a sequenceDiagram definition with a title without a :', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 title Diagram Title
 Alice->Bob:Hello Bob, how are you?
 Note right of Bob: Bob thinks
-Bob-->Alice: I am good thanks!`;
+Bob-->Alice: I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     actors.get('Bob').description = 'Bob';
 
-    expect(diagram.db.getAccDescription()).toBe('');
-    const messages = diagram.db.getMessages();
-    const title = diagram.db.getDiagramTitle();
+    expect(diagram1.db.getAccDescription()).toBe('');
+    const messages = diagram1.db.getMessages();
+    const title = diagram1.db.getDiagramTitle();
 
     expect(messages.length).toBe(3);
     expect(messages[0].from).toBe('Alice');
@@ -273,22 +278,20 @@ Bob-->Alice: I am good thanks!`;
   });
 
   it('should handle a sequenceDiagram definition with a accessibility title and description (accDescr)', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 title: Diagram Title
 accTitle: This is the title
 accDescr: Accessibility Description
 Alice->Bob:Hello Bob, how are you?
-`;
+`);
 
-    await mermaidAPI.parse(str);
-    expect(diagram.db.getDiagramTitle()).toBe('Diagram Title');
-    expect(diagram.db.getAccTitle()).toBe('This is the title');
-    expect(diagram.db.getAccDescription()).toBe('Accessibility Description');
-    const messages = diagram.db.getMessages();
+    expect(diagram1.db.getDiagramTitle()).toBe('Diagram Title');
+    expect(diagram1.db.getAccTitle()).toBe('This is the title');
+    expect(diagram1.db.getAccDescription()).toBe('Accessibility Description');
   });
   it('should handle a sequenceDiagram definition with a accessibility title and multiline description (accDescr)', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 accTitle: This is the title
 accDescr {
@@ -296,43 +299,39 @@ Accessibility
 Description
 }
 Alice->Bob:Hello Bob, how are you?
-`;
+`);
 
-    await mermaidAPI.parse(str);
-    expect(diagram.db.getAccTitle()).toBe('This is the title');
-    expect(diagram.db.getAccDescription()).toBe('Accessibility\nDescription');
-    const messages = diagram.db.getMessages();
+    expect(diagram1.db.getAccTitle()).toBe('This is the title');
+    expect(diagram1.db.getAccDescription()).toBe('Accessibility\nDescription');
   });
 
   it('should space in actor names', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 Alice->Bob:Hello Bob, how are - you?
-Bob-->Alice: I am good thanks!`;
+Bob-->Alice: I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     actors.get('Bob').description = 'Bob';
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(2);
     expect(messages[0].from).toBe('Alice');
     expect(messages[1].from).toBe('Bob');
   });
   it('should handle dashes in actor names', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 Alice-in-Wonderland->Bob:Hello Bob, how are - you?
-Bob-->Alice-in-Wonderland:I am good thanks!`;
+Bob-->Alice-in-Wonderland:I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice-in-Wonderland').description).toBe('Alice-in-Wonderland');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(2);
     expect(messages[0].from).toBe('Alice-in-Wonderland');
@@ -340,20 +339,19 @@ Bob-->Alice-in-Wonderland:I am good thanks!`;
   });
 
   it('should handle dashes in participant names', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 participant Alice-in-Wonderland
 participant Bob
 Alice-in-Wonderland->Bob:Hello Bob, how are - you?
-Bob-->Alice-in-Wonderland:I am good thanks!`;
+Bob-->Alice-in-Wonderland:I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect([...actors.keys()]).toEqual(['Alice-in-Wonderland', 'Bob']);
     expect(actors.get('Alice-in-Wonderland').description).toBe('Alice-in-Wonderland');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(2);
     expect(messages[0].from).toBe('Alice-in-Wonderland');
@@ -361,28 +359,26 @@ Bob-->Alice-in-Wonderland:I am good thanks!`;
   });
 
   it('should alias participants', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 participant A as Alice
 participant B as Bob
 A->B:Hello Bob, how are you?
-B-->A: I am good thanks!`;
+B-->A: I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
 
     expect([...actors.keys()]).toEqual(['A', 'B']);
     expect(actors.get('A').description).toBe('Alice');
     expect(actors.get('B').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
     expect(messages.length).toBe(2);
     expect(messages[0].from).toBe('A');
     expect(messages[1].from).toBe('B');
   });
   it('should alias a mix of actors and participants apa12', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
   actor Alice as Alice2
   actor Bob
@@ -392,11 +388,9 @@ sequenceDiagram
   Bob->>Alice: Hi Alice
   Alice->>John: Hi John
   John->>Mandy: Hi Mandy
-  Mandy ->>Joan: Hi Joan`;
+  Mandy ->>Joan: Hi Joan`);
 
-    await mermaidAPI.parse(str);
-
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect([...actors.keys()]).toEqual(['Alice', 'Bob', 'John', 'Mandy', 'Joan']);
     expect(actors.get('Alice').description).toBe('Alice2');
     expect(actors.get('Alice').type).toBe('actor');
@@ -404,219 +398,212 @@ sequenceDiagram
     expect(actors.get('John').type).toBe('participant');
     expect(actors.get('Joan').type).toBe('participant');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
     expect(messages.length).toBe(5);
     expect(messages[0].from).toBe('Alice');
     expect(messages[4].to).toBe('Joan');
   });
   it('should alias actors apa13', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 actor A as Alice
 actor B as Bob
 A->B:Hello Bob, how are you?
-B-->A: I am good thanks!`;
+B-->A: I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect([...actors.keys()]).toEqual(['A', 'B']);
     expect(actors.get('A').description).toBe('Alice');
     expect(actors.get('B').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
     expect(messages.length).toBe(2);
     expect(messages[0].from).toBe('A');
     expect(messages[1].from).toBe('B');
   });
   it('should handle in async messages', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
-Alice-xBob:Hello Bob, how are you?`;
+Alice-xBob:Hello Bob, how are you?`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(1);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.SOLID_CROSS);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.SOLID_CROSS);
   });
   it('should handle in async dotted messages', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
-Alice--xBob:Hello Bob, how are you?`;
+Alice--xBob:Hello Bob, how are you?`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(1);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.DOTTED_CROSS);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.DOTTED_CROSS);
   });
   it('should handle in sync messages', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
-Alice-)Bob:Hello Bob, how are you?`;
+Alice-)Bob:Hello Bob, how are you?`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(1);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.SOLID_POINT);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.SOLID_POINT);
   });
   it('should handle in sync dotted messages', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
-Alice--)Bob:Hello Bob, how are you?`;
+Alice--)Bob:Hello Bob, how are you?`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(1);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.DOTTED_POINT);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.DOTTED_POINT);
   });
   it('should handle in arrow messages', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
-Alice->>Bob:Hello Bob, how are you?`;
+Alice->>Bob:Hello Bob, how are you?`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(1);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.SOLID);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.SOLID);
   });
   it('should handle in arrow messages', async () => {
-    const str = 'sequenceDiagram\n' + 'Alice-->>Bob:Hello Bob, how are you?';
+    const diagram1 = await Diagram.fromText(
+      'sequenceDiagram\n' + 'Alice-->>Bob:Hello Bob, how are you?'
+    );
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(1);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.DOTTED);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.DOTTED);
   });
   it('should handle bidirectional arrow messages', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(
+      `
 sequenceDiagram
-Alice<<->>Bob:Hello Bob, how are you?`;
+Alice<<->>Bob:Hello Bob, how are you?`
+    );
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(1);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.BIDIRECTIONAL_SOLID);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.BIDIRECTIONAL_SOLID);
   });
   it('should handle bidirectional dotted arrow messages', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(
+      `
     sequenceDiagram
-    Alice<<-->>Bob:Hello Bob, how are you?`;
+    Alice<<-->>Bob:Hello Bob, how are you?`
+    );
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(1);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.BIDIRECTIONAL_DOTTED);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.BIDIRECTIONAL_DOTTED);
   });
   it('should handle actor activation', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 Alice-->>Bob:Hello Bob, how are you?
 activate Bob
 Bob-->>Alice:Hello Alice, I'm fine and  you?
-deactivate Bob`;
+deactivate Bob`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(4);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.DOTTED);
-    expect(messages[1].type).toBe(diagram.db.LINETYPE.ACTIVE_START);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.DOTTED);
+    expect(messages[1].type).toBe(diagram1.db.LINETYPE.ACTIVE_START);
     expect(messages[1].from).toBe('Bob');
-    expect(messages[2].type).toBe(diagram.db.LINETYPE.DOTTED);
-    expect(messages[3].type).toBe(diagram.db.LINETYPE.ACTIVE_END);
+    expect(messages[2].type).toBe(diagram1.db.LINETYPE.DOTTED);
+    expect(messages[3].type).toBe(diagram1.db.LINETYPE.ACTIVE_END);
     expect(messages[3].from).toBe('Bob');
   });
   it('should handle actor one line notation activation', async () => {
-    const str = `
-      sequenceDiagram
-      Alice-->>+Bob:Hello Bob, how are you?
-      Bob-->>- Alice:Hello Alice, I'm fine and  you?`;
+    const diagram1 = await Diagram.fromText(`
+    sequenceDiagram
+    Alice-->>+Bob:Hello Bob, how are you?
+    Bob-->>- Alice:Hello Alice, I'm fine and  you?`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(4);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.DOTTED);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.DOTTED);
     expect(messages[0].activate).toBeTruthy();
-    expect(messages[1].type).toBe(diagram.db.LINETYPE.ACTIVE_START);
+    expect(messages[1].type).toBe(diagram1.db.LINETYPE.ACTIVE_START);
     expect(messages[1].from).toBe('Bob');
-    expect(messages[2].type).toBe(diagram.db.LINETYPE.DOTTED);
-    expect(messages[3].type).toBe(diagram.db.LINETYPE.ACTIVE_END);
+    expect(messages[2].type).toBe(diagram1.db.LINETYPE.DOTTED);
+    expect(messages[3].type).toBe(diagram1.db.LINETYPE.ACTIVE_END);
     expect(messages[3].from).toBe('Bob');
   });
   it('should handle stacked activations', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
       sequenceDiagram
       Alice-->>+Bob:Hello Bob, how are you?
       Bob-->>+Carol:Carol, let me introduce Alice?
       Bob-->>- Alice:Hello Alice, please meet Carol?
-      Carol->>- Bob:Oh Bob, I'm so happy to be here!`;
+      Carol->>- Bob:Oh Bob, I'm so happy to be here!`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     expect(actors.get('Bob').description).toBe('Bob');
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(8);
-    expect(messages[0].type).toBe(diagram.db.LINETYPE.DOTTED);
-    expect(messages[1].type).toBe(diagram.db.LINETYPE.ACTIVE_START);
+    expect(messages[0].type).toBe(diagram1.db.LINETYPE.DOTTED);
+    expect(messages[1].type).toBe(diagram1.db.LINETYPE.ACTIVE_START);
     expect(messages[1].from).toBe('Bob');
-    expect(messages[2].type).toBe(diagram.db.LINETYPE.DOTTED);
-    expect(messages[3].type).toBe(diagram.db.LINETYPE.ACTIVE_START);
+    expect(messages[2].type).toBe(diagram1.db.LINETYPE.DOTTED);
+    expect(messages[3].type).toBe(diagram1.db.LINETYPE.ACTIVE_START);
     expect(messages[3].from).toBe('Carol');
-    expect(messages[5].type).toBe(diagram.db.LINETYPE.ACTIVE_END);
+    expect(messages[5].type).toBe(diagram1.db.LINETYPE.ACTIVE_END);
     expect(messages[5].from).toBe('Bob');
-    expect(messages[7].type).toBe(diagram.db.LINETYPE.ACTIVE_END);
+    expect(messages[7].type).toBe(diagram1.db.LINETYPE.ACTIVE_END);
     expect(messages[7].from).toBe('Carol');
   });
   it('should handle fail parsing when activating an inactive participant', async () => {
@@ -646,102 +633,97 @@ deactivate Bob`;
   });
 
   it('should handle comments in a sequenceDiagram', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
       sequenceDiagram
       Alice->Bob: Hello Bob, how are you?
       %% Comment
       Note right of Bob: Bob thinks
-      Bob-->Alice: I am good thanks!`;
+      Bob-->Alice: I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     actors.get('Bob').description = 'Bob';
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(3);
     expect(messages[0].from).toBe('Alice');
     expect(messages[2].from).toBe('Bob');
   });
   it('should handle new lines in a sequenceDiagram', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
       sequenceDiagram
       Alice->Bob: Hello Bob, how are you?
 
       %% Comment
       Note right of Bob: Bob thinks
       Bob-->Alice: I am good thanks!
-      `;
+      `);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     actors.get('Bob').description = 'Bob';
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(3);
     expect(messages[0].from).toBe('Alice');
     expect(messages[2].from).toBe('Bob');
   });
   it('should handle semicolons', async () => {
-    const str = `
-sequenceDiagram;Alice->Bob: Hello Bob, how are you?;Note right of Bob: Bob thinks;Bob-->Alice: I am good thanks!;`;
+    const diagram1 = await Diagram.fromText(`
+sequenceDiagram;Alice->Bob: Hello Bob, how are you?;Note right of Bob: Bob thinks;Bob-->Alice: I am good thanks!;`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     actors.get('Bob').description = 'Bob';
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(3);
     expect(messages[0].from).toBe('Alice');
     expect(messages[2].from).toBe('Bob');
   });
   it('should handle one leading space in lines in a sequenceDiagram', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
  Alice->Bob: Hello Bob, how are you?
 
 %% Comment
 Note right of Bob: Bob thinks
-Bob-->Alice: I am good thanks!`;
+Bob-->Alice: I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     actors.get('Bob').description = 'Bob';
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(3);
     expect(messages[0].from).toBe('Alice');
     expect(messages[2].from).toBe('Bob');
   });
   it('should handle several leading spaces in lines in a sequenceDiagram', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
    Alice->Bob: Hello Bob, how are you?
 
 %% Comment
 Note right of Bob: Bob thinks
-Bob-->Alice: I am good thanks!`;
+Bob-->Alice: I am good thanks!`);
 
-    await mermaidAPI.parse(str);
-    const actors = diagram.db.getActors();
+    const actors = diagram1.db.getActors();
     expect(actors.get('Alice').description).toBe('Alice');
     actors.get('Bob').description = 'Bob';
 
-    const messages = diagram.db.getMessages();
+    const messages = diagram1.db.getMessages();
 
     expect(messages.length).toBe(3);
     expect(messages[0].from).toBe('Alice');
     expect(messages[2].from).toBe('Bob');
   });
   it('should handle several leading spaces in lines in a sequenceDiagram', async () => {
-    const str = `
+    const diagram1 = await Diagram.fromText(`
 sequenceDiagram
 participant Alice
 participant Bob
@@ -752,21 +734,20 @@ John->John: Fight against hypochondria
 Note right of John: Rational thoughts
prevail... John-->Alice: Great! John->Bob: How about you? -Bob-->John: Jolly good!`; +Bob-->John: Jolly good!`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Alice').description).toBe('Alice'); actors.get('Bob').description = 'Bob'; - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(8); expect(messages[0].from).toBe('Alice'); expect(messages[2].from).toBe('John'); }); it('should handle different line breaks', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram participant 1 as multiline
text participant 2 as multiline
text @@ -780,17 +761,15 @@ note right of 3: multiline
text note right of 4: multiline
text 4->>1: multiline
text note right of 1: multiline
text -`; +`); - await mermaidAPI.parse(str); - - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('1').description).toBe('multiline
text'); expect(actors.get('2').description).toBe('multiline
text'); expect(actors.get('3').description).toBe('multiline
text'); expect(actors.get('4').description).toBe('multiline
text'); - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[0].message).toBe('multiline
text'); expect(messages[1].message).toBe('multiline
text'); expect(messages[2].message).toBe('multiline
text'); @@ -801,7 +780,7 @@ note right of 1: multiline
text expect(messages[7].message).toBe('multiline
text'); }); it('should handle notes and messages without wrap specified', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram participant 1 participant 2 @@ -815,11 +794,9 @@ note right of 3:nowrap: single-line text note right of 4: multiline
text 4->>1:nowrap: multiline
text note right of 1:nowrap: multiline
text -`; +`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[0].message).toBe('single-line text'); expect(messages[1].message).toBe('single-line text'); expect(messages[2].message).toBe('single-line text'); @@ -840,7 +817,7 @@ note right of 1:nowrap: multiline
text expect(messages[7].wrap).toBe(false); }); it('should handle notes and messages with wrap specified', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram participant 1 participant 2 @@ -850,11 +827,9 @@ participant 4 note right of 2:wrap: single-line text 2->>3:wrap: multiline
text note right of 3:wrap: multiline
text -`; +`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[0].message).toBe('single-line text'); expect(messages[1].message).toBe('single-line text'); expect(messages[2].message).toBe('multiline
text'); @@ -865,53 +840,47 @@ note right of 3:wrap: multiline
text expect(messages[3].wrap).toBe(true); }); it('should handle notes and messages with nowrap or line breaks', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram participant 1 participant 2 1->>2: single-line text note right of 2: single-line text -`; +`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[0].message).toBe('single-line text'); expect(messages[1].message).toBe('single-line text'); expect(messages[0].wrap).toBe(false); expect(messages[1].wrap).toBe(false); }); it('should handle notes over a single actor', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? Note over Bob: Bob thinks -`; +`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].from).toBe('Bob'); expect(messages[1].to).toBe('Bob'); }); it('should handle notes over multiple actors', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? Note over Alice,Bob: confusion Note over Bob,Alice: resolution -`; +`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].from).toBe('Alice'); expect(messages[1].to).toBe('Bob'); expect(messages[2].from).toBe('Bob'); expect(messages[2].to).toBe('Alice'); }); it('should handle loop statements', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? @@ -920,21 +889,20 @@ Note right of Bob: Bob thinks loop Multiple happy responses Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Alice').description).toBe('Alice'); actors.get('Bob').description = 'Bob'; - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(5); expect(messages[0].from).toBe('Alice'); expect(messages[1].from).toBe('Bob'); }); it('should add a rect around sequence', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? %% Comment @@ -942,23 +910,22 @@ end`; Note right of Bob: Bob thinks Bob-->Alice: I am good thanks end - `; + `); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Alice').description).toBe('Alice'); actors.get('Bob').description = 'Bob'; - const messages = diagram.db.getMessages(); - expect(messages[1].type).toEqual(diagram.db.LINETYPE.RECT_START); + const messages = diagram1.db.getMessages(); + expect(messages[1].type).toEqual(diagram1.db.LINETYPE.RECT_START); expect(messages[1].message).toBe('rgb(200, 255, 200)'); - expect(messages[2].type).toEqual(diagram.db.LINETYPE.NOTE); - expect(messages[3].type).toEqual(diagram.db.LINETYPE.DOTTED_OPEN); - expect(messages[4].type).toEqual(diagram.db.LINETYPE.RECT_END); + expect(messages[2].type).toEqual(diagram1.db.LINETYPE.NOTE); + expect(messages[3].type).toEqual(diagram1.db.LINETYPE.DOTTED_OPEN); + expect(messages[4].type).toEqual(diagram1.db.LINETYPE.RECT_END); }); it('should allow for nested rects', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? %% Comment @@ -968,24 +935,24 @@ end`; end Bob-->Alice: I am good thanks end - `; - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + `); + + const actors = diagram1.db.getActors(); expect(actors.get('Alice').description).toBe('Alice'); actors.get('Bob').description = 'Bob'; - const messages = diagram.db.getMessages(); - expect(messages[1].type).toEqual(diagram.db.LINETYPE.RECT_START); + const messages = diagram1.db.getMessages(); + expect(messages[1].type).toEqual(diagram1.db.LINETYPE.RECT_START); expect(messages[1].message).toBe('rgb(200, 255, 200)'); - expect(messages[2].type).toEqual(diagram.db.LINETYPE.RECT_START); + expect(messages[2].type).toEqual(diagram1.db.LINETYPE.RECT_START); expect(messages[2].message).toBe('rgb(0, 0, 0)'); - expect(messages[3].type).toEqual(diagram.db.LINETYPE.NOTE); - expect(messages[4].type).toEqual(diagram.db.LINETYPE.RECT_END); - expect(messages[5].type).toEqual(diagram.db.LINETYPE.DOTTED_OPEN); - expect(messages[6].type).toEqual(diagram.db.LINETYPE.RECT_END); + expect(messages[3].type).toEqual(diagram1.db.LINETYPE.NOTE); + expect(messages[4].type).toEqual(diagram1.db.LINETYPE.RECT_END); + expect(messages[5].type).toEqual(diagram1.db.LINETYPE.DOTTED_OPEN); + expect(messages[6].type).toEqual(diagram1.db.LINETYPE.RECT_END); }); it('should handle opt statements', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? @@ -994,21 +961,20 @@ Note right of Bob: Bob thinks opt Perhaps a happy response Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Alice').description).toBe('Alice'); actors.get('Bob').description = 'Bob'; - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(5); expect(messages[0].from).toBe('Alice'); expect(messages[1].from).toBe('Bob'); }); it('should handle alt statements', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? @@ -1019,22 +985,21 @@ alt isWell Bob-->Alice: I am good thanks! else isSick Bob-->Alice: Feel sick... -end`; +end`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Alice').description).toBe('Alice'); actors.get('Bob').description = 'Bob'; - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(7); expect(messages[0].from).toBe('Alice'); expect(messages[1].from).toBe('Bob'); }); it('should handle alt statements with multiple elses', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? @@ -1047,41 +1012,40 @@ else isSick Bob-->Alice: Feel sick... else default Bob-->Alice: :-) -end`; - await mermaidAPI.parse(str); - const messages = diagram.db.getMessages(); +end`); + + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(9); expect(messages[1].from).toBe('Bob'); - expect(messages[2].type).toBe(diagram.db.LINETYPE.ALT_START); + expect(messages[2].type).toBe(diagram1.db.LINETYPE.ALT_START); expect(messages[3].from).toBe('Bob'); - expect(messages[4].type).toBe(diagram.db.LINETYPE.ALT_ELSE); + expect(messages[4].type).toBe(diagram1.db.LINETYPE.ALT_ELSE); expect(messages[5].from).toBe('Bob'); - expect(messages[6].type).toBe(diagram.db.LINETYPE.ALT_ELSE); + expect(messages[6].type).toBe(diagram1.db.LINETYPE.ALT_ELSE); expect(messages[7].from).toBe('Bob'); - expect(messages[8].type).toBe(diagram.db.LINETYPE.ALT_END); + expect(messages[8].type).toBe(diagram1.db.LINETYPE.ALT_END); }); it('should handle critical statements without options', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram critical Establish a connection to the DB Service-->DB: connect - end`; + end`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Service').description).toBe('Service'); expect(actors.get('DB').description).toBe('DB'); - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(3); - expect(messages[0].type).toBe(diagram.db.LINETYPE.CRITICAL_START); + expect(messages[0].type).toBe(diagram1.db.LINETYPE.CRITICAL_START); expect(messages[1].from).toBe('Service'); - expect(messages[2].type).toBe(diagram.db.LINETYPE.CRITICAL_END); + expect(messages[2].type).toBe(diagram1.db.LINETYPE.CRITICAL_END); }); it('should handle critical statements with options', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram critical Establish a connection to the DB Service-->DB: connect @@ -1089,53 +1053,51 @@ sequenceDiagram Service-->Service: Log error option Credentials rejected Service-->Service: Log different error - end`; + end`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Service').description).toBe('Service'); expect(actors.get('DB').description).toBe('DB'); - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(7); - expect(messages[0].type).toBe(diagram.db.LINETYPE.CRITICAL_START); + expect(messages[0].type).toBe(diagram1.db.LINETYPE.CRITICAL_START); expect(messages[1].from).toBe('Service'); - expect(messages[2].type).toBe(diagram.db.LINETYPE.CRITICAL_OPTION); + expect(messages[2].type).toBe(diagram1.db.LINETYPE.CRITICAL_OPTION); expect(messages[3].from).toBe('Service'); - expect(messages[4].type).toBe(diagram.db.LINETYPE.CRITICAL_OPTION); + expect(messages[4].type).toBe(diagram1.db.LINETYPE.CRITICAL_OPTION); expect(messages[5].from).toBe('Service'); - expect(messages[6].type).toBe(diagram.db.LINETYPE.CRITICAL_END); + expect(messages[6].type).toBe(diagram1.db.LINETYPE.CRITICAL_END); }); it('should handle break statements', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Consumer-->API: Book something API-->BookingService: Start booking process break when the booking process fails API-->Consumer: show failure end - API-->BillingService: Start billing process`; + API-->BillingService: Start billing process`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Consumer').description).toBe('Consumer'); expect(actors.get('API').description).toBe('API'); - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(6); expect(messages[0].from).toBe('Consumer'); expect(messages[1].from).toBe('API'); - expect(messages[2].type).toBe(diagram.db.LINETYPE.BREAK_START); + expect(messages[2].type).toBe(diagram1.db.LINETYPE.BREAK_START); expect(messages[3].from).toBe('API'); - expect(messages[4].type).toBe(diagram.db.LINETYPE.BREAK_END); + expect(messages[4].type).toBe(diagram1.db.LINETYPE.BREAK_END); expect(messages[5].from).toBe('API'); }); it('should handle par statements a sequenceDiagram', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram par Parallel one Alice->>Bob: Hello Bob, how are you? @@ -1146,15 +1108,14 @@ Bob-->>Alice: Fine! and Parallel three Alice->>Bob: What do you think about it? Bob-->>Alice: It's good! -end`; +end`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Alice').description).toBe('Alice'); expect(actors.get('Bob').description).toBe('Bob'); - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(10); expect(messages[0].message).toBe('Parallel one'); @@ -1162,21 +1123,20 @@ end`; expect(messages[2].from).toBe('Bob'); }); it('it should handle par_over statements', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram par_over Parallel overlap Alice ->> Bob: Message Note left of Alice: Alice note Note right of Bob: Bob note -end`; +end`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('Alice').description).toBe('Alice'); expect(actors.get('Bob').description).toBe('Bob'); - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages.length).toBe(5); expect(messages[0].message).toBe('Parallel overlap'); @@ -1185,139 +1145,119 @@ end`; expect(messages[3].from).toBe('Bob'); }); it('should handle special characters in signals', async () => { - const str = 'sequenceDiagram\n' + 'Alice->Bob: -:<>,;# comment'; + const diagram1 = await Diagram.fromText('sequenceDiagram\n' + 'Alice->Bob: -:<>,;# comment'); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[0].message).toBe('-:<>,'); }); it('should handle special characters in notes', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? -Note right of Bob: -:<>,;# comment`; +Note right of Bob: -:<>,;# comment`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].message).toBe('-:<>,'); }); it('should handle special characters in loop', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? loop -:<>,;# comment Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].message).toBe('-:<>,'); }); it('should handle special characters in opt', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? opt -:<>,;# comment Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].message).toBe('-:<>,'); }); it('should handle special characters in alt', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? alt -:<>,;# comment Bob-->Alice: I am good thanks! else ,<>:-#; comment Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].message).toBe('-:<>,'); expect(messages[3].message).toBe(',<>:-'); }); it('should handle special characters in par', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? par -:<>,;# comment Bob-->Alice: I am good thanks! and ,<>:-#; comment Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].message).toBe('-:<>,'); expect(messages[3].message).toBe(',<>:-'); }); it('should handle no-label loop', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? loop Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].message).toBe(''); expect(messages[2].message).toBe('I am good thanks!'); }); it('should handle no-label opt', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? opt # comment Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].message).toBe(''); expect(messages[2].message).toBe('I am good thanks!'); }); it('should handle no-label alt', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? alt;Bob-->Alice: I am good thanks! else # comment Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].message).toBe(''); expect(messages[2].message).toBe('I am good thanks!'); expect(messages[3].message).toBe(''); expect(messages[4].message).toBe('I am good thanks!'); }); it('should handle no-label par', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram Alice->Bob: Hello Bob, how are you? par;Bob-->Alice: I am good thanks! and # comment Bob-->Alice: I am good thanks! -end`; +end`); - await mermaidAPI.parse(str); - - const messages = diagram.db.getMessages(); + const messages = diagram1.db.getMessages(); expect(messages[1].message).toBe(''); expect(messages[2].message).toBe('I am good thanks!'); expect(messages[3].message).toBe(''); @@ -1325,7 +1265,7 @@ end`; }); it('should handle links', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram participant a as Alice participant b as Bob @@ -1336,10 +1276,9 @@ links a: { "On-Call": "https://oncall.contoso.com/?svc=alice" } link a: Endpoint @ https://alice.contoso.com link a: Swagger @ https://swagger.contoso.com link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com -`; +`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('a').links.Repo).toBe('https://repo.contoso.com/'); expect(actors.get('b').links.Repo).toBe(undefined); expect(actors.get('a').links.Dashboard).toBe('https://dashboard.contoso.com/'); @@ -1353,17 +1292,16 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com it('should handle properties EXPERIMENTAL: USE WITH CAUTION', async () => { //Be aware that the syntax for "properties" is likely to be changed. - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram participant a as Alice participant b as Bob participant c as Charlie properties a: {"class": "internal-service-actor", "icon": "@clock"} properties b: {"class": "external-service-actor", "icon": "@computer"} -`; +`); - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect(actors.get('a').properties.class).toBe('internal-service-actor'); expect(actors.get('b').properties.class).toBe('external-service-actor'); expect(actors.get('a').properties.icon).toBe('@clock'); @@ -1372,7 +1310,7 @@ properties b: {"class": "external-service-actor", "icon": "@computer"} }); it('should handle box', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram box green Group 1 participant a as Alice @@ -1385,17 +1323,16 @@ links a: { "On-Call": "https://oncall.contoso.com/?svc=alice" } link a: Endpoint @ https://alice.contoso.com link a: Swagger @ https://swagger.contoso.com link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com -`; +`); - await mermaidAPI.parse(str); - const boxes = diagram.db.getBoxes(); + const boxes = diagram1.db.getBoxes(); expect(boxes[0].name).toEqual('Group 1'); expect(boxes[0].actorKeys).toEqual(['a', 'b']); expect(boxes[0].fill).toEqual('green'); }); it('should handle box without color', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram box Group 1 participant a as Alice @@ -1408,17 +1345,16 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com link a: Endpoint @ https://alice.contoso.com link a: Swagger @ https://swagger.contoso.com link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com - `; + `); - await mermaidAPI.parse(str); - const boxes = diagram.db.getBoxes(); + const boxes = diagram1.db.getBoxes(); expect(boxes[0].name).toEqual('Group 1'); expect(boxes[0].actorKeys).toEqual(['a', 'b']); expect(boxes[0].fill).toEqual('transparent'); }); it('should handle box without description', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram box Aqua participant a as Alice @@ -1431,17 +1367,16 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com link a: Endpoint @ https://alice.contoso.com link a: Swagger @ https://swagger.contoso.com link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com - `; + `); - await mermaidAPI.parse(str); - const boxes = diagram.db.getBoxes(); + const boxes = diagram1.db.getBoxes(); expect(boxes[0].name).toBeFalsy(); expect(boxes[0].actorKeys).toEqual(['a', 'b']); expect(boxes[0].fill).toEqual('Aqua'); }); it('should handle simple actor creation', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram participant a as Alice a ->>b: Hello Bob? @@ -1450,10 +1385,9 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com c ->> b: Hello b? create actor d as Donald a ->> d: Hello Donald? - `; - await mermaidAPI.parse(str); - const actors = diagram.db.getActors(); - const createdActors = diagram.db.getCreatedActors(); + `); + const actors = diagram1.db.getActors(); + const createdActors = diagram1.db.getCreatedActors(); expect(actors.get('c').name).toEqual('c'); expect(actors.get('c').description).toEqual('c'); expect(actors.get('c').type).toEqual('participant'); @@ -1464,7 +1398,7 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com expect(createdActors.get('d')).toEqual(3); }); it('should handle simple actor destruction', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram participant a as Alice a ->>b: Hello Bob? @@ -1473,14 +1407,13 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com b ->> c: Where is Alice? destroy c b ->> c: Where are you? - `; - await mermaidAPI.parse(str); - const destroyedActors = diagram.db.getDestroyedActors(); + `); + const destroyedActors = diagram1.db.getDestroyedActors(); expect(destroyedActors.get('a')).toEqual(1); expect(destroyedActors.get('c')).toEqual(3); }); it('should handle the creation and destruction of the same actor', async () => { - const str = ` + const diagram2 = await Diagram.fromText(` sequenceDiagram a ->>b: Hello Bob? create participant c @@ -1488,10 +1421,9 @@ link a: Tests @ https://tests.contoso.com/?svc=alice@contoso.com c ->> b: Hello b? destroy c b ->> c : Bye c ! - `; - await mermaidAPI.parse(str); - const createdActors = diagram.db.getCreatedActors(); - const destroyedActors = diagram.db.getDestroyedActors(); + `); + const createdActors = diagram2.db.getCreatedActors(); + const destroyedActors = diagram2.db.getDestroyedActors(); expect(createdActors.get('c')).toEqual(1); expect(destroyedActors.get('c')).toEqual(3); }); @@ -1571,7 +1503,7 @@ describe('when checking the bounds in a sequenceDiagram', function () { expect(bounds.stopx).toBe(300); expect(bounds.stopy).toBe(400); }); - it('should handle multiple loops withtout expanding the bounds', () => { + it('should handle multiple loops without expanding the bounds', () => { diagram.renderer.bounds.insert(100, 100, 1000, 1000); diagram.renderer.bounds.verticalPos = 200; diagram.renderer.bounds.newLoop(); @@ -1676,11 +1608,11 @@ it should handle one actor, when textPlacement is ${textPlacement}`, async () => sequenceDiagram participant Alice`; - await mermaidAPI.parse(str); + const diagram1 = await Diagram.fromText(str); // diagram.renderer.setConf(mermaidAPI.getConfig().sequence); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds } = diagram.renderer.bounds.getBounds(); + const { bounds } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); expect(bounds.stopx).toBe(conf.width); @@ -1688,16 +1620,14 @@ participant Alice`; }); }); it('should handle same actor with different whitespace properly', async () => { - const str = ` + const diagram1 = await Diagram.fromText(` sequenceDiagram participant Alice participant Alice participant Alice -`; +`); - await mermaidAPI.parse(str); - - const actors = diagram.db.getActors(); + const actors = diagram1.db.getActors(); expect([...actors.keys()]).toEqual(['Alice']); }); it('should handle one actor and a centered note', async () => { @@ -1706,12 +1636,12 @@ sequenceDiagram participant Alice Note over Alice: Alice thinks `; + const diagram1 = await Diagram.fromText(str); expect(mermaidAPI.getConfig().sequence.mirrorActors).toBeFalsy(); - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); expect(bounds.stopx).toBe(conf.width); @@ -1724,10 +1654,10 @@ sequenceDiagram participant Alice Note left of Alice: Alice thinks`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2); expect(bounds.starty).toBe(0); expect(bounds.stopx).toBe(conf.width); @@ -1740,10 +1670,10 @@ sequenceDiagram participant Alice Note right of Alice: Alice thinks`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); expect(bounds.stopx).toBe(conf.width / 2 + conf.actorMargin / 2 + conf.width); @@ -1755,10 +1685,10 @@ Note right of Alice: Alice thinks`; sequenceDiagram Alice->Bob: Hello Bob, how are you?`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin); @@ -1773,10 +1703,10 @@ participant Bob end Alice->Bob: Hello Bob, how are you?`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin + conf.boxTextMargin * 2); @@ -1787,11 +1717,11 @@ Alice->Bob: Hello Bob, how are you?`; %%{init: {'logLevel': 0}}%% sequenceDiagram Alice->Bob: Hello Bob, how are you?`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); const mermaid = mermaidAPI.getConfig(); expect(mermaid.logLevel).toBe(0); expect(bounds.startx).toBe(0); @@ -1803,16 +1733,15 @@ Alice->Bob: Hello Bob, how are you?`; const str = ` %%{init: { 'logLevel': 0}}%% sequenceDiagram -%%{ -wrap -}%% +%%{wrap}%% Alice->Bob: Hello Bob, how are you?`; await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const msgs = diagram.db.getMessages(); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const msgs = diagram1.db.getMessages(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); const mermaid = mermaidAPI.getConfig(); expect(mermaid.logLevel).toBe(0); expect(bounds.startx).toBe(0); @@ -1828,11 +1757,11 @@ Alice->Bob: Hello Bob, how are you? Note over Alice,Bob: Looks Note over Bob,Alice: Looks back `; + const diagram1 = await Diagram.fromText(str); // mermaidAPI.initialize({logLevel:0}) - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin); @@ -1844,10 +1773,10 @@ sequenceDiagram Alice->Bob: Hello Bob, how are you? Bob->Alice: Fine!`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); expect(bounds.stopx).toBe(conf.width * 2 + conf.actorMargin); @@ -1860,10 +1789,10 @@ Alice->Bob: Hello Bob, how are you? Note right of Bob: Bob thinks Bob->Alice: Fine!`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); @@ -1879,10 +1808,10 @@ Alice->Bob: Hello Bob, how are you? Note left of Alice: Bob thinks Bob->Alice: Fine!`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2); expect(bounds.starty).toBe(0); @@ -1896,11 +1825,11 @@ Alice->>Bob:wrap: Hello Bob, how are you? If you are not available right now, I Note left of Alice: Bob thinks Bob->>Alice: Fine!`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); - const msgs = diagram.db.getMessages(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); + const msgs = diagram1.db.getMessages(); expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2); expect(bounds.starty).toBe(0); expect(msgs[0].wrap).toBe(true); @@ -1918,10 +1847,11 @@ Note left of Alice: Bob thinks Bob->>Alice: Fine!`; await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); - const msgs = diagram.db.getMessages(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); + const msgs = diagram1.db.getMessages(); const mermaid = mermaidAPI.getConfig(); expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2); expect(bounds.starty).toBe(0); @@ -1941,10 +1871,12 @@ Note left of Alice: Bob thinks Bob->>Alice: Fine!`; await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); - const { bounds, models } = diagram.renderer.bounds.getBounds(); - const msgs = diagram.db.getMessages(); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); + + const { bounds, models } = diagram1.renderer.bounds.getBounds(); + const msgs = diagram1.db.getMessages(); const mermaid = mermaidAPI.getConfig(); expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2); expect(bounds.starty).toBe(0); @@ -1963,10 +1895,11 @@ Note left of Alice: Bob thinks Bob->>Alice: Fine!`; // mermaidAPI.initialize({ logLevel: 0 }); await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); - const msgs = diagram.db.getMessages(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); + const msgs = diagram1.db.getMessages(); const mermaid = mermaidAPI.getConfig(); expect(bounds.startx).toBe(-(conf.width / 2) - conf.actorMargin / 2); expect(bounds.starty).toBe(0); @@ -1986,10 +1919,10 @@ Alice->Bob: Hello Bob, how are you? loop Cheers Bob->Alice: Fine! end`; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); @@ -2004,9 +1937,9 @@ end`; Bob->Alice: I feel surrounded by darkness end `; - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const diagram1 = await Diagram.fromText(str); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); @@ -2050,11 +1983,11 @@ describe('when rendering a sequenceDiagram with actor mirror activated', () => { const str = ` sequenceDiagram participant Alice`; - diagram.renderer.bounds.init(); - await mermaidAPI.parse(str); - await diagram.renderer.draw(str, 'tst', '1.2.3', diagram); + const diagram1 = await Diagram.fromText(str); + diagram1.renderer.bounds.init(); + await diagram1.renderer.draw(str, 'tst', '1.2.3', diagram1); - const { bounds, models } = diagram.renderer.bounds.getBounds(); + const { bounds, models } = diagram1.renderer.bounds.getBounds(); expect(bounds.startx).toBe(0); expect(bounds.starty).toBe(0); expect(bounds.stopx).toBe(conf.width); From ffa7804af0701b3d044d6794e36bd9132d6c7e8d Mon Sep 17 00:00:00 2001 From: saurabhg772244 Date: Wed, 12 Feb 2025 16:04:22 +0530 Subject: [PATCH 170/230] added changeset --- .changeset/silver-olives-marry.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/silver-olives-marry.md diff --git a/.changeset/silver-olives-marry.md b/.changeset/silver-olives-marry.md new file mode 100644 index 000000000..d709b17ba --- /dev/null +++ b/.changeset/silver-olives-marry.md @@ -0,0 +1,5 @@ +--- +'mermaid': patch +--- + +fix: `mermaidAPI.getDiagramFromText()` now returns a new different db for each sequence diagram. Added unique IDs for messages. From 6ca8f46d3710e32972544716c9de6f19ad106bae Mon Sep 17 00:00:00 2001 From: saurabhg772244 Date: Wed, 12 Feb 2025 16:15:06 +0530 Subject: [PATCH 171/230] Updated unit tests. --- packages/mermaid/src/mermaidAPI.spec.ts | 29 +++++++++---------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/packages/mermaid/src/mermaidAPI.spec.ts b/packages/mermaid/src/mermaidAPI.spec.ts index e51d419d3..1a2289df2 100644 --- a/packages/mermaid/src/mermaidAPI.spec.ts +++ b/packages/mermaid/src/mermaidAPI.spec.ts @@ -67,10 +67,11 @@ vi.mock('stylis', () => { import { compile, serialize } from 'stylis'; import { Diagram } from './Diagram.js'; -import { decodeEntities, encodeEntities } from './utils.js'; -import { toBase64 } from './utils/base64.js'; import { ClassDB } from './diagrams/class/classDb.js'; import { FlowDB } from './diagrams/flowchart/flowDb.js'; +import { SequenceDB } from './diagrams/sequence/sequenceDb.js'; +import { decodeEntities, encodeEntities } from './utils.js'; +import { toBase64 } from './utils/base64.js'; /** * @see https://vitest.dev/guide/mocking.html Mock part of a module @@ -899,28 +900,18 @@ graph TD;A--x|text including URL space|B;`) ); const sequenceDiagram2 = await mermaidAPI.getDiagramFromText( `sequenceDiagram + actor A1 Alice->>+John: Hello John, how are you? Alice->>+John: John, can you hear me? John-->>-Alice: Hi Alice, I can hear you! John-->>-Alice: I feel great!` ); - // Since sequenceDiagram will return same Db object each time, we can compare the db to be same. - expect(sequenceDiagram1.db).toBe(sequenceDiagram2.db); + + // Since sequenceDiagram will return new Db object each time, we can compare the db to be different. + expect(sequenceDiagram1.db).not.toBe(sequenceDiagram2.db); + assert(sequenceDiagram1.db instanceof SequenceDB); + assert(sequenceDiagram2.db instanceof SequenceDB); + expect(sequenceDiagram1.db.getActors()).not.toEqual(sequenceDiagram2.db.getActors()); }); }); - - // Sequence Diagram currently uses a singleton DB, so this test will fail - it.fails('should not modify db when rendering different sequence diagrams', async () => { - const sequenceDiagram1 = await mermaidAPI.getDiagramFromText( - `sequenceDiagram - Alice->>Bob: Hello Bob, how are you? - Bob-->>John: How about you John?` - ); - const sequenceDiagram2 = await mermaidAPI.getDiagramFromText( - `sequenceDiagram - Alice->>Bob: Hello Bob, how are you? - Bob-->>John: How about you John?` - ); - expect(sequenceDiagram1.db).not.toBe(sequenceDiagram2.db); - }); }); From 72cf84cb40322baec2987cbe27baf2eaf3cc5537 Mon Sep 17 00:00:00 2001 From: Steph <35910788+huynhicode@users.noreply.github.com> Date: Wed, 12 Feb 2025 14:02:01 -0800 Subject: [PATCH 172/230] wip --- .../src/docs/.vitepress/components/TopBar.vue | 118 ++++++++++++++++-- 1 file changed, 107 insertions(+), 11 deletions(-) diff --git a/packages/mermaid/src/docs/.vitepress/components/TopBar.vue b/packages/mermaid/src/docs/.vitepress/components/TopBar.vue index ff0b344c5..51fd413cc 100644 --- a/packages/mermaid/src/docs/.vitepress/components/TopBar.vue +++ b/packages/mermaid/src/docs/.vitepress/components/TopBar.vue @@ -1,40 +1,136 @@