sandlot 0.1.1 → 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 (59) hide show
  1. package/README.md +145 -518
  2. package/dist/build-emitter.d.ts +47 -0
  3. package/dist/build-emitter.d.ts.map +1 -0
  4. package/dist/builder.d.ts +370 -0
  5. package/dist/builder.d.ts.map +1 -0
  6. package/dist/bundler.d.ts +3 -3
  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 +60 -42
  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 +304 -491
  23. package/dist/internal.d.ts +5 -0
  24. package/dist/internal.d.ts.map +1 -1
  25. package/dist/internal.js +174 -95
  26. package/dist/runner.d.ts +314 -0
  27. package/dist/runner.d.ts.map +1 -0
  28. package/dist/sandbox-manager.d.ts +45 -33
  29. package/dist/sandbox-manager.d.ts.map +1 -1
  30. package/dist/sandbox.d.ts +144 -70
  31. package/dist/sandbox.d.ts.map +1 -1
  32. package/dist/shared-modules.d.ts +22 -3
  33. package/dist/shared-modules.d.ts.map +1 -1
  34. package/dist/shared-resources.d.ts +0 -3
  35. package/dist/shared-resources.d.ts.map +1 -1
  36. package/dist/typechecker.d.ts +1 -1
  37. package/package.json +3 -17
  38. package/src/build-emitter.ts +64 -0
  39. package/src/builder.ts +498 -0
  40. package/src/bundler.ts +86 -57
  41. package/src/commands/compile.ts +236 -0
  42. package/src/commands/index.ts +51 -0
  43. package/src/commands/packages.ts +154 -0
  44. package/src/commands/run.ts +245 -0
  45. package/src/commands/types.ts +172 -0
  46. package/src/fs.ts +90 -216
  47. package/src/index.ts +34 -12
  48. package/src/internal.ts +5 -2
  49. package/src/sandbox.ts +214 -220
  50. package/src/shared-modules.ts +74 -4
  51. package/src/shared-resources.ts +0 -3
  52. package/src/ts-libs.ts +1 -1
  53. package/src/typechecker.ts +1 -1
  54. package/dist/react.d.ts +0 -159
  55. package/dist/react.d.ts.map +0 -1
  56. package/dist/react.js +0 -149
  57. package/src/commands.ts +0 -733
  58. package/src/react.tsx +0 -331
  59. package/src/sandbox-manager.ts +0 -490
package/README.md CHANGED
@@ -1,616 +1,243 @@
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
14
- - **React Integration** - Share your React instance with dynamic components
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.
15
4
 
16
5
  ## Installation
17
6
 
18
7
  ```bash
19
8
  npm install sandlot
20
- # or
21
- bun add sandlot
22
9
  ```
23
10
 
24
11
  ## Quick Start
25
12
 
26
13
  ```typescript
27
- import { createSandbox, loadModule } from "sandlot";
14
+ import { createBuilder, registerSharedModules } from "sandlot";
15
+ import * as React from "react";
28
16
 
29
- // Create a sandbox
30
- const sandbox = await createSandbox({
31
- fsOptions: {
32
- initialFiles: {
33
- "/tsconfig.json": JSON.stringify({
34
- compilerOptions: { target: "ES2020", module: "ESNext", strict: true },
35
- }),
36
- },
37
- },
38
- });
17
+ // Share React with dynamic code to avoid duplicate instances
18
+ registerSharedModules({ react: React });
39
19
 
40
- // Write code
41
- await sandbox.fs.writeFile(
42
- "/src/index.ts",
43
- `export function greet(name: string) {
44
- return \`Hello, \${name}!\`;
45
- }`,
46
- );
47
-
48
- // Capture build result via callback
49
- let bundle: BundleResult | null = null;
50
- const unsubscribe = sandbox.onBuild((result) => {
51
- bundle = result;
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");
29
+ },
52
30
  });
53
31
 
54
- // Build
55
- const result = await sandbox.bash.exec("build /src/index.ts");
56
- unsubscribe();
32
+ // Run with a prompt
33
+ const result = await runAgent("Create a counter component");
57
34
 
58
- if (result.exitCode === 0 && bundle) {
59
- const mod = await loadModule<{ greet: (name: string) => string }>(bundle);
60
- 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;
61
38
  }
