sandlot 0.1.3 → 0.2.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.
Files changed (115) hide show
  1. package/dist/browser/bundler.d.ts +68 -0
  2. package/dist/browser/bundler.d.ts.map +1 -0
  3. package/dist/browser/executor.d.ts +46 -0
  4. package/dist/browser/executor.d.ts.map +1 -0
  5. package/dist/browser/index.d.ts +9 -0
  6. package/dist/browser/index.d.ts.map +1 -0
  7. package/dist/browser/index.js +2692 -0
  8. package/dist/browser/preset.d.ts +63 -0
  9. package/dist/browser/preset.d.ts.map +1 -0
  10. package/dist/commands/index.d.ts +20 -11
  11. package/dist/commands/index.d.ts.map +1 -1
  12. package/dist/commands/types.d.ts +31 -132
  13. package/dist/commands/types.d.ts.map +1 -1
  14. package/dist/core/bundler-utils.d.ts +142 -0
  15. package/dist/core/bundler-utils.d.ts.map +1 -0
  16. package/dist/core/esm-types-resolver.d.ts +125 -0
  17. package/dist/core/esm-types-resolver.d.ts.map +1 -0
  18. package/dist/core/executor.d.ts +35 -0
  19. package/dist/core/executor.d.ts.map +1 -0
  20. package/dist/{fs.d.ts → core/fs.d.ts} +27 -29
  21. package/dist/core/fs.d.ts.map +1 -0
  22. package/dist/core/sandbox.d.ts +30 -0
  23. package/dist/core/sandbox.d.ts.map +1 -0
  24. package/dist/core/sandlot.d.ts +30 -0
  25. package/dist/core/sandlot.d.ts.map +1 -0
  26. package/dist/core/shared-module-registry.d.ts +46 -0
  27. package/dist/core/shared-module-registry.d.ts.map +1 -0
  28. package/dist/core/typechecker.d.ts +60 -0
  29. package/dist/core/typechecker.d.ts.map +1 -0
  30. package/dist/index.d.ts +11 -16
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +1405 -2049
  33. package/dist/node/bundler.d.ts +48 -0
  34. package/dist/node/bundler.d.ts.map +1 -0
  35. package/dist/node/executor.d.ts +48 -0
  36. package/dist/node/executor.d.ts.map +1 -0
  37. package/dist/node/index.d.ts +9 -0
  38. package/dist/node/index.d.ts.map +1 -0
  39. package/dist/node/index.js +2646 -0
  40. package/dist/node/preset.d.ts +62 -0
  41. package/dist/node/preset.d.ts.map +1 -0
  42. package/dist/types.d.ts +525 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/package.json +27 -8
  45. package/src/browser/bundler.ts +294 -0
  46. package/src/browser/executor.ts +71 -0
  47. package/src/browser/index.ts +57 -0
  48. package/src/browser/preset.ts +179 -0
  49. package/src/commands/index.ts +526 -43
  50. package/src/commands/types.ts +82 -146
  51. package/src/core/bundler-utils.ts +630 -0
  52. package/src/core/esm-types-resolver.ts +432 -0
  53. package/src/core/executor.ts +161 -0
  54. package/src/{fs.ts → core/fs.ts} +59 -37
  55. package/src/core/sandbox.ts +621 -0
  56. package/src/core/sandlot.ts +77 -0
  57. package/src/core/shared-module-registry.ts +138 -0
  58. package/src/core/typechecker.ts +607 -0
  59. package/src/index.ts +104 -139
  60. package/src/node/bundler.ts +194 -0
  61. package/src/node/executor.ts +87 -0
  62. package/src/node/index.ts +39 -0
  63. package/src/node/preset.ts +178 -0
  64. package/src/types.ts +668 -0
  65. package/README.md +0 -243
  66. package/dist/build-emitter.d.ts +0 -47
  67. package/dist/build-emitter.d.ts.map +0 -1
  68. package/dist/builder.d.ts +0 -370
  69. package/dist/builder.d.ts.map +0 -1
  70. package/dist/bundler.d.ts +0 -148
  71. package/dist/bundler.d.ts.map +0 -1
  72. package/dist/commands/compile.d.ts +0 -13
  73. package/dist/commands/compile.d.ts.map +0 -1
  74. package/dist/commands/packages.d.ts +0 -17
  75. package/dist/commands/packages.d.ts.map +0 -1
  76. package/dist/commands/run.d.ts +0 -40
  77. package/dist/commands/run.d.ts.map +0 -1
  78. package/dist/commands.d.ts +0 -179
  79. package/dist/commands.d.ts.map +0 -1
  80. package/dist/fs.d.ts.map +0 -1
  81. package/dist/internal.d.ts +0 -79
  82. package/dist/internal.d.ts.map +0 -1
  83. package/dist/internal.js +0 -1976
  84. package/dist/loader.d.ts +0 -164
  85. package/dist/loader.d.ts.map +0 -1
  86. package/dist/packages.d.ts +0 -199
  87. package/dist/packages.d.ts.map +0 -1
  88. package/dist/runner.d.ts +0 -314
  89. package/dist/runner.d.ts.map +0 -1
  90. package/dist/sandbox-manager.d.ts +0 -261
  91. package/dist/sandbox-manager.d.ts.map +0 -1
  92. package/dist/sandbox.d.ts +0 -267
  93. package/dist/sandbox.d.ts.map +0 -1
  94. package/dist/shared-modules.d.ts +0 -148
  95. package/dist/shared-modules.d.ts.map +0 -1
  96. package/dist/shared-resources.d.ts +0 -102
  97. package/dist/shared-resources.d.ts.map +0 -1
  98. package/dist/ts-libs.d.ts +0 -98
  99. package/dist/ts-libs.d.ts.map +0 -1
  100. package/dist/typechecker.d.ts +0 -127
  101. package/dist/typechecker.d.ts.map +0 -1
  102. package/src/build-emitter.ts +0 -64
  103. package/src/builder.ts +0 -498
  104. package/src/bundler.ts +0 -542
  105. package/src/commands/compile.ts +0 -236
  106. package/src/commands/packages.ts +0 -154
  107. package/src/commands/run.ts +0 -245
  108. package/src/internal.ts +0 -119
  109. package/src/loader.ts +0 -229
  110. package/src/packages.ts +0 -936
  111. package/src/sandbox.ts +0 -396
  112. package/src/shared-modules.ts +0 -280
  113. package/src/shared-resources.ts +0 -166
  114. package/src/ts-libs.ts +0 -320
  115. package/src/typechecker.ts +0 -635
