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
@@ -1,409 +0,0 @@
1
- /**
2
- * SandboxManager - Manages shared resources for multiple concurrent sandboxes.
3
- *
4
- * When running many sandboxes concurrently (e.g., for multiple AI agents),
5
- * this manager ensures heavy resources are loaded once and shared:
6
- *
7
- * - TypeScript lib files (~5MB) - loaded once, shared across all sandboxes
8
- * - esbuild WASM (~10MB) - already singleton, but pre-initialized here
9
- *
10
- * Usage:
11
- * ```ts
12
- * const manager = await createSandboxManager();
13
- *
14
- * // Create multiple sandboxes - all share the same libs and bundler
15
- * const sandbox1 = await manager.createSandbox({ ... });
16
- * const sandbox2 = await manager.createSandbox({ ... });
17
- *
18
- * // ... use sandboxes ...
19
- *
20
- * // Clean up
21
- * manager.destroyAll();
22
- * ```
23
- */
24
-
25
- import { Bash, defineCommand } from "just-bash/browser";
26
- import { IndexedDbFs, type IndexedDbFsOptions } from "./fs";
27
- import { type BundleResult } from "./bundler";
28
- import { getDefaultBrowserLibs } from "./ts-libs";
29
- import {
30
- createSharedResources,
31
- type SharedResources,
32
- type SharedResourcesOptions,
33
- } from "./shared-resources";
34
- import { createDefaultCommands, type CommandDeps } from "./commands";
35
- import { BuildEmitter } from "./build-emitter";
36
- import type { Sandbox } from "./sandbox";
37
-
38
- /**
39
- * Options for creating a sandbox via the manager
40
- */
41
- export interface ManagedSandboxOptions {
42
- /**
43
- * Unique identifier for this sandbox.
44
- *
45
- * Also used as the IndexedDB database name if `fsOptions.dbName` is not provided
46
- * and `inMemory` is false. If both are provided, `fsOptions.dbName` takes precedence.
47
- *
48
- * @default Auto-generated as "sandbox-1", "sandbox-2", etc.
49
- */
50
- id?: string;
51
-
52
- /**
53
- * Options for the IndexedDB filesystem.
54
- *
55
- * Note: `fsOptions.dbName` takes precedence over `id` for the database name.
56
- * If neither is provided, the auto-generated `id` is used.
57
- */
58
- fsOptions?: IndexedDbFsOptions;
59
-
60
- /**
61
- * Initial files to populate the filesystem with
62
- */
63
- initialFiles?: Record<string, string>;
64
-
65
- /**
66
- * Path to tsconfig.json in the virtual filesystem.
67
- * Default: "/tsconfig.json"
68
- */
69
- tsconfigPath?: string;
70
-
71
- /**
72
- * Callback invoked when a build succeeds.
73
- * Receives the bundle result with the compiled code.
74
- */
75
- onBuild?: (result: BundleResult) => void | Promise<void>;
76
-
77
- /**
78
- * Additional custom commands to add to the bash environment
79
- */
80
- customCommands?: ReturnType<typeof defineCommand>[];
81
-
82
- /**
83
- * If true, use in-memory filesystem only (no IndexedDB persistence).
84
- * Default: true
85
- */
86
- inMemory?: boolean;
87
-
88
- /**
89
- * Module IDs that should be resolved from the host's SharedModuleRegistry
90
- * instead of esm.sh CDN. Overrides manager-level sharedModules for this sandbox.
91
- *
92
- * @see SandboxOptions.sharedModules for full documentation
93
- */
94
- sharedModules?: string[];
95
- }
96
-
97
- /**
98
- * A sandbox instance managed by the SandboxManager.
99
- *
100
- * Extends the base Sandbox interface with an `id` property for
101
- * identification within the manager. This ensures ManagedSandbox
102
- * has full feature parity with Sandbox.
103
- */
104
- export interface ManagedSandbox extends Sandbox {
105
- /**
106
- * Unique identifier for this sandbox within the manager.
107
- */
108
- id: string;
109
- }
110
-
111
- /**
112
- * Statistics about the sandbox manager
113
- */
114
- export interface SandboxManagerStats {
115
- /**
116
- * Whether shared resources have been initialized
117
- */
118
- initialized: boolean;
119
-
120
- /**
121
- * Number of currently active sandboxes
122
- */
123
- activeSandboxes: number;
124
-
125
- /**
126
- * Number of TypeScript lib files loaded
127
- */
128
- libFilesCount: number;
129
-
130
- /**
131
- * IDs of active sandboxes
132
- */
133
- sandboxIds: string[];
134
- }
135
-
136
- /**
137
- * Options for creating a SandboxManager
138
- */
139
- export interface SandboxManagerOptions extends SharedResourcesOptions {
140
- /**
141
- * TypeScript libs to load. Defaults to browser libs (ES2020 + DOM).
142
- */
143
- libs?: string[];
144
-
145
- /**
146
- * Default shared modules for all sandboxes created by this manager.
147
- * Individual sandboxes can override with their own sharedModules option.
148
- *
149
- * Module IDs that should be resolved from the host's SharedModuleRegistry
150
- * instead of esm.sh CDN. The host must have registered these modules
151
- * using `registerSharedModules()` before loading dynamic code.
152
- *
153
- * @example
154
- * ```ts
155
- * const manager = await createSandboxManager({
156
- * sharedModules: ['react', 'react-dom/client'],
157
- * });
158
- * ```
159
- */
160
- sharedModules?: string[];
161
- }
162
-
163
- /**
164
- * Manager for creating and managing multiple sandboxes with shared resources.
165
- */
166
- export class SandboxManager {
167
- private resources: SharedResources | null = null;
168
- private sandboxes: Map<string, ManagedSandbox> = new Map();
169
- private initialized = false;
170
- private initPromise: Promise<void> | null = null;
171
- private nextId = 1;
172
- private options: SandboxManagerOptions;
173
-
174
- constructor(options: SandboxManagerOptions = {}) {
175
- this.options = {
176
- libs: options.libs ?? getDefaultBrowserLibs(),
177
- ...options,
178
- };
179
- }
180
-
181
- /**
182
- * Initialize shared resources (libs and bundler).
183
- * Called automatically on first sandbox creation, but can be called
184
- * explicitly to pre-warm.
185
- */
186
- async initialize(): Promise<void> {
187
- if (this.initialized) return;
188
-
189
- if (this.initPromise) {
190
- await this.initPromise;
191
- return;
192
- }
193
-
194
- this.initPromise = this.doInitialize();
195
- await this.initPromise;
196
- this.initialized = true;
197
- }
198
-
199
- private async doInitialize(): Promise<void> {
200
- this.resources = await createSharedResources({
201
- libs: this.options.libs,
202
- skipLibs: this.options.skipLibs,
203
- skipBundler: this.options.skipBundler,
204
- });
205
- }
206
-
207
- /**
208
- * Create a new managed sandbox.
209
- * Shares TypeScript libs and bundler with all other sandboxes.
210
- */
211
- async createSandbox(options: ManagedSandboxOptions = {}): Promise<ManagedSandbox> {
212
- // Ensure resources are initialized
213
- await this.initialize();
214
-
215
- const {
216
- id = `sandbox-${this.nextId++}`,
217
- fsOptions = {},
218
- initialFiles,
219
- tsconfigPath = "/tsconfig.json",
220
- onBuild,
221
- customCommands = [],
222
- inMemory = true,
223
- sharedModules = this.options.sharedModules,
224
- } = options;
225
-
226
- // Create filesystem
227
- let fs: IndexedDbFs;
228
- if (inMemory) {
229
- fs = IndexedDbFs.createInMemory({
230
- initialFiles,
231
- maxSizeBytes: fsOptions.maxSizeBytes,
232
- });
233
- } else {
234
- fs = await IndexedDbFs.create({
235
- dbName: fsOptions.dbName ?? id,
236
- initialFiles,
237
- maxSizeBytes: fsOptions.maxSizeBytes,
238
- });
239
- }
240
-
241
- // Create build event emitter
242
- const buildEmitter = new BuildEmitter();
243
-
244
- // If a callback was provided in options, subscribe it
245
- if (onBuild) {
246
- buildEmitter.on(onBuild);
247
- }
248
-
249
- // Create commands using shared resources
250
- const commandDeps: CommandDeps = {
251
- fs,
252
- libFiles: this.resources!.libFiles,
253
- tsconfigPath,
254
- onBuild: buildEmitter.emit,
255
- typesCache: this.resources!.typesCache,
256
- sharedModules,
257
- };
258
- const defaultCommands = createDefaultCommands(commandDeps);
259
-
260
- // Create bash environment
261
- const bash = new Bash({
262
- fs,
263
- cwd: "/",
264
- customCommands: [...defaultCommands, ...customCommands],
265
- });
266
-
267
- const sandbox: ManagedSandbox = {
268
- id,
269
- fs,
270
- bash,
271
- isDirty: () => fs.isDirty(),
272
- save: () => fs.save(),
273
- close: () => {
274
- fs.close();
275
- this.sandboxes.delete(id);
276
- },
277
- onBuild: (callback) => buildEmitter.on(callback),
278
- };
279
-
280
- this.sandboxes.set(id, sandbox);
281
- return sandbox;
282
- }
283
-
284
- /**
285
- * Get a sandbox by ID
286
- */
287
- getSandbox(id: string): ManagedSandbox | undefined {
288
- return this.sandboxes.get(id);
289
- }
290
-
291
- /**
292
- * Get all active sandboxes
293
- */
294
- getAllSandboxes(): ManagedSandbox[] {
295
- return Array.from(this.sandboxes.values());
296
- }
297
-
298
- /**
299
- * Close a specific sandbox
300
- */
301
- closeSandbox(id: string): boolean {
302
- const sandbox = this.sandboxes.get(id);
303
- if (sandbox) {
304
- sandbox.close();
305
- return true;
306
- }
307
- return false;
308
- }
309
-
310
- /**
311
- * Close all sandboxes and release resources
312
- */
313
- destroyAll(): void {
314
- for (const sandbox of this.sandboxes.values()) {
315
- sandbox.fs.close();
316
- }
317
- this.sandboxes.clear();
318
- }
319
-
320
- /**
321
- * Save all sandboxes that have unsaved changes.
322
- *
323
- * @returns Map of sandbox ID to save result (true if saved, false if nothing to save)
324
- *
325
- * @example
326
- * ```ts
327
- * const results = await manager.saveAll();
328
- * for (const [id, saved] of results) {
329
- * console.log(`${id}: ${saved ? 'saved' : 'no changes'}`);
330
- * }
331
- * ```
332
- */
333
- async saveAll(): Promise<Map<string, boolean>> {
334
- const results = new Map<string, boolean>();
335
- for (const [id, sandbox] of this.sandboxes) {
336
- const saved = await sandbox.save();
337
- results.set(id, saved);
338
- }
339
- return results;
340
- }
341
-
342
- /**
343
- * Get IDs of sandboxes with unsaved changes.
344
- *
345
- * @example
346
- * ```ts
347
- * const dirtyIds = manager.getDirtySandboxes();
348
- * if (dirtyIds.length > 0) {
349
- * console.log('Unsaved sandboxes:', dirtyIds.join(', '));
350
- * }
351
- * ```
352
- */
353
- getDirtySandboxes(): string[] {
354
- return Array.from(this.sandboxes.entries())
355
- .filter(([_, sandbox]) => sandbox.isDirty())
356
- .map(([id]) => id);
357
- }
358
-
359
- /**
360
- * Get statistics about the manager
361
- */
362
- getStats(): SandboxManagerStats {
363
- return {
364
- initialized: this.initialized,
365
- activeSandboxes: this.sandboxes.size,
366
- libFilesCount: this.resources?.libFiles.size ?? 0,
367
- sandboxIds: Array.from(this.sandboxes.keys()),
368
- };
369
- }
370
-
371
- /**
372
- * Get the shared resources (for advanced use cases)
373
- */
374
- getResources(): SharedResources | null {
375
- return this.resources;
376
- }
377
- }
378
-
379
- /**
380
- * Create a new SandboxManager instance.
381
- *
382
- * @example
383
- * ```ts
384
- * const manager = await createSandboxManager();
385
- *
386
- * // Create multiple sandboxes concurrently
387
- * const [sandbox1, sandbox2] = await Promise.all([
388
- * manager.createSandbox({ id: "agent-1", initialFiles: { ... } }),
389
- * manager.createSandbox({ id: "agent-2", initialFiles: { ... } }),
390
- * ]);
391
- *
392
- * // Run operations in parallel
393
- * const [result1, result2] = await Promise.all([
394
- * sandbox1.bash.exec("build"),
395
- * sandbox2.bash.exec("build"),
396
- * ]);
397
- *
398
- * // Clean up
399
- * manager.destroyAll();
400
- * ```
401
- */
402
- export async function createSandboxManager(
403
- options: SandboxManagerOptions = {}
404
- ): Promise<SandboxManager> {
405
- const manager = new SandboxManager(options);
406
- await manager.initialize();
407
- return manager;
408
- }
409
-