@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/node.js
CHANGED
|
@@ -1067,9 +1067,9 @@ var OpfsFs = class {
|
|
|
1067
1067
|
var NAME = /^[A-Za-z0-9._+-]+$/;
|
|
1068
1068
|
var ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1069
1069
|
var PACKAGE = /\$\{package\}/g;
|
|
1070
|
-
function inside(pkg,
|
|
1071
|
-
if (typeof
|
|
1072
|
-
const clean =
|
|
1070
|
+
function inside(pkg, rel2) {
|
|
1071
|
+
if (typeof rel2 !== "string" || rel2 === "") return void 0;
|
|
1072
|
+
const clean = rel2.replace(/^\.\//, "");
|
|
1073
1073
|
if (clean.startsWith("/") || clean.split("/").includes("..")) return void 0;
|
|
1074
1074
|
return `${pkg}/${clean}`;
|
|
1075
1075
|
}
|
|
@@ -1988,7 +1988,8 @@ function statJson(s) {
|
|
|
1988
1988
|
atimeMs: s.atime.getTime(),
|
|
1989
1989
|
ctimeMs: s.ctime.getTime(),
|
|
1990
1990
|
ino: s.ino,
|
|
1991
|
-
...s.dev !== void 0 ? { dev: s.dev } : {}
|
|
1991
|
+
...s.dev !== void 0 ? { dev: s.dev } : {},
|
|
1992
|
+
...s.readonly ? { readonly: true } : {}
|
|
1992
1993
|
};
|
|
1993
1994
|
}
|
|
1994
1995
|
async function readdirStat(fs, dir) {
|
|
@@ -4843,6 +4844,20 @@ var VfsNodes = class {
|
|
|
4843
4844
|
}
|
|
4844
4845
|
}
|
|
4845
4846
|
};
|
|
4847
|
+
async function refuseReadonly(fs, path, flags, opts = {}) {
|
|
4848
|
+
const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
|
|
4849
|
+
if (!writes && !opts.create || !fs.stat) return;
|
|
4850
|
+
const stat = (p) => fs.stat?.(p).catch((err) => {
|
|
4851
|
+
const code = err?.code;
|
|
4852
|
+
if (isMissing(err) || typeof code !== "string") return void 0;
|
|
4853
|
+
throw err;
|
|
4854
|
+
});
|
|
4855
|
+
const parent = path.slice(0, path.lastIndexOf("/")) || "/";
|
|
4856
|
+
const own = await stat(path);
|
|
4857
|
+
if (own && !writes) return;
|
|
4858
|
+
const st = own ?? await stat(parent);
|
|
4859
|
+
if (st?.readonly) throw new KernelError("EROFS");
|
|
4860
|
+
}
|
|
4846
4861
|
function vfsFile(fs, opts, nodes) {
|
|
4847
4862
|
const access = opts.flags & O_ACCMODE;
|
|
4848
4863
|
const readable = access !== O_WRONLY;
|
|
@@ -5113,6 +5128,10 @@ var WasmProcess = class {
|
|
|
5113
5128
|
if (e instanceof KernelError || e instanceof SpawnError) {
|
|
5114
5129
|
return { ok: false, errno: e.code, message: e.code };
|
|
5115
5130
|
}
|
|
5131
|
+
const code = e?.code;
|
|
5132
|
+
if (typeof code === "string" && /^E[A-Z]+$/.test(code)) {
|
|
5133
|
+
return { ok: false, errno: code, message: code };
|
|
5134
|
+
}
|
|
5116
5135
|
throw e;
|
|
5117
5136
|
}
|
|
5118
5137
|
}
|
|
@@ -5185,6 +5204,7 @@ var WasmProcess = class {
|
|
|
5185
5204
|
case "fd-poll":
|
|
5186
5205
|
return { ok: true, kind: "json", json: pollFile(this.fds.get(req.fd).file) };
|
|
5187
5206
|
case "fd-open-vfs":
|
|
5207
|
+
await refuseReadonly(this.options.fs, req.path, req.flags, req);
|
|
5188
5208
|
return { ok: true, kind: "json", json: this.fds.install(this.openVfsFile(req), 3) };
|
|
5189
5209
|
case "fd-info":
|
|
5190
5210
|
return { ok: true, kind: "json", json: this.fdInfo(req.fd) };
|
|
@@ -5911,6 +5931,57 @@ function serveFilesystem(port, handlers, capabilities = {}) {
|
|
|
5911
5931
|
return { invalidate: (paths) => reply({ invalidate: paths }) };
|
|
5912
5932
|
}
|
|
5913
5933
|
|
|
5934
|
+
// src/mount/medium.ts
|
|
5935
|
+
var EMPTY_ROOT = { kind: "directory", size: 0, mtime: 0, mode: 16877 };
|
|
5936
|
+
var absent = () => fsError2("ENOMEDIUM", "No medium found");
|
|
5937
|
+
function mediumSlot(lost, onLost) {
|
|
5938
|
+
let current;
|
|
5939
|
+
function wrap(pick) {
|
|
5940
|
+
return async (...args) => {
|
|
5941
|
+
const medium = current;
|
|
5942
|
+
if (!medium) throw absent();
|
|
5943
|
+
try {
|
|
5944
|
+
return await pick(medium.handlers)(...args);
|
|
5945
|
+
} catch (err) {
|
|
5946
|
+
if (!await lost(err, medium.key)) throw err;
|
|
5947
|
+
if (current === medium) {
|
|
5948
|
+
current = void 0;
|
|
5949
|
+
onLost(medium.key);
|
|
5950
|
+
}
|
|
5951
|
+
throw absent();
|
|
5952
|
+
}
|
|
5953
|
+
};
|
|
5954
|
+
}
|
|
5955
|
+
const getattr = wrap((h) => h.getattr);
|
|
5956
|
+
const readdir = wrap((h) => h.readdir);
|
|
5957
|
+
const statfs = wrap((h) => async () => await h.statfs?.() ?? null);
|
|
5958
|
+
return {
|
|
5959
|
+
handlers: {
|
|
5960
|
+
getattr: (path) => current || path !== "/" ? getattr(path) : Promise.resolve(EMPTY_ROOT),
|
|
5961
|
+
readdir: (path) => current || path !== "/" ? readdir(path) : Promise.resolve([]),
|
|
5962
|
+
open: wrap((h) => h.open),
|
|
5963
|
+
read: wrap((h) => h.read),
|
|
5964
|
+
write: wrap((h) => h.write),
|
|
5965
|
+
release: wrap((h) => h.release),
|
|
5966
|
+
mkdir: wrap((h) => h.mkdir),
|
|
5967
|
+
rmdir: wrap((h) => h.rmdir),
|
|
5968
|
+
unlink: wrap((h) => h.unlink),
|
|
5969
|
+
rename: wrap((h) => h.rename),
|
|
5970
|
+
symlink: wrap((h) => h.symlink),
|
|
5971
|
+
readlink: wrap((h) => h.readlink),
|
|
5972
|
+
setattr: wrap((h) => h.setattr),
|
|
5973
|
+
statfs: () => current ? statfs() : Promise.resolve(null)
|
|
5974
|
+
},
|
|
5975
|
+
present: () => current !== void 0,
|
|
5976
|
+
insert(key, handlers) {
|
|
5977
|
+
current = { key, handlers };
|
|
5978
|
+
},
|
|
5979
|
+
eject() {
|
|
5980
|
+
current = void 0;
|
|
5981
|
+
}
|
|
5982
|
+
};
|
|
5983
|
+
}
|
|
5984
|
+
|
|
5914
5985
|
// src/mount/fsa.ts
|
|
5915
5986
|
var FSA_CAPABILITIES = { listingStats: true };
|
|
5916
5987
|
var codeOf = (err) => err?.code;
|
|
@@ -5998,7 +6069,6 @@ function kernelFsHandlers(fs) {
|
|
|
5998
6069
|
}
|
|
5999
6070
|
};
|
|
6000
6071
|
}
|
|
6001
|
-
var EMPTY_ROOT = { kind: "directory", size: 0, mtime: 0, mode: 16877 };
|
|
6002
6072
|
function granted(handle) {
|
|
6003
6073
|
if (!handle.queryPermission) return Promise.resolve(true);
|
|
6004
6074
|
return handle.queryPermission({ mode: "readwrite" }).then(
|
|
@@ -6007,48 +6077,345 @@ function granted(handle) {
|
|
|
6007
6077
|
);
|
|
6008
6078
|
}
|
|
6009
6079
|
function removableMedium(onLost, open = (handle) => new OpfsFs(handle)) {
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6080
|
+
const slot = mediumSlot(
|
|
6081
|
+
async (err, handle) => denied(err) && !await granted(handle),
|
|
6082
|
+
onLost
|
|
6083
|
+
);
|
|
6084
|
+
return {
|
|
6085
|
+
handlers: slot.handlers,
|
|
6086
|
+
present: slot.present,
|
|
6087
|
+
insert: (handle) => slot.insert(handle, kernelFsHandlers(open(handle))),
|
|
6088
|
+
eject: slot.eject
|
|
6089
|
+
};
|
|
6090
|
+
}
|
|
6091
|
+
|
|
6092
|
+
// src/mount/hostfs.ts
|
|
6093
|
+
var HOSTFS_IO = 16 * 1024 * 1024;
|
|
6094
|
+
var codeOf2 = (err) => err?.code;
|
|
6095
|
+
var unreachable = (err) => codeOf2(err) === "ENOTCONN";
|
|
6096
|
+
function rel(path) {
|
|
6097
|
+
return path.replace(/^\/+/, "");
|
|
6098
|
+
}
|
|
6099
|
+
var HostfsClient = class {
|
|
6100
|
+
grant;
|
|
6101
|
+
regrant;
|
|
6102
|
+
fetch;
|
|
6103
|
+
constructor(grant, regrant, fetch2) {
|
|
6104
|
+
this.grant = grant;
|
|
6105
|
+
this.regrant = regrant;
|
|
6106
|
+
this.fetch = fetch2;
|
|
6107
|
+
}
|
|
6108
|
+
get capabilities() {
|
|
6109
|
+
return this.grant.capabilities ?? {};
|
|
6110
|
+
}
|
|
6111
|
+
get token() {
|
|
6112
|
+
return this.grant.token;
|
|
6113
|
+
}
|
|
6114
|
+
get maxIo() {
|
|
6115
|
+
return this.capabilities.maxIo ?? HOSTFS_IO;
|
|
6116
|
+
}
|
|
6117
|
+
async send(path, init, handle = false, retried = false) {
|
|
6118
|
+
let response;
|
|
6119
|
+
try {
|
|
6120
|
+
response = await this.fetch(`${this.grant.url}${path}`, {
|
|
6121
|
+
...init,
|
|
6122
|
+
credentials: "omit",
|
|
6123
|
+
headers: {
|
|
6124
|
+
...init.headers,
|
|
6125
|
+
"X-Hostfs-Token": this.grant.token
|
|
6023
6126
|
}
|
|
6024
|
-
|
|
6127
|
+
});
|
|
6128
|
+
} catch (err) {
|
|
6129
|
+
throw fsError2("ENOTCONN", `the host proxy is unreachable: ${err.message}`);
|
|
6130
|
+
}
|
|
6131
|
+
if (response.ok) return response;
|
|
6132
|
+
if (response.headers.get("X-Proxy-Error")) {
|
|
6133
|
+
await response.body?.cancel();
|
|
6134
|
+
if (retried || response.status !== 403) {
|
|
6135
|
+
throw fsError2("EACCES", `the host proxy refused this folder (${response.status})`);
|
|
6025
6136
|
}
|
|
6026
|
-
|
|
6137
|
+
this.grant = await this.regrant();
|
|
6138
|
+
if (handle)
|
|
6139
|
+
throw fsError2("ESTALE", "the folder was granted again, so its open files are gone");
|
|
6140
|
+
return this.send(path, init, false, true);
|
|
6141
|
+
}
|
|
6142
|
+
const errno = response.headers.get("X-Hostfs-Errno") ?? "EIO";
|
|
6143
|
+
const body = await response.json().catch(() => ({}));
|
|
6144
|
+
throw fsError2(errno, typeof body.message === "string" ? body.message : errno);
|
|
6145
|
+
}
|
|
6146
|
+
async call(op, body = {}, handle = false) {
|
|
6147
|
+
const response = await this.send(
|
|
6148
|
+
"/api/hostfs",
|
|
6149
|
+
{
|
|
6150
|
+
method: "POST",
|
|
6151
|
+
headers: { "Content-Type": "application/json" },
|
|
6152
|
+
body: JSON.stringify({ op, ...body })
|
|
6153
|
+
},
|
|
6154
|
+
handle
|
|
6155
|
+
);
|
|
6156
|
+
return await response.json();
|
|
6157
|
+
}
|
|
6158
|
+
async read(fh, offset, size, ifMatch) {
|
|
6159
|
+
const response = await this.send(
|
|
6160
|
+
"/api/hostfs",
|
|
6161
|
+
{
|
|
6162
|
+
method: "POST",
|
|
6163
|
+
headers: { "Content-Type": "application/json" },
|
|
6164
|
+
body: JSON.stringify({ op: "read", fh, offset, size, ...ifMatch ? { ifMatch } : {} })
|
|
6165
|
+
},
|
|
6166
|
+
true
|
|
6167
|
+
);
|
|
6168
|
+
return new Uint8Array(await response.arrayBuffer());
|
|
6169
|
+
}
|
|
6170
|
+
async write(fh, offset, bytes) {
|
|
6171
|
+
const response = await this.send(
|
|
6172
|
+
"/api/hostfs/write",
|
|
6173
|
+
{
|
|
6174
|
+
method: "PUT",
|
|
6175
|
+
headers: {
|
|
6176
|
+
"Content-Type": "application/octet-stream",
|
|
6177
|
+
"X-Hostfs-Request": JSON.stringify({ fh, offset })
|
|
6178
|
+
},
|
|
6179
|
+
body: bytes
|
|
6180
|
+
},
|
|
6181
|
+
true
|
|
6182
|
+
);
|
|
6183
|
+
await response.body?.cancel();
|
|
6184
|
+
}
|
|
6185
|
+
async watch(signal, onEvent, onOpen = () => void 0) {
|
|
6186
|
+
const response = await this.send("/api/hostfs/watch", { method: "POST", signal });
|
|
6187
|
+
onOpen();
|
|
6188
|
+
const reader = response.body.getReader();
|
|
6189
|
+
const decoder2 = new TextDecoder();
|
|
6190
|
+
let buffered = "";
|
|
6191
|
+
try {
|
|
6192
|
+
for (; ; ) {
|
|
6193
|
+
const { done: done4, value } = await reader.read();
|
|
6194
|
+
if (done4) return;
|
|
6195
|
+
buffered += decoder2.decode(value, { stream: true });
|
|
6196
|
+
let end = buffered.indexOf("\n");
|
|
6197
|
+
while (end >= 0) {
|
|
6198
|
+
const line = buffered.slice(0, end).trim();
|
|
6199
|
+
buffered = buffered.slice(end + 1);
|
|
6200
|
+
if (line) onEvent(JSON.parse(line));
|
|
6201
|
+
end = buffered.indexOf("\n");
|
|
6202
|
+
}
|
|
6203
|
+
}
|
|
6204
|
+
} catch (err) {
|
|
6205
|
+
if (signal.aborted) return;
|
|
6206
|
+
throw fsError2("ENOTCONN", `the host proxy's watch broke: ${err.message}`);
|
|
6207
|
+
} finally {
|
|
6208
|
+
reader.releaseLock();
|
|
6209
|
+
}
|
|
6210
|
+
}
|
|
6211
|
+
};
|
|
6212
|
+
function hostfsHandlers(current) {
|
|
6213
|
+
const handles = /* @__PURE__ */ new Map();
|
|
6214
|
+
let nextFh = 0;
|
|
6215
|
+
const fresh = (remote) => {
|
|
6216
|
+
if (remote.client.token === remote.token) return remote;
|
|
6217
|
+
throw fsError2("ESTALE", "the folder was granted again, so this open file is gone");
|
|
6218
|
+
};
|
|
6219
|
+
const live = (fh) => {
|
|
6220
|
+
const remote = handles.get(fh);
|
|
6221
|
+
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
6222
|
+
return fresh(remote);
|
|
6223
|
+
};
|
|
6224
|
+
const call = (op, body = {}) => current().call(op, body);
|
|
6225
|
+
async function writable(client, path, flags) {
|
|
6226
|
+
const opening = () => client.call("open", { path: rel(path), ...flags });
|
|
6227
|
+
try {
|
|
6228
|
+
return { opened: await opening(), restore: void 0 };
|
|
6229
|
+
} catch (err) {
|
|
6230
|
+
if (!flags.write || codeOf2(err) !== "EACCES") throw err;
|
|
6231
|
+
const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
|
|
6232
|
+
if (mode & 128) throw err;
|
|
6233
|
+
const restore = { path: rel(path), mode };
|
|
6234
|
+
await client.call("setattr", { path: rel(path), mode: mode | 128 });
|
|
6235
|
+
const opened2 = await opening().catch(async (again) => {
|
|
6236
|
+
await client.call("setattr", restore);
|
|
6237
|
+
throw again;
|
|
6238
|
+
});
|
|
6239
|
+
return { opened: opened2, restore };
|
|
6240
|
+
}
|
|
6027
6241
|
}
|
|
6028
|
-
const getattr = wrap((h) => h.getattr);
|
|
6029
|
-
const readdir = wrap((h) => h.readdir);
|
|
6030
6242
|
return {
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
mkdir: wrap((h) => h.mkdir),
|
|
6039
|
-
rmdir: wrap((h) => h.rmdir),
|
|
6040
|
-
unlink: wrap((h) => h.unlink),
|
|
6041
|
-
rename: wrap((h) => h.rename),
|
|
6042
|
-
symlink: wrap((h) => h.symlink),
|
|
6043
|
-
readlink: wrap((h) => h.readlink),
|
|
6044
|
-
setattr: wrap((h) => h.setattr)
|
|
6243
|
+
getattr: async (path) => await call("stat", { path: rel(path) }),
|
|
6244
|
+
async readdir(path) {
|
|
6245
|
+
const { entries } = await call(
|
|
6246
|
+
"list",
|
|
6247
|
+
{ path: rel(path) }
|
|
6248
|
+
);
|
|
6249
|
+
return entries.map(({ name: name2, attr }) => ({ name: name2, kind: attr.kind, attr }));
|
|
6045
6250
|
},
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6251
|
+
async open(path, flags) {
|
|
6252
|
+
const client = current();
|
|
6253
|
+
const token = client.token;
|
|
6254
|
+
const { opened: opened2, restore } = await writable(client, path, flags);
|
|
6255
|
+
handles.set(++nextFh, {
|
|
6256
|
+
client,
|
|
6257
|
+
token,
|
|
6258
|
+
fh: opened2.fh,
|
|
6259
|
+
etag: flags.write ? void 0 : opened2.attr?.etag,
|
|
6260
|
+
restore
|
|
6261
|
+
});
|
|
6262
|
+
return nextFh;
|
|
6049
6263
|
},
|
|
6050
|
-
|
|
6051
|
-
|
|
6264
|
+
async read(fh, offset, size) {
|
|
6265
|
+
const remote = live(fh);
|
|
6266
|
+
return remote.client.read(
|
|
6267
|
+
remote.fh,
|
|
6268
|
+
offset,
|
|
6269
|
+
Math.min(size, remote.client.maxIo),
|
|
6270
|
+
remote.etag
|
|
6271
|
+
);
|
|
6272
|
+
},
|
|
6273
|
+
async write(fh, offset, bytes) {
|
|
6274
|
+
const remote = live(fh);
|
|
6275
|
+
const most = remote.client.maxIo;
|
|
6276
|
+
for (let at = 0; at < bytes.length; at += most) {
|
|
6277
|
+
await remote.client.write(remote.fh, offset + at, bytes.subarray(at, at + most));
|
|
6278
|
+
}
|
|
6279
|
+
},
|
|
6280
|
+
async release(fh) {
|
|
6281
|
+
const remote = handles.get(fh);
|
|
6282
|
+
if (!remote) throw fsError2("EBADF", `handle ${fh}`);
|
|
6283
|
+
handles.delete(fh);
|
|
6284
|
+
try {
|
|
6285
|
+
await fresh(remote).client.call("release", { fh: remote.fh }, true);
|
|
6286
|
+
} finally {
|
|
6287
|
+
if (remote.restore) await current().call("setattr", remote.restore);
|
|
6288
|
+
}
|
|
6289
|
+
},
|
|
6290
|
+
mkdir: async (path) => void await call("mkdir", { path: rel(path) }),
|
|
6291
|
+
rmdir: async (path) => void await call("rmdir", { path: rel(path) }),
|
|
6292
|
+
unlink: async (path) => void await call("unlink", { path: rel(path) }),
|
|
6293
|
+
rename: async (from, to) => void await call("rename", { from: rel(from), to: rel(to) }),
|
|
6294
|
+
symlink: async (target, path) => void await call("symlink", { target, path: rel(path) }),
|
|
6295
|
+
readlink: async (path) => (await call("readlink", { path: rel(path) })).target,
|
|
6296
|
+
setattr: async (path, change) => void await call("setattr", { path: rel(path), ...change }),
|
|
6297
|
+
statfs: () => call("statfs")
|
|
6298
|
+
};
|
|
6299
|
+
}
|
|
6300
|
+
function sleep(ms, signal) {
|
|
6301
|
+
return new Promise((resolve) => {
|
|
6302
|
+
const done4 = () => {
|
|
6303
|
+
clearTimeout(timer);
|
|
6304
|
+
signal.removeEventListener("abort", done4);
|
|
6305
|
+
resolve();
|
|
6306
|
+
};
|
|
6307
|
+
const timer = setTimeout(done4, ms);
|
|
6308
|
+
signal.addEventListener("abort", done4);
|
|
6309
|
+
});
|
|
6310
|
+
}
|
|
6311
|
+
function keepWatching(loop, signal) {
|
|
6312
|
+
const silenceMs = loop.silenceMs ?? 45e3;
|
|
6313
|
+
const [first, most] = loop.backoffMs ?? [1e3, 3e4];
|
|
6314
|
+
const ready2 = Promise.withResolvers();
|
|
6315
|
+
let wait = first;
|
|
6316
|
+
let up = false;
|
|
6317
|
+
let current;
|
|
6318
|
+
async function session() {
|
|
6319
|
+
const session2 = new AbortController();
|
|
6320
|
+
current = session2;
|
|
6321
|
+
const stop = () => session2.abort();
|
|
6322
|
+
signal.addEventListener("abort", stop);
|
|
6323
|
+
let timer;
|
|
6324
|
+
const quiet = () => {
|
|
6325
|
+
clearTimeout(timer);
|
|
6326
|
+
timer = setTimeout(stop, silenceMs);
|
|
6327
|
+
};
|
|
6328
|
+
let opened2 = false;
|
|
6329
|
+
try {
|
|
6330
|
+
const client = await loop.connect();
|
|
6331
|
+
await client.watch(
|
|
6332
|
+
session2.signal,
|
|
6333
|
+
(event) => {
|
|
6334
|
+
quiet();
|
|
6335
|
+
if (!event.ping) loop.event(event);
|
|
6336
|
+
},
|
|
6337
|
+
() => {
|
|
6338
|
+
quiet();
|
|
6339
|
+
opened2 = true;
|
|
6340
|
+
up = true;
|
|
6341
|
+
loop.up(client);
|
|
6342
|
+
ready2.resolve();
|
|
6343
|
+
}
|
|
6344
|
+
);
|
|
6345
|
+
return opened2;
|
|
6346
|
+
} catch {
|
|
6347
|
+
return opened2;
|
|
6348
|
+
} finally {
|
|
6349
|
+
clearTimeout(timer);
|
|
6350
|
+
signal.removeEventListener("abort", stop);
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
void (async () => {
|
|
6354
|
+
while (!signal.aborted) {
|
|
6355
|
+
const began = Date.now();
|
|
6356
|
+
const opened2 = await session();
|
|
6357
|
+
if (signal.aborted) return;
|
|
6358
|
+
if (opened2 && Date.now() - began >= first) {
|
|
6359
|
+
wait = first;
|
|
6360
|
+
continue;
|
|
6361
|
+
}
|
|
6362
|
+
if (!opened2 && up) {
|
|
6363
|
+
up = false;
|
|
6364
|
+
loop.down();
|
|
6365
|
+
}
|
|
6366
|
+
ready2.resolve();
|
|
6367
|
+
await sleep(wait, signal);
|
|
6368
|
+
wait = Math.min(wait * 2, most);
|
|
6369
|
+
}
|
|
6370
|
+
})();
|
|
6371
|
+
return { ready: ready2.promise, kick: () => current?.abort() };
|
|
6372
|
+
}
|
|
6373
|
+
function invalidation(event) {
|
|
6374
|
+
return event.paths ?? true;
|
|
6375
|
+
}
|
|
6376
|
+
async function openHostfs(spec, hook, fetch2, timing = {}) {
|
|
6377
|
+
const readonly2 = spec.options?.ro !== void 0;
|
|
6378
|
+
const { port1, port2 } = new MessageChannel();
|
|
6379
|
+
const slot = mediumSlot(
|
|
6380
|
+
async (err) => unreachable(err),
|
|
6381
|
+
() => watching.kick()
|
|
6382
|
+
);
|
|
6383
|
+
const stop = new AbortController();
|
|
6384
|
+
const grant = () => hook(spec.source, { readonly: readonly2 });
|
|
6385
|
+
let active;
|
|
6386
|
+
const handlers = hostfsHandlers(() => active);
|
|
6387
|
+
const served = serveFilesystem(port2, slot.handlers, {
|
|
6388
|
+
symlinks: true,
|
|
6389
|
+
chmod: true,
|
|
6390
|
+
listingStats: true,
|
|
6391
|
+
maxIo: HOSTFS_IO,
|
|
6392
|
+
...readonly2 ? { readonly: true } : {}
|
|
6393
|
+
});
|
|
6394
|
+
const watching = keepWatching(
|
|
6395
|
+
{
|
|
6396
|
+
connect: async () => new HostfsClient(await grant(), grant, fetch2),
|
|
6397
|
+
up(client) {
|
|
6398
|
+
active = client;
|
|
6399
|
+
slot.insert(client, handlers);
|
|
6400
|
+
served.invalidate(true);
|
|
6401
|
+
},
|
|
6402
|
+
event: (event) => served.invalidate(invalidation(event)),
|
|
6403
|
+
down() {
|
|
6404
|
+
slot.eject();
|
|
6405
|
+
served.invalidate(true);
|
|
6406
|
+
},
|
|
6407
|
+
...timing
|
|
6408
|
+
},
|
|
6409
|
+
stop.signal
|
|
6410
|
+
);
|
|
6411
|
+
await watching.ready;
|
|
6412
|
+
return {
|
|
6413
|
+
port: port1,
|
|
6414
|
+
present: slot.present,
|
|
6415
|
+
dispose() {
|
|
6416
|
+
stop.abort();
|
|
6417
|
+
port1.close();
|
|
6418
|
+
port2.close();
|
|
6052
6419
|
}
|
|
6053
6420
|
};
|
|
6054
6421
|
}
|
|
@@ -6157,6 +6524,7 @@ var DriverConnection = class _DriverConnection {
|
|
|
6157
6524
|
|
|
6158
6525
|
// src/mount/mount-fs.ts
|
|
6159
6526
|
var MAX_LINKS4 = 40;
|
|
6527
|
+
var STALE_RETRIES = 3;
|
|
6160
6528
|
var DEFAULT_IO = 8 * 1024 * 1024;
|
|
6161
6529
|
var UNITS = { "": 1, k: 1024, m: 1024 ** 2, g: 1024 ** 3, t: 1024 ** 4 };
|
|
6162
6530
|
function parseSize(text) {
|
|
@@ -6170,7 +6538,8 @@ function heldUnder(held, target) {
|
|
|
6170
6538
|
return false;
|
|
6171
6539
|
}
|
|
6172
6540
|
var parentOf4 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
6173
|
-
|
|
6541
|
+
var readonly = (mount) => mount.caps.readonly === true;
|
|
6542
|
+
function statOf(attr, path, mount) {
|
|
6174
6543
|
const mtime = new Date(attr.mtime);
|
|
6175
6544
|
const fallback = attr.kind === "directory" ? 16877 : attr.kind === "symlink" ? 41471 : 33188;
|
|
6176
6545
|
return {
|
|
@@ -6183,7 +6552,8 @@ function statOf(attr, path, dev) {
|
|
|
6183
6552
|
atime: mtime,
|
|
6184
6553
|
ctime: mtime,
|
|
6185
6554
|
ino: attr.ino ?? inodeOf(path),
|
|
6186
|
-
dev
|
|
6555
|
+
dev: mount.dev,
|
|
6556
|
+
...readonly(mount) ? { readonly: true } : {}
|
|
6187
6557
|
};
|
|
6188
6558
|
}
|
|
6189
6559
|
function concat4(chunks2, total) {
|
|
@@ -6223,8 +6593,8 @@ var MountTable = class {
|
|
|
6223
6593
|
best = mount;
|
|
6224
6594
|
}
|
|
6225
6595
|
if (!best) return void 0;
|
|
6226
|
-
const
|
|
6227
|
-
return { mount: best, rel:
|
|
6596
|
+
const rel2 = best.target === "/" ? path : path.slice(best.target.length);
|
|
6597
|
+
return { mount: best, rel: rel2 || "/" };
|
|
6228
6598
|
}
|
|
6229
6599
|
async mount(spec, fs) {
|
|
6230
6600
|
const target = normalizePath(spec.target);
|
|
@@ -6329,20 +6699,20 @@ var MountTable = class {
|
|
|
6329
6699
|
mount.lists.delete(parentOf4(path));
|
|
6330
6700
|
for (const key of [...mount.lists.keys()]) if (within3(key, path)) mount.lists.delete(key);
|
|
6331
6701
|
}
|
|
6332
|
-
async getattr(mount, path,
|
|
6702
|
+
async getattr(mount, path, rel2) {
|
|
6333
6703
|
if (mount.conn.failure) throw mount.conn.failure;
|
|
6334
6704
|
const cached = mount.attrs.get(path);
|
|
6335
6705
|
if (cached && cached.expires > Date.now()) return cached.value;
|
|
6336
|
-
const attr = await mount.conn.call({ op: "getattr", path:
|
|
6706
|
+
const attr = await mount.conn.call({ op: "getattr", path: rel2 });
|
|
6337
6707
|
const ttl = mount.caps.attrTtl ?? 1e3;
|
|
6338
6708
|
if (ttl > 0) mount.attrs.set(path, { value: attr, expires: Date.now() + ttl });
|
|
6339
6709
|
return attr;
|
|
6340
6710
|
}
|
|
6341
|
-
async readdir(mount, path,
|
|
6711
|
+
async readdir(mount, path, rel2) {
|
|
6342
6712
|
if (mount.conn.failure) throw mount.conn.failure;
|
|
6343
6713
|
const cached = mount.lists.get(path);
|
|
6344
6714
|
if (cached && cached.expires > Date.now()) return cached.value;
|
|
6345
|
-
const entries = await mount.conn.call({ op: "readdir", path:
|
|
6715
|
+
const entries = await mount.conn.call({ op: "readdir", path: rel2 });
|
|
6346
6716
|
const ttl = mount.caps.entryTtl ?? mount.caps.attrTtl ?? 1e3;
|
|
6347
6717
|
if (ttl > 0) mount.lists.set(path, { value: entries, expires: Date.now() + ttl });
|
|
6348
6718
|
return entries;
|
|
@@ -6369,7 +6739,7 @@ var MountFs = class {
|
|
|
6369
6739
|
return this.table.locate(normalizePath(path));
|
|
6370
6740
|
}
|
|
6371
6741
|
writable(mount, path) {
|
|
6372
|
-
if (mount
|
|
6742
|
+
if (readonly(mount)) throw errnoError("EROFS", path);
|
|
6373
6743
|
}
|
|
6374
6744
|
async follow(path, hops = 0, create = false) {
|
|
6375
6745
|
const found = this.at(path);
|
|
@@ -6387,10 +6757,10 @@ var MountFs = class {
|
|
|
6387
6757
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
6388
6758
|
return this.follow(this.base.resolvePath(parentOf4(real), target), hops + 1, create);
|
|
6389
6759
|
}
|
|
6390
|
-
async open(mount,
|
|
6760
|
+
async open(mount, rel2, write) {
|
|
6391
6761
|
return await mount.conn.call({
|
|
6392
6762
|
op: "open",
|
|
6393
|
-
path:
|
|
6763
|
+
path: rel2,
|
|
6394
6764
|
write,
|
|
6395
6765
|
create: write,
|
|
6396
6766
|
truncate: write,
|
|
@@ -6407,13 +6777,22 @@ var MountFs = class {
|
|
|
6407
6777
|
const real = await this.follow(path);
|
|
6408
6778
|
const found = this.at(real);
|
|
6409
6779
|
if (!found) return this.base.readFileBuffer(real);
|
|
6410
|
-
|
|
6411
|
-
|
|
6780
|
+
for (let attempt = 1; ; attempt++) {
|
|
6781
|
+
try {
|
|
6782
|
+
return await this.readWhole(found.mount, real, found.rel);
|
|
6783
|
+
} catch (err) {
|
|
6784
|
+
if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
|
|
6785
|
+
this.table.forget(found.mount, real);
|
|
6786
|
+
}
|
|
6787
|
+
}
|
|
6788
|
+
}
|
|
6789
|
+
async readWhole(mount, real, rel2) {
|
|
6790
|
+
const attr = await this.table.getattr(mount, real, rel2);
|
|
6412
6791
|
if (attr.kind === "directory") throw errnoError("EISDIR", real);
|
|
6413
6792
|
if (mount.maxFile > 0 && attr.size > mount.maxFile) {
|
|
6414
6793
|
throw errnoError("EFBIG", `${real} is larger than this mount's maxfile (${mount.maxFile})`);
|
|
6415
6794
|
}
|
|
6416
|
-
const fh = await this.open(mount,
|
|
6795
|
+
const fh = await this.open(mount, rel2, false);
|
|
6417
6796
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
6418
6797
|
const chunks2 = [];
|
|
6419
6798
|
let total = 0;
|
|
@@ -6439,7 +6818,7 @@ var MountFs = class {
|
|
|
6439
6818
|
const real = await this.follow(path, 0, true);
|
|
6440
6819
|
const found = this.at(real);
|
|
6441
6820
|
if (!found) return this.base.writeFile(real, content);
|
|
6442
|
-
const { mount, rel } = found;
|
|
6821
|
+
const { mount, rel: rel2 } = found;
|
|
6443
6822
|
this.writable(mount, real);
|
|
6444
6823
|
const bytes = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
6445
6824
|
if (mount.maxFile > 0 && bytes.length > mount.maxFile) {
|
|
@@ -6448,8 +6827,17 @@ var MountFs = class {
|
|
|
6448
6827
|
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
6449
6828
|
);
|
|
6450
6829
|
}
|
|
6830
|
+
for (let attempt = 1; ; attempt++) {
|
|
6831
|
+
try {
|
|
6832
|
+
return await this.writeWhole(mount, real, rel2, bytes);
|
|
6833
|
+
} catch (err) {
|
|
6834
|
+
if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
|
|
6835
|
+
}
|
|
6836
|
+
}
|
|
6837
|
+
}
|
|
6838
|
+
async writeWhole(mount, real, rel2, bytes) {
|
|
6451
6839
|
this.table.forget(mount, real);
|
|
6452
|
-
const fh = await this.open(mount,
|
|
6840
|
+
const fh = await this.open(mount, rel2, true);
|
|
6453
6841
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
6454
6842
|
try {
|
|
6455
6843
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
@@ -6480,7 +6868,7 @@ var MountFs = class {
|
|
|
6480
6868
|
const found = this.at(path);
|
|
6481
6869
|
if (!found) return this.base.lstat(path);
|
|
6482
6870
|
const real = normalizePath(path);
|
|
6483
|
-
return statOf(await this.table.getattr(found.mount, real, found.rel), real, found.mount
|
|
6871
|
+
return statOf(await this.table.getattr(found.mount, real, found.rel), real, found.mount);
|
|
6484
6872
|
}
|
|
6485
6873
|
async stat(path) {
|
|
6486
6874
|
const real = await this.follow(path);
|
|
@@ -6507,7 +6895,7 @@ var MountFs = class {
|
|
|
6507
6895
|
list.map(async (entry) => {
|
|
6508
6896
|
const child = normalizePath(`${real}/${entry.name}`);
|
|
6509
6897
|
if (entry.attr && mount.caps.listingStats) {
|
|
6510
|
-
return [entry.name, statOf(entry.attr, child, mount
|
|
6898
|
+
return [entry.name, statOf(entry.attr, child, mount)];
|
|
6511
6899
|
}
|
|
6512
6900
|
return [entry.name, await this.lstat(child).catch(() => null)];
|
|
6513
6901
|
})
|
|
@@ -6516,16 +6904,16 @@ var MountFs = class {
|
|
|
6516
6904
|
async mkdir(path, options = {}) {
|
|
6517
6905
|
const found = this.at(path);
|
|
6518
6906
|
if (!found) return this.base.mkdir(path, options);
|
|
6519
|
-
const { mount, rel } = found;
|
|
6907
|
+
const { mount, rel: rel2 } = found;
|
|
6520
6908
|
const real = normalizePath(path);
|
|
6521
6909
|
this.writable(mount, real);
|
|
6522
6910
|
if (!options.recursive) {
|
|
6523
|
-
if (
|
|
6911
|
+
if (rel2 === "/") throw errnoError("EEXIST", real);
|
|
6524
6912
|
this.table.forget(mount, real);
|
|
6525
|
-
await mount.conn.call({ op: "mkdir", path:
|
|
6913
|
+
await mount.conn.call({ op: "mkdir", path: rel2 });
|
|
6526
6914
|
return;
|
|
6527
6915
|
}
|
|
6528
|
-
const parts =
|
|
6916
|
+
const parts = rel2.split("/").filter(Boolean);
|
|
6529
6917
|
for (let n = 1; n <= parts.length; n++) {
|
|
6530
6918
|
const sub = `/${parts.slice(0, n).join("/")}`;
|
|
6531
6919
|
const full = normalizePath(`${mount.target}${sub}`);
|
|
@@ -6539,25 +6927,25 @@ var MountFs = class {
|
|
|
6539
6927
|
async rm(path, options = {}) {
|
|
6540
6928
|
const found = this.at(path);
|
|
6541
6929
|
if (!found) return this.base.rm(path, options);
|
|
6542
|
-
const { mount, rel } = found;
|
|
6930
|
+
const { mount, rel: rel2 } = found;
|
|
6543
6931
|
const real = normalizePath(path);
|
|
6544
|
-
if (
|
|
6932
|
+
if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
|
|
6545
6933
|
this.writable(mount, real);
|
|
6546
6934
|
let attr;
|
|
6547
6935
|
try {
|
|
6548
|
-
attr = await this.table.getattr(mount, real,
|
|
6936
|
+
attr = await this.table.getattr(mount, real, rel2);
|
|
6549
6937
|
} catch (err) {
|
|
6550
6938
|
if (options.force && err.code === "ENOENT") return;
|
|
6551
6939
|
throw err;
|
|
6552
6940
|
}
|
|
6553
6941
|
this.table.forget(mount, real);
|
|
6554
6942
|
if (attr.kind === "directory" && options.recursive) {
|
|
6555
|
-
for (const entry of await this.table.readdir(mount, real,
|
|
6943
|
+
for (const entry of await this.table.readdir(mount, real, rel2)) {
|
|
6556
6944
|
await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
|
|
6557
6945
|
}
|
|
6558
6946
|
this.table.forget(mount, real);
|
|
6559
6947
|
}
|
|
6560
|
-
await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path:
|
|
6948
|
+
await mount.conn.call({ op: attr.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
|
|
6561
6949
|
this.table.forget(mount, real);
|
|
6562
6950
|
}
|
|
6563
6951
|
async rename(from, to) {
|
|
@@ -7019,6 +7407,7 @@ var Launcher = class {
|
|
|
7019
7407
|
media;
|
|
7020
7408
|
onMountPending;
|
|
7021
7409
|
removable = /* @__PURE__ */ new Map();
|
|
7410
|
+
hostfs;
|
|
7022
7411
|
inserted = /* @__PURE__ */ new Map();
|
|
7023
7412
|
openFiles = /* @__PURE__ */ new Set();
|
|
7024
7413
|
held = /* @__PURE__ */ new Set();
|
|
@@ -7036,6 +7425,7 @@ var Launcher = class {
|
|
|
7036
7425
|
this.createDriverWorker = options.createDriverWorker;
|
|
7037
7426
|
this.media = options.media ?? memoryMedia();
|
|
7038
7427
|
this.onMountPending = options.onMountPending;
|
|
7428
|
+
this.hostfs = options;
|
|
7039
7429
|
this.pnpmHome = options.env?.PNPM_HOME ?? PNPM_HOME;
|
|
7040
7430
|
this.modulesDir = options.modules ?? "/node_modules";
|
|
7041
7431
|
this.env = { ...networkEnv(), ...options.env };
|
|
@@ -7301,6 +7691,17 @@ var Launcher = class {
|
|
|
7301
7691
|
}
|
|
7302
7692
|
async driver(type, spec) {
|
|
7303
7693
|
if (type === "fsa") return this.fsaDriver(spec);
|
|
7694
|
+
if (type === "hostfs") {
|
|
7695
|
+
const { hostfs, hostfsFetch, hostfsTiming } = this.hostfs;
|
|
7696
|
+
if (!hostfs)
|
|
7697
|
+
throw fsError("ENODEV", "this kernel has no hostfs hook (createKernel({ hostfs }))");
|
|
7698
|
+
return openHostfs(
|
|
7699
|
+
spec,
|
|
7700
|
+
hostfs,
|
|
7701
|
+
hostfsFetch ?? ((url, init) => fetch(url, init)),
|
|
7702
|
+
hostfsTiming
|
|
7703
|
+
);
|
|
7704
|
+
}
|
|
7304
7705
|
if (type === "tmpfs") {
|
|
7305
7706
|
const { port1, port2 } = new MessageChannel();
|
|
7306
7707
|
serveFilesystem(port2, tmpfs(), { symlinks: true, chmod: true, attrTtl: 0 });
|