sandlot 0.1.2 → 0.1.4

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 (57) 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 +6 -2
  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 +300 -511
  23. package/dist/internal.js +161 -171
  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/ts-libs.d.ts +7 -20
  35. package/dist/ts-libs.d.ts.map +1 -1
  36. package/dist/typechecker.d.ts +1 -1
  37. package/package.json +5 -5
  38. package/src/build-emitter.ts +32 -29
  39. package/src/builder.ts +498 -0
  40. package/src/bundler.ts +76 -55
  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 +82 -221
  47. package/src/index.ts +17 -12
  48. package/src/sandbox.ts +219 -149
  49. package/src/shared-modules.ts +74 -4
  50. package/src/shared-resources.ts +0 -3
  51. package/src/ts-libs.ts +19 -121
  52. package/src/typechecker.ts +1 -1
  53. package/dist/react.d.ts +0 -159
  54. package/dist/react.d.ts.map +0 -1
  55. package/dist/react.js +0 -149
  56. package/src/commands.ts +0 -733
  57. package/src/sandbox-manager.ts +0 -409
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Types and utilities for sandbox bash commands.
3
+ */
4
+
5
+ import type { IFileSystem } from "just-bash/browser";
6
+ import type { BundleResult } from "../bundler";
7
+ import type { TypesCache } from "../packages";
8
+
9
+ /**
10
+ * The result of a successful build, including the bundle and loaded module.
11
+ */
12
+ export interface BuildOutput {
13
+ /**
14
+ * The compiled bundle (code, metadata, etc.)
15
+ */
16
+ bundle: BundleResult;
17
+
18
+ /**
19
+ * The loaded module exports.
20
+ * If validation was provided, this is the validated module.
21
+ */
22
+ module: Record<string, unknown>;
23
+ }
24
+
25
+ /**
26
+ * Validation function type for module validation.
27
+ * Takes the raw module exports and returns validated exports (or throws).
28
+ */
29
+ export type ValidateFn = (module: Record<string, unknown>) => Record<string, unknown>;
30
+
31
+ /**
32
+ * Dependencies required by command factories
33
+ */
34
+ export interface CommandDeps {
35
+ /**
36
+ * The virtual filesystem to operate on
37
+ */
38
+ fs: IFileSystem;
39
+
40
+ /**
41
+ * Pre-loaded TypeScript lib files for type checking
42
+ */
43
+ libFiles: Map<string, string>;
44
+
45
+ /**
46
+ * Path to tsconfig.json in the virtual filesystem
47
+ */
48
+ tsconfigPath: string;
49
+
50
+ /**
51
+ * Callback invoked when a build succeeds (after loading and validation).
52
+ */
53
+ onBuild?: (result: BuildOutput) => void | Promise<void>;
54
+
55
+ /**
56
+ * Getter for the current validation function.
57
+ * Called during build to check if validation should be performed.
58
+ */
59
+ getValidation?: () => ValidateFn | null;
60
+
61
+ /**
62
+ * Cache for package type definitions.
63
+ * When provided, avoids redundant network fetches for packages
64
+ * that have already been installed in other sandboxes.
65
+ */
66
+ typesCache?: TypesCache;
67
+
68
+ /**
69
+ * Options for the `run` command
70
+ */
71
+ runOptions?: RunOptions;
72
+
73
+ /**
74
+ * Module IDs that should be resolved from the host's SharedModuleRegistry
75
+ * instead of esm.sh CDN. The host must have registered these modules.
76
+ *
77
+ * Example: ['react', 'react-dom/client']
78
+ */
79
+ sharedModules?: string[];
80
+ }
81
+
82
+ /**
83
+ * Runtime context passed to the `main()` function when code is executed.
84
+ * This provides sandboxed code with access to sandbox capabilities.
85
+ */
86
+ export interface RunContext {
87
+ /**
88
+ * The virtual filesystem - read/write files within the sandbox
89
+ */
90
+ fs: IFileSystem;
91
+
92
+ /**
93
+ * Environment variables (configurable per-sandbox)
94
+ */
95
+ env: Record<string, string>;
96
+
97
+ /**
98
+ * Command-line arguments passed to `run`
99
+ */
100
+ args: string[];
101
+
102
+ /**
103
+ * Explicit logging function (alternative to console.log)
104
+ */
105
+ log: (...args: unknown[]) => void;
106
+
107
+ /**
108
+ * Explicit error logging function (alternative to console.error)
109
+ */
110
+ error: (...args: unknown[]) => void;
111
+ }
112
+
113
+ /**
114
+ * Options for configuring the `run` command behavior
115
+ */
116
+ export interface RunOptions {
117
+ /**
118
+ * Environment variables available via ctx.env
119
+ */
120
+ env?: Record<string, string>;
121
+
122
+ /**
123
+ * Maximum execution time in milliseconds (default: 30000 = 30s)
124
+ * Set to 0 to disable timeout.
125
+ */
126
+ timeout?: number;
127
+
128
+ /**
129
+ * Whether to skip type checking before running (default: false)
130
+ */
131
+ skipTypecheck?: boolean;
132
+ }
133
+
134
+ /**
135
+ * Result of running code via the `run` command
136
+ */
137
+ export interface RunResult {
138
+ /**
139
+ * Captured console output (log, warn, error)
140
+ */
141
+ logs: string[];
142
+
143
+ /**
144
+ * Return value from main() if present
145
+ */
146
+ returnValue?: unknown;
147
+
148
+ /**
149
+ * Execution time in milliseconds
150
+ */
151
+ executionTimeMs: number;
152
+ }
153
+
154
+ /**
155
+ * Format esbuild messages (warnings/errors) for display
156
+ */
157
+ export function formatEsbuildMessages(
158
+ messages: { text: string; location?: { file?: string; line?: number; column?: number } | null }[]
159
+ ): string {
160
+ if (messages.length === 0) return "";
161
+
162
+ return messages
163
+ .map((msg) => {
164
+ if (msg.location) {
165
+ const { file, line, column } = msg.location;
166
+ const loc = file ? `${file}${line ? `:${line}` : ""}${column ? `:${column}` : ""}` : "";
167
+ return loc ? `${loc}: ${msg.text}` : msg.text;
168
+ }
169
+ return msg.text;
170
+ })
171
+ .join("\n");
172
+ }
package/src/fs.ts CHANGED
@@ -48,85 +48,54 @@ const DEFAULT_SYMLINK_MODE = 0o777;
48
48
  const DEFAULT_MAX_SIZE_BYTES = 50 * 1024 * 1024; // 50MB default limit
