sandlot 0.2.1 → 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.
Files changed (38) hide show
  1. package/dist/browser/bundler.d.ts +8 -0
  2. package/dist/browser/bundler.d.ts.map +1 -1
  3. package/dist/browser/iframe-executor.d.ts +82 -0
  4. package/dist/browser/iframe-executor.d.ts.map +1 -0
  5. package/dist/browser/index.d.ts +4 -2
  6. package/dist/browser/index.d.ts.map +1 -1
  7. package/dist/browser/index.js +205 -9
  8. package/dist/browser/main-thread-executor.d.ts +46 -0
  9. package/dist/browser/main-thread-executor.d.ts.map +1 -0
  10. package/dist/browser/preset.d.ts +7 -2
  11. package/dist/browser/preset.d.ts.map +1 -1
  12. package/dist/core/executor.d.ts.map +1 -1
  13. package/dist/core/sandlot.d.ts.map +1 -1
  14. package/dist/index.js +5 -0
  15. package/dist/node/bundler.d.ts +5 -0
  16. package/dist/node/bundler.d.ts.map +1 -1
  17. package/dist/node/index.d.ts +2 -0
  18. package/dist/node/index.d.ts.map +1 -1
  19. package/dist/node/index.js +174 -8
  20. package/dist/node/preset.d.ts +16 -1
  21. package/dist/node/preset.d.ts.map +1 -1
  22. package/dist/node/wasm-bundler.d.ts +86 -0
  23. package/dist/node/wasm-bundler.d.ts.map +1 -0
  24. package/dist/types.d.ts +25 -0
  25. package/dist/types.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/browser/bundler.ts +17 -0
  28. package/src/browser/iframe-executor.ts +320 -0
  29. package/src/browser/index.ts +9 -2
  30. package/src/browser/preset.ts +30 -6
  31. package/src/core/executor.ts +8 -7
  32. package/src/core/sandlot.ts +7 -0
  33. package/src/node/bundler.ts +11 -0
  34. package/src/node/index.ts +10 -0
  35. package/src/node/preset.ts +59 -5
  36. package/src/node/wasm-bundler.ts +299 -0
  37. package/src/types.ts +27 -0
  38. /package/src/browser/{executor.ts → main-thread-executor.ts} +0 -0
