sandlot 0.1.3 → 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.
package/README.md CHANGED
@@ -176,7 +176,7 @@ localStorage.setItem("project", JSON.stringify(state));
176
176
  | `sharedModules` | `string[]` | Modules to resolve from host (e.g., `['react']`). |
177
177
  | `tsconfigPath` | `string` | Path to tsconfig.json (default: `/tsconfig.json`). |
178
178
  | `onBuild` | `(result) => void` | Callback when a build succeeds. |
179
- | `bashOptions` | `SandboxBashOptions` | Options for the just-bash shell (cwd, env, limits). |
179
+ | `bashOptions` | `SandboxBashOptions` | Options for the just-bash shell (env, limits). |
180
180
 
181
181
  ## Shell Commands
182
182
 
package/dist/bundler.d.ts CHANGED
@@ -88,8 +88,12 @@ export interface BundleResult {
88
88
  includedFiles: string[];
89
89
  }
90
90
  /**
91
- * Initialize esbuild-wasm. Called automatically on first bundle.
91
+ * Initialize esbuild. Called automatically on first bundle.
92
92
  * Can be called explicitly to pre-warm.
93
+ *
94
+ * In browser environments, this loads and initializes esbuild-wasm.
95
+ * In server environments (Node.js, Bun, Deno), this loads native esbuild
96
+ * which doesn't require WASM initialization.
93
97
  */
94
98
  export declare function initBundler(): Promise<void>;
95
99
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,KAAK,YAAY,MAAM,cAAc,CAAC;AA6BlD;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,EAAE,EAAE,WAAW,CAAC;IAEhB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAE5B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAEhC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,QAAQ,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;IAEjC;;OAEG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAsCD;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAmBjD;AA+ND;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA+D1E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAIzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAOrF"}
1
+ {"version":3,"file":"bundler.d.ts","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,KAAK,YAAY,MAAM,cAAc,CAAC;AAuDlD;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,UAAU,GAAG,QAAQ,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,EAAE,EAAE,WAAW,CAAC;IAEhB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,cAAc,CAAC;IAE5B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAEhC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,QAAQ,EAAE,YAAY,CAAC,OAAO,EAAE,CAAC;IAEjC;;OAEG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAsCD;;;;;;;GAOG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAsBjD;AA+ND;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA+D1E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAIzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,CAOrF"}
package/dist/index.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/sandbox.ts
2
10
  import { Bash } from "just-bash/browser";
3
11
 
@@ -1107,15 +1115,32 @@ function getSharedModuleRuntimeCode(moduleId) {
1107
1115
 
1108
1116
  // src/bundler.ts
1109
1117
  var esbuild = null;
1118
+ function isServerEnvironment() {
1119
+ if (typeof globalThis !== "undefined" && "Bun" in globalThis) {
1120
+ return true;
1121
+ }
1122
+ if (typeof globalThis !== "undefined" && "Deno" in globalThis) {
1123
+ return true;
1124
+ }
1125
+ if (typeof process !== "undefined" && process.versions?.node) {
1126
+ return true;
1127
+ }
1128
+ return false;
1129
+ }
1110
1130
  async function getEsbuild() {
1111
1131
  if (esbuild)
1112
1132
  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");
1133
+ if (isServerEnvironment()) {
1134
+ const mod = await import("esbuild");
1135
+ esbuild = mod.default ?? mod;
1136
+ } else {
1137
+ const cdnUrl = `https://esm.sh/esbuild-wasm@${ESBUILD_VERSION}`;
1138
+ const mod = await import(cdnUrl);
1139
+ esbuild = mod.default ?? mod;
1140
+ if (typeof esbuild?.initialize !== "function") {
1141
+ console.error("esbuild-wasm module structure:", mod);
1142
+ throw new Error("Failed to load esbuild-wasm: initialize function not found");
1143
+ }
1119
1144
  }
1120
1145
  return esbuild;
1121
1146
  }
@@ -1143,12 +1168,14 @@ async function initBundler() {
1143
1168
  await initPromise;
1144
1169
  return;
1145
1170
  }
1146
- checkCrossOriginIsolation();
1147
1171
  initPromise = (async () => {
1148
1172
  const es = await getEsbuild();
1149
- await es.initialize({
1150
- wasmURL: getWasmUrl()
1151
- });
1173
+ if (!isServerEnvironment() && typeof es.initialize === "function") {
1174
+ checkCrossOriginIsolation();
1175
+ await es.initialize({
1176
+ wasmURL: getWasmUrl()
1177
+ });
1178
+ }
1152
1179
  })();
1153
1180
  await initPromise;
1154
1181
  initialized = true;
@@ -2318,9 +2345,6 @@ function createDefaultCommands(deps) {
2318
2345
  // src/ts-libs.ts
2319
2346
  var TS_VERSION = "5.9.3";
2320
2347
  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
2348
  function getDefaultBrowserLibs() {
2325
2349
  return ["es2020", "dom", "dom.iterable"];
2326
2350
  }
@@ -2385,94 +2409,36 @@ async function fetchAllLibs(libs) {
2385
2409
  }
2386
2410
  return result;
2387
2411
  }
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
- });
2406
- }
2407
- function getCacheKey() {
2408
- return `libs-${TS_VERSION}`;
2409
- }
2412
+ var memoryCache = null;
2410
2413
 
