sandlot 0.1.2 → 0.1.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.
Files changed (54) hide show
  1. package/README.md +138 -408
  2. package/dist/build-emitter.d.ts +31 -13
  3. package/dist/build-emitter.d.ts.map +1 -1
  4. package/dist/builder.d.ts +370 -0
  5. package/dist/builder.d.ts.map +1 -0
  6. package/dist/bundler.d.ts +1 -1
  7. package/dist/bundler.d.ts.map +1 -1
  8. package/dist/commands/compile.d.ts +13 -0
  9. package/dist/commands/compile.d.ts.map +1 -0
  10. package/dist/commands/index.d.ts +17 -0
  11. package/dist/commands/index.d.ts.map +1 -0
  12. package/dist/commands/packages.d.ts +17 -0
  13. package/dist/commands/packages.d.ts.map +1 -0
  14. package/dist/commands/run.d.ts +40 -0
  15. package/dist/commands/run.d.ts.map +1 -0
  16. package/dist/commands/types.d.ts +141 -0
  17. package/dist/commands/types.d.ts.map +1 -0
  18. package/dist/fs.d.ts +53 -49
  19. package/dist/fs.d.ts.map +1 -1
  20. package/dist/index.d.ts +5 -4
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +249 -427
  23. package/dist/internal.js +111 -87
  24. package/dist/runner.d.ts +314 -0
  25. package/dist/runner.d.ts.map +1 -0
  26. package/dist/sandbox-manager.d.ts +45 -21
  27. package/dist/sandbox-manager.d.ts.map +1 -1
  28. package/dist/sandbox.d.ts +144 -62
  29. package/dist/sandbox.d.ts.map +1 -1
  30. package/dist/shared-modules.d.ts +22 -3
  31. package/dist/shared-modules.d.ts.map +1 -1
  32. package/dist/shared-resources.d.ts +0 -3
  33. package/dist/shared-resources.d.ts.map +1 -1
  34. package/dist/typechecker.d.ts +1 -1
  35. package/package.json +2 -11
  36. package/src/build-emitter.ts +32 -29
  37. package/src/builder.ts +498 -0
  38. package/src/bundler.ts +24 -36
  39. package/src/commands/compile.ts +236 -0
  40. package/src/commands/index.ts +51 -0
  41. package/src/commands/packages.ts +154 -0
  42. package/src/commands/run.ts +245 -0
  43. package/src/commands/types.ts +172 -0
  44. package/src/fs.ts +82 -221
  45. package/src/index.ts +17 -12
  46. package/src/sandbox.ts +217 -149
  47. package/src/shared-modules.ts +74 -4
  48. package/src/shared-resources.ts +0 -3
  49. package/src/typechecker.ts +1 -1
  50. package/dist/react.d.ts +0 -159
  51. package/dist/react.d.ts.map +0 -1
  52. package/dist/react.js +0 -149
  53. package/src/commands.ts +0 -733
  54. package/src/sandbox-manager.ts +0 -409
package/README.md CHANGED
@@ -1,472 +1,233 @@
1
1
  # Sandlot
2
2
 
3
- A browser-based TypeScript sandbox for AI agent-driven code generation.
4
-
5
- Sandlot provides a complete in-browser development environment with a virtual filesystem, TypeScript type checking, esbuild bundling, and npm package management—all without a server.
6
-
7
- ## Features
8
-
9
- - **Virtual Filesystem** - In-memory or IndexedDB-backed file storage
10
- - **TypeScript Type Checking** - Full type checking
11
- - **esbuild Bundling** - Fast bundling with automatic npm import handling via esbuild-wasm
12
- - **Package Management** - Install npm packages via esm.sh CDN
13
- - **Bash Shell** - Familiar command interface (`tsc`, `build`, `install`, etc.) via just-bash
3
+ Browser-based TypeScript sandbox with esbuild bundling and type checking. Designed for AI agent workflows where code needs to be written, validated, and executed in real-time, all in the browser.
14
4
 
