@ai-ecoverse/slicc-kernel 1.12.0 → 1.14.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.
- package/README.md +22 -1
- package/dist/driver-worker.js +574 -0
- package/dist/driver-worker.js.map +7 -0
- package/dist/driver.js +115 -0
- package/dist/driver.js.map +7 -0
- package/dist/index.js +18 -1
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +1018 -41
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-driver-worker.js +576 -0
- package/dist/node-driver-worker.js.map +7 -0
- package/dist/node-process-worker.js +51 -16
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +1010 -22
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +51 -16
- package/dist/process-worker.js.map +3 -3
- package/package.json +4 -2
package/dist/kernel-worker.js
CHANGED
|
@@ -194,7 +194,7 @@ function pinScript(url) {
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
// src/client/protocol.ts
|
|
197
|
-
var PROTOCOL = [1,
|
|
197
|
+
var PROTOCOL = [1, 2];
|
|
198
198
|
var FS_METHODS = [
|
|
199
199
|
"readFile",
|
|
200
200
|
"writeFile",
|
|
@@ -447,6 +447,11 @@ async function fsCall(fs, method, args) {
|
|
|
447
447
|
return { result: await fs.exists(path(0)) };
|
|
448
448
|
}
|
|
449
449
|
}
|
|
450
|
+
function signalOf(host) {
|
|
451
|
+
return host.signal ?? ((name2) => {
|
|
452
|
+
throw fsError("EINVAL", `this kernel names no signal ${name2}`);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
450
455
|
function serveClient(port, host) {
|
|
451
456
|
let state = "new";
|
|
452
457
|
const terminals = /* @__PURE__ */ new Map();
|
|
@@ -534,6 +539,9 @@ function serveClient(port, host) {
|
|
|
534
539
|
return { result: result.status };
|
|
535
540
|
}
|
|
536
541
|
async function handle(req) {
|
|
542
|
+
if (host.scope === "transport" && req.op !== "detach") {
|
|
543
|
+
throw fsError("EPERM", `${req.op}: this client may only use the network transport`);
|
|
544
|
+
}
|
|
537
545
|
const l = await host.launcher();
|
|
538
546
|
switch (req.op) {
|
|
539
547
|
case "spawn":
|
|
@@ -543,11 +551,11 @@ function serveClient(port, host) {
|
|
|
543
551
|
case "terminal": {
|
|
544
552
|
const session = terminals.get(req.terminal);
|
|
545
553
|
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
546
|
-
act(session, req, host
|
|
554
|
+
act(session, req, signalOf(host));
|
|
547
555
|
return { result: true };
|
|
548
556
|
}
|
|
549
557
|
case "kill":
|
|
550
|
-
if (!l.kill(req.pid, host
|
|
558
|
+
if (!l.kill(req.pid, signalOf(host)(req.signal))) throw fsError("ESRCH", `pid ${req.pid}`);
|
|
551
559
|
return { result: true };
|
|
552
560
|
case "ps":
|
|
553
561
|
return {
|
|
@@ -571,6 +579,13 @@ function serveClient(port, host) {
|
|
|
571
579
|
watches.get(req.watch)?.();
|
|
572
580
|
watches.delete(req.watch);
|
|
573
581
|
return { result: true };
|
|
582
|
+
case "mount":
|
|
583
|
+
return { result: await l.mount(req.spec) };
|
|
584
|
+
case "umount":
|
|
585
|
+
l.umount(req.target);
|
|
586
|
+
return { result: true };
|
|
587
|
+
case "mounts":
|
|
588
|
+
return { result: l.mounts.list() };
|
|
574
589
|
case "detach":
|
|
575
590
|
return { result: true };
|
|
576
591
|
default:
|
|
@@ -3007,9 +3022,28 @@ async function packages(fs, modules) {
|
|
|
3007
3022
|
}
|
|
3008
3023
|
return dirs;
|
|
3009
3024
|
}
|
|
3025
|
+
async function pnpmGlobalRoots(fs, home) {
|
|
3026
|
+
const roots = [];
|
|
3027
|
+
const global = `${home}/global`;
|
|
3028
|
+
for (const version of (await fs.readdir(global).catch(() => [])).sort()) {
|
|
3029
|
+
for (const group of (await fs.readdir(`${global}/${version}`).catch(() => [])).sort()) {
|
|
3030
|
+
const dir = `${global}/${version}/${group}`;
|
|
3031
|
+
const st = await fs.lstat(dir).catch(() => void 0);
|
|
3032
|
+
if (st?.isDirectory && await fs.exists(`${dir}/node_modules`))
|
|
3033
|
+
roots.push(`${dir}/node_modules`);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
return roots;
|
|
3037
|
+
}
|
|
3038
|
+
async function packagesIn(fs, roots) {
|
|
3039
|
+
const dirs = [];
|
|
3040
|
+
for (const root of typeof roots === "string" ? [roots] : roots)
|
|
3041
|
+
dirs.push(...await packages(fs, root));
|
|
3042
|
+
return dirs;
|
|
3043
|
+
}
|
|
3010
3044
|
async function scanCommands(fs, modules) {
|
|
3011
3045
|
const found = /* @__PURE__ */ new Map();
|
|
3012
|
-
for (const pkg of await
|
|
3046
|
+
for (const pkg of await packagesIn(fs, modules)) {
|
|
3013
3047
|
let manifest;
|
|
3014
3048
|
try {
|
|
3015
3049
|
manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
|
|
@@ -3026,6 +3060,24 @@ async function scanCommands(fs, modules) {
|
|
|
3026
3060
|
}
|
|
3027
3061
|
return found;
|
|
3028
3062
|
}
|
|
3063
|
+
async function scanFilesystems(fs, modules) {
|
|
3064
|
+
const found = /* @__PURE__ */ new Map();
|
|
3065
|
+
for (const pkg of await packagesIn(fs, modules)) {
|
|
3066
|
+
let manifest;
|
|
3067
|
+
try {
|
|
3068
|
+
manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
|
|
3069
|
+
} catch {
|
|
3070
|
+
continue;
|
|
3071
|
+
}
|
|
3072
|
+
const declared = manifest.slicc?.filesystems;
|
|
3073
|
+
if (!declared || typeof declared !== "object") continue;
|
|
3074
|
+
for (const [type, entry] of Object.entries(declared)) {
|
|
3075
|
+
const module = inside(pkg, entry?.module);
|
|
3076
|
+
if (NAME.test(type) && module && !found.has(type)) found.set(type, module);
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
return found;
|
|
3080
|
+
}
|
|
3029
3081
|
|
|
3030
3082
|
// src/fs/commands.ts
|
|
3031
3083
|
var S_IFDIR2 = 16384;
|
|
@@ -3444,7 +3496,8 @@ function statJson(s) {
|
|
|
3444
3496
|
mtimeMs: s.mtime.getTime(),
|
|
3445
3497
|
atimeMs: s.atime.getTime(),
|
|
3446
3498
|
ctimeMs: s.ctime.getTime(),
|
|
3447
|
-
ino: s.ino
|
|
3499
|
+
ino: s.ino,
|
|
3500
|
+
...s.dev !== void 0 ? { dev: s.dev } : {}
|
|
3448
3501
|
};
|
|
3449
3502
|
}
|
|
3450
3503
|
async function readdirStat(fs, dir) {
|
|
@@ -3505,8 +3558,6 @@ async function run(fs, path, req, cwd) {
|
|
|
3505
3558
|
case "utimes":
|
|
3506
3559
|
await fs.utimes(path, new Date(req.atimeMs ?? 0), new Date(req.mtimeMs ?? 0));
|
|
3507
3560
|
return done;
|
|
3508
|
-
case "statfs":
|
|
3509
|
-
return json(await storageUsage());
|
|
3510
3561
|
default:
|
|
3511
3562
|
return { ok: false, errno: "EINVAL", message: `sync-fs: unknown op '${req.op}'` };
|
|
3512
3563
|
}
|
|
@@ -3516,7 +3567,13 @@ async function dispatchSyncFs(req) {
|
|
|
3516
3567
|
if (!entry) return { ok: false, errno: "EACCES", message: "sync-fs: unknown or revoked token" };
|
|
3517
3568
|
const { fs, cwd } = entry;
|
|
3518
3569
|
try {
|
|
3519
|
-
|
|
3570
|
+
const path = fs.resolvePath(cwd, req.path);
|
|
3571
|
+
if (req.op === "statfs") return json(await entry.statfs?.(path) ?? await storageUsage());
|
|
3572
|
+
if (req.op === "hold") {
|
|
3573
|
+
entry.hold?.(path, req.mode === 1);
|
|
3574
|
+
return done;
|
|
3575
|
+
}
|
|
3576
|
+
return await run(fs, path, req, cwd);
|
|
3520
3577
|
} catch (err) {
|
|
3521
3578
|
return toErrno(err);
|
|
3522
3579
|
}
|
|
@@ -3580,7 +3637,7 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
3580
3637
|
const { header: header2, window } = views;
|
|
3581
3638
|
const pending = /* @__PURE__ */ new Map();
|
|
3582
3639
|
let disposed = false;
|
|
3583
|
-
const { dispatch } = opts;
|
|
3640
|
+
const { dispatch: dispatch2 } = opts;
|
|
3584
3641
|
function drop(id) {
|
|
3585
3642
|
const entry = pending.get(id);
|
|
3586
3643
|
if (!entry) return;
|
|
@@ -3635,7 +3692,7 @@ function attachSyncSabResponder(port, sab, token, opts) {
|
|
|
3635
3692
|
const req = { ...body, token };
|
|
3636
3693
|
let dispatched;
|
|
3637
3694
|
try {
|
|
3638
|
-
dispatched =
|
|
3695
|
+
dispatched = dispatch2(req);
|
|
3639
3696
|
} catch (err) {
|
|
3640
3697
|
settle2(id, {
|
|
3641
3698
|
ok: false,
|
|
@@ -4964,6 +5021,10 @@ var VfsNodes = class {
|
|
|
4964
5021
|
node.opens++;
|
|
4965
5022
|
return node;
|
|
4966
5023
|
}
|
|
5024
|
+
holds(prefix) {
|
|
5025
|
+
for (const path of this.byPath.keys()) if (within2(path, prefix)) return true;
|
|
5026
|
+
return false;
|
|
5027
|
+
}
|
|
4967
5028
|
closed(node) {
|
|
4968
5029
|
node.opens--;
|
|
4969
5030
|
if (node.opens === 0 && this.byPath.get(node.path) === node) this.byPath.delete(node.path);
|
|
@@ -5141,6 +5202,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
5141
5202
|
"proc-fork",
|
|
5142
5203
|
"proc-kill",
|
|
5143
5204
|
"proc-list",
|
|
5205
|
+
"mount-list",
|
|
5144
5206
|
"proc-exec",
|
|
5145
5207
|
"proc-setpgid",
|
|
5146
5208
|
"proc-getpgid",
|
|
@@ -5184,6 +5246,7 @@ var WasmProcess = class {
|
|
|
5184
5246
|
this.options = options;
|
|
5185
5247
|
this.children = new ChildTable(fds, options.spawner, options.forker);
|
|
5186
5248
|
this.nodes = new VfsNodes(options.fs);
|
|
5249
|
+
options.openFiles?.add(this.nodes);
|
|
5187
5250
|
this.children.onChildState = () => this.signal(SIG.CHLD);
|
|
5188
5251
|
this.children.onReap = options.onReap;
|
|
5189
5252
|
}
|
|
@@ -5595,6 +5658,8 @@ var WasmProcess = class {
|
|
|
5595
5658
|
kind: "json",
|
|
5596
5659
|
json: this.options.processes?.() ?? { boot: 0, processes: [] }
|
|
5597
5660
|
};
|
|
5661
|
+
case "mount-list":
|
|
5662
|
+
return { ok: true, kind: "json", json: this.options.mounts?.() ?? [] };
|
|
5598
5663
|
case "proc-alarm":
|
|
5599
5664
|
this.setAlarm(req.sig, req.firstMs ?? req.ms, req.repeat ? req.ms : 0, req.timer);
|
|
5600
5665
|
return { ok: true, kind: "void" };
|
|
@@ -5652,6 +5717,7 @@ var WasmProcess = class {
|
|
|
5652
5717
|
this.asyncOps.close();
|
|
5653
5718
|
for (const pid of this.children.pids()) this.options.onReap?.(pid);
|
|
5654
5719
|
await this.fds.closeAll();
|
|
5720
|
+
this.options.openFiles?.delete(this.nodes);
|
|
5655
5721
|
await this.options.http?.closeAll();
|
|
5656
5722
|
}
|
|
5657
5723
|
};
|
|
@@ -5699,6 +5765,8 @@ function spawnWasmProcess(opts) {
|
|
|
5699
5765
|
fs: opts.fs,
|
|
5700
5766
|
kill: opts.kill,
|
|
5701
5767
|
processes: opts.processes,
|
|
5768
|
+
openFiles: opts.openFiles,
|
|
5769
|
+
mounts: opts.mounts,
|
|
5702
5770
|
jobs: opts.jobs,
|
|
5703
5771
|
ptys: opts.ptys,
|
|
5704
5772
|
net: opts.net,
|
|
@@ -5711,13 +5779,24 @@ function spawnWasmProcess(opts) {
|
|
|
5711
5779
|
raise: (sig) => signal(sig),
|
|
5712
5780
|
...opts.program.abi === "wasi" ? { pendingBits: () => Atomics.load(header2, SAB_I_SIGNALS) } : {}
|
|
5713
5781
|
});
|
|
5714
|
-
const
|
|
5782
|
+
const holds = /* @__PURE__ */ new Map();
|
|
5783
|
+
opts.held?.add(holds);
|
|
5784
|
+
const token = mintSyncFsToken({
|
|
5785
|
+
fs: opts.fs,
|
|
5786
|
+
cwd: opts.cwd,
|
|
5787
|
+
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
5788
|
+
hold: (path, on) => {
|
|
5789
|
+
const count = (holds.get(path) ?? 0) + (on ? 1 : -1);
|
|
5790
|
+
if (count > 0) holds.set(path, count);
|
|
5791
|
+
else holds.delete(path);
|
|
5792
|
+
}
|
|
5793
|
+
});
|
|
5715
5794
|
const worker = opts.createWorker();
|
|
5716
|
-
const
|
|
5795
|
+
const dispatch2 = async (req) => {
|
|
5717
5796
|
if (isWasmSyscall(req)) return process.syscall(req);
|
|
5718
5797
|
return dispatchSyncFs(req);
|
|
5719
5798
|
};
|
|
5720
|
-
const responder = attachSyncSabResponder(worker, sab, token, { dispatch });
|
|
5799
|
+
const responder = attachSyncSabResponder(worker, sab, token, { dispatch: dispatch2 });
|
|
5721
5800
|
let settle2;
|
|
5722
5801
|
const exited = new Promise((resolve) => settle2 = resolve);
|
|
5723
5802
|
let done4 = false;
|
|
@@ -5731,6 +5810,7 @@ function spawnWasmProcess(opts) {
|
|
|
5731
5810
|
for (const tid of [...threads.keys()]) endThread(tid);
|
|
5732
5811
|
responder.dispose();
|
|
5733
5812
|
revokeSyncFsToken(token);
|
|
5813
|
+
opts.held?.delete(holds);
|
|
5734
5814
|
worker.terminate();
|
|
5735
5815
|
void process.exit().then(
|
|
5736
5816
|
() => settle2(code),
|
|
@@ -5770,7 +5850,7 @@ function spawnWasmProcess(opts) {
|
|
|
5770
5850
|
}
|
|
5771
5851
|
const tsab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
|
|
5772
5852
|
const tw = opts.createWorker();
|
|
5773
|
-
const tresponder = attachSyncSabResponder(tw, tsab, token, { dispatch });
|
|
5853
|
+
const tresponder = attachSyncSabResponder(tw, tsab, token, { dispatch: dispatch2 });
|
|
5774
5854
|
const onThreadMessage = (event) => {
|
|
5775
5855
|
const data = event.data;
|
|
5776
5856
|
if (data?.type === WASM_THREAD_EXIT) endThread(thread.tid);
|
|
@@ -5936,6 +6016,815 @@ var JobTable = class {
|
|
|
5936
6016
|
}
|
|
5937
6017
|
};
|
|
5938
6018
|
|
|
6019
|
+
// src/mount/protocol.ts
|
|
6020
|
+
var DRIVER_PROTOCOL = [1, 0];
|
|
6021
|
+
function driverVersionError(theirs) {
|
|
6022
|
+
const major = Array.isArray(theirs) ? theirs[0] : void 0;
|
|
6023
|
+
if (major === DRIVER_PROTOCOL[0]) return void 0;
|
|
6024
|
+
const named = typeof major === "number" ? `${major}.x` : "without a version";
|
|
6025
|
+
return `slicc-kernel driver protocol ${named} is not supported: this side speaks ${DRIVER_PROTOCOL.join(".")}`;
|
|
6026
|
+
}
|
|
6027
|
+
|
|
6028
|
+
// src/mount/driver.ts
|
|
6029
|
+
function fsError2(code, message2 = code) {
|
|
6030
|
+
return Object.assign(new Error(message2), { code });
|
|
6031
|
+
}
|
|
6032
|
+
async function dispatch(h, req) {
|
|
6033
|
+
switch (req.op) {
|
|
6034
|
+
case "getattr":
|
|
6035
|
+
return h.getattr(req.path);
|
|
6036
|
+
case "readdir":
|
|
6037
|
+
return h.readdir(req.path);
|
|
6038
|
+
case "open":
|
|
6039
|
+
return h.open(req.path, {
|
|
6040
|
+
write: req.write,
|
|
6041
|
+
create: req.create,
|
|
6042
|
+
truncate: req.truncate,
|
|
6043
|
+
exclusive: req.exclusive
|
|
6044
|
+
});
|
|
6045
|
+
case "read":
|
|
6046
|
+
return h.read(req.fh, req.offset, req.size);
|
|
6047
|
+
case "write":
|
|
6048
|
+
return h.write(req.fh, req.offset, req.bytes);
|
|
6049
|
+
case "release":
|
|
6050
|
+
return h.release(req.fh);
|
|
6051
|
+
case "mkdir":
|
|
6052
|
+
return h.mkdir(req.path);
|
|
6053
|
+
case "rmdir":
|
|
6054
|
+
return h.rmdir(req.path);
|
|
6055
|
+
case "unlink":
|
|
6056
|
+
return h.unlink(req.path);
|
|
6057
|
+
case "rename":
|
|
6058
|
+
return h.rename(req.from, req.to);
|
|
6059
|
+
case "symlink":
|
|
6060
|
+
if (!h.symlink) throw fsError2("EPERM", "this file system has no symlinks");
|
|
6061
|
+
return h.symlink(req.target, req.path);
|
|
6062
|
+
case "readlink":
|
|
6063
|
+
if (!h.readlink) throw fsError2("EINVAL", `${req.path} is not a symlink`);
|
|
6064
|
+
return h.readlink(req.path);
|
|
6065
|
+
case "setattr":
|
|
6066
|
+
return h.setattr?.(req.path, {
|
|
6067
|
+
...req.mode !== void 0 ? { mode: req.mode } : {},
|
|
6068
|
+
...req.mtime !== void 0 ? { mtime: req.mtime } : {}
|
|
6069
|
+
});
|
|
6070
|
+
case "statfs":
|
|
6071
|
+
return h.statfs?.() ?? null;
|
|
6072
|
+
default:
|
|
6073
|
+
throw fsError2("ENOSYS", `unknown driver op ${req.op}`);
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
function serveFilesystem(port, handlers, capabilities = {}) {
|
|
6077
|
+
let open = false;
|
|
6078
|
+
const reply = (message2, transfer = []) => port.postMessage(message2, transfer);
|
|
6079
|
+
async function hello(message2) {
|
|
6080
|
+
const refused = driverVersionError(message2.protocol);
|
|
6081
|
+
if (refused) {
|
|
6082
|
+
reply({ hello: { protocol: DRIVER_PROTOCOL, error: refused, errno: "EPROTO" } });
|
|
6083
|
+
return;
|
|
6084
|
+
}
|
|
6085
|
+
try {
|
|
6086
|
+
const refined = await handlers.mount?.(message2.mount);
|
|
6087
|
+
open = true;
|
|
6088
|
+
reply({
|
|
6089
|
+
hello: { protocol: DRIVER_PROTOCOL, capabilities: { ...capabilities, ...refined } }
|
|
6090
|
+
});
|
|
6091
|
+
} catch (err) {
|
|
6092
|
+
const code = err?.code;
|
|
6093
|
+
reply({
|
|
6094
|
+
hello: {
|
|
6095
|
+
protocol: DRIVER_PROTOCOL,
|
|
6096
|
+
error: err instanceof Error ? err.message : String(err),
|
|
6097
|
+
errno: typeof code === "string" ? code : "EIO"
|
|
6098
|
+
}
|
|
6099
|
+
});
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
port.addEventListener("message", (event) => {
|
|
6103
|
+
const data = event.data;
|
|
6104
|
+
if (data.hello) {
|
|
6105
|
+
void hello(data.hello);
|
|
6106
|
+
return;
|
|
6107
|
+
}
|
|
6108
|
+
if (!open) return;
|
|
6109
|
+
dispatch(handlers, data).then(
|
|
6110
|
+
(result) => {
|
|
6111
|
+
if (result instanceof Uint8Array) {
|
|
6112
|
+
const bytes = result.slice();
|
|
6113
|
+
reply({ id: data.id, result: bytes }, [bytes.buffer]);
|
|
6114
|
+
} else reply({ id: data.id, result: result ?? null });
|
|
6115
|
+
},
|
|
6116
|
+
(err) => {
|
|
6117
|
+
const code = err?.code;
|
|
6118
|
+
reply({
|
|
6119
|
+
id: data.id,
|
|
6120
|
+
errno: typeof code === "string" ? code : "EIO",
|
|
6121
|
+
message: err instanceof Error ? err.message : String(err)
|
|
6122
|
+
});
|
|
6123
|
+
}
|
|
6124
|
+
);
|
|
6125
|
+
});
|
|
6126
|
+
port.start?.();
|
|
6127
|
+
return { invalidate: (paths) => reply({ invalidate: paths }) };
|
|
6128
|
+
}
|
|
6129
|
+
|
|
6130
|
+
// src/mount/connection.ts
|
|
6131
|
+
var DRIVER_TIMEOUT_MS = 3e4;
|
|
6132
|
+
function unref(timer) {
|
|
6133
|
+
timer.unref?.();
|
|
6134
|
+
return timer;
|
|
6135
|
+
}
|
|
6136
|
+
function errnoError(code, message2) {
|
|
6137
|
+
return Object.assign(new Error(`${code}: ${message2}`), { code });
|
|
6138
|
+
}
|
|
6139
|
+
var DriverConnection = class _DriverConnection {
|
|
6140
|
+
capabilities;
|
|
6141
|
+
failure;
|
|
6142
|
+
port;
|
|
6143
|
+
waiting;
|
|
6144
|
+
timeoutMs;
|
|
6145
|
+
nextId = 0;
|
|
6146
|
+
onFail;
|
|
6147
|
+
constructor(port, capabilities, waiting, timeoutMs, onFail) {
|
|
6148
|
+
this.port = port;
|
|
6149
|
+
this.capabilities = capabilities;
|
|
6150
|
+
this.waiting = waiting;
|
|
6151
|
+
this.timeoutMs = timeoutMs;
|
|
6152
|
+
this.onFail = onFail;
|
|
6153
|
+
}
|
|
6154
|
+
static async open(port, mount, options = {}) {
|
|
6155
|
+
const timeoutMs = options.timeoutMs ?? DRIVER_TIMEOUT_MS;
|
|
6156
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
6157
|
+
const greeted = Promise.withResolvers();
|
|
6158
|
+
port.addEventListener("message", (event) => {
|
|
6159
|
+
const data = event.data;
|
|
6160
|
+
if (data.hello) return greeted.resolve(data.hello);
|
|
6161
|
+
if (data.invalidate !== void 0) return options.onInvalidate?.(data.invalidate);
|
|
6162
|
+
const call = waiting.get(data.id);
|
|
6163
|
+
if (!call) return;
|
|
6164
|
+
waiting.delete(data.id);
|
|
6165
|
+
clearTimeout(call.timer);
|
|
6166
|
+
if (data.errno) call.reject(errnoError(data.errno, data.message ?? data.errno));
|
|
6167
|
+
else call.resolve(data.result);
|
|
6168
|
+
});
|
|
6169
|
+
port.start?.();
|
|
6170
|
+
port.postMessage({ hello: { protocol: DRIVER_PROTOCOL, mount } });
|
|
6171
|
+
const timer = unref(
|
|
6172
|
+
setTimeout(
|
|
6173
|
+
() => greeted.reject(errnoError("EIO", `the driver did not answer within ${timeoutMs} ms`)),
|
|
6174
|
+
timeoutMs
|
|
6175
|
+
)
|
|
6176
|
+
);
|
|
6177
|
+
let hello;
|
|
6178
|
+
try {
|
|
6179
|
+
hello = await greeted.promise;
|
|
6180
|
+
} finally {
|
|
6181
|
+
clearTimeout(timer);
|
|
6182
|
+
}
|
|
6183
|
+
const refused = driverVersionError(hello.protocol);
|
|
6184
|
+
if (refused) throw errnoError("EPROTO", refused);
|
|
6185
|
+
if (hello.error) throw errnoError(hello.errno ?? "EIO", hello.error);
|
|
6186
|
+
return new _DriverConnection(port, hello.capabilities ?? {}, waiting, timeoutMs, options.onFail);
|
|
6187
|
+
}
|
|
6188
|
+
call(call, transfer = []) {
|
|
6189
|
+
if (this.failure) return Promise.reject(this.failure);
|
|
6190
|
+
const id = ++this.nextId;
|
|
6191
|
+
return new Promise((resolve, reject) => {
|
|
6192
|
+
const timer = unref(
|
|
6193
|
+
setTimeout(() => {
|
|
6194
|
+
this.waiting.delete(id);
|
|
6195
|
+
const error = errnoError(
|
|
6196
|
+
"EIO",
|
|
6197
|
+
`the driver did not answer ${call.op} within ${this.timeoutMs} ms`
|
|
6198
|
+
);
|
|
6199
|
+
reject(error);
|
|
6200
|
+
this.fail(error);
|
|
6201
|
+
this.onFail?.(error);
|
|
6202
|
+
}, this.timeoutMs)
|
|
6203
|
+
);
|
|
6204
|
+
this.waiting.set(id, { resolve, reject, timer });
|
|
6205
|
+
this.port.postMessage({ ...call, id }, transfer);
|
|
6206
|
+
});
|
|
6207
|
+
}
|
|
6208
|
+
fail(error) {
|
|
6209
|
+
if (this.failure) return;
|
|
6210
|
+
this.failure = error;
|
|
6211
|
+
for (const call of this.waiting.values()) {
|
|
6212
|
+
clearTimeout(call.timer);
|
|
6213
|
+
call.reject(error);
|
|
6214
|
+
}
|
|
6215
|
+
this.waiting.clear();
|
|
6216
|
+
}
|
|
6217
|
+
close() {
|
|
6218
|
+
this.fail(errnoError("EIO", "the file system is unmounted"));
|
|
6219
|
+
this.port.close?.();
|
|
6220
|
+
}
|
|
6221
|
+
};
|
|
6222
|
+
|
|
6223
|
+
// src/mount/mount-fs.ts
|
|
6224
|
+
var MAX_LINKS4 = 40;
|
|
6225
|
+
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
6226
|
+
var UNITS = { "": 1, k: 1024, m: 1024 ** 2, g: 1024 ** 3, t: 1024 ** 4 };
|
|
6227
|
+
function parseSize(text) {
|
|
6228
|
+
const match = /^(\d+(?:\.\d+)?)\s*([kmgt]?)i?b?$/i.exec(text.trim());
|
|
6229
|
+
if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
|
|
6230
|
+
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
6231
|
+
}
|
|
6232
|
+
var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
6233
|
+
function heldUnder(held, target) {
|
|
6234
|
+
for (const paths of held) for (const path of paths.keys()) if (within3(path, target)) return true;
|
|
6235
|
+
return false;
|
|
6236
|
+
}
|
|
6237
|
+
var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6238
|
+
function statOf(attr, path, dev) {
|
|
6239
|
+
const mtime = new Date(attr.mtime);
|
|
6240
|
+
const fallback = attr.kind === "directory" ? 16877 : attr.kind === "symlink" ? 41471 : 33188;
|
|
6241
|
+
return {
|
|
6242
|
+
isFile: attr.kind === "file",
|
|
6243
|
+
isDirectory: attr.kind === "directory",
|
|
6244
|
+
isSymbolicLink: attr.kind === "symlink",
|
|
6245
|
+
size: attr.size,
|
|
6246
|
+
mode: attr.mode ?? fallback,
|
|
6247
|
+
mtime,
|
|
6248
|
+
atime: mtime,
|
|
6249
|
+
ctime: mtime,
|
|
6250
|
+
ino: attr.ino ?? inodeOf(path),
|
|
6251
|
+
dev
|
|
6252
|
+
};
|
|
6253
|
+
}
|
|
6254
|
+
function concat4(chunks, total) {
|
|
6255
|
+
if (chunks.length === 1 && chunks[0]?.length === total) return chunks[0];
|
|
6256
|
+
const out = new Uint8Array(total);
|
|
6257
|
+
let at = 0;
|
|
6258
|
+
for (const chunk2 of chunks) {
|
|
6259
|
+
out.set(chunk2, at);
|
|
6260
|
+
at += chunk2.length;
|
|
6261
|
+
}
|
|
6262
|
+
return out;
|
|
6263
|
+
}
|
|
6264
|
+
var MountTable = class {
|
|
6265
|
+
mounts = /* @__PURE__ */ new Map();
|
|
6266
|
+
devices = 0;
|
|
6267
|
+
pending = /* @__PURE__ */ new Set();
|
|
6268
|
+
deps;
|
|
6269
|
+
constructor(deps) {
|
|
6270
|
+
this.deps = deps;
|
|
6271
|
+
}
|
|
6272
|
+
list() {
|
|
6273
|
+
return [...this.mounts.values()].map(({ type, source, target, options, state, error }) => ({
|
|
6274
|
+
type,
|
|
6275
|
+
source,
|
|
6276
|
+
target,
|
|
6277
|
+
options: { ...options },
|
|
6278
|
+
state,
|
|
6279
|
+
...error ? { error } : {}
|
|
6280
|
+
}));
|
|
6281
|
+
}
|
|
6282
|
+
locate(path) {
|
|
6283
|
+
let best;
|
|
6284
|
+
for (const mount of this.mounts.values()) {
|
|
6285
|
+
if (within3(path, mount.target) && (!best || mount.target.length > best.target.length))
|
|
6286
|
+
best = mount;
|
|
6287
|
+
}
|
|
6288
|
+
if (!best) return void 0;
|
|
6289
|
+
const rel = best.target === "/" ? path : path.slice(best.target.length);
|
|
6290
|
+
return { mount: best, rel: rel || "/" };
|
|
6291
|
+
}
|
|
6292
|
+
async mount(spec, fs) {
|
|
6293
|
+
const target = normalizePath(spec.target);
|
|
6294
|
+
if (this.mounts.has(target) || this.pending.has(target)) {
|
|
6295
|
+
throw errnoError("EBUSY", `${target} is already mounted`);
|
|
6296
|
+
}
|
|
6297
|
+
this.pending.add(target);
|
|
6298
|
+
try {
|
|
6299
|
+
return await this.attach(spec, target, fs);
|
|
6300
|
+
} finally {
|
|
6301
|
+
this.pending.delete(target);
|
|
6302
|
+
}
|
|
6303
|
+
}
|
|
6304
|
+
async attach(spec, target, fs) {
|
|
6305
|
+
const st = await fs.stat(target);
|
|
6306
|
+
if (!st.isDirectory) throw errnoError("ENOTDIR", target);
|
|
6307
|
+
const options = { ...spec.options };
|
|
6308
|
+
const opened2 = await this.deps.open(spec.type, { ...spec, target, options });
|
|
6309
|
+
let mounted;
|
|
6310
|
+
const started = Promise.withResolvers();
|
|
6311
|
+
opened2.onCrash?.((error) => {
|
|
6312
|
+
if (mounted) this.crashed(mounted, error);
|
|
6313
|
+
else
|
|
6314
|
+
started.reject(
|
|
6315
|
+
errnoError("EIO", `the ${spec.type} driver failed to start: ${error.message}`)
|
|
6316
|
+
);
|
|
6317
|
+
});
|
|
6318
|
+
let conn;
|
|
6319
|
+
try {
|
|
6320
|
+
conn = await Promise.race([
|
|
6321
|
+
DriverConnection.open(
|
|
6322
|
+
opened2.port,
|
|
6323
|
+
{ source: spec.source, options },
|
|
6324
|
+
{
|
|
6325
|
+
...this.deps.timeoutMs ? { timeoutMs: this.deps.timeoutMs } : {},
|
|
6326
|
+
onInvalidate: (paths) => this.invalidated(target, paths),
|
|
6327
|
+
onFail: (error) => {
|
|
6328
|
+
if (mounted) this.crashed(mounted, error);
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6331
|
+
),
|
|
6332
|
+
started.promise
|
|
6333
|
+
]);
|
|
6334
|
+
} catch (err) {
|
|
6335
|
+
opened2.dispose();
|
|
6336
|
+
throw err;
|
|
6337
|
+
}
|
|
6338
|
+
const caps = conn.capabilities;
|
|
6339
|
+
const mount = {
|
|
6340
|
+
type: spec.type,
|
|
6341
|
+
source: spec.source,
|
|
6342
|
+
target,
|
|
6343
|
+
options,
|
|
6344
|
+
state: "ok",
|
|
6345
|
+
dev: 256 + ++this.devices,
|
|
6346
|
+
conn,
|
|
6347
|
+
caps,
|
|
6348
|
+
maxFile: options.maxfile !== void 0 ? parseSize(options.maxfile) : caps.maxFile ?? 0,
|
|
6349
|
+
attrs: /* @__PURE__ */ new Map(),
|
|
6350
|
+
lists: /* @__PURE__ */ new Map(),
|
|
6351
|
+
dispose: () => opened2.dispose()
|
|
6352
|
+
};
|
|
6353
|
+
mounted = mount;
|
|
6354
|
+
this.mounts.set(target, mount);
|
|
6355
|
+
return this.list().find((m) => m.target === target);
|
|
6356
|
+
}
|
|
6357
|
+
umount(target) {
|
|
6358
|
+
const at = normalizePath(target);
|
|
6359
|
+
const mount = this.mounts.get(at);
|
|
6360
|
+
if (!mount) throw errnoError("EINVAL", `${at} is not mounted`);
|
|
6361
|
+
if (this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
|
|
6362
|
+
this.mounts.delete(at);
|
|
6363
|
+
mount.conn.close();
|
|
6364
|
+
mount.dispose();
|
|
6365
|
+
}
|
|
6366
|
+
crashed(mount, error) {
|
|
6367
|
+
mount.state = "failed";
|
|
6368
|
+
mount.attrs.clear();
|
|
6369
|
+
mount.lists.clear();
|
|
6370
|
+
mount.error = error.message;
|
|
6371
|
+
mount.conn.fail(
|
|
6372
|
+
errnoError("EIO", `the ${mount.type} driver for ${mount.target} failed: ${error.message}`)
|
|
6373
|
+
);
|
|
6374
|
+
}
|
|
6375
|
+
invalidated(target, paths) {
|
|
6376
|
+
const mount = this.mounts.get(target);
|
|
6377
|
+
if (!mount) return;
|
|
6378
|
+
if (paths === true) {
|
|
6379
|
+
mount.attrs.clear();
|
|
6380
|
+
mount.lists.clear();
|
|
6381
|
+
this.deps.changed?.(target);
|
|
6382
|
+
return;
|
|
6383
|
+
}
|
|
6384
|
+
const full = paths.map((p) => normalizePath(`${target}/${p}`));
|
|
6385
|
+
for (const path of full) this.forget(mount, path);
|
|
6386
|
+
this.deps.changed?.(...full);
|
|
6387
|
+
}
|
|
6388
|
+
forget(mount, path) {
|
|
6389
|
+
for (const key of [...mount.attrs.keys()]) if (within3(key, path)) mount.attrs.delete(key);
|
|
6390
|
+
mount.lists.delete(path);
|
|
6391
|
+
mount.lists.delete(parentOf4(path));
|
|
6392
|
+
for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
|
|
6393
|
+
}
|
|
6394
|
+
async getattr(mount, path, rel) {
|
|
6395
|
+
if (mount.conn.failure) throw mount.conn.failure;
|
|
6396
|
+
const cached = mount.attrs.get(path);
|
|
6397
|
+
if (cached && cached.expires > Date.now()) return cached.value;
|
|
6398
|
+
const attr = await mount.conn.call({ op: "getattr", path: rel });
|
|
6399
|
+
const ttl = mount.caps.attrTtl ?? 1e3;
|
|
6400
|
+
if (ttl > 0) mount.attrs.set(path, { value: attr, expires: Date.now() + ttl });
|
|
6401
|
+
return attr;
|
|
6402
|
+
}
|
|
6403
|
+
async readdir(mount, path, rel) {
|
|
6404
|
+
if (mount.conn.failure) throw mount.conn.failure;
|
|
6405
|
+
const cached = mount.lists.get(path);
|
|
6406
|
+
if (cached && cached.expires > Date.now()) return cached.value;
|
|
6407
|
+
const entries = await mount.conn.call({ op: "readdir", path: rel });
|
|
6408
|
+
const ttl = mount.caps.entryTtl ?? mount.caps.attrTtl ?? 1e3;
|
|
6409
|
+
if (ttl > 0) mount.lists.set(path, { value: entries, expires: Date.now() + ttl });
|
|
6410
|
+
return entries;
|
|
6411
|
+
}
|
|
6412
|
+
async statfs(path) {
|
|
6413
|
+
const found = this.locate(normalizePath(path));
|
|
6414
|
+
if (!found) return void 0;
|
|
6415
|
+
const st = await found.mount.conn.call({ op: "statfs" });
|
|
6416
|
+
if (!st) return { quota: 0, usage: 0 };
|
|
6417
|
+
return { quota: st.blocks * st.bsize, usage: (st.blocks - st.bfree) * st.bsize };
|
|
6418
|
+
}
|
|
6419
|
+
wrap(base) {
|
|
6420
|
+
return new MountFs(this, base);
|
|
6421
|
+
}
|
|
6422
|
+
};
|
|
6423
|
+
var MountFs = class {
|
|
6424
|
+
table;
|
|
6425
|
+
base;
|
|
6426
|
+
constructor(table, base) {
|
|
6427
|
+
this.table = table;
|
|
6428
|
+
this.base = base;
|
|
6429
|
+
}
|
|
6430
|
+
at(path) {
|
|
6431
|
+
return this.table.locate(normalizePath(path));
|
|
6432
|
+
}
|
|
6433
|
+
writable(mount, path) {
|
|
6434
|
+
if (mount.caps.readonly) throw errnoError("EROFS", path);
|
|
6435
|
+
}
|
|
6436
|
+
async follow(path, hops = 0, create = false) {
|
|
6437
|
+
const found = this.at(path);
|
|
6438
|
+
if (!found) return path;
|
|
6439
|
+
const real = normalizePath(path);
|
|
6440
|
+
let attr;
|
|
6441
|
+
try {
|
|
6442
|
+
attr = await this.table.getattr(found.mount, real, found.rel);
|
|
6443
|
+
} catch (err) {
|
|
6444
|
+
if (create && err.code === "ENOENT") return real;
|
|
6445
|
+
throw err;
|
|
6446
|
+
}
|
|
6447
|
+
if (attr.kind !== "symlink") return real;
|
|
6448
|
+
if (hops >= MAX_LINKS4) throw errnoError("ELOOP", path);
|
|
6449
|
+
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
6450
|
+
return this.follow(this.base.resolvePath(parentOf4(real), target), hops + 1, create);
|
|
6451
|
+
}
|
|
6452
|
+
async open(mount, rel, write) {
|
|
6453
|
+
return await mount.conn.call({
|
|
6454
|
+
op: "open",
|
|
6455
|
+
path: rel,
|
|
6456
|
+
write,
|
|
6457
|
+
create: write,
|
|
6458
|
+
truncate: write,
|
|
6459
|
+
exclusive: false
|
|
6460
|
+
});
|
|
6461
|
+
}
|
|
6462
|
+
resolvePath(base, path) {
|
|
6463
|
+
return this.base.resolvePath(base, path);
|
|
6464
|
+
}
|
|
6465
|
+
async readFile(path) {
|
|
6466
|
+
return new TextDecoder().decode(await this.readFileBuffer(path));
|
|
6467
|
+
}
|
|
6468
|
+
async readFileBuffer(path) {
|
|
6469
|
+
const real = await this.follow(path);
|
|
6470
|
+
const found = this.at(real);
|
|
6471
|
+
if (!found) return this.base.readFileBuffer(real);
|
|
6472
|
+
const { mount, rel } = found;
|
|
6473
|
+
const attr = await this.table.getattr(mount, real, rel);
|
|
6474
|
+
if (attr.kind === "directory") throw errnoError("EISDIR", real);
|
|
6475
|
+
if (mount.maxFile > 0 && attr.size > mount.maxFile) {
|
|
6476
|
+
throw errnoError("EFBIG", `${real} is larger than this mount's maxfile (${mount.maxFile})`);
|
|
6477
|
+
}
|
|
6478
|
+
const fh = await this.open(mount, rel, false);
|
|
6479
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
6480
|
+
const chunks = [];
|
|
6481
|
+
let total = 0;
|
|
6482
|
+
try {
|
|
6483
|
+
for (; ; ) {
|
|
6484
|
+
const bytes = await mount.conn.call({
|
|
6485
|
+
op: "read",
|
|
6486
|
+
fh,
|
|
6487
|
+
offset: total,
|
|
6488
|
+
size: chunk2
|
|
6489
|
+
});
|
|
6490
|
+
if (bytes.length === 0) break;
|
|
6491
|
+
chunks.push(bytes);
|
|
6492
|
+
total += bytes.length;
|
|
6493
|
+
if (bytes.length < chunk2 && total >= attr.size) break;
|
|
6494
|
+
}
|
|
6495
|
+
} finally {
|
|
6496
|
+
await mount.conn.call({ op: "release", fh });
|
|
6497
|
+
}
|
|
6498
|
+
return concat4(chunks, total);
|
|
6499
|
+
}
|
|
6500
|
+
async writeFile(path, content) {
|
|
6501
|
+
const real = await this.follow(path, 0, true);
|
|
6502
|
+
const found = this.at(real);
|
|
6503
|
+
if (!found) return this.base.writeFile(real, content);
|
|
6504
|
+
const { mount, rel } = found;
|
|
6505
|
+
this.writable(mount, real);
|
|
6506
|
+
const bytes = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
6507
|
+
if (mount.maxFile > 0 && bytes.length > mount.maxFile) {
|
|
6508
|
+
throw errnoError(
|
|
6509
|
+
"EFBIG",
|
|
6510
|
+
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
6511
|
+
);
|
|
6512
|
+
}
|
|
6513
|
+
this.table.forget(mount, real);
|
|
6514
|
+
const fh = await this.open(mount, rel, true);
|
|
6515
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
6516
|
+
try {
|
|
6517
|
+
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
6518
|
+
await mount.conn.call({
|
|
6519
|
+
op: "write",
|
|
6520
|
+
fh,
|
|
6521
|
+
offset,
|
|
6522
|
+
bytes: bytes.slice(offset, offset + chunk2)
|
|
6523
|
+
});
|
|
6524
|
+
}
|
|
6525
|
+
} finally {
|
|
6526
|
+
await mount.conn.call({ op: "release", fh });
|
|
6527
|
+
this.table.forget(mount, real);
|
|
6528
|
+
}
|
|
6529
|
+
}
|
|
6530
|
+
async exists(path) {
|
|
6531
|
+
if (!this.at(path)) return this.base.exists(path);
|
|
6532
|
+
try {
|
|
6533
|
+
await this.stat(path);
|
|
6534
|
+
return true;
|
|
6535
|
+
} catch (err) {
|
|
6536
|
+
const code = err.code;
|
|
6537
|
+
if (code === "ENOENT" || code === "ENOTDIR") return false;
|
|
6538
|
+
throw err;
|
|
6539
|
+
}
|
|
6540
|
+
}
|
|
6541
|
+
async lstat(path) {
|
|
6542
|
+
const found = this.at(path);
|
|
6543
|
+
if (!found) return this.base.lstat(path);
|
|
6544
|
+
const real = normalizePath(path);
|
|
6545
|
+
return statOf(await this.table.getattr(found.mount, real, found.rel), real, found.mount.dev);
|
|
6546
|
+
}
|
|
6547
|
+
async stat(path) {
|
|
6548
|
+
const real = await this.follow(path);
|
|
6549
|
+
return this.at(real) ? this.lstat(real) : this.base.stat(real);
|
|
6550
|
+
}
|
|
6551
|
+
async entries(path) {
|
|
6552
|
+
const real = await this.follow(path);
|
|
6553
|
+
const found = this.at(real);
|
|
6554
|
+
if (!found) throw errnoError("ENOTDIR", real);
|
|
6555
|
+
return {
|
|
6556
|
+
real,
|
|
6557
|
+
mount: found.mount,
|
|
6558
|
+
list: await this.table.readdir(found.mount, real, found.rel)
|
|
6559
|
+
};
|
|
6560
|
+
}
|
|
6561
|
+
async readdir(path) {
|
|
6562
|
+
if (!this.at(path)) return this.base.readdir(path);
|
|
6563
|
+
return (await this.entries(path)).list.map((e) => e.name);
|
|
6564
|
+
}
|
|
6565
|
+
async readdirStat(path) {
|
|
6566
|
+
if (!this.at(path)) return this.base.readdirStat(path);
|
|
6567
|
+
const { real, mount, list } = await this.entries(path);
|
|
6568
|
+
return Promise.all(
|
|
6569
|
+
list.map(async (entry) => {
|
|
6570
|
+
const child = normalizePath(`${real}/${entry.name}`);
|
|
6571
|
+
if (entry.attr && mount.caps.listingStats) {
|
|
6572
|
+
return [entry.name, statOf(entry.attr, child, mount.dev)];
|
|
6573
|
+
}
|
|
6574
|
+
return [entry.name, await this.lstat(child).catch(() => null)];
|
|
6575
|
+
})
|
|
6576
|
+
);
|
|
6577
|
+
}
|
|
6578
|
+
async mkdir(path, options = {}) {
|
|
6579
|
+
const found = this.at(path);
|
|
6580
|
+
if (!found) return this.base.mkdir(path, options);
|
|
6581
|
+
const { mount, rel } = found;
|
|
6582
|
+
const real = normalizePath(path);
|
|
6583
|
+
this.writable(mount, real);
|
|
6584
|
+
if (!options.recursive) {
|
|
6585
|
+
if (rel === "/") throw errnoError("EEXIST", real);
|
|
6586
|
+
this.table.forget(mount, real);
|
|
6587
|
+
await mount.conn.call({ op: "mkdir", path: rel });
|
|
6588
|
+
return;
|
|
6589
|
+
}
|
|
6590
|
+
const parts = rel.split("/").filter(Boolean);
|
|
6591
|
+
for (let n = 1; n <= parts.length; n++) {
|
|
6592
|
+
const sub = `/${parts.slice(0, n).join("/")}`;
|
|
6593
|
+
const full = normalizePath(`${mount.target}${sub}`);
|
|
6594
|
+
const exists = await this.table.getattr(mount, full, sub).catch(() => void 0);
|
|
6595
|
+
if (exists?.kind === "directory") continue;
|
|
6596
|
+
if (exists) throw errnoError("ENOTDIR", full);
|
|
6597
|
+
this.table.forget(mount, full);
|
|
6598
|
+
await mount.conn.call({ op: "mkdir", path: sub });
|
|
6599
|
+
}
|
|
6600
|
+
}
|
|
6601
|
+
async rm(path, options = {}) {
|
|
6602
|
+
const found = this.at(path);
|
|
6603
|
+
if (!found) return this.base.rm(path, options);
|
|
6604
|
+
const { mount, rel } = found;
|
|
6605
|
+
const real = normalizePath(path);
|
|
6606
|
+
if (rel === "/") throw errnoError("EBUSY", `${real} is a mount point`);
|
|
6607
|
+
this.writable(mount, real);
|
|
6608
|
+
let attr;
|
|
6609
|
+
try {
|
|
6610
|
+
attr = await this.table.getattr(mount, real, rel);
|
|
6611
|
+
} catch (err) {
|
|
6612
|
+
if (options.force && err.code === "ENOENT") return;
|
|
6613
|
+
throw err;
|
|
6614
|
+
}
|
|
6615
|
+
this.table.forget(mount, real);
|
|
6616
|
+
if (attr.kind === "directory" && options.recursive) {
|
|
6617
|
+
for (const entry of await this.table.readdir(mount, real, rel)) {
|
|
6618
|
+
await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
|
|
6619
|
+
}
|
|
6620
|
+
this.table.forget(mount, real);
|
|
6621
|
+
}
|
|
6622
|
+
await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path: rel });
|
|
6623
|
+
this.table.forget(mount, real);
|
|
6624
|
+
}
|
|
6625
|
+
async rename(from, to) {
|
|
6626
|
+
const a = this.at(from);
|
|
6627
|
+
const b = this.at(to);
|
|
6628
|
+
if (!a && !b) return this.base.rename(from, to);
|
|
6629
|
+
if (!a || !b || a.mount !== b.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
|
|
6630
|
+
this.writable(a.mount, normalizePath(to));
|
|
6631
|
+
this.table.forget(a.mount, normalizePath(from));
|
|
6632
|
+
this.table.forget(a.mount, normalizePath(to));
|
|
6633
|
+
try {
|
|
6634
|
+
await a.mount.conn.call({ op: "rename", from: a.rel, to: b.rel });
|
|
6635
|
+
} finally {
|
|
6636
|
+
this.table.forget(a.mount, normalizePath(from));
|
|
6637
|
+
this.table.forget(a.mount, normalizePath(to));
|
|
6638
|
+
}
|
|
6639
|
+
}
|
|
6640
|
+
async symlink(target, path) {
|
|
6641
|
+
const found = this.at(path);
|
|
6642
|
+
if (!found) return this.base.symlink(target, path);
|
|
6643
|
+
this.writable(found.mount, normalizePath(path));
|
|
6644
|
+
if (!found.mount.caps.symlinks)
|
|
6645
|
+
throw errnoError("EPERM", `${path}: this file system has no symlinks`);
|
|
6646
|
+
this.table.forget(found.mount, normalizePath(path));
|
|
6647
|
+
await found.mount.conn.call({ op: "symlink", target, path: found.rel });
|
|
6648
|
+
}
|
|
6649
|
+
async readlink(path) {
|
|
6650
|
+
const found = this.at(path);
|
|
6651
|
+
if (!found) return this.base.readlink(path);
|
|
6652
|
+
return await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
6653
|
+
}
|
|
6654
|
+
async chmod(path, mode) {
|
|
6655
|
+
const found = this.at(path);
|
|
6656
|
+
if (!found) return this.base.chmod(path, mode);
|
|
6657
|
+
this.writable(found.mount, normalizePath(path));
|
|
6658
|
+
if (!found.mount.caps.chmod) return;
|
|
6659
|
+
this.table.forget(found.mount, normalizePath(path));
|
|
6660
|
+
await found.mount.conn.call({ op: "setattr", path: found.rel, mode });
|
|
6661
|
+
}
|
|
6662
|
+
async utimes(path, atime, mtime) {
|
|
6663
|
+
const found = this.at(path);
|
|
6664
|
+
if (!found) return this.base.utimes(path, atime, mtime);
|
|
6665
|
+
this.writable(found.mount, normalizePath(path));
|
|
6666
|
+
this.table.forget(found.mount, normalizePath(path));
|
|
6667
|
+
await found.mount.conn.call({ op: "setattr", path: found.rel, mtime: mtime.getTime() });
|
|
6668
|
+
}
|
|
6669
|
+
};
|
|
6670
|
+
|
|
6671
|
+
// src/mount/tmpfs.ts
|
|
6672
|
+
function tmpfs() {
|
|
6673
|
+
let inodes = 0;
|
|
6674
|
+
const make = (kind, mode) => ({
|
|
6675
|
+
kind,
|
|
6676
|
+
ino: ++inodes,
|
|
6677
|
+
mode,
|
|
6678
|
+
mtime: Date.now(),
|
|
6679
|
+
data: new Uint8Array(0),
|
|
6680
|
+
target: "",
|
|
6681
|
+
children: /* @__PURE__ */ new Map()
|
|
6682
|
+
});
|
|
6683
|
+
const root = make("directory", 16877);
|
|
6684
|
+
const handles = /* @__PURE__ */ new Map();
|
|
6685
|
+
let nextFh = 0;
|
|
6686
|
+
const parts = (path) => path.split("/").filter(Boolean);
|
|
6687
|
+
function find(path) {
|
|
6688
|
+
let node = root;
|
|
6689
|
+
for (const name2 of parts(path)) {
|
|
6690
|
+
if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
|
|
6691
|
+
const next = node.children.get(name2);
|
|
6692
|
+
if (!next) throw fsError2("ENOENT", path);
|
|
6693
|
+
node = next;
|
|
6694
|
+
}
|
|
6695
|
+
return node;
|
|
6696
|
+
}
|
|
6697
|
+
function parent(path) {
|
|
6698
|
+
const names = parts(path);
|
|
6699
|
+
const name2 = names.pop();
|
|
6700
|
+
if (name2 === void 0) throw fsError2("EBUSY", "the root of a mount");
|
|
6701
|
+
const dir = find(`/${names.join("/")}`);
|
|
6702
|
+
if (dir.kind !== "directory") throw fsError2("ENOTDIR", path);
|
|
6703
|
+
return { dir, name: name2 };
|
|
6704
|
+
}
|
|
6705
|
+
const attr = (node) => ({
|
|
6706
|
+
kind: node.kind,
|
|
6707
|
+
size: node.kind === "file" ? node.data.length : node.kind === "symlink" ? node.target.length : 4096,
|
|
6708
|
+
mtime: node.mtime,
|
|
6709
|
+
mode: node.mode,
|
|
6710
|
+
ino: node.ino
|
|
6711
|
+
});
|
|
6712
|
+
const handle = (fh) => {
|
|
6713
|
+
const found = handles.get(fh);
|
|
6714
|
+
if (!found) throw fsError2("EBADF", `handle ${fh}`);
|
|
6715
|
+
return found;
|
|
6716
|
+
};
|
|
6717
|
+
function opened2(path, flags) {
|
|
6718
|
+
const { dir, name: name2 } = parent(path);
|
|
6719
|
+
const existing = dir.children.get(name2);
|
|
6720
|
+
if (existing && flags.exclusive) throw fsError2("EEXIST", path);
|
|
6721
|
+
if (existing?.kind === "directory" && flags.write) throw fsError2("EISDIR", path);
|
|
6722
|
+
if (existing) return existing;
|
|
6723
|
+
if (!flags.create) throw fsError2("ENOENT", path);
|
|
6724
|
+
const node = make("file", 33188);
|
|
6725
|
+
dir.children.set(name2, node);
|
|
6726
|
+
dir.mtime = Date.now();
|
|
6727
|
+
return node;
|
|
6728
|
+
}
|
|
6729
|
+
return {
|
|
6730
|
+
async getattr(path) {
|
|
6731
|
+
return attr(find(path));
|
|
6732
|
+
},
|
|
6733
|
+
async readdir(path) {
|
|
6734
|
+
const dir = find(path);
|
|
6735
|
+
if (dir.kind !== "directory") throw fsError2("ENOTDIR", path);
|
|
6736
|
+
return [...dir.children].map(
|
|
6737
|
+
([name2, node]) => ({ name: name2, kind: node.kind, attr: attr(node) })
|
|
6738
|
+
);
|
|
6739
|
+
},
|
|
6740
|
+
async open(path, flags) {
|
|
6741
|
+
const node = opened2(path, flags);
|
|
6742
|
+
const data = flags.truncate ? new Uint8Array(0) : node.data.slice();
|
|
6743
|
+
handles.set(++nextFh, { node, data, length: data.length, dirty: flags.truncate });
|
|
6744
|
+
return nextFh;
|
|
6745
|
+
},
|
|
6746
|
+
async read(fh, offset, size) {
|
|
6747
|
+
const h = handle(fh);
|
|
6748
|
+
return h.data.slice(offset, Math.min(h.length, offset + size));
|
|
6749
|
+
},
|
|
6750
|
+
async write(fh, offset, bytes) {
|
|
6751
|
+
const h = handle(fh);
|
|
6752
|
+
const end = offset + bytes.length;
|
|
6753
|
+
if (end > h.data.length) {
|
|
6754
|
+
const grown = new Uint8Array(Math.max(end, h.data.length * 2));
|
|
6755
|
+
grown.set(h.data.subarray(0, h.length));
|
|
6756
|
+
h.data = grown;
|
|
6757
|
+
}
|
|
6758
|
+
h.data.set(bytes, offset);
|
|
6759
|
+
h.length = Math.max(h.length, end);
|
|
6760
|
+
h.dirty = true;
|
|
6761
|
+
},
|
|
6762
|
+
async release(fh) {
|
|
6763
|
+
const h = handle(fh);
|
|
6764
|
+
handles.delete(fh);
|
|
6765
|
+
if (!h.dirty) return;
|
|
6766
|
+
h.node.data = h.data.slice(0, h.length);
|
|
6767
|
+
h.node.mtime = Date.now();
|
|
6768
|
+
},
|
|
6769
|
+
async mkdir(path) {
|
|
6770
|
+
const { dir, name: name2 } = parent(path);
|
|
6771
|
+
if (dir.children.has(name2)) throw fsError2("EEXIST", path);
|
|
6772
|
+
dir.children.set(name2, make("directory", 16877));
|
|
6773
|
+
},
|
|
6774
|
+
async rmdir(path) {
|
|
6775
|
+
const { dir, name: name2 } = parent(path);
|
|
6776
|
+
const node = dir.children.get(name2);
|
|
6777
|
+
if (!node) throw fsError2("ENOENT", path);
|
|
6778
|
+
if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
|
|
6779
|
+
if (node.children.size > 0) throw fsError2("ENOTEMPTY", path);
|
|
6780
|
+
dir.children.delete(name2);
|
|
6781
|
+
},
|
|
6782
|
+
async unlink(path) {
|
|
6783
|
+
const { dir, name: name2 } = parent(path);
|
|
6784
|
+
const node = dir.children.get(name2);
|
|
6785
|
+
if (!node) throw fsError2("ENOENT", path);
|
|
6786
|
+
if (node.kind === "directory") throw fsError2("EISDIR", path);
|
|
6787
|
+
dir.children.delete(name2);
|
|
6788
|
+
},
|
|
6789
|
+
async rename(from, to) {
|
|
6790
|
+
const a = `/${parts(from).join("/")}`;
|
|
6791
|
+
const b = `/${parts(to).join("/")}`;
|
|
6792
|
+
if (b !== a && b.startsWith(`${a}/`)) throw fsError2("EINVAL", `${to} is inside ${from}`);
|
|
6793
|
+
const source = parent(from);
|
|
6794
|
+
const node = source.dir.children.get(source.name);
|
|
6795
|
+
if (!node) throw fsError2("ENOENT", from);
|
|
6796
|
+
const target = parent(to);
|
|
6797
|
+
const replaced = target.dir.children.get(target.name);
|
|
6798
|
+
if (replaced === node) return;
|
|
6799
|
+
if (replaced?.kind === "directory" && (node.kind !== "directory" || replaced.children.size > 0)) {
|
|
6800
|
+
throw fsError2(node.kind === "directory" ? "ENOTEMPTY" : "EISDIR", to);
|
|
6801
|
+
}
|
|
6802
|
+
source.dir.children.delete(source.name);
|
|
6803
|
+
target.dir.children.set(target.name, node);
|
|
6804
|
+
},
|
|
6805
|
+
async symlink(target, path) {
|
|
6806
|
+
const { dir, name: name2 } = parent(path);
|
|
6807
|
+
if (dir.children.has(name2)) throw fsError2("EEXIST", path);
|
|
6808
|
+
const node = make("symlink", 41471);
|
|
6809
|
+
node.target = target;
|
|
6810
|
+
dir.children.set(name2, node);
|
|
6811
|
+
},
|
|
6812
|
+
async readlink(path) {
|
|
6813
|
+
const node = find(path);
|
|
6814
|
+
if (node.kind !== "symlink") throw fsError2("EINVAL", path);
|
|
6815
|
+
return node.target;
|
|
6816
|
+
},
|
|
6817
|
+
async setattr(path, change) {
|
|
6818
|
+
const node = find(path);
|
|
6819
|
+
if (change.mode !== void 0) node.mode = node.mode & ~4095 | change.mode & 4095;
|
|
6820
|
+
if (change.mtime !== void 0) node.mtime = change.mtime;
|
|
6821
|
+
},
|
|
6822
|
+
async statfs() {
|
|
6823
|
+
return { bsize: 4096, blocks: 262144, bfree: 262144 };
|
|
6824
|
+
}
|
|
6825
|
+
};
|
|
6826
|
+
}
|
|
6827
|
+
|
|
5939
6828
|
// src/process/wasi/wasi-module.ts
|
|
5940
6829
|
function importedMemory(bytes) {
|
|
5941
6830
|
let at = 8;
|
|
@@ -6096,6 +6985,7 @@ function foreignImports(bytes) {
|
|
|
6096
6985
|
}
|
|
6097
6986
|
|
|
6098
6987
|
// src/launcher.ts
|
|
6988
|
+
var PNPM_HOME = "/home/.local/share/pnpm";
|
|
6099
6989
|
var COMMAND2 = /^\/(?:usr\/)?bin\/([^/]+)$/;
|
|
6100
6990
|
var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
|
|
6101
6991
|
var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
@@ -6157,7 +7047,7 @@ function childHandle(handle) {
|
|
|
6157
7047
|
onState: (listener) => handle.onState(listener)
|
|
6158
7048
|
};
|
|
6159
7049
|
}
|
|
6160
|
-
function
|
|
7050
|
+
function concat5(chunks) {
|
|
6161
7051
|
const out = new Uint8Array(chunks.reduce((n, chunk2) => n + chunk2.length, 0));
|
|
6162
7052
|
let offset = 0;
|
|
6163
7053
|
for (const chunk2 of chunks) {
|
|
@@ -6185,12 +7075,24 @@ var Launcher = class {
|
|
|
6185
7075
|
ca;
|
|
6186
7076
|
transport;
|
|
6187
7077
|
watchers = new FsWatchers();
|
|
7078
|
+
pnpmHome;
|
|
7079
|
+
mounts;
|
|
7080
|
+
createDriverWorker;
|
|
7081
|
+
openFiles = /* @__PURE__ */ new Set();
|
|
7082
|
+
held = /* @__PURE__ */ new Set();
|
|
6188
7083
|
nextPid = 1e3;
|
|
6189
7084
|
boot = Date.now();
|
|
6190
7085
|
terminals = 0;
|
|
6191
7086
|
constructor(options) {
|
|
6192
|
-
this.
|
|
7087
|
+
this.mounts = new MountTable({
|
|
7088
|
+
open: (type, spec) => this.driver(type, spec),
|
|
7089
|
+
busy: (target) => [...this.openFiles].some((nodes) => nodes.holds(target)) || heldUnder(this.held, target),
|
|
7090
|
+
changed: this.watchers.changed.bind(this.watchers)
|
|
7091
|
+
});
|
|
7092
|
+
this.base = this.watchers.wrap(this.mounts.wrap(options.fs));
|
|
6193
7093
|
this.createWorker = options.createWorker;
|
|
7094
|
+
this.createDriverWorker = options.createDriverWorker;
|
|
7095
|
+
this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
|
|
6194
7096
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
6195
7097
|
this.env = { ...networkEnv(), ...options.env };
|
|
6196
7098
|
this.ca = kernelCa(options.caStore ?? memoryCaStore());
|
|
@@ -6201,11 +7103,17 @@ var Launcher = class {
|
|
|
6201
7103
|
ca: this.ca
|
|
6202
7104
|
});
|
|
6203
7105
|
this.fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
|
|
7106
|
+
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
7107
|
+
this.catalog = void 0;
|
|
7108
|
+
});
|
|
6204
7109
|
}
|
|
6205
7110
|
commands() {
|
|
6206
|
-
this.catalog ??= scanCommands(this.base,
|
|
7111
|
+
this.catalog ??= this.roots().then((roots) => scanCommands(this.base, roots));
|
|
6207
7112
|
return this.catalog;
|
|
6208
7113
|
}
|
|
7114
|
+
async roots() {
|
|
7115
|
+
return [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
|
|
7116
|
+
}
|
|
6209
7117
|
async resolve(file, argv0, cwd) {
|
|
6210
7118
|
const name2 = COMMAND2.exec(file)?.[1] ?? (file.includes("/") ? void 0 : file);
|
|
6211
7119
|
if (name2 !== void 0) {
|
|
@@ -6359,6 +7267,10 @@ var Launcher = class {
|
|
|
6359
7267
|
forker: this.forker(pid, req),
|
|
6360
7268
|
kill: (target, sig) => this.kill(target, sig),
|
|
6361
7269
|
processes: () => ({ boot: this.boot, processes: this.list() }),
|
|
7270
|
+
openFiles: this.openFiles,
|
|
7271
|
+
held: this.held,
|
|
7272
|
+
statfs: (path) => this.mounts.statfs(path),
|
|
7273
|
+
mounts: () => this.mounts.list(),
|
|
6362
7274
|
jobs: this.jobs,
|
|
6363
7275
|
ptys: this.ptys,
|
|
6364
7276
|
net: this.net,
|
|
@@ -6443,12 +7355,66 @@ var Launcher = class {
|
|
|
6443
7355
|
if (sig !== 0) handle.signal(sig);
|
|
6444
7356
|
return true;
|
|
6445
7357
|
}
|
|
7358
|
+
async driver(type, _spec) {
|
|
7359
|
+
if (type === "tmpfs") {
|
|
7360
|
+
const { port1, port2 } = new MessageChannel();
|
|
7361
|
+
serveFilesystem(port2, tmpfs(), { symlinks: true, chmod: true, attrTtl: 0 });
|
|
7362
|
+
return {
|
|
7363
|
+
port: port1,
|
|
7364
|
+
dispose: () => {
|
|
7365
|
+
port1.close();
|
|
7366
|
+
port2.close();
|
|
7367
|
+
}
|
|
7368
|
+
};
|
|
7369
|
+
}
|
|
7370
|
+
const module = (await scanFilesystems(this.base, await this.roots())).get(type);
|
|
7371
|
+
if (!module) throw fsError("ENODEV", `unknown file system type ${type}`);
|
|
7372
|
+
if (!this.createDriverWorker)
|
|
7373
|
+
throw fsError("ENODEV", `this kernel cannot start ${type} drivers`);
|
|
7374
|
+
const code = await this.base.readFile(module);
|
|
7375
|
+
const worker = this.createDriverWorker();
|
|
7376
|
+
const driver = new MessageChannel();
|
|
7377
|
+
const client = new MessageChannel();
|
|
7378
|
+
const served = serveClient(client.port1, {
|
|
7379
|
+
launcher: async () => this,
|
|
7380
|
+
scope: "transport"
|
|
7381
|
+
});
|
|
7382
|
+
worker.postMessage({ code, driver: driver.port2, client: client.port2 }, [
|
|
7383
|
+
driver.port2,
|
|
7384
|
+
client.port2
|
|
7385
|
+
]);
|
|
7386
|
+
return {
|
|
7387
|
+
port: driver.port1,
|
|
7388
|
+
dispose: () => {
|
|
7389
|
+
worker.terminate();
|
|
7390
|
+
served.detach();
|
|
7391
|
+
driver.port1.close();
|
|
7392
|
+
},
|
|
7393
|
+
onCrash: (listener) => worker.addEventListener("error", (event) => {
|
|
7394
|
+
event.preventDefault?.();
|
|
7395
|
+
listener(new Error(String(event.message)));
|
|
7396
|
+
})
|
|
7397
|
+
};
|
|
7398
|
+
}
|
|
7399
|
+
mount(spec) {
|
|
7400
|
+
return this.mounts.mount(spec, this.fs);
|
|
7401
|
+
}
|
|
7402
|
+
umount(target) {
|
|
7403
|
+
this.mounts.umount(target);
|
|
7404
|
+
}
|
|
6446
7405
|
async prepare() {
|
|
6447
7406
|
for (const dir of SHARED_DIRS) await this.base.mkdir(dir, { recursive: true });
|
|
6448
7407
|
await writeCaFile(this.base, this.ca).catch(() => void 0);
|
|
6449
7408
|
}
|
|
6450
7409
|
environment(cwd, extra) {
|
|
6451
|
-
return {
|
|
7410
|
+
return {
|
|
7411
|
+
PATH: `/usr/bin:/bin:${this.pnpmHome}/bin`,
|
|
7412
|
+
HOME: "/home",
|
|
7413
|
+
PNPM_HOME: this.pnpmHome,
|
|
7414
|
+
...this.env,
|
|
7415
|
+
PWD: cwd,
|
|
7416
|
+
...extra
|
|
7417
|
+
};
|
|
6452
7418
|
}
|
|
6453
7419
|
async starting(argv, dir) {
|
|
6454
7420
|
this.catalog = void 0;
|
|
@@ -6510,7 +7476,7 @@ var Launcher = class {
|
|
|
6510
7476
|
`));
|
|
6511
7477
|
if (!planned) {
|
|
6512
7478
|
report(`${file}: command not found`);
|
|
6513
|
-
return { status: NOT_FOUND, stdout:
|
|
7479
|
+
return { status: NOT_FOUND, stdout: concat5(out), stderr: concat5(err) };
|
|
6514
7480
|
}
|
|
6515
7481
|
const fds = new FdTable();
|
|
6516
7482
|
fds.installAt(0, options.stdin ? bytesSource(options.stdin) : nullFile());
|
|
@@ -6519,7 +7485,7 @@ var Launcher = class {
|
|
|
6519
7485
|
const handle = await this.launch(planned, { env, cwd, fds, report });
|
|
6520
7486
|
options.onStarted?.(handle.pid);
|
|
6521
7487
|
const status = await handle.exited;
|
|
6522
|
-
return { status, stdout:
|
|
7488
|
+
return { status, stdout: concat5(out), stderr: concat5(err) };
|
|
6523
7489
|
}
|
|
6524
7490
|
};
|
|
6525
7491
|
|
|
@@ -6587,30 +7553,39 @@ function serveKernel(port, deps) {
|
|
|
6587
7553
|
terminals.delete(req.id);
|
|
6588
7554
|
}
|
|
6589
7555
|
}
|
|
6590
|
-
async function
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
});
|
|
6606
|
-
await started.prepare();
|
|
6607
|
-
return started;
|
|
7556
|
+
async function init(req) {
|
|
7557
|
+
launcher = (req.root ? Promise.resolve(req.root) : deps.storage()).then(async (root) => {
|
|
7558
|
+
const name2 = req.metadata ?? META_DB;
|
|
7559
|
+
const meta = name2 === false ? void 0 : await deps.metadata?.(name2);
|
|
7560
|
+
remote = req.transport ? new RemoteTransport(port, req.transport) : void 0;
|
|
7561
|
+
const fs = new OpfsFs(root, meta, dirsChannel(name2));
|
|
7562
|
+
await fs.reconcile();
|
|
7563
|
+
const started = new Launcher({
|
|
7564
|
+
fs,
|
|
7565
|
+
createWorker: deps.createWorker,
|
|
7566
|
+
...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
|
|
7567
|
+
...req.modules ? { modules: req.modules } : {},
|
|
7568
|
+
...req.env ? { env: req.env } : {},
|
|
7569
|
+
...remote ? { transport: remote } : {},
|
|
7570
|
+
caStore: caStore(deps.metadata ? name2 : false)
|
|
6608
7571
|
});
|
|
6609
|
-
await
|
|
6610
|
-
return
|
|
6611
|
-
}
|
|
7572
|
+
await started.prepare();
|
|
7573
|
+
return started;
|
|
7574
|
+
});
|
|
7575
|
+
await launcher;
|
|
7576
|
+
return true;
|
|
7577
|
+
}
|
|
7578
|
+
async function mountOp(req) {
|
|
7579
|
+
const l = await ready2();
|
|
7580
|
+
if (req.op === "mount") return l.mount(req.spec);
|
|
7581
|
+
if (req.op === "umount") return l.umount(req.target);
|
|
7582
|
+
return l.mounts.list();
|
|
7583
|
+
}
|
|
7584
|
+
async function handle(req) {
|
|
7585
|
+
if (req.op === "init") return init(req);
|
|
6612
7586
|
if (req.op === "open-terminal") return terminal(req);
|
|
6613
7587
|
if (req.op === "connect") return connect();
|
|
7588
|
+
if (req.op === "mount" || req.op === "umount" || req.op === "mounts") return mountOp(req);
|
|
6614
7589
|
if (req.op === "terminal") {
|
|
6615
7590
|
const session = terminals.get(req.terminal);
|
|
6616
7591
|
if (!session) throw new Error(`no terminal ${req.terminal}`);
|
|
@@ -6638,9 +7613,11 @@ function serveKernel(port, deps) {
|
|
|
6638
7613
|
|
|
6639
7614
|
// src/kernel-worker.ts
|
|
6640
7615
|
var processWorker = pinScript(new URL("./process-worker.js", import.meta.url));
|
|
7616
|
+
var driverWorker = pinScript(new URL("./driver-worker.js", import.meta.url));
|
|
6641
7617
|
serveKernel(globalThis, {
|
|
6642
7618
|
storage: () => navigator.storage.getDirectory(),
|
|
6643
7619
|
createWorker: () => new Worker(processWorker, { type: "module" }),
|
|
7620
|
+
createDriverWorker: () => new Worker(driverWorker, { type: "module" }),
|
|
6644
7621
|
metadata: (name2) => openMeta(name2, indexedDB)
|
|
6645
7622
|
});
|
|
6646
7623
|
//# sourceMappingURL=kernel-worker.js.map
|