package/src/builder.ts DELETED
@@ -1,498 +0,0 @@
1
- /**
2
- * High-level builder for sandbox-based agent workflows.
3
- *
4
- * Provides a simple API that handles sandbox creation, build result capture,
5
- * validation, and timeout/cancellation—letting the caller focus on their agent logic.
6
- */
7
-
8
- import { createSandbox, type Sandbox, type SandboxOptions } from "./sandbox";
9
- import type { BundleResult } from "./bundler";
10
- import type { BuildOutput } from "./commands/types";
11
-
12
- /**
13
- * Result of running a builder.
14
- *
15
- * @typeParam T - The return type of the build function
16
- * @typeParam M - The type of the validated module (defaults to Record<string, unknown>)
17
- */
18
- export interface BuildResult<T, M = Record<string, unknown>> {
19
- /**
20
- * Whatever the `build` function returned, or `undefined` if it threw.
21
- */
22
- result: T | undefined;
23
-
24
- /**
25
- * The error thrown by the `build` function, or `null` if it succeeded.
26
- *
27
- * When an error occurs, the bundle and module may still be available
28
- * if a build succeeded before the error was thrown.
29
- */
30
- error: Error | null;
31
-
32
- /**
33
- * The last successful build result, or null if no build succeeded.
34
- *
35
- * This captures the bundle from any successful `build` command executed
36
- * during the run. If multiple builds succeed, this contains the last one.
37
- *
38
- * **Important**: This is available even if the `build` function threw an error,
39
- * as long as a build succeeded before the error occurred.
40
- */
41
- bundle: BundleResult | null;
42
-
43
- /**
44
- * The loaded module exports, or null if no bundle was produced.
45
- *
46
- * When a build succeeds, the bundle is automatically loaded. If a `validate`
47
- * function was provided, the module is passed through it and the result is
48
- * available here with the validated type.
49
- *
50
- * @example Without validation
51
- * ```ts
52
- * const result = await runAgent("Create a counter");
53
- * result.module // Record<string, unknown> | null
54
- * ```
55
- *
56
- * @example With validation
57
- * ```ts
58
- * const result = await runAgent("Create a counter", {
59
- * validate: (mod) => ({ App: mod.App as React.ComponentType }),
60
- * });
61
- * result.module // { App: React.ComponentType } | null
62
- * ```
63
- */
64
- module: M | null;
65
-
66
- /**
67
- * The sandbox that was used.
68
- *
69
- * Useful when the builder created an ephemeral sandbox—you can inspect
70
- * its state, save it, or reuse it for another build.
71
- */
72
- sandbox: Sandbox;
73
- }
74
-
75
- /**
76
- * Options passed when calling the builder function.
77
- *
78
- * @typeParam M - The type returned by the validate function (if provided)
79
- */
80
- export interface BuildCallOptions<M = Record<string, unknown>> {
81
- /**
82
- * Validation function for the built module.
83
- *
84
- * When provided, this function runs as part of the build command—after
85
- * bundling and loading, but before the build is considered successful.
86
- * If validation throws, the build fails and the agent sees the error,
87
- * giving it a chance to fix the code and try again.
88
- *
89
- * The return type determines the type of `result.module`.
90
- *
91
- * @example Simple validation
92
- * ```ts
93
- * const result = await runAgent("Create a counter", {
94
- * validate: (mod) => {
95
- * if (typeof mod.App !== 'function') {
96
- * throw new Error("Module must export an App component");
97
- * }
98
- * return { App: mod.App as React.ComponentType };
99
- * },
100
- * });
101
- * // If validation fails, agent sees: "Build failed: Validation error."
102
- * // Agent can fix the code and try again
103
- * result.module?.App // React.ComponentType (after successful build)
104
- * ```
105
- *
106
- * @example With Zod
107
- * ```ts
108
- * import { z } from 'zod';
109
- *
110
- * const CounterSchema = z.object({
111
- * App: z.custom<React.ComponentType>((v) => typeof v === 'function'),
112
- * initialCount: z.number().optional(),
113
- * });
114
- *
115
- * const result = await runAgent("Create a counter", {
116
- * validate: (mod) => CounterSchema.parse(mod),
117
- * });
118
- * result.module?.App // React.ComponentType
119
- * result.module?.initialCount // number | undefined
120
- * ```
121
- */
122
- validate?: (module: Record<string, unknown>) => M;
123
-
124
- /**
125
- * Maximum time in milliseconds for the build to complete.
126
- *
127
- * If the timeout is exceeded, the build is aborted and an error is thrown.
128
- * The agent's partial work may still be available in the sandbox.
129
- *
130
- * @example
131
- * ```ts
132
- * const result = await runAgent("Create a complex dashboard", {
133
- * timeout: 300_000, // 5 minutes
134
- * });
135
- * ```
136
- */
137
- timeout?: number;
138
-
139
- /**
140
- * AbortSignal for cancelling the build.
141
- *
142
- * If the signal is aborted, the build stops and an error is thrown.
143
- * Useful for user-initiated cancellation or external timeout management.
144
- *
145
- * @example
146
- * ```ts
147
- * const controller = new AbortController();
148
- *
149
- * // Start the build
150
- * const promise = runAgent("Create a counter", {
151
- * signal: controller.signal,
152
- * });
153
- *
154
- * // Cancel after 10 seconds
155
- * setTimeout(() => controller.abort(), 10_000);
156
- *
157
- * try {
158
- * const result = await promise;
159
- * } catch (err) {
160
- * if (err.name === 'AbortError') {
161
- * console.log('Build was cancelled');
162
- * }
163
- * }
164
- * ```
165
- */
166
- signal?: AbortSignal;
167
- }
168
-
169
- /**
170
- * Options for creating a reusable builder function.
171
- */
172
- export interface CreateBuilderOptions<T> {
173
- /**
174
- * Existing sandbox to reuse across all calls.
175
- *
176
- * When provided, the same sandbox is used for every call to the returned
177
- * builder function. Files and state persist between runs—useful for
178
- * iterative workflows where you want to build on previous work.
179
- *
180
- * @example
181
- * ```ts
182
- * const sandbox = await createSandbox({
183
- * initialFiles: { '/src/utils.ts': 'export const PI = 3.14;' },
184
- * });
185
- *
186
- * const runAgent = createBuilder({
187
- * sandbox, // Reused across all calls
188
- * build: async (sb, prompt) => myAgent.run(sb, prompt),
189
- * });
190
- *
191
- * await runAgent("Create a circle component"); // Uses existing utils.ts
192
- * await runAgent("Add a square component"); // Still has circle + utils
193
- * ```
194
- */
195
- sandbox?: Sandbox;
196
-
197
- /**
198
- * Options for creating fresh sandboxes (only used if `sandbox` is not provided).
199
- *
200
- * Each call to the returned builder function creates a new sandbox with
201
- * these options. Use this when you want isolated runs.
202
- *
203
- * @example
204
- * ```ts
205
- * const runAgent = createBuilder({
206
- * sandboxOptions: {
207
- * sharedModules: ['react', 'react-dom/client'],
208
- * },
209
- * build: async (sandbox, prompt) => myAgent.run(sandbox, prompt),
210
- * });
211
- *
212
- * // Each call gets a fresh sandbox
213
- * await runAgent("Create a counter"); // Fresh sandbox
214
- * await runAgent("Create a todo list"); // Another fresh sandbox
215
- * ```
216
- */
217
- sandboxOptions?: SandboxOptions;
218
-
219
- /**
220
- * The function to build with the sandbox and prompt.
221
- *
222
- * This receives the sandbox and the prompt string, and should return
223
- * whatever result your agent produces.
224
- *
225
- * @example
226
- * ```ts
227
- * const runAgent = createBuilder({
228
- * build: async (sandbox, prompt) => {
229
- * const agent = new MyAgent({
230
- * tools: { bash: sandbox.bash.exec },
231
- * });
232
- * return agent.run(prompt);
233
- * },
234
- * });
235
- * ```
236
- */
237
- build: (sandbox: Sandbox, prompt: string) => Promise<T>;
238
- }
239
-
240
- /**
241
- * The builder function returned by `createBuilder`.
242
- *
243
- * Can be called with just a prompt, or with options including validation,
244
- * timeout, and abort signal.
245
- */
246
- export interface BuilderFn<T> {
247
- /**
248
- * Run the builder with a prompt (no options).
249
- * Module will be typed as `Record<string, unknown>`.
250
- */
251
- (prompt: string): Promise<BuildResult<T>>;
252
-
253
- /**
254
- * Run the builder with a prompt and options.
255
- * If `validate` is provided, module will be typed based on its return type.
256
- */
257
- <M = Record<string, unknown>>(prompt: string, options: BuildCallOptions<M>): Promise<BuildResult<T, M>>;
258
- }
259
-
260
- /**
261
- * Create a reusable builder function for running prompts in a sandbox.
262
- *
263
- * This is the main API for sandlot. Define your agent logic once, then call
264
- * the returned function with different prompts. Each call:
265
- *
266
- * 1. Creates a sandbox (or uses one you provide)
267
- * 2. Sets up build result capture
268
- * 3. Runs your build function with the prompt
269
- * 4. Returns everything: your result, the bundle, the module, and the sandbox
270
- *
271
- * **Sandbox behavior:**
272
- * - If you provide `sandbox`: The same sandbox is reused for every call
273
- * (files persist, good for iteration)
274
- * - If you provide `sandboxOptions` (or nothing): A fresh sandbox is created
275
- * for each call (isolated runs)
276
- *
277
- * **Validation behavior:**
278
- * When you provide a `validate` function, it runs as part of the build command.
279
- * If validation fails, the build fails and the agent sees the error—giving it
280
- * a chance to fix the code and try again. This is more powerful than post-hoc
281
- * validation because the agent can iterate on validation errors.
282
- *
283
- * @example Basic usage
284
- * ```ts
285
- * import { createBuilder } from 'sandlot';
286
- *
287
- * const runAgent = createBuilder({
288
- * sandboxOptions: { sharedModules: ['react', 'react-dom/client'] },
289
- * build: async (sandbox, prompt) => {
290
- * const agent = new MyAgent({ tools: { bash: sandbox.bash.exec } });
291
- * return agent.run(prompt);
292
- * },
293
- * });
294
- *
295
- * // Use it multiple times with different prompts
296
- * const result1 = await runAgent("Create a counter component");
297
- * const result2 = await runAgent("Create a todo list");
298
- *
299
- * if (result1.module?.App) {
300
- * const Counter = result1.module.App as React.ComponentType;
301
- * }
302
- * ```
303
- *
304
- * @example With validation (agent can fix validation errors)
305
- * ```ts
306
- * const runAgent = createBuilder({
307
- * sandboxOptions: { sharedModules: ['react'] },
308
- * build: async (sandbox, prompt) => myAgent.run(sandbox, prompt),
309
- * });
310
- *
311
- * // Validation runs during build - agent sees errors and can fix them
312
- * const counter = await runAgent("Create a counter", {
313
- * validate: (mod) => {
314
- * if (typeof mod.App !== 'function') {
315
- * throw new Error("Must export an App component");
316
- * }
317
- * return { App: mod.App as React.ComponentType };
318
- * },
319
- * });
320
- * // If agent's first attempt fails validation, it sees:
321
- * // "Build failed: Validation error. Must export an App component"
322
- * // Agent can then fix the code and run build again
323
- * ```
324
- *
325
- * @example With Zod validation
326
- * ```ts
327
- * import { z } from 'zod';
328
- *
329
- * const CounterSchema = z.object({
330
- * App: z.custom<React.ComponentType>((v) => typeof v === 'function'),
331
- * initialCount: z.number().default(0),
332
- * });
333
- *
334
- * const result = await runAgent("Create a counter", {
335
- * validate: (mod) => CounterSchema.parse(mod),
336
- * });
337
- * // result.module is fully typed from Zod inference
338
- * ```
339
- *
340
- * @example Iterative workflow with shared sandbox
341
- * ```ts
342
- * const sandbox = await createSandbox();
343
- *
344
- * const runAgent = createBuilder({
345
- * sandbox, // Same sandbox for all calls
346
- * build: async (sb, prompt) => myAgent.run(sb, prompt),
347
- * });
348
- *
349
- * // First prompt creates initial component
350
- * await runAgent("Create a button component");
351
- *
352
- * // Second prompt can build on the first
353
- * await runAgent("Add a click counter to the button");
354
- * ```
355
- *
356
- * @example With timeout
357
- * ```ts
358
- * // Complex tasks get more time
359
- * const result = await runAgent("Create a full dashboard with charts", {
360
- * timeout: 300_000, // 5 minutes
361
- * });
362
- * ```
363
- *
364
- * @example With abort signal for cancellation
365
- * ```ts
366
- * const controller = new AbortController();
367
- *
368
- * // Start the build
369
- * const promise = runAgent("Create a counter", {
370
- * signal: controller.signal,
371
- * });
372
- *
373
- * // User clicks cancel button
374
- * cancelButton.onclick = () => controller.abort();
375
- *
376
- * try {
377
- * const result = await promise;
378
- * } catch (err) {
379
- * if (err.name === 'AbortError') {
380
- * console.log('Build was cancelled by user');
381
- * }
382
- * }
383
- * ```
384
- */
385
- export function createBuilder<T>(options: CreateBuilderOptions<T>): BuilderFn<T> {
386
- // Implementation handles both overloads
387
- return (async <M>(
388
- prompt: string,
389
- callOptions?: BuildCallOptions<M>
390
- ): Promise<BuildResult<T, M>> => {
391
- // Reuse provided sandbox, or create fresh each call
392
- const sandbox =
393
- options.sandbox ?? (await createSandbox(options.sandboxOptions));
394
-
395
- // Set validation function on sandbox before running agent
396
- // This makes validation part of the build command
397
- if (callOptions?.validate) {
398
- sandbox.setValidation(callOptions.validate as (mod: Record<string, unknown>) => Record<string, unknown>);
399
- }
400
-
401
- // Set up build capture - module is loaded during build command
402
- // Use object wrapper to avoid TypeScript narrowing issues with callbacks
403
- const captured: { output: BuildOutput | null } = { output: null };
404
- const unsubscribe = sandbox.onBuild((output) => {
405
- captured.output = output;
406
- });
407
-
408
- // Set up abort handling
409
- const { timeout, signal } = callOptions ?? {};
410
- let timeoutId: ReturnType<typeof setTimeout> | undefined;
411
- let abortController: AbortController | undefined;
412
-
413
- // Create a combined abort signal if timeout or signal is provided
414
- if (timeout !== undefined || signal !== undefined) {
415
- abortController = new AbortController();
416
-
417
- // Set up timeout
418
- if (timeout !== undefined) {
419
- timeoutId = setTimeout(() => {
420
- abortController!.abort(new Error(`Build timed out after ${timeout}ms`));
421
- }, timeout);
422
- }
423
-
424
- // Forward external signal to our controller
425
- if (signal !== undefined) {
426
- if (signal.aborted) {
427
- abortController.abort(signal.reason);
428
- } else {
429
- signal.addEventListener("abort", () => {
430
- abortController!.abort(signal.reason);
431
- }, { once: true });
432
- }
433
- }
434
- }
435
-
436
- let result: T | undefined;
437
- let error: Error | null = null;
438
-
439
- try {
440
- // Create the build promise
441
- const buildPromise = options.build(sandbox, prompt);
442
-
443
- // If we have an abort controller, race against it
444
- if (abortController) {
445
- const abortPromise = new Promise<never>((_, reject) => {
446
- abortController!.signal.addEventListener("abort", () => {
447
- const err = abortController!.signal.reason instanceof Error
448
- ? abortController!.signal.reason
449
- : new Error("Build aborted");
450
- err.name = "AbortError";
451
- reject(err);
452
- }, { once: true });
453
-
454
- // If already aborted, reject immediately
455
- if (abortController!.signal.aborted) {
456
- const err = abortController!.signal.reason instanceof Error
457
- ? abortController!.signal.reason
458
- : new Error("Build aborted");
459
- err.name = "AbortError";
460
- reject(err);
461
- }
462
- });
463
-
464
- result = await Promise.race([buildPromise, abortPromise]);
465
- } else {
466
- result = await buildPromise;
467
- }
468
- } catch (err) {
469
- // Capture the error but the module may still be available
470
- // if a build succeeded before the error was thrown
471
- error = err instanceof Error ? err : new Error(String(err));
472
- } finally {
473
- // Clean up timeout
474
- if (timeoutId !== undefined) {
475
- clearTimeout(timeoutId);
476
- }
477
-
478
- // Always clean up the subscription
479
- unsubscribe();
480
-
481
- // Always clear validation after the run
482
- if (callOptions?.validate) {
483
- sandbox.clearValidation();
484
- }
485
- }
486
-
487
- // Extract the build output
488
- const buildOutput = captured.output;
489
-
490
- return {
491
- result,
492
- error,
493
- bundle: buildOutput?.bundle ?? null,
494
- module: (buildOutput?.module ?? null) as M | null,
495
- sandbox,
496
- };
497
- }) as BuilderFn<T>;
498
- }