@ebowwa/sandbox 0.1.1

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 (108) hide show
  1. package/dist/compilers/index.d.ts +24 -0
  2. package/dist/compilers/index.d.ts.map +1 -0
  3. package/dist/compilers/index.js +42 -0
  4. package/dist/compilers/index.js.map +1 -0
  5. package/dist/compilers/javascript.d.ts +117 -0
  6. package/dist/compilers/javascript.d.ts.map +1 -0
  7. package/dist/compilers/javascript.js +462 -0
  8. package/dist/compilers/javascript.js.map +1 -0
  9. package/dist/compilers/python.d.ts +140 -0
  10. package/dist/compilers/python.d.ts.map +1 -0
  11. package/dist/compilers/python.js +650 -0
  12. package/dist/compilers/python.js.map +1 -0
  13. package/dist/compilers/typescript.d.ts +99 -0
  14. package/dist/compilers/typescript.d.ts.map +1 -0
  15. package/dist/compilers/typescript.js +323 -0
  16. package/dist/compilers/typescript.js.map +1 -0
  17. package/dist/core/cell.d.ts +160 -0
  18. package/dist/core/cell.d.ts.map +1 -0
  19. package/dist/core/cell.js +319 -0
  20. package/dist/core/cell.js.map +1 -0
  21. package/dist/core/compiler.d.ts +126 -0
  22. package/dist/core/compiler.d.ts.map +1 -0
  23. package/dist/core/compiler.js +123 -0
  24. package/dist/core/compiler.js.map +1 -0
  25. package/dist/core/index.d.ts +19 -0
  26. package/dist/core/index.d.ts.map +1 -0
  27. package/dist/core/index.js +14 -0
  28. package/dist/core/index.js.map +1 -0
  29. package/dist/core/limits.d.ts +173 -0
  30. package/dist/core/limits.d.ts.map +1 -0
  31. package/dist/core/limits.js +440 -0
  32. package/dist/core/limits.js.map +1 -0
  33. package/dist/core/permissions.d.ts +103 -0
  34. package/dist/core/permissions.d.ts.map +1 -0
  35. package/dist/core/permissions.js +341 -0
  36. package/dist/core/permissions.js.map +1 -0
  37. package/dist/core/runtime.d.ts +127 -0
  38. package/dist/core/runtime.d.ts.map +1 -0
  39. package/dist/core/runtime.js +325 -0
  40. package/dist/core/runtime.js.map +1 -0
  41. package/dist/core/types.d.ts +380 -0
  42. package/dist/core/types.d.ts.map +1 -0
  43. package/dist/core/types.js +67 -0
  44. package/dist/core/types.js.map +1 -0
  45. package/dist/index.d.ts +145 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +279 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/multi/index.d.ts +9 -0
  50. package/dist/multi/index.d.ts.map +1 -0
  51. package/dist/multi/index.js +7 -0
  52. package/dist/multi/index.js.map +1 -0
  53. package/dist/multi/polyglot.d.ts +179 -0
  54. package/dist/multi/polyglot.d.ts.map +1 -0
  55. package/dist/multi/polyglot.js +319 -0
  56. package/dist/multi/polyglot.js.map +1 -0
  57. package/dist/runtimes/docker.d.ts +97 -0
  58. package/dist/runtimes/docker.d.ts.map +1 -0
  59. package/dist/runtimes/docker.js +368 -0
  60. package/dist/runtimes/docker.js.map +1 -0
  61. package/dist/runtimes/index.d.ts +11 -0
  62. package/dist/runtimes/index.d.ts.map +1 -0
  63. package/dist/runtimes/index.js +9 -0
  64. package/dist/runtimes/index.js.map +1 -0
  65. package/dist/runtimes/process.d.ts +47 -0
  66. package/dist/runtimes/process.d.ts.map +1 -0
  67. package/dist/runtimes/process.js +230 -0
  68. package/dist/runtimes/process.js.map +1 -0
  69. package/dist/session/index.d.ts +12 -0
  70. package/dist/session/index.d.ts.map +1 -0
  71. package/dist/session/index.js +9 -0
  72. package/dist/session/index.js.map +1 -0
  73. package/dist/session/kernel.d.ts +199 -0
  74. package/dist/session/kernel.d.ts.map +1 -0
  75. package/dist/session/kernel.js +400 -0
  76. package/dist/session/kernel.js.map +1 -0
  77. package/dist/session/notebook.d.ts +168 -0
  78. package/dist/session/notebook.d.ts.map +1 -0
  79. package/dist/session/notebook.js +499 -0
  80. package/dist/session/notebook.js.map +1 -0
  81. package/dist/session/repl.d.ts +159 -0
  82. package/dist/session/repl.d.ts.map +1 -0
  83. package/dist/session/repl.js +409 -0
  84. package/dist/session/repl.js.map +1 -0
  85. package/package.json +142 -0
  86. package/src/compilers/index.ts +80 -0
  87. package/src/compilers/javascript.ts +571 -0
  88. package/src/compilers/python.ts +785 -0
  89. package/src/compilers/typescript.ts +442 -0
  90. package/src/core/cell.ts +439 -0
  91. package/src/core/compiler.ts +250 -0
  92. package/src/core/index.ts +123 -0
  93. package/src/core/limits.ts +508 -0
  94. package/src/core/permissions.ts +409 -0
  95. package/src/core/runtime.ts +499 -0
  96. package/src/core/types.ts +528 -0
  97. package/src/global.d.ts +59 -0
  98. package/src/index.ts +515 -0
  99. package/src/multi/index.ts +22 -0
  100. package/src/multi/polyglot.ts +461 -0
  101. package/src/runtimes/docker.ts +501 -0
  102. package/src/runtimes/index.ts +21 -0
  103. package/src/runtimes/process.ts +316 -0
  104. package/src/session/index.ts +41 -0
  105. package/src/session/kernel.ts +553 -0
  106. package/src/session/notebook.ts +635 -0
  107. package/src/session/repl.ts +521 -0
  108. package/src/wasm2wasm.d.ts +35 -0
