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/dist/index.js CHANGED
@@ -1,7 +1,106 @@
1
- // src/sandbox.ts
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined")
5
+ return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
8
+
9
+ // src/core/shared-module-registry.ts
10
+ var instanceCounter = 0;
11
+ function generateInstanceId() {
12
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
13
+ return crypto.randomUUID().slice(0, 8);
14
+ }
15
+ return `${Date.now().toString(36)}_${(++instanceCounter).toString(36)}`;
16
+ }
17
+
18
+ class SharedModuleRegistry {
19
+ modules;
20
+ exportNamesMap;
21
+ _registryKey;
22
+ constructor(modules) {
23
+ this._registryKey = `__sandlot_${generateInstanceId()}__`;
24
+ this.modules = new Map(Object.entries(modules));
25
+ this.exportNamesMap = new Map;
26
+ for (const [id, mod] of this.modules) {
27
+ this.exportNamesMap.set(id, this.introspectExports(mod));
28
+ }
29
+ }
30
+ get registryKey() {
31
+ return this._registryKey;
32
+ }
33
+ exposeGlobally() {
34
+ globalThis[this._registryKey] = this;
35
+ return this;
36
+ }
37
+ removeFromGlobal() {
38
+ if (globalThis[this._registryKey] === this) {
39
+ delete globalThis[this._registryKey];
40
+ }
41
+ }
42
+ get(moduleId) {
43
+ const mod = this.modules.get(moduleId);
44
+ if (mod === undefined && !this.modules.has(moduleId)) {
45
+ throw new Error(`Shared module "${moduleId}" not registered.`);
46
+ }
47
+ return mod;
48
+ }
49
+ has(moduleId) {
50
+ return this.modules.has(moduleId);
51
+ }
52
+ getExportNames(moduleId) {
53
+ return this.exportNamesMap.get(moduleId) ?? [];
54
+ }
55
+ list() {
56
+ return [...this.modules.keys()];
57
+ }
58
+ introspectExports(module) {
59
+ if (module === null || module === undefined) {
60
+ return [];
61
+ }
62
+ if (typeof module !== "object" && typeof module !== "function") {
63
+ return [];
64
+ }
65
+ const exports = [];
66
+ for (const key of Object.keys(module)) {
67
+ if (this.isValidIdentifier(key)) {
68
+ exports.push(key);
69
+ }
70
+ }
71
+ return exports;
72
+ }
73
+ isValidIdentifier(name) {
74
+ if (name.length === 0)
75
+ return false;
76
+ if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name))
77
+ return false;
78
+ const reserved = [
79
+ "default",
80
+ "class",
81
+ "function",
82
+ "var",
83
+ "let",
84
+ "const",
85
+ "import",
86
+ "export"
87
+ ];
88
+ return !reserved.includes(name);
89
+ }
90
+ }
91
+ function createSharedModuleRegistry(modules) {
92
+ if (!modules || Object.keys(modules).length === 0) {
93
+ return null;
94
+ }
95
+ const registry = new SharedModuleRegistry(modules);
96
+ registry.exposeGlobally();
97
+ return registry;
98
+ }
99
+
100
+ // src/core/sandbox.ts
2
101
  import { Bash } from "just-bash/browser";
3
102
 
4
- // src/fs.ts
103
+ // src/core/fs.ts
5
104
  var DEFAULT_FILE_MODE = 420;
6
105
  var DEFAULT_DIR_MODE = 493;
7
106
  var DEFAULT_SYMLINK_MODE = 511;
@@ -65,7 +164,7 @@ class Filesystem {
65
164
  }
66
165
  return size;
67
166
  }
