sandlot 0.2.0 → 0.2.2
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/dist/browser/bundler.d.ts +8 -0
- package/dist/browser/bundler.d.ts.map +1 -1
- package/dist/browser/iframe-executor.d.ts +82 -0
- package/dist/browser/iframe-executor.d.ts.map +1 -0
- package/dist/browser/index.d.ts +4 -2
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +249 -55
- package/dist/browser/main-thread-executor.d.ts +46 -0
- package/dist/browser/main-thread-executor.d.ts.map +1 -0
- package/dist/browser/preset.d.ts +7 -2
- package/dist/browser/preset.d.ts.map +1 -1
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/types.d.ts +9 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/core/executor.d.ts.map +1 -1
- package/dist/core/sandbox.d.ts.map +1 -1
- package/dist/core/sandlot.d.ts.map +1 -1
- package/dist/core/typechecker.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -46
- package/dist/node/bundler.d.ts +5 -0
- package/dist/node/bundler.d.ts.map +1 -1
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +218 -54
- package/dist/node/preset.d.ts +16 -1
- package/dist/node/preset.d.ts.map +1 -1
- package/dist/node/wasm-bundler.d.ts +86 -0
- package/dist/node/wasm-bundler.d.ts.map +1 -0
- package/dist/types.d.ts +35 -7
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/browser/bundler.ts +17 -0
- package/src/browser/iframe-executor.ts +320 -0
- package/src/browser/index.ts +9 -2
- package/src/browser/preset.ts +30 -6
- package/src/commands/index.ts +18 -40
- package/src/commands/types.ts +36 -0
- package/src/core/executor.ts +8 -7
- package/src/core/sandbox.ts +3 -0
- package/src/core/sandlot.ts +7 -0
- package/src/core/typechecker.ts +4 -2
- package/src/index.ts +2 -0
- package/src/node/bundler.ts +11 -0
- package/src/node/index.ts +10 -0
- package/src/node/preset.ts +59 -5
- package/src/node/wasm-bundler.ts +299 -0
- package/src/types.ts +38 -7
- /package/src/browser/{executor.ts → main-thread-executor.ts} +0 -0
package/dist/node/preset.d.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { type EsmTypesResolverOptions } from "../core/esm-types-resolver";
|
|
2
2
|
import type { Sandlot, SandlotOptions } from "../types";
|
|
3
3
|
import { type EsbuildNativeBundlerOptions } from "./bundler";
|
|
4
|
+
import { type EsbuildWasmNodeBundlerOptions } from "./wasm-bundler";
|
|
4
5
|
import { type TypecheckerOptions } from "../core/typechecker";
|
|
5
6
|
import { type NodeExecutorOptions } from "./executor";
|
|
6
7
|
export interface CreateNodeSandlotOptions extends Omit<SandlotOptions, "bundler" | "typechecker" | "typesResolver" | "executor"> {
|
|
7
8
|
/**
|
|
8
9
|
* Custom bundler options, or a pre-configured bundler instance.
|
|
10
|
+
*
|
|
11
|
+
* Set to `"wasm"` to use the WASM bundler (for testing consistency with browser).
|
|
12
|
+
* You can also pass `{ wasm: true, ...options }` for WASM bundler with custom options.
|
|
13
|
+
*
|
|
14
|
+
* @default EsbuildNativeBundler (fastest, uses native esbuild binary)
|
|
9
15
|
*/
|
|
10
|
-
bundler?: EsbuildNativeBundlerOptions |
|
|
16
|
+
bundler?: EsbuildNativeBundlerOptions | (EsbuildWasmNodeBundlerOptions & {
|
|
17
|
+
wasm: true;
|
|
18
|
+
}) | SandlotOptions["bundler"] | "wasm";
|
|
11
19
|
/**
|
|
12
20
|
* Custom typechecker options, or a pre-configured typechecker instance.
|
|
13
21
|
* Set to `false` to disable type checking.
|
|
@@ -57,6 +65,13 @@ export interface CreateNodeSandlotOptions extends Omit<SandlotOptions, "bundler"
|
|
|
57
65
|
* typechecker: false,
|
|
58
66
|
* });
|
|
59
67
|
* ```
|
|
68
|
+
*
|
|
69
|
+
* @example Use WASM bundler for testing consistency with browser
|
|
70
|
+
* ```ts
|
|
71
|
+
* const sandlot = await createNodeSandlot({
|
|
72
|
+
* bundler: "wasm",
|
|
73
|
+
* });
|
|
74
|
+
* ```
|
|
60
75
|
*/
|
|
61
76
|
export declare function createNodeSandlot(options?: CreateNodeSandlotOptions): Promise<Sandlot>;
|
|
62
77
|
//# sourceMappingURL=preset.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../src/node/preset.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAwB,KAAK,2BAA2B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,wBACf,SAAQ,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,UAAU,CAAC;IACtF
|
|
1
|
+
{"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../src/node/preset.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAwB,KAAK,2BAA2B,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,wBACf,SAAQ,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,UAAU,CAAC;IACtF;;;;;;;OAOG;IACH,OAAO,CAAC,EACJ,2BAA2B,GAC3B,CAAC,6BAA6B,GAAG;QAAE,IAAI,EAAE,IAAI,CAAA;KAAE,CAAC,GAChD,cAAc,CAAC,SAAS,CAAC,GACzB,MAAM,CAAC;IAEX;;;OAGG;IACH,WAAW,CAAC,EACV,kBAAkB,GAClB,cAAc,CAAC,aAAa,CAAC,GAC7B,KAAK,CAAC;IAER;;;OAGG;IACH,aAAa,CAAC,EACZ,uBAAuB,GACvB,cAAc,CAAC,eAAe,CAAC,GAC/B,KAAK,CAAC;IAER;;;;OAIG;IACH,QAAQ,CAAC,EACP,mBAAmB,GACnB,cAAc,CAAC,UAAU,CAAC,GAC1B,KAAK,CAAC;CACT;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,OAAO,CAAC,CA8ClB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node/Bun/Deno bundler implementation using esbuild-wasm.
|
|
3
|
+
*
|
|
4
|
+
* This bundler uses the same WebAssembly-based esbuild as the browser bundler,
|
|
5
|
+
* but runs in Node.js/Bun/Deno environments. It's primarily useful for:
|
|
6
|
+
*
|
|
7
|
+
* 1. Testing consistency with the browser bundler
|
|
8
|
+
* 2. Ensuring identical import resolution behavior
|
|
9
|
+
* 3. Validating that bundled output matches between browser and server
|
|
10
|
+
*
|
|
11
|
+
* For production use, prefer EsbuildNativeBundler which is ~3-5x faster.
|
|
12
|
+
*/
|
|
13
|
+
import type { IBundler, BundleOptions, BundleResult } from "../types";
|
|
14
|
+
export interface EsbuildWasmNodeBundlerOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Base URL for CDN imports.
|
|
17
|
+
* npm imports like "lodash" are rewritten to "{cdnBaseUrl}/lodash@{version}".
|
|
18
|
+
* @default "https://esm.sh"
|
|
19
|
+
*/
|
|
20
|
+
cdnBaseUrl?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Bundler implementation using esbuild-wasm for Node.js/Bun/Deno.
|
|
24
|
+
*
|
|
25
|
+
* Uses the same WebAssembly-based esbuild as the browser bundler,
|
|
26
|
+
* making it ideal for testing consistency between browser and server builds.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const bundler = new EsbuildWasmNodeBundler();
|
|
31
|
+
* await bundler.initialize();
|
|
32
|
+
*
|
|
33
|
+
* const result = await bundler.bundle({
|
|
34
|
+
* fs: myFilesystem,
|
|
35
|
+
* entryPoint: "/src/index.ts",
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @example Testing consistency with native bundler
|
|
40
|
+
* ```ts
|
|
41
|
+
* const native = new EsbuildNativeBundler();
|
|
42
|
+
* const wasm = new EsbuildWasmNodeBundler();
|
|
43
|
+
*
|
|
44
|
+
* await native.initialize();
|
|
45
|
+
* await wasm.initialize();
|
|
46
|
+
*
|
|
47
|
+
* const nativeResult = await native.bundle(options);
|
|
48
|
+
* const wasmResult = await wasm.bundle(options);
|
|
49
|
+
*
|
|
50
|
+
* // Results should be equivalent (modulo minor formatting differences)
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare class EsbuildWasmNodeBundler implements IBundler {
|
|
54
|
+
private options;
|
|
55
|
+
constructor(options?: EsbuildWasmNodeBundlerOptions);
|
|
56
|
+
/**
|
|
57
|
+
* Initialize the esbuild WASM module.
|
|
58
|
+
* Called automatically on first bundle() if not already initialized.
|
|
59
|
+
*
|
|
60
|
+
* Uses a global singleton pattern since esbuild-wasm can only be
|
|
61
|
+
* initialized once per process.
|
|
62
|
+
*/
|
|
63
|
+
initialize(): Promise<void>;
|
|
64
|
+
private doInitialize;
|
|
65
|
+
/**
|
|
66
|
+
* Get the initialized esbuild instance.
|
|
67
|
+
*/
|
|
68
|
+
private getEsbuild;
|
|
69
|
+
/**
|
|
70
|
+
* Dispose of the esbuild WASM service.
|
|
71
|
+
* This stops the esbuild service and allows the process to exit.
|
|
72
|
+
*
|
|
73
|
+
* Note: Since esbuild-wasm uses a global singleton, this affects all
|
|
74
|
+
* instances. After dispose(), you'll need to create a new bundler.
|
|
75
|
+
*/
|
|
76
|
+
dispose(): Promise<void>;
|
|
77
|
+
bundle(options: BundleOptions): Promise<BundleResult>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Create an esbuild-wasm bundler for Node.js/Bun/Deno.
|
|
81
|
+
*
|
|
82
|
+
* This is primarily useful for testing consistency with the browser bundler.
|
|
83
|
+
* For production use, prefer createEsbuildNativeBundler() which is ~3-5x faster.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createEsbuildWasmNodeBundler(options?: EsbuildWasmNodeBundlerOptions): EsbuildWasmNodeBundler;
|
|
86
|
+
//# sourceMappingURL=wasm-bundler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wasm-bundler.d.ts","sourceRoot":"","sources":["../../src/node/wasm-bundler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,YAAY,EAGb,MAAM,UAAU,CAAC;AAoClB,MAAM,WAAW,6BAA6B;IAC5C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,qBAAa,sBAAuB,YAAW,QAAQ;IACrD,OAAO,CAAC,OAAO,CAAgC;gBAEnC,OAAO,GAAE,6BAAkC;IAOvD;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBnB,YAAY;IAoB1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAUxB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CA0G5D;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,CAAC,EAAE,6BAA6B,GACtC,sBAAsB,CAExB"}
|
package/dist/types.d.ts
CHANGED
|
@@ -11,6 +11,11 @@ export interface IBundler {
|
|
|
11
11
|
* Bundle source files from a filesystem into a single output
|
|
12
12
|
*/
|
|
13
13
|
bundle(options: BundleOptions): Promise<BundleResult>;
|
|
14
|
+
/**
|
|
15
|
+
* Dispose of resources held by the bundler (optional).
|
|
16
|
+
* Called by Sandlot.dispose() to clean up background services.
|
|
17
|
+
*/
|
|
18
|
+
dispose?(): Promise<void>;
|
|
14
19
|
}
|
|
15
20
|
export interface BundleOptions {
|
|
16
21
|
fs: Filesystem;
|
|
@@ -284,8 +289,11 @@ export interface BuildSuccess {
|
|
|
284
289
|
/** Any warnings from the bundler */
|
|
285
290
|
warnings: BundleWarning[];
|
|
286
291
|
}
|
|
287
|
-
|
|
288
|
-
|
|
292
|
+
/**
|
|
293
|
+
* Details about why a build failed.
|
|
294
|
+
* Used by both BuildResult and RunResult.
|
|
295
|
+
*/
|
|
296
|
+
export interface BuildFailureDetails {
|
|
289
297
|
/** Which phase of the build failed */
|
|
290
298
|
phase: BuildPhase;
|
|
291
299
|
/** Error message (for entry failures) */
|
|
@@ -297,6 +305,9 @@ export interface BuildFailure {
|
|
|
297
305
|
/** Bundle warnings (may be present even on failure) */
|
|
298
306
|
bundleWarnings?: BundleWarning[];
|
|
299
307
|
}
|
|
308
|
+
export interface BuildFailure extends BuildFailureDetails {
|
|
309
|
+
success: false;
|
|
310
|
+
}
|
|
300
311
|
export interface InstallResult {
|
|
301
312
|
/** Package name */
|
|
302
313
|
name: string;
|
|
@@ -388,11 +399,8 @@ export interface RunOptions {
|
|
|
388
399
|
* If `buildFailure` is present, the build failed before execution.
|
|
389
400
|
*/
|
|
390
401
|
export interface RunResult extends ExecuteResult {
|
|
391
|
-
/** If build failed, contains failure details */
|
|
392
|
-
buildFailure?:
|
|
393
|
-
phase: BuildPhase;
|
|
394
|
-
message?: string;
|
|
395
|
-
};
|
|
402
|
+
/** If build failed, contains failure details (same structure as BuildFailure) */
|
|
403
|
+
buildFailure?: BuildFailureDetails;
|
|
396
404
|
}
|
|
397
405
|
export interface Sandbox {
|
|
398
406
|
/**
|
|
@@ -521,5 +529,25 @@ export interface Sandlot {
|
|
|
521
529
|
* The shared module registry (if shared modules were provided)
|
|
522
530
|
*/
|
|
523
531
|
readonly sharedModules: ISharedModuleRegistry | null;
|
|
532
|
+
/**
|
|
533
|
+
* Dispose of resources held by this Sandlot instance.
|
|
534
|
+
*
|
|
535
|
+
* This should be called when you're done using Sandlot to allow
|
|
536
|
+
* the process to exit cleanly. It stops any background services
|
|
537
|
+
* like the esbuild child process.
|
|
538
|
+
*
|
|
539
|
+
* After calling dispose(), this instance should not be used.
|
|
540
|
+
*
|
|
541
|
+
* @example
|
|
542
|
+
* ```ts
|
|
543
|
+
* const sandlot = await createNodeSandlot();
|
|
544
|
+
* const sandbox = await sandlot.createSandbox();
|
|
545
|
+
*
|
|
546
|
+
* // ... do work ...
|
|
547
|
+
*
|
|
548
|
+
* await sandlot.dispose(); // Allow process to exit
|
|
549
|
+
* ```
|
|
550
|
+
*/
|
|
551
|
+
dispose(): Promise<void>;
|
|
524
552
|
}
|
|
525
553
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAM5C,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACtE,YAAY,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAM5C;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB;;OAEG;IACH,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/browser/bundler.ts
CHANGED
|
@@ -177,6 +177,23 @@ export class EsbuildWasmBundler implements IBundler {
|
|
|
177
177
|
return state.esbuild;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
+
/**
|
|
181
|
+
* Dispose of the esbuild WASM service.
|
|
182
|
+
* This stops the esbuild service and allows the process to exit.
|
|
183
|
+
*
|
|
184
|
+
* Note: Since esbuild-wasm uses a global singleton, this affects all
|
|
185
|
+
* instances. After dispose(), you'll need to create a new bundler.
|
|
186
|
+
*/
|
|
187
|
+
async dispose(): Promise<void> {
|
|
188
|
+
const state = getGlobalState();
|
|
189
|
+
if (state.esbuild) {
|
|
190
|
+
await state.esbuild.stop();
|
|
191
|
+
state.esbuild = null;
|
|
192
|
+
state.initialized = false;
|
|
193
|
+
state.initPromise = null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
180
197
|
private checkCrossOriginIsolation(): void {
|
|
181
198
|
if (typeof window === "undefined") return;
|
|
182
199
|
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Iframe executor for browser environments.
|
|
3
|
+
*
|
|
4
|
+
* This executor runs code in a sandboxed iframe, providing DOM isolation
|
|
5
|
+
* and configurable security policies via the sandbox attribute.
|
|
6
|
+
*
|
|
7
|
+
* Key characteristics:
|
|
8
|
+
* - Per-execution lifecycle: fresh iframe for each execute() call
|
|
9
|
+
* - Configurable sandbox attributes (default: allow-scripts only)
|
|
10
|
+
* - No shared module support (use MainThreadExecutor for that)
|
|
11
|
+
* - Communication via postMessage
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { IExecutor, ExecuteOptions, ExecuteResult } from "../types";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Options for creating an IframeExecutor.
|
|
18
|
+
*/
|
|
19
|
+
export interface IframeExecutorOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Sandbox attributes for the iframe.
|
|
22
|
+
* @default ["allow-scripts"]
|
|
23
|
+
*
|
|
24
|
+
* Common options:
|
|
25
|
+
* - "allow-scripts": Required for code execution
|
|
26
|
+
* - "allow-same-origin": Enables localStorage, cookies (reduces isolation)
|
|
27
|
+
* - "allow-modals": Enables alert/confirm/prompt
|
|
28
|
+
*
|
|
29
|
+
* Security note: "allow-scripts" + "allow-same-origin" together allows
|
|
30
|
+
* the iframe code to potentially remove the sandbox via script.
|
|
31
|
+
*/
|
|
32
|
+
sandbox?: string[];
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Default timeout in milliseconds.
|
|
36
|
+
* @default 30000
|
|
37
|
+
*/
|
|
38
|
+
defaultTimeout?: number;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Container element for iframes.
|
|
42
|
+
* Iframes are created hidden (display: none).
|
|
43
|
+
* @default document.body
|
|
44
|
+
*/
|
|
45
|
+
container?: HTMLElement;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Message types for parent -> iframe communication.
|
|
50
|
+
*/
|
|
51
|
+
interface ExecuteMessage {
|
|
52
|
+
type: "execute";
|
|
53
|
+
code: string;
|
|
54
|
+
entryExport: "main" | "default";
|
|
55
|
+
context: Record<string, unknown>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Message types for iframe -> parent communication.
|
|
60
|
+
*/
|
|
61
|
+
interface LogMessage {
|
|
62
|
+
type: "log";
|
|
63
|
+
level: "log" | "warn" | "error" | "info" | "debug";
|
|
64
|
+
args: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface ResultMessage {
|
|
68
|
+
type: "result";
|
|
69
|
+
success: boolean;
|
|
70
|
+
returnValue?: unknown;
|
|
71
|
+
error?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface ReadyMessage {
|
|
75
|
+
type: "ready";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type IframeMessage = LogMessage | ResultMessage | ReadyMessage;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Bootstrap HTML that runs inside the iframe.
|
|
82
|
+
* This is injected via srcdoc and handles:
|
|
83
|
+
* 1. Console capture and forwarding
|
|
84
|
+
* 2. Code execution via Blob URL import
|
|
85
|
+
* 3. Result reporting back to parent
|
|
86
|
+
*/
|
|
87
|
+
const BOOTSTRAP_HTML = `<!DOCTYPE html>
|
|
88
|
+
<html>
|
|
89
|
+
<head>
|
|
90
|
+
<meta charset="utf-8">
|
|
91
|
+
</head>
|
|
92
|
+
<body>
|
|
93
|
+
<script type="module">
|
|
94
|
+
// Capture console methods and forward to parent
|
|
95
|
+
function formatArgs(...args) {
|
|
96
|
+
return args
|
|
97
|
+
.map(v => typeof v === "object" ? JSON.stringify(v) : String(v))
|
|
98
|
+
.join(" ");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function createLogger(level) {
|
|
102
|
+
return (...args) => {
|
|
103
|
+
parent.postMessage({ type: "log", level, args: formatArgs(...args) }, "*");
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.log = createLogger("log");
|
|
108
|
+
console.warn = createLogger("warn");
|
|
109
|
+
console.error = createLogger("error");
|
|
110
|
+
console.info = createLogger("info");
|
|
111
|
+
console.debug = createLogger("debug");
|
|
112
|
+
|
|
113
|
+
// Handle unhandled promise rejections
|
|
114
|
+
window.addEventListener("unhandledrejection", (event) => {
|
|
115
|
+
const message = event.reason instanceof Error
|
|
116
|
+
? event.reason.message
|
|
117
|
+
: String(event.reason);
|
|
118
|
+
parent.postMessage({
|
|
119
|
+
type: "result",
|
|
120
|
+
success: false,
|
|
121
|
+
error: "Unhandled promise rejection: " + message
|
|
122
|
+
}, "*");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// Handle uncaught errors
|
|
126
|
+
window.addEventListener("error", (event) => {
|
|
127
|
+
parent.postMessage({
|
|
128
|
+
type: "result",
|
|
129
|
+
success: false,
|
|
130
|
+
error: event.message || "Unknown error"
|
|
131
|
+
}, "*");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Listen for execute messages from parent
|
|
135
|
+
window.addEventListener("message", async (event) => {
|
|
136
|
+
if (event.data?.type !== "execute") return;
|
|
137
|
+
|
|
138
|
+
const { code, entryExport, context } = event.data;
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
// Create Blob URL and import as ESM module
|
|
142
|
+
const blob = new Blob([code], { type: "application/javascript" });
|
|
143
|
+
const url = URL.createObjectURL(blob);
|
|
144
|
+
|
|
145
|
+
let module;
|
|
146
|
+
try {
|
|
147
|
+
module = await import(url);
|
|
148
|
+
} finally {
|
|
149
|
+
URL.revokeObjectURL(url);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Execute the appropriate export
|
|
153
|
+
let returnValue;
|
|
154
|
+
|
|
155
|
+
if (entryExport === "main" && typeof module.main === "function") {
|
|
156
|
+
returnValue = await module.main(context);
|
|
157
|
+
} else if (entryExport === "default" && typeof module.default === "function") {
|
|
158
|
+
returnValue = await module.default();
|
|
159
|
+
} else if (entryExport === "default" && module.default !== undefined) {
|
|
160
|
+
returnValue = module.default;
|
|
161
|
+
}
|
|
162
|
+
// If neither export exists, top-level code already ran on import
|
|
163
|
+
|
|
164
|
+
parent.postMessage({ type: "result", success: true, returnValue }, "*");
|
|
165
|
+
} catch (err) {
|
|
166
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
167
|
+
parent.postMessage({ type: "result", success: false, error: message }, "*");
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Signal that we're ready to receive code
|
|
172
|
+
parent.postMessage({ type: "ready" }, "*");
|
|
173
|
+
<\/script>
|
|
174
|
+
</body>
|
|
175
|
+
</html>`;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Executor that runs code in a sandboxed iframe.
|
|
179
|
+
*
|
|
180
|
+
* Each execute() call creates a fresh iframe, runs the code, and destroys
|
|
181
|
+
* the iframe. This provides clean isolation between executions.
|
|
182
|
+
*
|
|
183
|
+
* Note: This executor does NOT support shared modules. The iframe runs
|
|
184
|
+
* in complete isolation. Use MainThreadExecutor if you need shared modules.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* // Default: strict sandboxing (allow-scripts only)
|
|
189
|
+
* const executor = createIframeExecutor();
|
|
190
|
+
*
|
|
191
|
+
* // With additional permissions
|
|
192
|
+
* const executor = createIframeExecutor({
|
|
193
|
+
* sandbox: ["allow-scripts", "allow-same-origin"],
|
|
194
|
+
* });
|
|
195
|
+
*
|
|
196
|
+
* const result = await executor.execute(bundledCode, {
|
|
197
|
+
* entryExport: 'main',
|
|
198
|
+
* context: { args: ['--verbose'] },
|
|
199
|
+
* timeout: 5000,
|
|
200
|
+
* });
|
|
201
|
+
* console.log(result.logs);
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export class IframeExecutor implements IExecutor {
|
|
205
|
+
private options: Required<Omit<IframeExecutorOptions, "container">> & {
|
|
206
|
+
container?: HTMLElement;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
constructor(options: IframeExecutorOptions = {}) {
|
|
210
|
+
this.options = {
|
|
211
|
+
sandbox: options.sandbox ?? ["allow-scripts"],
|
|
212
|
+
defaultTimeout: options.defaultTimeout ?? 30000,
|
|
213
|
+
container: options.container,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
async execute(code: string, options: ExecuteOptions = {}): Promise<ExecuteResult> {
|
|
218
|
+
const {
|
|
219
|
+
entryExport = "main",
|
|
220
|
+
context = {},
|
|
221
|
+
timeout = this.options.defaultTimeout,
|
|
222
|
+
} = options;
|
|
223
|
+
|
|
224
|
+
const startTime = performance.now();
|
|
225
|
+
const logs: string[] = [];
|
|
226
|
+
|
|
227
|
+
// Get container (default to document.body)
|
|
228
|
+
const container = this.options.container ?? document.body;
|
|
229
|
+
|
|
230
|
+
// Create iframe
|
|
231
|
+
const iframe = document.createElement("iframe");
|
|
232
|
+
iframe.style.display = "none";
|
|
233
|
+
iframe.sandbox.add(...this.options.sandbox);
|
|
234
|
+
iframe.srcdoc = BOOTSTRAP_HTML;
|
|
235
|
+
|
|
236
|
+
// Track whether we've received a result
|
|
237
|
+
let resolved = false;
|
|
238
|
+
|
|
239
|
+
return new Promise<ExecuteResult>((resolve) => {
|
|
240
|
+
const cleanup = () => {
|
|
241
|
+
if (iframe.parentNode) {
|
|
242
|
+
iframe.parentNode.removeChild(iframe);
|
|
243
|
+
}
|
|
244
|
+
window.removeEventListener("message", handleMessage);
|
|
245
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
const finish = (result: ExecuteResult) => {
|
|
249
|
+
if (resolved) return;
|
|
250
|
+
resolved = true;
|
|
251
|
+
cleanup();
|
|
252
|
+
resolve(result);
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// Handle messages from iframe
|
|
256
|
+
const handleMessage = (event: MessageEvent) => {
|
|
257
|
+
// Verify message is from our iframe
|
|
258
|
+
if (event.source !== iframe.contentWindow) return;
|
|
259
|
+
|
|
260
|
+
const data = event.data as IframeMessage;
|
|
261
|
+
|
|
262
|
+
if (data.type === "log") {
|
|
263
|
+
const prefix =
|
|
264
|
+
data.level === "log" ? "" : `[${data.level}] `;
|
|
265
|
+
logs.push(prefix + data.args);
|
|
266
|
+
} else if (data.type === "result") {
|
|
267
|
+
const executionTimeMs = performance.now() - startTime;
|
|
268
|
+
finish({
|
|
269
|
+
success: data.success,
|
|
270
|
+
logs,
|
|
271
|
+
returnValue: data.returnValue,
|
|
272
|
+
error: data.error,
|
|
273
|
+
executionTimeMs,
|
|
274
|
+
});
|
|
275
|
+
} else if (data.type === "ready") {
|
|
276
|
+
// Iframe is ready, send the code to execute
|
|
277
|
+
const message: ExecuteMessage = {
|
|
278
|
+
type: "execute",
|
|
279
|
+
code,
|
|
280
|
+
entryExport,
|
|
281
|
+
context,
|
|
282
|
+
};
|
|
283
|
+
iframe.contentWindow?.postMessage(message, "*");
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// Set up timeout
|
|
288
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
289
|
+
if (timeout > 0) {
|
|
290
|
+
timeoutId = setTimeout(() => {
|
|
291
|
+
const executionTimeMs = performance.now() - startTime;
|
|
292
|
+
finish({
|
|
293
|
+
success: false,
|
|
294
|
+
logs,
|
|
295
|
+
error: `Execution timed out after ${timeout}ms`,
|
|
296
|
+
executionTimeMs,
|
|
297
|
+
});
|
|
298
|
+
}, timeout);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Listen for messages
|
|
302
|
+
window.addEventListener("message", handleMessage);
|
|
303
|
+
|
|
304
|
+
// Append iframe to DOM (this starts loading the srcdoc)
|
|
305
|
+
container.appendChild(iframe);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Create an iframe executor.
|
|
312
|
+
*
|
|
313
|
+
* @param options - Executor options
|
|
314
|
+
* @returns A new IframeExecutor instance
|
|
315
|
+
*/
|
|
316
|
+
export function createIframeExecutor(
|
|
317
|
+
options?: IframeExecutorOptions
|
|
318
|
+
): IframeExecutor {
|
|
319
|
+
return new IframeExecutor(options);
|
|
320
|
+
}
|
package/src/browser/index.ts
CHANGED
|
@@ -46,8 +46,15 @@ export type { EsbuildWasmBundlerOptions } from "./bundler";
|
|
|
46
46
|
// Executor (browser-specific: runs in main thread)
|
|
47
47
|
// -----------------------------------------------------------------------------
|
|
48
48
|
|
|
49
|
-
export { MainThreadExecutor, createMainThreadExecutor } from "./executor";
|
|
50
|
-
export type { MainThreadExecutorOptions } from "./executor";
|
|
49
|
+
export { MainThreadExecutor, createMainThreadExecutor } from "./main-thread-executor";
|
|
50
|
+
export type { MainThreadExecutorOptions } from "./main-thread-executor";
|
|
51
|
+
|
|
52
|
+
// -----------------------------------------------------------------------------
|
|
53
|
+
// Iframe Executor (browser-specific: runs in sandboxed iframe)
|
|
54
|
+
// -----------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
export { IframeExecutor, createIframeExecutor } from "./iframe-executor";
|
|
57
|
+
export type { IframeExecutorOptions } from "./iframe-executor";
|
|
51
58
|
|
|
52
59
|
// -----------------------------------------------------------------------------
|
|
53
60
|
// Convenience Preset
|
package/src/browser/preset.ts
CHANGED
|
@@ -12,7 +12,11 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
MainThreadExecutor,
|
|
14
14
|
type MainThreadExecutorOptions,
|
|
15
|
-
} from "./executor";
|
|
15
|
+
} from "./main-thread-executor";
|
|
16
|
+
import {
|
|
17
|
+
IframeExecutor,
|
|
18
|
+
type IframeExecutorOptions,
|
|
19
|
+
} from "./iframe-executor";
|
|
16
20
|
|
|
17
21
|
export interface CreateBrowserSandlotOptions
|
|
18
22
|
extends Omit<SandlotOptions, "bundler" | "typechecker" | "typesResolver" | "executor"> {
|
|
@@ -42,11 +46,17 @@ export interface CreateBrowserSandlotOptions
|
|
|
42
46
|
/**
|
|
43
47
|
* Custom executor options, or a pre-configured executor instance.
|
|
44
48
|
* Set to `false` to disable execution (sandbox.run() will throw).
|
|
49
|
+
* Set to `"iframe"` to use IframeExecutor with default options.
|
|
45
50
|
* Defaults to MainThreadExecutor.
|
|
51
|
+
*
|
|
52
|
+
* Note: IframeExecutor does NOT support shared modules. Use MainThreadExecutor
|
|
53
|
+
* (the default) if you need shared modules like React.
|
|
46
54
|
*/
|
|
47
55
|
executor?:
|
|
48
56
|
| MainThreadExecutorOptions
|
|
57
|
+
| IframeExecutorOptions
|
|
49
58
|
| SandlotOptions["executor"]
|
|
59
|
+
| "iframe"
|
|
50
60
|
| false;
|
|
51
61
|
}
|
|
52
62
|
|
|
@@ -121,11 +131,15 @@ export async function createBrowserSandlot(
|
|
|
121
131
|
const executorInstance =
|
|
122
132
|
executor === false
|
|
123
133
|
? undefined
|
|
124
|
-
:
|
|
125
|
-
?
|
|
126
|
-
:
|
|
127
|
-
executor
|
|
128
|
-
|
|
134
|
+
: executor === "iframe"
|
|
135
|
+
? new IframeExecutor()
|
|
136
|
+
: isExecutor(executor)
|
|
137
|
+
? executor
|
|
138
|
+
: isIframeExecutorOptions(executor)
|
|
139
|
+
? new IframeExecutor(executor)
|
|
140
|
+
: new MainThreadExecutor(
|
|
141
|
+
executor as MainThreadExecutorOptions | undefined
|
|
142
|
+
);
|
|
129
143
|
|
|
130
144
|
return createSandlot({
|
|
131
145
|
...rest,
|
|
@@ -177,3 +191,13 @@ function isExecutor(value: unknown): value is SandlotOptions["executor"] {
|
|
|
177
191
|
typeof (value as { execute: unknown }).execute === "function"
|
|
178
192
|
);
|
|
179
193
|
}
|
|
194
|
+
|
|
195
|
+
function isIframeExecutorOptions(value: unknown): value is IframeExecutorOptions {
|
|
196
|
+
// IframeExecutorOptions has "sandbox" or "container" properties
|
|
197
|
+
// MainThreadExecutorOptions only has "defaultTimeout"
|
|
198
|
+
return (
|
|
199
|
+
typeof value === "object" &&
|
|
200
|
+
value !== null &&
|
|
201
|
+
("sandbox" in value || "container" in value)
|
|
202
|
+
);
|
|
203
|
+
}
|