@@ -0,0 +1,439 @@
1
+ /**
2
+ * Cell Primitive
3
+ *
4
+ * Represents a single unit of executable code.
5
+ * Cells can be combined into notebooks for sequential execution.
6
+ */
7
+
8
+ import type {
9
+ Cell,
10
+ Language,
11
+ ExecutionResult,
12
+ ExecutionOptions,
13
+ ExecutionContext,
14
+ Permissions,
15
+ Limits,
16
+ DisplayOutput,
17
+ } from "./types.js";
18
+ import type { CompileResult } from "./compiler.js";
19
+ import { v4 as uuid } from "uuid";
20
+
21
+ /**
22
+ * Cell metadata
23
+ */
24
+ export interface CellMetadata {
25
+ /** Cell display order */
26
+ index?: number;
27
+ /** Cell is collapsed */
28
+ collapsed?: boolean;
29
+ /** Cell execution count */
30
+ executionCount?: number;
31
+ /** Tags for organization */
32
+ tags?: string[];
33
+ /** Custom metadata */
34
+ [key: string]: unknown;
35
+ }
36
+
37
+ /**
38
+ * Cell execution state
39
+ */
40
+ export type CellState =
41
+ | "idle"
42
+ | "queued"
43
+ | "running"
44
+ | "success"
45
+ | "error"
46
+ | "cancelled";
47
+
48
+ /**
49
+ * Extended cell with execution tracking
50
+ */
51
+ export interface ExecutableCell extends Cell {
52
+ /** Current state */
53
+ state: CellState;
54
+ /** Last execution result */
55
+ result?: ExecutionResult;
56
+ /** Execution count (notebook-style) */
57
+ executionCount?: number;
58
+ /** Last execution timestamp */
59
+ lastRun?: Date;
60
+ /** Dependencies on other cells */
61
+ dependencies?: string[];
62
+ /** Exports from this cell */
63
+ exports?: string[];
64
+ }
65
+
66
+ /**
67
+ * Cell Builder
68
+ *
69
+ * Fluent API for creating cells.
70
+ */
71
+ export class CellBuilder {
72
+ private id = uuid();
73
+ private language: Language = "javascript";
74
+ private source = "";
75
+ private metadata: CellMetadata = {};
76
+
77
+ setId(id: string): this {
78
+ this.id = id;
79
+ return this;
80
+ }
81
+
82
+ setLanguage(lang: Language): this {
83
+ this.language = lang;
84
+ return this;
85
+ }
86
+
87
+ setSource(code: string): this {
88
+ this.source = code;
89
+ return this;
90
+ }
91
+
92
+ addMetadata(key: string, value: unknown): this {
93
+ this.metadata[key] = value;
94
+ return this;
95
+ }
96
+
97
+ setIndex(index: number): this {
98
+ this.metadata.index = index;
99
+ return this;
100
+ }
101
+
102
+ addTag(tag: string): this {
103
+ this.metadata.tags = [...(this.metadata.tags ?? []), tag];
104
+ return this;
105
+ }
106
+
107
+ build(): Cell {
108
+ return {
109
+ id: this.id,
110
+ language: this.language,
111
+ source: this.source,
112
+ metadata: Object.keys(this.metadata).length > 0 ? this.metadata : undefined,
113
+ };
114
+ }
115
+
116
+ buildExecutable(): ExecutableCell {
117
+ return {
118
+ ...this.build(),
119
+ state: "idle",
120
+ };
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Cell Executor
126
+ *
127
+ * Handles execution of individual cells.
128
+ */
129
+ export class CellExecutor {
130
+ constructor(
131
+ private compileFn: (cell: Cell) => Promise<CompileResult>,
132
+ private executeFn: (
133
+ compiled: CompileResult,
134
+ context: ExecutionContext
135
+ ) => Promise<ExecutionResult>
136
+ ) {}
137
+
138
+ /**
139
+ * Execute a cell
140
+ */
141
+ async execute(
142
+ cell: ExecutableCell,
143
+ context: ExecutionContext
144
+ ): Promise<ExecutionResult> {
145
+ cell.state = "running";
146
+ cell.lastRun = new Date();
147
+
148
+ try {
149
+ // Compile the cell
150
+ const compiled = await this.compileFn(cell);
151
+
152
+ // Execute
153
+ const result = await this.executeFn(compiled, context);
154
+
155
+ // Update cell state
156
+ cell.state = result.success ? "success" : "error";
157
+ cell.result = result;
158
+ cell.executionCount = (cell.executionCount ?? 0) + 1;
159
+
160
+ return result;
161
+ } catch (error) {
162
+ const result: ExecutionResult = {
163
+ success: false,
164
+ error: {
165
+ message: error instanceof Error ? error.message : String(error),
166
+ type: "runtime",
167
+ },
168
+ metrics: {
169
+ duration: 0,
170
+ memoryUsed: 0,
171
+ },
172
+ output: { stdout: [], stderr: [], displays: [] },
173
+ };
174
+
175
+ cell.state = "error";
176
+ cell.result = result;
177
+
178
+ return result;
179
+ }
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Cell Registry
185
+ *
186
+ * Tracks cells and their relationships.
187
+ */
188
+ export class CellRegistry {
189
+ private cells = new Map<string, ExecutableCell>();
190
+ private dependencies = new Map<string, Set<string>>();
191
+ private dependents = new Map<string, Set<string>>();
192
+
193
+ /**
194
+ * Add a cell
195
+ */
196
+ add(cell: ExecutableCell): void {
197
+ this.cells.set(cell.id, cell);
198
+
199
+ // Track dependencies
200
+ if (cell.dependencies) {
201
+ for (const depId of cell.dependencies) {
202
+ this.addDependency(cell.id, depId);
203
+ }
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Get a cell by ID
209
+ */
210
+ get(id: string): ExecutableCell | undefined {
211
+ return this.cells.get(id);
212
+ }
213
+
214
+ /**
215
+ * Remove a cell
216
+ */
217
+ remove(id: string): void {
218
+ const cell = this.cells.get(id);
219
+ if (!cell) return;
220
+
221
+ // Clean up dependencies
222
+ const deps = this.dependencies.get(id);
223
+ if (deps) {
224
+ for (const depId of deps) {
225
+ this.dependents.get(depId)?.delete(id);
226
+ }
227
+ }
228
+
229
+ // Clean up dependents
230
+ const dependents = this.dependents.get(id);
231
+ if (dependents) {
232
+ for (const dependentId of dependents) {
233
+ this.dependencies.get(dependentId)?.delete(id);
234
+ }
235
+ }
236
+
237
+ this.cells.delete(id);
238
+ this.dependencies.delete(id);
239
+ this.dependents.delete(id);
240
+ }
241
+
242
+ /**
243
+ * Get all cells
244
+ */
245
+ getAll(): ExecutableCell[] {
246
+ return Array.from(this.cells.values());
247
+ }
248
+
249
+ /**
250
+ * Get cells by language
251
+ */
252
+ getByLanguage(language: Language): ExecutableCell[] {
253
+ return this.getAll().filter(c => c.language === language);
254
+ }
255
+
256
+ /**
257
+ * Get cells by state
258
+ */
259
+ getByState(state: CellState): ExecutableCell[] {
260
+ return this.getAll().filter(c => c.state === state);
261
+ }
262
+
263
+ /**
264
+ * Add dependency between cells
265
+ */
266
+ addDependency(cellId: string, dependsOn: string): void {
267
+ if (!this.dependencies.has(cellId)) {
268
+ this.dependencies.set(cellId, new Set());
269
+ }
270
+ this.dependencies.get(cellId)!.add(dependsOn);
271
+
272
+ if (!this.dependents.has(dependsOn)) {
273
+ this.dependents.set(dependsOn, new Set());
274
+ }
275
+ this.dependents.get(dependsOn)!.add(cellId);
276
+ }
277
+
278
+ /**
279
+ * Get dependencies of a cell
280
+ */
281
+ getDependencies(cellId: string): string[] {
282
+ return Array.from(this.dependencies.get(cellId) ?? []);
283
+ }
284
+
285
+ /**
286
+ * Get dependents of a cell (cells that depend on this one)
287
+ */
288
+ getDependents(cellId: string): string[] {
289
+ return Array.from(this.dependents.get(cellId) ?? []);
290
+ }
291
+
292
+ /**
293
+ * Get execution order (topological sort)
294
+ */
295
+ getExecutionOrder(): string[] {
296
+ const order: string[] = [];
297
+ const visited = new Set<string>();
298
+ const visiting = new Set<string>();
299
+
300
+ const visit = (id: string) => {
301
+ if (visited.has(id)) return;
302
+ if (visiting.has(id)) {
303
+ throw new Error(`Circular dependency detected involving cell: ${id}`);
304
+ }
305
+
306
+ visiting.add(id);
307
+
308
+ const deps = this.dependencies.get(id);
309
+ if (deps) {
310
+ for (const dep of deps) {
311
+ visit(dep);
312
+ }
313
+ }
314
+
315
+ visiting.delete(id);
316
+ visited.add(id);
317
+ order.push(id);
318
+ };
319
+
320
+ for (const id of this.cells.keys()) {
321
+ visit(id);
322
+ }
323
+
324
+ return order;
325
+ }
326
+
327
+ /**
328
+ * Check if cell has circular dependencies
329
+ */
330
+ hasCircularDependency(cellId: string): boolean {
331
+ try {
332
+ this.getExecutionOrder();
333
+ return false;
334
+ } catch {
335
+ return true;
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Get cells that need re-execution after a cell changes
341
+ */
342
+ getCellsToReexecute(cellId: string): string[] {
343
+ const toReexecute: string[] = [];
344
+ const visited = new Set<string>();
345
+
346
+ const collect = (id: string) => {
347
+ if (visited.has(id)) return;
348
+ visited.add(id);
349
+ toReexecute.push(id);
350
+
351
+ const dependents = this.dependents.get(id);
352
+ if (dependents) {
353
+ for (const dependent of dependents) {
354
+ collect(dependent);
355
+ }
356
+ }
357
+ };
358
+
359
+ collect(cellId);
360
+ return toReexecute.slice(1); // Exclude the original cell
361
+ }
362
+
363
+ /**
364
+ * Clear all cells
365
+ */
366
+ clear(): void {
367
+ this.cells.clear();
368
+ this.dependencies.clear();
369
+ this.dependents.clear();
370
+ }
371
+
372
+ /**
373
+ * Get count
374
+ */
375
+ get count(): number {
376
+ return this.cells.size;
377
+ }
378
+ }
379
+
380
+ /**
381
+ * Create a simple cell
382
+ */
383
+ export function createCell(
384
+ language: Language,
385
+ source: string,
386
+ options?: {
387
+ id?: string;
388
+ metadata?: CellMetadata;
389
+ dependencies?: string[];
390
+ }
391
+ ): ExecutableCell {
392
+ return {
393
+ id: options?.id ?? uuid(),
394
+ language,
395
+ source,
396
+ metadata: options?.metadata,
397
+ dependencies: options?.dependencies,
398
+ state: "idle",
399
+ };
400
+ }
401
+
402
+ /**
403
+ * Create cells from code blocks
404
+ */
405
+ export function createCellsFromMarkdown(
406
+ markdown: string,
407
+ defaultLanguage: Language = "javascript"
408
+ ): ExecutableCell[] {
409
+ const cells: ExecutableCell[] = [];
410
+ const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
411
+ let match;
412
+ let index = 0;
413
+
414
+ while ((match = codeBlockRegex.exec(markdown)) !== null) {
415
+ const lang = (match[1] as Language) ?? defaultLanguage;
416
+ const source = match[2].trim();
417
+
418
+ cells.push(
419
+ createCell(lang, source, {
420
+ metadata: { index: index++ },
421
+ })
422
+ );
423
+ }
424
+
425
+ return cells;
426
+ }
427
+
428
+ /**
429
+ * Convert cells to markdown
430
+ */
431
+ export function cellsToMarkdown(cells: ExecutableCell[]): string {
432
+ return cells
433
+ .sort((a, b) => ((a.metadata?.index as number) ?? 0) - ((b.metadata?.index as number) ?? 0))
434
+ .map(cell => {
435
+ const lang = cell.language === "javascript" ? "js" : cell.language;
436
+ return `\`\`\`${lang}\n${cell.source}\n\`\`\``;
437
+ })
438
+ .join("\n\n");
439
+ }
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Compiler Primitive
3
+ *
4
+ * Transforms source code to executable format (typically WASM).
5
+ * Composable: mix and match compilers for different languages.
6
+ */
7
+
8
+ import type { Language, Permissions, Limits } from "./types.js";
9
+
10
+ /** Compiler result */
11
+ export interface CompileResult {
12
+ /** Compiled WASM bytes */
13
+ wasmBytes: Uint8Array;
14
+ /** Original source code (for interpreted languages) */
15
+ source?: string;
16
+ /** Source map for debugging */
17
+ sourceMap?: string;
18
+ /** Exported functions */
19
+ exports: string[];
20
+ /** Imported functions needed */
21
+ imports: ImportDefinition[];
22
+ /** Memory requirements */
23
+ memoryRequirements?: {
24
+ initial: number; // Pages
25
+ maximum?: number; // Pages
26
+ };
27
+ }
28
+
29
+ /** Import definition */
30
+ export interface ImportDefinition {
31
+ module: string;
32
+ name: string;
33
+ type: "function" | "memory" | "global" | "table";
34
+ signature?: string;
35
+ }
36
+
37
+ /** Compiler options */
38
+ export interface CompilerOptions {
39
+ /** Optimize output */
40
+ optimize?: boolean;
41
+ /** Debug info */
42
+ debug?: boolean;
43
+ /** Source map */
44
+ sourceMap?: boolean;
45
+ /** Target WASM features */
46
+ features?: {
47
+ simd?: boolean;
48
+ threads?: boolean;
49
+ referenceTypes?: boolean;
50
+ bulkMemory?: boolean;
51
+ };
52
+ /** Additional compiler-specific options */
53
+ extra?: Record<string, unknown>;
54
+ }
55
+
56
+ /** Base compiler interface */
57
+ export interface ICompiler {
58
+ /** Language this compiler handles */
59
+ readonly language: Language;
60
+
61
+ /** Check if this compiler is available */
62
+ isAvailable(): Promise<boolean>;
63
+
64
+ /** Check if code syntax is valid */
65
+ validate(source: string): Promise<{ valid: boolean; error?: string }>;
66
+
67
+ /** Compile source to WASM */
68
+ compile(
69
+ source: string,
70
+ permissions: Permissions,
71
+ limits: Limits,
72
+ options?: CompilerOptions
73
+ ): Promise<CompileResult>;
74
+
75
+ /** Get language capabilities */
76
+ getCapabilities(): LanguageCapabilities;
77
+ }
78
+
79
+ /** Language capabilities */
80
+ export interface LanguageCapabilities {
81
+ /** Supports REPL-style execution */
82
+ repl: boolean;
83
+ /** Can maintain state between executions */
84
+ stateful: boolean;
85
+ /** Requires compilation step */
86
+ compiled: boolean;
87
+ /** Has garbage collection */
88
+ gc: boolean;
89
+ /** Can import external modules */
90
+ imports: boolean;
91
+ /** Supports async/await */
92
+ async: boolean;
93
+ }
94
+
95
+ /**
96
+ * Base Compiler class
97
+ *
98
+ * Extend this to create language-specific compilers.
99
+ */
100
+ export abstract class BaseCompiler implements ICompiler {
101
+ abstract readonly language: Language;
102
+
103
+ async isAvailable(): Promise<boolean> {
104
+ return true; // Override if compiler needs external tools
105
+ }
106
+
107
+ async validate(_source: string): Promise<{ valid: boolean; error?: string }> {
108
+ return { valid: true }; // Override for language-specific validation
109
+ }
110
+
111
+ abstract compile(
112
+ source: string,
113
+ permissions: Permissions,
114
+ limits: Limits,
115
+ options?: CompilerOptions
116
+ ): Promise<CompileResult>;
117
+
118
+ getCapabilities(): LanguageCapabilities {
119
+ return {
120
+ repl: false,
121
+ stateful: false,
122
+ compiled: true,
123
+ gc: false,
124
+ imports: false,
125
+ async: false,
126
+ };
127
+ }
128
+ }
129
+
130
+ /**
131
+ * WASM Passthrough Compiler
132
+ *
133
+ * Treats already-compiled WASM as "compiled".
134
+ * Used when source is already WASM bytes.
135
+ */
136
+ export class WasmCompiler extends BaseCompiler {
137
+ readonly language = "wasm" as const;
138
+
139
+ async validate(source: string | Uint8Array): Promise<{ valid: boolean; error?: string }> {
140
+ try {
141
+ const bytes = typeof source === "string"
142
+ ? new TextEncoder().encode(source)
143
+ : source;
144
+
145
+ // Check WASM magic number
146
+ if (bytes[0] !== 0x00 || bytes[1] !== 0x61 || bytes[2] !== 0x73 || bytes[3] !== 0x6D) {
147
+ return { valid: false, error: "Invalid WASM magic number" };
148
+ }
149
+
150
+ // Use WebAssembly.validate
151
+ const valid = WebAssembly.validate(bytes);
152
+ return { valid };
153
+ } catch (error) {
154
+ return { valid: false, error: String(error) };
155
+ }
156
+ }
157
+
158
+ async compile(
159
+ source: string | Uint8Array,
160
+ _permissions: Permissions,
161
+ _limits: Limits,
162
+ _options?: CompilerOptions
163
+ ): Promise<CompileResult> {
164
+ const wasmBytes = typeof source === "string"
165
+ ? new TextEncoder().encode(source)
166
+ : source;
167
+
168
+ // Extract exports from module
169
+ const module = await WebAssembly.compile(wasmBytes);
170
+ const exports = WebAssembly.Module.exports(module).map(e => e.name);
171
+ const imports = WebAssembly.Module.imports(module).map(i => ({
172
+ module: i.module,
173
+ name: i.name,
174
+ type: i.kind as "function" | "memory" | "global" | "table",
175
+ }));
176
+
177
+ return {
178
+ wasmBytes,
179
+ exports,
180
+ imports,
181
+ };
182
+ }
183
+
184
+ getCapabilities(): LanguageCapabilities {
185
+ return {
186
+ repl: false,
187
+ stateful: true,
188
+ compiled: true,
189
+ gc: false,
190
+ imports: true,
191
+ async: true,
192
+ };
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Compiler Registry
198
+ *
199
+ * Manages available compilers and routes code to the right one.
200
+ */
201
+ export class CompilerRegistry {
202
+ private compilers = new Map<Language, ICompiler>();
203
+
204
+ register(compiler: ICompiler): void {
205
+ this.compilers.set(compiler.language, compiler);
206
+ }
207
+
208
+ get(language: Language): ICompiler | undefined {
209
+ return this.compilers.get(language);
210
+ }
211
+
212
+ has(language: Language): boolean {
213
+ return this.compilers.has(language);
214
+ }
215
+
216
+ async getAvailable(): Promise<Language[]> {
217
+ const available: Language[] = [];
218
+ for (const [lang, compiler] of this.compilers) {
219
+ if (await compiler.isAvailable()) {
220
+ available.push(lang);
221
+ }
222
+ }
223
+ return available;
224
+ }
225
+
226
+ async compile(
227
+ source: string | Uint8Array,
228
+ language: Language,
229
+ permissions: Permissions,
230
+ limits: Limits,
231
+ options?: CompilerOptions
232
+ ): Promise<CompileResult> {
233
+ const compiler = this.compilers.get(language);
234
+ if (!compiler) {
235
+ throw new Error(`No compiler registered for language: ${language}`);
236
+ }
237
+
238
+ if (!(await compiler.isAvailable())) {
239
+ throw new Error(`Compiler for ${language} is not available`);
240
+ }
241
+
242
+ return (compiler as unknown as {
243
+ compile: (src: string | Uint8Array, perms: Permissions, lim: Limits, opts?: CompilerOptions) => Promise<CompileResult>;
244
+ }).compile(source, permissions, limits, options);
245
+ }
246
+ }
247
+
248
+ // Default registry with built-in compilers
249
+ export const defaultCompilerRegistry = new CompilerRegistry();
250
+ defaultCompilerRegistry.register(new WasmCompiler());