@@ -62,6 +62,14 @@ export declare class EsbuildWasmBundler implements IBundler {
62
62
  * Get the initialized esbuild instance.
63
63
  */
64
64
  private getEsbuild;
65
+ /**
66
+ * Dispose of the esbuild WASM service.
67
+ * This stops the esbuild service and allows the process to exit.
68
+ *
69
+ * Note: Since esbuild-wasm uses a global singleton, this affects all
70
+ * instances. After dispose(), you'll need to create a new bundler.
71
+ */
72
+ dispose(): Promise<void>;
65
73
  private checkCrossOriginIsolation;
66
74
  bundle(options: BundleOptions): Promise<BundleResult>;
67
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/browser/bundler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,YAAY,EAGb,MAAM,UAAU,CAAC;AAuClB,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,OAAO,CAAC,OAAO,CAA4B;gBAE/B,OAAO,GAAE,yBAA8B;IAWnD;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBnB,YAAY;IA8B1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,yBAAyB;IAc3B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CAoG5D"}
1
+ {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/browser/bundler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,YAAY,EAGb,MAAM,UAAU,CAAC;AAuClB,MAAM,WAAW,yBAAyB;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,OAAO,CAAC,OAAO,CAA4B;gBAE/B,OAAO,GAAE,yBAA8B;IAWnD;;;;;;OAMG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBnB,YAAY;IA8B1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAU9B,OAAO,CAAC,yBAAyB;IAc3B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CAoG5D"}
@@ -0,0 +1,82 @@
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
+ import type { IExecutor, ExecuteOptions, ExecuteResult } from "../types";
14
+ /**
15
+ * Options for creating an IframeExecutor.
16
+ */
17
+ export interface IframeExecutorOptions {
18
+ /**
19
+ * Sandbox attributes for the iframe.
20
+ * @default ["allow-scripts"]
21
+ *
22
+ * Common options:
23
+ * - "allow-scripts": Required for code execution
24
+ * - "allow-same-origin": Enables localStorage, cookies (reduces isolation)
25
+ * - "allow-modals": Enables alert/confirm/prompt
26
+ *
27
+ * Security note: "allow-scripts" + "allow-same-origin" together allows
28
+ * the iframe code to potentially remove the sandbox via script.
29
+ */
30
+ sandbox?: string[];
31
+ /**
32
+ * Default timeout in milliseconds.
33
+ * @default 30000
34
+ */
35
+ defaultTimeout?: number;
36
+ /**
37
+ * Container element for iframes.
38
+ * Iframes are created hidden (display: none).
39
+ * @default document.body
40
+ */
41
+ container?: HTMLElement;
42
+ }
43
+ /**
44
+ * Executor that runs code in a sandboxed iframe.
45
+ *
46
+ * Each execute() call creates a fresh iframe, runs the code, and destroys
47
+ * the iframe. This provides clean isolation between executions.
48
+ *
49
+ * Note: This executor does NOT support shared modules. The iframe runs
50
+ * in complete isolation. Use MainThreadExecutor if you need shared modules.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * // Default: strict sandboxing (allow-scripts only)
55
+ * const executor = createIframeExecutor();
56
+ *
57
+ * // With additional permissions
58
+ * const executor = createIframeExecutor({
59
+ * sandbox: ["allow-scripts", "allow-same-origin"],
60
+ * });
61
+ *
62
+ * const result = await executor.execute(bundledCode, {
63
+ * entryExport: 'main',
64
+ * context: { args: ['--verbose'] },
65
+ * timeout: 5000,
66
+ * });
67
+ * console.log(result.logs);
68
+ * ```
69
+ */
70
+ export declare class IframeExecutor implements IExecutor {
71
+ private options;
72
+ constructor(options?: IframeExecutorOptions);
73
+ execute(code: string, options?: ExecuteOptions): Promise<ExecuteResult>;
74
+ }
75
+ /**
76
+ * Create an iframe executor.
77
+ *
78
+ * @param options - Executor options
79
+ * @returns A new IframeExecutor instance
80
+ */
81
+ export declare function createIframeExecutor(options?: IframeExecutorOptions): IframeExecutor;
82
+ //# sourceMappingURL=iframe-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iframe-executor.d.ts","sourceRoot":"","sources":["../../src/browser/iframe-executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB;AAmID;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,cAAe,YAAW,SAAS;IAC9C,OAAO,CAAC,OAAO,CAEb;gBAEU,OAAO,GAAE,qBAA0B;IAQzC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;CA2FlF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,cAAc,CAEhB"}
@@ -2,8 +2,10 @@ export { Typechecker, createTypechecker } from "../core/typechecker";
2
2
  export type { TypecheckerOptions } from "../core/typechecker";
3
3
  export { EsbuildWasmBundler } from "./bundler";
4
4
  export type { EsbuildWasmBundlerOptions } from "./bundler";
5
- export { MainThreadExecutor, createMainThreadExecutor } from "./executor";
6
- export type { MainThreadExecutorOptions } from "./executor";
5
+ export { MainThreadExecutor, createMainThreadExecutor } from "./main-thread-executor";
6
+ export type { MainThreadExecutorOptions } from "./main-thread-executor";
7
+ export { IframeExecutor, createIframeExecutor } from "./iframe-executor";
8
+ export type { IframeExecutorOptions } from "./iframe-executor";
7
9
  export { createBrowserSandlot } from "./preset";
8
10
  export type { CreateBrowserSandlotOptions } from "./preset";
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACrE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAM9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,YAAY,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAM3D,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAC1E,YAAY,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAM5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChD,YAAY,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACrE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAM9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC/C,YAAY,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAM3D,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACtF,YAAY,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAMxE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzE,YAAY,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAM/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChD,YAAY,EAAE,2BAA2B,EAAE,MAAM,UAAU,CAAC"}
@@ -708,6 +708,15 @@ class EsbuildWasmBundler {
708
708
  }
709
709
  return state.esbuild;
710
710
  }
711
+ async dispose() {
712
+ const state = getGlobalState();
713
+ if (state.esbuild) {
714
+ await state.esbuild.stop();
715
+ state.esbuild = null;
716
+ state.initialized = false;
717
+ state.initPromise = null;
718
+ }
719
+ }
711
720
  checkCrossOriginIsolation() {
712
721
  if (typeof window === "undefined")
713
722
  return;
@@ -813,23 +822,18 @@ function createBasicExecutor(loadModule, options = {}) {
813
822
  const formatArgs = (...args) => args.map((v) => typeof v === "object" ? JSON.stringify(v) : String(v)).join(" ");
814
823
  const captureLog = (...args) => {
815
824
  logs.push(formatArgs(...args));
816
- originalConsole.log.apply(console, args);
817
825
  };
818
826
  const captureWarn = (...args) => {
819
827
  logs.push(`[warn] ${formatArgs(...args)}`);
820
- originalConsole.warn.apply(console, args);
821
828
  };
822
829
  const captureError = (...args) => {
823
830
  logs.push(`[error] ${formatArgs(...args)}`);
824
- originalConsole.error.apply(console, args);
825
831
  };
826
832
  const captureInfo = (...args) => {
827
833
  logs.push(`[info] ${formatArgs(...args)}`);
828
- originalConsole.info.apply(console, args);
829
834
  };
830
835
  const captureDebug = (...args) => {
831
836
  logs.push(`[debug] ${formatArgs(...args)}`);
832
- originalConsole.debug.apply(console, args);
833
837
  };
834
838
  const restoreConsole = () => {
835
839
  console.log = originalConsole.log;
@@ -856,10 +860,16 @@ function createBasicExecutor(loadModule, options = {}) {
856
860
  }
857
861
  };
858
862
  if (timeout > 0) {
863
+ let timeoutId;
859
864
  const timeoutPromise = new Promise((_, reject) => {
860
- setTimeout(() => reject(new Error(`Execution timed out after ${timeout}ms`)), timeout);
865
+ timeoutId = setTimeout(() => reject(new Error(`Execution timed out after ${timeout}ms`)), timeout);
861
866
  });
862
- await Promise.race([executeExport(), timeoutPromise]);
867
+ try {
868
+ await Promise.race([executeExport(), timeoutPromise]);
869
+ } finally {
870
+ if (timeoutId)
871
+ clearTimeout(timeoutId);
872
+ }
863
873
  } else {
864
874
  await executeExport();
865
875
  }
@@ -885,7 +895,7 @@ function createBasicExecutor(loadModule, options = {}) {
885
895
  };
886
896
  }
887
897
 
888
- // src/browser/executor.ts
898
+ // src/browser/main-thread-executor.ts
889
899
  async function loadModuleFromBlobUrl(code) {
890
900
  const blob = new Blob([code], { type: "application/javascript" });
891
901
  const url = URL.createObjectURL(blob);
@@ -906,6 +916,182 @@ class MainThreadExecutor {
906
916
  function createMainThreadExecutor(options) {
907
917
  return new MainThreadExecutor(options);
908
918
  }
919
+ // src/browser/iframe-executor.ts
920
+ var BOOTSTRAP_HTML = `<!DOCTYPE html>
921
+ <html>
922
+ <head>
923
+ <meta charset="utf-8">
924
+ </head>
925
+ <body>
926
+ <script type="module">
927
+ // Capture console methods and forward to parent
928
+ function formatArgs(...args) {
929
+ return args
930
+ .map(v => typeof v === "object" ? JSON.stringify(v) : String(v))
931
+ .join(" ");
932
+ }
933
+
934
+ function createLogger(level) {
935
+ return (...args) => {
936
+ parent.postMessage({ type: "log", level, args: formatArgs(...args) }, "*");
937
+ };
938
+ }
939
+
940
+ console.log = createLogger("log");
941
+ console.warn = createLogger("warn");
942
+ console.error = createLogger("error");
943
+ console.info = createLogger("info");
944
+ console.debug = createLogger("debug");
945
+
946
+ // Handle unhandled promise rejections
947
+ window.addEventListener("unhandledrejection", (event) => {
948
+ const message = event.reason instanceof Error
949
+ ? event.reason.message
950
+ : String(event.reason);
951
+ parent.postMessage({
952
+ type: "result",
953
+ success: false,
954
+ error: "Unhandled promise rejection: " + message
955
+ }, "*");
956
+ });
957
+
958
+ // Handle uncaught errors
959
+ window.addEventListener("error", (event) => {
960
+ parent.postMessage({
961
+ type: "result",
962
+ success: false,
963
+ error: event.message || "Unknown error"
964
+ }, "*");
965
+ });
966
+
967
+ // Listen for execute messages from parent
968
+ window.addEventListener("message", async (event) => {
969
+ if (event.data?.type !== "execute") return;
970
+
971
+ const { code, entryExport, context } = event.data;
972
+
973
+ try {
974
+ // Create Blob URL and import as ESM module
975
+ const blob = new Blob([code], { type: "application/javascript" });
976
+ const url = URL.createObjectURL(blob);
977
+
978
+ let module;
979
+ try {
980
+ module = await import(url);
981
+ } finally {
982
+ URL.revokeObjectURL(url);
983
+ }
984
+
985
+ // Execute the appropriate export
986
+ let returnValue;
987
+
988
+ if (entryExport === "main" && typeof module.main === "function") {
989
+ returnValue = await module.main(context);
990
+ } else if (entryExport === "default" && typeof module.default === "function") {
991
+ returnValue = await module.default();
992
+ } else if (entryExport === "default" && module.default !== undefined) {
993
+ returnValue = module.default;
994
+ }
995
+ // If neither export exists, top-level code already ran on import
996
+
997
+ parent.postMessage({ type: "result", success: true, returnValue }, "*");
998
+ } catch (err) {
999
+ const message = err instanceof Error ? err.message : String(err);
1000
+ parent.postMessage({ type: "result", success: false, error: message }, "*");
1001
+ }
1002
+ });
1003
+
1004
+ // Signal that we're ready to receive code
1005
+ parent.postMessage({ type: "ready" }, "*");
1006
+ </script>
1007
+ </body>
1008
+ </html>`;
1009
+
1010
+ class IframeExecutor {
1011
+ options;
1012
+ constructor(options = {}) {
1013
+ this.options = {
1014
+ sandbox: options.sandbox ?? ["allow-scripts"],
1015
+ defaultTimeout: options.defaultTimeout ?? 30000,
1016
+ container: options.container
1017
+ };
1018
+ }
1019
+ async execute(code, options = {}) {
1020
+ const {
1021
+ entryExport = "main",
1022
+ context = {},
1023
+ timeout = this.options.defaultTimeout
1024
+ } = options;
1025
+ const startTime = performance.now();
1026
+ const logs = [];
1027
+ const container = this.options.container ?? document.body;
1028
+ const iframe = document.createElement("iframe");
1029
+ iframe.style.display = "none";
1030
+ iframe.sandbox.add(...this.options.sandbox);
1031
+ iframe.srcdoc = BOOTSTRAP_HTML;
1032
+ let resolved = false;
1033
+ return new Promise((resolve) => {
1034
+ const cleanup = () => {
1035
+ if (iframe.parentNode) {
1036
+ iframe.parentNode.removeChild(iframe);
1037
+ }
1038
+ window.removeEventListener("message", handleMessage);
1039
+ if (timeoutId)
1040
+ clearTimeout(timeoutId);
1041
+ };
1042
+ const finish = (result) => {
1043
+ if (resolved)
1044
+ return;
1045
+ resolved = true;
1046
+ cleanup();
1047
+ resolve(result);
1048
+ };
1049
+ const handleMessage = (event) => {
1050
+ if (event.source !== iframe.contentWindow)
1051
+ return;
1052
+ const data = event.data;
1053
+ if (data.type === "log") {
1054
+ const prefix = data.level === "log" ? "" : `[${data.level}] `;
1055
+ logs.push(prefix + data.args);
1056
+ } else if (data.type === "result") {
1057
+ const executionTimeMs = performance.now() - startTime;
1058
+ finish({
1059
+ success: data.success,
1060
+ logs,
1061
+ returnValue: data.returnValue,
1062
+ error: data.error,
1063
+ executionTimeMs
1064
+ });
1065
+ } else if (data.type === "ready") {
1066
+ const message = {
1067
+ type: "execute",
1068
+ code,
1069
+ entryExport,
1070
+ context
1071
+ };
1072
+ iframe.contentWindow?.postMessage(message, "*");
1073
+ }
1074
+ };
1075
+ let timeoutId;
1076
+ if (timeout > 0) {
1077
+ timeoutId = setTimeout(() => {
1078
+ const executionTimeMs = performance.now() - startTime;
1079
+ finish({
1080
+ success: false,
1081
+ logs,
1082
+ error: `Execution timed out after ${timeout}ms`,
1083
+ executionTimeMs
1084
+ });
1085
+ }, timeout);
1086
+ }
1087
+ window.addEventListener("message", handleMessage);
1088
+ container.appendChild(iframe);
1089
+ });
1090
+ }
1091
+ }
1092
+ function createIframeExecutor(options) {
1093
+ return new IframeExecutor(options);
1094
+ }
909
1095
  // src/core/shared-module-registry.ts
910
1096
  var instanceCounter = 0;
911
1097
  function generateInstanceId() {
@@ -2450,6 +2636,11 @@ function createSandlot(options) {
2450
2636
  },
2451
2637
  get sharedModules() {
2452
2638
  return sharedModuleRegistry;
2639
+ },
2640
+ async dispose() {
2641
+ if (bundler.dispose) {
2642
+ await bundler.dispose();
2643
+ }
2453
2644
  }
2454
2645
  };
2455
2646
  }
@@ -2647,7 +2838,7 @@ async function createBrowserSandlot(options = {}) {
2647
2838
  await bundlerInstance.initialize();
2648
2839
  const typecheckerInstance = typechecker === false ? undefined : isTypechecker(typechecker) ? typechecker : new Typechecker(typechecker);
2649
2840
  const typesResolverInstance = typesResolver === false ? undefined : isTypesResolver(typesResolver) ? typesResolver : new EsmTypesResolver(typesResolver);
2650
- const executorInstance = executor === false ? undefined : isExecutor(executor) ? executor : new MainThreadExecutor(executor);
2841
+ const executorInstance = executor === false ? undefined : executor === "iframe" ? new IframeExecutor : isExecutor(executor) ? executor : isIframeExecutorOptions(executor) ? new IframeExecutor(executor) : new MainThreadExecutor(executor);
2651
2842
  return createSandlot({
2652
2843
  ...rest,
2653
2844
  bundler: bundlerInstance,
@@ -2668,6 +2859,9 @@ function isTypesResolver(value) {
2668
2859
  function isExecutor(value) {
2669
2860
  return typeof value === "object" && value !== null && "execute" in value && typeof value.execute === "function";
2670
2861
  }
2862
+ function isIframeExecutorOptions(value) {
2863
+ return typeof value === "object" && value !== null && (("sandbox" in value) || ("container" in value));
2864
+ }
2671
2865
 
2672
2866
  // src/browser/index.ts
2673
2867
  if (typeof window !== "undefined" && typeof globalThis.process === "undefined") {
@@ -2683,8 +2877,10 @@ if (typeof window !== "undefined" && typeof globalThis.process === "undefined")
2683
2877
  export {
2684
2878
  createTypechecker,
2685
2879
  createMainThreadExecutor,
2880
+ createIframeExecutor,
2686
2881
  createBrowserSandlot,
2687
2882
  Typechecker,
2688
2883
  MainThreadExecutor,
2884
+ IframeExecutor,
2689
2885
  EsbuildWasmBundler
2690
2886
  };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Main thread executor for browser environments.
3
+ *
4
+ * This executor runs code directly in the main thread. It provides no
5
+ * isolation from the host environment - use only for trusted code.
6
+ *
7
+ * For untrusted code, consider using a Worker or iframe-based executor
8
+ * that provides proper sandboxing.
9
+ */
10
+ import type { IExecutor } from "../types";
11
+ import { type BasicExecutorOptions } from "../core/executor";
12
+ /**
13
+ * Options for creating a MainThreadExecutor.
14
+ */
15
+ export type MainThreadExecutorOptions = BasicExecutorOptions;
16
+ /**
17
+ * Executor that runs code in the main browser thread.
18
+ *
19
+ * WARNING: This executor provides NO isolation. The executed code has
20
+ * full access to the page's DOM, global variables, and network.
21
+ * Only use for trusted code (e.g., code you're developing).
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const executor = createMainThreadExecutor();
26
+ * const result = await executor.execute(bundledCode, {
27
+ * entryExport: 'main',
28
+ * context: { args: ['--verbose'] },
29
+ * timeout: 5000,
30
+ * });
31
+ * console.log(result.logs);
32
+ * ```
33
+ */
34
+ export declare class MainThreadExecutor implements IExecutor {
35
+ private executor;
36
+ constructor(options?: MainThreadExecutorOptions);
37
+ execute: IExecutor["execute"];
38
+ }
39
+ /**
40
+ * Create a main thread executor.
41
+ *
42
+ * @param options - Executor options
43
+ * @returns A new MainThreadExecutor instance
44
+ */
45
+ export declare function createMainThreadExecutor(options?: MainThreadExecutorOptions): MainThreadExecutor;
46
+ //# sourceMappingURL=main-thread-executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main-thread-executor.d.ts","sourceRoot":"","sources":["../../src/browser/main-thread-executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAuB,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAElF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,CAAC;AAgB7D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IAClD,OAAO,CAAC,QAAQ,CAAY;gBAEhB,OAAO,GAAE,yBAA8B;IAInD,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,CAA+C;CAC7E;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,CAAC,EAAE,yBAAyB,GAClC,kBAAkB,CAEpB"}
@@ -2,7 +2,8 @@ import { type EsmTypesResolverOptions } from "../core/esm-types-resolver";
2
2
  import type { Sandlot, SandlotOptions } from "../types";
3
3
  import { type EsbuildWasmBundlerOptions } from "./bundler";
4
4
  import { type TypecheckerOptions } from "../core/typechecker";
5
- import { type MainThreadExecutorOptions } from "./executor";
5
+ import { type MainThreadExecutorOptions } from "./main-thread-executor";
6
+ import { type IframeExecutorOptions } from "./iframe-executor";
6
7
  export interface CreateBrowserSandlotOptions extends Omit<SandlotOptions, "bundler" | "typechecker" | "typesResolver" | "executor"> {
7
8
  /**
8
9
  * Custom bundler options, or a pre-configured bundler instance.
@@ -21,9 +22,13 @@ export interface CreateBrowserSandlotOptions extends Omit<SandlotOptions, "bundl
21
22
  /**
22
23
  * Custom executor options, or a pre-configured executor instance.
23
24
  * Set to `false` to disable execution (sandbox.run() will throw).
25
+ * Set to `"iframe"` to use IframeExecutor with default options.
24
26
  * Defaults to MainThreadExecutor.
27
+ *
28
+ * Note: IframeExecutor does NOT support shared modules. Use MainThreadExecutor
29
+ * (the default) if you need shared modules like React.
25
30
  */
26
- executor?: MainThreadExecutorOptions | SandlotOptions["executor"] | false;
31
+ executor?: MainThreadExecutorOptions | IframeExecutorOptions | SandlotOptions["executor"] | "iframe" | false;
27
32
  }
28
33
  /**
29
34
  * Create a Sandlot instance pre-configured for browser environments.
@@ -1 +1 @@
1
- {"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../src/browser/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,EAAsB,KAAK,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAC/E,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,2BACf,SAAQ,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,UAAU,CAAC;IACtF;;OAEG;IACH,OAAO,CAAC,EAAE,yBAAyB,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAEhE;;;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,yBAAyB,GACzB,cAAc,CAAC,UAAU,CAAC,GAC1B,KAAK,CAAC;CACT;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,OAAO,CAAC,CAgDlB"}
1
+ {"version":3,"file":"preset.d.ts","sourceRoot":"","sources":["../../src/browser/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,EAAsB,KAAK,yBAAyB,EAAE,MAAM,WAAW,CAAC;AAC/E,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,2BACf,SAAQ,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,aAAa,GAAG,eAAe,GAAG,UAAU,CAAC;IACtF;;OAEG;IACH,OAAO,CAAC,EAAE,yBAAyB,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAEhE;;;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;;;;;;;;OAQG;IACH,QAAQ,CAAC,EACP,yBAAyB,GACzB,qBAAqB,GACrB,cAAc,CAAC,UAAU,CAAC,GAC1B,QAAQ,GACR,KAAK,CAAC;CACT;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,OAAO,CAAC,CAoDlB"}
@@ -1 +1 @@
1
- {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/core/executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiC,MAAM,UAAU,CAAC;AAEzE;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,YAAY,EACxB,OAAO,GAAE,oBAAyB,GACjC,SAAS,CAwHX"}
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/core/executor.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiC,MAAM,UAAU,CAAC;AAEzE;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,YAAY,EACxB,OAAO,GAAE,oBAAyB,GACjC,SAAS,CAyHX"}
@@ -1 +1 @@
1
- {"version":3,"file":"sandlot.d.ts","sourceRoot":"","sources":["../../src/core/sandlot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EAIf,MAAM,UAAU,CAAC;AAKlB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAsC9D"}
1
+ {"version":3,"file":"sandlot.d.ts","sourceRoot":"","sources":["../../src/core/sandlot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EAIf,MAAM,UAAU,CAAC;AAKlB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CA6C9D"}
package/dist/index.js CHANGED
@@ -1550,6 +1550,11 @@ function createSandlot(options) {
1550
1550
  },
1551
1551
  get sharedModules() {
1552
1552
  return sharedModuleRegistry;
1553
+ },
1554
+ async dispose() {
1555
+ if (bundler.dispose) {
1556
+ await bundler.dispose();
1557
+ }
1553
1558
  }
1554
1559
  };
1555
1560
  }
@@ -39,6 +39,11 @@ export declare class EsbuildNativeBundler implements IBundler {
39
39
  */
40
40
  initialize(): Promise<void>;
41
41
  private getEsbuild;
42
+ /**
43
+ * Dispose of the esbuild service.
44
+ * This stops the esbuild child process and allows the Node.js process to exit.
45
+ */
46
+ dispose(): Promise<void>;
42
47
  bundle(options: BundleOptions): Promise<BundleResult>;
43
48
  }
44
49
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/node/bundler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,YAAY,EAGb,MAAM,UAAU,CAAC;AAOlB,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,oBAAqB,YAAW,QAAQ;IACnD,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,OAAO,CAAoC;gBAEvC,OAAO,GAAE,2BAAgC;IAOrD;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAUjC,OAAO,CAAC,UAAU;IAOZ,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CA0G5D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,2BAA2B,GACpC,oBAAoB,CAEtB"}
1
+ {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../../src/node/bundler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EACb,YAAY,EAGb,MAAM,UAAU,CAAC;AAOlB,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,oBAAqB,YAAW,QAAQ;IACnD,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,OAAO,CAAoC;gBAEvC,OAAO,GAAE,2BAAgC;IAOrD;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAUjC,OAAO,CAAC,UAAU;IAOlB;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOxB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;CA0G5D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,CAAC,EAAE,2BAA2B,GACpC,oBAAoB,CAEtB"}
@@ -1,5 +1,7 @@
1
1
  export { EsbuildNativeBundler, createEsbuildNativeBundler } from "./bundler";
2
2
  export type { EsbuildNativeBundlerOptions } from "./bundler";
3
+ export { EsbuildWasmNodeBundler, createEsbuildWasmNodeBundler, } from "./wasm-bundler";
4
+ export type { EsbuildWasmNodeBundlerOptions } from "./wasm-bundler";
3
5
  export { Typechecker, createTypechecker } from "../core/typechecker";
4
6
  export type { TypecheckerOptions } from "../core/typechecker";
5
7
  export { NodeExecutor, createNodeExecutor } from "./executor";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AAM7D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACrE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAM9D,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC9D,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAMtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAC7E,YAAY,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AAM7D,OAAO,EACL,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AAMpE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACrE,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAM9D,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC9D,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAMtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,wBAAwB,EAAE,MAAM,UAAU,CAAC"}