68
- async readFile(path, options) {
167
+ readFile(path, options) {
69
168
  const normalizedPath = this.normalizePath(path);
70
169
  const entry = this.resolveSymlinks(normalizedPath);
71
170
  if (!entry) {
@@ -81,7 +180,7 @@ class Filesystem {
81
180
  const encoding = this.getEncoding(options) ?? "utf8";
82
181
  return this.decodeBuffer(content, encoding);
83
182
  }
84
- async readFileBuffer(path) {
183
+ readFileBuffer(path) {
85
184
  const normalizedPath = this.normalizePath(path);
86
185
  const entry = this.resolveSymlinks(normalizedPath);
87
186
  if (!entry) {
@@ -96,7 +195,7 @@ class Filesystem {
96
195
  }
97
196
  return new TextEncoder().encode(content);
98
197
  }
99
- async writeFile(path, content, _options) {
198
+ writeFile(path, content, _options) {
100
199
  const normalizedPath = this.normalizePath(path);
101
200
  this.checkSizeLimit(content);
102
201
  this.ensureParentDirs(normalizedPath);
@@ -111,20 +210,20 @@ class Filesystem {
111
210
  mtime: new Date
112
211
  });
113
212
  }
114
- async appendFile(path, content, options) {
213
+ appendFile(path, content, options) {
115
214
  const normalizedPath = this.normalizePath(path);
116
215
  let existing = "";
117
216
  try {
118
- existing = await this.readFile(normalizedPath);
217
+ existing = this.readFile(normalizedPath);
119
218
  } catch {}
120
219
  const newContent = typeof existing === "string" && typeof content === "string" ? existing + content : this.concatBuffers(typeof existing === "string" ? new TextEncoder().encode(existing) : existing, typeof content === "string" ? new TextEncoder().encode(content) : content);
121
- await this.writeFile(normalizedPath, newContent, options);
220
+ this.writeFile(normalizedPath, newContent, options);
122
221
  }
123
- async exists(path) {
222
+ exists(path) {
124
223
  const normalizedPath = this.normalizePath(path);
125
224
  return this.entries.has(normalizedPath);
126
225
  }
127
- async stat(path) {
226
+ stat(path) {
128
227
  const normalizedPath = this.normalizePath(path);
129
228
  const entry = this.resolveSymlinks(normalizedPath);
130
229
  if (!entry) {
@@ -132,7 +231,7 @@ class Filesystem {
132
231
  }
133
232
  return this.entryToStat(entry);
134
233
  }
135
- async lstat(path) {
234
+ lstat(path) {
136
235
  const normalizedPath = this.normalizePath(path);
137
236
  const entry = this.entries.get(normalizedPath);
138
237
  if (!entry) {
@@ -140,7 +239,7 @@ class Filesystem {
140
239
  }
141
240
  return this.entryToStat(entry);
142
241
  }
143
- async mkdir(path, options) {
242
+ mkdir(path, options) {
144
243
  const normalizedPath = this.normalizePath(path);
145
244
  if (this.entries.has(normalizedPath)) {
146
245
  if (options?.recursive) {
@@ -162,7 +261,7 @@ class Filesystem {
162
261
  mtime: new Date
163
262
  });
164
263
  }
165
- async readdir(path) {
264
+ readdir(path) {
166
265
  const normalizedPath = this.normalizePath(path);
167
266
  const entry = this.resolveSymlinks(normalizedPath);
168
267
  if (!entry) {
@@ -185,7 +284,7 @@ class Filesystem {
185
284
  }
186
285
  return names.sort();
187
286
  }
188
- async readdirWithFileTypes(path) {
287
+ readdirWithFileTypes(path) {
189
288
  const normalizedPath = this.normalizePath(path);
190
289
  const entry = this.resolveSymlinks(normalizedPath);
191
290
  if (!entry) {
@@ -213,7 +312,7 @@ class Filesystem {
213
312
  }
214
313
  return dirents.sort((a, b) => a.name.localeCompare(b.name));
215
314
  }
216
- async rm(path, options) {
315
+ rm(path, options) {
217
316
  const normalizedPath = this.normalizePath(path);
218
317
  const entry = this.entries.get(normalizedPath);
219
318
  if (!entry) {
@@ -222,7 +321,7 @@ class Filesystem {
222
321
  throw new Error(`ENOENT: no such file or directory, rm '${path}'`);
223
322
  }
224
323
  if (entry.type === "directory") {
225
- const children = await this.readdir(normalizedPath);
324
+ const children = this.readdir(normalizedPath);
226
325
  if (children.length > 0 && !options?.recursive) {
227
326
  throw new Error(`ENOTEMPTY: directory not empty, rm '${path}'`);
228
327
  }
@@ -237,7 +336,7 @@ class Filesystem {
237
336
  }
238
337
  this.entries.delete(normalizedPath);
239
338
  }
240
- async cp(src, dest, options) {
339
+ cp(src, dest, options) {
241
340
  const srcPath = this.normalizePath(src);
242
341
  const destPath = this.normalizePath(dest);
243
342
  const entry = this.entries.get(srcPath);
@@ -263,7 +362,7 @@ class Filesystem {
263
362
  this.entries.set(destPath, this.cloneEntry(entry));
264
363
  }
265
364
  }
266
- async mv(src, dest) {
365
+ mv(src, dest) {
267
366
  const srcPath = this.normalizePath(src);
268
367
  const destPath = this.normalizePath(dest);
269
368
  const entry = this.entries.get(srcPath);
@@ -309,7 +408,7 @@ class Filesystem {
309
408
  getAllPaths() {
310
409
  return [...this.entries.keys()].sort();
311
410
  }
312
- async chmod(path, mode) {
411
+ chmod(path, mode) {
313
412
  const normalizedPath = this.normalizePath(path);
314
413
  const entry = this.entries.get(normalizedPath);
315
414
  if (!entry) {
@@ -318,7 +417,7 @@ class Filesystem {
318
417
  entry.mode = mode;
319
418
  entry.mtime = new Date;
320
419
  }
321
- async symlink(target, linkPath) {
420
+ symlink(target, linkPath) {
322
421
  const normalizedLinkPath = this.normalizePath(linkPath);
323
422
  if (this.entries.has(normalizedLinkPath)) {
324
423
  throw new Error(`EEXIST: file already exists, symlink '${linkPath}'`);
@@ -331,7 +430,7 @@ class Filesystem {
331
430
  mtime: new Date
332
431
  });
333
432
  }
334
- async link(existingPath, newPath) {
433
+ link(existingPath, newPath) {
335
434
  const srcPath = this.normalizePath(existingPath);
336
435
  const destPath = this.normalizePath(newPath);
337
436
  const entry = this.entries.get(srcPath);
@@ -352,7 +451,7 @@ class Filesystem {
352
451
  mtime: new Date
353
452
  });
354
453
  }
355
- async readlink(path) {
454
+ readlink(path) {
356
455
  const normalizedPath = this.normalizePath(path);
357
456
  const entry = this.entries.get(normalizedPath);
358
457
  if (!entry) {
@@ -363,7 +462,7 @@ class Filesystem {
363
462
  }
364
463
  return entry.target;
365
464
  }
366
- async realpath(path) {
465
+ realpath(path) {
367
466
  const normalizedPath = this.normalizePath(path);
368
467
  const parts = normalizedPath.split("/").filter(Boolean);
369
468
  let resolved = "/";
@@ -389,7 +488,7 @@ class Filesystem {
389
488
  }
390
489
  return resolved;
391
490
  }
392
- async utimes(path, atime, mtime) {
491
+ utimes(path, atime, mtime) {
393
492
  const normalizedPath = this.normalizePath(path);
394
493
  const entry = this.entries.get(normalizedPath);
395
494
  if (!entry) {
@@ -533,39 +632,559 @@ class Filesystem {
533
632
  function createFilesystem(options) {
534
633
  return Filesystem.create(options);
535
634
  }
635
+ function wrapFilesystemForJustBash(fs) {
636
+ return {
637
+ readFile: async (path, options) => fs.readFile(path, options),
638
+ writeFile: async (path, content, options) => fs.writeFile(path, content, options),
639
+ appendFile: async (path, content, options) => fs.appendFile(path, content, options),
640
+ exists: async (path) => fs.exists(path),
641
+ stat: async (path) => fs.stat(path),
642
+ lstat: async (path) => fs.lstat(path),
643
+ mkdir: async (path, options) => fs.mkdir(path, options),
644
+ readdir: async (path) => fs.readdir(path),
645
+ readdirWithFileTypes: async (path) => fs.readdirWithFileTypes(path),
646
+ rm: async (path, options) => fs.rm(path, options),
647
+ cp: async (src, dest, options) => fs.cp(src, dest, options),
648
+ mv: async (src, dest) => fs.mv(src, dest),
649
+ chmod: async (path, mode) => fs.chmod(path, mode),
650
+ symlink: async (target, linkPath) => fs.symlink(target, linkPath),
651
+ link: async (existingPath, newPath) => fs.link(existingPath, newPath),
652
+ readlink: async (path) => fs.readlink(path),
653
+ realpath: async (path) => fs.realpath(path),
654
+ utimes: async (path, atime, mtime) => fs.utimes(path, atime, mtime),
655
+ readFileBuffer: async (path) => fs.readFileBuffer(path),
656
+ resolvePath: (base, path) => fs.resolvePath(base, path),
657
+ getAllPaths: () => fs.getAllPaths()
658
+ };
659
+ }
536
660
 
537
- // src/packages.ts
538
- var ESM_CDN_BASE = "https://esm.sh";
539
- var KNOWN_SUBPATHS = {
540
- react: ["jsx-runtime", "jsx-dev-runtime"],
541
- "react-dom": ["client", "server"]
542
- };
661
+ // src/commands/index.ts
662
+ import { defineCommand } from "just-bash/browser";
543
663
 
544
- class InMemoryTypesCache {
545
- cache = new Map;
546
- key(name, version) {
547
- return `${name}@${version}`;
664
+ // src/commands/types.ts
665
+ function formatSize(bytes) {
666
+ if (bytes < 1024)
667
+ return `${bytes} B`;
668
+ if (bytes < 1024 * 1024)
669
+ return `${(bytes / 1024).toFixed(2)} KB`;
670
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
671
+ }
672
+ function formatDiagnostics(diagnostics) {
673
+ if (diagnostics.length === 0)
674
+ return "";
675
+ return diagnostics.map((d) => {
676
+ const severity = d.severity.toUpperCase();
677
+ if (d.file) {
678
+ const loc = `${d.file}${d.line ? `:${d.line}` : ""}${d.column ? `:${d.column}` : ""}`;
679
+ return `${severity}: ${loc}: ${d.message}`;
680
+ }
681
+ return `${severity}: ${d.message}`;
682
+ }).join(`
683
+ `);
684
+ }
685
+ function formatBundleErrors(errors) {
686
+ if (errors.length === 0)
687
+ return "";
688
+ return errors.map((e) => {
689
+ let output = "";
690
+ if (e.location) {
691
+ const loc = `${e.location.file}:${e.location.line}${e.location.column ? `:${e.location.column}` : ""}`;
692
+ output += `ERROR: ${loc}: ${e.text}`;
693
+ if (e.location.lineText) {
694
+ output += `
695
+ ${e.location.line} | ${e.location.lineText}`;
696
+ if (e.location.column) {
697
+ const padding = " ".repeat(String(e.location.line).length + 3 + e.location.column - 1);
698
+ output += `
699
+ ${padding}^`;
700
+ }
701
+ }
702
+ } else {
703
+ output += `ERROR: ${e.text}`;
704
+ }
705
+ return output;
706
+ }).join(`
707
+
708
+ `);
709
+ }
710
+ // src/commands/index.ts
711
+ function createSandlotCommand(sandboxRef) {
712
+ return defineCommand("sandlot", async (args, ctx) => {
713
+ const subcommand = args[0];
714
+ if (!subcommand || subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
715
+ return showHelp();
716
+ }
717
+ switch (subcommand) {
718
+ case "build":
719
+ return handleBuild(sandboxRef, args.slice(1));
720
+ case "typecheck":
721
+ case "tsc":
722
+ return handleTypecheck(sandboxRef, args.slice(1));
723
+ case "install":
724
+ case "add":
725
+ case "i":
726
+ return handleInstall(sandboxRef, args.slice(1));
727
+ case "uninstall":
728
+ case "remove":
729
+ case "rm":
730
+ return handleUninstall(sandboxRef, args.slice(1));
731
+ case "run":
732
+ return handleRun(sandboxRef, args.slice(1));
733
+ default:
734
+ return {
735
+ stdout: "",
736
+ stderr: `Unknown command: sandlot ${subcommand}
737
+
738
+ Run 'sandlot help' for available commands.
739
+ `,
740
+ exitCode: 1
741
+ };
742
+ }
743
+ });
744
+ }
745
+ function showHelp() {
746
+ return {
747
+ stdout: `sandlot - In-browser TypeScript sandbox
748
+
749
+ Usage: sandlot <command> [options]
750
+
751
+ Commands:
752
+ build Build the project (typecheck, bundle)
753
+ run Build and execute code
754
+ typecheck Type check without building (alias: tsc)
755
+ install Install packages (aliases: add, i)
756
+ uninstall Remove packages (aliases: remove, rm)
757
+ help Show this help message
758
+
759
+ Run 'sandlot <command> --help' for command-specific options.
760
+
761
+ Examples:
762
+ sandlot build
763
+ sandlot run
764
+ sandlot run --skip-typecheck --timeout 5000
765
+ sandlot install react react-dom
766
+ sandlot typecheck
767
+ `,
768
+ stderr: "",
769
+ exitCode: 0
770
+ };
771
+ }
772
+ async function handleBuild(sandboxRef, args) {
773
+ let entryPoint;
774
+ let skipTypecheck = false;
775
+ let minify = false;
776
+ let format = "esm";
777
+ for (let i = 0;i < args.length; i++) {
778
+ const arg = args[i];
779
+ if (arg === "--skip-typecheck" || arg === "-s") {
780
+ skipTypecheck = true;
781
+ } else if (arg === "--minify" || arg === "-m") {
782
+ minify = true;
783
+ } else if ((arg === "--format" || arg === "-f") && args[i + 1]) {
784
+ const f = args[++i].toLowerCase();
785
+ if (f === "esm" || f === "iife" || f === "cjs") {
786
+ format = f;
787
+ }
788
+ } else if ((arg === "--entry" || arg === "-e") && args[i + 1]) {
789
+ entryPoint = args[++i];
790
+ } else if (arg === "--help" || arg === "-h") {
791
+ return {
792
+ stdout: `Usage: sandlot build [options]
793
+
794
+ Options:
795
+ --entry, -e <path> Entry point (default: from package.json main)
796
+ --skip-typecheck, -s Skip type checking
797
+ --minify, -m Minify output
798
+ --format, -f <fmt> Output format (esm|iife|cjs)
799
+ --help, -h Show this help message
800
+
801
+ Examples:
802
+ sandlot build
803
+ sandlot build --entry /src/main.ts
804
+ sandlot build --skip-typecheck --minify
805
+ `,
806
+ stderr: "",
807
+ exitCode: 0
808
+ };
809
+ } else if (arg && !arg.startsWith("-") && !entryPoint) {
810
+ entryPoint = arg;
811
+ }
812
+ }
813
+ const result = await sandboxRef.build({
814
+ entryPoint,
815
+ skipTypecheck,
816
+ minify,
817
+ format
818
+ });
819
+ if (!result.success) {
820
+ let stderr = `Build failed`;
821
+ switch (result.phase) {
822
+ case "entry":
823
+ stderr = `Build failed: ${result.message}
824
+ `;
825
+ break;
826
+ case "typecheck":
827
+ if (result.diagnostics) {
828
+ const errors = result.diagnostics.filter((d) => d.severity === "error");
829
+ stderr = `Build failed: Type check errors
830
+
831
+ ${formatDiagnostics(errors)}
832
+ `;
833
+ } else {
834
+ stderr = `Build failed: Type check errors
835
+ `;
836
+ }
837
+ break;
838
+ case "bundle":
839
+ if (result.bundleErrors && result.bundleErrors.length > 0) {
840
+ stderr = `Build failed: Bundle errors
841
+
842
+ ${formatBundleErrors(result.bundleErrors)}
843
+ `;
844
+ } else {
845
+ stderr = `Build failed: Bundle error
846
+ `;
847
+ }
848
+ break;
849
+ default:
850
+ stderr = `Build failed: Unknown error
851
+ `;
852
+ }
853
+ return {
854
+ stdout: "",
855
+ stderr,
856
+ exitCode: 1
857
+ };
858
+ }
859
+ let output = `Build successful!
860
+ `;
861
+ output += `Size: ${formatSize(result.code.length)}
862
+ `;
863
+ output += `Files: ${result.includedFiles.length}
864
+ `;
865
+ if (result.warnings.length > 0) {
866
+ output += `
867
+ Warnings:
868
+ `;
869
+ for (const warning of result.warnings) {
870
+ if (warning.location) {
871
+ output += ` ${warning.location.file}:${warning.location.line}: ${warning.text}
872
+ `;
873
+ } else {
874
+ output += ` ${warning.text}
875
+ `;
876
+ }
877
+ }
878
+ }
879
+ return {
880
+ stdout: output,
881
+ stderr: "",
882
+ exitCode: 0
883
+ };
884
+ }
885
+ async function handleTypecheck(sandboxRef, args) {
886
+ let entryPoint;
887
+ for (let i = 0;i < args.length; i++) {
888
+ const arg = args[i];
889
+ if ((arg === "--entry" || arg === "-e") && args[i + 1]) {
890
+ entryPoint = args[++i];
891
+ } else if (arg === "--help" || arg === "-h") {
892
+ return {
893
+ stdout: `Usage: sandlot typecheck [options]
894
+
895
+ Options:
896
+ --entry, -e <path> Entry point (default: from package.json main)
897
+ --help, -h Show this help message
898
+
899
+ Aliases: sandlot tsc
900
+
901
+ Examples:
902
+ sandlot typecheck
903
+ sandlot typecheck --entry /src/main.ts
904
+ `,
905
+ stderr: "",
906
+ exitCode: 0
907
+ };
908
+ } else if (arg && !arg.startsWith("-") && !entryPoint) {
909
+ entryPoint = arg;
910
+ }
911
+ }
912
+ try {
913
+ const result = await sandboxRef.typecheck({ entryPoint });
914
+ if (!result.success) {
915
+ const errors = result.diagnostics.filter((d) => d.severity === "error");
916
+ const formatted = formatDiagnostics(errors);
917
+ return {
918
+ stdout: "",
919
+ stderr: `Type check failed:
920
+ ${formatted}
921
+ `,
922
+ exitCode: 1
923
+ };
924
+ }
925
+ const warnings = result.diagnostics.filter((d) => d.severity === "warning");
926
+ let output = `Type check passed.
927
+ `;
928
+ if (warnings.length > 0) {
929
+ output += `
930
+ Warnings:
931
+ ${formatDiagnostics(warnings)}
932
+ `;
933
+ }
934
+ return {
935
+ stdout: output,
936
+ stderr: "",
937
+ exitCode: 0
938
+ };
939
+ } catch (err) {
940
+ const message = err instanceof Error ? err.message : String(err);
941
+ return {
942
+ stdout: "",
943
+ stderr: `Type check error: ${message}
944
+ `,
945
+ exitCode: 1
946
+ };
548
947
  }
549
- get(name, version) {
550
- return this.cache.get(this.key(name, version)) ?? null;
948
+ }
949
+ async function handleInstall(sandboxRef, args) {
950
+ if (args.includes("--help") || args.includes("-h")) {
951
+ return {
952
+ stdout: `Usage: sandlot install <package>[@version] [...packages]
953
+
954
+ Examples:
955
+ sandlot install react
956
+ sandlot install lodash@4.17.21
957
+ sandlot install @tanstack/react-query@5
958
+ sandlot install react react-dom
959
+
960
+ Aliases: sandlot add, sandlot i
961
+ `,
962
+ stderr: "",
963
+ exitCode: 0
964
+ };
551
965
  }
552
- set(name, version, types) {
553
- this.cache.set(this.key(name, version), types);
966
+ const packages = args.filter((a) => !!a && !a.startsWith("-"));
967
+ if (packages.length === 0) {
968
+ return {
969
+ stdout: "",
970
+ stderr: `Usage: sandlot install <package>[@version] [...packages]
971
+
972
+ Run 'sandlot install --help' for more information.
973
+ `,
974
+ exitCode: 1
975
+ };
554
976
  }
555
- has(name, version) {
556
- return this.cache.has(this.key(name, version));
977
+ const results = [];
978
+ let hasError = false;
979
+ for (const packageSpec of packages) {
980
+ try {
981
+ const result = await sandboxRef.install(packageSpec);
982
+ let status = `+ ${result.name}@${result.version}`;
983
+ if (result.typesInstalled) {
984
+ status += ` (${result.typeFilesCount} type file${result.typeFilesCount !== 1 ? "s" : ""})`;
985
+ if (result.fromCache) {
986
+ status += " [cached]";
987
+ }
988
+ } else if (result.typesError) {
989
+ status += ` (no types: ${result.typesError})`;
990
+ }
991
+ results.push(status);
992
+ } catch (err) {
993
+ hasError = true;
994
+ const message = err instanceof Error ? err.message : String(err);
995
+ results.push(`x ${packageSpec}: ${message}`);
996
+ }
557
997
  }
558
- delete(name, version) {
559
- return this.cache.delete(this.key(name, version));
998
+ const output = results.join(`
999
+ `) + `
1000
+ `;
1001
+ return hasError ? { stdout: "", stderr: output, exitCode: 1 } : { stdout: output, stderr: "", exitCode: 0 };
1002
+ }
1003
+ async function handleUninstall(sandboxRef, args) {
1004
+ if (args.includes("--help") || args.includes("-h")) {
1005
+ return {
1006
+ stdout: `Usage: sandlot uninstall <package> [...packages]
1007
+
1008
+ Examples:
1009
+ sandlot uninstall lodash
1010
+ sandlot uninstall react react-dom
1011
+
1012
+ Aliases: sandlot remove, sandlot rm
1013
+ `,
1014
+ stderr: "",
1015
+ exitCode: 0
1016
+ };
560
1017
  }
561
- clear() {
562
- this.cache.clear();
1018
+ const packages = args.filter((a) => !!a && !a.startsWith("-"));
1019
+ if (packages.length === 0) {
1020
+ return {
1021
+ stdout: "",
1022
+ stderr: `Usage: sandlot uninstall <package> [...packages]
1023
+
1024
+ Run 'sandlot uninstall --help' for more information.
1025
+ `,
1026
+ exitCode: 1
1027
+ };
1028
+ }
1029
+ const results = [];
1030
+ let hasError = false;
1031
+ for (const packageName of packages) {
1032
+ try {
1033
+ const result = await sandboxRef.uninstall(packageName);
1034
+ if (result.removed) {
1035
+ results.push(`- ${result.name}`);
1036
+ } else {
1037
+ results.push(`x ${packageName}: not installed`);
1038
+ hasError = true;
1039
+ }
1040
+ } catch (err) {
1041
+ hasError = true;
1042
+ const message = err instanceof Error ? err.message : String(err);
1043
+ results.push(`x ${packageName}: ${message}`);
1044
+ }
1045
+ }
1046
+ const output = results.join(`
1047
+ `) + `
1048
+ `;
1049
+ return hasError ? { stdout: "", stderr: output, exitCode: 1 } : { stdout: output, stderr: "", exitCode: 0 };
1050
+ }
1051
+ async function handleRun(sandboxRef, args) {
1052
+ let entryPoint;
1053
+ let skipTypecheck = false;
1054
+ let timeout = 30000;
1055
+ let entryExport = "main";
1056
+ for (let i = 0;i < args.length; i++) {
1057
+ const arg = args[i];
1058
+ if (arg === "--skip-typecheck" || arg === "-s") {
1059
+ skipTypecheck = true;
1060
+ } else if ((arg === "--timeout" || arg === "-t") && args[i + 1]) {
1061
+ const t = parseInt(args[++i], 10);
1062
+ if (!isNaN(t))
1063
+ timeout = t;
1064
+ } else if ((arg === "--entry" || arg === "-e") && args[i + 1]) {
1065
+ entryPoint = args[++i];
1066
+ } else if ((arg === "--export" || arg === "-x") && args[i + 1]) {
1067
+ const e = args[++i].toLowerCase();
1068
+ if (e === "main" || e === "default") {
1069
+ entryExport = e;
1070
+ }
1071
+ } else if (arg === "--help" || arg === "-h") {
1072
+ return {
1073
+ stdout: `Usage: sandlot run [options]
1074
+
1075
+ Options:
1076
+ --entry, -e <path> Entry point (default: from package.json main)
1077
+ --skip-typecheck, -s Skip type checking
1078
+ --timeout, -t <ms> Execution timeout (default: 30000, 0 = none)
1079
+ --export, -x <name> Export to call: main or default (default: main)
1080
+ --help, -h Show this help message
1081
+
1082
+ Examples:
1083
+ sandlot run
1084
+ sandlot run --entry /src/main.ts
1085
+ sandlot run --skip-typecheck --timeout 5000
1086
+ sandlot run --export default
1087
+ `,
1088
+ stderr: "",
1089
+ exitCode: 0
1090
+ };
1091
+ } else if (arg && !arg.startsWith("-") && !entryPoint) {
1092
+ entryPoint = arg;
1093
+ }
563
1094
  }
564
- get size() {
565
- return this.cache.size;
1095
+ try {
1096
+ const result = await sandboxRef.run({
1097
+ entryPoint,
1098
+ skipTypecheck,
1099
+ timeout,
1100
+ entryExport
1101
+ });
1102
+ if (!result.success) {
1103
+ let stderr = "";
1104
+ if (result.buildFailure) {
1105
+ stderr = `Run failed: Build error in ${result.buildFailure.phase} phase`;
1106
+ if (result.buildFailure.message) {
1107
+ stderr += `
1108
+ ${result.buildFailure.message}`;
1109
+ }
1110
+ stderr += `
1111
+ `;
1112
+ } else {
1113
+ stderr = `Run failed: ${result.error ?? "Unknown error"}
1114
+ `;
1115
+ }
1116
+ let stdout = "";
1117
+ if (result.logs.length > 0) {
1118
+ stdout = result.logs.join(`
1119
+ `) + `
1120
+ `;
1121
+ }
1122
+ return {
1123
+ stdout,
1124
+ stderr,
1125
+ exitCode: 1
1126
+ };
1127
+ }
1128
+ let output = "";
1129
+ if (result.logs.length > 0) {
1130
+ output = result.logs.join(`
1131
+ `) + `
1132
+ `;
1133
+ }
1134
+ if (result.returnValue !== undefined) {
1135
+ const returnStr = typeof result.returnValue === "object" ? JSON.stringify(result.returnValue, null, 2) : String(result.returnValue);
1136
+ output += `[return] ${returnStr}
1137
+ `;
1138
+ }
1139
+ if (result.executionTimeMs !== undefined) {
1140
+ output += `
1141
+ Completed in ${result.executionTimeMs.toFixed(2)}ms
1142
+ `;
1143
+ }
1144
+ return {
1145
+ stdout: output,
1146
+ stderr: "",
1147
+ exitCode: 0
1148
+ };
1149
+ } catch (err) {
1150
+ const message = err instanceof Error ? err.message : String(err);
1151
+ return {
1152
+ stdout: "",
1153
+ stderr: `Run error: ${message}
1154
+ `,
1155
+ exitCode: 1
1156
+ };
566
1157
  }
567
1158
  }
1159
+ function createDefaultCommands(sandboxRef) {
1160
+ return [createSandlotCommand(sandboxRef)];
1161
+ }
1162
+
1163
+ // src/core/sandbox.ts
1164
+ var DEFAULT_ENTRY_POINT = "./index.ts";
1165
+ var TSCONFIG_PATH = "/tsconfig.json";
568
1166
  var PACKAGE_JSON_PATH = "/package.json";
1167
+ var DEFAULT_PACKAGE_JSON = {
1168
+ main: DEFAULT_ENTRY_POINT,
1169
+ dependencies: {}
1170
+ };
1171
+ var DEFAULT_TSCONFIG = {
1172
+ compilerOptions: {
1173
+ target: "ES2020",
1174
+ lib: ["ES2020", "DOM", "DOM.Iterable"],
1175
+ module: "ESNext",
1176
+ moduleResolution: "bundler",
1177
+ jsx: "react-jsx",
1178
+ strict: true,
1179
+ noEmit: true,
1180
+ esModuleInterop: true,
1181
+ skipLibCheck: true,
1182
+ resolveJsonModule: true,
1183
+ isolatedModules: true
1184
+ },
1185
+ include: ["**/*.ts", "**/*.tsx"],
1186
+ exclude: ["node_modules"]
1187
+ };
569
1188
  function parsePackageSpec(spec) {
570
1189
  if (spec.startsWith("@")) {
571
1190
  const slashIndex = spec.indexOf("/");
@@ -591,1739 +1210,358 @@ function parsePackageSpec(spec) {
591
1210
  version: spec.slice(atIndex + 1)
592
1211
  };
593
1212
  }
594
- function isTypesPackage(name) {
595
- return name.startsWith("@types/");
596
- }
597
- function extractVersionFromUrl(url, packageName) {
598
- const exactRegex = new RegExp(`${escapeRegExp(packageName)}@([^/]+)`);
599
- const exactMatch = url.match(exactRegex);
600
- if (exactMatch?.[1]) {
601
- return exactMatch[1];
602
- }
603
- if (packageName.startsWith("@")) {
604
- const scopedParts = packageName.split("/");
605
- if (scopedParts.length === 2 && scopedParts[1]) {
606
- const partialRegex = new RegExp(`${escapeRegExp(scopedParts[1])}@([^/]+)`);
607
- const partialMatch = url.match(partialRegex);
608
- if (partialMatch?.[1]) {
609
- return partialMatch[1];
610
- }
611
- }
612
- }
613
- const genericMatch = url.match(/@(\d+\.\d+\.\d+[^/]*)/);
614
- if (genericMatch?.[1]) {
615
- return genericMatch[1];
616
- }
617
- return null;
618
- }
619
- async function fetchVersionFromNpm(name) {
620
- const registryUrl = `https://registry.npmjs.org/${name}/latest`;
621
- const response = await fetch(registryUrl);
622
- if (!response.ok) {
623
- throw new Error(`Failed to fetch version from npm: ${response.status}`);
624
- }
625
- const data = await response.json();
626
- return data.version;
627
- }
628
- function extractVersionFromHeaders(headers, packageName) {
629
- const esmId = headers.get("x-esm-id");
630
- if (esmId) {
631
- const version = extractVersionFromUrl(esmId, packageName);
632
- if (version)
633
- return version;
634
- }
635
- const typesHeader = headers.get("X-TypeScript-Types");
636
- if (typesHeader) {
637
- const versionMatch = typesHeader.match(/@(\d+\.\d+\.\d+[^/]*)/);
638
- if (versionMatch?.[1]) {
639
- return versionMatch[1];
640
- }
641
- }
642
- return null;
643
- }
644
- async function fetchPackageInfo(name, version, subpath) {
645
- let url = version ? `${ESM_CDN_BASE}/${name}@${version}` : `${ESM_CDN_BASE}/${name}`;
646
- if (subpath) {
647
- url += `/${subpath}`;
648
- }
649
- const response = await fetch(url, { method: "HEAD" });
650
- if (!response.ok) {
651
- throw new Error(`Package not found: ${name}${version ? `@${version}` : ""}${subpath ? `/${subpath}` : ""}`);
652
- }
653
- const resolvedUrl = response.url;
654
- let resolvedVersion = extractVersionFromUrl(resolvedUrl, name);
655
- if (!resolvedVersion) {
656
- resolvedVersion = extractVersionFromHeaders(response.headers, name);
657
- }
658
- if (!resolvedVersion && version && version !== "latest") {
659
- resolvedVersion = version;
660
- }
661
- if (!resolvedVersion) {
662
- try {
663
- resolvedVersion = await fetchVersionFromNpm(name);
664
- } catch (err) {
665
- console.warn(`Could not resolve version for ${name}:`, err);
666
- resolvedVersion = "latest";
667
- }
668
- }
669
- const typesUrl = response.headers.get("X-TypeScript-Types") ?? undefined;
670
- return {
671
- version: resolvedVersion,
672
- typesUrl: typesUrl ? new URL(typesUrl, resolvedUrl).href : undefined
673
- };
674
- }
675
- function escapeRegExp(string) {
676
- return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
677
- }
678
- async function fetchTypeDefinitions(typesUrl, packageName) {
679
- const types = new Map;
1213
+ function readPackageJson(fs) {
680
1214
  try {
681
- const response = await fetch(typesUrl);
682
- if (!response.ok) {
683
- throw new Error(`Failed to fetch types: ${response.status}`);
1215
+ if (fs.exists(PACKAGE_JSON_PATH)) {
1216
+ const content = fs.readFile(PACKAGE_JSON_PATH);
1217
+ return JSON.parse(content);
684
1218
  }
685
- const content = await response.text();
686
- const typePath = `/node_modules/${packageName}/index.d.ts`;
687
- types.set(typePath, content);
688
- const refs = parseTypeReferences(content);
689
- await fetchReferencedTypes(refs, typesUrl, packageName, types);
690
- } catch (err) {
691
- console.warn(`Failed to fetch types for ${packageName}:`, err);
692
- throw err;
693
- }
694
- return types;
695
- }
696
- function parseTypeReferences(content) {
697
- const paths = [];
698
- const types = [];
699
- const pathRegex = /\/\/\/\s*<reference\s+path="([^"]+)"\s*\/>/g;
700
- let match;
701
- while ((match = pathRegex.exec(content)) !== null) {
702
- if (match[1])
703
- paths.push(match[1]);
704
- }
705
- const typesRegex = /\/\/\/\s*<reference\s+types="([^"]+)"\s*\/>/g;
706
- while ((match = typesRegex.exec(content)) !== null) {
707
- if (match[1])
708
- types.push(match[1]);
709
- }
710
- return { paths, types };
711
- }
712
- async function fetchReferencedTypes(refs, baseUrl, packageName, collected, visited = new Set) {
713
- for (const pathRef of refs.paths) {
714
- const refUrl = new URL(pathRef, baseUrl).href;
715
- if (visited.has(refUrl))
716
- continue;
717
- visited.add(refUrl);
718
- try {
719
- const response = await fetch(refUrl);
720
- if (!response.ok)
721
- continue;
722
- const content = await response.text();
723
- const fileName = pathRef.split("/").pop() ?? "types.d.ts";
724
- const typePath = `/node_modules/${packageName}/${fileName}`;
725
- collected.set(typePath, content);
726
- const nestedRefs = parseTypeReferences(content);
727
- await fetchReferencedTypes(nestedRefs, refUrl, packageName, collected, visited);
728
- } catch {}
729
- }
1219
+ } catch {}
1220
+ return {};
730
1221
  }
731
- async function fetchSubpathTypes(packageName, subpath, version) {
732
- const types = new Map;
733
- try {
734
- const info = await fetchPackageInfo(packageName, version, subpath);
735
- if (!info.typesUrl) {
736
- return types;
737
- }
738
- const response = await fetch(info.typesUrl);
739
- if (!response.ok) {
740
- return types;
741
- }
742
- const content = await response.text();
743
- const typePath = `/node_modules/${packageName}/${subpath}.d.ts`;
744
- types.set(typePath, content);
745
- const indexTypePath = `/node_modules/${packageName}/${subpath}/index.d.ts`;
746
- types.set(indexTypePath, content);
747
- const refs = parseTypeReferences(content);
748
- await fetchReferencedTypes(refs, info.typesUrl, packageName, types);
749
- } catch (err) {
750
- console.warn(`Failed to fetch types for ${packageName}/${subpath}:`, err);
1222
+ function getEntryPoint(fs) {
1223
+ const pkg = readPackageJson(fs);
1224
+ const main = pkg.main ?? DEFAULT_ENTRY_POINT;
1225
+ if (main.startsWith("/")) {
1226
+ return main;
751
1227
  }
752
- return types;
753
- }
754
- async function fetchTypesPackageContent(name, version) {
755
- const url = version ? `${ESM_CDN_BASE}/${name}@${version}` : `${ESM_CDN_BASE}/${name}`;
756
- const headResponse = await fetch(url, { method: "HEAD" });
757
- if (!headResponse.ok) {
758
- throw new Error(`Package not found: ${name}${version ? `@${version}` : ""}`);
759
- }
760
- const resolvedVersion = extractVersionFromUrl(headResponse.url, name) ?? version ?? "latest";
761
- const indexUrl = `${ESM_CDN_BASE}/${name}@${resolvedVersion}/index.d.ts`;
762
- const response = await fetch(indexUrl);
763
- if (!response.ok) {
764
- throw new Error(`Failed to fetch types from ${name}: ${response.status}`);
765
- }
766
- const content = await response.text();
767
- const types = new Map;
768
- const typePath = `/node_modules/${name}/index.d.ts`;
769
- types.set(typePath, content);
770
- const refs = parseTypeReferences(content);
771
- for (const pathRef of refs.paths) {
772
- try {
773
- const refUrl = new URL(pathRef, indexUrl).href;
774
- const refResponse = await fetch(refUrl);
775
- if (refResponse.ok) {
776
- const refContent = await refResponse.text();
777
- const fileName = pathRef.startsWith("./") ? pathRef.slice(2) : pathRef;
778
- const refTypePath = `/node_modules/${name}/${fileName}`;
779
- types.set(refTypePath, refContent);
780
- }
781
- } catch {}
1228
+ if (main.startsWith("./")) {
1229
+ return "/" + main.slice(2);
782
1230
  }
783
- return { version: resolvedVersion, types };
1231
+ return "/" + main;
784
1232
  }
785
- async function getPackageManifest(fs) {
786
- try {
787
- if (await fs.exists(PACKAGE_JSON_PATH)) {
788
- const content = await fs.readFile(PACKAGE_JSON_PATH);
789
- const parsed = JSON.parse(content);
790
- return {
791
- dependencies: parsed.dependencies ?? {}
792
- };
793
- }
794
- } catch {}
795
- return { dependencies: {} };
1233
+ function getInstalledPackages(fs) {
1234
+ const pkg = readPackageJson(fs);
1235
+ return pkg.dependencies ?? {};
796
1236
  }
797
- async function savePackageManifest(fs, manifest) {
1237
+ function saveInstalledPackages(fs, dependencies) {
798
1238
  let existing = {};
799
1239
  try {
800
- if (await fs.exists(PACKAGE_JSON_PATH)) {
801
- const content = await fs.readFile(PACKAGE_JSON_PATH);
1240
+ if (fs.exists(PACKAGE_JSON_PATH)) {
1241
+ const content = fs.readFile(PACKAGE_JSON_PATH);
802
1242
  existing = JSON.parse(content);
803
1243
  }
804
1244
  } catch {}
805
1245
  const updated = {
806
1246
  ...existing,
807
- dependencies: manifest.dependencies
808
- };
809
- await fs.writeFile(PACKAGE_JSON_PATH, JSON.stringify(updated, null, 2));
810
- }
811
- async function installPackage(fs, packageSpec, options) {
812
- const { name, version } = parsePackageSpec(packageSpec);
813
- const { cache } = options ?? {};
814
- if (isTypesPackage(name)) {
815
- return installTypesPackage(fs, name, version, cache);
816
- }
817
- const info = await fetchPackageInfo(name, version);
818
- const packageDir = `/node_modules/${name}`;
819
- await ensureDir(fs, packageDir);
820
- const packageJsonPath = `${packageDir}/package.json`;
821
- const packageJson = {
822
- name,
823
- version: info.version,
824
- types: "./index.d.ts",
825
- main: "./index.js"
826
- };
827
- await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
828
- let typeFiles = null;
829
- let fromCache = false;
830
- if (cache) {
831
- typeFiles = cache.get(name, info.version);
832
- if (typeFiles) {
833
- fromCache = true;
834
- }
835
- }
836
- let typesError;
837
- if (!typeFiles) {
838
- typeFiles = new Map;
839
- if (info.typesUrl) {
840
- try {
841
- const mainTypes = await fetchTypeDefinitions(info.typesUrl, name);
842
- for (const [path, content] of mainTypes) {
843
- typeFiles.set(path, content);
844
- }
845
- } catch (err) {
846
- typesError = err instanceof Error ? err.message : String(err);
847
- }
848
- } else {
849
- typesError = "No TypeScript types available from esm.sh";
850
- }
851
- const knownSubpaths = KNOWN_SUBPATHS[name];
852
- if (knownSubpaths && knownSubpaths.length > 0) {
853
- const subpathResults = await Promise.allSettled(knownSubpaths.map((subpath) => fetchSubpathTypes(name, subpath, info.version)));
854
- for (const result of subpathResults) {
855
- if (result.status === "fulfilled") {
856
- for (const [path, content] of result.value) {
857
- typeFiles.set(path, content);
858
- }
859
- }
860
- }
861
- }
862
- if (cache && typeFiles.size > 0) {
863
- cache.set(name, info.version, typeFiles);
864
- }
865
- }
866
- for (const [path, content] of typeFiles) {
867
- const dir = path.substring(0, path.lastIndexOf("/"));
868
- await ensureDir(fs, dir);
869
- await fs.writeFile(path, content);
870
- }
871
- const manifest = await getPackageManifest(fs);
872
- manifest.dependencies[name] = info.version;
873
- await savePackageManifest(fs, manifest);
874
- return {
875
- name,
876
- version: info.version,
877
- typesInstalled: typeFiles.size > 0,
878
- typeFilesCount: typeFiles.size,
879
- typesError,
880
- fromCache
881
- };
882
- }
883
- async function installTypesPackage(fs, name, version, cache) {
884
- const url = version ? `${ESM_CDN_BASE}/${name}@${version}` : `${ESM_CDN_BASE}/${name}`;
885
- const headResponse = await fetch(url, { method: "HEAD" });
886
- if (!headResponse.ok) {
887
- throw new Error(`Package not found: ${name}${version ? `@${version}` : ""}`);
888
- }
889
- const resolvedVersion = extractVersionFromUrl(headResponse.url, name) ?? version ?? "latest";
890
- let types = null;
891
- let fromCache = false;
892
- if (cache) {
893
- types = cache.get(name, resolvedVersion);
894
- if (types) {
895
- fromCache = true;
896
- }
897
- }
898
- if (!types) {
899
- const result = await fetchTypesPackageContent(name, version);
900
- types = result.types;
901
- if (cache && types.size > 0) {
902
- cache.set(name, resolvedVersion, types);
903
- }
904
- }
905
- const packageDir = `/node_modules/${name}`;
906
- await ensureDir(fs, packageDir);
907
- const packageJsonPath = `${packageDir}/package.json`;
908
- const packageJson = {
909
- name,
910
- version: resolvedVersion,
911
- types: "./index.d.ts"
912
- };
913
- await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
914
- for (const [path, content] of types) {
915
- const dir = path.substring(0, path.lastIndexOf("/"));
916
- await ensureDir(fs, dir);
917
- await fs.writeFile(path, content);
918
- }
919
- const manifest = await getPackageManifest(fs);
920
- manifest.dependencies[name] = resolvedVersion;
921
- await savePackageManifest(fs, manifest);
922
- return {
923
- name,
924
- version: resolvedVersion,
925
- typesInstalled: types.size > 0,
926
- typeFilesCount: types.size,
927
- fromCache
1247
+ dependencies
928
1248
  };
1249
+ fs.writeFile(PACKAGE_JSON_PATH, JSON.stringify(updated, null, 2));
929
1250
  }
930
- async function ensureDir(fs, path) {
1251
+ function ensureDir(fs, path) {
931
1252
  if (path === "/" || path === "")
932
1253
  return;
933
- if (await fs.exists(path)) {
934
- const stat = await fs.stat(path);
1254
+ if (fs.exists(path)) {
1255
+ const stat = fs.stat(path);
935
1256
  if (stat.isDirectory)
936
1257
  return;
937
1258
  }
938
1259
  const parent = path.substring(0, path.lastIndexOf("/")) || "/";
939
- await ensureDir(fs, parent);
940
- await fs.mkdir(path);
941
- }
942
- async function uninstallPackage(fs, packageName) {
943
- const manifest = await getPackageManifest(fs);
944
- if (!(packageName in manifest.dependencies)) {
945
- return false;
946
- }
947
- delete manifest.dependencies[packageName];
948
- await savePackageManifest(fs, manifest);
949
- const typesPath = `/node_modules/${packageName}`;
950
- if (await fs.exists(typesPath)) {
951
- await removePath(fs, typesPath);
952
- }
953
- return true;
954
- }
955
- async function removePath(fs, path) {
956
- if (!await fs.exists(path))
957
- return;
958
- await fs.rm(path, { recursive: true, force: true });
959
- }
960
- function resolveToEsmUrl(importPath, installedPackages) {
961
- const { packageName, subpath } = parseImportPath(importPath);
962
- const version = installedPackages[packageName];
963
- if (!version) {
964
- return null;
965
- }
966
- const baseUrl = `${ESM_CDN_BASE}/${packageName}@${version}`;
967
- return subpath ? `${baseUrl}/${subpath}` : baseUrl;
1260
+ ensureDir(fs, parent);
1261
+ fs.mkdir(path);
968
1262
  }
969
- function parseImportPath(importPath) {
970
- if (importPath.startsWith("@")) {
971
- const parts = importPath.split("/");
972
- if (parts.length >= 2) {
973
- const packageName = `${parts[0]}/${parts[1]}`;
974
- const subpath = parts.slice(2).join("/") || undefined;
975
- return { packageName, subpath };
1263
+ async function createSandboxImpl(fs, options, context) {
1264
+ const {
1265
+ bundler,
1266
+ typechecker,
1267
+ typesResolver,
1268
+ sharedModuleRegistry,
1269
+ executor
1270
+ } = context;
1271
+ let lastBuild = null;
1272
+ const onBuildCallbacks = new Set;
1273
+ if (options.onBuild) {
1274
+ onBuildCallbacks.add(options.onBuild);
1275
+ }
1276
+ if (options.initialFiles) {
1277
+ for (const [path, content] of Object.entries(options.initialFiles)) {
1278
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
1279
+ const dir = normalizedPath.substring(0, normalizedPath.lastIndexOf("/"));
1280
+ if (dir && dir !== "/") {
1281
+ ensureDir(fs, dir);
1282
+ }
1283
+ fs.writeFile(normalizedPath, content);
1284
+ }
1285
+ }
1286
+ if (!fs.exists(PACKAGE_JSON_PATH)) {
1287
+ fs.writeFile(PACKAGE_JSON_PATH, JSON.stringify(DEFAULT_PACKAGE_JSON, null, 2));
1288
+ }
1289
+ if (!fs.exists(TSCONFIG_PATH)) {
1290
+ fs.writeFile(TSCONFIG_PATH, JSON.stringify(DEFAULT_TSCONFIG, null, 2));
1291
+ }
1292
+ async function install(packageSpec) {
1293
+ const { name, version } = parsePackageSpec(packageSpec);
1294
+ let resolvedVersion = version ?? "latest";
1295
+ let typesInstalled = false;
1296
+ let typeFilesCount = 0;
1297
+ let typesError;
1298
+ const fromCache = false;
1299
+ if (typesResolver) {
1300
+ try {
1301
+ const typeFiles = await typesResolver.resolveTypes(name, version);
1302
+ const packageDir = `/node_modules/${name}`;
1303
+ ensureDir(fs, packageDir);
1304
+ let typesEntry = "index.d.ts";
1305
+ for (const [filePath, content] of Object.entries(typeFiles)) {
1306
+ const fullPath = filePath.startsWith("/") ? filePath : `${packageDir}/${filePath}`;
1307
+ const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
1308
+ ensureDir(fs, dir);
1309
+ fs.writeFile(fullPath, content);
1310
+ typeFilesCount++;
1311
+ const relativePath = fullPath.replace(`${packageDir}/`, "");
1312
+ if (relativePath === "index.d.ts") {
1313
+ typesEntry = "index.d.ts";
1314
+ } else if (typesEntry === "index.d.ts" && relativePath.endsWith(".d.ts") && !relativePath.includes("/")) {
1315
+ typesEntry = relativePath;
1316
+ }
1317
+ }
1318
+ typesInstalled = typeFilesCount > 0;
1319
+ if (typesInstalled) {
1320
+ const pkgJsonPath = `${packageDir}/package.json`;
1321
+ const pkgJson = {
1322
+ name,
1323
+ version: resolvedVersion,
1324
+ types: typesEntry,
1325
+ main: typesEntry.replace(/\.d\.ts$/, ".js")
1326
+ };
1327
+ fs.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
1328
+ }
1329
+ if (!version) {
1330
+ resolvedVersion = "latest";
1331
+ }
1332
+ } catch (err) {
1333
+ typesError = err instanceof Error ? err.message : String(err);
1334
+ }
976
1335
  }
977
- return { packageName: importPath };
978
- }
979
- const slashIndex = importPath.indexOf("/");
980
- if (slashIndex === -1) {
981
- return { packageName: importPath };
982
- }
983
- return {
984
- packageName: importPath.slice(0, slashIndex),
985
- subpath: importPath.slice(slashIndex + 1)
986
- };
987
- }
988
- async function listPackages(fs) {
989
- const manifest = await getPackageManifest(fs);
990
- return Object.entries(manifest.dependencies).map(([name, version]) => ({
991
- name,
992
- version
993
- }));
994
- }
995
-
996
- // src/shared-modules.ts
997
- var GLOBAL_KEY = "__sandlot_shared_modules__";
998
-
999
- class SharedModuleRegistry {
1000
- modules = new Map;
1001
- exportNames = new Map;
1002
- constructor() {
1003
- globalThis[GLOBAL_KEY] = this;
1004
- }
1005
- register(moduleId, module) {
1006
- this.modules.set(moduleId, module);
1007
- this.exportNames.set(moduleId, introspectExports(module));
1008
- return this;
1336
+ const dependencies = getInstalledPackages(fs);
1337
+ dependencies[name] = resolvedVersion;
1338
+ saveInstalledPackages(fs, dependencies);
1339
+ return {
1340
+ name,
1341
+ version: resolvedVersion,
1342
+ typesInstalled,
1343
+ typeFilesCount,
1344
+ typesError,
1345
+ fromCache
1346
+ };
1009
1347
  }
1010
- registerAll(modules) {
1011
- for (const [id, mod] of Object.entries(modules)) {
1012
- this.register(id, mod);
1348
+ async function uninstall(packageName) {
1349
+ const dependencies = getInstalledPackages(fs);
1350
+ if (!(packageName in dependencies)) {
1351
+ return { name: packageName, removed: false };
1352
+ }
1353
+ delete dependencies[packageName];
1354
+ saveInstalledPackages(fs, dependencies);
1355
+ const typesPath = `/node_modules/${packageName}`;
1356
+ if (fs.exists(typesPath)) {
1357
+ fs.rm(typesPath, { recursive: true, force: true });
1358
+ }
1359
+ return { name: packageName, removed: true };
1360
+ }
1361
+ async function build(buildOptions) {
1362
+ const buildEntryPoint = buildOptions?.entryPoint ?? getEntryPoint(fs);
1363
+ const skipTypecheck = buildOptions?.skipTypecheck ?? false;
1364
+ const minify = buildOptions?.minify ?? false;
1365
+ const format = buildOptions?.format ?? "esm";
1366
+ if (!fs.exists(buildEntryPoint)) {
1367
+ return {
1368
+ success: false,
1369
+ phase: "entry",
1370
+ message: `Entry point not found: ${buildEntryPoint}`
1371
+ };
1013
1372
  }
1014
- return this;
1015
- }
1016
- unregister(moduleId) {
1017
- return this.modules.delete(moduleId);
1018
- }
1019
- get(moduleId) {
1020
- const mod = this.modules.get(moduleId);
1021
- if (mod === undefined && !this.modules.has(moduleId)) {
1022
- const available = this.list();
1023
- throw new Error(`Shared module "${moduleId}" not registered. ` + `Available: ${available.length > 0 ? available.join(", ") : "(none)"}. ` + `Call registerSharedModules({ '${moduleId}': ... }) in your host application.`);
1373
+ if (!skipTypecheck && typechecker) {
1374
+ const typecheckResult = await typechecker.typecheck({
1375
+ fs,
1376
+ entryPoint: buildEntryPoint,
1377
+ tsconfigPath: TSCONFIG_PATH
1378
+ });
1379
+ if (!typecheckResult.success) {
1380
+ return {
1381
+ success: false,
1382
+ phase: "typecheck",
1383
+ diagnostics: typecheckResult.diagnostics
1384
+ };
1385
+ }
1024
1386
  }
1025
- return mod;
1026
- }
1027
- has(moduleId) {
1028
- return this.modules.has(moduleId);
1029
- }
1030
- list() {
1031
- return [...this.modules.keys()];
1032
- }
1033
- getExportNames(moduleId) {
1034
- return this.exportNames.get(moduleId) ?? [];
1035
- }
1036
- clear() {
1037
- this.modules.clear();
1038
- this.exportNames.clear();
1039
- }
1040
- get size() {
1041
- return this.modules.size;
1042
- }
1043
- }
1044
- function introspectExports(module) {
1045
- if (module === null || module === undefined) {
1046
- return [];
1047
- }
1048
- if (typeof module !== "object" && typeof module !== "function") {
1049
- return [];
1050
- }
1051
- const exports = [];
1052
- for (const key of Object.keys(module)) {
1053
- if (isValidIdentifier(key)) {
1054
- exports.push(key);
1387
+ const installedPackages = getInstalledPackages(fs);
1388
+ const bundleResult = await bundler.bundle({
1389
+ fs,
1390
+ entryPoint: buildEntryPoint,
1391
+ installedPackages,
1392
+ sharedModules: sharedModuleRegistry?.list() ?? [],
1393
+ sharedModuleRegistry: sharedModuleRegistry ?? undefined,
1394
+ format,
1395
+ minify
1396
+ });
1397
+ if (!bundleResult.success) {
1398
+ return {
1399
+ success: false,
1400
+ phase: "bundle",
1401
+ bundleErrors: bundleResult.errors,
1402
+ bundleWarnings: bundleResult.warnings
1403
+ };
1055
1404
  }
1405
+ const output = {
1406
+ success: true,
1407
+ code: bundleResult.code,
1408
+ includedFiles: bundleResult.includedFiles,
1409
+ warnings: bundleResult.warnings
1410
+ };
1411
+ lastBuild = output;
1412
+ for (const callback of onBuildCallbacks) {
1413
+ try {
1414
+ await callback(output);
1415
+ } catch (err) {
1416
+ console.error("[sandlot] onBuild callback error:", err);
1417
+ }
1418
+ }
1419
+ return output;
1056
1420
  }
1057
- return exports;
1058
- }
1059
- function isValidIdentifier(name) {
1060
- if (name.length === 0)
1061
- return false;
1062
- if (!/^[a-zA-Z_$]/.test(name))
1063
- return false;
1064
- if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name))
1065
- return false;
1066
- const reserved = ["default", "class", "function", "var", "let", "const", "import", "export"];
1067
- if (reserved.includes(name))
1068
- return false;
1069
- return true;
1070
- }
1071
- var defaultRegistry = null;
1072
- function getSharedModuleRegistry() {
1073
- if (!defaultRegistry) {
1074
- defaultRegistry = new SharedModuleRegistry;
1075
- }
1076
- return defaultRegistry;
1077
- }
1078
- function hasSharedModuleRegistry() {
1079
- return GLOBAL_KEY in globalThis;
1080
- }
1081
- function registerSharedModules(modules) {
1082
- getSharedModuleRegistry().registerAll(modules);
1083
- }
1084
- function unregisterSharedModule(moduleId) {
1085
- return getSharedModuleRegistry().unregister(moduleId);
1086
- }
1087
- function clearSharedModules() {
1088
- getSharedModuleRegistry().clear();
1089
- }
1090
- function getSharedModuleExports(moduleId) {
1091
- return getSharedModuleRegistry().getExportNames(moduleId);
1092
- }
1093
- function getSharedModuleRuntimeCode(moduleId) {
1094
- return `
1095
- (function() {
1096
- var registry = globalThis["${GLOBAL_KEY}"];
1097
- if (!registry) {
1098
- throw new Error(
1099
- 'Sandlot SharedModuleRegistry not found. ' +
1100
- 'Call registerSharedModules() in your host application before loading dynamic modules.'
1101
- );
1102
- }
1103
- return registry.get(${JSON.stringify(moduleId)});
1104
- })()
1105
- `.trim();
1106
- }
1107
-
1108
- // src/bundler.ts
1109
- var esbuild = null;
1110
- async function getEsbuild() {
1111
- if (esbuild)
1112
- return esbuild;
1113
- const cdnUrl = `https://esm.sh/esbuild-wasm@${ESBUILD_VERSION}`;
1114
- const mod = await import(cdnUrl);
1115
- esbuild = mod.default ?? mod;
1116
- if (typeof esbuild?.initialize !== "function") {
1117
- console.error("esbuild-wasm module structure:", mod);
1118
- throw new Error("Failed to load esbuild-wasm: initialize function not found");
1119
- }
1120
- return esbuild;
1121
- }
1122
- var ESBUILD_VERSION = "0.27.2";
1123
- var initialized = false;
1124
- var initPromise = null;
1125
- function getWasmUrl() {
1126
- return `https://unpkg.com/esbuild-wasm@${ESBUILD_VERSION}/esbuild.wasm`;
1127
- }
1128
- function checkCrossOriginIsolation() {
1129
- if (typeof window === "undefined")
1130
- return;
1131
- if (!window.crossOriginIsolated) {
1132
- console.warn(`[sandlot] Cross-origin isolation is not enabled. esbuild-wasm may have reduced performance or fail on some browsers.
1133
- To enable, add these headers to your dev server:
1134
- Cross-Origin-Embedder-Policy: require-corp
1135
- Cross-Origin-Opener-Policy: same-origin
1136
- In Vite, add a plugin to configureServer. See sandlot README for details.`);
1137
- }
1138
- }
1139
- async function initBundler() {
1140
- if (initialized)
1141
- return;
1142
- if (initPromise) {
1143
- await initPromise;
1144
- return;
1145
- }
1146
- checkCrossOriginIsolation();
1147
- initPromise = (async () => {
1148
- const es = await getEsbuild();
1149
- await es.initialize({
1150
- wasmURL: getWasmUrl()
1421
+ async function typecheck(typecheckOptions) {
1422
+ if (!typechecker) {
1423
+ return { success: true, diagnostics: [] };
1424
+ }
1425
+ const checkEntryPoint = typecheckOptions?.entryPoint ?? getEntryPoint(fs);
1426
+ if (!fs.exists(checkEntryPoint)) {
1427
+ return {
1428
+ success: false,
1429
+ diagnostics: [
1430
+ {
1431
+ message: `Entry point not found: ${checkEntryPoint}`,
1432
+ severity: "error"
1433
+ }
1434
+ ]
1435
+ };
1436
+ }
1437
+ return typechecker.typecheck({
1438
+ fs,
1439
+ entryPoint: checkEntryPoint,
1440
+ tsconfigPath: TSCONFIG_PATH
1151
1441
  });
1152
- })();
1153
- await initPromise;
1154
- initialized = true;
1155
- }
1156
- function isBareImport(path) {
1157
- return !path.startsWith(".") && !path.startsWith("/");
1158
- }
1159
- function getLoader(path) {
1160
- const ext = path.split(".").pop()?.toLowerCase();
1161
- switch (ext) {
1162
- case "ts":
1163
- return "ts";
1164
- case "tsx":
1165
- return "tsx";
1166
- case "jsx":
1167
- return "jsx";
1168
- case "js":
1169
- case "mjs":
1170
- return "js";
1171
- case "json":
1172
- return "json";
1173
- case "css":
1174
- return "css";
1175
- case "txt":
1176
- return "text";
1177
- default:
1178
- return "js";
1179
- }
1180
- }
1181
- function matchesSharedModule(importPath, sharedModuleIds) {
1182
- if (sharedModuleIds.has(importPath)) {
1183
- return importPath;
1184
1442
  }
1185
- for (const moduleId of sharedModuleIds) {
1186
- if (importPath === moduleId || importPath.startsWith(moduleId + "/")) {
1187
- return importPath;
1443
+ async function run(runOptions) {
1444
+ if (!executor) {
1445
+ throw new Error("[sandlot] No executor configured. Provide an executor when creating Sandlot to use run().");
1446
+ }
1447
+ const buildResult = await build({
1448
+ entryPoint: runOptions?.entryPoint,
1449
+ skipTypecheck: runOptions?.skipTypecheck
1450
+ });
1451
+ if (!buildResult.success) {
1452
+ return {
1453
+ success: false,
1454
+ logs: [],
1455
+ error: buildResult.message ?? `Build failed in ${buildResult.phase} phase`,
1456
+ buildFailure: {
1457
+ phase: buildResult.phase,
1458
+ message: buildResult.message
1459
+ }
1460
+ };
1188
1461
  }
1462
+ const executeResult = await executor.execute(buildResult.code, {
1463
+ entryExport: runOptions?.entryExport ?? "main",
1464
+ context: runOptions?.context,
1465
+ timeout: runOptions?.timeout
1466
+ });
1467
+ return {
1468
+ success: executeResult.success,
1469
+ logs: executeResult.logs,
1470
+ returnValue: executeResult.returnValue,
1471
+ error: executeResult.error,
1472
+ executionTimeMs: executeResult.executionTimeMs
1473
+ };
1189
1474
  }
1190
- return null;
1191
- }
1192
- function createVfsPlugin(options) {
1193
- const {
1475
+ const sandboxRef = {
1194
1476
  fs,
1195
- entryPoint,
1196
- npmImports,
1197
- installedPackages,
1198
- includedFiles,
1199
- sharedModuleIds
1200
- } = options;
1201
- return {
1202
- name: "virtual-fs",
1203
- setup(build) {
1204
- build.onResolve({ filter: /.*/ }, async (args) => {
1205
- if (args.kind === "entry-point") {
1206
- return { path: entryPoint, namespace: "vfs" };
1207
- }
1208
- if (isBareImport(args.path)) {
1209
- const sharedMatch = matchesSharedModule(args.path, sharedModuleIds);
1210
- if (sharedMatch) {
1211
- return {
1212
- path: sharedMatch,
1213
- namespace: "sandlot-shared"
1214
- };
1215
- }
1216
- switch (npmImports) {
1217
- case "cdn": {
1218
- const esmUrl = resolveToEsmUrl(args.path, installedPackages);
1219
- if (esmUrl) {
1220
- return { path: esmUrl, external: true };
1221
- }
1222
- return { path: args.path, external: true };
1223
- }
1224
- case "external":
1225
- return { path: args.path, external: true };
1226
- case "bundle": {
1227
- const resolved2 = fs.resolvePath(args.resolveDir, `node_modules/${args.path}`);
1228
- const exists = await fs.exists(resolved2);
1229
- if (exists) {
1230
- return { path: resolved2, namespace: "vfs" };
1231
- }
1232
- return { path: args.path, external: true };
1233
- }
1234
- }
1235
- }
1236
- const resolved = fs.resolvePath(args.resolveDir, args.path);
1237
- const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".json"];
1238
- const hasExtension = extensions.some((ext) => resolved.endsWith(ext));
1239
- if (hasExtension) {
1240
- const exists = await fs.exists(resolved);
1241
- if (exists) {
1242
- return { path: resolved, namespace: "vfs" };
1243
- }
1244
- return { errors: [{ text: `File not found: ${resolved}` }] };
1245
- }
1246
- for (const ext of extensions) {
1247
- const withExt = resolved + ext;
1248
- if (await fs.exists(withExt)) {
1249
- return { path: withExt, namespace: "vfs" };
1250
- }
1251
- }
1252
- for (const ext of extensions) {
1253
- const indexPath = `${resolved}/index${ext}`;
1254
- if (await fs.exists(indexPath)) {
1255
- return { path: indexPath, namespace: "vfs" };
1256
- }
1257
- }
1258
- return { errors: [{ text: `Cannot resolve: ${args.path} from ${args.resolveDir}` }] };
1259
- });
1260
- build.onLoad({ filter: /.*/, namespace: "sandlot-shared" }, (args) => {
1261
- const contents = `
1262
- const __sandlot_mod__ = ${getSharedModuleRuntimeCode(args.path)};
1263
- export default __sandlot_mod__.default ?? __sandlot_mod__;
1264
- ${generateNamedExports(args.path)}
1265
- `;
1266
- return {
1267
- contents: contents.trim(),
1268
- loader: "js"
1269
- };
1270
- });
1271
- build.onLoad({ filter: /.*/, namespace: "vfs" }, async (args) => {
1272
- try {
1273
- const contents = await fs.readFile(args.path);
1274
- includedFiles.add(args.path);
1275
- return {
1276
- contents,
1277
- loader: getLoader(args.path),
1278
- resolveDir: args.path.substring(0, args.path.lastIndexOf("/"))
1279
- };
1280
- } catch (err) {
1281
- return {
1282
- errors: [{ text: `Failed to read ${args.path}: ${err}` }]
1283
- };
1284
- }
1477
+ install,
1478
+ uninstall,
1479
+ build,
1480
+ typecheck,
1481
+ run
1482
+ };
1483
+ let bashInstance = null;
1484
+ function getBash() {
1485
+ if (!bashInstance) {
1486
+ const commands = createDefaultCommands(sandboxRef);
1487
+ bashInstance = new Bash({
1488
+ cwd: "/",
1489
+ fs: wrapFilesystemForJustBash(fs),
1490
+ customCommands: commands
1285
1491
  });
1286
1492
  }
1287
- };
1288
- }
1289
- function generateNamedExports(moduleId) {
1290
- const exports = getSharedModuleExports(moduleId);
1291
- if (exports.length > 0) {
1292
- return exports.map((name) => `export const ${name} = __sandlot_mod__.${name};`).join(`
1293
- `);
1493
+ return bashInstance;
1494
+ }
1495
+ async function exec(command) {
1496
+ const bash = getBash();
1497
+ const result = await bash.exec(command);
1498
+ return {
1499
+ exitCode: result.exitCode,
1500
+ stdout: result.stdout,
1501
+ stderr: result.stderr
1502
+ };
1294
1503
  }
1295
- return `// No exports discovered for "${moduleId}" - use default import or call registerSharedModules() first`;
1296
- }
1297
- async function bundle(options) {
1298
- await initBundler();
1299
- const {
1300
- fs,
1301
- entryPoint,
1302
- external = [],
1303
- npmImports = "cdn",
1304
- sharedModules = [],
1305
- format = "esm",
1306
- minify = false,
1307
- sourcemap = false,
1308
- globalName,
1309
- target = ["es2020"]
1310
- } = options;
1311
- const normalizedEntry = entryPoint.startsWith("/") ? entryPoint : `/${entryPoint}`;
1312
- if (!await fs.exists(normalizedEntry)) {
1313
- throw new Error(`Entry point not found: ${normalizedEntry}`);
1314
- }
1315
- const manifest = await getPackageManifest(fs);
1316
- const installedPackages = manifest.dependencies;
1317
- const sharedModuleIds = new Set(sharedModules);
1318
- const includedFiles = new Set;
1319
- const plugin = createVfsPlugin({
1320
- fs,
1321
- entryPoint: normalizedEntry,
1322
- npmImports,
1323
- installedPackages,
1324
- includedFiles,
1325
- sharedModuleIds
1326
- });
1327
- const es = await getEsbuild();
1328
- const result = await es.build({
1329
- entryPoints: [normalizedEntry],
1330
- bundle: true,
1331
- write: false,
1332
- format,
1333
- minify,
1334
- sourcemap: sourcemap ? "inline" : false,
1335
- globalName,
1336
- target,
1337
- external,
1338
- plugins: [plugin],
1339
- jsx: "automatic"
1340
- });
1341
- const code = result.outputFiles?.[0]?.text ?? "";
1342
1504
  return {
1343
- code,
1344
- warnings: result.warnings,
1345
- includedFiles: Array.from(includedFiles)
1505
+ fs,
1506
+ exec,
1507
+ get lastBuild() {
1508
+ return lastBuild;
1509
+ },
1510
+ getState() {
1511
+ return { files: fs.getFiles() };
1512
+ },
1513
+ onBuild(callback) {
1514
+ onBuildCallbacks.add(callback);
1515
+ return () => {
1516
+ onBuildCallbacks.delete(callback);
1517
+ };
1518
+ },
1519
+ install,
1520
+ uninstall,
1521
+ build,
1522
+ typecheck,
1523
+ run,
1524
+ readFile: (path) => fs.readFile(path),
1525
+ writeFile: (path, content) => fs.writeFile(path, content)
1346
1526
  };
1347
1527
  }
1348
- async function bundleToUrl(options) {
1349
- const result = await bundle(options);
1350
- const blob = new Blob([result.code], { type: "application/javascript" });
1351
- return URL.createObjectURL(blob);
1352
- }
1353
- async function bundleAndImport(options) {
1354
- const url = await bundleToUrl(options);
1355
- try {
1356
- return await import(url);
1357
- } finally {
1358
- URL.revokeObjectURL(url);
1359
- }
1360
- }
1361
1528
 
1362
- // src/commands/types.ts
1363
- function formatEsbuildMessages(messages) {
1364
- if (messages.length === 0)
1365
- return "";
1366
- return messages.map((msg) => {
1367
- if (msg.location) {
1368
- const { file, line, column } = msg.location;
1369
- const loc = file ? `${file}${line ? `:${line}` : ""}${column ? `:${column}` : ""}` : "";
1370
- return loc ? `${loc}: ${msg.text}` : msg.text;
1371
- }
1372
- return msg.text;
1373
- }).join(`
1374
- `);
1375
- }
1376
- // src/commands/compile.ts
1377
- import { defineCommand } from "just-bash/browser";
1378
-
1379
- // src/typechecker.ts
1380
- import ts from "typescript";
1381
- function categoryToString(category) {
1382
- switch (category) {
1383
- case ts.DiagnosticCategory.Error:
1384
- return "error";
1385
- case ts.DiagnosticCategory.Warning:
1386
- return "warning";
1387
- case ts.DiagnosticCategory.Suggestion:
1388
- return "suggestion";
1389
- case ts.DiagnosticCategory.Message:
1390
- return "message";
1391
- default:
1392
- return "error";
1393
- }
1394
- }
1395
- function convertDiagnostic(diag) {
1396
- let file = null;
1397
- let line = null;
1398
- let column = null;
1399
- if (diag.file && diag.start !== undefined) {
1400
- file = diag.file.fileName;
1401
- const pos = diag.file.getLineAndCharacterOfPosition(diag.start);
1402
- line = pos.line + 1;
1403
- column = pos.character + 1;
1404
- }
1405
- return {
1406
- file,
1407
- line,
1408
- column,
1409
- code: diag.code,
1410
- category: categoryToString(diag.category),
1411
- message: ts.flattenDiagnosticMessageText(diag.messageText, `
1412
- `)
1413
- };
1414
- }
1415
- function normalizePath(path) {
1416
- if (!path.startsWith("/")) {
1417
- return "/" + path;
1418
- }
1419
- return path;
1420
- }
1421
- async function preloadFiles(fs) {
1422
- const cache = new Map;
1423
- const allPaths = fs.getAllPaths();
1424
- for (const path of allPaths) {
1425
- if (path.endsWith(".ts") || path.endsWith(".tsx") || path.endsWith(".js") || path.endsWith(".jsx") || path.endsWith(".json") || path.endsWith(".d.ts")) {
1426
- try {
1427
- const stat = await fs.stat(path);
1428
- if (stat.isFile) {
1429
- const content = await fs.readFile(path);
1430
- cache.set(path, content);
1431
- }
1432
- } catch {}
1433
- }
1434
- }
1435
- return cache;
1436
- }
1437
- function parseTsConfig(configText, _configPath) {
1438
- try {
1439
- const config = JSON.parse(configText);
1440
- const compilerOptions = config.compilerOptions || {};
1441
- const options = {
1442
- ...getDefaultCompilerOptions()
1443
- };
1444
- if (compilerOptions.target) {
1445
- const targetMap = {
1446
- es5: ts.ScriptTarget.ES5,
1447
- es6: ts.ScriptTarget.ES2015,
1448
- es2015: ts.ScriptTarget.ES2015,
1449
- es2016: ts.ScriptTarget.ES2016,
1450
- es2017: ts.ScriptTarget.ES2017,
1451
- es2018: ts.ScriptTarget.ES2018,
1452
- es2019: ts.ScriptTarget.ES2019,
1453
- es2020: ts.ScriptTarget.ES2020,
1454
- es2021: ts.ScriptTarget.ES2021,
1455
- es2022: ts.ScriptTarget.ES2022,
1456
- esnext: ts.ScriptTarget.ESNext
1457
- };
1458
- options.target = targetMap[compilerOptions.target.toLowerCase()] ?? ts.ScriptTarget.ES2020;
1459
- }
1460
- if (compilerOptions.module) {
1461
- const moduleMap = {
1462
- commonjs: ts.ModuleKind.CommonJS,
1463
- amd: ts.ModuleKind.AMD,
1464
- umd: ts.ModuleKind.UMD,
1465
- system: ts.ModuleKind.System,
1466
- es6: ts.ModuleKind.ES2015,
1467
- es2015: ts.ModuleKind.ES2015,
1468
- es2020: ts.ModuleKind.ES2020,
1469
- es2022: ts.ModuleKind.ES2022,
1470
- esnext: ts.ModuleKind.ESNext,
1471
- node16: ts.ModuleKind.Node16,
1472
- nodenext: ts.ModuleKind.NodeNext
1473
- };
1474
- options.module = moduleMap[compilerOptions.module.toLowerCase()] ?? ts.ModuleKind.ESNext;
1475
- }
1476
- if (compilerOptions.moduleResolution) {
1477
- const resolutionMap = {
1478
- classic: ts.ModuleResolutionKind.Classic,
1479
- node: ts.ModuleResolutionKind.NodeJs,
1480
- node10: ts.ModuleResolutionKind.NodeJs,
1481
- node16: ts.ModuleResolutionKind.Node16,
1482
- nodenext: ts.ModuleResolutionKind.NodeNext,
1483
- bundler: ts.ModuleResolutionKind.Bundler
1484
- };
1485
- options.moduleResolution = resolutionMap[compilerOptions.moduleResolution.toLowerCase()] ?? ts.ModuleResolutionKind.Bundler;
1486
- }
1487
- if (compilerOptions.jsx) {
1488
- const jsxMap = {
1489
- preserve: ts.JsxEmit.Preserve,
1490
- react: ts.JsxEmit.React,
1491
- "react-jsx": ts.JsxEmit.ReactJSX,
1492
- "react-jsxdev": ts.JsxEmit.ReactJSXDev,
1493
- "react-native": ts.JsxEmit.ReactNative
1494
- };
1495
- options.jsx = jsxMap[compilerOptions.jsx.toLowerCase()] ?? ts.JsxEmit.ReactJSX;
1496
- }
1497
- if (compilerOptions.strict !== undefined)
1498
- options.strict = compilerOptions.strict;
1499
- if (compilerOptions.esModuleInterop !== undefined)
1500
- options.esModuleInterop = compilerOptions.esModuleInterop;
1501
- if (compilerOptions.skipLibCheck !== undefined)
1502
- options.skipLibCheck = compilerOptions.skipLibCheck;
1503
- if (compilerOptions.allowJs !== undefined)
1504
- options.allowJs = compilerOptions.allowJs;
1505
- if (compilerOptions.resolveJsonModule !== undefined)
1506
- options.resolveJsonModule = compilerOptions.resolveJsonModule;
1507
- if (compilerOptions.noImplicitAny !== undefined)
1508
- options.noImplicitAny = compilerOptions.noImplicitAny;
1509
- if (compilerOptions.strictNullChecks !== undefined)
1510
- options.strictNullChecks = compilerOptions.strictNullChecks;
1511
- if (Array.isArray(compilerOptions.lib)) {
1512
- options.lib = compilerOptions.lib.map((lib) => lib.toLowerCase().startsWith("lib.") ? lib : `lib.${lib.toLowerCase()}.d.ts`);
1513
- }
1514
- options.noEmit = true;
1515
- return options;
1516
- } catch (err) {
1517
- console.warn("Error parsing tsconfig.json:", err);
1518
- return getDefaultCompilerOptions();
1519
- }
1520
- }
1521
- function getDefaultCompilerOptions() {
1522
- return {
1523
- target: ts.ScriptTarget.ES2020,
1524
- module: ts.ModuleKind.ESNext,
1525
- moduleResolution: ts.ModuleResolutionKind.Bundler,
1526
- esModuleInterop: true,
1527
- strict: true,
1528
- skipLibCheck: true,
1529
- noEmit: true,
1530
- jsx: ts.JsxEmit.ReactJSX,
1531
- allowJs: true,
1532
- resolveJsonModule: true,
1533
- noLib: false,
1534
- lib: [
1535
- "lib.es2020.d.ts",
1536
- "lib.dom.d.ts",
1537
- "lib.dom.iterable.d.ts"
1538
- ]
1539
- };
1540
- }
1541
- var LIB_PATH_PREFIX = "/node_modules/typescript/lib/";
1542
- function createVfsCompilerHost(fileCache, libFiles, _options) {
1543
- function getLibContent(fileName) {
1544
- const libMatch = fileName.match(/lib\.([^/]+)\.d\.ts$/);
1545
- if (libMatch && libMatch[1]) {
1546
- return libFiles.get(libMatch[1]);
1547
- }
1548
- return;
1549
- }
1550
- return {
1551
- getSourceFile(fileName, languageVersion, onError) {
1552
- const normalizedPath = normalizePath(fileName);
1553
- const content = fileCache.get(normalizedPath);
1554
- if (content !== undefined) {
1555
- return ts.createSourceFile(normalizedPath, content, languageVersion, true);
1556
- }
1557
- const altContent = fileCache.get(fileName);
1558
- if (altContent !== undefined) {
1559
- return ts.createSourceFile(fileName, altContent, languageVersion, true);
1560
- }
1561
- const libContent = getLibContent(fileName);
1562
- if (libContent !== undefined) {
1563
- return ts.createSourceFile(fileName, libContent, languageVersion, true);
1564
- }
1565
- if (onError) {
1566
- onError(`File not found: ${fileName}`);
1567
- }
1568
- return;
1569
- },
1570
- getDefaultLibFileName(options) {
1571
- return LIB_PATH_PREFIX + ts.getDefaultLibFileName(options);
1572
- },
1573
- writeFile() {},
1574
- getCurrentDirectory() {
1575
- return "/";
1576
- },
1577
- getCanonicalFileName(fileName) {
1578
- return fileName;
1579
- },
1580
- useCaseSensitiveFileNames() {
1581
- return true;
1582
- },
1583
- getNewLine() {
1584
- return `
1585
- `;
1586
- },
1587
- fileExists(fileName) {
1588
- const normalizedPath = normalizePath(fileName);
1589
- if (fileCache.has(normalizedPath) || fileCache.has(fileName)) {
1590
- return true;
1591
- }
1592
- return getLibContent(fileName) !== undefined;
1593
- },
1594
- readFile(fileName) {
1595
- const normalizedPath = normalizePath(fileName);
1596
- const content = fileCache.get(normalizedPath) ?? fileCache.get(fileName);
1597
- if (content !== undefined) {
1598
- return content;
1599
- }
1600
- return getLibContent(fileName);
1601
- },
1602
- directoryExists(directoryName) {
1603
- const normalizedDir = normalizePath(directoryName);
1604
- for (const path of fileCache.keys()) {
1605
- if (path.startsWith(normalizedDir + "/")) {
1606
- return true;
1607
- }
1608
- }
1609
- if (normalizedDir === "/node_modules/typescript/lib" || normalizedDir === "/node_modules/typescript") {
1610
- return libFiles.size > 0;
1611
- }
1612
- if (normalizedDir === "/node_modules") {
1613
- return libFiles.size > 0 || Array.from(fileCache.keys()).some((p) => p.startsWith("/node_modules/"));
1614
- }
1615
- if (normalizedDir === "/node_modules/@types") {
1616
- return Array.from(fileCache.keys()).some((p) => p.startsWith("/node_modules/@types/"));
1617
- }
1618
- return false;
1619
- },
1620
- getDirectories(path) {
1621
- const normalizedPath = normalizePath(path);
1622
- const prefix = normalizedPath === "/" ? "/" : normalizedPath + "/";
1623
- const dirs = new Set;
1624
- for (const filePath of fileCache.keys()) {
1625
- if (filePath.startsWith(prefix)) {
1626
- const relative = filePath.slice(prefix.length);
1627
- const firstSlash = relative.indexOf("/");
1628
- if (firstSlash > 0) {
1629
- dirs.add(relative.slice(0, firstSlash));
1630
- }
1631
- }
1632
- }
1633
- if (normalizedPath === "/") {
1634
- const hasNodeModules = libFiles.size > 0 || Array.from(fileCache.keys()).some((p) => p.startsWith("/node_modules/"));
1635
- if (hasNodeModules) {
1636
- dirs.add("node_modules");
1637
- }
1638
- }
1639
- return Array.from(dirs);
1640
- },
1641
- realpath(path) {
1642
- return path;
1643
- },
1644
- getEnvironmentVariable() {
1645
- return;
1646
- }
1647
- };
1648
- }
1649
- async function typecheck(options) {
1529
+ // src/core/sandlot.ts
1530
+ function createSandlot(options) {
1650
1531
  const {
1651
- fs,
1652
- entryPoint,
1653
- tsconfigPath = "/tsconfig.json",
1654
- libFiles = new Map
1532
+ bundler,
1533
+ typechecker,
1534
+ typesResolver,
1535
+ executor,
1536
+ sharedModules,
1537
+ sandboxDefaults = {}
1655
1538
  } = options;
1656
- const normalizedEntry = normalizePath(entryPoint);
1657
- if (!await fs.exists(normalizedEntry)) {
1658
- throw new Error(`Entry point not found: ${normalizedEntry}`);
1659
- }
1660
- const fileCache = await preloadFiles(fs);
1661
- let compilerOptions = getDefaultCompilerOptions();
1662
- const tsconfigContent = fileCache.get(normalizePath(tsconfigPath));
1663
- if (tsconfigContent) {
1664
- compilerOptions = {
1665
- ...parseTsConfig(tsconfigContent, tsconfigPath),
1666
- noEmit: true
1667
- };
1668
- }
1669
- const host = createVfsCompilerHost(fileCache, libFiles, compilerOptions);
1670
- const program = ts.createProgram([normalizedEntry], compilerOptions, host);
1671
- const allDiagnostics = [
1672
- ...program.getSyntacticDiagnostics(),
1673
- ...program.getSemanticDiagnostics(),
1674
- ...program.getDeclarationDiagnostics()
1675
- ];
1676
- const diagnostics = allDiagnostics.map(convertDiagnostic);
1677
- const checkedFiles = program.getSourceFiles().map((sf) => sf.fileName).filter((f) => !f.includes("node_modules/typescript/lib"));
1678
- const hasErrors = diagnostics.some((d) => d.category === "error");
1679
- return {
1680
- diagnostics,
1681
- hasErrors,
1682
- checkedFiles
1683
- };
1684
- }
1685
- function formatDiagnostics(diagnostics) {
1686
- return diagnostics.map((d) => {
1687
- const location = d.file ? `${d.file}${d.line ? `:${d.line}` : ""}${d.column ? `:${d.column}` : ""}` : "(global)";
1688
- return `${location} - ${d.category} TS${d.code}: ${d.message}`;
1689
- }).join(`
1690
- `);
1691
- }
1692
- function formatDiagnosticsForAgent(diagnostics) {
1693
- if (diagnostics.length === 0) {
1694
- return "No type errors found.";
1695
- }
1696
- const errorCount = diagnostics.filter((d) => d.category === "error").length;
1697
- const warningCount = diagnostics.filter((d) => d.category === "warning").length;
1698
- const summary = errorCount > 0 || warningCount > 0 ? `Found ${errorCount} error(s) and ${warningCount} warning(s):
1699
-
1700
- ` : "";
1701
- const formatted = diagnostics.map((d) => {
1702
- const location = d.file ? `${d.file}${d.line ? ` at line ${d.line}` : ""}` : "Global";
1703
- const prefix = d.category === "error" ? "Error" : d.category === "warning" ? "Warning" : "Info";
1704
- return `${prefix} in ${location}: ${d.message} (TS${d.code})`;
1705
- }).join(`
1706
-
1707
- `);
1708
- return summary + formatted;
1709
- }
1710
-
1711
- // src/loader.ts
1712
- class ModuleLoadError extends Error {
1713
- constructor(message, cause) {
1714
- super(message, { cause });
1715
- this.name = "ModuleLoadError";
1716
- }
1717
- }
1718
-
1719
- class ExportNotFoundError extends Error {
1720
- exportName;
1721
- availableExports;
1722
- constructor(exportName, availableExports) {
1723
- super(`Export "${exportName}" not found. Available exports: ${availableExports.length > 0 ? availableExports.join(", ") : "(none)"}`);
1724
- this.exportName = exportName;
1725
- this.availableExports = availableExports;
1726
- this.name = "ExportNotFoundError";
1727
- }
1728
- }
1729
- function createModuleUrl(result) {
1730
- const blob = new Blob([result.code], { type: "application/javascript" });
1731
- return URL.createObjectURL(blob);
1732
- }
1733
- function revokeModuleUrl(url) {
1734
- URL.revokeObjectURL(url);
1735
- }
1736
- async function loadModule(result) {
1737
- const url = createModuleUrl(result);
1738
- try {
1739
- const module = await import(url);
1740
- return module;
1741
- } catch (err) {
1742
- throw new ModuleLoadError(`Failed to load module: ${err instanceof Error ? err.message : String(err)}`, err);
1743
- } finally {
1744
- revokeModuleUrl(url);
1745
- }
1746
- }
1747
- async function loadExport(result, exportName = "default") {
1748
- const module = await loadModule(result);
1749
- if (!(exportName in module)) {
1750
- const availableExports = Object.keys(module).filter((key) => !key.startsWith("__"));
1751
- throw new ExportNotFoundError(exportName, availableExports);
1752
- }
1753
- return module[exportName];
1754
- }
1755
- async function loadDefault(result) {
1756
- return loadExport(result, "default");
1757
- }
1758
- async function getExportNames(result) {
1759
- const module = await loadModule(result);
1760
- return Object.keys(module).filter((key) => !key.startsWith("__"));
1761
- }
1762
- async function hasExport(result, exportName) {
1763
- const module = await loadModule(result);
1764
- return exportName in module;
1765
- }
1766
-
1767
- // src/commands/compile.ts
1768
- function createTscCommand(deps) {
1769
- const { fs, libFiles, tsconfigPath } = deps;
1770
- return defineCommand("tsc", async (args, _ctx) => {
1771
- const entryPoint = args[0];
1772
- if (!entryPoint) {
1773
- return {
1774
- stdout: "",
1775
- stderr: `Usage: tsc <entry-point>
1776
-
1777
- Example: tsc /src/index.ts
1778
- `,
1779
- exitCode: 1
1780
- };
1781
- }
1782
- try {
1783
- if (!await fs.exists(entryPoint)) {
1784
- return {
1785
- stdout: "",
1786
- stderr: `Error: Entry point not found: ${entryPoint}
1787
- `,
1788
- exitCode: 1
1789
- };
1790
- }
1791
- const result = await typecheck({
1792
- fs,
1793
- entryPoint,
1794
- tsconfigPath,
1795
- libFiles
1796
- });
1797
- if (result.hasErrors) {
1798
- const formatted = formatDiagnosticsForAgent(result.diagnostics);
1799
- return {
1800
- stdout: "",
1801
- stderr: formatted + `
1802
- `,
1803
- exitCode: 1
1804
- };
1805
- }
1806
- const checkedCount = result.checkedFiles.length;
1807
- const warningCount = result.diagnostics.filter((d) => d.category === "warning").length;
1808
- let output = `Type check passed. Checked ${checkedCount} file(s).
1809
- `;
1810
- if (warningCount > 0) {
1811
- output += `
1812
- Warnings:
1813
- ${formatDiagnosticsForAgent(result.diagnostics.filter((d) => d.category === "warning"))}
1814
- `;
1815
- }
1816
- return {
1817
- stdout: output,
1818
- stderr: "",
1819
- exitCode: 0
1820
- };
1821
- } catch (err) {
1822
- return {
1823
- stdout: "",
1824
- stderr: `Type check failed: ${err instanceof Error ? err.message : String(err)}
1825
- `,
1826
- exitCode: 1
1827
- };
1828
- }
1829
- });
1830
- }
1831
- function createBuildCommand(deps) {
1832
- const { fs, libFiles, tsconfigPath, onBuild, getValidation, sharedModules } = deps;
1833
- return defineCommand("build", async (args, _ctx) => {
1834
- let entryPoint = null;
1835
- let skipTypecheck = false;
1836
- let minify = false;
1837
- let format = "esm";
1838
- for (let i = 0;i < args.length; i++) {
1839
- const arg = args[i];
1840
- if (arg === "--skip-typecheck" || arg === "-s") {
1841
- skipTypecheck = true;
1842
- } else if (arg === "--minify" || arg === "-m") {
1843
- minify = true;
1844
- } else if ((arg === "--format" || arg === "-f") && args[i + 1]) {
1845
- const f = args[++i].toLowerCase();
1846
- if (f === "esm" || f === "iife" || f === "cjs") {
1847
- format = f;
1848
- }
1849
- } else if (!arg.startsWith("-")) {
1850
- entryPoint = arg;
1851
- }
1852
- }
1853
- if (!entryPoint) {
1854
- return {
1855
- stdout: "",
1856
- stderr: `Usage: build <entry-point> [options]
1857
-
1858
- Options:
1859
- --skip-typecheck, -s Skip type checking
1860
- --minify, -m Minify output
1861
- --format, -f <fmt> Output format (esm|iife|cjs)
1862
-
1863
- Example: build /src/index.ts
1864
- `,
1865
- exitCode: 1
1866
- };
1867
- }
1868
- try {
1869
- if (!await fs.exists(entryPoint)) {
1870
- return {
1871
- stdout: "",
1872
- stderr: `Error: Entry point not found: ${entryPoint}
1873
- `,
1874
- exitCode: 1
1875
- };
1876
- }
1877
- let typecheckResult = null;
1878
- if (!skipTypecheck) {
1879
- typecheckResult = await typecheck({
1880
- fs,
1881
- entryPoint,
1882
- tsconfigPath,
1883
- libFiles
1884
- });
1885
- if (typecheckResult.hasErrors) {
1886
- const formatted = formatDiagnosticsForAgent(typecheckResult.diagnostics);
1887
- return {
1888
- stdout: "",
1889
- stderr: `Build failed: Type errors found.
1890
-
1891
- ${formatted}
1892
- `,
1893
- exitCode: 1
1894
- };
1895
- }
1896
- }
1897
- const bundleResult = await bundle({
1898
- fs,
1899
- entryPoint,
1900
- format,
1901
- minify,
1902
- sharedModules
1903
- });
1904
- let loadedModule;
1905
- try {
1906
- loadedModule = await loadModule(bundleResult);
1907
- } catch (err) {
1908
- const errorMessage = err instanceof Error ? err.message : String(err);
1909
- return {
1910
- stdout: "",
1911
- stderr: `Build failed: Module failed to load.
1912
-
1913
- ${errorMessage}
1914
- `,
1915
- exitCode: 1
1916
- };
1917
- }
1918
- const validateFn = getValidation?.();
1919
- let validatedModule = loadedModule;
1920
- if (validateFn) {
1921
- try {
1922
- validatedModule = validateFn(loadedModule);
1923
- } catch (err) {
1924
- const errorMessage = err instanceof Error ? err.message : String(err);
1925
- return {
1926
- stdout: "",
1927
- stderr: `Build failed: Validation error.
1928
-
1929
- ${errorMessage}
1930
- `,
1931
- exitCode: 1
1932
- };
1933
- }
1934
- }
1935
- if (onBuild) {
1936
- await onBuild({ bundle: bundleResult, module: validatedModule });
1937
- }
1938
- let output = `Build successful!
1939
- `;
1940
- output += `Entry: ${entryPoint}
1941
- `;
1942
- output += `Format: ${format}
1943
- `;
1944
- output += `Size: ${(bundleResult.code.length / 1024).toFixed(2)} KB
1945
- `;
1946
- if (typecheckResult) {
1947
- output += `Type checked: ${typecheckResult.checkedFiles.length} file(s)
1948
- `;
1949
- }
1950
- output += `Bundled: ${bundleResult.includedFiles.length} file(s)
1951
- `;
1952
- const exportNames = Object.keys(loadedModule).filter((k) => !k.startsWith("__"));
1953
- if (exportNames.length > 0) {
1954
- output += `Exports: ${exportNames.join(", ")}
1955
- `;
1956
- }
1957
- if (validateFn) {
1958
- output += `Validation: passed
1959
- `;
1960
- }
1961
- if (bundleResult.warnings.length > 0) {
1962
- output += `
1963
- Build warnings:
1964
- ${formatEsbuildMessages(bundleResult.warnings)}
1965
- `;
1966
- }
1967
- if (typecheckResult) {
1968
- const warnings = typecheckResult.diagnostics.filter((d) => d.category === "warning");
1969
- if (warnings.length > 0) {
1970
- output += `
1971
- Type warnings:
1972
- ${formatDiagnosticsForAgent(warnings)}
1973
- `;
1974
- }
1975
- }
1976
- return {
1977
- stdout: output,
1978
- stderr: "",
1979
- exitCode: 0
1980
- };
1981
- } catch (err) {
1982
- const errorMessage = err instanceof Error ? err.message : String(err);
1983
- return {
1984
- stdout: "",
1985
- stderr: `Build failed: ${errorMessage}
1986
- `,
1987
- exitCode: 1
1988
- };
1989
- }
1990
- });
1991
- }
1992
- // src/commands/packages.ts
1993
- import { defineCommand as defineCommand2 } from "just-bash/browser";
1994
- function createInstallCommand(deps) {
1995
- const { fs, typesCache } = deps;
1996
- return defineCommand2("install", async (args, _ctx) => {
1997
- if (args.length === 0) {
1998
- return {
1999
- stdout: "",
2000
- stderr: `Usage: install <package>[@version] [...packages]
2001
-
2002
- Examples:
2003
- install react
2004
- install lodash@4.17.21
2005
- install @tanstack/react-query@5
2006
- `,
2007
- exitCode: 1
2008
- };
2009
- }
2010
- const results = [];
2011
- let hasError = false;
2012
- for (const packageSpec of args) {
2013
- try {
2014
- const result = await installPackage(fs, packageSpec, { cache: typesCache });
2015
- let status = `+ ${result.name}@${result.version}`;
2016
- if (result.typesInstalled) {
2017
- status += ` (${result.typeFilesCount} type file${result.typeFilesCount !== 1 ? "s" : ""})`;
2018
- if (result.fromCache) {
2019
- status += " [cached]";
2020
- }
2021
- } else if (result.typesError) {
2022
- status += ` (no types: ${result.typesError})`;
2023
- }
2024
- results.push(status);
2025
- } catch (err) {
2026
- hasError = true;
2027
- const message = err instanceof Error ? err.message : String(err);
2028
- results.push(`x ${packageSpec}: ${message}`);
2029
- }
2030
- }
2031
- const output = results.join(`
2032
- `) + `
2033
- `;
2034
- if (hasError) {
2035
- return {
2036
- stdout: "",
2037
- stderr: output,
2038
- exitCode: 1
2039
- };
2040
- }
2041
- return {
2042
- stdout: output,
2043
- stderr: "",
2044
- exitCode: 0
2045
- };
2046
- });
2047
- }
2048
- function createUninstallCommand(deps) {
2049
- const { fs } = deps;
2050
- return defineCommand2("uninstall", async (args, _ctx) => {
2051
- if (args.length === 0) {
2052
- return {
2053
- stdout: "",
2054
- stderr: `Usage: uninstall <package> [...packages]
2055
- `,
2056
- exitCode: 1
2057
- };
2058
- }
2059
- const results = [];
2060
- let hasError = false;
2061
- for (const packageName of args) {
2062
- try {
2063
- const removed = await uninstallPackage(fs, packageName);
2064
- if (removed) {
2065
- results.push(`- ${packageName}`);
2066
- } else {
2067
- results.push(`x ${packageName}: not installed`);
2068
- hasError = true;
2069
- }
2070
- } catch (err) {
2071
- hasError = true;
2072
- const message = err instanceof Error ? err.message : String(err);
2073
- results.push(`x ${packageName}: ${message}`);
2074
- }
2075
- }
2076
- const output = results.join(`
2077
- `) + `
2078
- `;
2079
- if (hasError) {
2080
- return {
2081
- stdout: "",
2082
- stderr: output,
2083
- exitCode: 1
2084
- };
2085
- }
2086
- return {
2087
- stdout: output,
2088
- stderr: "",
2089
- exitCode: 0
2090
- };
2091
- });
2092
- }
2093
- function createListCommand(deps) {
2094
- const { fs } = deps;
2095
- return defineCommand2("list", async (_args, _ctx) => {
2096
- try {
2097
- const packages = await listPackages(fs);
2098
- if (packages.length === 0) {
2099
- return {
2100
- stdout: `No packages installed.
2101
- `,
2102
- stderr: "",
2103
- exitCode: 0
2104
- };
2105
- }
2106
- const output = packages.map((pkg) => `${pkg.name}@${pkg.version}`).join(`
2107
- `) + `
2108
- `;
2109
- return {
2110
- stdout: output,
2111
- stderr: "",
2112
- exitCode: 0
2113
- };
2114
- } catch (err) {
2115
- const message = err instanceof Error ? err.message : String(err);
2116
- return {
2117
- stdout: "",
2118
- stderr: `Failed to list packages: ${message}
2119
- `,
2120
- exitCode: 1
2121
- };
2122
- }
2123
- });
2124
- }
2125
- // src/commands/run.ts
2126
- import { defineCommand as defineCommand3 } from "just-bash/browser";
2127
- function createRunCommand(deps) {
2128
- const { fs, libFiles, tsconfigPath, runOptions = {}, sharedModules } = deps;
2129
- return defineCommand3("run", async (args, _ctx) => {
2130
- let entryPoint = null;
2131
- let skipTypecheck = runOptions.skipTypecheck ?? false;
2132
- let timeout = runOptions.timeout ?? 30000;
2133
- const scriptArgs = [];
2134
- let collectingArgs = false;
2135
- for (let i = 0;i < args.length; i++) {
2136
- const arg = args[i];
2137
- if (collectingArgs) {
2138
- scriptArgs.push(arg);
2139
- continue;
2140
- }
2141
- if (arg === "--") {
2142
- collectingArgs = true;
2143
- } else if (arg === "--skip-typecheck" || arg === "-s") {
2144
- skipTypecheck = true;
2145
- } else if ((arg === "--timeout" || arg === "-t") && args[i + 1]) {
2146
- timeout = parseInt(args[++i], 10);
2147
- if (isNaN(timeout))
2148
- timeout = 30000;
2149
- } else if (!arg.startsWith("-")) {
2150
- entryPoint = arg;
2151
- }
2152
- }
2153
- if (!entryPoint) {
2154
- return {
2155
- stdout: "",
2156
- stderr: `Usage: run <entry-point> [options] [-- args...]
2157
-
2158
- Options:
2159
- --skip-typecheck, -s Skip type checking
2160
- --timeout, -t <ms> Execution timeout (default: 30000)
2161
-
2162
- Example: run /src/index.ts
2163
- `,
2164
- exitCode: 1
2165
- };
2166
- }
2167
- const logs = [];
2168
- const originalConsole = {
2169
- log: console.log,
2170
- warn: console.warn,
2171
- error: console.error,
2172
- info: console.info,
2173
- debug: console.debug
2174
- };
2175
- const formatArgs = (...a) => a.map((v) => typeof v === "object" ? JSON.stringify(v) : String(v)).join(" ");
2176
- const captureLog = (...a) => {
2177
- logs.push(formatArgs(...a));
2178
- originalConsole.log.apply(console, a);
2179
- };
2180
- const captureWarn = (...a) => {
2181
- logs.push(`[warn] ${formatArgs(...a)}`);
2182
- originalConsole.warn.apply(console, a);
2183
- };
2184
- const captureError = (...a) => {
2185
- logs.push(`[error] ${formatArgs(...a)}`);
2186
- originalConsole.error.apply(console, a);
2187
- };
2188
- const captureInfo = (...a) => {
2189
- logs.push(`[info] ${formatArgs(...a)}`);
2190
- originalConsole.info.apply(console, a);
2191
- };
2192
- const captureDebug = (...a) => {
2193
- logs.push(`[debug] ${formatArgs(...a)}`);
2194
- originalConsole.debug.apply(console, a);
2195
- };
2196
- const restoreConsole = () => {
2197
- console.log = originalConsole.log;
2198
- console.warn = originalConsole.warn;
2199
- console.error = originalConsole.error;
2200
- console.info = originalConsole.info;
2201
- console.debug = originalConsole.debug;
2202
- };
2203
- try {
2204
- if (!await fs.exists(entryPoint)) {
2205
- return {
2206
- stdout: "",
2207
- stderr: `Error: Entry point not found: ${entryPoint}
2208
- `,
2209
- exitCode: 1
2210
- };
2211
- }
2212
- if (!skipTypecheck) {
2213
- const typecheckResult = await typecheck({
2214
- fs,
2215
- entryPoint,
2216
- tsconfigPath,
2217
- libFiles
2218
- });
2219
- if (typecheckResult.hasErrors) {
2220
- const formatted = formatDiagnosticsForAgent(typecheckResult.diagnostics);
2221
- return {
2222
- stdout: "",
2223
- stderr: `Type errors:
2224
- ${formatted}
2225
- `,
2226
- exitCode: 1
2227
- };
2228
- }
2229
- }
2230
- const bundleResult = await bundle({
2231
- fs,
2232
- entryPoint,
2233
- format: "esm",
2234
- sharedModules
2235
- });
2236
- console.log = captureLog;
2237
- console.warn = captureWarn;
2238
- console.error = captureError;
2239
- console.info = captureInfo;
2240
- console.debug = captureDebug;
2241
- const context = {
2242
- fs,
2243
- env: { ...runOptions.env },
2244
- args: scriptArgs,
2245
- log: captureLog,
2246
- error: captureError
2247
- };
2248
- const startTime = performance.now();
2249
- let returnValue;
2250
- const executeCode = async () => {
2251
- const module = await loadModule(bundleResult);
2252
- if (typeof module.main === "function") {
2253
- returnValue = await module.main(context);
2254
- }
2255
- };
2256
- if (timeout > 0) {
2257
- const timeoutPromise = new Promise((_, reject) => {
2258
- setTimeout(() => reject(new Error(`Execution timed out after ${timeout}ms`)), timeout);
2259
- });
2260
- await Promise.race([executeCode(), timeoutPromise]);
2261
- } else {
2262
- await executeCode();
2263
- }
2264
- const executionTimeMs = performance.now() - startTime;
2265
- restoreConsole();
2266
- let output = "";
2267
- if (logs.length > 0) {
2268
- output = logs.join(`
2269
- `) + `
2270
- `;
2271
- }
2272
- if (returnValue !== undefined) {
2273
- const returnStr = typeof returnValue === "object" ? JSON.stringify(returnValue, null, 2) : String(returnValue);
2274
- output += `[return] ${returnStr}
2275
- `;
2276
- }
2277
- output += `
2278
- Execution completed in ${executionTimeMs.toFixed(2)}ms
2279
- `;
2280
- return {
2281
- stdout: output,
2282
- stderr: "",
2283
- exitCode: 0
2284
- };
2285
- } catch (err) {
2286
- restoreConsole();
2287
- const errorMessage = err instanceof Error ? err.message : String(err);
2288
- const errorStack = err instanceof Error && err.stack ? `
2289
- ${err.stack}` : "";
2290
- let output = "";
2291
- if (logs.length > 0) {
2292
- output = logs.join(`
2293
- `) + `
2294
-
2295
- `;
2296
- }
2297
- return {
2298
- stdout: output,
2299
- stderr: `Runtime error: ${errorMessage}${errorStack}
2300
- `,
2301
- exitCode: 1
2302
- };
1539
+ const sharedModuleRegistry = createSharedModuleRegistry(sharedModules);
1540
+ const sandboxContext = {
1541
+ bundler,
1542
+ typechecker,
1543
+ typesResolver,
1544
+ sharedModuleRegistry,
1545
+ executor
1546
+ };
1547
+ return {
1548
+ async createSandbox(sandboxOptions = {}) {
1549
+ const fs = Filesystem.create({
1550
+ maxSizeBytes: sandboxOptions.maxFilesystemSize ?? sandboxDefaults.maxFilesystemSize
1551
+ });
1552
+ return createSandboxImpl(fs, sandboxOptions, sandboxContext);
1553
+ },
1554
+ get sharedModules() {
1555
+ return sharedModuleRegistry;
2303
1556
  }
2304
- });
2305
- }
2306
- // src/commands/index.ts
2307
- function createDefaultCommands(deps) {
2308
- return [
2309
- createTscCommand(deps),
2310
- createBuildCommand(deps),
2311
- createRunCommand(deps),
2312
- createInstallCommand(deps),
2313
- createUninstallCommand(deps),
2314
- createListCommand(deps)
2315
- ];
1557
+ };
2316
1558
  }
2317
-
2318
- // src/ts-libs.ts
1559
+ // src/core/typechecker.ts
1560
+ import ts from "typescript";
2319
1561
  var TS_VERSION = "5.9.3";
2320
- var CDN_BASE = `https://cdn.jsdelivr.net/npm/typescript@${TS_VERSION}/lib`;
2321
- var DB_NAME = "ts-lib-cache";
2322
- var DB_VERSION = 1;
2323
- var STORE_NAME = "libs";
2324
- function getDefaultBrowserLibs() {
2325
- return ["es2020", "dom", "dom.iterable"];
2326
- }
1562
+ var DEFAULT_CDN_BASE = `https://cdn.jsdelivr.net/npm/typescript@${TS_VERSION}/lib`;
1563
+ var DEFAULT_LIBS = ["es2020", "dom", "dom.iterable"];
1564
+ var LIB_PATH_PREFIX = "/node_modules/typescript/lib/";
2327
1565
  function parseLibReferences(content) {
2328
1566
  const refs = [];
2329
1567
  const regex = /\/\/\/\s*<reference\s+lib="([^"]+)"\s*\/>/g;
@@ -2338,20 +1576,16 @@ function parseLibReferences(content) {
2338
1576
  function libNameToFileName(name) {
2339
1577
  return `lib.${name}.d.ts`;
2340
1578
  }
2341
- function extractLibName(filePath) {
2342
- const match = filePath.match(/lib\.([^/]+)\.d\.ts$/);
2343
- return match?.[1] ?? null;
2344
- }
2345
- async function fetchLibFile(name) {
1579
+ async function fetchLibFile(name, baseUrl) {
2346
1580
  const fileName = libNameToFileName(name);
2347
- const url = `${CDN_BASE}/${fileName}`;
1581
+ const url = `${baseUrl}/${fileName}`;
2348
1582
  const response = await fetch(url);
2349
1583
  if (!response.ok) {
2350
1584
  throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
2351
1585
  }
2352
1586
  return response.text();
2353
1587
  }
2354
- async function fetchAllLibs(libs) {
1588
+ async function fetchAllLibs(libs, baseUrl) {
2355
1589
  const result = new Map;
2356
1590
  const pending = new Set(libs);
2357
1591
  const fetched = new Set;
@@ -2364,10 +1598,10 @@ async function fetchAllLibs(libs) {
2364
1598
  }
2365
1599
  fetched.add(name);
2366
1600
  try {
2367
- const content = await fetchLibFile(name);
1601
+ const content = await fetchLibFile(name, baseUrl);
2368
1602
  return { name, content };
2369
1603
  } catch (err) {
2370
- console.warn(`Failed to fetch lib.${name}.d.ts:`, err);
1604
+ console.warn(`[typechecker] Failed to fetch lib.${name}.d.ts:`, err);
2371
1605
  return { name, content: null };
2372
1606
  }
2373
1607
  }));
@@ -2385,349 +1619,471 @@ async function fetchAllLibs(libs) {
2385
1619
  }
2386
1620
  return result;
2387
1621
  }
2388
- async function openDatabase() {
2389
- return new Promise((resolve, reject) => {
2390
- const request = indexedDB.open(DB_NAME, DB_VERSION);
2391
- request.onerror = () => reject(request.error);
2392
- request.onsuccess = () => resolve(request.result);
2393
- request.onupgradeneeded = (event) => {
2394
- const db = event.target.result;
2395
- if (!db.objectStoreNames.contains(STORE_NAME)) {
2396
- db.createObjectStore(STORE_NAME);
2397
- }
2398
- };
2399
- });
2400
- }
2401
- function promisifyRequest(request) {
2402
- return new Promise((resolve, reject) => {
2403
- request.onsuccess = () => resolve(request.result);
2404
- request.onerror = () => reject(request.error);
2405
- });
1622
+ function normalizePath(path) {
1623
+ if (!path.startsWith("/")) {
1624
+ return "/" + path;
1625
+ }
1626
+ return path;
2406
1627
  }
2407
- function getCacheKey() {
2408
- return `libs-${TS_VERSION}`;
1628
+ function getLibContent(fileName, libFiles) {
1629
+ const match = fileName.match(/lib\.([^/]+)\.d\.ts$/);
1630
+ if (match?.[1]) {
1631
+ return libFiles.get(match[1]);
1632
+ }
1633
+ return;
2409
1634
  }
2410
-
2411
- class LibCache {
2412
- db;
2413
- constructor(db) {
2414
- this.db = db;
2415
- }
2416
- static async create() {
2417
- const db = await openDatabase();
2418
- return new LibCache(db);
2419
- }
2420
- async getOrFetch(libs) {
2421
- const cached = await this.get();
2422
- if (cached) {
2423
- const missing = libs.filter((lib) => !cached.has(lib));
2424
- if (missing.length === 0) {
2425
- return cached;
1635
+ function createCompilerHost(fs, libFiles, options) {
1636
+ return {
1637
+ getSourceFile(fileName, languageVersion, onError) {
1638
+ const normalizedPath = normalizePath(fileName);
1639
+ try {
1640
+ if (fs.exists(normalizedPath)) {
1641
+ const stat = fs.stat(normalizedPath);
1642
+ if (stat.isFile) {
1643
+ const content = fs.readFile(normalizedPath);
1644
+ return ts.createSourceFile(normalizedPath, content, languageVersion, true);
1645
+ }
1646
+ }
1647
+ } catch {}
1648
+ try {
1649
+ if (fs.exists(fileName)) {
1650
+ const stat = fs.stat(fileName);
1651
+ if (stat.isFile) {
1652
+ const content = fs.readFile(fileName);
1653
+ return ts.createSourceFile(fileName, content, languageVersion, true);
1654
+ }
1655
+ }
1656
+ } catch {}
1657
+ const libContent = getLibContent(fileName, libFiles);
1658
+ if (libContent !== undefined) {
1659
+ return ts.createSourceFile(fileName, libContent, languageVersion, true);
2426
1660
  }
2427
- console.log(`Cache missing libs: ${missing.join(", ")}, fetching all...`);
2428
- }
2429
- console.log(`Fetching TypeScript libs from CDN: ${libs.join(", ")}...`);
2430
- const fetched = await fetchAllLibs(libs);
2431
- console.log(`Fetched ${fetched.size} lib files`);
2432
- await this.set(fetched);
2433
- return fetched;
2434
- }
2435
- async get() {
2436
- const tx = this.db.transaction(STORE_NAME, "readonly");
2437
- const store = tx.objectStore(STORE_NAME);
2438
- const key = getCacheKey();
2439
- const cached = await promisifyRequest(store.get(key));
2440
- if (!cached) {
2441
- return null;
1661
+ if (onError) {
1662
+ onError(`File not found: ${fileName}`);
1663
+ }
1664
+ return;
1665
+ },
1666
+ getDefaultLibFileName(opts) {
1667
+ return LIB_PATH_PREFIX + ts.getDefaultLibFileName(opts);
1668
+ },
1669
+ writeFile() {},
1670
+ getCurrentDirectory() {
1671
+ return "/";
1672
+ },
1673
+ getCanonicalFileName(fileName) {
1674
+ return fileName;
1675
+ },
1676
+ useCaseSensitiveFileNames() {
1677
+ return true;
1678
+ },
1679
+ getNewLine() {
1680
+ return `
1681
+ `;
1682
+ },
1683
+ fileExists(fileName) {
1684
+ const normalizedPath = normalizePath(fileName);
1685
+ try {
1686
+ if (fs.exists(normalizedPath)) {
1687
+ return fs.stat(normalizedPath).isFile;
1688
+ }
1689
+ } catch {}
1690
+ return getLibContent(fileName, libFiles) !== undefined;
1691
+ },
1692
+ readFile(fileName) {
1693
+ const normalizedPath = normalizePath(fileName);
1694
+ try {
1695
+ if (fs.exists(normalizedPath)) {
1696
+ return fs.readFile(normalizedPath);
1697
+ }
1698
+ } catch {}
1699
+ return getLibContent(fileName, libFiles);
1700
+ },
1701
+ directoryExists(directoryName) {
1702
+ const normalizedDir = normalizePath(directoryName);
1703
+ try {
1704
+ if (fs.exists(normalizedDir)) {
1705
+ return fs.stat(normalizedDir).isDirectory;
1706
+ }
1707
+ } catch {}
1708
+ if (normalizedDir === "/node_modules/typescript/lib" || normalizedDir === "/node_modules/typescript" || normalizedDir === "/node_modules") {
1709
+ return libFiles.size > 0;
1710
+ }
1711
+ return false;
1712
+ },
1713
+ getDirectories(path) {
1714
+ const normalizedPath = normalizePath(path);
1715
+ try {
1716
+ if (!fs.exists(normalizedPath)) {
1717
+ return [];
1718
+ }
1719
+ const stat = fs.stat(normalizedPath);
1720
+ if (!stat.isDirectory) {
1721
+ return [];
1722
+ }
1723
+ const entries = fs.readdir(normalizedPath);
1724
+ const dirs = [];
1725
+ for (const name of entries) {
1726
+ const childPath = normalizedPath === "/" ? `/${name}` : `${normalizedPath}/${name}`;
1727
+ try {
1728
+ if (fs.stat(childPath).isDirectory) {
1729
+ dirs.push(name);
1730
+ }
1731
+ } catch {}
1732
+ }
1733
+ return dirs;
1734
+ } catch {
1735
+ return [];
1736
+ }
1737
+ },
1738
+ realpath(path) {
1739
+ return path;
1740
+ },
1741
+ getEnvironmentVariable() {
1742
+ return;
2442
1743
  }
2443
- if (cached.version !== TS_VERSION) {
2444
- console.log(`Cache version mismatch: ${cached.version} vs ${TS_VERSION}`);
2445
- return null;
1744
+ };
1745
+ }
1746
+ function getDefaultCompilerOptions() {
1747
+ return {
1748
+ target: ts.ScriptTarget.ES2020,
1749
+ module: ts.ModuleKind.ESNext,
1750
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
1751
+ esModuleInterop: true,
1752
+ strict: true,
1753
+ skipLibCheck: true,
1754
+ noEmit: true,
1755
+ jsx: ts.JsxEmit.ReactJSX,
1756
+ allowJs: true,
1757
+ resolveJsonModule: true,
1758
+ lib: ["lib.es2020.d.ts", "lib.dom.d.ts", "lib.dom.iterable.d.ts"]
1759
+ };
1760
+ }
1761
+ function parseTsConfig(fs, configPath) {
1762
+ try {
1763
+ if (!fs.exists(configPath)) {
1764
+ return getDefaultCompilerOptions();
1765
+ }
1766
+ const configText = fs.readFile(configPath);
1767
+ const { config, error } = ts.parseConfigFileTextToJson(configPath, configText);
1768
+ if (error) {
1769
+ console.warn("[typechecker] Error parsing tsconfig:", error.messageText);
1770
+ return getDefaultCompilerOptions();
1771
+ }
1772
+ const parseHost = {
1773
+ useCaseSensitiveFileNames: true,
1774
+ readDirectory: () => [],
1775
+ fileExists: (path) => fs.exists(normalizePath(path)),
1776
+ readFile: (path) => {
1777
+ try {
1778
+ return fs.readFile(normalizePath(path));
1779
+ } catch {
1780
+ return;
1781
+ }
1782
+ }
1783
+ };
1784
+ const parsed = ts.parseJsonConfigFileContent(config, parseHost, "/", undefined, configPath);
1785
+ if (parsed.errors.length > 0) {
1786
+ console.warn("[typechecker] tsconfig parse errors:", parsed.errors.map((e) => e.messageText));
2446
1787
  }
2447
- return new Map(Object.entries(cached.libs));
2448
- }
2449
- async set(libs) {
2450
- const tx = this.db.transaction(STORE_NAME, "readwrite");
2451
- const store = tx.objectStore(STORE_NAME);
2452
- const key = getCacheKey();
2453
- const cached = {
2454
- version: TS_VERSION,
2455
- timestamp: Date.now(),
2456
- libs: Object.fromEntries(libs)
1788
+ return {
1789
+ ...parsed.options,
1790
+ noEmit: true
2457
1791
  };
2458
- await promisifyRequest(store.put(cached, key));
2459
- }
2460
- async clear() {
2461
- const tx = this.db.transaction(STORE_NAME, "readwrite");
2462
- const store = tx.objectStore(STORE_NAME);
2463
- await promisifyRequest(store.clear());
2464
- }
2465
- close() {
2466
- this.db.close();
1792
+ } catch (err) {
1793
+ console.warn("[typechecker] Error reading tsconfig:", err);
1794
+ return getDefaultCompilerOptions();
2467
1795
  }
2468
1796
  }
2469
- async function fetchAndCacheLibs(libs = getDefaultBrowserLibs()) {
2470
- const cache = await LibCache.create();
2471
- try {
2472
- return await cache.getOrFetch(libs);
2473
- } finally {
2474
- cache.close();
1797
+ function categoryToSeverity(category) {
1798
+ switch (category) {
1799
+ case ts.DiagnosticCategory.Error:
1800
+ return "error";
1801
+ case ts.DiagnosticCategory.Warning:
1802
+ return "warning";
1803
+ default:
1804
+ return "info";
2475
1805
  }
2476
1806
  }
2477
-
2478
- // src/shared-resources.ts
2479
- async function createSharedResources(options = {}) {
2480
- const { libs = getDefaultBrowserLibs(), skipLibs = false, skipBundler = false } = options;
2481
- const libsPromise = skipLibs ? Promise.resolve(new Map) : fetchAndCacheLibs(libs);
2482
- const bundlerPromise = skipBundler ? Promise.resolve() : initBundler();
2483
- const typesCache = new InMemoryTypesCache;
2484
- const [libFiles] = await Promise.all([libsPromise, bundlerPromise]);
1807
+ function convertDiagnostic(diag) {
1808
+ let file;
1809
+ let line;
1810
+ let column;
1811
+ if (diag.file && diag.start !== undefined) {
1812
+ file = diag.file.fileName;
1813
+ const pos = diag.file.getLineAndCharacterOfPosition(diag.start);
1814
+ line = pos.line + 1;
1815
+ column = pos.character + 1;
1816
+ }
2485
1817
  return {
2486
- libFiles,
2487
- bundlerReady: Promise.resolve(),
2488
- typesCache
1818
+ file,
1819
+ line,
1820
+ column,
1821
+ message: ts.flattenDiagnosticMessageText(diag.messageText, `
1822
+ `),
1823
+ severity: categoryToSeverity(diag.category)
2489
1824
  };
2490
1825
  }
2491
- var defaultResourcesInstance = null;
2492
- var defaultResourcesPromise = null;
2493
- async function getDefaultResources() {
2494
- if (defaultResourcesInstance) {
2495
- return defaultResourcesInstance;
2496
- }
2497
- if (!defaultResourcesPromise) {
2498
- defaultResourcesPromise = createSharedResources().then((resources) => {
2499
- defaultResourcesInstance = resources;
2500
- return resources;
2501
- });
1826
+
1827
+ class Typechecker {
1828
+ options;
1829
+ libCache = new Map;
1830
+ initPromise = null;
1831
+ constructor(options = {}) {
1832
+ this.options = options;
1833
+ }
1834
+ async initialize() {
1835
+ if (this.initPromise) {
1836
+ await this.initPromise;
1837
+ return;
1838
+ }
1839
+ if (this.libCache.size > 0) {
1840
+ return;
1841
+ }
1842
+ this.initPromise = this.fetchLibs();
1843
+ await this.initPromise;
1844
+ }
1845
+ async fetchLibs() {
1846
+ const libs = this.options.libs ?? DEFAULT_LIBS;
1847
+ const baseUrl = this.options.libsBaseUrl ?? DEFAULT_CDN_BASE;
1848
+ console.log(`[typechecker] Fetching TypeScript libs: ${libs.join(", ")}...`);
1849
+ const fetched = await fetchAllLibs(libs, baseUrl);
1850
+ console.log(`[typechecker] Fetched ${fetched.size} lib files`);
1851
+ this.libCache = fetched;
1852
+ }
1853
+ async typecheck(options) {
1854
+ await this.initialize();
1855
+ const { fs, entryPoint, tsconfigPath = "/tsconfig.json" } = options;
1856
+ const normalizedEntry = normalizePath(entryPoint);
1857
+ if (!fs.exists(normalizedEntry)) {
1858
+ return {
1859
+ success: false,
1860
+ diagnostics: [
1861
+ {
1862
+ file: normalizedEntry,
1863
+ message: `Entry point not found: ${normalizedEntry}`,
1864
+ severity: "error"
1865
+ }
1866
+ ]
1867
+ };
1868
+ }
1869
+ const compilerOptions = parseTsConfig(fs, tsconfigPath);
1870
+ const host = createCompilerHost(fs, this.libCache, compilerOptions);
1871
+ const program = ts.createProgram([normalizedEntry], compilerOptions, host);
1872
+ const allDiagnostics = [
1873
+ ...program.getSyntacticDiagnostics(),
1874
+ ...program.getSemanticDiagnostics(),
1875
+ ...program.getDeclarationDiagnostics()
1876
+ ];
1877
+ const diagnostics = allDiagnostics.map(convertDiagnostic);
1878
+ const success = !diagnostics.some((d) => d.severity === "error");
1879
+ return {
1880
+ success,
1881
+ diagnostics
1882
+ };
2502
1883
  }
2503
- return defaultResourcesPromise;
2504
1884
  }
2505
- function clearDefaultResources() {
2506
- defaultResourcesInstance = null;
2507
- defaultResourcesPromise = null;
1885
+ function createTypechecker(options) {
1886
+ return new Typechecker(options);
2508
1887
  }
2509
- function hasDefaultResources() {
2510
- return defaultResourcesInstance !== null;
1888
+ // src/core/esm-types-resolver.ts
1889
+ class InMemoryTypesCache {
1890
+ cache = new Map;
1891
+ async get(key) {
1892
+ return this.cache.get(key) ?? null;
1893
+ }
1894
+ async set(key, value) {
1895
+ this.cache.set(key, value);
1896
+ }
1897
+ async has(key) {
1898
+ return this.cache.has(key);
1899
+ }
1900
+ clear() {
1901
+ this.cache.clear();
1902
+ }
2511
1903
  }
2512
1904
 
2513
- // src/build-emitter.ts
2514
- class BuildEmitter {
2515
- listeners = new Set;
2516
- emit = async (result) => {
2517
- const promises = [];
2518
- for (const listener of this.listeners) {
2519
- const ret = listener(result);
2520
- if (ret instanceof Promise) {
2521
- promises.push(ret);
2522
- }
1905
+ class EsmTypesResolver {
1906
+ baseUrl;
1907
+ cache;
1908
+ tryTypesPackages;
1909
+ constructor(options = {}) {
1910
+ this.baseUrl = options.baseUrl ?? "https://esm.sh";
1911
+ this.cache = options.cache ?? null;
1912
+ this.tryTypesPackages = options.tryTypesPackages ?? true;
1913
+ }
1914
+ async resolveTypes(specifier, version) {
1915
+ const resolved = await this.resolve(specifier, version);
1916
+ if (!resolved) {
1917
+ return {};
1918
+ }
1919
+ const result = {};
1920
+ const pkgPath = `/node_modules/${resolved.packageName}`;
1921
+ for (const [relativePath, content] of Object.entries(resolved.files)) {
1922
+ result[`${pkgPath}/${relativePath}`] = content;
2523
1923
  }
2524
- await Promise.all(promises);
2525
- };
2526
- on(callback) {
2527
- this.listeners.add(callback);
2528
- return () => {
2529
- this.listeners.delete(callback);
2530
- };
1924
+ return result;
2531
1925
  }
2532
- }
2533
-
2534
- // src/sandbox.ts
2535
- async function createSandbox(options = {}) {
2536
- const {
2537
- initialFiles,
2538
- maxFilesystemSize,
2539
- tsconfigPath = "/tsconfig.json",
2540
- resources: providedResources,
2541
- onBuild: onBuildCallback,
2542
- customCommands = [],
2543
- sharedModules,
2544
- bashOptions = {}
2545
- } = options;
2546
- const fs = Filesystem.create({
2547
- initialFiles,
2548
- maxSizeBytes: maxFilesystemSize
2549
- });
2550
- const resourcesPromise = providedResources ? Promise.resolve(providedResources) : getDefaultResources();
2551
- const bundlerPromise = initBundler();
2552
- const [resources] = await Promise.all([resourcesPromise, bundlerPromise]);
2553
- const libFiles = resources.libFiles;
2554
- const typesCache = resources.typesCache;
2555
- if (sharedModules && sharedModules.length > 0) {
2556
- const basePackages = new Set;
2557
- for (const moduleId of sharedModules) {
2558
- const { packageName } = parseImportPath(moduleId);
2559
- basePackages.add(packageName);
2560
- }
2561
- await Promise.all(Array.from(basePackages).map(async (packageName) => {
2562
- try {
2563
- await installPackage(fs, packageName, { cache: typesCache });
2564
- } catch (err) {
2565
- console.warn(`[sandlot] Failed to install types for shared module "${packageName}":`, err);
1926
+ async resolve(specifier, version) {
1927
+ const { packageName, subpath } = parseSpecifier(specifier);
1928
+ const cacheKey = makeCacheKey(packageName, subpath, version);
1929
+ if (this.cache) {
1930
+ const cached = await this.cache.get(cacheKey);
1931
+ if (cached) {
1932
+ return cached;
2566
1933
  }
2567
- }));
1934
+ }
1935
+ let result = await this.tryResolve(packageName, subpath, version);
1936
+ if (!result && this.tryTypesPackages && !packageName.startsWith("@types/")) {
1937
+ const typesPackageName = toTypesPackageName(packageName);
1938
+ result = await this.tryResolve(typesPackageName, subpath, version);
1939
+ if (result) {
1940
+ result.fromTypesPackage = true;
1941
+ result.packageName = packageName;
1942
+ }
1943
+ }
1944
+ if (result && this.cache) {
1945
+ await this.cache.set(cacheKey, result);
1946
+ }
1947
+ return result;
2568
1948
  }
2569
- const buildEmitter = new BuildEmitter;
2570
- let lastBuild = null;
2571
- buildEmitter.on((result) => {
2572
- lastBuild = result;
2573
- });
2574
- if (onBuildCallback) {
2575
- buildEmitter.on(onBuildCallback);
1949
+ async tryResolve(packageName, subpath, version) {
1950
+ try {
1951
+ const versionSuffix = version ? `@${version}` : "";
1952
+ const pathSuffix = subpath ? `/${subpath}` : "";
1953
+ const url = `${this.baseUrl}/${packageName}${versionSuffix}${pathSuffix}`;
1954
+ const response = await fetch(url, { method: "HEAD" });
1955
+ if (!response.ok) {
1956
+ return null;
1957
+ }
1958
+ const resolvedVersion = this.extractVersion(response, packageName, version);
1959
+ const typesHeader = response.headers.get("X-TypeScript-Types");
1960
+ if (!typesHeader) {
1961
+ return null;
1962
+ }
1963
+ const typesUrl = new URL(typesHeader, response.url).href;
1964
+ const files = await this.fetchTypesRecursively(typesUrl, subpath);
1965
+ if (Object.keys(files).length === 0) {
1966
+ return null;
1967
+ }
1968
+ return {
1969
+ packageName,
1970
+ version: resolvedVersion,
1971
+ files,
1972
+ fromTypesPackage: packageName.startsWith("@types/")
1973
+ };
1974
+ } catch {
1975
+ return null;
1976
+ }
2576
1977
  }
2577
- let validationFn = null;
2578
- const commandDeps = {
2579
- fs,
2580
- libFiles,
2581
- tsconfigPath,
2582
- onBuild: buildEmitter.emit,
2583
- getValidation: () => validationFn,
2584
- typesCache,
2585
- sharedModules
2586
- };
2587
- const defaultCommands = createDefaultCommands(commandDeps);
2588
- const bash = new Bash({
2589
- ...bashOptions,
2590
- fs,
2591
- customCommands: [...defaultCommands, ...customCommands]
2592
- });
2593
- return {
2594
- fs,
2595
- bash,
2596
- get lastBuild() {
2597
- return lastBuild;
2598
- },
2599
- getState: () => ({ files: fs.getFiles() }),
2600
- onBuild: (callback) => buildEmitter.on(callback),
2601
- setValidation: (fn) => {
2602
- validationFn = fn;
2603
- },
2604
- clearValidation: () => {
2605
- validationFn = null;
1978
+ async fetchTypesRecursively(entryUrl, subpath, visited = new Set) {
1979
+ if (visited.has(entryUrl)) {
1980
+ return {};
2606
1981
  }
2607
- };
2608
- }
2609
- // src/builder.ts
2610
- function createBuilder(options) {
2611
- return async (prompt, callOptions) => {
2612
- const sandbox = options.sandbox ?? await createSandbox(options.sandboxOptions);
2613
- if (callOptions?.validate) {
2614
- sandbox.setValidation(callOptions.validate);
2615
- }
2616
- const captured = { output: null };
2617
- const unsubscribe = sandbox.onBuild((output) => {
2618
- captured.output = output;
2619
- });
2620
- const { timeout, signal } = callOptions ?? {};
2621
- let timeoutId;
2622
- let abortController;
2623
- if (timeout !== undefined || signal !== undefined) {
2624
- abortController = new AbortController;
2625
- if (timeout !== undefined) {
2626
- timeoutId = setTimeout(() => {
2627
- abortController.abort(new Error(`Build timed out after ${timeout}ms`));
2628
- }, timeout);
2629
- }
2630
- if (signal !== undefined) {
2631
- if (signal.aborted) {
2632
- abortController.abort(signal.reason);
1982
+ visited.add(entryUrl);
1983
+ const response = await fetch(entryUrl);
1984
+ if (!response.ok) {
1985
+ return {};
1986
+ }
1987
+ const content = await response.text();
1988
+ const files = {};
1989
+ const fileName = subpath ? `${subpath}.d.ts` : "index.d.ts";
1990
+ files[fileName] = content;
1991
+ if (subpath) {
1992
+ files[`${subpath}/index.d.ts`] = content;
1993
+ }
1994
+ const refs = parseReferences(content);
1995
+ for (const ref of refs.paths) {
1996
+ const refUrl = new URL(ref, entryUrl).href;
1997
+ const refFiles = await this.fetchTypesRecursively(refUrl, undefined, visited);
1998
+ for (const [refPath, refContent] of Object.entries(refFiles)) {
1999
+ const normalizedRef = ref.replace(/^\.\//, "");
2000
+ if (refPath === "index.d.ts") {
2001
+ files[normalizedRef] = refContent;
2633
2002
  } else {
2634
- signal.addEventListener("abort", () => {
2635
- abortController.abort(signal.reason);
2636
- }, { once: true });
2003
+ const dir = normalizedRef.replace(/\.d\.ts$/, "");
2004
+ files[`${dir}/${refPath}`] = refContent;
2637
2005
  }
2638
2006
  }
2639
2007
  }
2640
- let result;
2641
- let error = null;
2642
- try {
2643
- const buildPromise = options.build(sandbox, prompt);
2644
- if (abortController) {
2645
- const abortPromise = new Promise((_, reject) => {
2646
- abortController.signal.addEventListener("abort", () => {
2647
- const err = abortController.signal.reason instanceof Error ? abortController.signal.reason : new Error("Build aborted");
2648
- err.name = "AbortError";
2649
- reject(err);
2650
- }, { once: true });
2651
- if (abortController.signal.aborted) {
2652
- const err = abortController.signal.reason instanceof Error ? abortController.signal.reason : new Error("Build aborted");
2653
- err.name = "AbortError";
2654
- reject(err);
2655
- }
2656
- });
2657
- result = await Promise.race([buildPromise, abortPromise]);
2658
- } else {
2659
- result = await buildPromise;
2660
- }
2661
- } catch (err) {
2662
- error = err instanceof Error ? err : new Error(String(err));
2663
- } finally {
2664
- if (timeoutId !== undefined) {
2665
- clearTimeout(timeoutId);
2666
- }
2667
- unsubscribe();
2668
- if (callOptions?.validate) {
2669
- sandbox.clearValidation();
2008
+ return files;
2009
+ }
2010
+ extractVersion(response, packageName, requestedVersion) {
2011
+ const esmId = response.headers.get("x-esm-id");
2012
+ if (esmId) {
2013
+ const match = esmId.match(new RegExp(`${escapeRegex(packageName)}@([^/]+)`));
2014
+ if (match?.[1]) {
2015
+ return match[1];
2670
2016
  }
2671
2017
  }
2672
- const buildOutput = captured.output;
2673
- return {
2674
- result,
2675
- error,
2676
- bundle: buildOutput?.bundle ?? null,
2677
- module: buildOutput?.module ?? null,
2678
- sandbox
2679
- };
2680
- };
2018
+ const urlMatch = response.url.match(new RegExp(`${escapeRegex(packageName)}@([^/]+)`));
2019
+ if (urlMatch?.[1]) {
2020
+ return urlMatch[1];
2021
+ }
2022
+ return requestedVersion ?? "latest";
2023
+ }
2681
2024
  }
2682
-
2683
- // src/index.ts
2684
- if (typeof window !== "undefined" && typeof globalThis.process === "undefined") {
2685
- globalThis.process = {
2686
- env: {},
2687
- platform: "browser",
2688
- version: "v20.0.0",
2689
- browser: true,
2690
- cwd: () => "/",
2691
- nextTick: (fn) => setTimeout(fn, 0)
2025
+ function parseSpecifier(specifier) {
2026
+ if (specifier.startsWith("@")) {
2027
+ const parts = specifier.split("/");
2028
+ if (parts.length >= 2) {
2029
+ const packageName = `${parts[0]}/${parts[1]}`;
2030
+ const subpath = parts.length > 2 ? parts.slice(2).join("/") : undefined;
2031
+ return { packageName, subpath };
2032
+ }
2033
+ return { packageName: specifier, subpath: undefined };
2034
+ }
2035
+ const slashIndex = specifier.indexOf("/");
2036
+ if (slashIndex === -1) {
2037
+ return { packageName: specifier, subpath: undefined };
2038
+ }
2039
+ return {
2040
+ packageName: specifier.slice(0, slashIndex),
2041
+ subpath: specifier.slice(slashIndex + 1)
2692
2042
  };
2693
2043
  }
2044
+ function toTypesPackageName(packageName) {
2045
+ if (packageName.startsWith("@")) {
2046
+ return "@types/" + packageName.slice(1).replace("/", "__");
2047
+ }
2048
+ return `@types/${packageName}`;
2049
+ }
2050
+ function parseReferences(content) {
2051
+ const paths = [];
2052
+ const types = [];
2053
+ const pathRegex = /\/\/\/\s*<reference\s+path="([^"]+)"\s*\/>/g;
2054
+ let match;
2055
+ while ((match = pathRegex.exec(content)) !== null) {
2056
+ if (match[1])
2057
+ paths.push(match[1]);
2058
+ }
2059
+ const typesRegex = /\/\/\/\s*<reference\s+types="([^"]+)"\s*\/>/g;
2060
+ while ((match = typesRegex.exec(content)) !== null) {
2061
+ if (match[1])
2062
+ types.push(match[1]);
2063
+ }
2064
+ return { paths, types };
2065
+ }
2066
+ function makeCacheKey(packageName, subpath, version) {
2067
+ const base = version ? `${packageName}@${version}` : packageName;
2068
+ return subpath ? `${base}/${subpath}` : base;
2069
+ }
2070
+ function escapeRegex(str) {
2071
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2072
+ }
2694
2073
  export {
2695
- unregisterSharedModule,
2696
- uninstallPackage,
2697
- typecheck,
2698
- registerSharedModules,
2699
- loadModule,
2700
- loadExport,
2701
- loadDefault,
2702
- listPackages,
2703
- installPackage,
2704
- initBundler,
2705
- hasExport,
2706
- hasDefaultResources,
2707
- getPackageManifest,
2708
- getExportNames,
2709
- getDefaultResources,
2710
- getDefaultBrowserLibs,
2711
- formatDiagnosticsForAgent,
2074
+ wrapFilesystemForJustBash,
2075
+ formatSize,
2712
2076
  formatDiagnostics,
2713
- fetchAndCacheLibs,
2714
- createUninstallCommand,
2715
- createTscCommand,
2716
- createSharedResources,
2717
- createSandbox,
2718
- createRunCommand,
2719
- createListCommand,
2720
- createInstallCommand,
2077
+ formatBundleErrors,
2078
+ createTypechecker,
2079
+ createSharedModuleRegistry,
2080
+ createSandlotCommand,
2081
+ createSandlot,
2721
2082
  createFilesystem,
2722
2083
  createDefaultCommands,
2723
- createBuilder,
2724
- createBuildCommand,
2725
- clearSharedModules,
2726
- clearDefaultResources,
2727
- bundleToUrl,
2728
- bundleAndImport,
2729
- bundle,
2730
- ModuleLoadError,
2084
+ Typechecker,
2085
+ SharedModuleRegistry,
2086
+ InMemoryTypesCache,
2731
2087
  Filesystem,
2732
- ExportNotFoundError
2088
+ EsmTypesResolver
2733
2089
  };