62
-
63
- sandbox.close();
64
39
  ```
65
40
 
66
- ## Sandbox API
41
+ ## Builder API
67
42
 
68
- The Sandbox API provides direct access to the filesystem and bash shell.
43
+ The `createBuilder()` function is the primary API for agent workflows. It handles sandbox lifecycle, build capture, validation, and cancellation.
69
44
 
70
- ### Creating a Sandbox
45
+ ### `createBuilder(options)`
71
46
 
72
- ```typescript
73
- import { createSandbox, createInMemorySandbox } from "sandlot";
74
-
75
- // Persistent sandbox (IndexedDB-backed)
76
- const sandbox = await createSandbox({
77
- fsOptions: {
78
- dbName: "my-project",
79
- initialFiles: {
80
- "/src/index.ts": "export const x = 1;",
81
- },
82
- },
83
- });
84
-
85
- // In-memory sandbox (no persistence)
86
- const memSandbox = await createInMemorySandbox({
87
- initialFiles: {
88
- "/src/index.ts": "export const x = 1;",
89
- },
90
- });
91
- ```
92
-
93
- ### Using Bash Commands
47
+ Returns a reusable builder function that can be called with different prompts.
94
48
 
95
49
  ```typescript
96
- // Write files using bash
97
- await sandbox.bash.exec('echo "export const x = 1;" > /src/index.ts');
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)
98
54
 
99
- // Type check (entry point required)
100
- const tscResult = await sandbox.bash.exec("tsc /src/index.ts");
101
- console.log(tscResult.stdout);
102
-
103
- // Build (entry point required) - capture result via onBuild callback
104
- let bundleResult: BundleResult | null = null;
105
- const unsubscribe = sandbox.onBuild((result) => {
106
- bundleResult = result;
55
+ // Your agent logic
56
+ build: (sandbox: Sandbox, prompt: string) => Promise<T>,
107
57
  });
108
-
109
- const buildCmd = await sandbox.bash.exec("build /src/index.ts");
110
- if (buildCmd.exitCode === 0 && bundleResult) {
111
- console.log(bundleResult.code); // Compiled JavaScript
112
- }
113
- unsubscribe();
114
-
115
- // Install packages
116
- await sandbox.bash.exec("install react lodash@4.17.21");
117
-
118
- // List installed packages
119
- const listResult = await sandbox.bash.exec("list");
120
- console.log(listResult.stdout);
121
-
122
- // Run code (entry point required)
123
- await sandbox.bash.exec("run /src/script.ts");
124
58
  ```
125
59
 
126
- ### Build Command Options
127
-
128
- ```bash
129
- build <entry> [options]
60
+ #### Options
130
61
 
131
- Arguments:
132
- entry Entry point file (required, e.g., /src/index.ts)
133
-
134
- Options:
135
- --skip-typecheck, -s Skip type checking
136
- --minify, -m Enable minification
137
- --format, -f <format> Output format: esm (default), iife, cjs
138
- ```
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. |
139
67
 
140
- ### Sandbox Events
68
+ ### Calling the Builder
141
69
 
142
70
  ```typescript
143
- // Subscribe to build events to capture build results
144
- let bundleResult: BundleResult | null = null;
145
- const unsubscribe = sandbox.onBuild((result) => {
146
- bundleResult = result;
147
- console.log("Build completed:", result.code.length, "bytes");
148
- });
149
-
150
- // Build with entry point (required)
151
- const buildCmd = await sandbox.bash.exec("build /src/index.ts");
152
- if (buildCmd.exitCode !== 0) {
153
- console.error("Build failed:", buildCmd.stderr);
154
- } else if (bundleResult) {
155
- console.log(bundleResult.code);
156
- }
157
- unsubscribe();
158
-
159
- // Check for unsaved changes
160
- if (sandbox.isDirty()) {
161
- await sandbox.save(); // Persist to IndexedDB
162
- }
163
-
164
- // Clean up
165
- sandbox.close();
71
+ const result = await runAgent(prompt, options?);
166
72
  ```
167
73
 
168
- > **Note:** The `onBuild` callback is invoked synchronously during successful builds.
169
- > Use it to capture the `BundleResult` for loading modules.
170
-
171
- ### Sandbox Manager
172
-
173
- For managing multiple sandboxes with shared resources:
174
-
175
- ```typescript
176
- import { createSandboxManager } from "sandlot";
177
-
178
- const manager = await createSandboxManager({
179
- sharedModules: ["react", "react-dom/client"],
180
- });
181
-
182
- // Create multiple sandboxes - they share TypeScript libs and bundler
183
- const sandbox1 = await manager.createSandbox({ id: "agent-1" });
184
- const sandbox2 = await manager.createSandbox({ id: "agent-2" });
74
+ #### Call Options
185
75
 
