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
|
@@ -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"}
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/browser/index.js
CHANGED
|
@@ -232,8 +232,9 @@ function parseTsConfig(fs, configPath) {
|
|
|
232
232
|
}
|
|
233
233
|
};
|
|
234
234
|
const parsed = ts.parseJsonConfigFileContent(config, parseHost, "/", undefined, configPath);
|
|
235
|
-
|
|
236
|
-
|
|
235
|
+
const relevantErrors = parsed.errors.filter((e) => e.code !== 18003);
|
|
236
|
+
if (relevantErrors.length > 0) {
|
|
237
|
+
console.warn("[typechecker] tsconfig parse errors:", relevantErrors.map((e) => e.messageText));
|
|
237
238
|
}
|
|
238
239
|
return {
|
|
239
240
|
...parsed.options,
|
|
@@ -707,6 +708,15 @@ class EsbuildWasmBundler {
|
|
|
707
708
|
}
|
|
708
709
|
return state.esbuild;
|
|
709
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
|
+
}
|
|
710
720
|
checkCrossOriginIsolation() {
|
|
711
721
|
if (typeof window === "undefined")
|
|
712
722
|
return;
|
|
@@ -812,23 +822,18 @@ function createBasicExecutor(loadModule, options = {}) {
|
|
|
812
822
|
const formatArgs = (...args) => args.map((v) => typeof v === "object" ? JSON.stringify(v) : String(v)).join(" ");
|
|
813
823
|
const captureLog = (...args) => {
|
|
814
824
|
logs.push(formatArgs(...args));
|
|
815
|
-
originalConsole.log.apply(console, args);
|
|
816
825
|
};
|
|
817
826
|
const captureWarn = (...args) => {
|
|
818
827
|
logs.push(`[warn] ${formatArgs(...args)}`);
|
|
819
|
-
originalConsole.warn.apply(console, args);
|
|
820
828
|
};
|
|
821
829
|
const captureError = (...args) => {
|
|
822
830
|
logs.push(`[error] ${formatArgs(...args)}`);
|
|
823
|
-
originalConsole.error.apply(console, args);
|
|
824
831
|
};
|
|
825
832
|
const captureInfo = (...args) => {
|
|
826
833
|
logs.push(`[info] ${formatArgs(...args)}`);
|
|
827
|
-
originalConsole.info.apply(console, args);
|
|
828
834
|
};
|
|
829
835
|
const captureDebug = (...args) => {
|
|
830
836
|
logs.push(`[debug] ${formatArgs(...args)}`);
|
|
831
|
-
originalConsole.debug.apply(console, args);
|
|
832
837
|
};
|
|
833
838
|
const restoreConsole = () => {
|
|
834
839
|
console.log = originalConsole.log;
|
|
@@ -855,10 +860,16 @@ function createBasicExecutor(loadModule, options = {}) {
|
|
|
855
860
|
}
|
|
856
861
|
};
|
|
857
862
|
if (timeout > 0) {
|
|
863
|
+
let timeoutId;
|
|
858
864
|
const timeoutPromise = new Promise((_, reject) => {
|
|
859
|
-
setTimeout(() => reject(new Error(`Execution timed out after ${timeout}ms`)), timeout);
|
|
865
|
+
timeoutId = setTimeout(() => reject(new Error(`Execution timed out after ${timeout}ms`)), timeout);
|
|
860
866
|
});
|
|
861
|
-
|
|
867
|
+
try {
|
|
868
|
+
await Promise.race([executeExport(), timeoutPromise]);
|
|
869
|
+
} finally {
|
|
870
|
+
if (timeoutId)
|
|
871
|
+
clearTimeout(timeoutId);
|
|
872
|
+
}
|
|
862
873
|
} else {
|
|
863
874
|
await executeExport();
|
|
864
875
|
}
|
|
@@ -884,7 +895,7 @@ function createBasicExecutor(loadModule, options = {}) {
|
|
|
884
895
|
};
|
|
885
896
|
}
|
|
886
897
|
|
|
887
|
-
// src/browser/executor.ts
|
|
898
|
+
// src/browser/main-thread-executor.ts
|
|
888
899
|
async function loadModuleFromBlobUrl(code) {
|
|
889
900
|
const blob = new Blob([code], { type: "application/javascript" });
|
|
890
901
|
const url = URL.createObjectURL(blob);
|
|
@@ -905,6 +916,182 @@ class MainThreadExecutor {
|
|
|
905
916
|
function createMainThreadExecutor(options) {
|
|
906
917
|
return new MainThreadExecutor(options);
|
|
907
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
|
+
}
|
|
908
1095
|
// src/core/shared-module-registry.ts
|
|
909
1096
|
var instanceCounter = 0;
|
|
910
1097
|
function generateInstanceId() {
|
|
@@ -1606,6 +1793,37 @@ ${padding}^`;
|
|
|
1606
1793
|
|
|
1607
1794
|
`);
|
|
1608
1795
|
}
|
|
1796
|
+
function formatBuildFailure(failure, prefix = "Build failed") {
|
|
1797
|
+
switch (failure.phase) {
|
|
1798
|
+
case "entry":
|
|
1799
|
+
return `${prefix}: ${failure.message}
|
|
1800
|
+
`;
|
|
1801
|
+
case "typecheck":
|
|
1802
|
+
if (failure.diagnostics && failure.diagnostics.length > 0) {
|
|
1803
|
+
const errors = failure.diagnostics.filter((d) => d.severity === "error");
|
|
1804
|
+
if (errors.length > 0) {
|
|
1805
|
+
return `${prefix}: Type check errors
|
|
1806
|
+
|
|
1807
|
+
${formatDiagnostics(errors)}
|
|
1808
|
+
`;
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
return `${prefix}: Type check errors
|
|
1812
|
+
`;
|
|
1813
|
+
case "bundle":
|
|
1814
|
+
if (failure.bundleErrors && failure.bundleErrors.length > 0) {
|
|
1815
|
+
return `${prefix}: Bundle errors
|
|
1816
|
+
|
|
1817
|
+
${formatBundleErrors(failure.bundleErrors)}
|
|
1818
|
+
`;
|
|
1819
|
+
}
|
|
1820
|
+
return `${prefix}: Bundle error
|
|
1821
|
+
`;
|
|
1822
|
+
default:
|
|
1823
|
+
return `${prefix}: Unknown error
|
|
1824
|
+
`;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1609
1827
|
// src/commands/index.ts
|
|
1610
1828
|
function createSandlotCommand(sandboxRef) {
|
|
1611
1829
|
return defineCommand("sandlot", async (args, ctx) => {
|
|
@@ -1716,42 +1934,9 @@ Examples:
|
|
|
1716
1934
|
format
|
|
1717
1935
|
});
|
|
1718
1936
|
if (!result.success) {
|
|
1719
|
-
let stderr = `Build failed`;
|
|
1720
|
-
switch (result.phase) {
|
|
1721
|
-
case "entry":
|
|
1722
|
-
stderr = `Build failed: ${result.message}
|
|
1723
|
-
`;
|
|
1724
|
-
break;
|
|
1725
|
-
case "typecheck":
|
|
1726
|
-
if (result.diagnostics) {
|
|
1727
|
-
const errors = result.diagnostics.filter((d) => d.severity === "error");
|
|
1728
|
-
stderr = `Build failed: Type check errors
|
|
1729
|
-
|
|
1730
|
-
${formatDiagnostics(errors)}
|
|
1731
|
-
`;
|
|
1732
|
-
} else {
|
|
1733
|
-
stderr = `Build failed: Type check errors
|
|
1734
|
-
`;
|
|
1735
|
-
}
|
|
1736
|
-
break;
|
|
1737
|
-
case "bundle":
|
|
1738
|
-
if (result.bundleErrors && result.bundleErrors.length > 0) {
|
|
1739
|
-
stderr = `Build failed: Bundle errors
|
|
1740
|
-
|
|
1741
|
-
${formatBundleErrors(result.bundleErrors)}
|
|
1742
|
-
`;
|
|
1743
|
-
} else {
|
|
1744
|
-
stderr = `Build failed: Bundle error
|
|
1745
|
-
`;
|
|
1746
|
-
}
|
|
1747
|
-
break;
|
|
1748
|
-
default:
|
|
1749
|
-
stderr = `Build failed: Unknown error
|
|
1750
|
-
`;
|
|
1751
|
-
}
|
|
1752
1937
|
return {
|
|
1753
1938
|
stdout: "",
|
|
1754
|
-
stderr,
|
|
1939
|
+
stderr: formatBuildFailure(result),
|
|
1755
1940
|
exitCode: 1
|
|
1756
1941
|
};
|
|
1757
1942
|
}
|
|
@@ -1815,18 +2000,20 @@ Examples:
|
|
|
1815
2000
|
const formatted = formatDiagnostics(errors);
|
|
1816
2001
|
return {
|
|
1817
2002
|
stdout: "",
|
|
1818
|
-
stderr: `Type check failed
|
|
2003
|
+
stderr: `Type check failed
|
|
2004
|
+
|
|
1819
2005
|
${formatted}
|
|
1820
2006
|
`,
|
|
1821
2007
|
exitCode: 1
|
|
1822
2008
|
};
|
|
1823
2009
|
}
|
|
1824
2010
|
const warnings = result.diagnostics.filter((d) => d.severity === "warning");
|
|
1825
|
-
let output = `Type check passed
|
|
2011
|
+
let output = `Type check passed
|
|
1826
2012
|
`;
|
|
1827
2013
|
if (warnings.length > 0) {
|
|
1828
2014
|
output += `
|
|
1829
2015
|
Warnings:
|
|
2016
|
+
|
|
1830
2017
|
${formatDiagnostics(warnings)}
|
|
1831
2018
|
`;
|
|
1832
2019
|
}
|
|
@@ -2001,13 +2188,7 @@ Examples:
|
|
|
2001
2188
|
if (!result.success) {
|
|
2002
2189
|
let stderr = "";
|
|
2003
2190
|
if (result.buildFailure) {
|
|
2004
|
-
stderr =
|
|
2005
|
-
if (result.buildFailure.message) {
|
|
2006
|
-
stderr += `
|
|
2007
|
-
${result.buildFailure.message}`;
|
|
2008
|
-
}
|
|
2009
|
-
stderr += `
|
|
2010
|
-
`;
|
|
2191
|
+
stderr = formatBuildFailure(result.buildFailure, "Run failed");
|
|
2011
2192
|
} else {
|
|
2012
2193
|
stderr = `Run failed: ${result.error ?? "Unknown error"}
|
|
2013
2194
|
`;
|
|
@@ -2354,7 +2535,10 @@ async function createSandboxImpl(fs, options, context) {
|
|
|
2354
2535
|
error: buildResult.message ?? `Build failed in ${buildResult.phase} phase`,
|
|
2355
2536
|
buildFailure: {
|
|
2356
2537
|
phase: buildResult.phase,
|
|
2357
|
-
message: buildResult.message
|
|
2538
|
+
message: buildResult.message,
|
|
2539
|
+
diagnostics: buildResult.diagnostics,
|
|
2540
|
+
bundleErrors: buildResult.bundleErrors,
|
|
2541
|
+
bundleWarnings: buildResult.bundleWarnings
|
|
2358
2542
|
}
|
|
2359
2543
|
};
|
|
2360
2544
|
}
|
|
@@ -2452,6 +2636,11 @@ function createSandlot(options) {
|
|
|
2452
2636
|
},
|
|
2453
2637
|
get sharedModules() {
|
|
2454
2638
|
return sharedModuleRegistry;
|
|
2639
|
+
},
|
|
2640
|
+
async dispose() {
|
|
2641
|
+
if (bundler.dispose) {
|
|
2642
|
+
await bundler.dispose();
|
|
2643
|
+
}
|
|
2455
2644
|
}
|
|
2456
2645
|
};
|
|
2457
2646
|
}
|
|
@@ -2649,7 +2838,7 @@ async function createBrowserSandlot(options = {}) {
|
|
|
2649
2838
|
await bundlerInstance.initialize();
|
|
2650
2839
|
const typecheckerInstance = typechecker === false ? undefined : isTypechecker(typechecker) ? typechecker : new Typechecker(typechecker);
|
|
2651
2840
|
const typesResolverInstance = typesResolver === false ? undefined : isTypesResolver(typesResolver) ? typesResolver : new EsmTypesResolver(typesResolver);
|
|
2652
|
-
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);
|
|
2653
2842
|
return createSandlot({
|
|
2654
2843
|
...rest,
|
|
2655
2844
|
bundler: bundlerInstance,
|
|
@@ -2670,6 +2859,9 @@ function isTypesResolver(value) {
|
|
|
2670
2859
|
function isExecutor(value) {
|
|
2671
2860
|
return typeof value === "object" && value !== null && "execute" in value && typeof value.execute === "function";
|
|
2672
2861
|
}
|
|
2862
|
+
function isIframeExecutorOptions(value) {
|
|
2863
|
+
return typeof value === "object" && value !== null && (("sandbox" in value) || ("container" in value));
|
|
2864
|
+
}
|
|
2673
2865
|
|
|
2674
2866
|
// src/browser/index.ts
|
|
2675
2867
|
if (typeof window !== "undefined" && typeof globalThis.process === "undefined") {
|
|
@@ -2685,8 +2877,10 @@ if (typeof window !== "undefined" && typeof globalThis.process === "undefined")
|
|
|
2685
2877
|
export {
|
|
2686
2878
|
createTypechecker,
|
|
2687
2879
|
createMainThreadExecutor,
|
|
2880
|
+
createIframeExecutor,
|
|
2688
2881
|
createBrowserSandlot,
|
|
2689
2882
|
Typechecker,
|
|
2690
2883
|
MainThreadExecutor,
|
|
2884
|
+
IframeExecutor,
|
|
2691
2885
|
EsbuildWasmBundler
|
|
2692
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"}
|
package/dist/browser/preset.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/commands/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { SandboxRef } from "./types";
|
|
12
12
|
export type { SandboxRef } from "./types";
|
|
13
|
-
export { formatSize, formatDiagnostics, formatBundleErrors } from "./types";
|
|
13
|
+
export { formatSize, formatDiagnostics, formatBundleErrors, formatBuildFailure, } from "./types";
|
|
14
14
|
/**
|
|
15
15
|
* Create the main `sandlot` command with all subcommands.
|
|
16
16
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAEjB;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,UAAU,uCAuC1D;AAubD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,UAAU,yCAE3D"}
|