@ai-ecoverse/slicc-kernel 1.15.1 → 1.16.1
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 +12 -1
- package/dist/index.js +15 -2
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +491 -71
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +67 -11
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +469 -68
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +67 -11
- package/dist/process-worker.js.map +3 -3
- package/package.json +3 -2
package/dist/kernel-worker.js
CHANGED
|
@@ -76,9 +76,9 @@ var IndexedDbMeta = class {
|
|
|
76
76
|
if (trees.some((tree) => under(key, tree))) this.cache.delete(key);
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
changed(
|
|
80
|
-
this.drop(
|
|
81
|
-
this.channel.postMessage(
|
|
79
|
+
changed(invalidation2) {
|
|
80
|
+
this.drop(invalidation2);
|
|
81
|
+
this.channel.postMessage(invalidation2);
|
|
82
82
|
}
|
|
83
83
|
transaction(mode) {
|
|
84
84
|
const transaction = this.db.transaction(META_STORE, mode);
|
|
@@ -2952,9 +2952,9 @@ var RemoteTransport = class {
|
|
|
2952
2952
|
var NAME = /^[A-Za-z0-9._+-]+$/;
|
|
2953
2953
|
var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2954
2954
|
var PACKAGE = /\$\{package\}/g;
|
|
2955
|
-
function inside(pkg,
|
|
2956
|
-
if (typeof
|
|
2957
|
-
const clean =
|
|
2955
|
+
function inside(pkg, rel2) {
|
|
2956
|
+
if (typeof rel2 !== "string" || rel2 === "") return void 0;
|
|
2957
|
+
const clean = rel2.replace(/^\.\//, "");
|
|
2958
2958
|
if (clean.startsWith("/") || clean.split("/").includes("..")) return void 0;
|
|
2959
2959
|
return `${pkg}/${clean}`;
|
|
2960
2960
|
}
|
|
@@ -3529,7 +3529,8 @@ function statJson(s) {
|
|
|
3529
3529
|
atimeMs: s.atime.getTime(),
|
|
3530
3530
|
ctimeMs: s.ctime.getTime(),
|
|
3531
3531
|
ino: s.ino,
|
|
3532
|
-
...s.dev !== void 0 ? { dev: s.dev } : {}
|
|
3532
|
+
...s.dev !== void 0 ? { dev: s.dev } : {},
|
|
3533
|
+
...s.readonly ? { readonly: true } : {}
|
|
3533
3534
|
};
|
|
3534
3535
|
}
|
|
3535
3536
|
async function readdirStat(fs, dir) {
|
|
@@ -5091,6 +5092,20 @@ var VfsNodes = class {
|
|
|
5091
5092
|
}
|
|
5092
5093
|
}
|
|
5093
5094
|
};
|
|
5095
|
+
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
5096
|
+
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
5097
|
+
if (!writes && !opts.create || !fs.stat) return;
|
|
5098
|
+
const stat = (p) => fs.stat?.(p).catch((err) => {
|
|
5099
|
+
const code = err?.code;
|
|
5100
|
+
if (isMissing(err) || typeof code !== "string") return void 0;
|
|
5101
|
+
throw err;
|
|
5102
|
+
});
|
|
5103
|
+
const parent = path.slice(0, path.lastIndexOf("/")) || "/";
|
|
5104
|
+
const own = await stat(path);
|
|
5105
|
+
if (own && !writes) return;
|
|
5106
|
+
const st = own ?? await stat(parent);
|
|
5107
|
+
if (st?.readonly) throw new KernelError("EROFS");
|
|
5108
|
+
}
|
|
5094
5109
|
function vfsFile(fs, opts, nodes) {
|
|
5095
5110
|
const access = opts.flags & O_ACCMODE;
|
|
5096
5111
|
const readable = access !== O_WRONLY;
|
|
@@ -5361,6 +5376,10 @@ var WasmProcess = class {
|
|
|
5361
5376
|
if (e instanceof KernelError || e instanceof SpawnError) {
|
|
5362
5377
|
return { ok: false, errno: e.code, message: e.code };
|
|
5363
5378
|
}
|
|
5379
|
+
const code = e?.code;
|
|
5380
|
+
if (typeof code === "string" && /^E[A-Z]+$/.test(code)) {
|
|
5381
|
+
return { ok: false, errno: code, message: code };
|
|
5382
|
+
}
|
|
5364
5383
|
throw e;
|
|
5365
5384
|
}
|
|
5366
5385
|
}
|
|
@@ -5433,6 +5452,7 @@ var WasmProcess = class {
|
|
|
5433
5452
|
case "fd-poll":
|
|
5434
5453
|
return { ok: true, kind: "json", json: pollFile(this.fds.get(req.fd).file) };
|
|
5435
5454
|
case "fd-open-vfs":
|
|
5455
|
+
await refuseReadonly(this.options.fs, req.path, req.flags, req);
|
|
5436
5456
|
return { ok: true, kind: "json", json: this.fds.install(this.openVfsFile(req), 3) };
|
|
5437
5457
|
case "fd-info":
|
|
5438
5458
|
return { ok: true, kind: "json", json: this.fdInfo(req.fd) };
|
|
@@ -6159,6 +6179,57 @@ function serveFilesystem(port, handlers, capabilities = {}) {
|
|
|
6159
6179
|
return { invalidate: (paths) => reply({ invalidate: paths }) };
|
|
6160
6180
|
}
|
|
6161
6181
|
|
|
6182
|
+
// src/mount/medium.ts
|
|
6183
|
+
var EMPTY_ROOT = { kind: "directory", size: 0, mtime: 0, mode: 16877 };
|
|
6184
|
+
var absent = () => fsError2("ENOMEDIUM", "No medium found");
|
|
6185
|
+
function mediumSlot(lost, onLost) {
|
|
6186
|
+
let current;
|
|
6187
|
+
function wrap(pick) {
|
|
6188
|
+
return async (...args) => {
|
|
6189
|
+
const medium = current;
|
|
6190
|
+
if (!medium) throw absent();
|
|
6191
|
+
try {
|
|
6192
|
+
return await pick(medium.handlers)(...args);
|
|
6193
|
+
} catch (err) {
|
|
6194
|
+
if (!await lost(err, medium.key)) throw err;
|
|
6195
|
+
if (current === medium) {
|
|
6196
|
+
current = void 0;
|
|
6197
|
+
onLost(medium.key);
|
|
6198
|
+
}
|
|
6199
|
+
throw absent();
|
|
6200
|
+
}
|
|
6201
|
+
};
|
|
6202
|
+
}
|
|
6203
|
+
const getattr = wrap((h) => h.getattr);
|
|
6204
|
+
const readdir = wrap((h) => h.readdir);
|
|
6205
|
+
const statfs = wrap((h) => async () => await h.statfs?.() ?? null);
|
|
6206
|
+
return {
|
|
6207
|
+
handlers: {
|
|
6208
|
+
getattr: (path) => current || path !== "/" ? getattr(path) : Promise.resolve(EMPTY_ROOT),
|
|
6209
|
+
readdir: (path) => current || path !== "/" ? readdir(path) : Promise.resolve([]),
|
|
6210
|
+
open: wrap((h) => h.open),
|
|
6211
|
+
read: wrap((h) => h.read),
|
|
6212
|
+
write: wrap((h) => h.write),
|
|
6213
|
+
release: wrap((h) => h.release),
|
|
6214
|
+
mkdir: wrap((h) => h.mkdir),
|
|
6215
|
+
rmdir: wrap((h) => h.rmdir),
|
|
6216
|
+
unlink: wrap((h) => h.unlink),
|
|
6217
|
+
rename: wrap((h) => h.rename),
|
|
6218
|
+
symlink: wrap((h) => h.symlink),
|
|
6219
|
+
readlink: wrap((h) => h.readlink),
|
|
6220
|
+
setattr: wrap((h) => h.setattr),
|
|
6221
|
+
statfs: () => current ? statfs() : Promise.resolve(null)
|
|
6222
|
+
},
|
|
6223
|
+
present: () => current !== void 0,
|
|
6224
|
+
insert(key, handlers) {
|
|
6225
|
+
current = { key, handlers };
|
|
6226
|
+
},
|
|
6227
|
+
eject() {
|
|
6228
|
+
current = void 0;
|
|
6229
|
+
}
|
|
6230
|
+
};
|
|
6231
|
+
}
|
|
6232
|
+
|
|
6162
6233
|
// src/mount/fsa.ts
|
|
6163
6234
|
var FSA_CAPABILITIES = { listingStats: true };
|
|
6164
6235
|
var codeOf = (err) => err?.code;
|
|
@@ -6246,7 +6317,6 @@ function kernelFsHandlers(fs) {
|
|
|
6246
6317
|
}
|
|
6247
6318
|
};
|
|
6248
6319
|
}
|
|
6249
|
-
var EMPTY_ROOT = { kind: "directory", size: 0, mtime: 0, mode: 16877 };
|
|
6250
6320
|
function granted(handle) {
|
|
6251
6321
|
if (!handle.queryPermission) return Promise.resolve(true);
|
|
6252
6322
|
return handle.queryPermission({ mode: "readwrite" }).then(
|
|
@@ -6255,48 +6325,345 @@ function granted(handle) {
|
|
|
6255
6325
|
);
|
|
6256
6326
|
}
|
|
6257
6327
|
function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6328
|
+
const slot = mediumSlot(
|
|
6329
|
+
async (err, handle) => denied(err) && !await granted(handle),
|
|
6330
|
+
onLost
|
|
6331
|
+
);
|
|
6332
|
+
return {
|
|
6333
|
+
handlers: slot.handlers,
|
|
6334
|
+
present: slot.present,
|
|
6335
|
+
insert: (handle) => slot.insert(handle, kernelFsHandlers(open(handle))),
|
|
6336
|
+
eject: slot.eject
|
|
6337
|
+
};
|
|
6338
|
+
}
|
|
6339
|
+
|
|
6340
|
+
// src/mount/hostfs.ts
|
|
6341
|
+
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
6342
|
+
var codeOf2 = (err) => err?.code;
|
|
6343
|
+
var unreachable = (err) => codeOf2(err) === "ENOTCONN";
|
|
6344
|
+
function rel(path) {
|
|
6345
|
+
return path.replace(/^\/+/, "");
|
|
6346
|
+
}
|
|
6347
|
+
var HostfsClient = class {
|
|
6348
|
+
grant;
|
|
6349
|
+
regrant;
|
|
6350
|
+
fetch;
|
|
6351
|
+
constructor(grant, regrant, fetch2) {
|
|
6352
|
+
this.grant = grant;
|
|
6353
|
+
this.regrant = regrant;
|
|
6354
|
+
this.fetch = fetch2;
|
|
6355
|
+
}
|
|
6356
|
+
get capabilities() {
|
|
6357
|
+
return this.grant.capabilities ?? {};
|
|
6358
|
+
}
|
|
6359
|
+
get token() {
|
|
6360
|
+
return this.grant.token;
|
|
6361
|
+
}
|
|
6362
|
+
get maxIo() {
|
|
6363
|
+
return this.capabilities.maxIo ?? HOSTFS_IO;
|
|
6364
|
+
}
|
|
6365
|
+
async send(path, init, handle = false, retried = false) {
|
|
6366
|
+
let response;
|
|
6367
|
+
try {
|
|
6368
|
+
response = await this.fetch(`${this.grant.url}${path}`, {
|
|
6369
|
+
...init,
|
|
6370
|
+
credentials: "omit",
|
|
6371
|
+
headers: {
|
|
6372
|
+
...init.headers,
|
|
6373
|
+
"X-Hostfs-Token": this.grant.token
|
|
6271
6374
|
}
|
|
6272
|
-
|
|
6375
|
+
});
|
|
6376
|
+
} catch (err) {
|
|
6377
|
+
throw fsError2("ENOTCONN", `the host proxy is unreachable: ${err.message}`);
|
|
6378
|
+
}
|
|
6379
|
+
if (response.ok) return response;
|
|
6380
|
+
if (response.headers.get("X-Proxy-Error")) {
|
|
6381
|
+
await response.body?.cancel();
|
|
6382
|
+
if (retried || response.status !== 403) {
|
|
6383
|
+
throw fsError2("EACCES", `the host proxy refused this folder (${response.status})`);
|
|
6273
6384
|
}
|
|
6274
|
-
|
|
6385
|
+
this.grant = await this.regrant();
|
|
6386
|
+
if (handle)
|
|
6387
|
+
throw fsError2("ESTALE", "the folder was granted again, so its open files are gone");
|
|
6388
|
+
return this.send(path, init, false, true);
|
|
6389
|
+
}
|
|
6390
|
+
const errno = response.headers.get("X-Hostfs-Errno") ?? "EIO";
|
|
6391
|
+
const body = await response.json().catch(() => ({}));
|
|
6392
|
+
throw fsError2(errno, typeof body.message === "string" ? body.message : errno);
|
|
6393
|
+
}
|
|
6394
|
+
async call(op, body = {}, handle = false) {
|
|
6395
|
+
const response = await this.send(
|
|
6396
|
+
"/api/hostfs",
|
|
6397
|
+
{
|
|
6398
|
+
method: "POST",
|
|
6399
|
+
headers: { "Content-Type": "application/json" },
|
|
6400
|
+
body: JSON.stringify({ op, ...body })
|
|
6401
|
+
},
|
|
6402
|
+
handle
|
|
6403
|
+
);
|
|
6404
|
+
return await response.json();
|
|
6405
|
+
}
|
|
6406
|
+
async read(fh, offset, size, ifMatch) {
|
|
6407
|
+
const response = await this.send(
|
|
6408
|
+
"/api/hostfs",
|
|
6409
|
+
{
|
|
6410
|
+
method: "POST",
|
|
6411
|
+
headers: { "Content-Type": "application/json" },
|
|
6412
|
+
body: JSON.stringify({ op: "read", fh, offset, size, ...ifMatch ? { ifMatch } : {} })
|
|
6413
|
+
},
|
|
6414
|
+
true
|
|
6415
|
+
);
|
|
6416
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
6417
|
+
}
|
|
6418
|
+
async write(fh, offset, bytes) {
|
|
6419
|
+
const response = await this.send(
|
|
6420
|
+
"/api/hostfs/write",
|
|
6421
|
+
{
|
|
6422
|
+
method: "PUT",
|
|
6423
|
+
headers: {
|
|
6424
|
+
"Content-Type": "application/octet-stream",
|
|
6425
|
+
"X-Hostfs-Request": JSON.stringify({ fh, offset })
|
|
6426
|
+
},
|
|
6427
|
+
body: bytes
|
|
6428
|
+
},
|
|
6429
|
+
true
|
|
6430
|
+
);
|
|
6431
|
+
await response.body?.cancel();
|
|
6432
|
+
}
|
|
6433
|
+
async watch(signal, onEvent, onOpen = () => void 0) {
|
|
6434
|
+
const response = await this.send("/api/hostfs/watch", { method: "POST", signal });
|
|
6435
|
+
onOpen();
|
|
6436
|
+
const reader = response.body.getReader();
|
|
6437
|
+
const decoder2 = new TextDecoder();
|
|
6438
|
+
let buffered = "";
|
|
6439
|
+
try {
|
|
6440
|
+
for (; ; ) {
|
|
6441
|
+
const { done: done4, value } = await reader.read();
|
|
6442
|
+
if (done4) return;
|
|
6443
|
+
buffered += decoder2.decode(value, { stream: true });
|
|
6444
|
+
let end = buffered.indexOf("\n");
|
|
6445
|
+
while (end >= 0) {
|
|
6446
|
+
const line = buffered.slice(0, end).trim();
|
|
6447
|
+
buffered = buffered.slice(end + 1);
|
|
6448
|
+
if (line) onEvent(JSON.parse(line));
|
|
6449
|
+
end = buffered.indexOf("\n");
|
|
6450
|
+
}
|
|
6451
|
+
}
|
|
6452
|
+
} catch (err) {
|
|
6453
|
+
if (signal.aborted) return;
|
|
6454
|
+
throw fsError2("ENOTCONN", `the host proxy's watch broke: ${err.message}`);
|
|
6455
|
+
} finally {
|
|
6456
|
+
reader.releaseLock();
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
};
|
|
6460
|
+
function hostfsHandlers(current) {
|
|
6461
|
+
const handles = /* @__PURE__ */ new Map();
|
|
6462
|
+
let nextFh = 0;
|
|
6463
|
+
const fresh = (remote) => {
|
|
6464
|
+
if (remote.client.token === remote.token) return remote;
|
|
6465
|
+
throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
|
|
6466
|
+
};
|
|
6467
|
+
const live = (fh) => {
|
|
6468
|
+
const remote = handles.get(fh);
|
|
6469
|
+
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
6470
|
+
return fresh(remote);
|
|
6471
|
+
};
|
|
6472
|
+
const call = (op, body = {}) => current().call(op, body);
|
|
6473
|
+
async function writable(client, path, flags) {
|
|
6474
|
+
const opening = () => client.call("open", { path: rel(path), ...flags });
|
|
6475
|
+
try {
|
|
6476
|
+
return { opened: await opening(), restore: void 0 };
|
|
6477
|
+
} catch (err) {
|
|
6478
|
+
if (!flags.write || codeOf2(err) !== "EACCES") throw err;
|
|
6479
|
+
const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
|
|
6480
|
+
if (mode & 128) throw err;
|
|
6481
|
+
const restore = { path: rel(path), mode };
|
|
6482
|
+
await client.call("setattr", { path: rel(path), mode: mode | 128 });
|
|
6483
|
+
const opened2 = await opening().catch(async (again) => {
|
|
6484
|
+
await client.call("setattr", restore);
|
|
6485
|
+
throw again;
|
|
6486
|
+
});
|
|
6487
|
+
return { opened: opened2, restore };
|
|
6488
|
+
}
|
|
6275
6489
|
}
|
|
6276
|
-
const getattr = wrap((h) => h.getattr);
|
|
6277
|
-
const readdir = wrap((h) => h.readdir);
|
|
6278
6490
|
return {
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
mkdir: wrap((h) => h.mkdir),
|
|
6287
|
-
rmdir: wrap((h) => h.rmdir),
|
|
6288
|
-
unlink: wrap((h) => h.unlink),
|
|
6289
|
-
rename: wrap((h) => h.rename),
|
|
6290
|
-
symlink: wrap((h) => h.symlink),
|
|
6291
|
-
readlink: wrap((h) => h.readlink),
|
|
6292
|
-
setattr: wrap((h) => h.setattr)
|
|
6491
|
+
getattr: async (path) => await call("stat", { path: rel(path) }),
|
|
6492
|
+
async readdir(path) {
|
|
6493
|
+
const { entries } = await call(
|
|
6494
|
+
"list",
|
|
6495
|
+
{ path: rel(path) }
|
|
6496
|
+
);
|
|
6497
|
+
return entries.map(({ name: name2, attr }) => ({ name: name2, kind: attr.kind, attr }));
|
|
6293
6498
|
},
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6499
|
+
async open(path, flags) {
|
|
6500
|
+
const client = current();
|
|
6501
|
+
const token = client.token;
|
|
6502
|
+
const { opened: opened2, restore } = await writable(client, path, flags);
|
|
6503
|
+
handles.set(++nextFh, {
|
|
6504
|
+
client,
|
|
6505
|
+
token,
|
|
6506
|
+
fh: opened2.fh,
|
|
6507
|
+
etag: flags.write ? void 0 : opened2.attr?.etag,
|
|
6508
|
+
restore
|
|
6509
|
+
});
|
|
6510
|
+
return nextFh;
|
|
6297
6511
|
},
|
|
6298
|
-
|
|
6299
|
-
|
|
6512
|
+
async read(fh, offset, size) {
|
|
6513
|
+
const remote = live(fh);
|
|
6514
|
+
return remote.client.read(
|
|
6515
|
+
remote.fh,
|
|
6516
|
+
offset,
|
|
6517
|
+
Math.min(size, remote.client.maxIo),
|
|
6518
|
+
remote.etag
|
|
6519
|
+
);
|
|
6520
|
+
},
|
|
6521
|
+
async write(fh, offset, bytes) {
|
|
6522
|
+
const remote = live(fh);
|
|
6523
|
+
const most = remote.client.maxIo;
|
|
6524
|
+
for (let at = 0; at < bytes.length; at += most) {
|
|
6525
|
+
await remote.client.write(remote.fh, offset + at, bytes.subarray(at, at + most));
|
|
6526
|
+
}
|
|
6527
|
+
},
|
|
6528
|
+
async release(fh) {
|
|
6529
|
+
const remote = handles.get(fh);
|
|
6530
|
+
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
6531
|
+
handles.delete(fh);
|
|
6532
|
+
try {
|
|
6533
|
+
await fresh(remote).client.call("release", { fh: remote.fh }, true);
|
|
6534
|
+
} finally {
|
|
6535
|
+
if (remote.restore) await current().call("setattr", remote.restore);
|
|
6536
|
+
}
|
|
6537
|
+
},
|
|
6538
|
+
mkdir: async (path) => void await call("mkdir", { path: rel(path) }),
|
|
6539
|
+
rmdir: async (path) => void await call("rmdir", { path: rel(path) }),
|
|
6540
|
+
unlink: async (path) => void await call("unlink", { path: rel(path) }),
|
|
6541
|
+
rename: async (from, to) => void await call("rename", { from: rel(from), to: rel(to) }),
|
|
6542
|
+
symlink: async (target, path) => void await call("symlink", { target, path: rel(path) }),
|
|
6543
|
+
readlink: async (path) => (await call("readlink", { path: rel(path) })).target,
|
|
6544
|
+
setattr: async (path, change) => void await call("setattr", { path: rel(path), ...change }),
|
|
6545
|
+
statfs: () => call("statfs")
|
|
6546
|
+
};
|
|
6547
|
+
}
|
|
6548
|
+
function sleep(ms, signal) {
|
|
6549
|
+
return new Promise((resolve) => {
|
|
6550
|
+
const done4 = () => {
|
|
6551
|
+
clearTimeout(timer);
|
|
6552
|
+
signal.removeEventListener("abort", done4);
|
|
6553
|
+
resolve();
|
|
6554
|
+
};
|
|
6555
|
+
const timer = setTimeout(done4, ms);
|
|
6556
|
+
signal.addEventListener("abort", done4);
|
|
6557
|
+
});
|
|
6558
|
+
}
|
|
6559
|
+
function keepWatching(loop, signal) {
|
|
6560
|
+
const silenceMs = loop.silenceMs ?? 45e3;
|
|
6561
|
+
const [first, most] = loop.backoffMs ?? [1e3, 3e4];
|
|
6562
|
+
const ready2 = Promise.withResolvers();
|
|
6563
|
+
let wait = first;
|
|
6564
|
+
let up = false;
|
|
6565
|
+
let current;
|
|
6566
|
+
async function session() {
|
|
6567
|
+
const session2 = new AbortController();
|
|
6568
|
+
current = session2;
|
|
6569
|
+
const stop = () => session2.abort();
|
|
6570
|
+
signal.addEventListener("abort", stop);
|
|
6571
|
+
let timer;
|
|
6572
|
+
const quiet = () => {
|
|
6573
|
+
clearTimeout(timer);
|
|
6574
|
+
timer = setTimeout(stop, silenceMs);
|
|
6575
|
+
};
|
|
6576
|
+
let opened2 = false;
|
|
6577
|
+
try {
|
|
6578
|
+
const client = await loop.connect();
|
|
6579
|
+
await client.watch(
|
|
6580
|
+
session2.signal,
|
|
6581
|
+
(event) => {
|
|
6582
|
+
quiet();
|
|
6583
|
+
if (!event.ping) loop.event(event);
|
|
6584
|
+
},
|
|
6585
|
+
() => {
|
|
6586
|
+
quiet();
|
|
6587
|
+
opened2 = true;
|
|
6588
|
+
up = true;
|
|
6589
|
+
loop.up(client);
|
|
6590
|
+
ready2.resolve();
|
|
6591
|
+
}
|
|
6592
|
+
);
|
|
6593
|
+
return opened2;
|
|
6594
|
+
} catch {
|
|
6595
|
+
return opened2;
|
|
6596
|
+
} finally {
|
|
6597
|
+
clearTimeout(timer);
|
|
6598
|
+
signal.removeEventListener("abort", stop);
|
|
6599
|
+
}
|
|
6600
|
+
}
|
|
6601
|
+
void (async () => {
|
|
6602
|
+
while (!signal.aborted) {
|
|
6603
|
+
const began = Date.now();
|
|
6604
|
+
const opened2 = await session();
|
|
6605
|
+
if (signal.aborted) return;
|
|
6606
|
+
if (opened2 && Date.now() - began >= first) {
|
|
6607
|
+
wait = first;
|
|
6608
|
+
continue;
|
|
6609
|
+
}
|
|
6610
|
+
if (!opened2 && up) {
|
|
6611
|
+
up = false;
|
|
6612
|
+
loop.down();
|
|
6613
|
+
}
|
|
6614
|
+
ready2.resolve();
|
|
6615
|
+
await sleep(wait, signal);
|
|
6616
|
+
wait = Math.min(wait * 2, most);
|
|
6617
|
+
}
|
|
6618
|
+
})();
|
|
6619
|
+
return { ready: ready2.promise, kick: () => current?.abort() };
|
|
6620
|
+
}
|
|
6621
|
+
function invalidation(event) {
|
|
6622
|
+
return event.paths ?? true;
|
|
6623
|
+
}
|
|
6624
|
+
async function openHostfs(spec, hook, fetch2, timing = {}) {
|
|
6625
|
+
const readonly2 = spec.options?.ro !== void 0;
|
|
6626
|
+
const { port1, port2 } = new MessageChannel();
|
|
6627
|
+
const slot = mediumSlot(
|
|
6628
|
+
async (err) => unreachable(err),
|
|
6629
|
+
() => watching.kick()
|
|
6630
|
+
);
|
|
6631
|
+
const stop = new AbortController();
|
|
6632
|
+
const grant = () => hook(spec.source, { readonly: readonly2 });
|
|
6633
|
+
let active;
|
|
6634
|
+
const handlers = hostfsHandlers(() => active);
|
|
6635
|
+
const served = serveFilesystem(port2, slot.handlers, {
|
|
6636
|
+
symlinks: true,
|
|
6637
|
+
chmod: true,
|
|
6638
|
+
listingStats: true,
|
|
6639
|
+
maxIo: HOSTFS_IO,
|
|
6640
|
+
...readonly2 ? { readonly: true } : {}
|
|
6641
|
+
});
|
|
6642
|
+
const watching = keepWatching(
|
|
6643
|
+
{
|
|
6644
|
+
connect: async () => new HostfsClient(await grant(), grant, fetch2),
|
|
6645
|
+
up(client) {
|
|
6646
|
+
active = client;
|
|
6647
|
+
slot.insert(client, handlers);
|
|
6648
|
+
served.invalidate(true);
|
|
6649
|
+
},
|
|
6650
|
+
event: (event) => served.invalidate(invalidation(event)),
|
|
6651
|
+
down() {
|
|
6652
|
+
slot.eject();
|
|
6653
|
+
served.invalidate(true);
|
|
6654
|
+
},
|
|
6655
|
+
...timing
|
|
6656
|
+
},
|
|
6657
|
+
stop.signal
|
|
6658
|
+
);
|
|
6659
|
+
await watching.ready;
|
|
6660
|
+
return {
|
|
6661
|
+
port: port1,
|
|
6662
|
+
present: slot.present,
|
|
6663
|
+
dispose() {
|
|
6664
|
+
stop.abort();
|
|
6665
|
+
port1.close();
|
|
6666
|
+
port2.close();
|
|
6300
6667
|
}
|
|
6301
6668
|
};
|
|
6302
6669
|
}
|
|
@@ -6396,6 +6763,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
6396
6763
|
|
|
6397
6764
|
// src/mount/mount-fs.ts
|
|
6398
6765
|
var MAX_LINKS4 = 40;
|
|
6766
|
+
var STALE_RETRIES = 3;
|
|
6399
6767
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
6400
6768
|
var UNITS = { "": 1, k: 1024, m: 1024 ** 2, g: 1024 ** 3, t: 1024 ** 4 };
|
|
6401
6769
|
function parseSize(text) {
|
|
@@ -6409,7 +6777,8 @@ function heldUnder(held, target) {
|
|
|
6409
6777
|
return false;
|
|
6410
6778
|
}
|
|
6411
6779
|
var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6412
|
-
|
|
6780
|
+
var readonly = (mount) => mount.caps.readonly === true;
|
|
6781
|
+
function statOf(attr, path, mount) {
|
|
6413
6782
|
const mtime = new Date(attr.mtime);
|
|
6414
6783
|
const fallback = attr.kind === "directory" ? 16877 : attr.kind === "symlink" ? 41471 : 33188;
|
|
6415
6784
|
return {
|
|
@@ -6422,7 +6791,8 @@ function statOf(attr, path, dev) {
|
|
|
6422
6791
|
atime: mtime,
|
|
6423
6792
|
ctime: mtime,
|
|
6424
6793
|
ino: attr.ino ?? inodeOf(path),
|
|
6425
|
-
dev
|
|
6794
|
+
dev: mount.dev,
|
|
6795
|
+
...readonly(mount) ? { readonly: true } : {}
|
|
6426
6796
|
};
|
|
6427
6797
|
}
|
|
6428
6798
|
function concat4(chunks, total) {
|
|
@@ -6462,8 +6832,8 @@ var MountTable = class {
|
|
|
6462
6832
|
best = mount;
|
|
6463
6833
|
}
|
|
6464
6834
|
if (!best) return void 0;
|
|
6465
|
-
const
|
|
6466
|
-
return { mount: best, rel:
|
|
6835
|
+
const rel2 = best.target === "/" ? path : path.slice(best.target.length);
|
|
6836
|
+
return { mount: best, rel: rel2 || "/" };
|
|
6467
6837
|
}
|
|
6468
6838
|
async mount(spec, fs) {
|
|
6469
6839
|
const target = normalizePath(spec.target);
|
|
@@ -6568,20 +6938,20 @@ var MountTable = class {
|
|
|
6568
6938
|
mount.lists.delete(parentOf4(path));
|
|
6569
6939
|
for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
|
|
6570
6940
|
}
|
|
6571
|
-
async getattr(mount, path,
|
|
6941
|
+
async getattr(mount, path, rel2) {
|
|
6572
6942
|
if (mount.conn.failure) throw mount.conn.failure;
|
|
6573
6943
|
const cached = mount.attrs.get(path);
|
|
6574
6944
|
if (cached && cached.expires > Date.now()) return cached.value;
|
|
6575
|
-
const attr = await mount.conn.call({ op: "getattr", path:
|
|
6945
|
+
const attr = await mount.conn.call({ op: "getattr", path: rel2 });
|
|
6576
6946
|
const ttl = mount.caps.attrTtl ?? 1e3;
|
|
6577
6947
|
if (ttl > 0) mount.attrs.set(path, { value: attr, expires: Date.now() + ttl });
|
|
6578
6948
|
return attr;
|
|
6579
6949
|
}
|
|
6580
|
-
async readdir(mount, path,
|
|
6950
|
+
async readdir(mount, path, rel2) {
|
|
6581
6951
|
if (mount.conn.failure) throw mount.conn.failure;
|
|
6582
6952
|
const cached = mount.lists.get(path);
|
|
6583
6953
|
if (cached && cached.expires > Date.now()) return cached.value;
|
|
6584
|
-
const entries = await mount.conn.call({ op: "readdir", path:
|
|
6954
|
+
const entries = await mount.conn.call({ op: "readdir", path: rel2 });
|
|
6585
6955
|
const ttl = mount.caps.entryTtl ?? mount.caps.attrTtl ?? 1e3;
|
|
6586
6956
|
if (ttl > 0) mount.lists.set(path, { value: entries, expires: Date.now() + ttl });
|
|
6587
6957
|
return entries;
|
|
@@ -6608,7 +6978,7 @@ var MountFs = class {
|
|
|
6608
6978
|
return this.table.locate(normalizePath(path));
|
|
6609
6979
|
}
|
|
6610
6980
|
writable(mount, path) {
|
|
6611
|
-
if (mount
|
|
6981
|
+
if (readonly(mount)) throw errnoError("EROFS", path);
|
|
6612
6982
|
}
|
|
6613
6983
|
async follow(path, hops = 0, create = false) {
|
|
6614
6984
|
const found = this.at(path);
|
|
@@ -6626,10 +6996,10 @@ var MountFs = class {
|
|
|
6626
6996
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
6627
6997
|
return this.follow(this.base.resolvePath(parentOf4(real), target), hops + 1, create);
|
|
6628
6998
|
}
|
|
6629
|
-
async open(mount,
|
|
6999
|
+
async open(mount, rel2, write) {
|
|
6630
7000
|
return await mount.conn.call({
|
|
6631
7001
|
op: "open",
|
|
6632
|
-
path:
|
|
7002
|
+
path: rel2,
|
|
6633
7003
|
write,
|
|
6634
7004
|
create: write,
|
|
6635
7005
|
truncate: write,
|
|
@@ -6646,13 +7016,22 @@ var MountFs = class {
|
|
|
6646
7016
|
const real = await this.follow(path);
|
|
6647
7017
|
const found = this.at(real);
|
|
6648
7018
|
if (!found) return this.base.readFileBuffer(real);
|
|
6649
|
-
|
|
6650
|
-
|
|
7019
|
+
for (let attempt = 1; ; attempt++) {
|
|
7020
|
+
try {
|
|
7021
|
+
return await this.readWhole(found.mount, real, found.rel);
|
|
7022
|
+
} catch (err) {
|
|
7023
|
+
if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
|
|
7024
|
+
this.table.forget(found.mount, real);
|
|
7025
|
+
}
|
|
7026
|
+
}
|
|
7027
|
+
}
|
|
7028
|
+
async readWhole(mount, real, rel2) {
|
|
7029
|
+
const attr = await this.table.getattr(mount, real, rel2);
|
|
6651
7030
|
if (attr.kind === "directory") throw errnoError("EISDIR", real);
|
|
6652
7031
|
if (mount.maxFile > 0 && attr.size > mount.maxFile) {
|
|
6653
7032
|
throw errnoError("EFBIG", `${real} is larger than this mount's maxfile (${mount.maxFile})`);
|
|
6654
7033
|
}
|
|
6655
|
-
const fh = await this.open(mount,
|
|
7034
|
+
const fh = await this.open(mount, rel2, false);
|
|
6656
7035
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
6657
7036
|
const chunks = [];
|
|
6658
7037
|
let total = 0;
|
|
@@ -6678,7 +7057,7 @@ var MountFs = class {
|
|
|
6678
7057
|
const real = await this.follow(path, 0, true);
|
|
6679
7058
|
const found = this.at(real);
|
|
6680
7059
|
if (!found) return this.base.writeFile(real, content);
|
|
6681
|
-
const { mount, rel } = found;
|
|
7060
|
+
const { mount, rel: rel2 } = found;
|
|
6682
7061
|
this.writable(mount, real);
|
|
6683
7062
|
const bytes = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
6684
7063
|
if (mount.maxFile > 0 && bytes.length > mount.maxFile) {
|
|
@@ -6687,8 +7066,17 @@ var MountFs = class {
|
|
|
6687
7066
|
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
6688
7067
|
);
|
|
6689
7068
|
}
|
|
7069
|
+
for (let attempt = 1; ; attempt++) {
|
|
7070
|
+
try {
|
|
7071
|
+
return await this.writeWhole(mount, real, rel2, bytes);
|
|
7072
|
+
} catch (err) {
|
|
7073
|
+
if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
|
|
7074
|
+
}
|
|
7075
|
+
}
|
|
7076
|
+
}
|
|
7077
|
+
async writeWhole(mount, real, rel2, bytes) {
|
|
6690
7078
|
this.table.forget(mount, real);
|
|
6691
|
-
const fh = await this.open(mount,
|
|
7079
|
+
const fh = await this.open(mount, rel2, true);
|
|
6692
7080
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
6693
7081
|
try {
|
|
6694
7082
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
@@ -6719,7 +7107,7 @@ var MountFs = class {
|
|
|
6719
7107
|
const found = this.at(path);
|
|
6720
7108
|
if (!found) return this.base.lstat(path);
|
|
6721
7109
|
const real = normalizePath(path);
|
|
6722
|
-
return statOf(await this.table.getattr(found.mount, real, found.rel), real, found.mount
|
|
7110
|
+
return statOf(await this.table.getattr(found.mount, real, found.rel), real, found.mount);
|
|
6723
7111
|
}
|
|
6724
7112
|
async stat(path) {
|
|
6725
7113
|
const real = await this.follow(path);
|
|
@@ -6746,7 +7134,7 @@ var MountFs = class {
|
|
|
6746
7134
|
list.map(async (entry) => {
|
|
6747
7135
|
const child = normalizePath(`${real}/${entry.name}`);
|
|
6748
7136
|
if (entry.attr && mount.caps.listingStats) {
|
|
6749
|
-
return [entry.name, statOf(entry.attr, child, mount
|
|
7137
|
+
return [entry.name, statOf(entry.attr, child, mount)];
|
|
6750
7138
|
}
|
|
6751
7139
|
return [entry.name, await this.lstat(child).catch(() => null)];
|
|
6752
7140
|
})
|
|
@@ -6755,16 +7143,16 @@ var MountFs = class {
|
|
|
6755
7143
|
async mkdir(path, options = {}) {
|
|
6756
7144
|
const found = this.at(path);
|
|
6757
7145
|
if (!found) return this.base.mkdir(path, options);
|
|
6758
|
-
const { mount, rel } = found;
|
|
7146
|
+
const { mount, rel: rel2 } = found;
|
|
6759
7147
|
const real = normalizePath(path);
|
|
6760
7148
|
this.writable(mount, real);
|
|
6761
7149
|
if (!options.recursive) {
|
|
6762
|
-
if (
|
|
7150
|
+
if (rel2 === "/") throw errnoError("EEXIST", real);
|
|
6763
7151
|
this.table.forget(mount, real);
|
|
6764
|
-
await mount.conn.call({ op: "mkdir", path:
|
|
7152
|
+
await mount.conn.call({ op: "mkdir", path: rel2 });
|
|
6765
7153
|
return;
|
|
6766
7154
|
}
|
|
6767
|
-
const parts =
|
|
7155
|
+
const parts = rel2.split("/").filter(Boolean);
|
|
6768
7156
|
for (let n = 1; n <= parts.length; n++) {
|
|
6769
7157
|
const sub = `/${parts.slice(0, n).join("/")}`;
|
|
6770
7158
|
const full = normalizePath(`${mount.target}${sub}`);
|
|
@@ -6778,25 +7166,25 @@ var MountFs = class {
|
|
|
6778
7166
|
async rm(path, options = {}) {
|
|
6779
7167
|
const found = this.at(path);
|
|
6780
7168
|
if (!found) return this.base.rm(path, options);
|
|
6781
|
-
const { mount, rel } = found;
|
|
7169
|
+
const { mount, rel: rel2 } = found;
|
|
6782
7170
|
const real = normalizePath(path);
|
|
6783
|
-
if (
|
|
7171
|
+
if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
|
|
6784
7172
|
this.writable(mount, real);
|
|
6785
7173
|
let attr;
|
|
6786
7174
|
try {
|
|
6787
|
-
attr = await this.table.getattr(mount, real,
|
|
7175
|
+
attr = await this.table.getattr(mount, real, rel2);
|
|
6788
7176
|
} catch (err) {
|
|
6789
7177
|
if (options.force && err.code === "ENOENT") return;
|
|
6790
7178
|
throw err;
|
|
6791
7179
|
}
|
|
6792
7180
|
this.table.forget(mount, real);
|
|
6793
7181
|
if (attr.kind === "directory" && options.recursive) {
|
|
6794
|
-
for (const entry of await this.table.readdir(mount, real,
|
|
7182
|
+
for (const entry of await this.table.readdir(mount, real, rel2)) {
|
|
6795
7183
|
await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
|
|
6796
7184
|
}
|
|
6797
7185
|
this.table.forget(mount, real);
|
|
6798
7186
|
}
|
|
6799
|
-
await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path:
|
|
7187
|
+
await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
|
|
6800
7188
|
this.table.forget(mount, real);
|
|
6801
7189
|
}
|
|
6802
7190
|
async rename(from, to) {
|
|
@@ -7258,6 +7646,7 @@ var Launcher = class {
|
|
|
7258
7646
|
media;
|
|
7259
7647
|
onMountPending;
|
|
7260
7648
|
removable = /* @__PURE__ */ new Map();
|
|
7649
|
+
hostfs;
|
|
7261
7650
|
inserted = /* @__PURE__ */ new Map();
|
|
7262
7651
|
openFiles = /* @__PURE__ */ new Set();
|
|
7263
7652
|
held = /* @__PURE__ */ new Set();
|
|
@@ -7275,6 +7664,7 @@ var Launcher = class {
|
|
|
7275
7664
|
this.createDriverWorker = options.createDriverWorker;
|
|
7276
7665
|
this.media = options.media ?? memoryMedia();
|
|
7277
7666
|
this.onMountPending = options.onMountPending;
|
|
7667
|
+
this.hostfs = options;
|
|
7278
7668
|
this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
|
|
7279
7669
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
7280
7670
|
this.env = { ...networkEnv(), ...options.env };
|
|
@@ -7540,6 +7930,17 @@ var Launcher = class {
|
|
|
7540
7930
|
}
|
|
7541
7931
|
async driver(type, spec) {
|
|
7542
7932
|
if (type === "fsa") return this.fsaDriver(spec);
|
|
7933
|
+
if (type === "hostfs") {
|
|
7934
|
+
const { hostfs, hostfsFetch, hostfsTiming } = this.hostfs;
|
|
7935
|
+
if (!hostfs)
|
|
7936
|
+
throw fsError("ENODEV", "this kernel has no hostfs hook (createKernel({ hostfs }))");
|
|
7937
|
+
return openHostfs(
|
|
7938
|
+
spec,
|
|
7939
|
+
hostfs,
|
|
7940
|
+
hostfsFetch ?? ((url, init) => fetch(url, init)),
|
|
7941
|
+
hostfsTiming
|
|
7942
|
+
);
|
|
7943
|
+
}
|
|
7543
7944
|
if (type === "tmpfs") {
|
|
7544
7945
|
const { port1, port2 } = new MessageChannel();
|
|
7545
7946
|
serveFilesystem(port2, tmpfs(), { symlinks: true, chmod: true, attrTtl: 0 });
|
|
@@ -7796,6 +8197,7 @@ function serveKernel(port, deps) {
|
|
|
7796
8197
|
...remote ? { transport: remote } : {},
|
|
7797
8198
|
...media ? { media } : {},
|
|
7798
8199
|
onMountPending: (medium) => port.postMessage({ medium }),
|
|
8200
|
+
...req.hostfs ? { hostfs: askGrant } : {},
|
|
7799
8201
|
caStore: caStore(deps.metadata ? name2 : false)
|
|
7800
8202
|
});
|
|
7801
8203
|
await started.prepare();
|
|
@@ -7829,7 +8231,25 @@ function serveKernel(port, deps) {
|
|
|
7829
8231
|
onStderr: (bytes) => reply(req.id, { fd: 2, bytes })
|
|
7830
8232
|
});
|
|
7831
8233
|
}
|
|
8234
|
+
const grants = /* @__PURE__ */ new Map();
|
|
8235
|
+
let nextGrant = 0;
|
|
8236
|
+
const askGrant = (source, options) => {
|
|
8237
|
+
const id = ++nextGrant;
|
|
8238
|
+
const waiting = Promise.withResolvers();
|
|
8239
|
+
grants.set(id, waiting);
|
|
8240
|
+
port.postMessage({ hostfs: { id, source, ...options } });
|
|
8241
|
+
return waiting.promise;
|
|
8242
|
+
};
|
|
8243
|
+
function granted2({ id, grant, error }) {
|
|
8244
|
+
const waiting = grants.get(id);
|
|
8245
|
+
if (!waiting) return;
|
|
8246
|
+
grants.delete(id);
|
|
8247
|
+
if (grant) waiting.resolve(grant);
|
|
8248
|
+
else waiting.reject(Object.assign(new Error(error ?? "no grant"), { code: "EACCES" }));
|
|
8249
|
+
}
|
|
7832
8250
|
port.addEventListener("message", (event) => {
|
|
8251
|
+
const grant = event.data.hostfsGrant;
|
|
8252
|
+
if (grant) return granted2(grant);
|
|
7833
8253
|
if (event.data.net !== void 0) {
|
|
7834
8254
|
remote?.receive(event.data);
|
|
7835
8255
|
return;
|