2411
2414
  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
2415
  async getOrFetch(libs) {
2421
- const cached = await this.get();
2422
- if (cached) {
2423
- const missing = libs.filter((lib) => !cached.has(lib));
2416
+ if (memoryCache) {
2417
+ const missing = libs.filter((lib) => !memoryCache.has(lib));
2424
2418
  if (missing.length === 0) {
2425
- return cached;
2419
+ return memoryCache;
2426
2420
  }
2427
2421
  console.log(`Cache missing libs: ${missing.join(", ")}, fetching all...`);
2428
2422
  }
2429
2423
  console.log(`Fetching TypeScript libs from CDN: ${libs.join(", ")}...`);
2430
2424
  const fetched = await fetchAllLibs(libs);
2431
2425
  console.log(`Fetched ${fetched.size} lib files`);
2432
- await this.set(fetched);
2426
+ memoryCache = fetched;
2433
2427
  return fetched;
2434
2428
  }
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;
2442
- }
2443
- if (cached.version !== TS_VERSION) {
2444
- console.log(`Cache version mismatch: ${cached.version} vs ${TS_VERSION}`);
2445
- return null;
2446
- }
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)
2457
- };
2458
- await promisifyRequest(store.put(cached, key));
2429
+ get() {
2430
+ return memoryCache;
2459
2431
  }
