mrmd-js 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/index.cjs +347 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +346 -19
- package/dist/index.js.map +1 -1
- package/dist/mrmd-js.iife.js +347 -18
- package/dist/mrmd-js.iife.js.map +1 -1
- package/package.json +1 -1
- package/src/execute/index.js +3 -0
- package/src/execute/javascript.js +13 -1
- package/src/execute/mermaid.js +137 -0
- package/src/index.js +3 -0
- package/src/session/console-capture.js +36 -0
- package/src/transform/async.js +165 -15
- package/src/transform/persistence.js +2 -2
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Maxime Rivest
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -56,6 +56,15 @@ const DEFAULT_FEATURES = {
|
|
|
56
56
|
* @typedef {import('./context/interface.js').LogEntry} LogEntry
|
|
57
57
|
*/
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Check if a value is a Promise
|
|
61
|
+
* @param {*} value
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
function isPromise(value) {
|
|
65
|
+
return value && typeof value === 'object' && typeof value.then === 'function';
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
/**
|
|
60
69
|
* Format arguments for logging
|
|
61
70
|
* @param {Array<*>} args
|
|
@@ -78,6 +87,21 @@ function formatArgs(args) {
|
|
|
78
87
|
.join(' ');
|
|
79
88
|
}
|
|
80
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Check args for Promises and return warning message if found
|
|
92
|
+
* @param {Array<*>} args
|
|
93
|
+
* @returns {string | null}
|
|
94
|
+
*/
|
|
95
|
+
function checkForPromises(args) {
|
|
96
|
+
for (const arg of args) {
|
|
97
|
+
if (isPromise(arg)) {
|
|
98
|
+
return '⚠️ Promise detected - this value needs to be awaited. ' +
|
|
99
|
+
'Add "await" before the async call, or assign it to get the resolved value.';
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
81
105
|
/**
|
|
82
106
|
* Create a console capture for a window context
|
|
83
107
|
*/
|
|
@@ -120,11 +144,23 @@ class ConsoleCapture {
|
|
|
120
144
|
// Intercept methods
|
|
121
145
|
console.log = (...args) => {
|
|
122
146
|
this.#queue.push({ type: 'log', args, timestamp: Date.now() });
|
|
147
|
+
// Check for unresolved Promises and warn
|
|
148
|
+
const promiseWarning = checkForPromises(args);
|
|
149
|
+
if (promiseWarning) {
|
|
150
|
+
this.#queue.push({ type: 'warn', args: [promiseWarning], timestamp: Date.now() });
|
|
151
|
+
this.#originalConsole?.warn?.(promiseWarning);
|
|
152
|
+
}
|
|
123
153
|
this.#originalConsole?.log?.(...args);
|
|
124
154
|
};
|
|
125
155
|
|
|
126
156
|
console.info = (...args) => {
|
|
127
157
|
this.#queue.push({ type: 'info', args, timestamp: Date.now() });
|
|
158
|
+
// Check for unresolved Promises and warn
|
|
159
|
+
const promiseWarning = checkForPromises(args);
|
|
160
|
+
if (promiseWarning) {
|
|
161
|
+
this.#queue.push({ type: 'warn', args: [promiseWarning], timestamp: Date.now() });
|
|
162
|
+
this.#originalConsole?.warn?.(promiseWarning);
|
|
163
|
+
}
|
|
128
164
|
this.#originalConsole?.info?.(...args);
|
|
129
165
|
};
|
|
130
166
|
|
|
@@ -1510,12 +1546,12 @@ function transformForPersistence(code) {
|
|
|
1510
1546
|
|
|
1511
1547
|
// Check for const/let keywords
|
|
1512
1548
|
if (isWordBoundary(code, i)) {
|
|
1513
|
-
if (code.slice(i, i + 5) === 'const' &&
|
|
1549
|
+
if (code.slice(i, i + 5) === 'const' && isWordBoundaryAfter(code, i + 5)) {
|
|
1514
1550
|
result += 'var';
|
|
1515
1551
|
i += 5;
|
|
1516
1552
|
continue;
|
|
1517
1553
|
}
|
|
1518
|
-
if (code.slice(i, i + 3) === 'let' &&
|
|
1554
|
+
if (code.slice(i, i + 3) === 'let' && isWordBoundaryAfter(code, i + 3)) {
|
|
1519
1555
|
result += 'var';
|
|
1520
1556
|
i += 3;
|
|
1521
1557
|
continue;
|
|
@@ -1552,15 +1588,124 @@ function isWordBoundary(code, pos) {
|
|
|
1552
1588
|
return true;
|
|
1553
1589
|
}
|
|
1554
1590
|
|
|
1591
|
+
/**
|
|
1592
|
+
* Check if position after keyword is a word boundary
|
|
1593
|
+
* @param {string} code
|
|
1594
|
+
* @param {number} pos - Position after the keyword
|
|
1595
|
+
* @returns {boolean}
|
|
1596
|
+
*/
|
|
1597
|
+
function isWordBoundaryAfter(code, pos) {
|
|
1598
|
+
if (pos >= code.length) return true;
|
|
1599
|
+
return !/[a-zA-Z0-9_$]/.test(code[pos]);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1555
1602
|
/**
|
|
1556
1603
|
* Async Transform
|
|
1557
1604
|
*
|
|
1558
|
-
* Wraps code to support top-level await.
|
|
1605
|
+
* Wraps code to support top-level await and auto-awaits common async patterns.
|
|
1606
|
+
* This makes JavaScript feel more linear like Python/R/Julia.
|
|
1559
1607
|
* @module transform/async
|
|
1560
1608
|
*/
|
|
1561
1609
|
|
|
1610
|
+
|
|
1562
1611
|
/**
|
|
1563
|
-
*
|
|
1612
|
+
* Auto-insert await before common async function calls
|
|
1613
|
+
* This makes JavaScript feel more linear like Python/R
|
|
1614
|
+
*
|
|
1615
|
+
* @param {string} code - Source code
|
|
1616
|
+
* @returns {string} Code with auto-awaits inserted
|
|
1617
|
+
*/
|
|
1618
|
+
function autoInsertAwaits(code) {
|
|
1619
|
+
// Don't process if code already uses await extensively
|
|
1620
|
+
// (user knows what they're doing)
|
|
1621
|
+
const awaitCount = (code.match(/\bawait\b/g) || []).length;
|
|
1622
|
+
const lines = code.split('\n').length;
|
|
1623
|
+
if (awaitCount > lines / 2) {
|
|
1624
|
+
return code;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
let result = code;
|
|
1628
|
+
|
|
1629
|
+
// Track positions to avoid double-processing
|
|
1630
|
+
// We need to be careful not to add await before already-awaited expressions
|
|
1631
|
+
|
|
1632
|
+
// First, temporarily replace existing awaits to protect them
|
|
1633
|
+
const awaitPlaceholders = [];
|
|
1634
|
+
result = result.replace(/\bawait\s+/g, (match) => {
|
|
1635
|
+
const placeholder = `__AWAIT_PLACEHOLDER_${awaitPlaceholders.length}__`;
|
|
1636
|
+
awaitPlaceholders.push(match);
|
|
1637
|
+
return placeholder;
|
|
1638
|
+
});
|
|
1639
|
+
|
|
1640
|
+
// Also protect strings, comments, and template literals
|
|
1641
|
+
const protectedStrings = [];
|
|
1642
|
+
|
|
1643
|
+
// Protect comments FIRST (before strings) to handle apostrophes in comments like "doesn't"
|
|
1644
|
+
result = result.replace(/\/\/[^\n]*/g, (match) => {
|
|
1645
|
+
const placeholder = `__PROTECTED_${protectedStrings.length}__`;
|
|
1646
|
+
protectedStrings.push(match);
|
|
1647
|
+
return placeholder;
|
|
1648
|
+
});
|
|
1649
|
+
result = result.replace(/\/\*[\s\S]*?\*\//g, (match) => {
|
|
1650
|
+
const placeholder = `__PROTECTED_${protectedStrings.length}__`;
|
|
1651
|
+
protectedStrings.push(match);
|
|
1652
|
+
return placeholder;
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
// Protect template literals
|
|
1656
|
+
result = result.replace(/`(?:[^`\\]|\\.)*`/g, (match) => {
|
|
1657
|
+
const placeholder = `__PROTECTED_${protectedStrings.length}__`;
|
|
1658
|
+
protectedStrings.push(match);
|
|
1659
|
+
return placeholder;
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
// Protect strings (after comments, so apostrophes in comments don't interfere)
|
|
1663
|
+
result = result.replace(/"(?:[^"\\]|\\.)*"/g, (match) => {
|
|
1664
|
+
const placeholder = `__PROTECTED_${protectedStrings.length}__`;
|
|
1665
|
+
protectedStrings.push(match);
|
|
1666
|
+
return placeholder;
|
|
1667
|
+
});
|
|
1668
|
+
result = result.replace(/'(?:[^'\\]|\\.)*'/g, (match) => {
|
|
1669
|
+
const placeholder = `__PROTECTED_${protectedStrings.length}__`;
|
|
1670
|
+
protectedStrings.push(match);
|
|
1671
|
+
return placeholder;
|
|
1672
|
+
});
|
|
1673
|
+
|
|
1674
|
+
// Now auto-insert awaits for common patterns
|
|
1675
|
+
// fetch(...) -> await fetch(...)
|
|
1676
|
+
result = result.replace(/\bfetch\s*\(/g, 'await fetch(');
|
|
1677
|
+
|
|
1678
|
+
// import(...) -> await import(...)
|
|
1679
|
+
// But not "import x from" statements
|
|
1680
|
+
result = result.replace(/(?<![.\w])import\s*\(/g, 'await import(');
|
|
1681
|
+
|
|
1682
|
+
// .json() .text() .blob() etc on response objects
|
|
1683
|
+
// These methods also return Promises, so we need to await both:
|
|
1684
|
+
// response.json() -> await (await response).json()
|
|
1685
|
+
result = result.replace(/([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\.\s*json\s*\(\s*\)/g, 'await (await $1).json()');
|
|
1686
|
+
result = result.replace(/([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\.\s*text\s*\(\s*\)/g, 'await (await $1).text()');
|
|
1687
|
+
result = result.replace(/([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\.\s*blob\s*\(\s*\)/g, 'await (await $1).blob()');
|
|
1688
|
+
result = result.replace(/([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\.\s*arrayBuffer\s*\(\s*\)/g, 'await (await $1).arrayBuffer()');
|
|
1689
|
+
result = result.replace(/([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\.\s*formData\s*\(\s*\)/g, 'await (await $1).formData()');
|
|
1690
|
+
|
|
1691
|
+
// Restore protected content
|
|
1692
|
+
for (let i = protectedStrings.length - 1; i >= 0; i--) {
|
|
1693
|
+
result = result.replace(`__PROTECTED_${i}__`, protectedStrings[i]);
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
// Restore existing awaits
|
|
1697
|
+
for (let i = awaitPlaceholders.length - 1; i >= 0; i--) {
|
|
1698
|
+
result = result.replace(`__AWAIT_PLACEHOLDER_${i}__`, awaitPlaceholders[i]);
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
// Clean up any double awaits we might have introduced
|
|
1702
|
+
result = result.replace(/\bawait\s+await\b/g, 'await');
|
|
1703
|
+
|
|
1704
|
+
return result;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/**
|
|
1708
|
+
* Check if code contains top-level await (or will after auto-insertion)
|
|
1564
1709
|
* @param {string} code
|
|
1565
1710
|
* @returns {boolean}
|
|
1566
1711
|
*/
|
|
@@ -1569,16 +1714,17 @@ function hasTopLevelAwait(code) {
|
|
|
1569
1714
|
// This is a heuristic; a proper check would need AST parsing
|
|
1570
1715
|
|
|
1571
1716
|
// Remove strings, comments, and regex to avoid false positives
|
|
1717
|
+
// IMPORTANT: Remove comments FIRST to handle apostrophes in comments like "doesn't"
|
|
1572
1718
|
const cleaned = code
|
|
1573
|
-
// Remove
|
|
1574
|
-
.replace(/`[^`]*`/g, '')
|
|
1575
|
-
// Remove strings
|
|
1576
|
-
.replace(/"(?:[^"\\]|\\.)*"/g, '')
|
|
1577
|
-
.replace(/'(?:[^'\\]|\\.)*'/g, '')
|
|
1578
|
-
// Remove single-line comments
|
|
1719
|
+
// Remove single-line comments FIRST (before strings)
|
|
1579
1720
|
.replace(/\/\/[^\n]*/g, '')
|
|
1580
1721
|
// Remove multi-line comments
|
|
1581
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
1722
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
1723
|
+
// Remove template literals
|
|
1724
|
+
.replace(/`[^`]*`/g, '')
|
|
1725
|
+
// Remove strings (after comments, so apostrophes in comments don't interfere)
|
|
1726
|
+
.replace(/"(?:[^"\\]|\\.)*"/g, '')
|
|
1727
|
+
.replace(/'(?:[^'\\]|\\.)*'/g, '');
|
|
1582
1728
|
let i = 0;
|
|
1583
1729
|
|
|
1584
1730
|
while (i < cleaned.length) {
|
|
@@ -1650,19 +1796,50 @@ ${code}
|
|
|
1650
1796
|
* @returns {string} Wrapped code that returns last expression
|
|
1651
1797
|
*/
|
|
1652
1798
|
function wrapWithLastExpression(code) {
|
|
1653
|
-
|
|
1799
|
+
// Auto-insert awaits for common async patterns (fetch, import, .json(), etc.)
|
|
1800
|
+
// This makes JavaScript feel more linear like Python/R/Julia
|
|
1801
|
+
const autoAwaitedCode = autoInsertAwaits(code);
|
|
1654
1802
|
|
|
1655
|
-
//
|
|
1656
|
-
|
|
1803
|
+
// Check if code needs async (either explicit await or auto-inserted)
|
|
1804
|
+
const needsAsync = hasTopLevelAwait(autoAwaitedCode);
|
|
1805
|
+
|
|
1806
|
+
if (needsAsync) {
|
|
1807
|
+
// For code with await, wrap in async IIFE
|
|
1808
|
+
// This allows await to work at the "top level" of the user's code
|
|
1809
|
+
const asyncWrappedCode = `(async () => {\n${autoAwaitedCode}\n})()`;
|
|
1810
|
+
|
|
1811
|
+
// Now wrap to capture the result
|
|
1812
|
+
// Use indirect eval (0, eval)() to run in global scope so var declarations persist
|
|
1813
|
+
const wrapped = `
|
|
1814
|
+
;(async function() {
|
|
1815
|
+
let __result__;
|
|
1816
|
+
try {
|
|
1817
|
+
__result__ = await (0, eval)(${JSON.stringify(asyncWrappedCode)});
|
|
1818
|
+
} catch (e) {
|
|
1819
|
+
if (e instanceof SyntaxError) {
|
|
1820
|
+
await (0, eval)(${JSON.stringify(asyncWrappedCode)});
|
|
1821
|
+
__result__ = undefined;
|
|
1822
|
+
} else {
|
|
1823
|
+
throw e;
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
return __result__;
|
|
1827
|
+
})()`;
|
|
1828
|
+
return wrapped.trim();
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
// No async needed - use simpler synchronous wrapper
|
|
1832
|
+
// Use indirect eval (0, eval)() to run in global scope so var declarations persist
|
|
1833
|
+
// Note: Still use autoAwaitedCode in case auto-awaits were added but hasTopLevelAwait missed them
|
|
1657
1834
|
const wrapped = `
|
|
1658
|
-
;(
|
|
1835
|
+
;(function() {
|
|
1659
1836
|
let __result__;
|
|
1660
1837
|
try {
|
|
1661
|
-
__result__ = eval(${JSON.stringify(
|
|
1838
|
+
__result__ = (0, eval)(${JSON.stringify(autoAwaitedCode)});
|
|
1662
1839
|
} catch (e) {
|
|
1663
1840
|
if (e instanceof SyntaxError) {
|
|
1664
1841
|
// Code might be statements, not expression
|
|
1665
|
-
eval(${JSON.stringify(
|
|
1842
|
+
(0, eval)(${JSON.stringify(autoAwaitedCode)});
|
|
1666
1843
|
__result__ = undefined;
|
|
1667
1844
|
} else {
|
|
1668
1845
|
throw e;
|
|
@@ -1760,7 +1937,19 @@ class JavaScriptExecutor extends BaseExecutor {
|
|
|
1760
1937
|
|
|
1761
1938
|
try {
|
|
1762
1939
|
// Execute in context (pass execId for input() support)
|
|
1763
|
-
|
|
1940
|
+
let rawResult = await context.execute(wrapped, { execId: options.execId });
|
|
1941
|
+
|
|
1942
|
+
// Auto-await if result is a Promise (catch cases not handled by auto-await transform)
|
|
1943
|
+
if (rawResult.result && typeof rawResult.result === 'object' && typeof rawResult.result.then === 'function') {
|
|
1944
|
+
try {
|
|
1945
|
+
rawResult.result = await rawResult.result;
|
|
1946
|
+
} catch (promiseError) {
|
|
1947
|
+
// Promise rejected - treat as error
|
|
1948
|
+
rawResult.error = promiseError instanceof Error ? promiseError : new Error(String(promiseError));
|
|
1949
|
+
rawResult.result = undefined;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1764
1953
|
const duration = performance.now() - startTime;
|
|
1765
1954
|
|
|
1766
1955
|
// Format result
|
|
@@ -6198,6 +6387,143 @@ function createCssExecutor() {
|
|
|
6198
6387
|
return new CssExecutor();
|
|
6199
6388
|
}
|
|
6200
6389
|
|
|
6390
|
+
/**
|
|
6391
|
+
* Mermaid Executor
|
|
6392
|
+
*
|
|
6393
|
+
* Executes Mermaid diagram cells by rendering them to SVG.
|
|
6394
|
+
* Loads mermaid from CDN on first use and returns HTML displayData.
|
|
6395
|
+
*
|
|
6396
|
+
* @module execute/mermaid
|
|
6397
|
+
*/
|
|
6398
|
+
|
|
6399
|
+
|
|
6400
|
+
/**
|
|
6401
|
+
* @typedef {import('../session/context/interface.js').ExecutionContext} ExecutionContext
|
|
6402
|
+
* @typedef {import('../types/execution.js').ExecuteOptions} ExecuteOptions
|
|
6403
|
+
* @typedef {import('../types/execution.js').ExecutionResult} ExecutionResult
|
|
6404
|
+
* @typedef {import('../types/execution.js').DisplayData} DisplayData
|
|
6405
|
+
*/
|
|
6406
|
+
|
|
6407
|
+
/** CDN URL for mermaid */
|
|
6408
|
+
const MERMAID_CDN = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
|
|
6409
|
+
|
|
6410
|
+
/** Counter for unique diagram IDs */
|
|
6411
|
+
let diagramCounter = 0;
|
|
6412
|
+
|
|
6413
|
+
/**
|
|
6414
|
+
* Load mermaid from CDN if not already loaded
|
|
6415
|
+
* @returns {Promise<void>}
|
|
6416
|
+
*/
|
|
6417
|
+
async function ensureMermaidLoaded() {
|
|
6418
|
+
// Check if already loaded
|
|
6419
|
+
if (typeof window !== 'undefined' && window.mermaid) {
|
|
6420
|
+
return;
|
|
6421
|
+
}
|
|
6422
|
+
|
|
6423
|
+
// Load from CDN
|
|
6424
|
+
return new Promise((resolve, reject) => {
|
|
6425
|
+
const script = document.createElement('script');
|
|
6426
|
+
script.src = MERMAID_CDN;
|
|
6427
|
+
script.onload = () => {
|
|
6428
|
+
// Initialize mermaid with safe defaults
|
|
6429
|
+
window.mermaid.initialize({
|
|
6430
|
+
startOnLoad: false,
|
|
6431
|
+
theme: 'default',
|
|
6432
|
+
securityLevel: 'loose', // Allow clicks/links in diagrams
|
|
6433
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
6434
|
+
});
|
|
6435
|
+
resolve();
|
|
6436
|
+
};
|
|
6437
|
+
script.onerror = () => reject(new Error('Failed to load mermaid from CDN'));
|
|
6438
|
+
document.head.appendChild(script);
|
|
6439
|
+
});
|
|
6440
|
+
}
|
|
6441
|
+
|
|
6442
|
+
/**
|
|
6443
|
+
* Mermaid executor - renders Mermaid diagrams to SVG
|
|
6444
|
+
*/
|
|
6445
|
+
class MermaidExecutor extends BaseExecutor {
|
|
6446
|
+
/** @type {readonly string[]} */
|
|
6447
|
+
languages = ['mermaid'];
|
|
6448
|
+
|
|
6449
|
+
/**
|
|
6450
|
+
* Execute Mermaid diagram cell
|
|
6451
|
+
* @param {string} code - Mermaid diagram definition
|
|
6452
|
+
* @param {ExecutionContext} context - Execution context
|
|
6453
|
+
* @param {ExecuteOptions} [options] - Execution options
|
|
6454
|
+
* @returns {Promise<ExecutionResult>}
|
|
6455
|
+
*/
|
|
6456
|
+
async execute(code, context, options = {}) {
|
|
6457
|
+
const startTime = performance.now();
|
|
6458
|
+
|
|
6459
|
+
try {
|
|
6460
|
+
// Ensure mermaid is loaded
|
|
6461
|
+
await ensureMermaidLoaded();
|
|
6462
|
+
|
|
6463
|
+
// Generate unique ID for this diagram
|
|
6464
|
+
const diagramId = `mermaid-diagram-${++diagramCounter}`;
|
|
6465
|
+
|
|
6466
|
+
// Render the diagram
|
|
6467
|
+
const { svg } = await window.mermaid.render(diagramId, code.trim());
|
|
6468
|
+
|
|
6469
|
+
const duration = performance.now() - startTime;
|
|
6470
|
+
|
|
6471
|
+
// Build display data with the rendered SVG
|
|
6472
|
+
/** @type {DisplayData[]} */
|
|
6473
|
+
const displayData = [
|
|
6474
|
+
{
|
|
6475
|
+
data: {
|
|
6476
|
+
'text/html': svg,
|
|
6477
|
+
'text/plain': `[Mermaid diagram rendered]`,
|
|
6478
|
+
},
|
|
6479
|
+
metadata: {
|
|
6480
|
+
mermaid: true,
|
|
6481
|
+
diagramId,
|
|
6482
|
+
},
|
|
6483
|
+
},
|
|
6484
|
+
];
|
|
6485
|
+
|
|
6486
|
+
return {
|
|
6487
|
+
success: true,
|
|
6488
|
+
stdout: '',
|
|
6489
|
+
stderr: '',
|
|
6490
|
+
result: undefined,
|
|
6491
|
+
displayData,
|
|
6492
|
+
assets: [],
|
|
6493
|
+
executionCount: 0,
|
|
6494
|
+
duration,
|
|
6495
|
+
};
|
|
6496
|
+
} catch (error) {
|
|
6497
|
+
const duration = performance.now() - startTime;
|
|
6498
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6499
|
+
|
|
6500
|
+
// Return error as stderr with helpful message
|
|
6501
|
+
return {
|
|
6502
|
+
success: false,
|
|
6503
|
+
stdout: '',
|
|
6504
|
+
stderr: `Mermaid rendering error: ${errorMessage}`,
|
|
6505
|
+
result: undefined,
|
|
6506
|
+
error: {
|
|
6507
|
+
type: 'MermaidError',
|
|
6508
|
+
message: errorMessage,
|
|
6509
|
+
},
|
|
6510
|
+
displayData: [],
|
|
6511
|
+
assets: [],
|
|
6512
|
+
executionCount: 0,
|
|
6513
|
+
duration,
|
|
6514
|
+
};
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
}
|
|
6518
|
+
|
|
6519
|
+
/**
|
|
6520
|
+
* Create a Mermaid executor
|
|
6521
|
+
* @returns {MermaidExecutor}
|
|
6522
|
+
*/
|
|
6523
|
+
function createMermaidExecutor() {
|
|
6524
|
+
return new MermaidExecutor();
|
|
6525
|
+
}
|
|
6526
|
+
|
|
6201
6527
|
/**
|
|
6202
6528
|
* Execute Module
|
|
6203
6529
|
*
|
|
@@ -6216,6 +6542,7 @@ function createDefaultExecutorRegistry() {
|
|
|
6216
6542
|
registry.register(new JavaScriptExecutor());
|
|
6217
6543
|
registry.register(new HtmlExecutor());
|
|
6218
6544
|
registry.register(new CssExecutor());
|
|
6545
|
+
registry.register(new MermaidExecutor());
|
|
6219
6546
|
return registry;
|
|
6220
6547
|
}
|
|
6221
6548
|
|
|
@@ -7542,6 +7869,7 @@ exports.HtmlRenderer = HtmlRenderer;
|
|
|
7542
7869
|
exports.IframeContext = IframeContext;
|
|
7543
7870
|
exports.JavaScriptExecutor = JavaScriptExecutor;
|
|
7544
7871
|
exports.MainContext = MainContext;
|
|
7872
|
+
exports.MermaidExecutor = MermaidExecutor;
|
|
7545
7873
|
exports.MrpRuntime = MrpRuntime;
|
|
7546
7874
|
exports.RUNTIME_NAME = RUNTIME_NAME;
|
|
7547
7875
|
exports.RUNTIME_VERSION = RUNTIME_VERSION;
|
|
@@ -7561,6 +7889,7 @@ exports.createHtmlRenderer = createHtmlRenderer;
|
|
|
7561
7889
|
exports.createIframeContext = createIframeContext;
|
|
7562
7890
|
exports.createJavaScriptExecutor = createJavaScriptExecutor;
|
|
7563
7891
|
exports.createMainContext = createMainContext;
|
|
7892
|
+
exports.createMermaidExecutor = createMermaidExecutor;
|
|
7564
7893
|
exports.createRuntime = createRuntime;
|
|
7565
7894
|
exports.createSession = createSession;
|
|
7566
7895
|
exports.createSessionManager = createSessionManager;
|