15
5
  ## Installation
16
6
 
17
7
  ```bash
18
8
  npm install sandlot
19
- # or
20
- bun add sandlot
21
9
  ```
22
10
 
23
11
  ## Quick Start
24
12
 
25
13
  ```typescript
26
- import { createSandbox, loadModule } from "sandlot";
27
-
28
- // Create a sandbox
29
- const sandbox = await createSandbox({
30
- fsOptions: {
31
- initialFiles: {
32
- "/tsconfig.json": JSON.stringify({
33
- compilerOptions: { target: "ES2020", module: "ESNext", strict: true },
34
- }),
35
- },
14
+ import { createBuilder, registerSharedModules } from "sandlot";
15
+ import * as React from "react";
16
+
17
+ // Share React with dynamic code to avoid duplicate instances
18
+ registerSharedModules({ react: React });
19
+
20
+ // Create a reusable builder
21
+ const runAgent = createBuilder({
22
+ sandboxOptions: { sharedModules: ["react"] },
23
+ build: async (sandbox, prompt) => {
24
+ // Your agent logic here - execute commands via sandbox.bash.exec()
25
+ await sandbox.bash.exec(
26
+ 'echo "export const App = () => <div>Hello</div>" > /src/index.tsx',
27
+ );
28
+ await sandbox.bash.exec("build /src/index.tsx");
36
29
  },
37
30
  });
38
31
 
39
- // Write code
40
- await sandbox.fs.writeFile(
41
- "/src/index.ts",
42
- `export function greet(name: string) {
43
- return \`Hello, \${name}!\`;
44
- }`,
45
- );
46
-
47
- // Capture build result via callback
48
- let bundle: BundleResult | null = null;
49
- const unsubscribe = sandbox.onBuild((result) => {
50
- bundle = result;
51
- });
52
-
53
- // Build
54
- const result = await sandbox.bash.exec("build /src/index.ts");
55
- unsubscribe();
32
+ // Run with a prompt
33
+ const result = await runAgent("Create a counter component");
56
34
 