186
- // Run operations in parallel
187
- await Promise.all([sandbox1.bash.exec("build"), sandbox2.bash.exec("build")]);
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. |
188
81
 
189
- // Save all with unsaved changes
190
- await manager.saveAll();
191
-
192
- // Get dirty sandbox IDs
193
- const unsaved = manager.getDirtySandboxes();
194
-
195
- // Clean up
196
- manager.destroyAll();
197
- ```
198
-
199
- ### Sandbox Options
82
+ #### Result
200
83
 
201
84
  ```typescript
202
- interface SandboxOptions {
203
- // Filesystem configuration
204
- fsOptions?: {
205
- dbName?: string; // IndexedDB database name
206
- initialFiles?: Record<string, string>;
207
- maxSizeBytes?: number; // Filesystem size limit
208
- };
209
-
210
- // Build configuration
211
- tsconfigPath?: string; // Default: "/tsconfig.json"
212
-
213
- // Module sharing (see React Integration)
214
- sharedModules?: string[];
215
-
216
- // Build callback - use to capture successful build results
217
- onBuild?: (result: BundleResult) => void | Promise<void>;
218
-
219
- // Custom bash commands
220
- 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)
221
91
  }
222
92
  ```
223
93
 
224
- ---
225
-
226
- ## React Integration
94
+ ### Validation
227
95
 
228
- Sandlot solves the "multiple React instances" problem by letting dynamic components use your host application's React.
229
-
230
- ### Setup
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.
231
97
 
232
98
  ```typescript
233
- import * as React from "react";
234
- import * as ReactDOM from "react-dom/client";
235
- import { registerSharedModules, createSandbox } from "sandlot";
236
- import { DynamicMount } from "sandlot/react";
237
-
238
- // Register your React instances (do this once at app startup)
239
- registerSharedModules({
240
- react: React,
241
- "react-dom/client": ReactDOM,
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
+ },
242
106
  });
107
+ // result.module is typed as { App: React.ComponentType } | null
243
108
  ```
244
109
 
245
- ### Generating React Components
110
+ With Zod:
246
111
 
247
112
  ```typescript
248
- import { createSandbox, loadModule, BundleResult } from "sandlot";
113
+ import { z } from "zod";
249
114
 