49
49
 
50
50
  /**
51
- * Options for creating an IndexedDbFs instance
51
+ * Options for creating a Filesystem instance
52
52
  */
53
- export interface IndexedDbFsOptions {
54
- /** Database name in IndexedDB */
55
- dbName?: string;
53
+ export interface FilesystemOptions {
56
54
  /** Maximum total size in bytes (default: 50MB) */
57
55
  maxSizeBytes?: number;
58
- /** Initial files to populate (only used if DB is empty) */
56
+ /** Initial files to populate */
59
57
  initialFiles?: InitialFiles;
60
58
  }
61
59
 
62
60
  /**
63
- * In-memory filesystem with IndexedDB persistence.
64
- * All operations are fast (in-memory), persistence is manual via save().
61
+ * In-memory virtual filesystem for sandlot sandboxes.
62
+ *
63
+ * All operations are synchronous in-memory. Use `getFiles()` to export
64
+ * the current state for persistence, and `initialFiles` to restore.
65
+ *
66
+ * @example
67
+ * ```ts
68
+ * // Create filesystem
69
+ * const fs = Filesystem.create({ initialFiles: { '/src/index.ts': 'export const x = 1;' } });
70
+ *
71
+ * // Use filesystem
72
+ * await fs.writeFile('/src/app.ts', 'console.log("hello")');
73
+ *
74
+ * // Export for persistence
75
+ * const files = fs.getFiles();
76
+ * localStorage.setItem('my-project', JSON.stringify(files));
77
+ *
78
+ * // Later, restore
79
+ * const saved = JSON.parse(localStorage.getItem('my-project'));
80
+ * const fs2 = Filesystem.create({ initialFiles: saved });
81
+ * ```
65
82
  */
66
- export class IndexedDbFs implements IFileSystem {
83
+ export class Filesystem implements IFileSystem {
67
84
  private entries: Map<string, FsEntry>;
68
- private db: IDBDatabase | null = null;
69
- private dbName: string;
70
85
  private maxSizeBytes: number;
71
- private dirty = false;
72
86
 
73
87
  private constructor(
74
88
  entries: Map<string, FsEntry>,
75
- db: IDBDatabase | null,
76
- dbName: string,
77
89
  maxSizeBytes: number
78
90
  ) {
79
91
  this.entries = entries;
80
- this.db = db;
81
- this.dbName = dbName;
82
92
  this.maxSizeBytes = maxSizeBytes;
83
93
  }
84
94
 
85
95
  /**
86
- * Create and initialize a new IndexedDbFs instance
96
+ * Create a new Filesystem instance
87
97
  */
88
- static async create(options: IndexedDbFsOptions = {}): Promise<IndexedDbFs> {
89
- const dbName = options.dbName ?? "sandlot-fs";
90
- const maxSizeBytes = options.maxSizeBytes ?? DEFAULT_MAX_SIZE_BYTES;
91
-
92
- const db = await IndexedDbFs.openDatabase(dbName);
93
- const entries = await IndexedDbFs.loadEntries(db);
94
-
95
- // If empty and initialFiles provided, populate
96
- if (entries.size === 0) {
97
- // Always ensure root exists
98
- entries.set("/", {
99
- type: "directory",
100
- mode: DEFAULT_DIR_MODE,
101
- mtime: new Date(),
102
- });
103
-
104
- if (options.initialFiles) {
105
- for (const [path, value] of Object.entries(options.initialFiles)) {
106
- const normalizedPath = IndexedDbFs.normalizePath(path);
107
- const init = IndexedDbFs.parseFileInit(value);
108
-
109
- // Ensure parent directories exist
110
- IndexedDbFs.ensureParentDirs(entries, normalizedPath);
111
-
112
- entries.set(normalizedPath, {
113
- type: "file",
114
- content: init.content,
115
- mode: init.mode ?? DEFAULT_FILE_MODE,
116
- mtime: init.mtime ?? new Date(),
117
- });
118
- }
119
- }
120
- }
121
-
122
- const fs = new IndexedDbFs(entries, db, dbName, maxSizeBytes);
123
- return fs;
124
- }
125
-
126
- /**
127
- * Create an in-memory only instance (no IndexedDB)
128
- */
129
- static createInMemory(options: Omit<IndexedDbFsOptions, "dbName"> = {}): IndexedDbFs {
98
+ static create(options: FilesystemOptions = {}): Filesystem {
130
99
  const maxSizeBytes = options.maxSizeBytes ?? DEFAULT_MAX_SIZE_BYTES;
131
100
  const entries = new Map<string, FsEntry>();
132
101
 
@@ -139,11 +108,11 @@ export class IndexedDbFs implements IFileSystem {
139
108
 
140
109
  if (options.initialFiles) {
141
110
  for (const [path, value] of Object.entries(options.initialFiles)) {
142
- const normalizedPath = IndexedDbFs.normalizePath(path);
143
- const init = IndexedDbFs.parseFileInit(value);
111
+ const normalizedPath = Filesystem.normalizePath(path);
112
+ const init = Filesystem.parseFileInit(value);
144
113
 
145
114
  // Ensure parent directories exist
146
- IndexedDbFs.ensureParentDirs(entries, normalizedPath);
115
+ Filesystem.ensureParentDirs(entries, normalizedPath);
147
116
 
148
117
  entries.set(normalizedPath, {
149
118
  type: "file",
@@ -154,52 +123,50 @@ export class IndexedDbFs implements IFileSystem {
154
123
  }
155
124
  }
156
125
 
157
- return new IndexedDbFs(entries, null, "", maxSizeBytes);
126
+ return new Filesystem(entries, maxSizeBytes);
158
127
  }
159
128
 
160
- // ============ Persistence Methods ============
129
+ // ============ State Export ============
161
130
 
162
131
  /**
163
- * Save all entries to IndexedDB
164
- * @returns true if saved, false if no db or not dirty
132
+ * Get all files as a serializable object.
133
+ *
134
+ * Returns a Record<string, string> that can be JSON-serialized and
135
+ * used as `initialFiles` when creating a new filesystem.
136
+ *
137
+ * Note: Only includes files, not directories (directories are
138
+ * automatically created from file paths). Binary files are
139
+ * base64-encoded with a `data:` prefix.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * const files = fs.getFiles();
144
+ * // { '/src/index.ts': 'export const x = 1;', '/package.json': '{"name":"app"}' }
145
+ *
146
+ * // Persist however you want
147
+ * localStorage.setItem('project', JSON.stringify(files));
148
+ *
149
+ * // Restore later
150
+ * const saved = JSON.parse(localStorage.getItem('project'));
151
+ * const fs2 = Filesystem.create({ initialFiles: saved });
152
+ * ```
165
153
  */
166
- async save(): Promise<boolean> {
167
- if (!this.db || !this.dirty) {
168
- return false;
169
- }
170
-
171
- const tx = this.db.transaction("entries", "readwrite");
172
- const store = tx.objectStore("entries");
173
-
174
- // Clear and rewrite all entries
175
- await this.promisifyRequest(store.clear());
176
-
154
+ getFiles(): Record<string, string> {
155
+ const files: Record<string, string> = {};
156
+
177
157
  for (const [path, entry] of this.entries) {
178
- store.put({ path, entry: this.serializeEntry(entry) });
158
+ if (entry.type === "file") {
159
+ if (typeof entry.content === "string") {
160
+ files[path] = entry.content;
161
+ } else {
162
+ // Binary content - base64 encode with data URI prefix
163
+ const base64 = this.encodeBase64(entry.content);
164
+ files[path] = `data:application/octet-stream;base64,${base64}`;
165
+ }
166
+ }
179
167
  }
180
-
181
- await this.promisifyTransaction(tx);
182
- this.dirty = false;
183
- return true;
184
- }
185
-
186
- /**
187
- * Reload entries from IndexedDB, discarding unsaved changes
188
- */
189
- async reload(): Promise<void> {
190
- if (!this.db) {
191
- return;
192
- }
193
-
194
- this.entries = await IndexedDbFs.loadEntries(this.db);
195
- this.dirty = false;
196
- }
197
-
198
- /**
199
- * Check if there are unsaved changes
200
- */
201
- isDirty(): boolean {
202
- return this.dirty;
168
+
169
+ return files;
203
170
  }
204
171
 
205
172
  /**
@@ -220,16 +187,6 @@ export class IndexedDbFs implements IFileSystem {
220
187
  return size;
221
188
  }
222
189
 
223
- /**
224
- * Close the database connection
225
- */
226
- close(): void {
227
- if (this.db) {
228
- this.db.close();
229
- this.db = null;
230
- }
231
- }
232
-
233
190
  // ============ IFileSystem Implementation ============
234
191
 
235
192
  async readFile(
@@ -296,7 +253,6 @@ export class IndexedDbFs implements IFileSystem {
296
253
  mode: existing?.mode ?? DEFAULT_FILE_MODE,
297
254
  mtime: new Date(),
298
255
  });
299
- this.dirty = true;
300
256
  }
301
257
 
302
258
  async appendFile(
@@ -375,7 +331,6 @@ export class IndexedDbFs implements IFileSystem {
375
331
  mode: DEFAULT_DIR_MODE,
376
332
  mtime: new Date(),
377
333
  });
378
- this.dirty = true;
379
334
  }
380
335
 
381
336
  async readdir(path: string): Promise<string[]> {
@@ -464,7 +419,6 @@ export class IndexedDbFs implements IFileSystem {
464
419
  }
465
420
 
466
421
  this.entries.delete(normalizedPath);
467
- this.dirty = true;
468
422
  }
469
423
 
470
424
  async cp(src: string, dest: string, options?: CpOptions): Promise<void> {
@@ -497,8 +451,6 @@ export class IndexedDbFs implements IFileSystem {
497
451
  this.ensureParentDirs(destPath);
498
452
  this.entries.set(destPath, this.cloneEntry(entry));
499
453
  }
500
-
501
- this.dirty = true;
502
454
  }
503
455
 
504
456
  async mv(src: string, dest: string): Promise<void> {
@@ -532,8 +484,6 @@ export class IndexedDbFs implements IFileSystem {
532
484
  this.entries.delete(srcPath);
533
485
  this.entries.set(destPath, entry);
534
486
  }
535
-
536
- this.dirty = true;
537
487
  }
538
488
 
539
489
  resolvePath(base: string, path: string): string {
@@ -571,7 +521,6 @@ export class IndexedDbFs implements IFileSystem {
571
521
 
572
522
  entry.mode = mode;
573
523
  entry.mtime = new Date();
574
- this.dirty = true;
575
524
  }
576
525
 
577
526
  async symlink(target: string, linkPath: string): Promise<void> {
@@ -589,7 +538,6 @@ export class IndexedDbFs implements IFileSystem {
589
538
  mode: DEFAULT_SYMLINK_MODE,
590
539
  mtime: new Date(),
591
540
  });
592
- this.dirty = true;
593
541
  }
594
542
 
595
543
  async link(existingPath: string, newPath: string): Promise<void> {
@@ -616,7 +564,6 @@ export class IndexedDbFs implements IFileSystem {
616
564
  mode: entry.mode,
617
565
  mtime: new Date(),
618
566
  });
619
- this.dirty = true;
620
567
  }
621
568
 
622
569
  async readlink(path: string): Promise<string> {
@@ -680,13 +627,12 @@ export class IndexedDbFs implements IFileSystem {
680
627
 
681
628
  // Update mtime (atime is ignored as per interface docs, kept for API compatibility)
682
629
  entry.mtime = mtime;
683
- this.dirty = true;
684
630
  }
685
631
 
686
632
  // ============ Private Helpers ============
687
633
 
688
634
  private normalizePath(path: string): string {
689
- return IndexedDbFs.normalizePath(path);
635
+ return Filesystem.normalizePath(path);
690
636
  }
691
637
 
692
638
  private static normalizePath(path: string): string {
@@ -718,8 +664,7 @@ export class IndexedDbFs implements IFileSystem {
718
664
  }
719
665
 
720
666
  private ensureParentDirs(path: string): void {
721
- IndexedDbFs.ensureParentDirs(this.entries, path);
722
- this.dirty = true;
667
+ Filesystem.ensureParentDirs(this.entries, path);
723
668
  }
724
669
 
725
670
  private static ensureParentDirs(entries: Map<string, FsEntry>, path: string): void {
@@ -818,11 +763,7 @@ export class IndexedDbFs implements IFileSystem {
818
763
  return new TextDecoder("utf-8").decode(buffer);
819
764
  }
820
765
  if (encoding === "base64") {
821
- let binary = "";
822
- for (let i = 0; i < buffer.byteLength; i++) {
823
- binary += String.fromCharCode(buffer[i]!);
824
- }
825
- return btoa(binary);
766
+ return this.encodeBase64(buffer);
826
767
  }
827
768
  if (encoding === "hex") {
828
769
  return Array.from(buffer)
@@ -833,6 +774,14 @@ export class IndexedDbFs implements IFileSystem {
833
774
  return new TextDecoder("utf-8").decode(buffer);
834
775
  }
835
776
 
777
+ private encodeBase64(buffer: Uint8Array): string {
778
+ let binary = "";
779
+ for (let i = 0; i < buffer.byteLength; i++) {
780
+ binary += String.fromCharCode(buffer[i]!);
781
+ }
782
+ return btoa(binary);
783
+ }
784
+
836
785
  private concatBuffers(a: Uint8Array, b: Uint8Array): Uint8Array {
837
786
  const result = new Uint8Array(a.byteLength + b.byteLength);
838
787
  result.set(a, 0);
@@ -846,103 +795,15 @@ export class IndexedDbFs implements IFileSystem {
846
795
  }
847
796
  return value;
848
797
  }
849
-
850
- // ============ IndexedDB Helpers ============
851
-
852
- private static openDatabase(dbName: string): Promise<IDBDatabase> {
853
- return new Promise((resolve, reject) => {
854
- const request = indexedDB.open(dbName, 1);
855
-
856
- request.onerror = () => reject(request.error);
857
- request.onsuccess = () => resolve(request.result);
858
-
859
- request.onupgradeneeded = (event) => {
860
- const db = (event.target as IDBOpenDBRequest).result;
861
- if (!db.objectStoreNames.contains("entries")) {
862
- db.createObjectStore("entries", { keyPath: "path" });
863
- }
864
- };
865
- });
866
- }
867
-
868
- private static async loadEntries(db: IDBDatabase): Promise<Map<string, FsEntry>> {
869
- const tx = db.transaction("entries", "readonly");
870
- const store = tx.objectStore("entries");
871
-
872
- return new Promise((resolve, reject) => {
873
- const request = store.getAll();
874
- request.onerror = () => reject(request.error);
875
- request.onsuccess = () => {
876
- const entries = new Map<string, FsEntry>();
877
- for (const record of request.result) {
878
- entries.set(record.path, IndexedDbFs.deserializeEntry(record.entry));
879
- }
880
- resolve(entries);
881
- };
882
- });
883
- }
884
-
885
- private serializeEntry(entry: FsEntry): object {
886
- if (entry.type === "file" && entry.content instanceof Uint8Array) {
887
- return {
888
- ...entry,
889
- content: Array.from(entry.content),
890
- contentType: "uint8array",
891
- mtime: entry.mtime.toISOString(),
892
- };
893
- }
894
- return {
895
- ...entry,
896
- mtime: entry.mtime.toISOString(),
897
- };
898
- }
899
-
900
- private static deserializeEntry(data: any): FsEntry {
901
- const mtime = new Date(data.mtime);
902
-
903
- if (data.type === "file") {
904
- let content = data.content;
905
- if (data.contentType === "uint8array" && Array.isArray(content)) {
906
- content = new Uint8Array(content);
907
- }
908
- return { type: "file", content, mode: data.mode, mtime };
909
- }
910
- if (data.type === "symlink") {
911
- return { type: "symlink", target: data.target, mode: data.mode, mtime };
912
- }
913
- return { type: "directory", mode: data.mode, mtime };
914
- }
915
-
916
- private promisifyRequest<T>(request: IDBRequest<T>): Promise<T> {
917
- return new Promise((resolve, reject) => {
918
- request.onerror = () => reject(request.error);
919
- request.onsuccess = () => resolve(request.result);
920
- });
921
- }
922
-
923
- private promisifyTransaction(tx: IDBTransaction): Promise<void> {
924
- return new Promise((resolve, reject) => {
925
- tx.onerror = () => reject(tx.error);
926
- tx.oncomplete = () => resolve();
927
- });
928
- }
929
798
  }
930
799
 
931
800
  /**
932
- * Synchronous factory function for creating an in-memory filesystem.
933
- *
934
- * Note: For IndexedDB-backed persistence, use `IndexedDbFs.create()` instead.
935
- * This function exists for compatibility with sync factory patterns.
801
+ * Create an in-memory filesystem.
936
802
  *
937
803
  * @param initialFiles - Optional initial files to populate the filesystem
938
- * @returns An in-memory filesystem (no persistence)
804
+ * @returns A new Filesystem instance
939
805
  */
940
- export function createInMemoryFs(initialFiles?: InitialFiles): IFileSystem {
941
- return IndexedDbFs.createInMemory({ initialFiles });
806
+ export function createFilesystem(options?: FilesystemOptions): Filesystem {
807
+ return Filesystem.create(options);
942
808
  }
943
809
 
944
- /**
945
- * @deprecated Use `createInMemoryFs` for in-memory filesystems or
946
- * `IndexedDbFs.create()` for IndexedDB-backed persistence.
947
- */
948
- export const createIndexedDbFs = createInMemoryFs;
package/src/index.ts CHANGED
@@ -25,19 +25,23 @@ if (typeof window !== "undefined" && typeof globalThis.process === "undefined")
25
25
 
26
26
  export {
27
27
  createSandbox,
28
- createInMemorySandbox,
29
28
  type Sandbox,
30
29
  type SandboxOptions,
30
+ type SandboxState,
31
+ type SandboxBashOptions,
31
32
  } from "./sandbox";
32
33
 
34
+ // -----------------------------------------------------------------------------
35
+ // Builder (recommended for agent workflows)
36
+ // -----------------------------------------------------------------------------
37
+
33
38
  export {
34
- SandboxManager,
35
- createSandboxManager,
36
- type ManagedSandbox,
37
- type ManagedSandboxOptions,
38
- type SandboxManagerStats,
39
- type SandboxManagerOptions,
40
- } from "./sandbox-manager";
39
+ createBuilder,
40
+ type BuildResult,
41
+ type CreateBuilderOptions,
42
+ type BuildCallOptions,
43
+ type BuilderFn,
44
+ } from "./builder";
41
45
 
42
46
  // -----------------------------------------------------------------------------
43
47
  // Module Loading (use after build to get exports)
@@ -68,6 +72,7 @@ export {
68
72
  // -----------------------------------------------------------------------------
69
73
 
70
74
  export type { BundleResult } from "./bundler";
75
+ export type { BuildOutput, ValidateFn } from "./commands/types";
71
76
  export type { TypecheckResult, Diagnostic } from "./typechecker";
72
77
  export type { PackageManifest, InstallResult } from "./packages";
73
78
 
@@ -131,9 +136,9 @@ export {
131
136
  // -----------------------------------------------------------------------------
132
137
 
133
138
  export {
134
- IndexedDbFs,
135
- createInMemoryFs,
136
- type IndexedDbFsOptions,
139
+ Filesystem,
140
+ createFilesystem,
141
+ type FilesystemOptions,
137
142
  } from "./fs";
138
143
 
139
144
  export type { IFileSystem, FsEntry } from "just-bash/browser";
@@ -163,4 +168,4 @@ export {
163
168
  type RunContext,
164
169
  type RunOptions,
165
170
  type RunResult,
166
- } from "./commands";
171
+ } from "./commands/index";