57
- if (result.exitCode === 0 && bundle) {
58
- const mod = await loadModule<{ greet: (name: string) => string }>(bundle);
59
- console.log(mod.greet("World")); // "Hello, World!"
35
+ if (result.module?.App) {
36
+ // Use the generated component
37
+ const App = result.module.App as React.ComponentType;
60
38
  }
61
-
62
- sandbox.close();
63
39
  ```
64
40
 
65
- ## Sandbox API
66
-
67
- The Sandbox API provides direct access to the filesystem and bash shell.
41
+ ## Builder API
68
42
 
69
- ### Creating a Sandbox
43
+ The `createBuilder()` function is the primary API for agent workflows. It handles sandbox lifecycle, build capture, validation, and cancellation.
70
44
 
71
- ```typescript
72
- import { createSandbox, createInMemorySandbox } from "sandlot";
45
+ ### `createBuilder(options)`
73
46
 
74
- // Persistent sandbox (IndexedDB-backed)
75
- const sandbox = await createSandbox({
76
- fsOptions: {
77
- dbName: "my-project",
78
- initialFiles: {
79
- "/src/index.ts": "export const x = 1;",
80
- },
81
- },
82
- });
83
-
84
- // In-memory sandbox (no persistence)
85
- const memSandbox = await createInMemorySandbox({
86
- initialFiles: {
87
- "/src/index.ts": "export const x = 1;",
88
- },
89
- });
90
- ```
91
-
92
- ### Using Bash Commands
47
+ Returns a reusable builder function that can be called with different prompts.
93
48
 
94
49
  ```typescript
95
- // Write files using bash
96
- await sandbox.bash.exec('echo "export const x = 1;" > /src/index.ts');
97
-
98
- // Type check (entry point required)
99
- const tscResult = await sandbox.bash.exec("tsc /src/index.ts");
100
- console.log(tscResult.stdout);
50
+ const runAgent = createBuilder<T>({
51
+ // Sandbox configuration (pick one)
52
+ sandbox?: Sandbox, // Reuse an existing sandbox (state persists between calls)
53
+ sandboxOptions?: SandboxOptions, // Create fresh sandbox per call (isolated runs)
101
54
 
102
- // Build (entry point required) - capture result via onBuild callback
103
- let bundleResult: BundleResult | null = null;
104
- const unsubscribe = sandbox.onBuild((result) => {
105
- bundleResult = result;
55
+ // Your agent logic
56
+ build: (sandbox: Sandbox, prompt: string) => Promise<T>,
106
57
  });
107
-
108
- const buildCmd = await sandbox.bash.exec("build /src/index.ts");
109
- if (buildCmd.exitCode === 0 && bundleResult) {
110
- console.log(bundleResult.code); // Compiled JavaScript
111
- }
112
- unsubscribe();
113
-
114
- // Install packages
115
- await sandbox.bash.exec("install react lodash@4.17.21");
116
-
117
- // List installed packages
118
- const listResult = await sandbox.bash.exec("list");
119
- console.log(listResult.stdout);
120
-
121
- // Run code (entry point required)
122
- await sandbox.bash.exec("run /src/script.ts");
123
58
  ```
124
59
 
125
- ### Build Command Options
126
-
127
- ```bash
128
- build <entry> [options]
129
-
130
- Arguments:
131
- entry Entry point file (required, e.g., /src/index.ts)
60
+ #### Options
132
61
 
133
- Options:
134
- --skip-typecheck, -s Skip type checking
135
- --minify, -m Enable minification
136
- --format, -f <format> Output format: esm (default), iife, cjs
137
- ```
62
+ | Option | Type | Description |
63
+ | ---------------- | --------------------------------- | ------------------------------------------------------- |
64
+ | `sandbox` | `Sandbox` | Existing sandbox to reuse. Files persist between calls. |
65
+ | `sandboxOptions` | `SandboxOptions` | Options for creating fresh sandboxes per call. |
66
+ | `build` | `(sandbox, prompt) => Promise<T>` | Your agent logic. Receives the sandbox and prompt. |
138
67
 
139
- ### Sandbox Events
68
+ ### Calling the Builder
140
69
 
141
70
  ```typescript
142
- // Subscribe to build events to capture build results
143
- let bundleResult: BundleResult | null = null;
144
- const unsubscribe = sandbox.onBuild((result) => {
145
- bundleResult = result;
146
- console.log("Build completed:", result.code.length, "bytes");
147
- });
148
-
149
- // Build with entry point (required)
150
- const buildCmd = await sandbox.bash.exec("build /src/index.ts");
151
- if (buildCmd.exitCode !== 0) {
152
- console.error("Build failed:", buildCmd.stderr);
153
- } else if (bundleResult) {
154
- console.log(bundleResult.code);
155
- }
156
- unsubscribe();
157
-
158
- // Check for unsaved changes
159
- if (sandbox.isDirty()) {
160
- await sandbox.save(); // Persist to IndexedDB
161
- }
162
-
163
- // Clean up
164
- sandbox.close();
71
+ const result = await runAgent(prompt, options?);
165
72
  ```
166
73
 
167
- > **Note:** The `onBuild` callback is invoked synchronously during successful builds.
168
- > Use it to capture the `BundleResult` for loading modules.
74
+ #### Call Options
169
75
 
170
- ### Sandbox Manager
76
+ | Option | Type | Description |
77
+ | ---------- | --------------- | --------------------------------------------------------------- |
78
+ | `validate` | `(module) => M` | Validate exports during build. Errors are visible to the agent. |
79
+ | `timeout` | `number` | Max time in ms. Throws `AbortError` if exceeded. |
80
+ | `signal` | `AbortSignal` | For external cancellation. |
171
81
 
172
- For managing multiple sandboxes with shared resources:
82
+ #### Result
173
83
 
174
84
  ```typescript
175
- import { createSandboxManager } from "sandlot";
176
-
177
- const manager = await createSandboxManager({
178
- sharedModules: ["react", "react-dom/client"],
179
- });
180
-
181
- // Create multiple sandboxes - they share TypeScript libs and bundler
182
- const sandbox1 = await manager.createSandbox({ id: "agent-1" });
183
- const sandbox2 = await manager.createSandbox({ id: "agent-2" });
184
-
185
- // Run operations in parallel
186
- await Promise.all([sandbox1.bash.exec("build"), sandbox2.bash.exec("build")]);
187
-
188
- // Save all with unsaved changes
189
- await manager.saveAll();
190
-
191
- // Get dirty sandbox IDs
192
- const unsaved = manager.getDirtySandboxes();
193
-
194
- // Clean up
195
- manager.destroyAll();
196
- ```
197
-
198
- ### Sandbox Options
199
-
200
- ```typescript
201
- interface SandboxOptions {
202
- // Filesystem configuration
203
- fsOptions?: {
204
- dbName?: string; // IndexedDB database name
205
- initialFiles?: Record<string, string>;
206
- maxSizeBytes?: number; // Filesystem size limit
207
- };
208
-
209
- // Build configuration
210
- tsconfigPath?: string; // Default: "/tsconfig.json"
211
-
212
- // Module sharing (see React Integration)
213
- sharedModules?: string[];
214
-
215
- // Build callback - use to capture successful build results
216
- onBuild?: (result: BundleResult) => void | Promise<void>;
217
-
218
- // Custom bash commands
219
- customCommands?: Command[];
85
+ interface BuildResult<T, M> {
86
+ result: T | undefined; // Return value from your build function
87
+ error: Error | null; // Error if build function threw
88
+ bundle: BundleResult | null; // The compiled JavaScript bundle
89
+ module: M | null; // Loaded module exports (validated if validate provided)
90
+ sandbox: Sandbox; // The sandbox used (for inspection or reuse)
220
91
  }
221
92
  ```
222
93
 
223
- ---
224
-
225
- ## Package Management
94
+ ### Validation
226
95
 
227
- Sandlot installs packages via esm.sh CDN and fetches TypeScript type definitions.
228
-
229
- ### Installing Packages
96
+ Validation runs as part of the `build` command. If it throws, the build fails and the agent sees the error, allowing it to fix the code and retry.
230
97
 
231
98
  ```typescript
232
- // Via bash
233
- await sandbox.bash.exec("install lodash date-fns@3.0.0");
234
-
235
- // Via direct API
236
- import { installPackage } from "sandlot";
237
- await installPackage(fs, "lodash@4.17.21");
238
- ```
239
-
240
- ### How It Works
241
-
242
- 1. Resolves package version from esm.sh CDN
243
- 2. Fetches TypeScript type definitions
244
- 3. Stores types in `/node_modules/<package>/`
245
- 4. Updates `/package.json` with installed version
246
- 5. At bundle time, imports are rewritten to esm.sh URLs
247
-
248
- ### Package Commands
249
-
250
- ```bash
251
- install <package>[@version] [...] # Install packages
252
- uninstall <package> [...] # Remove packages
253
- list # Show installed packages
99
+ const result = await runAgent("Create a counter", {
100
+ validate: (mod) => {
101
+ if (typeof mod.App !== "function") {
102
+ throw new Error("Must export an App component");
103
+ }
104
+ return { App: mod.App as React.ComponentType };
105
+ },
106
+ });
107
+ // result.module is typed as { App: React.ComponentType } | null
254
108
  ```
255
109
 
256
- ---
257
-
258
- ## Loading Modules
259
-
260
- After building, use loaders to access the compiled code:
110
+ With Zod:
261
111
 
262
112
  ```typescript
263
- import { loadModule, loadExport, loadDefault } from "sandlot";
264
-
265
- // Load all exports
266
- const mod = await loadModule<{ add: Function; multiply: Function }>(
267
- result.bundle,
268
- );
113
+ import { z } from "zod";
269
114
 
270
- // Load a specific export
271
- const add = await loadExport<(a: number, b: number) => number>(
272
- result.bundle,
273
- "add",
274
- );
275
-
276
- // Load default export
277
- const Calculator = await loadDefault<typeof Calculator>(result.bundle);
278
-
279
- // Check what's exported
280
- const names = await getExportNames(result.bundle); // ["add", "multiply", "default"]
281
- const hasAdd = await hasExport(result.bundle, "add"); // true
282
- ```
283
-
284
- ---
285
-
286
- ## Advanced Usage
287
-
288
- ### Direct Bundler Access
289
-
290
- ```typescript
291
- import { bundle, initBundler } from "sandlot";
292
-
293
- // Pre-warm the bundler (optional)
294
- await initBundler();
295
-
296
- const result = await bundle({
297
- fs: myFilesystem,
298
- entryPoint: "/src/index.ts",
299
- format: "esm", // "esm" | "iife" | "cjs"
300
- minify: false,
301
- sourcemap: false,
302
- sharedModules: ["react"],
303
- npmImports: "cdn", // "cdn" | "external" | "bundle"
115
+ const Schema = z.object({
116
+ App: z.custom<React.ComponentType>((v) => typeof v === "function"),
117
+ initialCount: z.number().optional(),
304
118
  });
305
119
 
306
- console.log(result.code);
307
- console.log(result.warnings);
308
- console.log(result.includedFiles);
309
- ```
310
-
311
- ### Direct Type Checking
312
-
313
- ```typescript
314
- import {
315
- typecheck,
316
- formatDiagnostics,
317
- formatDiagnosticsForAgent,
318
- } from "sandlot";
319
-
320
- const result = await typecheck({
321
- fs: myFilesystem,
322
- entryPoint: "/src/index.ts",
323
- tsconfigPath: "/tsconfig.json",
324
- libFiles: myLibFiles, // Map of lib name to content
120
+ const result = await runAgent("Create a counter", {
121
+ validate: (mod) => Schema.parse(mod),
325
122
  });
326
-
327
- if (result.hasErrors) {
328
- // Human-readable format
329
- console.log(formatDiagnostics(result.diagnostics));
330
-
331
- // Agent-friendly format
332
- console.log(formatDiagnosticsForAgent(result.diagnostics));
333
- }
334
123
  ```
335
124
 
336
- ### Custom Filesystem
125
+ ### Cancellation
337
126
 
338
127
  ```typescript
339
- import { createIndexedDbFs, IndexedDbFs } from "sandlot";
128
+ const controller = new AbortController();
340
129
 
341
- // Persistent filesystem
342
- const fs = await createIndexedDbFs({
343
- dbName: "my-project",
344
- initialFiles: { "/README.md": "# Hello" },
345
- maxSizeBytes: 50 * 1024 * 1024, // 50MB limit
130
+ const promise = runAgent("Create a dashboard", {
131
+ signal: controller.signal,
132
+ timeout: 60_000, // 1 minute
346
133
  });
347
134
 
348
- // In-memory filesystem
349
- const memFs = IndexedDbFs.createInMemory({
350
- initialFiles: { "/README.md": "# Hello" },
351
- });
352
-
353
- // File operations
354
- await fs.writeFile("/src/index.ts", "export const x = 1;");
355
- const content = await fs.readFile("/src/index.ts");
356
- const exists = await fs.exists("/src/index.ts");
357
- await fs.mkdir("/src/utils", { recursive: true });
358
- await fs.rm("/src/old.ts");
359
-
360
- // Persistence
361
- if (fs.isDirty()) {
362
- await fs.save();
363
- }
364
-
365
- fs.close();
135
+ // Cancel on user action
136
+ cancelButton.onclick = () => controller.abort();
366
137
  ```
367
138
 
368
- ### Shared Resources
139
+ ## Sandbox API
369
140
 
370
- Share TypeScript libs and bundler across multiple sandboxes:
141
+ For lower-level control, use `createSandbox()` directly.
371
142
 
372
143
  ```typescript
373
- import { createSharedResources, getDefaultResources } from "sandlot";
374
-
375
- // Create custom resources
376
- const resources = await createSharedResources({
377
- libs: ["ES2022", "DOM", "DOM.Iterable"],
378
- });
144
+ import { createSandbox } from "sandlot";
379
145
 
380
- // Use with sandbox
381
146
  const sandbox = await createSandbox({
382
- resources,
147
+ initialFiles: {
148
+ "/src/index.ts": "export const x = 1;",
149
+ "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }),
150
+ },
151
+ sharedModules: ["react", "react-dom/client"],
152
+ onBuild: (result) => console.log("Build succeeded:", result),
383
153
  });
384
154
 
385
- // Or use the default singleton
386
- const defaultResources = await getDefaultResources();
387
- ```
388
-
389
- ---
390
-
391
- ## TypeScript Configuration
392
-
393
- Sandlot respects your `tsconfig.json`:
155
+ // Execute shell commands
156
+ await sandbox.bash.exec("tsc /src/index.ts"); // Type check
157
+ await sandbox.bash.exec("build /src/index.ts"); // Bundle
158
+ await sandbox.bash.exec("install lodash"); // Install package
159
+ await sandbox.bash.exec("list"); // List packages
394
160
 
395
- ```json
396
- {
397
- "compilerOptions": {
398
- "target": "ES2020",
399
- "module": "ESNext",
400
- "moduleResolution": "bundler",
401
- "jsx": "react-jsx",
402
- "strict": true,
403
- "esModuleInterop": true,
404
- "skipLibCheck": true,
405
- "lib": ["ES2020", "DOM", "DOM.Iterable"]
406
- }
161
+ // Access the last successful build
162
+ if (sandbox.lastBuild) {
163
+ const { bundle, module } = sandbox.lastBuild;
407
164
  }
165
+
166
+ // Serialize state for persistence
167
+ const state = sandbox.getState();
168
+ localStorage.setItem("project", JSON.stringify(state));
408
169
  ```
409
170
 
410
- Supported options:
171
+ ### Sandbox Options
411
172
 
412
- - `target`: ES5 through ESNext
413
- - `module`: CommonJS, ES2015, ES2020, ESNext, Node16, NodeNext
414
- - `moduleResolution`: Classic, Node, Node16, NodeNext, Bundler
415
- - `jsx`: preserve, react, react-jsx, react-jsxdev, react-native
416
- - `strict`, `noImplicitAny`, `strictNullChecks`
417
- - `esModuleInterop`, `allowJs`, `resolveJsonModule`
418
- - `lib`: Array of lib names
173
+ | Option | Type | Description |
174
+ | --------------- | ------------------------ | --------------------------------------------------- |
175
+ | `initialFiles` | `Record<string, string>` | Files to populate the filesystem with. |
176
+ | `sharedModules` | `string[]` | Modules to resolve from host (e.g., `['react']`). |
177
+ | `tsconfigPath` | `string` | Path to tsconfig.json (default: `/tsconfig.json`). |
178
+ | `onBuild` | `(result) => void` | Callback when a build succeeds. |
179
+ | `bashOptions` | `SandboxBashOptions` | Options for the just-bash shell (cwd, env, limits). |
419
180
 
420
- ---
181
+ ## Shell Commands
421
182
 
422
- ## API Reference
183
+ The sandbox provides these built-in commands:
423
184
 
424
- ### Exports from `sandlot`
185
+ | Command | Description |
186
+ | ------------------------- | ----------------------------------- |
187
+ | `tsc [entry]` | Type check (uses tsconfig.json) |
188
+ | `build [entry] [options]` | Bundle (runs typecheck first) |
189
+ | `install <pkg>` | Install npm package (fetches types) |
190
+ | `uninstall <pkg>` | Remove package |
191
+ | `list` | List installed packages |
192
+ | `run <entry>` | Execute a script |
425
193
 
426
- ```typescript
427
- // Sandbox API
428
- export { createSandbox, createInMemorySandbox } from "sandlot";
429
- export { SandboxManager, createSandboxManager } from "sandlot";
194
+ Build options: `--format <esm\|iife\|cjs>`, `--minify`, `--skip-typecheck`
430
195
 
431
- // Module Loading
432
- export { loadModule, loadExport, loadDefault } from "sandlot";
433
- export { getExportNames, hasExport } from "sandlot";
196
+ ## Shared Modules
434
197
 
435
- // Shared Modules
436
- export { registerSharedModules, clearSharedModules } from "sandlot";
437
-
438
- // Types
439
- export type { Sandbox, SandboxOptions } from "sandlot";
440
- export type { BundleResult, TypecheckResult } from "sandlot";
441
- ```
442
-
443
- ### Exports from `sandlot/react`
198
+ To avoid duplicate library instances (important for React context/hooks), register shared modules before creating sandboxes:
444
199
 
445
200
  ```typescript
446
- export { DynamicMount, useDynamicComponent } from "sandlot/react";
447
- export { generateRenderFunction, REACT_RENDER_TEMPLATE } from "sandlot/react";
448
- export type { DynamicRenderModule, DynamicMountProps } from "sandlot/react";
201
+ import { registerSharedModules } from "sandlot";
202
+ import * as React from "react";
203
+ import * as ReactDOM from "react-dom/client";
204
+
205
+ registerSharedModules({
206
+ react: React,
207
+ "react-dom/client": ReactDOM,
208
+ });
449
209
  ```
450
210
 
451
- ---
211
+ Then include them in `sharedModules` when creating a sandbox.
452
212
 
453
- ## Framework Setup
213
+ ## Cross-Origin Isolation
454
214
 
455
- ### Vite
215
+ For optimal esbuild-wasm performance, enable cross-origin isolation by adding these headers to your dev server:
456
216
 
457
- For optimal performance, add cross-origin isolation headers to enable SharedArrayBuffer:
217
+ ```
218
+ Cross-Origin-Embedder-Policy: require-corp
219
+ Cross-Origin-Opener-Policy: same-origin
220
+ ```
458
221
 
459
- ```typescript
460
- // vite.config.ts
461
- import { defineConfig } from "vite";
222
+ In Vite:
462
223
 
224
+ ```typescript
463
225
  export default defineConfig({
464
226
  plugins: [
465
- // Add COOP/COEP headers for SharedArrayBuffer (recommended for esbuild-wasm)
466
227
  {
467
- name: "cross-origin-isolation",
228
+ name: "isolation",
468
229
  configureServer: (server) => {
469
- server.middlewares.use((_req, res, next) => {
230
+ server.middlewares.use((_, res, next) => {
470
231
  res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
471
232
  res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
472
233
  next();
@@ -477,37 +238,6 @@ export default defineConfig({
477
238
  });
478
239
  ```
479
240
 
480
- > **Note:** Without these headers, sandlot will still work but may have reduced performance.
481
- > The console will show a warning if cross-origin isolation is not enabled.
482
-
483
- ### Next.js
484
-
485
- Add headers in `next.config.js`:
486
-
487
- ```javascript
488
- module.exports = {
489
- async headers() {
490
- return [
491
- {
492
- source: "/(.*)",
493
- headers: [
494
- { key: "Cross-Origin-Embedder-Policy", value: "require-corp" },
495
- { key: "Cross-Origin-Opener-Policy", value: "same-origin" },
496
- ],
497
- },
498
- ];
499
- },
500
- };
501
- ```
502
-
503
- ---
504
-
505
- ## Requirements
506
-
507
- - Modern browser with ES2020 support
508
- - IndexedDB (for persistent filesystems)
509
- - WebAssembly (for esbuild)
510
-
511
241
  ## License
512
242
 
513
243
  MIT