sandlot 0.1.0
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/README.md +616 -0
- package/dist/bundler.d.ts +148 -0
- package/dist/bundler.d.ts.map +1 -0
- package/dist/commands.d.ts +179 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/fs.d.ts +125 -0
- package/dist/fs.d.ts.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2920 -0
- package/dist/internal.d.ts +74 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +1897 -0
- package/dist/loader.d.ts +164 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/packages.d.ts +199 -0
- package/dist/packages.d.ts.map +1 -0
- package/dist/react.d.ts +159 -0
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +149 -0
- package/dist/sandbox-manager.d.ts +249 -0
- package/dist/sandbox-manager.d.ts.map +1 -0
- package/dist/sandbox.d.ts +193 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/shared-modules.d.ts +129 -0
- package/dist/shared-modules.d.ts.map +1 -0
- package/dist/shared-resources.d.ts +105 -0
- package/dist/shared-resources.d.ts.map +1 -0
- package/dist/ts-libs.d.ts +98 -0
- package/dist/ts-libs.d.ts.map +1 -0
- package/dist/typechecker.d.ts +127 -0
- package/dist/typechecker.d.ts.map +1 -0
- package/package.json +64 -0
- package/src/bundler.ts +513 -0
- package/src/commands.ts +733 -0
- package/src/fs.ts +935 -0
- package/src/index.ts +149 -0
- package/src/internal.ts +116 -0
- package/src/loader.ts +229 -0
- package/src/packages.ts +936 -0
- package/src/react.tsx +331 -0
- package/src/sandbox-manager.ts +490 -0
- package/src/sandbox.ts +402 -0
- package/src/shared-modules.ts +210 -0
- package/src/shared-resources.ts +169 -0
- package/src/ts-libs.ts +320 -0
- package/src/typechecker.ts +635 -0
package/README.md
ADDED
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
# Sandlot
|
|
2
|
+
|
|
3
|
+
A browser-based TypeScript sandbox for AI agent-driven code generation.
|
|
4
|
+
|
|
5
|
+
San dy 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
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install sandlot
|
|
20
|
+
# or
|
|
21
|
+
bun add sandlot
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { createSandbox, loadModule } from "sandlot";
|
|
28
|
+
|
|
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
|
+
});
|
|
39
|
+
|
|
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;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Build
|
|
55
|
+
const result = await sandbox.bash.exec("build /src/index.ts");
|
|
56
|
+
unsubscribe();
|
|
57
|
+
|
|
58
|
+
if (result.exitCode === 0 && bundle) {
|
|
59
|
+
const mod = await loadModule<{ greet: (name: string) => string }>(bundle);
|
|
60
|
+
console.log(mod.greet("World")); // "Hello, World!"
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
sandbox.close();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Sandbox API
|
|
67
|
+
|
|
68
|
+
The Sandbox API provides direct access to the filesystem and bash shell.
|
|
69
|
+
|
|
70
|
+
### Creating a Sandbox
|
|
71
|
+
|
|
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
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
// Write files using bash
|
|
97
|
+
await sandbox.bash.exec('echo "export const x = 1;" > /src/index.ts');
|
|
98
|
+
|
|
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;
|
|
107
|
+
});
|
|
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
|
+
```
|
|
125
|
+
|
|
126
|
+
### Build Command Options
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
build <entry> [options]
|
|
130
|
+
|
|
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
|
+
```
|
|
139
|
+
|
|
140
|
+
### Sandbox Events
|
|
141
|
+
|
|
142
|
+
```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();
|
|
166
|
+
```
|
|
167
|
+
|
|
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" });
|
|
185
|
+
|
|
186
|
+
// Run operations in parallel
|
|
187
|
+
await Promise.all([sandbox1.bash.exec("build"), sandbox2.bash.exec("build")]);
|
|
188
|
+
|
|
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
|
|
200
|
+
|
|
201
|
+
```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[];
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## React Integration
|
|
227
|
+
|
|
228
|
+
Sandlot solves the "multiple React instances" problem by letting dynamic components use your host application's React.
|
|
229
|
+
|
|
230
|
+
### Setup
|
|
231
|
+
|
|
232
|
+
```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,
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Generating React Components
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { createSandbox, loadModule, BundleResult } from "sandlot";
|
|
249
|
+
|
|
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"],
|
|
264
|
+
});
|
|
265
|
+
|
|
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;
|
|
297
|
+
});
|
|
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
|
+
```
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## Package Management
|
|
381
|
+
|
|
382
|
+
Sandlot installs packages via esm.sh CDN and fetches TypeScript type definitions.
|
|
383
|
+
|
|
384
|
+
### Installing Packages
|
|
385
|
+
|
|
386
|
+
```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
|
|
396
|
+
|
|
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);
|
|
433
|
+
|
|
434
|
+
// Check what's exported
|
|
435
|
+
const names = await getExportNames(result.bundle); // ["add", "multiply", "default"]
|
|
436
|
+
const hasAdd = await hasExport(result.bundle, "add"); // true
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## Advanced Usage
|
|
442
|
+
|
|
443
|
+
### Direct Bundler Access
|
|
444
|
+
|
|
445
|
+
```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
|
|
467
|
+
|
|
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
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
if (result.hasErrors) {
|
|
483
|
+
// Human-readable format
|
|
484
|
+
console.log(formatDiagnostics(result.diagnostics));
|
|
485
|
+
|
|
486
|
+
// Agent-friendly format
|
|
487
|
+
console.log(formatDiagnosticsForAgent(result.diagnostics));
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Custom Filesystem
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
import { createIndexedDbFs, IndexedDbFs } from "sandlot";
|
|
495
|
+
|
|
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
|
+
});
|
|
502
|
+
|
|
503
|
+
// In-memory filesystem
|
|
504
|
+
const memFs = IndexedDbFs.createInMemory({
|
|
505
|
+
initialFiles: { "/README.md": "# Hello" },
|
|
506
|
+
});
|
|
507
|
+
|
|
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");
|
|
514
|
+
|
|
515
|
+
// Persistence
|
|
516
|
+
if (fs.isDirty()) {
|
|
517
|
+
await fs.save();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
fs.close();
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Shared Resources
|
|
524
|
+
|
|
525
|
+
Share TypeScript libs and bundler across multiple sandboxes:
|
|
526
|
+
|
|
527
|
+
```typescript
|
|
528
|
+
import { createSharedResources, getDefaultResources } from "sandlot";
|
|
529
|
+
|
|
530
|
+
// Create custom resources
|
|
531
|
+
const resources = await createSharedResources({
|
|
532
|
+
libs: ["ES2022", "DOM", "DOM.Iterable"],
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
// Use with sandbox
|
|
536
|
+
const sandbox = await createSandbox({
|
|
537
|
+
resources,
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
// Or use the default singleton
|
|
541
|
+
const defaultResources = await getDefaultResources();
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## TypeScript Configuration
|
|
547
|
+
|
|
548
|
+
Sandlot respects your `tsconfig.json`:
|
|
549
|
+
|
|
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
|
+
```
|
|
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";
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### Exports from `sandlot/react`
|
|
599
|
+
|
|
600
|
+
```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";
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
---
|
|
607
|
+
|
|
608
|
+
## Requirements
|
|
609
|
+
|
|
610
|
+
- Modern browser with ES2020 support
|
|
611
|
+
- IndexedDB (for persistent filesystems)
|
|
612
|
+
- WebAssembly (for esbuild)
|
|
613
|
+
|
|
614
|
+
## License
|
|
615
|
+
|
|
616
|
+
MIT
|