sandlot 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +138 -408
  2. package/dist/build-emitter.d.ts +31 -13
  3. package/dist/build-emitter.d.ts.map +1 -1
  4. package/dist/builder.d.ts +370 -0
  5. package/dist/builder.d.ts.map +1 -0
  6. package/dist/bundler.d.ts +6 -2
  7. package/dist/bundler.d.ts.map +1 -1
  8. package/dist/commands/compile.d.ts +13 -0
  9. package/dist/commands/compile.d.ts.map +1 -0
  10. package/dist/commands/index.d.ts +17 -0
  11. package/dist/commands/index.d.ts.map +1 -0
  12. package/dist/commands/packages.d.ts +17 -0
  13. package/dist/commands/packages.d.ts.map +1 -0
  14. package/dist/commands/run.d.ts +40 -0
  15. package/dist/commands/run.d.ts.map +1 -0
  16. package/dist/commands/types.d.ts +141 -0
  17. package/dist/commands/types.d.ts.map +1 -0
  18. package/dist/fs.d.ts +53 -49
  19. package/dist/fs.d.ts.map +1 -1
  20. package/dist/index.d.ts +5 -4
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +300 -511
  23. package/dist/internal.js +161 -171
  24. package/dist/runner.d.ts +314 -0
  25. package/dist/runner.d.ts.map +1 -0
  26. package/dist/sandbox-manager.d.ts +45 -21
  27. package/dist/sandbox-manager.d.ts.map +1 -1
  28. package/dist/sandbox.d.ts +144 -62
  29. package/dist/sandbox.d.ts.map +1 -1
  30. package/dist/shared-modules.d.ts +22 -3
  31. package/dist/shared-modules.d.ts.map +1 -1
  32. package/dist/shared-resources.d.ts +0 -3
  33. package/dist/shared-resources.d.ts.map +1 -1
  34. package/dist/ts-libs.d.ts +7 -20
  35. package/dist/ts-libs.d.ts.map +1 -1
  36. package/dist/typechecker.d.ts +1 -1
  37. package/package.json +5 -5
  38. package/src/build-emitter.ts +32 -29
  39. package/src/builder.ts +498 -0
  40. package/src/bundler.ts +76 -55
  41. package/src/commands/compile.ts +236 -0
  42. package/src/commands/index.ts +51 -0
  43. package/src/commands/packages.ts +154 -0
  44. package/src/commands/run.ts +245 -0
  45. package/src/commands/types.ts +172 -0
  46. package/src/fs.ts +82 -221
  47. package/src/index.ts +17 -12
  48. package/src/sandbox.ts +219 -149
  49. package/src/shared-modules.ts +74 -4
  50. package/src/shared-resources.ts +0 -3
  51. package/src/ts-libs.ts +19 -121
  52. package/src/typechecker.ts +1 -1
  53. package/dist/react.d.ts +0 -159
  54. package/dist/react.d.ts.map +0 -1
  55. package/dist/react.js +0 -149
  56. package/src/commands.ts +0 -733
  57. package/src/sandbox-manager.ts +0 -409
package/dist/internal.js CHANGED
@@ -1,3 +1,11 @@
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
+
1
9
  // src/loader.ts
