@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,571 @@
1
+ /**
2
+ * JavaScript Compiler for @ebowwa/sandbox
3
+ *
4
+ * Compiles JavaScript to WASM or provides sandboxed execution.
5
+ * Uses AssemblyScript for strict JS/TS-to-WASM compilation when possible,
6
+ * falls back to sandboxed QuickJS or native execution for dynamic JS.
7
+ */
8
+
9
+ import {
10
+ BaseCompiler,
11
+ type CompileResult,
12
+ type CompilerOptions,
13
+ type LanguageCapabilities,
14
+ } from "../core/compiler.js";
15
+ import type { Permissions, Limits } from "../core/types.js";
16
+
17
+ /** JavaScript compiler options */
18
+ export interface JavaScriptCompilerOptions extends CompilerOptions {
19
+ /** Target ES version */
20
+ target?: "es2015" | "es2016" | "es2017" | "es2018" | "es2019" | "es2020" | "es2021" | "es2022" | "esnext";
21
+ /** Module format */
22
+ module?: "esm" | "cjs" | "iife";
23
+ /** Enable strict mode */
24
+ strict?: boolean;
25
+ /** Use AssemblyScript for compilation (requires strict typing) */
26
+ useAssemblyScript?: boolean;
27
+ /** Sandbox mode when not using WASM */
28
+ sandboxMode?: "quickjs" | "isolated-vm" | "vm" | "none";
29
+ }
30
+
31
+ // Dynamic import type for esbuild (optional dependency)
32
+ type EsbuildTransformResult = { code: string; map?: string; warnings?: unknown[] };
33
+ type EsbuildModule = {
34
+ transform: (input: string, options?: Record<string, unknown>) => Promise<EsbuildTransformResult>;
35
+ };
36
+
37
+ // Dynamic import type for AssemblyScript (optional dependency)
38
+ type AssemblyScriptModule = {
39
+ main: (args: string[], options: unknown, callback: (err: Error | null, result?: { stdout: Uint8Array }) => void) => void;
40
+ builtins: unknown;
41
+ };
42
+
43
+ /**
44
+ * JavaScript Compiler
45
+ *
46
+ * Provides JavaScript execution in a sandboxed environment.
47
+ * For true WASM compilation, AssemblyScript is used but requires
48
+ * strict typing with WebAssembly types (i32, i64, f32, f64).
49
+ */
50
+ export class JavaScriptCompiler extends BaseCompiler {
51
+ readonly language = "javascript" as const;
52
+
53
+ private assemblyScriptAvailable: boolean | null = null;
54
+ private esbuildAvailable: boolean | null = null;
55
+
56
+ /**
57
+ * Check if AssemblyScript is available for WASM compilation
58
+ */
59
+ async isAvailable(): Promise<boolean> {
60
+ if (this.assemblyScriptAvailable !== null) {
61
+ return this.assemblyScriptAvailable;
62
+ }
63
+
64
+ try {
65
+ await import("assemblyscript");
66
+ this.assemblyScriptAvailable = true;
67
+ return true;
68
+ } catch {
69
+ this.assemblyScriptAvailable = false;
70
+ return false;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Validate JavaScript syntax
76
+ */
77
+ async validate(source: string): Promise<{ valid: boolean; error?: string }> {
78
+ try {
79
+ // Use esbuild for syntax validation if available
80
+ const esbuild = await this.getEsbuild();
81
+ if (esbuild) {
82
+ await esbuild.transform(source, {
83
+ loader: "js",
84
+ target: "es2020",
85
+ });
86
+ return { valid: true };
87
+ }
88
+
89
+ // Fallback: use Function constructor for basic syntax check
90
+ // This doesn't execute the code, just parses it
91
+ new Function(source);
92
+ return { valid: true };
93
+ } catch (error) {
94
+ return {
95
+ valid: false,
96
+ error: error instanceof Error ? error.message : String(error),
97
+ };
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Compile JavaScript to WASM or prepare for sandboxed execution
103
+ *
104
+ * For dynamic JavaScript (with closures, objects, etc.), we create a WASM
105
+ * wrapper that includes a minimal JS runtime or use sandboxed execution.
106
+ */
107
+ async compile(
108
+ source: string,
109
+ permissions: Permissions,
110
+ limits: Limits,
111
+ options?: JavaScriptCompilerOptions
112
+ ): Promise<CompileResult> {
113
+ const opts: JavaScriptCompilerOptions = {
114
+ target: "es2020",
115
+ module: "esm",
116
+ strict: true,
117
+ useAssemblyScript: false, // Default to sandboxed execution
118
+ sandboxMode: "vm",
119
+ ...options,
120
+ };
121
+
122
+ // Try AssemblyScript compilation if explicitly requested and available
123
+ if (opts.useAssemblyScript && (await this.isAvailable())) {
124
+ return this.compileWithAssemblyScript(source, permissions, limits, opts);
125
+ }
126
+
127
+ // For standard JavaScript, create a sandboxed execution wrapper
128
+ return this.createSandboxedWrapper(source, permissions, limits, opts);
129
+ }
130
+
131
+ /**
132
+ * Get language capabilities
133
+ */
134
+ getCapabilities(): LanguageCapabilities {
135
+ return {
136
+ repl: true,
137
+ stateful: true,
138
+ compiled: false, // JS is interpreted, not compiled to WASM
139
+ gc: true, // JavaScript has garbage collection
140
+ imports: true,
141
+ async: true,
142
+ };
143
+ }
144
+
145
+ /**
146
+ * Compile with AssemblyScript
147
+ *
148
+ * Note: AssemblyScript requires strict typing with WASM types.
149
+ * Standard JavaScript with dynamic features won't compile.
150
+ */
151
+ private async compileWithAssemblyScript(
152
+ source: string,
153
+ _permissions: Permissions,
154
+ _limits: Limits,
155
+ _options: JavaScriptCompilerOptions
156
+ ): Promise<CompileResult> {
157
+ try {
158
+ const asc = (await import("assemblyscript")) as unknown as AssemblyScriptModule;
159
+
160
+ // Wrap user code in a module structure
161
+ const assemblyScriptSource = this.wrapForAssemblyScript(source);
162
+
163
+ return new Promise((resolve, reject) => {
164
+ asc.main(
165
+ [
166
+ "--textFile", "/dev/stdout",
167
+ "--outFile", "/dev/stdout",
168
+ "--optimize",
169
+ "--target", "release",
170
+ ],
171
+ {
172
+ stdin: () => assemblyScriptSource,
173
+ listBuiltins: () => asc.builtins,
174
+ reportDiagnostic: (diagnostic: { messageText: string }) => {
175
+ console.error(diagnostic.messageText);
176
+ },
177
+ },
178
+ (error: Error | null, result?: { stdout: Uint8Array }) => {
179
+ if (error) {
180
+ reject(new Error(`AssemblyScript compilation failed: ${error.message}`));
181
+ return;
182
+ }
183
+
184
+ if (!result?.stdout) {
185
+ reject(new Error("AssemblyScript produced no output"));
186
+ return;
187
+ }
188
+
189
+ // Parse the WASM module to extract exports/imports
190
+ WebAssembly.compile(result.stdout)
191
+ .then((module) => {
192
+ const exports = WebAssembly.Module.exports(module).map((e) => e.name);
193
+ const imports = WebAssembly.Module.imports(module).map((i) => ({
194
+ module: i.module,
195
+ name: i.name,
196
+ type: i.kind as "function" | "memory" | "global" | "table",
197
+ }));
198
+
199
+ resolve({
200
+ wasmBytes: result.stdout,
201
+ exports,
202
+ imports,
203
+ memoryRequirements: {
204
+ initial: 1,
205
+ maximum: 256, // 16MB max
206
+ },
207
+ });
208
+ })
209
+ .catch(reject);
210
+ }
211
+ );
212
+ });
213
+ } catch (error) {
214
+ throw new Error(
215
+ `AssemblyScript compilation failed: ${error instanceof Error ? error.message : String(error)}`
216
+ );
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Create a sandboxed execution wrapper
222
+ *
223
+ * Instead of compiling to WASM, we create a WASM module that
224
+ * provides a sandboxed JavaScript execution environment.
225
+ * For simplicity, we use a minimal wrapper that can be executed
226
+ * by a JavaScript runtime.
227
+ */
228
+ private async createSandboxedWrapper(
229
+ source: string,
230
+ permissions: Permissions,
231
+ limits: Limits,
232
+ options: JavaScriptCompilerOptions
233
+ ): Promise<CompileResult> {
234
+ // Transpile with esbuild if available
235
+ let processedSource = source;
236
+ const esbuild = await this.getEsbuild();
237
+
238
+ if (esbuild) {
239
+ const result = await esbuild.transform(source, {
240
+ loader: "js",
241
+ target: options.target || "es2020",
242
+ format: options.module === "cjs" ? "cjs" : "esm",
243
+ minify: options.optimize,
244
+ });
245
+ processedSource = result.code;
246
+ }
247
+
248
+ // Create a WASM wrapper that provides sandboxed execution
249
+ // This is a minimal WASM module that exports a function to get the JS source
250
+ // The actual execution happens in the JavaScript runtime
251
+ return this.createSandboxWasmWrapper(processedSource, permissions, limits);
252
+ }
253
+
254
+ /**
255
+ * Create a WASM wrapper for sandboxed JavaScript execution
256
+ *
257
+ * This creates a WASM module that:
258
+ * 1. Contains the JavaScript source code as data
259
+ * 2. Exports functions for the runtime to execute
260
+ * 3. Provides memory for execution results
261
+ */
262
+ private async createSandboxWasmWrapper(
263
+ source: string,
264
+ permissions: Permissions,
265
+ limits: Limits
266
+ ): Promise<CompileResult> {
267
+ // Encode the source and configuration
268
+ const sourceBytes = new TextEncoder().encode(source);
269
+ const configBytes = new TextEncoder().encode(
270
+ JSON.stringify({
271
+ permissions: {
272
+ fs: permissions.fs ? {
273
+ read: permissions.fs.read ?? false,
274
+ write: permissions.fs.write ?? false,
275
+ delete: permissions.fs.delete ?? false,
276
+ } : undefined,
277
+ network: permissions.network ? {
278
+ outbound: permissions.network.outbound ?? false,
279
+ inbound: permissions.network.inbound ?? false,
280
+ } : undefined,
281
+ },
282
+ limits: {
283
+ timeout: typeof limits.timeout === "string"
284
+ ? this.parseTime(limits.timeout)
285
+ : limits.timeout ?? 30000,
286
+ memory: typeof limits.memory === "string"
287
+ ? this.parseMemory(limits.memory)
288
+ : limits.memory ?? 16 * 1024 * 1024,
289
+ },
290
+ })
291
+ );
292
+
293
+ // Create a minimal WASM module that holds the JavaScript source
294
+ // This module exports functions to access the source and config
295
+ const wasmBytes = this.buildSandboxWasm(sourceBytes, configBytes);
296
+
297
+ return {
298
+ wasmBytes,
299
+ exports: ["getSource", "getConfig", "execute", "getResult"],
300
+ imports: [
301
+ { module: "env", name: "log", type: "function" },
302
+ { module: "env", name: "abort", type: "function" },
303
+ ],
304
+ memoryRequirements: {
305
+ initial: Math.max(1, Math.ceil((sourceBytes.length + configBytes.length) / 65536)),
306
+ maximum: 256,
307
+ },
308
+ };
309
+ }
310
+
311
+ /**
312
+ * Build the sandbox WASM module
313
+ *
314
+ * Creates a minimal WASM binary that contains:
315
+ * - Data section with JS source
316
+ * - Exported functions for runtime interaction
317
+ */
318
+ private buildSandboxWasm(sourceBytes: Uint8Array, configBytes: Uint8Array): Uint8Array {
319
+ // WASM module structure:
320
+ // - Magic number + version
321
+ // - Type section (function signatures)
322
+ // - Function section
323
+ // - Memory section
324
+ // - Export section
325
+ // - Code section
326
+ // - Data section (JS source and config)
327
+
328
+ const magicNumber = new Uint8Array([0x00, 0x61, 0x73, 0x6D]); // \0asm
329
+ const version = new Uint8Array([0x01, 0x00, 0x00, 0x00]); // version 1
330
+
331
+ // Build sections
332
+ const typeSection = this.buildTypeSection();
333
+ const functionSection = this.buildFunctionSection();
334
+ const memorySection = this.buildMemorySection(sourceBytes.length + configBytes.length + 1024);
335
+ const exportSection = this.buildExportSection();
336
+ const codeSection = this.buildCodeSection();
337
+ const dataSection = this.buildDataSection(sourceBytes, configBytes);
338
+
339
+ // Combine all sections
340
+ const sections = [
341
+ typeSection,
342
+ functionSection,
343
+ memorySection,
344
+ exportSection,
345
+ codeSection,
346
+ dataSection,
347
+ ];
348
+
349
+ const totalLength = magicNumber.length + version.length +
350
+ sections.reduce((sum, s) => sum + s.length, 0);
351
+
352
+ const wasm = new Uint8Array(totalLength);
353
+ let offset = 0;
354
+
355
+ // Write header
356
+ wasm.set(magicNumber, offset);
357
+ offset += magicNumber.length;
358
+ wasm.set(version, offset);
359
+ offset += version.length;
360
+
361
+ // Write sections
362
+ for (const section of sections) {
363
+ wasm.set(section, offset);
364
+ offset += section.length;
365
+ }
366
+
367
+ return wasm;
368
+ }
369
+
370
+ private buildTypeSection(): Uint8Array {
371
+ // Section ID 1 (type), 1 type, function type () -> i32
372
+ return new Uint8Array([
373
+ 0x01, // section id
374
+ 0x04, // section size
375
+ 0x01, // num types
376
+ 0x60, // func type
377
+ 0x00, // 0 params
378
+ 0x01, 0x7F, // 1 result: i32
379
+ ]);
380
+ }
381
+
382
+ private buildFunctionSection(): Uint8Array {
383
+ // Section ID 3 (function), 4 functions
384
+ return new Uint8Array([
385
+ 0x03, // section id
386
+ 0x05, // section size
387
+ 0x04, // num functions
388
+ 0x00, 0x00, 0x00, 0x00, // function indices (all type 0)
389
+ ]);
390
+ }
391
+
392
+ private buildMemorySection(dataSize: number): Uint8Array {
393
+ const pages = Math.max(1, Math.ceil(dataSize / 65536));
394
+ // Section ID 5 (memory), 1 memory, limits
395
+ return new Uint8Array([
396
+ 0x05, // section id
397
+ 0x03, // section size
398
+ 0x01, // num memories
399
+ 0x01, // has max
400
+ pages, // initial pages
401
+ 0x80, 0x02, // max pages (256)
402
+ ]);
403
+ }
404
+
405
+ private buildExportSection(): Uint8Array {
406
+ // Section ID 7 (export)
407
+ // Export: memory, getSource, getConfig, execute, getResult
408
+ const exports = [
409
+ { name: "memory", kind: 0x02, index: 0 }, // memory
410
+ { name: "getSource", kind: 0x00, index: 0 }, // func 0
411
+ { name: "getConfig", kind: 0x00, index: 1 }, // func 1
412
+ { name: "execute", kind: 0x00, index: 2 }, // func 2
413
+ { name: "getResult", kind: 0x00, index: 3 }, // func 3
414
+ ];
415
+
416
+ const nameBytes: number[] = [];
417
+ for (const exp of exports) {
418
+ const nameEncoded = new TextEncoder().encode(exp.name);
419
+ nameBytes.push(nameEncoded.length);
420
+ nameBytes.push(...nameEncoded);
421
+ nameBytes.push(exp.kind);
422
+ nameBytes.push(exp.index);
423
+ }
424
+
425
+ const sectionContent = [exports.length, ...nameBytes];
426
+ const sectionSize = sectionContent.length;
427
+
428
+ return new Uint8Array([0x07, sectionSize, ...sectionContent]);
429
+ }
430
+
431
+ private buildCodeSection(): Uint8Array {
432
+ // Section ID 10 (code)
433
+ // 4 function bodies, each returning a constant
434
+ const functions = [
435
+ [0x00, 0x41, 0x00, 0x0B], // getSource: return 0 (offset)
436
+ [0x00, 0x41, 0x00, 0x0B], // getConfig: return 0 (placeholder)
437
+ [0x00, 0x41, 0x00, 0x0B], // execute: return 0 (placeholder)
438
+ [0x00, 0x41, 0x00, 0x0B], // getResult: return 0 (placeholder)
439
+ ];
440
+
441
+ const bodies: number[] = [];
442
+ for (const body of functions) {
443
+ bodies.push(body.length);
444
+ bodies.push(...body);
445
+ }
446
+
447
+ const sectionContent = [functions.length, ...bodies];
448
+ const sectionSize = sectionContent.length;
449
+
450
+ return new Uint8Array([0x0A, sectionSize, ...sectionContent]);
451
+ }
452
+
453
+ private buildDataSection(sourceBytes: Uint8Array, configBytes: Uint8Array): Uint8Array {
454
+ // Section ID 11 (data)
455
+ // 2 data segments: source and config
456
+ const sourceLen = this.encodeLEB128(sourceBytes.length);
457
+ const configLen = this.encodeLEB128(configBytes.length);
458
+
459
+ const segment1 = [
460
+ 0x00, // active, memory 0
461
+ 0x41, 0x00, 0x23, 0x00, // i32.const 0 (offset placeholder)
462
+ ...sourceLen,
463
+ ...sourceBytes,
464
+ ];
465
+
466
+ const segment2 = [
467
+ 0x00, // active, memory 0
468
+ 0x41, 0x00, 0x23, 0x00, // i32.const 0 (offset placeholder)
469
+ ...configLen,
470
+ ...configBytes,
471
+ ];
472
+
473
+ const sectionContent = [
474
+ 0x02, // 2 data segments
475
+ ...segment1,
476
+ ...segment2,
477
+ ];
478
+
479
+ const sectionSize = sectionContent.length;
480
+ return new Uint8Array([0x0B, sectionSize, ...sectionContent]);
481
+ }
482
+
483
+ private encodeLEB128(value: number): number[] {
484
+ const result: number[] = [];
485
+ let remaining = value;
486
+
487
+ do {
488
+ let byte = remaining & 0x7F;
489
+ remaining >>>= 7;
490
+ if (remaining !== 0) {
491
+ byte |= 0x80;
492
+ }
493
+ result.push(byte);
494
+ } while (remaining !== 0);
495
+
496
+ return result;
497
+ }
498
+
499
+ /**
500
+ * Wrap JavaScript source for AssemblyScript compilation
501
+ *
502
+ * AssemblyScript requires explicit types and specific syntax.
503
+ * This wrapper attempts to make standard JS compatible.
504
+ */
505
+ private wrapForAssemblyScript(source: string): string {
506
+ return `
507
+ // AssemblyScript wrapper
508
+ // Note: AssemblyScript requires strict typing with i32, i64, f32, f64
509
+
510
+ ${source}
511
+
512
+ // Export a main function if not present
513
+ export function _start(): void {
514
+ // Entry point
515
+ }
516
+ `;
517
+ }
518
+
519
+ /**
520
+ * Get esbuild instance for transpilation
521
+ */
522
+ private async getEsbuild(): Promise<EsbuildModule | null> {
523
+ if (this.esbuildAvailable === false) {
524
+ return null;
525
+ }
526
+
527
+ try {
528
+ const esbuild = await import("esbuild") as EsbuildModule;
529
+ this.esbuildAvailable = true;
530
+ return esbuild;
531
+ } catch {
532
+ this.esbuildAvailable = false;
533
+ return null;
534
+ }
535
+ }
536
+
537
+ /**
538
+ * Parse memory string to bytes
539
+ */
540
+ private parseMemory(mem: string): number {
541
+ const match = mem.match(/^(\d+(?:\.\d+)?)\s*(b|kb|mb|gb)?$/i);
542
+ if (!match) return 16 * 1024 * 1024;
543
+ const [, num, unit] = match;
544
+ const multipliers: Record<string, number> = {
545
+ b: 1,
546
+ kb: 1024,
547
+ mb: 1024 ** 2,
548
+ gb: 1024 ** 3,
549
+ };
550
+ return Math.floor(parseFloat(num) * (multipliers[unit?.toLowerCase() ?? "b"] ?? 1));
551
+ }
552
+
553
+ /**
554
+ * Parse time string to milliseconds
555
+ */
556
+ private parseTime(time: string): number {
557
+ const match = time.match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/i);
558
+ if (!match) return 30000;
559
+ const [, num, unit] = match;
560
+ const multipliers: Record<string, number> = {
561
+ ms: 1,
562
+ s: 1000,
563
+ m: 60000,
564
+ h: 3600000,
565
+ };
566
+ return Math.floor(parseFloat(num) * (multipliers[unit?.toLowerCase() ?? "ms"] ?? 1));
567
+ }
568
+ }
569
+
570
+ // Default instance
571
+ export const javascriptCompiler = new JavaScriptCompiler();