mrmd-js 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +842 -0
- package/dist/index.cjs +7613 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +7530 -0
- package/dist/index.js.map +1 -0
- package/dist/mrmd-js.iife.js +7618 -0
- package/dist/mrmd-js.iife.js.map +1 -0
- package/package.json +47 -0
- package/src/analysis/format.js +371 -0
- package/src/analysis/index.js +18 -0
- package/src/analysis/is-complete.js +394 -0
- package/src/constants.js +44 -0
- package/src/execute/css.js +205 -0
- package/src/execute/html.js +162 -0
- package/src/execute/index.js +41 -0
- package/src/execute/interface.js +144 -0
- package/src/execute/javascript.js +197 -0
- package/src/execute/registry.js +245 -0
- package/src/index.js +136 -0
- package/src/lsp/complete.js +353 -0
- package/src/lsp/format.js +310 -0
- package/src/lsp/hover.js +126 -0
- package/src/lsp/index.js +55 -0
- package/src/lsp/inspect.js +466 -0
- package/src/lsp/parse.js +455 -0
- package/src/lsp/variables.js +283 -0
- package/src/runtime.js +518 -0
- package/src/session/console-capture.js +181 -0
- package/src/session/context/iframe.js +407 -0
- package/src/session/context/index.js +12 -0
- package/src/session/context/interface.js +38 -0
- package/src/session/context/main.js +357 -0
- package/src/session/index.js +16 -0
- package/src/session/manager.js +327 -0
- package/src/session/session.js +678 -0
- package/src/transform/async.js +133 -0
- package/src/transform/extract.js +251 -0
- package/src/transform/index.js +10 -0
- package/src/transform/persistence.js +176 -0
- package/src/types/analysis.js +24 -0
- package/src/types/capabilities.js +44 -0
- package/src/types/completion.js +47 -0
- package/src/types/execution.js +62 -0
- package/src/types/index.js +16 -0
- package/src/types/inspection.js +39 -0
- package/src/types/session.js +32 -0
- package/src/types/streaming.js +74 -0
- package/src/types/variables.js +54 -0
- package/src/utils/ansi-renderer.js +301 -0
- package/src/utils/css-applicator.js +149 -0
- package/src/utils/html-renderer.js +355 -0
- package/src/utils/index.js +25 -0
package/README.md
ADDED
|
@@ -0,0 +1,842 @@
|
|
|
1
|
+
# mrmd-js
|
|
2
|
+
|
|
3
|
+
MRP-compliant browser JavaScript runtime for notebook-style code execution with LSP-like features, multi-session isolation, and rich output rendering.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Notebook-style execution** - Variables persist across cell executions
|
|
8
|
+
- **MRP Protocol compliance** - Implements the MRMD Runtime Protocol
|
|
9
|
+
- **Multi-language support** - JavaScript, HTML, and CSS executors
|
|
10
|
+
- **LSP-like features** - Runtime-aware completions, hover info, variable inspection
|
|
11
|
+
- **Session isolation** - Multiple isolated execution contexts (iframe or main window)
|
|
12
|
+
- **Rich output** - Display data with HTML, CSS, images, and more
|
|
13
|
+
- **Streaming execution** - Real-time output with async generators
|
|
14
|
+
- **Code analysis** - Statement completeness checking and formatting
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install mrmd-js
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### Using MrpRuntime (Recommended)
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
import { MrpRuntime } from 'mrmd-js';
|
|
28
|
+
|
|
29
|
+
const runtime = new MrpRuntime();
|
|
30
|
+
|
|
31
|
+
// Create a session
|
|
32
|
+
const session = runtime.createSession({ language: 'javascript' });
|
|
33
|
+
|
|
34
|
+
// Execute code - variables persist across executions
|
|
35
|
+
await session.execute(`
|
|
36
|
+
const data = [1, 2, 3, 4, 5];
|
|
37
|
+
const sum = data.reduce((a, b) => a + b, 0);
|
|
38
|
+
console.log("Sum:", sum);
|
|
39
|
+
`);
|
|
40
|
+
// Output: Sum: 15
|
|
41
|
+
|
|
42
|
+
// Variables persist!
|
|
43
|
+
const result = await session.execute(`
|
|
44
|
+
const doubled = data.map(n => n * 2);
|
|
45
|
+
doubled
|
|
46
|
+
`);
|
|
47
|
+
console.log(result.resultString); // "[2, 4, 6, 8, 10]"
|
|
48
|
+
|
|
49
|
+
// Get completions
|
|
50
|
+
const completions = session.complete('data.', 5);
|
|
51
|
+
// → { matches: [{ label: 'map', kind: 'function' }, ...] }
|
|
52
|
+
|
|
53
|
+
// Get hover info
|
|
54
|
+
const hover = session.hover('sum', 1);
|
|
55
|
+
// → { found: true, name: 'sum', type: 'number', value: '15' }
|
|
56
|
+
|
|
57
|
+
// List all variables
|
|
58
|
+
const vars = session.listVariables();
|
|
59
|
+
// → [{ name: 'data', type: 'Array', expandable: true }, ...]
|
|
60
|
+
|
|
61
|
+
// Clean up
|
|
62
|
+
runtime.destroy();
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Using Session Directly
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
import { SessionManager, createSessionManager } from 'mrmd-js';
|
|
69
|
+
|
|
70
|
+
const manager = createSessionManager();
|
|
71
|
+
|
|
72
|
+
// Create isolated sessions
|
|
73
|
+
const dataSession = manager.create({ id: 'data', language: 'javascript' });
|
|
74
|
+
const vizSession = manager.create({ id: 'viz', language: 'javascript' });
|
|
75
|
+
|
|
76
|
+
// Each session has its own scope
|
|
77
|
+
await dataSession.execute('const x = 100');
|
|
78
|
+
await vizSession.execute('const x = 200');
|
|
79
|
+
|
|
80
|
+
dataSession.listVariables(); // x = 100
|
|
81
|
+
vizSession.listVariables(); // x = 200 - completely isolated!
|
|
82
|
+
|
|
83
|
+
// Clean up
|
|
84
|
+
manager.destroyAll();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## API Reference
|
|
88
|
+
|
|
89
|
+
### MrpRuntime
|
|
90
|
+
|
|
91
|
+
The main entry point implementing the MRP protocol.
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
import { MrpRuntime, createRuntime } from 'mrmd-js';
|
|
95
|
+
|
|
96
|
+
const runtime = new MrpRuntime({
|
|
97
|
+
maxSessions: 10, // Max concurrent sessions
|
|
98
|
+
defaultIsolation: 'iframe', // 'iframe' or 'main'
|
|
99
|
+
defaultAllowMainAccess: false, // Allow main window access
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Or use factory function
|
|
103
|
+
const runtime = createRuntime();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Capabilities
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
const caps = runtime.getCapabilities();
|
|
110
|
+
// {
|
|
111
|
+
// runtime: 'mrmd-js',
|
|
112
|
+
// version: '2.0.0',
|
|
113
|
+
// languages: ['javascript', 'html', 'css'],
|
|
114
|
+
// features: { execute: true, complete: true, ... },
|
|
115
|
+
// maxSessions: 10,
|
|
116
|
+
// environment: { userAgent: '...', platform: '...' }
|
|
117
|
+
// }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### Session Management
|
|
121
|
+
|
|
122
|
+
```javascript
|
|
123
|
+
// Create session
|
|
124
|
+
const session = runtime.createSession({
|
|
125
|
+
id: 'my-session', // Optional ID
|
|
126
|
+
language: 'javascript', // Language
|
|
127
|
+
isolation: 'iframe', // 'iframe' or 'main'
|
|
128
|
+
allowMainAccess: false, // Access main window from iframe
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Get/list sessions
|
|
132
|
+
runtime.getSession('my-session');
|
|
133
|
+
runtime.listSessions();
|
|
134
|
+
|
|
135
|
+
// Get or create
|
|
136
|
+
runtime.getOrCreateSession('my-session', { language: 'javascript' });
|
|
137
|
+
|
|
138
|
+
// Reset/destroy
|
|
139
|
+
runtime.resetSession('my-session');
|
|
140
|
+
runtime.destroySession('my-session');
|
|
141
|
+
runtime.destroy(); // Destroy all
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### Convenience Methods
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
// Execute in default session
|
|
148
|
+
const result = await runtime.execute('1 + 2');
|
|
149
|
+
const stream = runtime.executeStream('console.log("hi")');
|
|
150
|
+
|
|
151
|
+
// LSP features
|
|
152
|
+
runtime.complete('arr.', 4);
|
|
153
|
+
runtime.hover('myVar', 3);
|
|
154
|
+
runtime.inspect('obj', 2, { detail: 1 });
|
|
155
|
+
runtime.listVariables();
|
|
156
|
+
runtime.getVariable('myVar');
|
|
157
|
+
|
|
158
|
+
// Analysis
|
|
159
|
+
runtime.isComplete('const x = {'); // { status: 'incomplete', indent: ' ' }
|
|
160
|
+
await runtime.format('const x=1'); // { formatted: 'const x = 1;\n', changed: true }
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Session
|
|
164
|
+
|
|
165
|
+
Individual execution context with full LSP support.
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
// Get session info
|
|
169
|
+
session.getInfo();
|
|
170
|
+
// { id: '...', language: 'javascript', executionCount: 5, ... }
|
|
171
|
+
|
|
172
|
+
// Execute code
|
|
173
|
+
const result = await session.execute('const x = 1 + 2; x');
|
|
174
|
+
// {
|
|
175
|
+
// success: true,
|
|
176
|
+
// stdout: '',
|
|
177
|
+
// stderr: '',
|
|
178
|
+
// result: 3,
|
|
179
|
+
// resultString: '3',
|
|
180
|
+
// duration: 5,
|
|
181
|
+
// displayData: []
|
|
182
|
+
// }
|
|
183
|
+
|
|
184
|
+
// Streaming execution
|
|
185
|
+
for await (const event of session.executeStream('console.log("hi")')) {
|
|
186
|
+
if (event.type === 'stdout') console.log(event.text);
|
|
187
|
+
if (event.type === 'result') console.log(event.result);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Interrupt execution
|
|
191
|
+
session.interrupt();
|
|
192
|
+
|
|
193
|
+
// Reset session (clear all variables)
|
|
194
|
+
session.reset();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### LSP Features
|
|
198
|
+
|
|
199
|
+
```javascript
|
|
200
|
+
// Completions
|
|
201
|
+
const completions = session.complete('data.fi', 7);
|
|
202
|
+
// {
|
|
203
|
+
// matches: [{ label: 'filter', kind: 'function', valuePreview: 'ƒ' }, ...],
|
|
204
|
+
// cursorStart: 5,
|
|
205
|
+
// cursorEnd: 7
|
|
206
|
+
// }
|
|
207
|
+
|
|
208
|
+
// Hover
|
|
209
|
+
const hover = session.hover('myArray', 3);
|
|
210
|
+
// { found: true, name: 'myArray', type: 'Array', value: '[1, 2, 3]' }
|
|
211
|
+
|
|
212
|
+
// Inspect (detailed)
|
|
213
|
+
const info = session.inspect('user', 2, { detail: 1 });
|
|
214
|
+
// { found: true, name: 'user', type: 'Object', children: [...] }
|
|
215
|
+
|
|
216
|
+
// Variables
|
|
217
|
+
const vars = session.listVariables({ types: ['Object', 'Array'] });
|
|
218
|
+
// [{ name: 'user', type: 'Object', expandable: true, size: '3 keys' }, ...]
|
|
219
|
+
|
|
220
|
+
const detail = session.getVariable('user', { depth: 2 });
|
|
221
|
+
// { name: 'user', type: 'Object', children: [...], methods: [...] }
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Analysis
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
// Check statement completeness
|
|
228
|
+
session.isComplete('const x = 1'); // { status: 'complete', indent: '' }
|
|
229
|
+
session.isComplete('const x = {'); // { status: 'incomplete', indent: ' ' }
|
|
230
|
+
session.isComplete('const const'); // { status: 'invalid', indent: '' }
|
|
231
|
+
|
|
232
|
+
// Format code
|
|
233
|
+
const formatted = await session.format('const x=1');
|
|
234
|
+
// { formatted: 'const x = 1;\n', changed: true }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Executors
|
|
238
|
+
|
|
239
|
+
Built-in language executors.
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
import {
|
|
243
|
+
ExecutorRegistry,
|
|
244
|
+
createDefaultExecutorRegistry,
|
|
245
|
+
JavaScriptExecutor,
|
|
246
|
+
HtmlExecutor,
|
|
247
|
+
CssExecutor,
|
|
248
|
+
} from 'mrmd-js';
|
|
249
|
+
|
|
250
|
+
// Default registry includes JS, HTML, CSS
|
|
251
|
+
const registry = createDefaultExecutorRegistry();
|
|
252
|
+
|
|
253
|
+
// Check language support
|
|
254
|
+
registry.supports('javascript'); // true
|
|
255
|
+
registry.supports('js'); // true (alias)
|
|
256
|
+
registry.supports('html'); // true
|
|
257
|
+
|
|
258
|
+
// Get executor
|
|
259
|
+
const jsExecutor = registry.get('javascript');
|
|
260
|
+
|
|
261
|
+
// Register custom executor
|
|
262
|
+
registry.register('python', myPythonExecutor);
|
|
263
|
+
registry.registerAlias('py', 'python');
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### JavaScript Executor
|
|
267
|
+
|
|
268
|
+
Executes JavaScript with variable persistence and async support.
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
import { JavaScriptExecutor, createJavaScriptExecutor } from 'mrmd-js';
|
|
272
|
+
|
|
273
|
+
const executor = createJavaScriptExecutor();
|
|
274
|
+
|
|
275
|
+
// Execute with context
|
|
276
|
+
const result = await executor.execute(code, context, {
|
|
277
|
+
timeout: 30000,
|
|
278
|
+
storeVariables: true,
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### HTML Executor
|
|
283
|
+
|
|
284
|
+
Executes HTML, extracting and running scripts.
|
|
285
|
+
|
|
286
|
+
```javascript
|
|
287
|
+
import { HtmlExecutor, createHtmlExecutor } from 'mrmd-js';
|
|
288
|
+
|
|
289
|
+
const executor = createHtmlExecutor();
|
|
290
|
+
|
|
291
|
+
const result = await executor.execute(`
|
|
292
|
+
<div id="app">Hello</div>
|
|
293
|
+
<script>
|
|
294
|
+
document.getElementById('app').textContent = 'World';
|
|
295
|
+
</script>
|
|
296
|
+
`, context);
|
|
297
|
+
|
|
298
|
+
// Returns displayData with text/html
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
#### CSS Executor
|
|
302
|
+
|
|
303
|
+
Applies CSS styles with optional scoping.
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
import { CssExecutor, createCssExecutor } from 'mrmd-js';
|
|
307
|
+
|
|
308
|
+
const executor = createCssExecutor();
|
|
309
|
+
|
|
310
|
+
const result = await executor.execute(`
|
|
311
|
+
.container { background: blue; }
|
|
312
|
+
`, context, { scope: '.my-scope' });
|
|
313
|
+
|
|
314
|
+
// Returns displayData with text/css
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Client Utilities
|
|
318
|
+
|
|
319
|
+
Utilities for rendering execution output.
|
|
320
|
+
|
|
321
|
+
#### HtmlRenderer
|
|
322
|
+
|
|
323
|
+
Render HTML displayData with script execution and isolation modes.
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
import { HtmlRenderer, createHtmlRenderer } from 'mrmd-js';
|
|
327
|
+
|
|
328
|
+
const renderer = createHtmlRenderer();
|
|
329
|
+
|
|
330
|
+
// Render modes: 'direct', 'shadow', 'scoped'
|
|
331
|
+
renderer.render('<p>Hello</p>', container, { mode: 'direct' });
|
|
332
|
+
|
|
333
|
+
// Shadow DOM isolation
|
|
334
|
+
renderer.render('<p>Isolated</p>', container, { mode: 'shadow' });
|
|
335
|
+
|
|
336
|
+
// CSS scoped (prefixes selectors)
|
|
337
|
+
renderer.render('<style>.foo { color: red; }</style>', container, {
|
|
338
|
+
mode: 'scoped',
|
|
339
|
+
scopeClass: 'my-scope',
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Render displayData
|
|
343
|
+
renderer.renderDisplayData(displayData, container);
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
#### CssApplicator
|
|
347
|
+
|
|
348
|
+
Manage CSS styles in the document.
|
|
349
|
+
|
|
350
|
+
```javascript
|
|
351
|
+
import { CssApplicator, createCssApplicator } from 'mrmd-js';
|
|
352
|
+
|
|
353
|
+
const applicator = createCssApplicator();
|
|
354
|
+
|
|
355
|
+
// Apply CSS
|
|
356
|
+
const { id, element } = applicator.apply('.foo { color: red; }', {
|
|
357
|
+
id: 'my-styles',
|
|
358
|
+
scope: '.my-scope', // Prefix selectors
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Update existing
|
|
362
|
+
applicator.apply('.foo { color: blue; }', { id: 'my-styles' });
|
|
363
|
+
|
|
364
|
+
// Remove
|
|
365
|
+
applicator.remove('my-styles');
|
|
366
|
+
applicator.clear();
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### AnsiRenderer
|
|
370
|
+
|
|
371
|
+
Convert ANSI escape codes to HTML.
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
import { AnsiRenderer, ansiToHtml, stripAnsi } from 'mrmd-js';
|
|
375
|
+
|
|
376
|
+
// Convert to HTML
|
|
377
|
+
const html = ansiToHtml('\x1b[31mRed text\x1b[0m');
|
|
378
|
+
// '<span style="color:#cc0000">Red text</span>'
|
|
379
|
+
|
|
380
|
+
// Strip ANSI codes
|
|
381
|
+
const plain = stripAnsi('\x1b[1mBold\x1b[0m');
|
|
382
|
+
// 'Bold'
|
|
383
|
+
|
|
384
|
+
// Renderer instance
|
|
385
|
+
const renderer = new AnsiRenderer({ escapeHtml: true });
|
|
386
|
+
renderer.renderTo(ansiText, container);
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Analysis Utilities
|
|
390
|
+
|
|
391
|
+
Standalone code analysis functions.
|
|
392
|
+
|
|
393
|
+
```javascript
|
|
394
|
+
import {
|
|
395
|
+
isComplete,
|
|
396
|
+
getSuggestedIndent,
|
|
397
|
+
formatCode,
|
|
398
|
+
basicFormat,
|
|
399
|
+
formatHtml,
|
|
400
|
+
formatCss,
|
|
401
|
+
setPrettier,
|
|
402
|
+
} from 'mrmd-js';
|
|
403
|
+
|
|
404
|
+
// Statement completeness
|
|
405
|
+
isComplete('const x = 1'); // { status: 'complete', indent: '' }
|
|
406
|
+
isComplete('function f() {'); // { status: 'incomplete', indent: ' ' }
|
|
407
|
+
|
|
408
|
+
// Suggested indent for continuation
|
|
409
|
+
getSuggestedIndent('if (true) {'); // ' '
|
|
410
|
+
|
|
411
|
+
// Format code (async, uses Prettier if available)
|
|
412
|
+
await formatCode('const x=1', { tabWidth: 2 });
|
|
413
|
+
|
|
414
|
+
// Basic formatting (sync, no dependencies)
|
|
415
|
+
basicFormat('const x=1');
|
|
416
|
+
|
|
417
|
+
// Inject Prettier for better formatting
|
|
418
|
+
import * as prettier from 'prettier';
|
|
419
|
+
setPrettier(prettier);
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### LSP Utilities
|
|
423
|
+
|
|
424
|
+
Low-level LSP helpers for custom integrations.
|
|
425
|
+
|
|
426
|
+
```javascript
|
|
427
|
+
import {
|
|
428
|
+
// Parsing
|
|
429
|
+
parseIdentifierAtPosition,
|
|
430
|
+
parseCompletionContext,
|
|
431
|
+
getStringOrCommentContext,
|
|
432
|
+
splitObjectPath,
|
|
433
|
+
isKeyword,
|
|
434
|
+
|
|
435
|
+
// Formatting
|
|
436
|
+
formatValue,
|
|
437
|
+
formatValueShort,
|
|
438
|
+
getTypeName,
|
|
439
|
+
isExpandable,
|
|
440
|
+
getFunctionSignature,
|
|
441
|
+
|
|
442
|
+
// Completions/Hover/Inspect
|
|
443
|
+
getCompletions,
|
|
444
|
+
getHoverInfo,
|
|
445
|
+
getInspectInfo,
|
|
446
|
+
listVariables,
|
|
447
|
+
expandVariable,
|
|
448
|
+
} from 'mrmd-js';
|
|
449
|
+
|
|
450
|
+
// Parse context at cursor
|
|
451
|
+
const ctx = parseCompletionContext('obj.foo', 7);
|
|
452
|
+
// { type: 'member', object: 'obj', prefix: 'foo' }
|
|
453
|
+
|
|
454
|
+
// Format values for display
|
|
455
|
+
formatValue({ a: 1, b: 2 }); // '{ a: 1, b: 2 }'
|
|
456
|
+
formatValueShort(largeObject, 50); // Truncated
|
|
457
|
+
|
|
458
|
+
// Type utilities
|
|
459
|
+
getTypeName([1, 2, 3]); // 'Array'
|
|
460
|
+
isExpandable({ a: 1 }); // true
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Types
|
|
464
|
+
|
|
465
|
+
### ExecutionResult
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
interface ExecutionResult {
|
|
469
|
+
success: boolean; // Completed without error
|
|
470
|
+
stdout: string; // Captured console.log output
|
|
471
|
+
stderr: string; // Captured console.error/warn
|
|
472
|
+
result?: unknown; // Return value
|
|
473
|
+
resultString?: string; // String representation
|
|
474
|
+
error?: ExecutionError; // Error details if failed
|
|
475
|
+
duration?: number; // Execution time (ms)
|
|
476
|
+
displayData?: DisplayData[]; // Rich outputs
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
interface ExecutionError {
|
|
480
|
+
name: string; // Error type
|
|
481
|
+
message: string; // Error message
|
|
482
|
+
stack?: string; // Stack trace
|
|
483
|
+
line?: number; // Line number
|
|
484
|
+
column?: number; // Column number
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
interface DisplayData {
|
|
488
|
+
data: Record<string, string>; // MIME type → content
|
|
489
|
+
metadata?: Record<string, unknown>;
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### CompletionResult
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
interface CompletionResult {
|
|
497
|
+
matches: CompletionItem[]; // Suggestions
|
|
498
|
+
cursorStart: number; // Replace from
|
|
499
|
+
cursorEnd: number; // Replace to
|
|
500
|
+
source: 'runtime' | 'static';
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
interface CompletionItem {
|
|
504
|
+
label: string; // Text to insert
|
|
505
|
+
kind: string; // 'function' | 'property' | 'variable' | ...
|
|
506
|
+
type?: string; // Value type
|
|
507
|
+
valuePreview?: string; // Current value preview
|
|
508
|
+
documentation?: string; // Description
|
|
509
|
+
sortText?: string; // Sort order
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### HoverResult
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
interface HoverResult {
|
|
517
|
+
found: boolean;
|
|
518
|
+
name?: string;
|
|
519
|
+
type?: string;
|
|
520
|
+
value?: string;
|
|
521
|
+
signature?: string; // For functions
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### InspectResult
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
interface InspectResult {
|
|
529
|
+
found: boolean;
|
|
530
|
+
name?: string;
|
|
531
|
+
type?: string;
|
|
532
|
+
kind?: string;
|
|
533
|
+
value?: unknown;
|
|
534
|
+
signature?: string;
|
|
535
|
+
source?: string;
|
|
536
|
+
documentation?: string;
|
|
537
|
+
children?: ChildInfo[];
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### VariableInfo
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
interface VariableInfo {
|
|
545
|
+
name: string;
|
|
546
|
+
type: string;
|
|
547
|
+
value: string;
|
|
548
|
+
size?: string; // '5 items', '3 keys'
|
|
549
|
+
expandable?: boolean;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
interface VariableDetail extends VariableInfo {
|
|
553
|
+
children?: VariableInfo[];
|
|
554
|
+
methods?: MethodInfo[];
|
|
555
|
+
attributes?: AttributeInfo[];
|
|
556
|
+
}
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### IsCompleteResult
|
|
560
|
+
|
|
561
|
+
```typescript
|
|
562
|
+
interface IsCompleteResult {
|
|
563
|
+
status: 'complete' | 'incomplete' | 'invalid' | 'unknown';
|
|
564
|
+
indent: string; // Suggested indent for continuation
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
## Isolation Modes
|
|
569
|
+
|
|
570
|
+
### Iframe Isolation (Default)
|
|
571
|
+
|
|
572
|
+
Code executes in a hidden iframe with full isolation:
|
|
573
|
+
|
|
574
|
+
```
|
|
575
|
+
┌─────────────────────────────────────────┐
|
|
576
|
+
│ Main Page │
|
|
577
|
+
│ ┌───────────────────────────────────┐ │
|
|
578
|
+
│ │ <iframe sandbox="allow-scripts │ │
|
|
579
|
+
│ │ allow-same-origin"> │ │
|
|
580
|
+
│ │ │ │
|
|
581
|
+
│ │ Session code runs here │ │
|
|
582
|
+
│ │ - Isolated global scope │ │
|
|
583
|
+
│ │ - Full browser APIs │ │
|
|
584
|
+
│ │ - Can't access parent* │ │
|
|
585
|
+
│ │ │ │
|
|
586
|
+
│ └───────────────────────────────────┘ │
|
|
587
|
+
└─────────────────────────────────────────┘
|
|
588
|
+
|
|
589
|
+
* Unless allowMainAccess: true
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Main Context
|
|
593
|
+
|
|
594
|
+
Execute directly in the host page's window:
|
|
595
|
+
|
|
596
|
+
```javascript
|
|
597
|
+
const session = runtime.createSession({
|
|
598
|
+
language: 'javascript',
|
|
599
|
+
isolation: 'main',
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// Access real page DOM
|
|
603
|
+
await session.execute(`
|
|
604
|
+
document.title = 'Modified!';
|
|
605
|
+
console.log(window.myAppState);
|
|
606
|
+
`);
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
## How It Works
|
|
610
|
+
|
|
611
|
+
### Variable Persistence
|
|
612
|
+
|
|
613
|
+
Top-level declarations are transformed to persist in the global scope:
|
|
614
|
+
|
|
615
|
+
```javascript
|
|
616
|
+
// Your code
|
|
617
|
+
const x = 1;
|
|
618
|
+
let y = 2;
|
|
619
|
+
function greet() { return 'hi'; }
|
|
620
|
+
|
|
621
|
+
// Transformed
|
|
622
|
+
x = 1;
|
|
623
|
+
y = 2;
|
|
624
|
+
greet = function() { return 'hi'; }
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### Async Support
|
|
628
|
+
|
|
629
|
+
Code is automatically wrapped for top-level await:
|
|
630
|
+
|
|
631
|
+
```javascript
|
|
632
|
+
// Your code
|
|
633
|
+
const data = await fetch('/api');
|
|
634
|
+
data.json()
|
|
635
|
+
|
|
636
|
+
// Executed as
|
|
637
|
+
(async () => {
|
|
638
|
+
data = await fetch('/api');
|
|
639
|
+
return data.json();
|
|
640
|
+
})()
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
### Runtime Completions
|
|
644
|
+
|
|
645
|
+
Unlike static analysis, completions come from actual runtime values:
|
|
646
|
+
|
|
647
|
+
```javascript
|
|
648
|
+
const obj = { foo: 1, bar: 2 };
|
|
649
|
+
// Typing "obj." shows actual properties: foo, bar
|
|
650
|
+
|
|
651
|
+
const dynamicObj = JSON.parse(apiResponse);
|
|
652
|
+
// Shows actual parsed properties, not just "any"
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
## Examples
|
|
656
|
+
|
|
657
|
+
### Interactive Input
|
|
658
|
+
|
|
659
|
+
The `input()` function lets you prompt for user input, just like Python:
|
|
660
|
+
|
|
661
|
+
```javascript
|
|
662
|
+
const runtime = new MrpRuntime();
|
|
663
|
+
const session = runtime.createSession({ language: 'javascript' });
|
|
664
|
+
|
|
665
|
+
// Use input() in your code - it returns a Promise
|
|
666
|
+
const stream = session.executeStream(`
|
|
667
|
+
const name = await input("What is your name? ");
|
|
668
|
+
console.log("Hello, " + name + "!");
|
|
669
|
+
|
|
670
|
+
const age = await input("How old are you? ");
|
|
671
|
+
console.log("You are " + age + " years old.");
|
|
672
|
+
`);
|
|
673
|
+
|
|
674
|
+
// Handle stdin_request events
|
|
675
|
+
for await (const event of stream) {
|
|
676
|
+
if (event.type === 'stdin_request') {
|
|
677
|
+
// Show input UI to user
|
|
678
|
+
const userInput = await showInputDialog(event.prompt);
|
|
679
|
+
|
|
680
|
+
// Send the input back
|
|
681
|
+
session.sendInput(event.execId, userInput);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (event.type === 'stdout') {
|
|
685
|
+
console.log(event.content);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
The `input()` function:
|
|
691
|
+
- Prints the prompt to stdout (like Python)
|
|
692
|
+
- Pauses execution until input is provided
|
|
693
|
+
- Returns the user's input (without trailing newline)
|
|
694
|
+
- Supports `{ password: true }` option to hide input
|
|
695
|
+
|
|
696
|
+
Without a stdin handler configured, `input()` falls back to the browser's `prompt()` dialog.
|
|
697
|
+
|
|
698
|
+
### Data Analysis
|
|
699
|
+
|
|
700
|
+
```javascript
|
|
701
|
+
const runtime = new MrpRuntime();
|
|
702
|
+
const session = runtime.createSession({ language: 'javascript' });
|
|
703
|
+
|
|
704
|
+
// Cell 1: Load data
|
|
705
|
+
await session.execute(`
|
|
706
|
+
const sales = [
|
|
707
|
+
{ month: 'Jan', revenue: 1200 },
|
|
708
|
+
{ month: 'Feb', revenue: 1800 },
|
|
709
|
+
{ month: 'Mar', revenue: 2400 },
|
|
710
|
+
];
|
|
711
|
+
`);
|
|
712
|
+
|
|
713
|
+
// Cell 2: Analyze
|
|
714
|
+
await session.execute(`
|
|
715
|
+
const total = sales.reduce((sum, s) => sum + s.revenue, 0);
|
|
716
|
+
const avg = total / sales.length;
|
|
717
|
+
console.log('Total:', total, 'Average:', avg.toFixed(2));
|
|
718
|
+
`);
|
|
719
|
+
// Output: Total: 5400 Average: 1800.00
|
|
720
|
+
|
|
721
|
+
// Explore variables
|
|
722
|
+
session.listVariables();
|
|
723
|
+
// [{ name: 'sales', type: 'Array', size: '3 items' }, ...]
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Streaming Output
|
|
727
|
+
|
|
728
|
+
```javascript
|
|
729
|
+
const stream = session.executeStream(`
|
|
730
|
+
for (let i = 1; i <= 5; i++) {
|
|
731
|
+
console.log('Processing', i);
|
|
732
|
+
await new Promise(r => setTimeout(r, 500));
|
|
733
|
+
}
|
|
734
|
+
`);
|
|
735
|
+
|
|
736
|
+
for await (const event of stream) {
|
|
737
|
+
if (event.type === 'stdout') {
|
|
738
|
+
updateUI(event.text); // Real-time updates
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
```
|
|
742
|
+
|
|
743
|
+
### HTML/CSS Rendering
|
|
744
|
+
|
|
745
|
+
```javascript
|
|
746
|
+
// HTML cell
|
|
747
|
+
const htmlResult = await session.execute(`
|
|
748
|
+
<div class="card">
|
|
749
|
+
<h2>Hello World</h2>
|
|
750
|
+
<p>This is rendered HTML</p>
|
|
751
|
+
</div>
|
|
752
|
+
`, { executor: 'html' });
|
|
753
|
+
|
|
754
|
+
// CSS cell
|
|
755
|
+
const cssResult = await session.execute(`
|
|
756
|
+
.card {
|
|
757
|
+
padding: 20px;
|
|
758
|
+
border-radius: 8px;
|
|
759
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
760
|
+
}
|
|
761
|
+
`, { executor: 'css' });
|
|
762
|
+
|
|
763
|
+
// Render with utilities
|
|
764
|
+
const renderer = createHtmlRenderer();
|
|
765
|
+
renderer.renderDisplayData(htmlResult.displayData[0], container);
|
|
766
|
+
|
|
767
|
+
const applicator = createCssApplicator();
|
|
768
|
+
applicator.applyDisplayData(cssResult.displayData[0]);
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
### Variable Explorer
|
|
772
|
+
|
|
773
|
+
```javascript
|
|
774
|
+
await session.execute(`
|
|
775
|
+
const user = {
|
|
776
|
+
name: 'Alice',
|
|
777
|
+
profile: {
|
|
778
|
+
email: 'alice@example.com',
|
|
779
|
+
settings: { theme: 'dark' }
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
`);
|
|
783
|
+
|
|
784
|
+
// Get top-level variables
|
|
785
|
+
const vars = session.listVariables();
|
|
786
|
+
|
|
787
|
+
// Expand nested object
|
|
788
|
+
const detail = session.getVariable('user', { depth: 2 });
|
|
789
|
+
// {
|
|
790
|
+
// name: 'user',
|
|
791
|
+
// type: 'Object',
|
|
792
|
+
// children: [
|
|
793
|
+
// { name: 'name', type: 'string', value: '"Alice"' },
|
|
794
|
+
// { name: 'profile', type: 'Object', expandable: true, ... }
|
|
795
|
+
// ]
|
|
796
|
+
// }
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
## Test Application
|
|
800
|
+
|
|
801
|
+
A test playground is included:
|
|
802
|
+
|
|
803
|
+
```bash
|
|
804
|
+
# Build and serve
|
|
805
|
+
npm run demo
|
|
806
|
+
|
|
807
|
+
# Or manually
|
|
808
|
+
npm run build
|
|
809
|
+
npm run serve
|
|
810
|
+
# Open http://localhost:3000
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
## Development
|
|
814
|
+
|
|
815
|
+
```bash
|
|
816
|
+
# Install dependencies
|
|
817
|
+
npm install
|
|
818
|
+
|
|
819
|
+
# Build
|
|
820
|
+
npm run build
|
|
821
|
+
|
|
822
|
+
# Run tests
|
|
823
|
+
npm test
|
|
824
|
+
|
|
825
|
+
# Watch mode
|
|
826
|
+
npm run dev
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
## Browser Support
|
|
830
|
+
|
|
831
|
+
- Chrome/Edge 80+
|
|
832
|
+
- Firefox 75+
|
|
833
|
+
- Safari 14+
|
|
834
|
+
|
|
835
|
+
Requires:
|
|
836
|
+
- ES2020+ (async/await, optional chaining)
|
|
837
|
+
- iframe sandbox support
|
|
838
|
+
- Blob URLs
|
|
839
|
+
|
|
840
|
+
## License
|
|
841
|
+
|
|
842
|
+
MIT
|