sandlot 0.2.1 → 0.2.3
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 +339 -76
- 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/core/bundler-utils.d.ts +43 -1
- package/dist/core/bundler-utils.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/index.js +63 -0
- 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 +243 -75
- 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 +25 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/browser/bundler.ts +23 -106
- package/src/browser/iframe-executor.ts +320 -0
- package/src/browser/index.ts +9 -2
- package/src/browser/preset.ts +30 -6
- package/src/core/bundler-utils.ts +148 -0
- package/src/core/executor.ts +8 -7
- package/src/core/sandbox.ts +82 -0
- package/src/core/sandlot.ts +7 -0
- package/src/node/bundler.ts +17 -110
- package/src/node/index.ts +10 -0
- package/src/node/preset.ts +59 -5
- package/src/node/wasm-bundler.ts +195 -0
- package/src/types.ts +27 -0
- /package/src/browser/{executor.ts → main-thread-executor.ts} +0 -0
|
@@ -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,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAgCtE,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;CAY5D;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;
|
|
@@ -524,5 +529,25 @@ export interface Sandlot {
|
|
|
524
529
|
* The shared module registry (if shared modules were provided)
|
|
525
530
|
*/
|
|
526
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>;
|
|
527
552
|
}
|
|
528
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
|
@@ -6,18 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type * as EsbuildTypes from "esbuild-wasm";
|
|
9
|
-
import type {
|
|
10
|
-
|
|
11
|
-
BundleOptions,
|
|
12
|
-
BundleResult,
|
|
13
|
-
BundleWarning,
|
|
14
|
-
BundleError,
|
|
15
|
-
} from "../types";
|
|
16
|
-
import {
|
|
17
|
-
createVfsPlugin,
|
|
18
|
-
isEsbuildBuildFailure,
|
|
19
|
-
convertEsbuildMessage,
|
|
20
|
-
} from "../core/bundler-utils";
|
|
9
|
+
import type { IBundler, BundleOptions, BundleResult } from "../types";
|
|
10
|
+
import { executeBundleWithEsbuild } from "../core/bundler-utils";
|
|
21
11
|
|
|
22
12
|
/**
|
|
23
13
|
* esbuild-wasm version - should match what's in package.json
|
|
@@ -177,6 +167,23 @@ export class EsbuildWasmBundler implements IBundler {
|
|
|
177
167
|
return state.esbuild;
|
|
178
168
|
}
|
|
179
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Dispose of the esbuild WASM service.
|
|
172
|
+
* This stops the esbuild service and allows the process to exit.
|
|
173
|
+
*
|
|
174
|
+
* Note: Since esbuild-wasm uses a global singleton, this affects all
|
|
175
|
+
* instances. After dispose(), you'll need to create a new bundler.
|
|
176
|
+
*/
|
|
177
|
+
async dispose(): Promise<void> {
|
|
178
|
+
const state = getGlobalState();
|
|
179
|
+
if (state.esbuild) {
|
|
180
|
+
await state.esbuild.stop();
|
|
181
|
+
state.esbuild = null;
|
|
182
|
+
state.initialized = false;
|
|
183
|
+
state.initPromise = null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
180
187
|
private checkCrossOriginIsolation(): void {
|
|
181
188
|
if (typeof window === "undefined") return;
|
|
182
189
|
|
|
@@ -194,101 +201,11 @@ export class EsbuildWasmBundler implements IBundler {
|
|
|
194
201
|
async bundle(options: BundleOptions): Promise<BundleResult> {
|
|
195
202
|
await this.initialize();
|
|
196
203
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
fs,
|
|
201
|
-
entryPoint,
|
|
202
|
-
installedPackages = {},
|
|
203
|
-
sharedModules = [],
|
|
204
|
-
sharedModuleRegistry,
|
|
205
|
-
external = [],
|
|
206
|
-
format = "esm",
|
|
207
|
-
minify = false,
|
|
208
|
-
sourcemap = false,
|
|
209
|
-
target = ["es2020"],
|
|
210
|
-
} = options;
|
|
211
|
-
|
|
212
|
-
// Normalize entry point to absolute path
|
|
213
|
-
const normalizedEntry = entryPoint.startsWith("/")
|
|
214
|
-
? entryPoint
|
|
215
|
-
: `/${entryPoint}`;
|
|
216
|
-
|
|
217
|
-
// Verify entry point exists
|
|
218
|
-
if (!fs.exists(normalizedEntry)) {
|
|
219
|
-
return {
|
|
220
|
-
success: false,
|
|
221
|
-
errors: [{ text: `Entry point not found: ${normalizedEntry}` }],
|
|
222
|
-
warnings: [],
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Track files included in the bundle
|
|
227
|
-
const includedFiles = new Set<string>();
|
|
228
|
-
|
|
229
|
-
// Create the VFS plugin
|
|
230
|
-
const plugin = createVfsPlugin({
|
|
231
|
-
fs,
|
|
232
|
-
entryPoint: normalizedEntry,
|
|
233
|
-
installedPackages,
|
|
234
|
-
sharedModules: new Set(sharedModules),
|
|
235
|
-
sharedModuleRegistry: sharedModuleRegistry ?? null,
|
|
204
|
+
return executeBundleWithEsbuild({
|
|
205
|
+
esbuild: this.getEsbuild(),
|
|
206
|
+
bundleOptions: options,
|
|
236
207
|
cdnBaseUrl: this.options.cdnBaseUrl!,
|
|
237
|
-
|
|
208
|
+
bundleCdnImports: false, // Browser can fetch CDN imports at runtime
|
|
238
209
|
});
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
// Run esbuild
|
|
242
|
-
const result = await esbuild.build({
|
|
243
|
-
entryPoints: [normalizedEntry],
|
|
244
|
-
bundle: true,
|
|
245
|
-
write: false,
|
|
246
|
-
format,
|
|
247
|
-
minify,
|
|
248
|
-
sourcemap: sourcemap ? "inline" : false,
|
|
249
|
-
target,
|
|
250
|
-
external,
|
|
251
|
-
// Cast to esbuild's Plugin type since our minimal interface is compatible
|
|
252
|
-
plugins: [plugin as EsbuildTypes.Plugin],
|
|
253
|
-
jsx: "automatic",
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
const code = result.outputFiles?.[0]?.text ?? "";
|
|
257
|
-
|
|
258
|
-
// Convert esbuild warnings to our format
|
|
259
|
-
const warnings: BundleWarning[] = result.warnings.map((w) =>
|
|
260
|
-
convertEsbuildMessage(w)
|
|
261
|
-
);
|
|
262
|
-
|
|
263
|
-
return {
|
|
264
|
-
success: true,
|
|
265
|
-
code,
|
|
266
|
-
warnings,
|
|
267
|
-
includedFiles: Array.from(includedFiles),
|
|
268
|
-
};
|
|
269
|
-
} catch (err) {
|
|
270
|
-
// esbuild throws BuildFailure with .errors array
|
|
271
|
-
if (isEsbuildBuildFailure(err)) {
|
|
272
|
-
const errors: BundleError[] = err.errors.map((e) =>
|
|
273
|
-
convertEsbuildMessage(e)
|
|
274
|
-
);
|
|
275
|
-
const warnings: BundleWarning[] = err.warnings.map((w) =>
|
|
276
|
-
convertEsbuildMessage(w)
|
|
277
|
-
);
|
|
278
|
-
return {
|
|
279
|
-
success: false,
|
|
280
|
-
errors,
|
|
281
|
-
warnings,
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Unknown error - wrap it
|
|
286
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
287
|
-
return {
|
|
288
|
-
success: false,
|
|
289
|
-
errors: [{ text: message }],
|
|
290
|
-
warnings: [],
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
210
|
}
|
|
294
211
|
}
|
|
@@ -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
|