2460
- async clear() {
2461
- const tx = this.db.transaction(STORE_NAME, "readwrite");
2462
- const store = tx.objectStore(STORE_NAME);
2463
- await promisifyRequest(store.clear());
2432
+ set(libs) {
2433
+ memoryCache = libs;
2464
2434
  }
2465
- close() {
2466
- this.db.close();
2435
+ clear() {
2436
+ memoryCache = null;
2467
2437
  }
2468
2438
  }
2469
2439
  async function fetchAndCacheLibs(libs = getDefaultBrowserLibs()) {
2470
- const cache = await LibCache.create();
2471
- try {
2472
- return await cache.getOrFetch(libs);
2473
- } finally {
2474
- cache.close();
2475
- }
2440
+ const cache = new LibCache;
2441
+ return cache.getOrFetch(libs);
2476
2442
  }
2477
2443
 
2478
2444
  // src/shared-resources.ts
@@ -2587,6 +2553,7 @@ async function createSandbox(options = {}) {
2587
2553
  const defaultCommands = createDefaultCommands(commandDeps);
2588
2554
  const bash = new Bash({
2589
2555
  ...bashOptions,
2556
+ cwd: "/",
2590
2557
  fs,
2591
2558
  customCommands: [...defaultCommands, ...customCommands]
2592
2559
  });
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,94 +586,36 @@ 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__";
@@ -1132,15 +1079,32 @@ function formatDiagnosticsForAgent(diagnostics) {
1132
1079
 
1133
1080
  // src/bundler.ts
1134
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
+ }
1135
1094
  async function getEsbuild() {
1136
1095
  if (esbuild)
1137
1096
  return esbuild;
1138
- const cdnUrl = `https://esm.sh/esbuild-wasm@${ESBUILD_VERSION}`;
1139
- const mod = await import(cdnUrl);
1140
- esbuild = mod.default ?? mod;
1141
- if (typeof esbuild?.initialize !== "function") {
1142
- console.error("esbuild-wasm module structure:", mod);
1143
- 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
+ }
1144
1108
  }
1145
1109
  return esbuild;
1146
1110
  }
@@ -1168,12 +1132,14 @@ async function initBundler() {
1168
1132
  await initPromise;
1169
1133
  return;
1170
1134
  }
1171
- checkCrossOriginIsolation();
1172
1135
  initPromise = (async () => {
1173
1136
  const es = await getEsbuild();
1174
- await es.initialize({
1175
- wasmURL: getWasmUrl()
1176
- });
1137
+ if (!isServerEnvironment() && typeof es.initialize === "function") {
1138
+ checkCrossOriginIsolation();
1139
+ await es.initialize({
1140
+ wasmURL: getWasmUrl()
1141
+ });
1142
+ }
1177
1143
  })();
1178
1144
  await initPromise;
1179
1145
  initialized = true;
package/dist/sandbox.d.ts CHANGED
@@ -4,9 +4,10 @@ import { type BuildOutput, type ValidateFn } from "./commands";
4
4
  import { type SharedResources } from "./shared-resources";
5
5
  /**
6
6
  * Options that can be passed through to the just-bash Bash constructor.
7
- * Excludes options that sandlot controls internally (fs, customCommands, files).
7
+ * Excludes options that sandlot controls internally (fs, customCommands, files, cwd).
8
+ * The working directory is always root (/).
8
9
  */
9
- export type SandboxBashOptions = Omit<BashOptions, 'fs' | 'customCommands' | 'files'>;
10
+ export type SandboxBashOptions = Omit<BashOptions, 'fs' | 'customCommands' | 'files' | 'cwd'>;
10
11
  /**
11
12
  * Options for creating a sandbox environment
12
13
  */
@@ -89,14 +90,13 @@ export interface SandboxOptions {
89
90
  * Use this to configure environment variables, execution limits,
90
91
  * network access, logging, and other bash-level settings.
91
92
  *
92
- * Note: `fs`, `customCommands`, and `files` are controlled by sandlot
93
- * and cannot be overridden here.
93
+ * Note: `fs`, `customCommands`, `files`, and `cwd` are controlled by sandlot
94
+ * and cannot be overridden here. The working directory is always root (/).
94
95
  *
95
96
  * @example
96
97
  * ```ts
97
98
  * const sandbox = await createSandbox({
98
99
  * bashOptions: {
99
- * cwd: '/src',
100
100
  * env: { NODE_ENV: 'development' },
101
101
  * executionLimits: { maxCommandCount: 1000 },
102
102
  * },
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAA0B,MAAM,MAAM,CAAC;AAE1D,OAAO,EAA2C,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AACxG,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAI/E;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,gBAAgB,GAAG,OAAO,CAAC,CAAC;AAEtF;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExD;;OAEG;IACH,cAAc,CAAC,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,EAAE,CAAC;IAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;;;;;;;;;OAkBG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,EAAE,EAAE,UAAU,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,IAAI,CAAC;IAEX;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAE9B;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,IAAI,YAAY,CAAC;IAEzB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;IAE7E;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI,CAAC;IAEpC;;;OAGG;IACH,eAAe,IAAI,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CA4GlF"}
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAA0B,MAAM,MAAM,CAAC;AAE1D,OAAO,EAA2C,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AACxG,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAI/E;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,gBAAgB,GAAG,OAAO,GAAG,KAAK,CAAC,CAAC;AAE9F;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExD;;OAEG;IACH,cAAc,CAAC,EAAE,UAAU,CAAC,OAAO,aAAa,CAAC,EAAE,CAAC;IAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB;;;;;;;;;;;;;;;;;OAiBG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,EAAE,EAAE,UAAU,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,IAAI,CAAC;IAEX;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,EAAE,WAAW,GAAG,IAAI,CAAC;IAE9B;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,IAAI,YAAY,CAAC;IAEzB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;IAE7E;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI,CAAC;IAEpC;;;OAGG;IACH,eAAe,IAAI,IAAI,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,wBAAsB,aAAa,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,OAAO,CAAC,CA8GlF"}
package/dist/ts-libs.d.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  * TypeScript standard library fetcher and cache.
3
3
  *
4
4
  * Fetches TypeScript's lib.*.d.ts files from jsDelivr CDN and caches
5
- * them in IndexedDB for reuse. These files provide types for built-in
6
- * JavaScript APIs (Array, Number, String) and browser APIs (console, window, document).
5
+ * them in memory. These files provide types for built-in JavaScript APIs
6
+ * (Array, Number, String) and browser APIs (console, window, document).
7
7
  */
8
8
  /**
9
9
  * Default libs for browser environment with ES2020 target.
@@ -46,21 +46,15 @@ export declare function fetchLibFile(name: string): Promise<string>;
46
46
  */
47
47
  export declare function fetchAllLibs(libs: string[]): Promise<Map<string, string>>;
48
48
  /**
49
- * LibCache provides IndexedDB-backed caching for TypeScript lib files.
49
+ * LibCache provides in-memory caching for TypeScript lib files.
50
50
  *
51
51
  * Usage:
52
52
  * ```ts
53
- * const cache = await LibCache.create();
53
+ * const cache = new LibCache();
54
54
  * const libs = await cache.getOrFetch(getDefaultBrowserLibs());
55
55
  * ```
56
56
  */
57
57
  export declare class LibCache {
58
- private db;
59
- private constructor();
60
- /**
61
- * Create and initialize a LibCache instance.
62
- */
63
- static create(): Promise<LibCache>;
64
58
  /**
65
59
  * Get cached libs if available, otherwise fetch from CDN and cache.
66
60
  *
@@ -71,25 +65,18 @@ export declare class LibCache {
71
65
  /**
72
66
  * Get cached libs if available.
73
67
  */
74
- get(): Promise<Map<string, string> | null>;
68
+ get(): Map<string, string> | null;
75
69
  /**
76
70
  * Store libs in the cache.
77
71
  */
78
- set(libs: Map<string, string>): Promise<void>;
72
+ set(libs: Map<string, string>): void;
79
73
  /**
80
74
  * Clear all cached libs.
81
75
  */
82
- clear(): Promise<void>;
83
- /**
84
- * Close the database connection.
85
- */
86
- close(): void;
76
+ clear(): void;
87
77
  }
88
78
  /**
89
79
  * Convenience function to fetch and cache libs in one call.
90
- * Creates a temporary LibCache, fetches libs, and returns the result.
91
- *
92
- * For repeated use, prefer creating a LibCache instance directly.
93
80
  *
94
81
  * @param libs - Lib names to fetch (defaults to getDefaultBrowserLibs())
95
82
  * @returns Map of lib name to content
@@ -1 +1 @@
1
- {"version":3,"file":"ts-libs.d.ts","sourceRoot":"","sources":["../src/ts-libs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoBH;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAY5D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUhE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA6C/E;AA+CD;;;;;;;;GAQG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,EAAE,CAAc;IAExB,OAAO;IAIP;;OAEG;WACU,MAAM,IAAI,OAAO,CAAC,QAAQ,CAAC;IAKxC;;;;;OAKG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAwB9D;;OAEG;IACG,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAmBhD;;OAEG;IACG,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAcnD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,GAAE,MAAM,EAA4B,GACvC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAO9B"}
1
+ {"version":3,"file":"ts-libs.d.ts","sourceRoot":"","sources":["../src/ts-libs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAY5D;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUhE;AAED;;;;;;GAMG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA6C/E;AAQD;;;;;;;;GAQG;AACH,qBAAa,QAAQ;IACnB;;;;;OAKG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAuB9D;;OAEG;IACH,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAIjC;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAIpC;;OAEG;IACH,KAAK,IAAI,IAAI;CAGd;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,GAAE,MAAM,EAA4B,GACvC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAG9B"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sandlot",
3
- "version": "0.1.3",
4
- "description": "Browser-based TypeScript sandbox with esbuild bundling and type checking",
3
+ "version": "0.1.4",
4
+ "description": "TypeScript sandbox with esbuild bundling and type checking for browser and server",
5
5
  "author": "blindmansion",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -43,8 +43,17 @@
43
43
  "just-bash": "^2.6.0",
44
44
  "typescript": "^5.9.3"
45
45
  },
46
+ "peerDependencies": {
47
+ "esbuild": ">=0.20.0"
48
+ },
49
+ "peerDependenciesMeta": {
50
+ "esbuild": {
51
+ "optional": true
52
+ }
53
+ },
46
54
  "devDependencies": {
47
55
  "@types/bun": "latest",
48
- "@types/react": "^19"
56
+ "@types/react": "^19",
57
+ "esbuild": "^0.27.2"
49
58
  }
50
59
  }
package/src/bundler.ts CHANGED
@@ -3,29 +3,55 @@ import type * as EsbuildTypes from "esbuild-wasm";
3
3
  import { getPackageManifest, resolveToEsmUrl } from "./packages";
4
4
  import { getSharedModuleRuntimeCode, getSharedModuleExports } from "./shared-modules";
5
5
 
6
- // Lazily loaded esbuild module - loaded from CDN to avoid bundler issues
6
+ // Lazily loaded esbuild module
7
7
  let esbuild: typeof EsbuildTypes | null = null;
8
8
 
9
+ /**
10
+ * Detect if we're running in a server environment (Node.js, Bun, Deno)
11
+ * vs a browser environment.
12
+ */
13
+ function isServerEnvironment(): boolean {
14
+ // Check for Bun
15
+ if (typeof globalThis !== "undefined" && "Bun" in globalThis) {
16
+ return true;
17
+ }
18
+ // Check for Deno
19
+ if (typeof globalThis !== "undefined" && "Deno" in globalThis) {
20
+ return true;
21
+ }
22
+ // Check for Node.js (process.versions.node exists)
23
+ if (typeof process !== "undefined" && process.versions?.node) {
24
+ return true;
25
+ }
26
+ return false;
27
+ }
28
+
9
29
  async function getEsbuild(): Promise<typeof EsbuildTypes> {
10
30
  if (esbuild) return esbuild;
11
31
 
12
- // Load esbuild-wasm from esm.sh CDN to avoid bundler transformation issues
13
- // esm.sh provides proper ESM wrappers for npm packages
14
- const cdnUrl = `https://esm.sh/esbuild-wasm@${ESBUILD_VERSION}`;
15
-
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
- const mod: any = await import(/* @vite-ignore */ cdnUrl);
18
-
19
- // esm.sh typically provides both default and named exports
20
- esbuild = mod.default ?? mod;
21
-
22
- // Verify we have the initialize function
23
- if (typeof esbuild?.initialize !== 'function') {
24
- console.error('esbuild-wasm module structure:', mod);
25
- throw new Error('Failed to load esbuild-wasm: initialize function not found');
32
+ if (isServerEnvironment()) {
33
+ // In server environments, use native esbuild for better performance
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ const mod: any = await import("esbuild");
36
+ esbuild = mod.default ?? mod;
37
+ } else {
38
+ // In browser, load esbuild-wasm from esm.sh CDN
39
+ const cdnUrl = `https://esm.sh/esbuild-wasm@${ESBUILD_VERSION}`;
40
+
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ const mod: any = await import(/* @vite-ignore */ cdnUrl);
43
+
44
+ // esm.sh typically provides both default and named exports
45
+ esbuild = mod.default ?? mod;
46
+
47
+ // Verify we have the initialize function (only needed for wasm version)
48
+ if (typeof esbuild?.initialize !== "function") {
49
+ console.error("esbuild-wasm module structure:", mod);
50
+ throw new Error("Failed to load esbuild-wasm: initialize function not found");
51
+ }
26
52
  }
27
53
 
28
- return esbuild;
54
+ return esbuild!;
29
55
  }
30
56
 
31
57
  /**
@@ -166,8 +192,12 @@ function checkCrossOriginIsolation(): void {
166
192
  }
167
193
 
168
194
  /**
169
- * Initialize esbuild-wasm. Called automatically on first bundle.
195
+ * Initialize esbuild. Called automatically on first bundle.
170
196
  * Can be called explicitly to pre-warm.
197
+ *
198
+ * In browser environments, this loads and initializes esbuild-wasm.
199
+ * In server environments (Node.js, Bun, Deno), this loads native esbuild
200
+ * which doesn't require WASM initialization.
171
201
  */
172
202
  export async function initBundler(): Promise<void> {
173
203
  if (initialized) return;
@@ -177,13 +207,16 @@ export async function initBundler(): Promise<void> {
177
207
  return;
178
208
  }
179
209
 
180
- checkCrossOriginIsolation();
181
-
182
210
  initPromise = (async () => {
183
211
  const es = await getEsbuild();
184
- await es.initialize({
185
- wasmURL: getWasmUrl(),
186
- });
212
+
213
+ // Native esbuild doesn't need initialization, only esbuild-wasm does
214
+ if (!isServerEnvironment() && typeof es.initialize === "function") {
215
+ checkCrossOriginIsolation();
216
+ await es.initialize({
217
+ wasmURL: getWasmUrl(),
218
+ });
219
+ }
187
220
  })();
188
221
 
189
222
  await initPromise;
package/src/sandbox.ts CHANGED
@@ -8,9 +8,10 @@ import { installPackage, parseImportPath } from "./packages";
8
8
 
9
9
  /**
10
10
  * Options that can be passed through to the just-bash Bash constructor.
11
- * Excludes options that sandlot controls internally (fs, customCommands, files).
11
+ * Excludes options that sandlot controls internally (fs, customCommands, files, cwd).
12
+ * The working directory is always root (/).
12
13
  */
13
- export type SandboxBashOptions = Omit<BashOptions, 'fs' | 'customCommands' | 'files'>;
14
+ export type SandboxBashOptions = Omit<BashOptions, 'fs' | 'customCommands' | 'files' | 'cwd'>;
14
15
 
15
16
  /**
16
17
  * Options for creating a sandbox environment
@@ -101,14 +102,13 @@ export interface SandboxOptions {
101
102
  * Use this to configure environment variables, execution limits,
102
103
  * network access, logging, and other bash-level settings.
103
104
  *
104
- * Note: `fs`, `customCommands`, and `files` are controlled by sandlot
105
- * and cannot be overridden here.
105
+ * Note: `fs`, `customCommands`, `files`, and `cwd` are controlled by sandlot
106
+ * and cannot be overridden here. The working directory is always root (/).
106
107
  *
107
108
  * @example
108
109
  * ```ts
109
110
  * const sandbox = await createSandbox({
110
111
  * bashOptions: {
111
- * cwd: '/src',
112
112
  * env: { NODE_ENV: 'development' },
113
113
  * executionLimits: { maxCommandCount: 1000 },
114
114
  * },
@@ -371,8 +371,10 @@ export async function createSandbox(options: SandboxOptions = {}): Promise<Sandb
371
371
  const defaultCommands = createDefaultCommands(commandDeps);
372
372
 
373
373
  // Create bash environment with the custom filesystem
374
+ // Always start in root directory (/) for consistent behavior
374
375
  const bash = new Bash({
375
376
  ...bashOptions,
377
+ cwd: '/',
376
378
  fs,
377
379
  customCommands: [...defaultCommands, ...customCommands],
378
380
  });
package/src/ts-libs.ts CHANGED
@@ -2,8 +2,8 @@
2
2
  * TypeScript standard library fetcher and cache.
3
3
  *
4
4
  * Fetches TypeScript's lib.*.d.ts files from jsDelivr CDN and caches
5
- * them in IndexedDB for reuse. These files provide types for built-in
6
- * JavaScript APIs (Array, Number, String) and browser APIs (console, window, document).
5
+ * them in memory. These files provide types for built-in JavaScript APIs
6
+ * (Array, Number, String) and browser APIs (console, window, document).
7
7
  */
8
8
 
9
9
  /**
@@ -17,13 +17,6 @@ const TS_VERSION = "5.9.3";
17
17
  */
18
18
  const CDN_BASE = `https://cdn.jsdelivr.net/npm/typescript@${TS_VERSION}/lib`;
19
19
 
20
- /**
21
- * IndexedDB database name for lib cache
22
- */
23
- const DB_NAME = "ts-lib-cache";
24
- const DB_VERSION = 1;
25
- const STORE_NAME = "libs";
26
-
27
20
  /**
28
21
  * Default libs for browser environment with ES2020 target.
29
22
  * These provide types for console, DOM APIs, and modern JS features.
@@ -145,74 +138,21 @@ export async function fetchAllLibs(libs: string[]): Promise<Map<string, string>>
145
138
  }
146
139
 
147
140
  /**
148
- * Open the IndexedDB database for lib caching.
149
- */
150
- async function openDatabase(): Promise<IDBDatabase> {
151
- return new Promise((resolve, reject) => {
152
- const request = indexedDB.open(DB_NAME, DB_VERSION);
153
-
154
- request.onerror = () => reject(request.error);
155
- request.onsuccess = () => resolve(request.result);
156
-
157
- request.onupgradeneeded = (event) => {
158
- const db = (event.target as IDBOpenDBRequest).result;
159
- if (!db.objectStoreNames.contains(STORE_NAME)) {
160
- db.createObjectStore(STORE_NAME);
161
- }
162
- };
163
- });
164
- }
165
-
166
- /**
167
- * Promisify an IDBRequest
141
+ * In-memory cache for TypeScript lib files.
142
+ * Shared across all LibCache instances.
168
143
  */
169
- function promisifyRequest<T>(request: IDBRequest<T>): Promise<T> {
170
- return new Promise((resolve, reject) => {
171
- request.onsuccess = () => resolve(request.result);
172
- request.onerror = () => reject(request.error);
173
- });
174
- }
175
-
176
- /**
177
- * Cache key for the current TypeScript version
178
- */
179
- function getCacheKey(): string {
180
- return `libs-${TS_VERSION}`;
181
- }
144
+ let memoryCache: Map<string, string> | null = null;
182
145
 
183
146
  /**
184
- * Serializable format for storing libs in IndexedDB
185
- */
186
- interface CachedLibs {
187
- version: string;
188
- timestamp: number;
189
- libs: Record<string, string>;
190
- }
191
-
192
- /**
193
- * LibCache provides IndexedDB-backed caching for TypeScript lib files.
147
+ * LibCache provides in-memory caching for TypeScript lib files.
194
148
  *
195
149
  * Usage:
196
150
  * ```ts
197
- * const cache = await LibCache.create();
151
+ * const cache = new LibCache();
198
152
  * const libs = await cache.getOrFetch(getDefaultBrowserLibs());
199
153
  * ```
200
154
  */
201
155
  export class LibCache {
202
- private db: IDBDatabase;
203
-
204
- private constructor(db: IDBDatabase) {
205
- this.db = db;
206
- }
207
-
208
- /**
209
- * Create and initialize a LibCache instance.
210
- */
211
- static async create(): Promise<LibCache> {
212
- const db = await openDatabase();
213
- return new LibCache(db);
214
- }
215
-
216
156
  /**
217
157
  * Get cached libs if available, otherwise fetch from CDN and cache.
218
158
  *
@@ -221,12 +161,11 @@ export class LibCache {
221
161
  */
222
162
  async getOrFetch(libs: string[]): Promise<Map<string, string>> {
223
163
  // Try to get from cache first
224
- const cached = await this.get();
225
- if (cached) {
164
+ if (memoryCache) {
226
165
  // Verify all requested libs are in cache
227
- const missing = libs.filter((lib) => !cached.has(lib));
166
+ const missing = libs.filter((lib) => !memoryCache!.has(lib));
228
167
  if (missing.length === 0) {
229
- return cached;
168
+ return memoryCache;
230
169
  }
231
170
  // Some libs missing, fetch all and update cache
232
171
  console.log(`Cache missing libs: ${missing.join(", ")}, fetching all...`);
@@ -238,7 +177,7 @@ export class LibCache {
238
177
  console.log(`Fetched ${fetched.size} lib files`);
239
178
 
240
179
  // Cache the results
241
- await this.set(fetched);
180
+ memoryCache = fetched;
242
181
 
243
182
  return fetched;
244
183
  }
@@ -246,64 +185,27 @@ export class LibCache {
246
185
  /**
247
186
  * Get cached libs if available.
248
187
  */
249
- async get(): Promise<Map<string, string> | null> {
250
- const tx = this.db.transaction(STORE_NAME, "readonly");
251
- const store = tx.objectStore(STORE_NAME);
252
- const key = getCacheKey();
253
-
254
- const cached = await promisifyRequest<CachedLibs | undefined>(store.get(key));
255
- if (!cached) {
256
- return null;
257
- }
258
-
259
- // Verify version matches
260
- if (cached.version !== TS_VERSION) {
261
- console.log(`Cache version mismatch: ${cached.version} vs ${TS_VERSION}`);
262
- return null;
263
- }
264
-
265
- return new Map(Object.entries(cached.libs));
188
+ get(): Map<string, string> | null {
189
+ return memoryCache;
266
190
  }
267
191
 
268
192
  /**
269
193
  * Store libs in the cache.
270
194
  */
271
- async set(libs: Map<string, string>): Promise<void> {
272
- const tx = this.db.transaction(STORE_NAME, "readwrite");
273
- const store = tx.objectStore(STORE_NAME);
274
- const key = getCacheKey();
275
-
276
- const cached: CachedLibs = {
277
- version: TS_VERSION,
278
- timestamp: Date.now(),
279
- libs: Object.fromEntries(libs),
280
- };
281
-
282
- await promisifyRequest(store.put(cached, key));
195
+ set(libs: Map<string, string>): void {
196
+ memoryCache = libs;
283
197
  }
284
198
 
285
199
  /**
286
200
  * Clear all cached libs.
287
201
  */
288
- async clear(): Promise<void> {
289
- const tx = this.db.transaction(STORE_NAME, "readwrite");
290
- const store = tx.objectStore(STORE_NAME);
291
- await promisifyRequest(store.clear());
292
- }
293
-
294
- /**
295
- * Close the database connection.
296
- */
297
- close(): void {
298
- this.db.close();
202
+ clear(): void {
203
+ memoryCache = null;
299
204
  }
300
205
  }
301
206
 
302
207
  /**
303
208
  * Convenience function to fetch and cache libs in one call.
304
- * Creates a temporary LibCache, fetches libs, and returns the result.
305
- *
306
- * For repeated use, prefer creating a LibCache instance directly.
307
209
  *
308
210
  * @param libs - Lib names to fetch (defaults to getDefaultBrowserLibs())
309
211
  * @returns Map of lib name to content
@@ -311,10 +213,6 @@ export class LibCache {
311
213
  export async function fetchAndCacheLibs(
312
214
  libs: string[] = getDefaultBrowserLibs()
313
215
  ): Promise<Map<string, string>> {
314
- const cache = await LibCache.create();
315
- try {
316
- return await cache.getOrFetch(libs);
317
- } finally {
318
- cache.close();
319
- }
216
+ const cache = new LibCache();
217
+ return cache.getOrFetch(libs);
320
218
  }