250
- const sandbox = await createSandbox({
251
- fsOptions: {
252
- initialFiles: {
253
- "/tsconfig.json": JSON.stringify({
254
- compilerOptions: {
255
- target: "ES2020",
256
- module: "ESNext",
257
- jsx: "react-jsx",
258
- strict: true,
259
- },
260
- }),
261
- },
262
- },
263
- sharedModules: ["react", "react-dom/client"],
115
+ const Schema = z.object({
116
+ App: z.custom<React.ComponentType>((v) => typeof v === "function"),
117
+ initialCount: z.number().optional(),
264
118
  });
265
119
 
266
- // Install React types for TypeScript compilation
267
- // (sharedModules provides runtime React, but types are still needed)
268
- await sandbox.bash.exec("install react react-dom");
269
-
270
- await sandbox.fs.writeFile(
271
- "/src/index.tsx",
272
- `
273
- import React, { useState } from "react";
274
- import { createRoot } from "react-dom/client";
275
-
276
- function Counter() {
277
- const [count, setCount] = useState(0);
278
- return (
279
- <button onClick={() => setCount(c => c + 1)}>
280
- Count: {count}
281
- </button>
282
- );
283
- }
284
-
285
- export function render(container: HTMLElement) {
286
- const root = createRoot(container);
287
- root.render(<Counter />);
288
- return () => root.unmount();
289
- }
290
- `,
291
- );
292
-
293
- // Capture build result
294
- let bundle: BundleResult | null = null;
295
- const unsubscribe = sandbox.onBuild((result) => {
296
- bundle = result;
120
+ const result = await runAgent("Create a counter", {
121
+ validate: (mod) => Schema.parse(mod),
297
122
  });
298
-
299
- const buildResult = await sandbox.bash.exec("build /src/index.tsx");
300
- unsubscribe();
301
-
302
- if (buildResult.exitCode === 0 && bundle) {
303
- // Load the module and call render
304
- const mod = await loadModule<{ render: (el: HTMLElement) => () => void }>(
305
- bundle,
306
- );
307
- // mod.render is ready to use
308
- }
309
-
310
- sandbox.close();
311
- ```
312
-
313
- ### Rendering Dynamic Components
314
-
315
- Use `DynamicMount` to render the generated component:
316
-
317
- ```tsx
318
- import { DynamicMount } from "sandlot/react";
319
-
320
- function App() {
321
- const [module, setModule] = useState(null);
322
-
323
- const generate = async () => {
324
- // ... build with sandbox, capture bundle via onBuild ...
325
- setModule(bundle);
326
- };
327
-
328
- return (
329
- <div>
330
- <button onClick={generate}>Generate</button>
331
- <DynamicMount
332
- module={module}
333
- props={{ name: "World" }}
334
- fallback={<div>Click to generate...</div>}
335
- onMount={() => console.log("Mounted")}
336
- onError={(err) => console.error(err)}
337
- />
338
- </div>
339
- );
340
- }
341
- ```
342
-
343
- ### Using the Hook
344
-
345
- For more control, use `useDynamicComponent`:
346
-
347
- ```tsx
348
- import { useDynamicComponent } from "sandlot/react";
349
-
350
- function App() {
351
- const { containerRef, isMounted, error, unmount } = useDynamicComponent(
352
- module,
353
- { name: "World" },
354
- );
355
-
356
- return (
357
- <div>
358
- <div ref={containerRef} />
359
- {isMounted && <button onClick={unmount}>Remove</button>}
360
- {error && <div>Error: {error.message}</div>}
361
- </div>
362
- );
363
- }
364
- ```
365
-
366
- ### Render Function Pattern
367
-
368
- Dynamic components must export a `render` function:
369
-
370
- ```typescript
371
- export function render(container: HTMLElement, props?: MyProps) {
372
- const root = createRoot(container);
373
- root.render(<MyComponent {...props} />);
374
- return () => root.unmount(); // Cleanup function
375
- }
376
123
  ```
377
124
 
378
- ---
379
-
380
- ## Package Management
381
-
382
- Sandlot installs packages via esm.sh CDN and fetches TypeScript type definitions.
383
-
384
- ### Installing Packages
125
+ ### Cancellation
385
126
 
386
127
  ```typescript
387
- // Via bash
388
- await sandbox.bash.exec("install lodash date-fns@3.0.0");
389
-
390
- // Via direct API
391
- import { installPackage } from "sandlot";
392
- await installPackage(fs, "lodash@4.17.21");
393
- ```
394
-
395
- ### How It Works
128
+ const controller = new AbortController();
396
129
 
397
- 1. Resolves package version from esm.sh CDN
398
- 2. Fetches TypeScript type definitions
399
- 3. Stores types in `/node_modules/<package>/`
400
- 4. Updates `/package.json` with installed version
401
- 5. At bundle time, imports are rewritten to esm.sh URLs
402
-
403
- ### Package Commands
404
-
405
- ```bash
406
- install <package>[@version] [...] # Install packages
407
- uninstall <package> [...] # Remove packages
408
- list # Show installed packages
409
- ```
410
-
411
- ---
412
-
413
- ## Loading Modules
414
-
415
- After building, use loaders to access the compiled code:
416
-
417
- ```typescript
418
- import { loadModule, loadExport, loadDefault } from "sandlot";
419
-
420
- // Load all exports
421
- const mod = await loadModule<{ add: Function; multiply: Function }>(
422
- result.bundle,
423
- );
424
-
425
- // Load a specific export
426
- const add = await loadExport<(a: number, b: number) => number>(
427
- result.bundle,
428
- "add",
429
- );
430
-
431
- // Load default export
432
- const Calculator = await loadDefault<typeof Calculator>(result.bundle);
130
+ const promise = runAgent("Create a dashboard", {
131
+ signal: controller.signal,
132
+ timeout: 60_000, // 1 minute
133
+ });
433
134
 
434
- // Check what's exported
435
- const names = await getExportNames(result.bundle); // ["add", "multiply", "default"]
436
- const hasAdd = await hasExport(result.bundle, "add"); // true
135
+ // Cancel on user action
136
+ cancelButton.onclick = () => controller.abort();
437
137
  ```
438
138
 
439
- ---
440
-
441
- ## Advanced Usage
139
+ ## Sandbox API
442
140
 
443
- ### Direct Bundler Access
141
+ For lower-level control, use `createSandbox()` directly.
444
142
 
445
143
  ```typescript
446
- import { bundle, initBundler } from "sandlot";
447
-
448
- // Pre-warm the bundler (optional)
449
- await initBundler();
450
-
451
- const result = await bundle({
452
- fs: myFilesystem,
453
- entryPoint: "/src/index.ts",
454
- format: "esm", // "esm" | "iife" | "cjs"
455
- minify: false,
456
- sourcemap: false,
457
- sharedModules: ["react"],
458
- npmImports: "cdn", // "cdn" | "external" | "bundle"
459
- });
460
-
461
- console.log(result.code);
462
- console.log(result.warnings);
463
- console.log(result.includedFiles);
464
- ```
465
-
466
- ### Direct Type Checking
144
+ import { createSandbox } from "sandlot";
467
145
 
468
- ```typescript
469
- import {
470
- typecheck,
471
- formatDiagnostics,
472
- formatDiagnosticsForAgent,
473
- } from "sandlot";
474
-
475
- const result = await typecheck({
476
- fs: myFilesystem,
477
- entryPoint: "/src/index.ts",
478
- tsconfigPath: "/tsconfig.json",
479
- libFiles: myLibFiles, // Map of lib name to content
146
+ const sandbox = await createSandbox({
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),
480
153
  });
481
154
 
482
- if (result.hasErrors) {
483
- // Human-readable format
484
- console.log(formatDiagnostics(result.diagnostics));
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
485
160
 
486
- // Agent-friendly format
487
- console.log(formatDiagnosticsForAgent(result.diagnostics));
161
+ // Access the last successful build
162
+ if (sandbox.lastBuild) {
163
+ const { bundle, module } = sandbox.lastBuild;
488
164
  }
489
- ```
490
165
 
491
- ### Custom Filesystem
166
+ // Serialize state for persistence
167
+ const state = sandbox.getState();
168
+ localStorage.setItem("project", JSON.stringify(state));
169
+ ```
492
170
 
493
- ```typescript
494
- import { createIndexedDbFs, IndexedDbFs } from "sandlot";
171
+ ### Sandbox Options
495
172
 
496
- // Persistent filesystem
497
- const fs = await createIndexedDbFs({
498
- dbName: "my-project",
499
- initialFiles: { "/README.md": "# Hello" },
500
- maxSizeBytes: 50 * 1024 * 1024, // 50MB limit
501
- });
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). |
502
180
 
503
- // In-memory filesystem
504
- const memFs = IndexedDbFs.createInMemory({
505
- initialFiles: { "/README.md": "# Hello" },
506
- });
181
+ ## Shell Commands
507
182
 
508
- // File operations
509
- await fs.writeFile("/src/index.ts", "export const x = 1;");
510
- const content = await fs.readFile("/src/index.ts");
511
- const exists = await fs.exists("/src/index.ts");
512
- await fs.mkdir("/src/utils", { recursive: true });
513
- await fs.rm("/src/old.ts");
183
+ The sandbox provides these built-in commands:
514
184
 
515
- // Persistence
516
- if (fs.isDirty()) {
517
- await fs.save();
518
- }
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 |
519
193
 
520
- fs.close();
521
- ```
194
+ Build options: `--format <esm\|iife\|cjs>`, `--minify`, `--skip-typecheck`
522
195
 
523
- ### Shared Resources
196
+ ## Shared Modules
524
197
 
525
- Share TypeScript libs and bundler across multiple sandboxes:
198
+ To avoid duplicate library instances (important for React context/hooks), register shared modules before creating sandboxes:
526
199
 
527
200
  ```typescript
528
- import { createSharedResources, getDefaultResources } from "sandlot";
529
-
530
- // Create custom resources
531
- const resources = await createSharedResources({
532
- libs: ["ES2022", "DOM", "DOM.Iterable"],
533
- });
201
+ import { registerSharedModules } from "sandlot";
202
+ import * as React from "react";
203
+ import * as ReactDOM from "react-dom/client";
534
204
 
535
- // Use with sandbox
536
- const sandbox = await createSandbox({
537
- resources,
205
+ registerSharedModules({
206
+ react: React,
207
+ "react-dom/client": ReactDOM,
538
208
  });
539
-
540
- // Or use the default singleton
541
- const defaultResources = await getDefaultResources();
542
209
  ```
543
210
 
544
- ---
211
+ Then include them in `sharedModules` when creating a sandbox.
545
212
 
546
- ## TypeScript Configuration
213
+ ## Cross-Origin Isolation
547
214
 
548
- Sandlot respects your `tsconfig.json`:
215
+ For optimal esbuild-wasm performance, enable cross-origin isolation by adding these headers to your dev server:
549
216
 
550
- ```json
551
- {
552
- "compilerOptions": {
553
- "target": "ES2020",
554
- "module": "ESNext",
555
- "moduleResolution": "bundler",
556
- "jsx": "react-jsx",
557
- "strict": true,
558
- "esModuleInterop": true,
559
- "skipLibCheck": true,
560
- "lib": ["ES2020", "DOM", "DOM.Iterable"]
561
- }
562
- }
563
217
  ```
564
-
565
- Supported options:
566
-
567
- - `target`: ES5 through ESNext
568
- - `module`: CommonJS, ES2015, ES2020, ESNext, Node16, NodeNext
569
- - `moduleResolution`: Classic, Node, Node16, NodeNext, Bundler
570
- - `jsx`: preserve, react, react-jsx, react-jsxdev, react-native
571
- - `strict`, `noImplicitAny`, `strictNullChecks`
572
- - `esModuleInterop`, `allowJs`, `resolveJsonModule`
573
- - `lib`: Array of lib names
574
-
575
- ---
576
-
577
- ## API Reference
578
-
579
- ### Exports from `sandlot`
580
-
581
- ```typescript
582
- // Sandbox API
583
- export { createSandbox, createInMemorySandbox } from "sandlot";
584
- export { SandboxManager, createSandboxManager } from "sandlot";
585
-
586
- // Module Loading
587
- export { loadModule, loadExport, loadDefault } from "sandlot";
588
- export { getExportNames, hasExport } from "sandlot";
589
-
590
- // Shared Modules
591
- export { registerSharedModules, clearSharedModules } from "sandlot";
592
-
593
- // Types
594
- export type { Sandbox, SandboxOptions } from "sandlot";
595
- export type { BundleResult, TypecheckResult } from "sandlot";
218
+ Cross-Origin-Embedder-Policy: require-corp
219
+ Cross-Origin-Opener-Policy: same-origin
596
220
  ```
597
221
 
598
- ### Exports from `sandlot/react`
222
+ In Vite:
599
223
 
600
224
  ```typescript
601
- export { DynamicMount, useDynamicComponent } from "sandlot/react";
602
- export { generateRenderFunction, REACT_RENDER_TEMPLATE } from "sandlot/react";
603
- export type { DynamicRenderModule, DynamicMountProps } from "sandlot/react";
225
+ export default defineConfig({
226
+ plugins: [
227
+ {
228
+ name: "isolation",
229
+ configureServer: (server) => {
230
+ server.middlewares.use((_, res, next) => {
231
+ res.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
232
+ res.setHeader("Cross-Origin-Opener-Policy", "same-origin");
233
+ next();
234
+ });
235
+ },
236
+ },
237
+ ],
238
+ });
604
239
  ```
605
240
 
606
- ---
607
-
608
- ## Requirements
609
-
610
- - Modern browser with ES2020 support
611
- - IndexedDB (for persistent filesystems)
612
- - WebAssembly (for esbuild)
613
-
614
241
  ## License
615
242
 
616
243
  MIT