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.
Files changed (52) hide show
  1. package/README.md +842 -0
  2. package/dist/index.cjs +7613 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.js +7530 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/mrmd-js.iife.js +7618 -0
  7. package/dist/mrmd-js.iife.js.map +1 -0
  8. package/package.json +47 -0
  9. package/src/analysis/format.js +371 -0
  10. package/src/analysis/index.js +18 -0
  11. package/src/analysis/is-complete.js +394 -0
  12. package/src/constants.js +44 -0
  13. package/src/execute/css.js +205 -0
  14. package/src/execute/html.js +162 -0
  15. package/src/execute/index.js +41 -0
  16. package/src/execute/interface.js +144 -0
  17. package/src/execute/javascript.js +197 -0
  18. package/src/execute/registry.js +245 -0
  19. package/src/index.js +136 -0
  20. package/src/lsp/complete.js +353 -0
  21. package/src/lsp/format.js +310 -0
  22. package/src/lsp/hover.js +126 -0
  23. package/src/lsp/index.js +55 -0
  24. package/src/lsp/inspect.js +466 -0
  25. package/src/lsp/parse.js +455 -0
  26. package/src/lsp/variables.js +283 -0
  27. package/src/runtime.js +518 -0
  28. package/src/session/console-capture.js +181 -0
  29. package/src/session/context/iframe.js +407 -0
  30. package/src/session/context/index.js +12 -0
  31. package/src/session/context/interface.js +38 -0
  32. package/src/session/context/main.js +357 -0
  33. package/src/session/index.js +16 -0
  34. package/src/session/manager.js +327 -0
  35. package/src/session/session.js +678 -0
  36. package/src/transform/async.js +133 -0
  37. package/src/transform/extract.js +251 -0
  38. package/src/transform/index.js +10 -0
  39. package/src/transform/persistence.js +176 -0
  40. package/src/types/analysis.js +24 -0
  41. package/src/types/capabilities.js +44 -0
  42. package/src/types/completion.js +47 -0
  43. package/src/types/execution.js +62 -0
  44. package/src/types/index.js +16 -0
  45. package/src/types/inspection.js +39 -0
  46. package/src/types/session.js +32 -0
  47. package/src/types/streaming.js +74 -0
  48. package/src/types/variables.js +54 -0
  49. package/src/utils/ansi-renderer.js +301 -0
  50. package/src/utils/css-applicator.js +149 -0
  51. package/src/utils/html-renderer.js +355 -0
  52. 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