2
10
  class ModuleLoadError extends Error {
3
11
  constructor(message, cause) {
@@ -514,9 +522,6 @@ async function listPackages(fs) {
514
522
  // src/ts-libs.ts
515
523
  var TS_VERSION = "5.9.3";
516
524
  var CDN_BASE = `https://cdn.jsdelivr.net/npm/typescript@${TS_VERSION}/lib`;
517
- var DB_NAME = "ts-lib-cache";
518
- var DB_VERSION = 1;
519
- var STORE_NAME = "libs";
520
525
  function getDefaultBrowserLibs() {
521
526
  return ["es2020", "dom", "dom.iterable"];
522
527
  }
@@ -581,110 +586,54 @@ async function fetchAllLibs(libs) {
581
586
  }
582
587
  return result;
583
588
  }
584
- async function openDatabase() {
585
- return new Promise((resolve, reject) => {
586
- const request = indexedDB.open(DB_NAME, DB_VERSION);
587
- request.onerror = () => reject(request.error);
588
- request.onsuccess = () => resolve(request.result);
589
- request.onupgradeneeded = (event) => {
590
- const db = event.target.result;
591
- if (!db.objectStoreNames.contains(STORE_NAME)) {
592
- db.createObjectStore(STORE_NAME);
593
- }
594
- };
595
- });
596
- }
597
- function promisifyRequest(request) {
598
- return new Promise((resolve, reject) => {
599
- request.onsuccess = () => resolve(request.result);
600
- request.onerror = () => reject(request.error);
601
- });
602
- }
603
- function getCacheKey() {
604
- return `libs-${TS_VERSION}`;
605
- }
589
+ var memoryCache = null;
606
590
 
607
591
  class LibCache {
608
- db;
609
- constructor(db) {
610
- this.db = db;
611
- }
612
- static async create() {
613
- const db = await openDatabase();
614
- return new LibCache(db);
615
- }
616
592
  async getOrFetch(libs) {
617
- const cached = await this.get();
618
- if (cached) {
619
- const missing = libs.filter((lib) => !cached.has(lib));
593
+ if (memoryCache) {
594
+ const missing = libs.filter((lib) => !memoryCache.has(lib));
620
595
  if (missing.length === 0) {
621
- return cached;
596
+ return memoryCache;
622
597
  }
623
598
  console.log(`Cache missing libs: ${missing.join(", ")}, fetching all...`);
624
599
  }
625
600
  console.log(`Fetching TypeScript libs from CDN: ${libs.join(", ")}...`);
626
601
  const fetched = await fetchAllLibs(libs);
627
602
  console.log(`Fetched ${fetched.size} lib files`);
628
- await this.set(fetched);
603
+ memoryCache = fetched;
629
604
  return fetched;
630
605
  }
631
- async get() {
632
- const tx = this.db.transaction(STORE_NAME, "readonly");
633
- const store = tx.objectStore(STORE_NAME);
634
- const key = getCacheKey();
635
- const cached = await promisifyRequest(store.get(key));
636
- if (!cached) {
637
- return null;
638
- }
639
- if (cached.version !== TS_VERSION) {
640
- console.log(`Cache version mismatch: ${cached.version} vs ${TS_VERSION}`);
641
- return null;
642
- }
643
- return new Map(Object.entries(cached.libs));
644
- }
645
- async set(libs) {
646
- const tx = this.db.transaction(STORE_NAME, "readwrite");
647
- const store = tx.objectStore(STORE_NAME);
648
- const key = getCacheKey();
649
- const cached = {
650
- version: TS_VERSION,
651
- timestamp: Date.now(),
652
- libs: Object.fromEntries(libs)
653
- };
654
- await promisifyRequest(store.put(cached, key));
606
+ get() {
607
+ return memoryCache;
655
608
  }
656
- async clear() {
657
- const tx = this.db.transaction(STORE_NAME, "readwrite");
658
- const store = tx.objectStore(STORE_NAME);
659
- await promisifyRequest(store.clear());
609
+ set(libs) {
610
+ memoryCache = libs;
660
611
  }
661
- close() {
662
- this.db.close();
612
+ clear() {
613
+ memoryCache = null;
663
614
  }
664
615
  }
665
616
  async function fetchAndCacheLibs(libs = getDefaultBrowserLibs()) {
666
- const cache = await LibCache.create();
667
- try {
668
- return await cache.getOrFetch(libs);
669
- } finally {
670
- cache.close();
671
- }
617
+ const cache = new LibCache;
618
+ return cache.getOrFetch(libs);
672
619
  }
673
620
  // src/shared-modules.ts
674
621
  var GLOBAL_KEY = "__sandlot_shared_modules__";
675
622
 
676
623
  class SharedModuleRegistry {
677
624
  modules = new Map;
625
+ exportNames = new Map;
678
626
  constructor() {
679
627
  globalThis[GLOBAL_KEY] = this;
680
628
  }
681
629
  register(moduleId, module) {
682
630
  this.modules.set(moduleId, module);
631
+ this.exportNames.set(moduleId, introspectExports(module));
683
632
  return this;
684
633
  }
685
634
  registerAll(modules) {
686
635
  for (const [id, mod] of Object.entries(modules)) {
687
- this.modules.set(id, mod);
636
+ this.register(id, mod);
688
637
  }
689
638
  return this;
690
639
  }
@@ -705,13 +654,44 @@ class SharedModuleRegistry {
705
654
  list() {
706
655
  return [...this.modules.keys()];
707
656
  }
657
+ getExportNames(moduleId) {
658
+ return this.exportNames.get(moduleId) ?? [];
659
+ }
708
660
  clear() {
709
661
  this.modules.clear();
662
+ this.exportNames.clear();
710
663
  }
711
664
  get size() {
712
665
  return this.modules.size;
713
666
  }
714
667
  }
668
+ function introspectExports(module) {
669
+ if (module === null || module === undefined) {
670
+ return [];
671
+ }
672
+ if (typeof module !== "object" && typeof module !== "function") {
673
+ return [];
674
+ }
675
+ const exports = [];
676
+ for (const key of Object.keys(module)) {
677
+ if (isValidIdentifier(key)) {
678
+ exports.push(key);
679
+ }
680
+ }
681
+ return exports;
682
+ }
683
+ function isValidIdentifier(name) {
684
+ if (name.length === 0)
685
+ return false;
686
+ if (!/^[a-zA-Z_$]/.test(name))
687
+ return false;
688
+ if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name))
689
+ return false;
690
+ const reserved = ["default", "class", "function", "var", "let", "const", "import", "export"];
691
+ if (reserved.includes(name))
692
+ return false;
693
+ return true;
694
+ }
715
695
  var defaultRegistry = null;
716
696
  function getSharedModuleRegistry() {
717
697
  if (!defaultRegistry) {
@@ -731,6 +711,9 @@ function unregisterSharedModule(moduleId) {
731
711
  function clearSharedModules() {
732
712
  getSharedModuleRegistry().clear();
733
713
  }
714
+ function getSharedModuleExports(moduleId) {
715
+ return getSharedModuleRegistry().getExportNames(moduleId);
716
+ }
734
717
  function getSharedModuleRuntimeCode(moduleId) {
735
718
  return `
736
719
  (function() {
@@ -745,7 +728,21 @@ function getSharedModuleRuntimeCode(moduleId) {
745
728
  })()
746
729
  `.trim();
747
730
  }
748
- // src/commands.ts
731
+ // src/commands/types.ts
732
+ function formatEsbuildMessages(messages) {
733
+ if (messages.length === 0)
734
+ return "";
735
+ return messages.map((msg) => {
736
+ if (msg.location) {
737
+ const { file, line, column } = msg.location;
738
+ const loc = file ? `${file}${line ? `:${line}` : ""}${column ? `:${column}` : ""}` : "";
739
+ return loc ? `${loc}: ${msg.text}` : msg.text;
740
+ }
741
+ return msg.text;
742
+ }).join(`
743
+ `);
744
+ }
745
+ // src/commands/compile.ts
749
746
  import { defineCommand } from "just-bash/browser";
750
747
 
751
748
  // src/typechecker.ts
@@ -1082,15 +1079,32 @@ function formatDiagnosticsForAgent(diagnostics) {
1082
1079
 
1083
1080
  // src/bundler.ts
1084
1081
  var esbuild = null;
1082
+ function isServerEnvironment() {
1083
+ if (typeof globalThis !== "undefined" && "Bun" in globalThis) {
1084
+ return true;
1085
+ }
1086
+ if (typeof globalThis !== "undefined" && "Deno" in globalThis) {
1087
+ return true;
1088
+ }
1089
+ if (typeof process !== "undefined" && process.versions?.node) {
1090
+ return true;
1091
+ }
1092
+ return false;
1093
+ }
1085
1094
  async function getEsbuild() {
1086
1095
  if (esbuild)
1087
1096
  return esbuild;
1088
- const cdnUrl = `https://esm.sh/esbuild-wasm@${ESBUILD_VERSION}`;
1089
- const mod = await import(cdnUrl);
1090
- esbuild = mod.default ?? mod;
1091
- if (typeof esbuild?.initialize !== "function") {
1092
- console.error("esbuild-wasm module structure:", mod);
1093
- throw new Error("Failed to load esbuild-wasm: initialize function not found");
1097
+ if (isServerEnvironment()) {
1098
+ const mod = await import("esbuild");
1099
+ esbuild = mod.default ?? mod;
1100
+ } else {
1101
+ const cdnUrl = `https://esm.sh/esbuild-wasm@${ESBUILD_VERSION}`;
1102
+ const mod = await import(cdnUrl);
1103
+ esbuild = mod.default ?? mod;
1104
+ if (typeof esbuild?.initialize !== "function") {
1105
+ console.error("esbuild-wasm module structure:", mod);
1106
+ throw new Error("Failed to load esbuild-wasm: initialize function not found");
1107
+ }
1094
1108
  }
1095
1109
  return esbuild;
1096
1110
  }
@@ -1118,12 +1132,14 @@ async function initBundler() {
1118
1132
  await initPromise;
1119
1133
  return;
1120
1134
  }
1121
- checkCrossOriginIsolation();
1122
1135
  initPromise = (async () => {
1123
1136
  const es = await getEsbuild();
1124
- await es.initialize({
1125
- wasmURL: getWasmUrl()
1126
- });
1137
+ if (!isServerEnvironment() && typeof es.initialize === "function") {
1138
+ checkCrossOriginIsolation();
1139
+ await es.initialize({
1140
+ wasmURL: getWasmUrl()
1141
+ });
1142
+ }
1127
1143
  })();
1128
1144
  await initPromise;
1129
1145
  initialized = true;
@@ -1262,55 +1278,12 @@ ${generateNamedExports(args.path)}
1262
1278
  };
1263
1279
  }
1264
1280
  function generateNamedExports(moduleId) {
1265
- const knownExports = {
1266
- react: [
1267
- "useState",
1268
- "useEffect",
1269
- "useContext",
1270
- "useReducer",
1271
- "useCallback",
1272
- "useMemo",
1273
- "useRef",
1274
- "useImperativeHandle",
1275
- "useLayoutEffect",
1276
- "useDebugValue",
1277
- "useDeferredValue",
1278
- "useTransition",
1279
- "useId",
1280
- "useSyncExternalStore",
1281
- "useInsertionEffect",
1282
- "useOptimistic",
1283
- "useActionState",
1284
- "createElement",
1285
- "cloneElement",
1286
- "createContext",
1287
- "forwardRef",
1288
- "lazy",
1289
- "memo",
1290
- "startTransition",
1291
- "Children",
1292
- "Component",
1293
- "PureComponent",
1294
- "Fragment",
1295
- "Profiler",
1296
- "StrictMode",
1297
- "Suspense",
1298
- "version",
1299
- "isValidElement"
1300
- ],
1301
- "react-dom": ["createPortal", "flushSync", "version"],
1302
- "react-dom/client": ["createRoot", "hydrateRoot"],
1303
- "react-dom/server": ["renderToString", "renderToStaticMarkup", "renderToPipeableStream"]
1304
- };
1305
- const exports = knownExports[moduleId];
1306
- if (!exports) {
1307
- return `
1308
- // Dynamic re-export for unknown module
1309
- export const __moduleProxy__ = __sandlot_mod__;
1310
- `;
1311
- }
1312
- return exports.map((name) => `export const ${name} = __sandlot_mod__.${name};`).join(`
1281
+ const exports = getSharedModuleExports(moduleId);
1282
+ if (exports.length > 0) {
1283
+ return exports.map((name) => `export const ${name} = __sandlot_mod__.${name};`).join(`
1313
1284
  `);
1285
+ }
1286
+ return `// No exports discovered for "${moduleId}" - use default import or call registerSharedModules() first`;
1314
1287
  }
1315
1288
  async function bundle(options) {
1316
1289
  await initBundler();
@@ -1353,7 +1326,8 @@ async function bundle(options) {
1353
1326
  globalName,
1354
1327
  target,
1355
1328
  external,
1356
- plugins: [plugin]
1329
+ plugins: [plugin],
1330
+ jsx: "automatic"
1357
1331
  });
1358
1332
  const code = result.outputFiles?.[0]?.text ?? "";
1359
1333
  return {
@@ -1376,20 +1350,7 @@ async function bundleAndImport(options) {
1376
1350
  }
1377
1351
  }
1378
1352
 
1379
- // src/commands.ts
1380
- function formatEsbuildMessages(messages) {
1381
- if (messages.length === 0)
1382
- return "";
1383
- return messages.map((msg) => {
1384
- if (msg.location) {
1385
- const { file, line, column } = msg.location;
1386
- const loc = file ? `${file}${line ? `:${line}` : ""}${column ? `:${column}` : ""}` : "";
1387
- return loc ? `${loc}: ${msg.text}` : msg.text;
1388
- }
1389
- return msg.text;
1390
- }).join(`
1391
- `);
1392
- }
1353
+ // src/commands/compile.ts
1393
1354
  function createTscCommand(deps) {
1394
1355
  const { fs, libFiles, tsconfigPath } = deps;
1395
1356
  return defineCommand("tsc", async (args, _ctx) => {
@@ -1454,7 +1415,7 @@ ${formatDiagnosticsForAgent(result.diagnostics.filter((d) => d.category === "war
1454
1415
  });
1455
1416
  }
1456
1417
  function createBuildCommand(deps) {
1457
- const { fs, libFiles, tsconfigPath, onBuild, sharedModules } = deps;
1418
+ const { fs, libFiles, tsconfigPath, onBuild, getValidation, sharedModules } = deps;
1458
1419
  return defineCommand("build", async (args, _ctx) => {
1459
1420
  let entryPoint = null;
1460
1421
  let skipTypecheck = false;
@@ -1526,8 +1487,39 @@ ${formatted}
1526
1487
  minify,
1527
1488
  sharedModules
1528
1489
  });
1490
+ let loadedModule;
1491
+ try {
1492
+ loadedModule = await loadModule(bundleResult);
1493
+ } catch (err) {
1494
+ const errorMessage = err instanceof Error ? err.message : String(err);
1495
+ return {
1496
+ stdout: "",
1497
+ stderr: `Build failed: Module failed to load.
1498
+
1499
+ ${errorMessage}
1500
+ `,
1501
+ exitCode: 1
1502
+ };
1503
+ }
1504
+ const validateFn = getValidation?.();
1505
+ let validatedModule = loadedModule;
1506
+ if (validateFn) {
1507
+ try {
1508
+ validatedModule = validateFn(loadedModule);
1509
+ } catch (err) {
1510
+ const errorMessage = err instanceof Error ? err.message : String(err);
1511
+ return {
1512
+ stdout: "",
1513
+ stderr: `Build failed: Validation error.
1514
+
1515
+ ${errorMessage}
1516
+ `,
1517
+ exitCode: 1
1518
+ };
1519
+ }
1520
+ }
1529
1521
  if (onBuild) {
1530
- await onBuild(bundleResult);
1522
+ await onBuild({ bundle: bundleResult, module: validatedModule });
1531
1523
  }
1532
1524
  let output = `Build successful!
1533
1525
  `;
@@ -1543,6 +1535,15 @@ ${formatted}
1543
1535
  }
1544
1536
  output += `Bundled: ${bundleResult.includedFiles.length} file(s)
1545
1537
  `;
1538
+ const exportNames = Object.keys(loadedModule).filter((k) => !k.startsWith("__"));
1539
+ if (exportNames.length > 0) {
1540
+ output += `Exports: ${exportNames.join(", ")}
1541
+ `;
1542
+ }
1543
+ if (validateFn) {
1544
+ output += `Validation: passed
1545
+ `;
1546
+ }
1546
1547
  if (bundleResult.warnings.length > 0) {
1547
1548
  output += `
1548
1549
  Build warnings:
@@ -1574,9 +1575,11 @@ ${formatDiagnosticsForAgent(warnings)}
1574
1575
  }
1575
1576
  });
1576
1577
  }
1578
+ // src/commands/packages.ts
1579
+ import { defineCommand as defineCommand2 } from "just-bash/browser";
1577
1580
  function createInstallCommand(deps) {
1578
1581
  const { fs, typesCache } = deps;
1579
- return defineCommand("install", async (args, _ctx) => {
1582
+ return defineCommand2("install", async (args, _ctx) => {
1580
1583
  if (args.length === 0) {
1581
1584
  return {
1582
1585
  stdout: "",
@@ -1630,7 +1633,7 @@ Examples:
1630
1633
  }
1631
1634
  function createUninstallCommand(deps) {
1632
1635
  const { fs } = deps;
1633
- return defineCommand("uninstall", async (args, _ctx) => {
1636
+ return defineCommand2("uninstall", async (args, _ctx) => {
1634
1637
  if (args.length === 0) {
1635
1638
  return {
1636
1639
  stdout: "",
@@ -1675,7 +1678,7 @@ function createUninstallCommand(deps) {
1675
1678
  }
1676
1679
  function createListCommand(deps) {
1677
1680
  const { fs } = deps;
1678
- return defineCommand("list", async (_args, _ctx) => {
1681
+ return defineCommand2("list", async (_args, _ctx) => {
1679
1682
  try {
1680
1683
  const packages = await listPackages(fs);
1681
1684
  if (packages.length === 0) {
@@ -1705,9 +1708,11 @@ function createListCommand(deps) {
1705
1708
  }
1706
1709
  });
1707
1710
  }
1711
+ // src/commands/run.ts
1712
+ import { defineCommand as defineCommand3 } from "just-bash/browser";
1708
1713
  function createRunCommand(deps) {
1709
1714
  const { fs, libFiles, tsconfigPath, runOptions = {}, sharedModules } = deps;
1710
- return defineCommand("run", async (args, _ctx) => {
1715
+ return defineCommand3("run", async (args, _ctx) => {
1711
1716
  let entryPoint = null;
1712
1717
  let skipTypecheck = runOptions.skipTypecheck ?? false;
1713
1718
  let timeout = runOptions.timeout ?? 30000;
@@ -1884,6 +1889,7 @@ ${err.stack}` : "";
1884
1889
  }
1885
1890
  });
1886
1891
  }
1892
+ // src/commands/index.ts
1887
1893
  function createDefaultCommands(deps) {
1888
1894
  return [
1889
1895
  createTscCommand(deps),
@@ -1897,9 +1903,7 @@ function createDefaultCommands(deps) {
1897
1903
  // src/build-emitter.ts
1898
1904
  class BuildEmitter {
1899
1905
  listeners = new Set;
1900
- lastResult = null;
1901
1906
  emit = async (result) => {
1902
- this.lastResult = result;
1903
1907
  const promises = [];
1904
1908
  for (const listener of this.listeners) {
1905
1909
  const ret = listener(result);
@@ -1915,20 +1919,6 @@ class BuildEmitter {
1915
1919
  this.listeners.delete(callback);
1916
1920
  };
1917
1921
  }
1918
- waitFor() {
1919
- if (this.lastResult) {
1920
- const result = this.lastResult;
1921
- this.lastResult = null;
1922
- return Promise.resolve(result);
1923
- }
1924
- return new Promise((resolve) => {
1925
- const unsub = this.on((result) => {
1926
- unsub();
1927
- this.lastResult = null;
1928
- resolve(result);
1929
- });
1930
- });
1931
- }
1932
1922
  }
1933
1923
  export {
1934
1924
  revokeModuleUrl,