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
@@ -1,166 +0,0 @@
1
- /**
2
- * Shared resources for sandbox environments.
3
- *
4
- * Provides centralized management of expensive shared resources:
5
- * - TypeScript lib files (~5MB) - loaded once, shared across all sandboxes
6
- * - esbuild WASM (~10MB) - singleton bundler initialization
7
- * - Types cache - avoids redundant network fetches when multiple sandboxes
8
- * install the same packages
9
- */
10
-
11
- import { fetchAndCacheLibs, getDefaultBrowserLibs } from "./ts-libs";
12
- import { initBundler } from "./bundler";
13
- import { InMemoryTypesCache, type TypesCache } from "./packages";
14
-
15
- // Re-export for consumers
16
- export type { TypesCache } from "./packages";
17
- export { InMemoryTypesCache } from "./packages";
18
-
19
- /**
20
- * Shared resources that can be reused across multiple sandboxes
21
- */
22
- export interface SharedResources {
23
- /**
24
- * Pre-loaded TypeScript lib files for type checking
25
- */
26
- libFiles: Map<string, string>;
27
-
28
- /**
29
- * Promise that resolves when the bundler is ready
30
- */
31
- bundlerReady: Promise<void>;
32
-
33
- /**
34
- * Cache for package type definitions.
35
- * Avoids redundant network fetches when multiple sandboxes
36
- * install the same packages.
37
- */
38
- typesCache: TypesCache;
39
- }
40
-
41
- /**
42
- * Options for creating shared resources
43
- */
44
- export interface SharedResourcesOptions {
45
- /**
46
- * TypeScript libs to load. Defaults to browser libs (ES2020 + DOM).
47
- */
48
- libs?: string[];
49
-
50
- /**
51
- * If true, skip fetching TypeScript libs.
52
- * libFiles will be an empty Map.
53
- * Default: false
54
- */
55
- skipLibs?: boolean;
56
-
57
- /**
58
- * If true, skip pre-initializing the bundler.
59
- * bundlerReady will resolve immediately.
60
- * Default: false
61
- */
62
- skipBundler?: boolean;
63
- }
64
-
65
- /**
66
- * Create a new SharedResources instance.
67
- *
68
- * Use this when you want to manage resource lifecycle explicitly,
69
- * or when you need custom libs configuration.
70
- *
71
- * @example
72
- * ```ts
73
- * // Create resources with custom libs
74
- * const resources = await createSharedResources({
75
- * libs: ['es2022', 'dom', 'webworker'],
76
- * });
77
- *
78
- * // Pass to sandbox creation
79
- * const sandbox = await createSandbox({
80
- * resources,
81
- * fsOptions: { ... },
82
- * });
83
- * ```
84
- */
85
- export async function createSharedResources(
86
- options: SharedResourcesOptions = {}
87
- ): Promise<SharedResources> {
88
- const { libs = getDefaultBrowserLibs(), skipLibs = false, skipBundler = false } = options;
89
-
90
- // Start both in parallel
91
- const libsPromise = skipLibs
92
- ? Promise.resolve(new Map<string, string>())
93
- : fetchAndCacheLibs(libs);
94
-
95
- const bundlerPromise = skipBundler ? Promise.resolve() : initBundler();
96
-
97
- // Create types cache (synchronous, just an in-memory Map)
98
- const typesCache = new InMemoryTypesCache();
99
-
100
- // Wait for async initialization
101
- const [libFiles] = await Promise.all([libsPromise, bundlerPromise]);
102
-
103
- return {
104
- libFiles,
105
- bundlerReady: Promise.resolve(), // Already initialized
106
- typesCache,
107
- };
108
- }
109
-
110
- // ============ Module-level Singleton ============
111
-
112
- /**
113
- * Module-level singleton for default shared resources.
114
- * Used by createSandbox() when no resources are provided.
115
- */
116
- let defaultResourcesInstance: SharedResources | null = null;
117
- let defaultResourcesPromise: Promise<SharedResources> | null = null;
118
-
119
- /**
120
- * Get the default shared resources singleton.
121
- *
122
- * Loads resources once and returns the same instance for all callers.
123
- * This is the recommended way to get shared resources for most use cases.
124
- *
125
- * @example
126
- * ```ts
127
- * // Get default resources (creates on first call)
128
- * const resources = await getDefaultResources();
129
- *
130
- * // Create multiple sandboxes sharing the same resources
131
- * const sandbox1 = await createSandbox({ resources, ... });
132
- * const sandbox2 = await createSandbox({ resources, ... });
133
- * ```
134
- */
135
- export async function getDefaultResources(): Promise<SharedResources> {
136
- if (defaultResourcesInstance) {
137
- return defaultResourcesInstance;
138
- }
139
-
140
- if (!defaultResourcesPromise) {
141
- defaultResourcesPromise = createSharedResources().then((resources) => {
142
- defaultResourcesInstance = resources;
143
- return resources;
144
- });
145
- }
146
-
147
- return defaultResourcesPromise;
148
- }
149
-
150
- /**
151
- * Clear the default resources singleton (for testing).
152
- *
153
- * Note: This doesn't unload the bundler WASM - that stays in memory
154
- * until page reload. This only clears the cached lib files reference.
155
- */
156
- export function clearDefaultResources(): void {
157
- defaultResourcesInstance = null;
158
- defaultResourcesPromise = null;
159
- }
160
-
161
- /**
162
- * Check if the default resources have been initialized.
163
- */
164
- export function hasDefaultResources(): boolean {
165
- return defaultResourcesInstance !== null;
166
- }
package/src/ts-libs.ts DELETED
@@ -1,320 +0,0 @@
1
- /**
2
- * TypeScript standard library fetcher and cache.
3
- *
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).
7
- */
8
-
9
- /**
10
- * TypeScript version to fetch libs for.
11
- * MUST match the version in package.json dependencies.
12
- */
13
- const TS_VERSION = "5.9.3";
14
-
15
- /**
16
- * CDN base URL for TypeScript lib files
17
- */
18
- const CDN_BASE = `https://cdn.jsdelivr.net/npm/typescript@${TS_VERSION}/lib`;
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
- /**
28
- * Default libs for browser environment with ES2020 target.
29
- * These provide types for console, DOM APIs, and modern JS features.
30
- */
31
- export function getDefaultBrowserLibs(): string[] {
32
- return ["es2020", "dom", "dom.iterable"];
33
- }
34
-
35
- /**
36
- * Parse `/// <reference lib="..." />` directives from a lib file.
37
- * These directives indicate dependencies on other lib files.
38
- *
39
- * @param content - The content of a lib.*.d.ts file
40
- * @returns Array of lib names referenced (without "lib." prefix or ".d.ts" suffix)
41
- */
42
- export function parseLibReferences(content: string): string[] {
43
- const refs: string[] = [];
44
- const regex = /\/\/\/\s*<reference\s+lib="([^"]+)"\s*\/>/g;
45
- let match;
46
-
47
- while ((match = regex.exec(content)) !== null) {
48
- if (match[1]) {
49
- refs.push(match[1]);
50
- }
51
- }
52
-
53
- return refs;
54
- }
55
-
56
- /**
57
- * Convert a lib name to its filename.
58
- * e.g., "es2020" -> "lib.es2020.d.ts"
59
- */
60
- export function libNameToFileName(name: string): string {
61
- return `lib.${name}.d.ts`;
62
- }
63
-
64
- /**
65
- * Extract lib name from a file path.
66
- * e.g., "/node_modules/typescript/lib/lib.es2020.d.ts" -> "es2020"
67
- * "lib.dom.d.ts" -> "dom"
68
- */
69
- export function extractLibName(filePath: string): string | null {
70
- const match = filePath.match(/lib\.([^/]+)\.d\.ts$/);
71
- return match?.[1] ?? null;
72
- }
73
-
74
- /**
75
- * Fetch a single lib file from the CDN.
76
- *
77
- * @param name - The lib name (e.g., "es2020", "dom")
78
- * @returns The content of the lib file
79
- * @throws Error if the fetch fails
80
- */
81
- export async function fetchLibFile(name: string): Promise<string> {
82
- const fileName = libNameToFileName(name);
83
- const url = `${CDN_BASE}/${fileName}`;
84
-
85
- const response = await fetch(url);
86
- if (!response.ok) {
87
- throw new Error(`Failed to fetch ${url}: ${response.status} ${response.statusText}`);
88
- }
89
-
90
- return response.text();
91
- }
92
-
93
- /**
94
- * Recursively fetch all lib files needed for the given libs.
95
- * Parses `/// <reference lib="..." />` directives and fetches dependencies.
96
- *
97
- * @param libs - Initial lib names to fetch (e.g., ["es2020", "dom"])
98
- * @returns Map of lib name to content
99
- */
100
- export async function fetchAllLibs(libs: string[]): Promise<Map<string, string>> {
101
- const result = new Map<string, string>();
102
- const pending = new Set<string>(libs);
103
- const fetched = new Set<string>();
104
-
105
- while (pending.size > 0) {
106
- // Get next batch of libs to fetch
107
- const batch = Array.from(pending);
108
- pending.clear();
109
-
110
- // Fetch all in parallel
111
- const results = await Promise.all(
112
- batch.map(async (name) => {
113
- if (fetched.has(name)) {
114
- return { name, content: null };
115
- }
116
- fetched.add(name);
117
-
118
- try {
119
- const content = await fetchLibFile(name);
120
- return { name, content };
121
- } catch (err) {
122
- console.warn(`Failed to fetch lib.${name}.d.ts:`, err);
123
- return { name, content: null };
124
- }
125
- })
126
- );
127
-
128
- // Process results and find new dependencies
129
- for (const { name, content } of results) {
130
- if (content === null) continue;
131
-
132
- result.set(name, content);
133
-
134
- // Parse references and add unfetched ones to pending
135
- const refs = parseLibReferences(content);
136
- for (const ref of refs) {
137
- if (!fetched.has(ref) && !pending.has(ref)) {
138
- pending.add(ref);
139
- }
140
- }
141
- }
142
- }
143
-
144
- return result;
145
- }
146
-
147
- /**
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
168
- */
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
- }
182
-
183
- /**
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.
194
- *
195
- * Usage:
196
- * ```ts
197
- * const cache = await LibCache.create();
198
- * const libs = await cache.getOrFetch(getDefaultBrowserLibs());
199
- * ```
200
- */
201
- 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
- /**
217
- * Get cached libs if available, otherwise fetch from CDN and cache.
218
- *
219
- * @param libs - Lib names to fetch (e.g., ["es2020", "dom"])
220
- * @returns Map of lib name to content (includes all transitive dependencies)
221
- */
222
- async getOrFetch(libs: string[]): Promise<Map<string, string>> {
223
- // Try to get from cache first
224
- const cached = await this.get();
225
- if (cached) {
226
- // Verify all requested libs are in cache
227
- const missing = libs.filter((lib) => !cached.has(lib));
228
- if (missing.length === 0) {
229
- return cached;
230
- }
231
- // Some libs missing, fetch all and update cache
232
- console.log(`Cache missing libs: ${missing.join(", ")}, fetching all...`);
233
- }
234
-
235
- // Fetch from CDN
236
- console.log(`Fetching TypeScript libs from CDN: ${libs.join(", ")}...`);
237
- const fetched = await fetchAllLibs(libs);
238
- console.log(`Fetched ${fetched.size} lib files`);
239
-
240
- // Cache the results
241
- await this.set(fetched);
242
-
243
- return fetched;
244
- }
245
-
246
- /**
247
- * Get cached libs if available.
248
- */
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));
266
- }
267
-
268
- /**
269
- * Store libs in the cache.
270
- */
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));
283
- }
284
-
285
- /**
286
- * Clear all cached libs.
287
- */
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();
299
- }
300
- }
301
-
302
- /**
303
- * 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
- *
308
- * @param libs - Lib names to fetch (defaults to getDefaultBrowserLibs())
309
- * @returns Map of lib name to content
310
- */
311
- export async function fetchAndCacheLibs(
312
- libs: string[] = getDefaultBrowserLibs()
313
- ): Promise<Map<string, string>> {
314
- const cache = await LibCache.create();
315
- try {
316
- return await cache.getOrFetch(libs);
317
- } finally {
318
- cache.close();
319
- }
320
- }