sandlot 0.1.1 → 0.1.3
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 +145 -518
- package/dist/build-emitter.d.ts +47 -0
- package/dist/build-emitter.d.ts.map +1 -0
- package/dist/builder.d.ts +370 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/bundler.d.ts +3 -3
- package/dist/bundler.d.ts.map +1 -1
- package/dist/commands/compile.d.ts +13 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/index.d.ts +17 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/packages.d.ts +17 -0
- package/dist/commands/packages.d.ts.map +1 -0
- package/dist/commands/run.d.ts +40 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/types.d.ts +141 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/fs.d.ts +60 -42
- package/dist/fs.d.ts.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +304 -491
- package/dist/internal.d.ts +5 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +174 -95
- package/dist/runner.d.ts +314 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/sandbox-manager.d.ts +45 -33
- package/dist/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox.d.ts +144 -70
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/shared-modules.d.ts +22 -3
- package/dist/shared-modules.d.ts.map +1 -1
- package/dist/shared-resources.d.ts +0 -3
- package/dist/shared-resources.d.ts.map +1 -1
- package/dist/typechecker.d.ts +1 -1
- package/package.json +3 -17
- package/src/build-emitter.ts +64 -0
- package/src/builder.ts +498 -0
- package/src/bundler.ts +86 -57
- package/src/commands/compile.ts +236 -0
- package/src/commands/index.ts +51 -0
- package/src/commands/packages.ts +154 -0
- package/src/commands/run.ts +245 -0
- package/src/commands/types.ts +172 -0
- package/src/fs.ts +90 -216
- package/src/index.ts +34 -12
- package/src/internal.ts +5 -2
- package/src/sandbox.ts +214 -220
- package/src/shared-modules.ts +74 -4
- package/src/shared-resources.ts +0 -3
- package/src/ts-libs.ts +1 -1
- package/src/typechecker.ts +1 -1
- package/dist/react.d.ts +0 -159
- package/dist/react.d.ts.map +0 -1
- package/dist/react.js +0 -149
- package/src/commands.ts +0 -733
- package/src/react.tsx +0 -331
- package/src/sandbox-manager.ts +0 -490
package/dist/index.js
CHANGED
|
@@ -7,47 +7,14 @@ var DEFAULT_DIR_MODE = 493;
|
|
|
7
7
|
var DEFAULT_SYMLINK_MODE = 511;
|
|
8
8
|
var DEFAULT_MAX_SIZE_BYTES = 50 * 1024 * 1024;
|
|
9
9
|
|
|
10
|
-
class
|
|
10
|
+
class Filesystem {
|
|
11
11
|
entries;
|
|
12
|
-
db = null;
|
|
13
|
-
dbName;
|
|
14
12
|
maxSizeBytes;
|
|
15
|
-
|
|
16
|
-
constructor(entries, db, dbName, maxSizeBytes) {
|
|
13
|
+
constructor(entries, maxSizeBytes) {
|
|
17
14
|
this.entries = entries;
|
|
18
|
-
this.db = db;
|
|
19
|
-
this.dbName = dbName;
|
|
20
15
|
this.maxSizeBytes = maxSizeBytes;
|
|
21
16
|
}
|
|
22
|
-
static
|
|
23
|
-
const dbName = options.dbName ?? "sandlot-fs";
|
|
24
|
-
const maxSizeBytes = options.maxSizeBytes ?? DEFAULT_MAX_SIZE_BYTES;
|
|
25
|
-
const db = await IndexedDbFs.openDatabase(dbName);
|
|
26
|
-
const entries = await IndexedDbFs.loadEntries(db);
|
|
27
|
-
if (entries.size === 0) {
|
|
28
|
-
entries.set("/", {
|
|
29
|
-
type: "directory",
|
|
30
|
-
mode: DEFAULT_DIR_MODE,
|
|
31
|
-
mtime: new Date
|
|
32
|
-
});
|
|
33
|
-
if (options.initialFiles) {
|
|
34
|
-
for (const [path, value] of Object.entries(options.initialFiles)) {
|
|
35
|
-
const normalizedPath = IndexedDbFs.normalizePath(path);
|
|
36
|
-
const init = IndexedDbFs.parseFileInit(value);
|
|
37
|
-
IndexedDbFs.ensureParentDirs(entries, normalizedPath);
|
|
38
|
-
entries.set(normalizedPath, {
|
|
39
|
-
type: "file",
|
|
40
|
-
content: init.content,
|
|
41
|
-
mode: init.mode ?? DEFAULT_FILE_MODE,
|
|
42
|
-
mtime: init.mtime ?? new Date
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const fs = new IndexedDbFs(entries, db, dbName, maxSizeBytes);
|
|
48
|
-
return fs;
|
|
49
|
-
}
|
|
50
|
-
static createInMemory(options = {}) {
|
|
17
|
+
static create(options = {}) {
|
|
51
18
|
const maxSizeBytes = options.maxSizeBytes ?? DEFAULT_MAX_SIZE_BYTES;
|
|
52
19
|
const entries = new Map;
|
|
53
20
|
entries.set("/", {
|
|
@@ -57,9 +24,9 @@ class IndexedDbFs {
|
|
|
57
24
|
});
|
|
58
25
|
if (options.initialFiles) {
|
|
59
26
|
for (const [path, value] of Object.entries(options.initialFiles)) {
|
|
60
|
-
const normalizedPath =
|
|
61
|
-
const init =
|
|
62
|
-
|
|
27
|
+
const normalizedPath = Filesystem.normalizePath(path);
|
|
28
|
+
const init = Filesystem.parseFileInit(value);
|
|
29
|
+
Filesystem.ensureParentDirs(entries, normalizedPath);
|
|
63
30
|
entries.set(normalizedPath, {
|
|
64
31
|
type: "file",
|
|
65
32
|
content: init.content,
|
|
@@ -68,31 +35,21 @@ class IndexedDbFs {
|
|
|
68
35
|
});
|
|
69
36
|
}
|
|
70
37
|
}
|
|
71
|
-
return new
|
|
38
|
+
return new Filesystem(entries, maxSizeBytes);
|
|
72
39
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
const tx = this.db.transaction("entries", "readwrite");
|
|
78
|
-
const store = tx.objectStore("entries");
|
|
79
|
-
await this.promisifyRequest(store.clear());
|
|
40
|
+
getFiles() {
|
|
41
|
+
const files = {};
|
|
80
42
|
for (const [path, entry] of this.entries) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
return;
|
|
43
|
+
if (entry.type === "file") {
|
|
44
|
+
if (typeof entry.content === "string") {
|
|
45
|
+
files[path] = entry.content;
|
|
46
|
+
} else {
|
|
47
|
+
const base64 = this.encodeBase64(entry.content);
|
|
48
|
+
files[path] = `data:application/octet-stream;base64,${base64}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
90
51
|
}
|
|
91
|
-
|
|
92
|
-
this.dirty = false;
|
|
93
|
-
}
|
|
94
|
-
isDirty() {
|
|
95
|
-
return this.dirty;
|
|
52
|
+
return files;
|
|
96
53
|
}
|
|
97
54
|
getSize() {
|
|
98
55
|
let size = 0;
|
|
@@ -108,12 +65,6 @@ class IndexedDbFs {
|
|
|
108
65
|
}
|
|
109
66
|
return size;
|
|
110
67
|
}
|
|
111
|
-
close() {
|
|
112
|
-
if (this.db) {
|
|
113
|
-
this.db.close();
|
|
114
|
-
this.db = null;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
68
|
async readFile(path, options) {
|
|
118
69
|
const normalizedPath = this.normalizePath(path);
|
|
119
70
|
const entry = this.resolveSymlinks(normalizedPath);
|
|
@@ -145,7 +96,7 @@ class IndexedDbFs {
|
|
|
145
96
|
}
|
|
146
97
|
return new TextEncoder().encode(content);
|
|
147
98
|
}
|
|
148
|
-
async writeFile(path, content,
|
|
99
|
+
async writeFile(path, content, _options) {
|
|
149
100
|
const normalizedPath = this.normalizePath(path);
|
|
150
101
|
this.checkSizeLimit(content);
|
|
151
102
|
this.ensureParentDirs(normalizedPath);
|
|
@@ -159,7 +110,6 @@ class IndexedDbFs {
|
|
|
159
110
|
mode: existing?.mode ?? DEFAULT_FILE_MODE,
|
|
160
111
|
mtime: new Date
|
|
161
112
|
});
|
|
162
|
-
this.dirty = true;
|
|
163
113
|
}
|
|
164
114
|
async appendFile(path, content, options) {
|
|
165
115
|
const normalizedPath = this.normalizePath(path);
|
|
@@ -211,7 +161,6 @@ class IndexedDbFs {
|
|
|
211
161
|
mode: DEFAULT_DIR_MODE,
|
|
212
162
|
mtime: new Date
|
|
213
163
|
});
|
|
214
|
-
this.dirty = true;
|
|
215
164
|
}
|
|
216
165
|
async readdir(path) {
|
|
217
166
|
const normalizedPath = this.normalizePath(path);
|
|
@@ -287,7 +236,6 @@ class IndexedDbFs {
|
|
|
287
236
|
}
|
|
288
237
|
}
|
|
289
238
|
this.entries.delete(normalizedPath);
|
|
290
|
-
this.dirty = true;
|
|
291
239
|
}
|
|
292
240
|
async cp(src, dest, options) {
|
|
293
241
|
const srcPath = this.normalizePath(src);
|
|
@@ -314,7 +262,6 @@ class IndexedDbFs {
|
|
|
314
262
|
this.ensureParentDirs(destPath);
|
|
315
263
|
this.entries.set(destPath, this.cloneEntry(entry));
|
|
316
264
|
}
|
|
317
|
-
this.dirty = true;
|
|
318
265
|
}
|
|
319
266
|
async mv(src, dest) {
|
|
320
267
|
const srcPath = this.normalizePath(src);
|
|
@@ -341,7 +288,6 @@ class IndexedDbFs {
|
|
|
341
288
|
this.entries.delete(srcPath);
|
|
342
289
|
this.entries.set(destPath, entry);
|
|
343
290
|
}
|
|
344
|
-
this.dirty = true;
|
|
345
291
|
}
|
|
346
292
|
resolvePath(base, path) {
|
|
347
293
|
if (path.startsWith("/")) {
|
|
@@ -371,7 +317,6 @@ class IndexedDbFs {
|
|
|
371
317
|
}
|
|
372
318
|
entry.mode = mode;
|
|
373
319
|
entry.mtime = new Date;
|
|
374
|
-
this.dirty = true;
|
|
375
320
|
}
|
|
376
321
|
async symlink(target, linkPath) {
|
|
377
322
|
const normalizedLinkPath = this.normalizePath(linkPath);
|
|
@@ -385,7 +330,6 @@ class IndexedDbFs {
|
|
|
385
330
|
mode: DEFAULT_SYMLINK_MODE,
|
|
386
331
|
mtime: new Date
|
|
387
332
|
});
|
|
388
|
-
this.dirty = true;
|
|
389
333
|
}
|
|
390
334
|
async link(existingPath, newPath) {
|
|
391
335
|
const srcPath = this.normalizePath(existingPath);
|
|
@@ -407,7 +351,6 @@ class IndexedDbFs {
|
|
|
407
351
|
mode: entry.mode,
|
|
408
352
|
mtime: new Date
|
|
409
353
|
});
|
|
410
|
-
this.dirty = true;
|
|
411
354
|
}
|
|
412
355
|
async readlink(path) {
|
|
413
356
|
const normalizedPath = this.normalizePath(path);
|
|
@@ -453,10 +396,9 @@ class IndexedDbFs {
|
|
|
453
396
|
throw new Error(`ENOENT: no such file or directory, utimes '${path}'`);
|
|
454
397
|
}
|
|
455
398
|
entry.mtime = mtime;
|
|
456
|
-
this.dirty = true;
|
|
457
399
|
}
|
|
458
400
|
normalizePath(path) {
|
|
459
|
-
return
|
|
401
|
+
return Filesystem.normalizePath(path);
|
|
460
402
|
}
|
|
461
403
|
static normalizePath(path) {
|
|
462
404
|
if (!path || path === ".")
|
|
@@ -484,8 +426,7 @@ class IndexedDbFs {
|
|
|
484
426
|
return lastSlash === 0 ? "/" : path.slice(0, lastSlash);
|
|
485
427
|
}
|
|
486
428
|
ensureParentDirs(path) {
|
|
487
|
-
|
|
488
|
-
this.dirty = true;
|
|
429
|
+
Filesystem.ensureParentDirs(this.entries, path);
|
|
489
430
|
}
|
|
490
431
|
static ensureParentDirs(entries, path) {
|
|
491
432
|
const parts = path.split("/").filter(Boolean);
|
|
@@ -562,17 +503,20 @@ class IndexedDbFs {
|
|
|
562
503
|
return new TextDecoder("utf-8").decode(buffer);
|
|
563
504
|
}
|
|
564
505
|
if (encoding === "base64") {
|
|
565
|
-
|
|
566
|
-
for (let i = 0;i < buffer.byteLength; i++) {
|
|
567
|
-
binary += String.fromCharCode(buffer[i]);
|
|
568
|
-
}
|
|
569
|
-
return btoa(binary);
|
|
506
|
+
return this.encodeBase64(buffer);
|
|
570
507
|
}
|
|
571
508
|
if (encoding === "hex") {
|
|
572
509
|
return Array.from(buffer).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
573
510
|
}
|
|
574
511
|
return new TextDecoder("utf-8").decode(buffer);
|
|
575
512
|
}
|
|
513
|
+
encodeBase64(buffer) {
|
|
514
|
+
let binary = "";
|
|
515
|
+
for (let i = 0;i < buffer.byteLength; i++) {
|
|
516
|
+
binary += String.fromCharCode(buffer[i]);
|
|
517
|
+
}
|
|
518
|
+
return btoa(binary);
|
|
519
|
+
}
|
|
576
520
|
concatBuffers(a, b) {
|
|
577
521
|
const result = new Uint8Array(a.byteLength + b.byteLength);
|
|
578
522
|
result.set(a, 0);
|
|
@@ -585,82 +529,11 @@ class IndexedDbFs {
|
|
|
585
529
|
}
|
|
586
530
|
return value;
|
|
587
531
|
}
|
|
588
|
-
static openDatabase(dbName) {
|
|
589
|
-
return new Promise((resolve, reject) => {
|
|
590
|
-
const request = indexedDB.open(dbName, 1);
|
|
591
|
-
request.onerror = () => reject(request.error);
|
|
592
|
-
request.onsuccess = () => resolve(request.result);
|
|
593
|
-
request.onupgradeneeded = (event) => {
|
|
594
|
-
const db = event.target.result;
|
|
595
|
-
if (!db.objectStoreNames.contains("entries")) {
|
|
596
|
-
db.createObjectStore("entries", { keyPath: "path" });
|
|
597
|
-
}
|
|
598
|
-
};
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
static async loadEntries(db) {
|
|
602
|
-
const tx = db.transaction("entries", "readonly");
|
|
603
|
-
const store = tx.objectStore("entries");
|
|
604
|
-
return new Promise((resolve, reject) => {
|
|
605
|
-
const request = store.getAll();
|
|
606
|
-
request.onerror = () => reject(request.error);
|
|
607
|
-
request.onsuccess = () => {
|
|
608
|
-
const entries = new Map;
|
|
609
|
-
for (const record of request.result) {
|
|
610
|
-
entries.set(record.path, IndexedDbFs.deserializeEntry(record.entry));
|
|
611
|
-
}
|
|
612
|
-
resolve(entries);
|
|
613
|
-
};
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
serializeEntry(entry) {
|
|
617
|
-
if (entry.type === "file" && entry.content instanceof Uint8Array) {
|
|
618
|
-
return {
|
|
619
|
-
...entry,
|
|
620
|
-
content: Array.from(entry.content),
|
|
621
|
-
contentType: "uint8array",
|
|
622
|
-
mtime: entry.mtime.toISOString()
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
return {
|
|
626
|
-
...entry,
|
|
627
|
-
mtime: entry.mtime.toISOString()
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
static deserializeEntry(data) {
|
|
631
|
-
const mtime = new Date(data.mtime);
|
|
632
|
-
if (data.type === "file") {
|
|
633
|
-
let content = data.content;
|
|
634
|
-
if (data.contentType === "uint8array" && Array.isArray(content)) {
|
|
635
|
-
content = new Uint8Array(content);
|
|
636
|
-
}
|
|
637
|
-
return { type: "file", content, mode: data.mode, mtime };
|
|
638
|
-
}
|
|
639
|
-
if (data.type === "symlink") {
|
|
640
|
-
return { type: "symlink", target: data.target, mode: data.mode, mtime };
|
|
641
|
-
}
|
|
642
|
-
return { type: "directory", mode: data.mode, mtime };
|
|
643
|
-
}
|
|
644
|
-
promisifyRequest(request) {
|
|
645
|
-
return new Promise((resolve, reject) => {
|
|
646
|
-
request.onerror = () => reject(request.error);
|
|
647
|
-
request.onsuccess = () => resolve(request.result);
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
promisifyTransaction(tx) {
|
|
651
|
-
return new Promise((resolve, reject) => {
|
|
652
|
-
tx.onerror = () => reject(tx.error);
|
|
653
|
-
tx.oncomplete = () => resolve();
|
|
654
|
-
});
|
|
655
|
-
}
|
|
656
532
|
}
|
|
657
|
-
function
|
|
658
|
-
return
|
|
533
|
+
function createFilesystem(options) {
|
|
534
|
+
return Filesystem.create(options);
|
|
659
535
|
}
|
|
660
536
|
|
|
661
|
-
// src/bundler.ts
|
|
662
|
-
import * as esbuild from "esbuild-wasm";
|
|
663
|
-
|
|
664
537
|
// src/packages.ts
|
|
665
538
|
var ESM_CDN_BASE = "https://esm.sh";
|
|
666
539
|
var KNOWN_SUBPATHS = {
|
|
@@ -1125,16 +998,18 @@ var GLOBAL_KEY = "__sandlot_shared_modules__";
|
|
|
1125
998
|
|
|
1126
999
|
class SharedModuleRegistry {
|
|
1127
1000
|
modules = new Map;
|
|
1001
|
+
exportNames = new Map;
|
|
1128
1002
|
constructor() {
|
|
1129
1003
|
globalThis[GLOBAL_KEY] = this;
|
|
1130
1004
|
}
|
|
1131
1005
|
register(moduleId, module) {
|
|
1132
1006
|
this.modules.set(moduleId, module);
|
|
1007
|
+
this.exportNames.set(moduleId, introspectExports(module));
|
|
1133
1008
|
return this;
|
|
1134
1009
|
}
|
|
1135
1010
|
registerAll(modules) {
|
|
1136
1011
|
for (const [id, mod] of Object.entries(modules)) {
|
|
1137
|
-
this.
|
|
1012
|
+
this.register(id, mod);
|
|
1138
1013
|
}
|
|
1139
1014
|
return this;
|
|
1140
1015
|
}
|
|
@@ -1155,13 +1030,44 @@ class SharedModuleRegistry {
|
|
|
1155
1030
|
list() {
|
|
1156
1031
|
return [...this.modules.keys()];
|
|
1157
1032
|
}
|
|
1033
|
+
getExportNames(moduleId) {
|
|
1034
|
+
return this.exportNames.get(moduleId) ?? [];
|
|
1035
|
+
}
|
|
1158
1036
|
clear() {
|
|
1159
1037
|
this.modules.clear();
|
|
1038
|
+
this.exportNames.clear();
|
|
1160
1039
|
}
|
|
1161
1040
|
get size() {
|
|
1162
1041
|
return this.modules.size;
|
|
1163
1042
|
}
|
|
1164
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);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
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
|
+
}
|
|
1165
1071
|
var defaultRegistry = null;
|
|
1166
1072
|
function getSharedModuleRegistry() {
|
|
1167
1073
|
if (!defaultRegistry) {
|
|
@@ -1181,6 +1087,9 @@ function unregisterSharedModule(moduleId) {
|
|
|
1181
1087
|
function clearSharedModules() {
|
|
1182
1088
|
getSharedModuleRegistry().clear();
|
|
1183
1089
|
}
|
|
1090
|
+
function getSharedModuleExports(moduleId) {
|
|
1091
|
+
return getSharedModuleRegistry().getExportNames(moduleId);
|
|
1092
|
+
}
|
|
1184
1093
|
function getSharedModuleRuntimeCode(moduleId) {
|
|
1185
1094
|
return `
|
|
1186
1095
|
(function() {
|
|
@@ -1197,11 +1106,35 @@ function getSharedModuleRuntimeCode(moduleId) {
|
|
|
1197
1106
|
}
|
|
1198
1107
|
|
|
1199
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";
|
|
1200
1123
|
var initialized = false;
|
|
1201
1124
|
var initPromise = null;
|
|
1202
1125
|
function getWasmUrl() {
|
|
1203
|
-
|
|
1204
|
-
|
|
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
|
+
}
|
|
1205
1138
|
}
|
|
1206
1139
|
async function initBundler() {
|
|
1207
1140
|
if (initialized)
|
|
@@ -1210,9 +1143,13 @@ async function initBundler() {
|
|
|
1210
1143
|
await initPromise;
|
|
1211
1144
|
return;
|
|
1212
1145
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1146
|
+
checkCrossOriginIsolation();
|
|
1147
|
+
initPromise = (async () => {
|
|
1148
|
+
const es = await getEsbuild();
|
|
1149
|
+
await es.initialize({
|
|
1150
|
+
wasmURL: getWasmUrl()
|
|
1151
|
+
});
|
|
1152
|
+
})();
|
|
1216
1153
|
await initPromise;
|
|
1217
1154
|
initialized = true;
|
|
1218
1155
|
}
|
|
@@ -1263,8 +1200,8 @@ function createVfsPlugin(options) {
|
|
|
1263
1200
|
} = options;
|
|
1264
1201
|
return {
|
|
1265
1202
|
name: "virtual-fs",
|
|
1266
|
-
setup(
|
|
1267
|
-
|
|
1203
|
+
setup(build) {
|
|
1204
|
+
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
1268
1205
|
if (args.kind === "entry-point") {
|
|
1269
1206
|
return { path: entryPoint, namespace: "vfs" };
|
|
1270
1207
|
}
|
|
@@ -1320,28 +1257,18 @@ function createVfsPlugin(options) {
|
|
|
1320
1257
|
}
|
|
1321
1258
|
return { errors: [{ text: `Cannot resolve: ${args.path} from ${args.resolveDir}` }] };
|
|
1322
1259
|
});
|
|
1323
|
-
|
|
1324
|
-
const contents = `
|
|
1325
|
-
export * from ${JSON.stringify(args.path)};
|
|
1326
|
-
// Re-export all named exports by importing from registry
|
|
1327
|
-
const __mod__ = ${getSharedModuleRuntimeCode(args.path)};
|
|
1328
|
-
for (const __k__ in __mod__) {
|
|
1329
|
-
if (__k__ !== 'default') Object.defineProperty(exports, __k__, {
|
|
1330
|
-
enumerable: true,
|
|
1331
|
-
get: function() { return __mod__[__k__]; }
|
|
1332
|
-
});
|
|
1333
|
-
}`;
|
|
1334
|
-
const esmContents = `
|
|
1260
|
+
build.onLoad({ filter: /.*/, namespace: "sandlot-shared" }, (args) => {
|
|
1261
|
+
const contents = `
|
|
1335
1262
|
const __sandlot_mod__ = ${getSharedModuleRuntimeCode(args.path)};
|
|
1336
1263
|
export default __sandlot_mod__.default ?? __sandlot_mod__;
|
|
1337
1264
|
${generateNamedExports(args.path)}
|
|
1338
1265
|
`;
|
|
1339
1266
|
return {
|
|
1340
|
-
contents:
|
|
1267
|
+
contents: contents.trim(),
|
|
1341
1268
|
loader: "js"
|
|
1342
1269
|
};
|
|
1343
1270
|
});
|
|
1344
|
-
|
|
1271
|
+
build.onLoad({ filter: /.*/, namespace: "vfs" }, async (args) => {
|
|
1345
1272
|
try {
|
|
1346
1273
|
const contents = await fs.readFile(args.path);
|
|
1347
1274
|
includedFiles.add(args.path);
|
|
@@ -1360,55 +1287,12 @@ ${generateNamedExports(args.path)}
|
|
|
1360
1287
|
};
|
|
1361
1288
|
}
|
|
1362
1289
|
function generateNamedExports(moduleId) {
|
|
1363
|
-
const
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
"useEffect",
|
|
1367
|
-
"useContext",
|
|
1368
|
-
"useReducer",
|
|
1369
|
-
"useCallback",
|
|
1370
|
-
"useMemo",
|
|
1371
|
-
"useRef",
|
|
1372
|
-
"useImperativeHandle",
|
|
1373
|
-
"useLayoutEffect",
|
|
1374
|
-
"useDebugValue",
|
|
1375
|
-
"useDeferredValue",
|
|
1376
|
-
"useTransition",
|
|
1377
|
-
"useId",
|
|
1378
|
-
"useSyncExternalStore",
|
|
1379
|
-
"useInsertionEffect",
|
|
1380
|
-
"useOptimistic",
|
|
1381
|
-
"useActionState",
|
|
1382
|
-
"createElement",
|
|
1383
|
-
"cloneElement",
|
|
1384
|
-
"createContext",
|
|
1385
|
-
"forwardRef",
|
|
1386
|
-
"lazy",
|
|
1387
|
-
"memo",
|
|
1388
|
-
"startTransition",
|
|
1389
|
-
"Children",
|
|
1390
|
-
"Component",
|
|
1391
|
-
"PureComponent",
|
|
1392
|
-
"Fragment",
|
|
1393
|
-
"Profiler",
|
|
1394
|
-
"StrictMode",
|
|
1395
|
-
"Suspense",
|
|
1396
|
-
"version",
|
|
1397
|
-
"isValidElement"
|
|
1398
|
-
],
|
|
1399
|
-
"react-dom": ["createPortal", "flushSync", "version"],
|
|
1400
|
-
"react-dom/client": ["createRoot", "hydrateRoot"],
|
|
1401
|
-
"react-dom/server": ["renderToString", "renderToStaticMarkup", "renderToPipeableStream"]
|
|
1402
|
-
};
|
|
1403
|
-
const exports = knownExports[moduleId];
|
|
1404
|
-
if (!exports) {
|
|
1405
|
-
return `
|
|
1406
|
-
// Dynamic re-export for unknown module
|
|
1407
|
-
export const __moduleProxy__ = __sandlot_mod__;
|
|
1408
|
-
`;
|
|
1409
|
-
}
|
|
1410
|
-
return exports.map((name) => `export const ${name} = __sandlot_mod__.${name};`).join(`
|
|
1290
|
+
const exports = getSharedModuleExports(moduleId);
|
|
1291
|
+
if (exports.length > 0) {
|
|
1292
|
+
return exports.map((name) => `export const ${name} = __sandlot_mod__.${name};`).join(`
|
|
1411
1293
|
`);
|
|
1294
|
+
}
|
|
1295
|
+
return `// No exports discovered for "${moduleId}" - use default import or call registerSharedModules() first`;
|
|
1412
1296
|
}
|
|
1413
1297
|
async function bundle(options) {
|
|
1414
1298
|
await initBundler();
|
|
@@ -1440,7 +1324,8 @@ async function bundle(options) {
|
|
|
1440
1324
|
includedFiles,
|
|
1441
1325
|
sharedModuleIds
|
|
1442
1326
|
});
|
|
1443
|
-
const
|
|
1327
|
+
const es = await getEsbuild();
|
|
1328
|
+
const result = await es.build({
|
|
1444
1329
|
entryPoints: [normalizedEntry],
|
|
1445
1330
|
bundle: true,
|
|
1446
1331
|
write: false,
|
|
@@ -1450,7 +1335,8 @@ async function bundle(options) {
|
|
|
1450
1335
|
globalName,
|
|
1451
1336
|
target,
|
|
1452
1337
|
external,
|
|
1453
|
-
plugins: [plugin]
|
|
1338
|
+
plugins: [plugin],
|
|
1339
|
+
jsx: "automatic"
|
|
1454
1340
|
});
|
|
1455
1341
|
const code = result.outputFiles?.[0]?.text ?? "";
|
|
1456
1342
|
return {
|
|
@@ -1473,7 +1359,21 @@ async function bundleAndImport(options) {
|
|
|
1473
1359
|
}
|
|
1474
1360
|
}
|
|
1475
1361
|
|
|
1476
|
-
// src/commands.ts
|
|
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
|
|
1477
1377
|
import { defineCommand } from "just-bash/browser";
|
|
1478
1378
|
|
|
1479
1379
|
// src/typechecker.ts
|
|
@@ -1864,20 +1764,7 @@ async function hasExport(result, exportName) {
|
|
|
1864
1764
|
return exportName in module;
|
|
1865
1765
|
}
|
|
1866
1766
|
|
|
1867
|
-
// src/commands.ts
|
|
1868
|
-
function formatEsbuildMessages(messages) {
|
|
1869
|
-
if (messages.length === 0)
|
|
1870
|
-
return "";
|
|
1871
|
-
return messages.map((msg) => {
|
|
1872
|
-
if (msg.location) {
|
|
1873
|
-
const { file, line, column } = msg.location;
|
|
1874
|
-
const loc = file ? `${file}${line ? `:${line}` : ""}${column ? `:${column}` : ""}` : "";
|
|
1875
|
-
return loc ? `${loc}: ${msg.text}` : msg.text;
|
|
1876
|
-
}
|
|
1877
|
-
return msg.text;
|
|
1878
|
-
}).join(`
|
|
1879
|
-
`);
|
|
1880
|
-
}
|
|
1767
|
+
// src/commands/compile.ts
|
|
1881
1768
|
function createTscCommand(deps) {
|
|
1882
1769
|
const { fs, libFiles, tsconfigPath } = deps;
|
|
1883
1770
|
return defineCommand("tsc", async (args, _ctx) => {
|
|
@@ -1942,7 +1829,7 @@ ${formatDiagnosticsForAgent(result.diagnostics.filter((d) => d.category === "war
|
|
|
1942
1829
|
});
|
|
1943
1830
|
}
|
|
1944
1831
|
function createBuildCommand(deps) {
|
|
1945
|
-
const { fs, libFiles, tsconfigPath, onBuild, sharedModules } = deps;
|
|
1832
|
+
const { fs, libFiles, tsconfigPath, onBuild, getValidation, sharedModules } = deps;
|
|
1946
1833
|
return defineCommand("build", async (args, _ctx) => {
|
|
1947
1834
|
let entryPoint = null;
|
|
1948
1835
|
let skipTypecheck = false;
|
|
@@ -2014,8 +1901,39 @@ ${formatted}
|
|
|
2014
1901
|
minify,
|
|
2015
1902
|
sharedModules
|
|
2016
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
|
+
}
|
|
2017
1935
|
if (onBuild) {
|
|
2018
|
-
await onBuild(bundleResult);
|
|
1936
|
+
await onBuild({ bundle: bundleResult, module: validatedModule });
|
|
2019
1937
|
}
|
|
2020
1938
|
let output = `Build successful!
|
|
2021
1939
|
`;
|
|
@@ -2031,6 +1949,15 @@ ${formatted}
|
|
|
2031
1949
|
}
|
|
2032
1950
|
output += `Bundled: ${bundleResult.includedFiles.length} file(s)
|
|
2033
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
|
+
}
|
|
2034
1961
|
if (bundleResult.warnings.length > 0) {
|
|
2035
1962
|
output += `
|
|
2036
1963
|
Build warnings:
|
|
@@ -2062,9 +1989,11 @@ ${formatDiagnosticsForAgent(warnings)}
|
|
|
2062
1989
|
}
|
|
2063
1990
|
});
|
|
2064
1991
|
}
|
|
1992
|
+
// src/commands/packages.ts
|
|
1993
|
+
import { defineCommand as defineCommand2 } from "just-bash/browser";
|
|
2065
1994
|
function createInstallCommand(deps) {
|
|
2066
1995
|
const { fs, typesCache } = deps;
|
|
2067
|
-
return
|
|
1996
|
+
return defineCommand2("install", async (args, _ctx) => {
|
|
2068
1997
|
if (args.length === 0) {
|
|
2069
1998
|
return {
|
|
2070
1999
|
stdout: "",
|
|
@@ -2118,7 +2047,7 @@ Examples:
|
|
|
2118
2047
|
}
|
|
2119
2048
|
function createUninstallCommand(deps) {
|
|
2120
2049
|
const { fs } = deps;
|
|
2121
|
-
return
|
|
2050
|
+
return defineCommand2("uninstall", async (args, _ctx) => {
|
|
2122
2051
|
if (args.length === 0) {
|
|
2123
2052
|
return {
|
|
2124
2053
|
stdout: "",
|
|
@@ -2163,7 +2092,7 @@ function createUninstallCommand(deps) {
|
|
|
2163
2092
|
}
|
|
2164
2093
|
function createListCommand(deps) {
|
|
2165
2094
|
const { fs } = deps;
|
|
2166
|
-
return
|
|
2095
|
+
return defineCommand2("list", async (_args, _ctx) => {
|
|
2167
2096
|
try {
|
|
2168
2097
|
const packages = await listPackages(fs);
|
|
2169
2098
|
if (packages.length === 0) {
|
|
@@ -2193,9 +2122,11 @@ function createListCommand(deps) {
|
|
|
2193
2122
|
}
|
|
2194
2123
|
});
|
|
2195
2124
|
}
|
|
2125
|
+
// src/commands/run.ts
|
|
2126
|
+
import { defineCommand as defineCommand3 } from "just-bash/browser";
|
|
2196
2127
|
function createRunCommand(deps) {
|
|
2197
2128
|
const { fs, libFiles, tsconfigPath, runOptions = {}, sharedModules } = deps;
|
|
2198
|
-
return
|
|
2129
|
+
return defineCommand3("run", async (args, _ctx) => {
|
|
2199
2130
|
let entryPoint = null;
|
|
2200
2131
|
let skipTypecheck = runOptions.skipTypecheck ?? false;
|
|
2201
2132
|
let timeout = runOptions.timeout ?? 30000;
|
|
@@ -2372,6 +2303,7 @@ ${err.stack}` : "";
|
|
|
2372
2303
|
}
|
|
2373
2304
|
});
|
|
2374
2305
|
}
|
|
2306
|
+
// src/commands/index.ts
|
|
2375
2307
|
function createDefaultCommands(deps) {
|
|
2376
2308
|
return [
|
|
2377
2309
|
createTscCommand(deps),
|
|
@@ -2578,12 +2510,10 @@ function hasDefaultResources() {
|
|
|
2578
2510
|
return defaultResourcesInstance !== null;
|
|
2579
2511
|
}
|
|
2580
2512
|
|
|
2581
|
-
// src/
|
|
2513
|
+
// src/build-emitter.ts
|
|
2582
2514
|
class BuildEmitter {
|
|
2583
2515
|
listeners = new Set;
|
|
2584
|
-
lastResult = null;
|
|
2585
2516
|
emit = async (result) => {
|
|
2586
|
-
this.lastResult = result;
|
|
2587
2517
|
const promises = [];
|
|
2588
2518
|
for (const listener of this.listeners) {
|
|
2589
2519
|
const ret = listener(result);
|
|
@@ -2599,282 +2529,167 @@ class BuildEmitter {
|
|
|
2599
2529
|
this.listeners.delete(callback);
|
|
2600
2530
|
};
|
|
2601
2531
|
}
|
|
2602
|
-
waitFor() {
|
|
2603
|
-
if (this.lastResult) {
|
|
2604
|
-
const result = this.lastResult;
|
|
2605
|
-
this.lastResult = null;
|
|
2606
|
-
return Promise.resolve(result);
|
|
2607
|
-
}
|
|
2608
|
-
return new Promise((resolve) => {
|
|
2609
|
-
const unsub = this.on((result) => {
|
|
2610
|
-
unsub();
|
|
2611
|
-
this.lastResult = null;
|
|
2612
|
-
resolve(result);
|
|
2613
|
-
});
|
|
2614
|
-
});
|
|
2615
|
-
}
|
|
2616
2532
|
}
|
|
2533
|
+
|
|
2534
|
+
// src/sandbox.ts
|
|
2617
2535
|
async function createSandbox(options = {}) {
|
|
2618
|
-
const {
|
|
2619
|
-
fsOptions = {},
|
|
2620
|
-
tsconfigPath = "/tsconfig.json",
|
|
2621
|
-
resources: providedResources,
|
|
2622
|
-
onBuild: onBuildCallback,
|
|
2623
|
-
customCommands = [],
|
|
2624
|
-
sharedModules
|
|
2625
|
-
} = options;
|
|
2626
|
-
const fsPromise = IndexedDbFs.create(fsOptions);
|
|
2627
|
-
const resourcesPromise = providedResources ? Promise.resolve(providedResources) : getDefaultResources();
|
|
2628
|
-
const bundlerPromise = initBundler();
|
|
2629
|
-
const [fs, resources] = await Promise.all([fsPromise, resourcesPromise, bundlerPromise]);
|
|
2630
|
-
const libFiles = resources.libFiles;
|
|
2631
|
-
const typesCache = resources.typesCache;
|
|
2632
|
-
const buildEmitter = new BuildEmitter;
|
|
2633
|
-
if (onBuildCallback) {
|
|
2634
|
-
buildEmitter.on(onBuildCallback);
|
|
2635
|
-
}
|
|
2636
|
-
const commandDeps = {
|
|
2637
|
-
fs,
|
|
2638
|
-
libFiles,
|
|
2639
|
-
tsconfigPath,
|
|
2640
|
-
onBuild: buildEmitter.emit,
|
|
2641
|
-
typesCache,
|
|
2642
|
-
sharedModules
|
|
2643
|
-
};
|
|
2644
|
-
const defaultCommands = createDefaultCommands(commandDeps);
|
|
2645
|
-
const bash = new Bash({
|
|
2646
|
-
fs,
|
|
2647
|
-
cwd: "/",
|
|
2648
|
-
customCommands: [...defaultCommands, ...customCommands]
|
|
2649
|
-
});
|
|
2650
|
-
return {
|
|
2651
|
-
fs,
|
|
2652
|
-
bash,
|
|
2653
|
-
isDirty: () => fs.isDirty(),
|
|
2654
|
-
save: () => fs.save(),
|
|
2655
|
-
close: () => fs.close(),
|
|
2656
|
-
onBuild: (callback) => buildEmitter.on(callback)
|
|
2657
|
-
};
|
|
2658
|
-
}
|
|
2659
|
-
async function createInMemorySandbox(options = {}) {
|
|
2660
2536
|
const {
|
|
2661
2537
|
initialFiles,
|
|
2538
|
+
maxFilesystemSize,
|
|
2662
2539
|
tsconfigPath = "/tsconfig.json",
|
|
2663
2540
|
resources: providedResources,
|
|
2664
2541
|
onBuild: onBuildCallback,
|
|
2665
2542
|
customCommands = [],
|
|
2666
|
-
sharedModules
|
|
2543
|
+
sharedModules,
|
|
2544
|
+
bashOptions = {}
|
|
2667
2545
|
} = options;
|
|
2546
|
+
const fs = Filesystem.create({
|
|
2547
|
+
initialFiles,
|
|
2548
|
+
maxSizeBytes: maxFilesystemSize
|
|
2549
|
+
});
|
|
2668
2550
|
const resourcesPromise = providedResources ? Promise.resolve(providedResources) : getDefaultResources();
|
|
2669
2551
|
const bundlerPromise = initBundler();
|
|
2670
|
-
const fs = IndexedDbFs.createInMemory({ initialFiles });
|
|
2671
2552
|
const [resources] = await Promise.all([resourcesPromise, bundlerPromise]);
|
|
2672
2553
|
const libFiles = resources.libFiles;
|
|
2673
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);
|
|
2566
|
+
}
|
|
2567
|
+
}));
|
|
2568
|
+
}
|
|
2674
2569
|
const buildEmitter = new BuildEmitter;
|
|
2570
|
+
let lastBuild = null;
|
|
2571
|
+
buildEmitter.on((result) => {
|
|
2572
|
+
lastBuild = result;
|
|
2573
|
+
});
|
|
2675
2574
|
if (onBuildCallback) {
|
|
2676
2575
|
buildEmitter.on(onBuildCallback);
|
|
2677
2576
|
}
|
|
2577
|
+
let validationFn = null;
|
|
2678
2578
|
const commandDeps = {
|
|
2679
2579
|
fs,
|
|
2680
2580
|
libFiles,
|
|
2681
2581
|
tsconfigPath,
|
|
2682
2582
|
onBuild: buildEmitter.emit,
|
|
2583
|
+
getValidation: () => validationFn,
|
|
2683
2584
|
typesCache,
|
|
2684
2585
|
sharedModules
|
|
2685
2586
|
};
|
|
2686
2587
|
const defaultCommands = createDefaultCommands(commandDeps);
|
|
2687
2588
|
const bash = new Bash({
|
|
2589
|
+
...bashOptions,
|
|
2688
2590
|
fs,
|
|
2689
|
-
cwd: "/",
|
|
2690
2591
|
customCommands: [...defaultCommands, ...customCommands]
|
|
2691
2592
|
});
|
|
2692
2593
|
return {
|
|
2693
2594
|
fs,
|
|
2694
2595
|
bash,
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
lastResult = null;
|
|
2706
|
-
emit = async (result) => {
|
|
2707
|
-
this.lastResult = result;
|
|
2708
|
-
const promises = [];
|
|
2709
|
-
for (const listener of this.listeners) {
|
|
2710
|
-
const ret = listener(result);
|
|
2711
|
-
if (ret instanceof Promise) {
|
|
2712
|
-
promises.push(ret);
|
|
2713
|
-
}
|
|
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;
|
|
2714
2606
|
}
|
|
2715
|
-
await Promise.all(promises);
|
|
2716
2607
|
};
|
|
2717
|
-
on(callback) {
|
|
2718
|
-
this.listeners.add(callback);
|
|
2719
|
-
return () => {
|
|
2720
|
-
this.listeners.delete(callback);
|
|
2721
|
-
};
|
|
2722
|
-
}
|
|
2723
|
-
waitFor() {
|
|
2724
|
-
if (this.lastResult) {
|
|
2725
|
-
const result = this.lastResult;
|
|
2726
|
-
this.lastResult = null;
|
|
2727
|
-
return Promise.resolve(result);
|
|
2728
|
-
}
|
|
2729
|
-
return new Promise((resolve) => {
|
|
2730
|
-
const unsub = this.on((result) => {
|
|
2731
|
-
unsub();
|
|
2732
|
-
this.lastResult = null;
|
|
2733
|
-
resolve(result);
|
|
2734
|
-
});
|
|
2735
|
-
});
|
|
2736
|
-
}
|
|
2737
2608
|
}
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
nextId = 1;
|
|
2745
|
-
options;
|
|
2746
|
-
constructor(options = {}) {
|
|
2747
|
-
this.options = {
|
|
2748
|
-
libs: options.libs ?? getDefaultBrowserLibs(),
|
|
2749
|
-
...options
|
|
2750
|
-
};
|
|
2751
|
-
}
|
|
2752
|
-
async initialize() {
|
|
2753
|
-
if (this.initialized)
|
|
2754
|
-
return;
|
|
2755
|
-
if (this.initPromise) {
|
|
2756
|
-
await this.initPromise;
|
|
2757
|
-
return;
|
|
2758
|
-
}
|
|
2759
|
-
this.initPromise = this.doInitialize();
|
|
2760
|
-
await this.initPromise;
|
|
2761
|
-
this.initialized = true;
|
|
2762
|
-
}
|
|
2763
|
-
async doInitialize() {
|
|
2764
|
-
this.resources = await createSharedResources({
|
|
2765
|
-
libs: this.options.libs,
|
|
2766
|
-
skipLibs: this.options.skipLibs,
|
|
2767
|
-
skipBundler: this.options.skipBundler
|
|
2768
|
-
});
|
|
2769
|
-
}
|
|
2770
|
-
async createSandbox(options = {}) {
|
|
2771
|
-
await this.initialize();
|
|
2772
|
-
const {
|
|
2773
|
-
id = `sandbox-${this.nextId++}`,
|
|
2774
|
-
fsOptions = {},
|
|
2775
|
-
initialFiles,
|
|
2776
|
-
tsconfigPath = "/tsconfig.json",
|
|
2777
|
-
onBuild,
|
|
2778
|
-
customCommands = [],
|
|
2779
|
-
inMemory = true,
|
|
2780
|
-
sharedModules = this.options.sharedModules
|
|
2781
|
-
} = options;
|
|
2782
|
-
let fs;
|
|
2783
|
-
if (inMemory) {
|
|
2784
|
-
fs = IndexedDbFs.createInMemory({
|
|
2785
|
-
initialFiles,
|
|
2786
|
-
maxSizeBytes: fsOptions.maxSizeBytes
|
|
2787
|
-
});
|
|
2788
|
-
} else {
|
|
2789
|
-
fs = await IndexedDbFs.create({
|
|
2790
|
-
dbName: fsOptions.dbName ?? id,
|
|
2791
|
-
initialFiles,
|
|
2792
|
-
maxSizeBytes: fsOptions.maxSizeBytes
|
|
2793
|
-
});
|
|
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);
|
|
2794
2615
|
}
|
|
2795
|
-
const
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
}
|
|
2799
|
-
const commandDeps = {
|
|
2800
|
-
fs,
|
|
2801
|
-
libFiles: this.resources.libFiles,
|
|
2802
|
-
tsconfigPath,
|
|
2803
|
-
onBuild: buildEmitter.emit,
|
|
2804
|
-
typesCache: this.resources.typesCache,
|
|
2805
|
-
sharedModules
|
|
2806
|
-
};
|
|
2807
|
-
const defaultCommands = createDefaultCommands(commandDeps);
|
|
2808
|
-
const bash = new Bash2({
|
|
2809
|
-
fs,
|
|
2810
|
-
cwd: "/",
|
|
2811
|
-
customCommands: [...defaultCommands, ...customCommands]
|
|
2616
|
+
const captured = { output: null };
|
|
2617
|
+
const unsubscribe = sandbox.onBuild((output) => {
|
|
2618
|
+
captured.output = output;
|
|
2812
2619
|
});
|
|
2813
|
-
const
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
}
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
return Array.from(this.sandboxes.values());
|
|
2833
|
-
}
|
|
2834
|
-
closeSandbox(id) {
|
|
2835
|
-
const sandbox = this.sandboxes.get(id);
|
|
2836
|
-
if (sandbox) {
|
|
2837
|
-
sandbox.close();
|
|
2838
|
-
return true;
|
|
2839
|
-
}
|
|
2840
|
-
return false;
|
|
2841
|
-
}
|
|
2842
|
-
destroyAll() {
|
|
2843
|
-
for (const sandbox of this.sandboxes.values()) {
|
|
2844
|
-
sandbox.fs.close();
|
|
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);
|
|
2633
|
+
} else {
|
|
2634
|
+
signal.addEventListener("abort", () => {
|
|
2635
|
+
abortController.abort(signal.reason);
|
|
2636
|
+
}, { once: true });
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2845
2639
|
}
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
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();
|
|
2670
|
+
}
|
|
2853
2671
|
}
|
|
2854
|
-
|
|
2855
|
-
}
|
|
2856
|
-
getDirtySandboxes() {
|
|
2857
|
-
return Array.from(this.sandboxes.entries()).filter(([_, sandbox]) => sandbox.isDirty()).map(([id]) => id);
|
|
2858
|
-
}
|
|
2859
|
-
getStats() {
|
|
2672
|
+
const buildOutput = captured.output;
|
|
2860
2673
|
return {
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2674
|
+
result,
|
|
2675
|
+
error,
|
|
2676
|
+
bundle: buildOutput?.bundle ?? null,
|
|
2677
|
+
module: buildOutput?.module ?? null,
|
|
2678
|
+
sandbox
|
|
2865
2679
|
};
|
|
2866
|
-
}
|
|
2867
|
-
getResources() {
|
|
2868
|
-
return this.resources;
|
|
2869
|
-
}
|
|
2870
|
-
getLibFiles() {
|
|
2871
|
-
return this.resources?.libFiles ?? new Map;
|
|
2872
|
-
}
|
|
2680
|
+
};
|
|
2873
2681
|
}
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
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)
|
|
2692
|
+
};
|
|
2878
2693
|
}
|
|
2879
2694
|
export {
|
|
2880
2695
|
unregisterSharedModule,
|
|
@@ -2899,22 +2714,20 @@ export {
|
|
|
2899
2714
|
createUninstallCommand,
|
|
2900
2715
|
createTscCommand,
|
|
2901
2716
|
createSharedResources,
|
|
2902
|
-
createSandboxManager,
|
|
2903
2717
|
createSandbox,
|
|
2904
2718
|
createRunCommand,
|
|
2905
2719
|
createListCommand,
|
|
2906
2720
|
createInstallCommand,
|
|
2907
|
-
|
|
2908
|
-
createInMemorySandbox,
|
|
2721
|
+
createFilesystem,
|
|
2909
2722
|
createDefaultCommands,
|
|
2723
|
+
createBuilder,
|
|
2910
2724
|
createBuildCommand,
|
|
2911
2725
|
clearSharedModules,
|
|
2912
2726
|
clearDefaultResources,
|
|
2913
2727
|
bundleToUrl,
|
|
2914
2728
|
bundleAndImport,
|
|
2915
2729
|
bundle,
|
|
2916
|
-
SandboxManager,
|
|
2917
2730
|
ModuleLoadError,
|
|
2918
|
-
|
|
2731
|
+
Filesystem,
|
|
2919
2732
|
ExportNotFoundError
|
|
2920
2733
|
};
|