@ai-ecoverse/slicc-kernel 1.35.1 → 1.37.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 +5 -3
- package/dist/driver-worker.js.map +2 -2
- package/dist/driver.js.map +2 -2
- package/dist/kernel-worker.js +304 -61
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node-driver-worker.js.map +2 -2
- package/dist/node.js +304 -61
- package/dist/node.js.map +3 -3
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -1938,11 +1938,41 @@ var OpfsFs = class {
|
|
|
1938
1938
|
const fallback = directory || EXECUTABLE.test(path) ? 493 : 420;
|
|
1939
1939
|
return (directory ? S_IFDIR : S_IFREG) | (meta?.mode ?? fallback);
|
|
1940
1940
|
}
|
|
1941
|
+
dirTimes = /* @__PURE__ */ new Map();
|
|
1942
|
+
dirDirty = /* @__PURE__ */ new Set();
|
|
1943
|
+
dirFlush;
|
|
1944
|
+
touch(...dirs) {
|
|
1945
|
+
const now = Date.now();
|
|
1946
|
+
for (const dir of dirs) {
|
|
1947
|
+
this.dirTimes.set(dir, now);
|
|
1948
|
+
this.dirDirty.add(dir);
|
|
1949
|
+
}
|
|
1950
|
+
this.dirFlush ??= Promise.resolve().then(() => this.flushDirTimes()).catch(() => void 0).finally(() => {
|
|
1951
|
+
this.dirFlush = void 0;
|
|
1952
|
+
});
|
|
1953
|
+
}
|
|
1954
|
+
async flushDirTimes() {
|
|
1955
|
+
for (const path of this.dirDirty) {
|
|
1956
|
+
this.dirDirty.delete(path);
|
|
1957
|
+
const ms = this.dirTimes.get(path);
|
|
1958
|
+
if (ms === void 0) continue;
|
|
1959
|
+
await this.meta.update(path, (entry) => ({
|
|
1960
|
+
...entry,
|
|
1961
|
+
path,
|
|
1962
|
+
mtimeMs: ms,
|
|
1963
|
+
mtimeFor: 0,
|
|
1964
|
+
ctimeMs: ms
|
|
1965
|
+
}));
|
|
1966
|
+
if (this.dirTimes.get(path) === ms) this.dirTimes.delete(path);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1941
1969
|
async statOf(path, handle, meta) {
|
|
1942
1970
|
const directory = handle.kind === "directory";
|
|
1943
1971
|
const file = directory ? void 0 : await handle.getFile();
|
|
1944
1972
|
const modified = file?.lastModified ?? 0;
|
|
1945
|
-
const
|
|
1973
|
+
const touched = directory ? this.dirTimes.get(path) : void 0;
|
|
1974
|
+
const kept = meta?.mtimeMs !== void 0 && meta.mtimeFor === modified ? meta.mtimeMs : modified;
|
|
1975
|
+
const mtime = touched ?? kept;
|
|
1946
1976
|
return {
|
|
1947
1977
|
isFile: !directory,
|
|
1948
1978
|
isDirectory: directory,
|
|
@@ -1951,7 +1981,7 @@ var OpfsFs = class {
|
|
|
1951
1981
|
mode: this.mode(path, directory, meta),
|
|
1952
1982
|
mtime: new Date(mtime),
|
|
1953
1983
|
atime: new Date(meta?.atimeMs ?? mtime),
|
|
1954
|
-
ctime: new Date(Math.max(meta?.ctimeMs ?? mtime, modified)),
|
|
1984
|
+
ctime: new Date(Math.max(touched ?? meta?.ctimeMs ?? mtime, modified)),
|
|
1955
1985
|
ino: meta?.ino ?? inodeOf(path),
|
|
1956
1986
|
...this.ranged && !directory ? { ranged: true } : {}
|
|
1957
1987
|
};
|
|
@@ -2028,6 +2058,19 @@ var OpfsFs = class {
|
|
|
2028
2058
|
const handle = await this.existing(path);
|
|
2029
2059
|
await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
|
|
2030
2060
|
}
|
|
2061
|
+
async writeSession(path, truncate) {
|
|
2062
|
+
const handle = (await this.fileFor(path))[0];
|
|
2063
|
+
const writable = await this.retrying(
|
|
2064
|
+
path,
|
|
2065
|
+
() => handle.createWritable({ keepExistingData: !truncate }),
|
|
2066
|
+
true
|
|
2067
|
+
);
|
|
2068
|
+
return {
|
|
2069
|
+
write: (at, bytes) => writable.write({ type: "write", position: at, data: bytes }),
|
|
2070
|
+
truncate: (size) => writable.truncate(size),
|
|
2071
|
+
close: () => writable.close()
|
|
2072
|
+
};
|
|
2073
|
+
}
|
|
2031
2074
|
async readFile(path) {
|
|
2032
2075
|
return new TextDecoder().decode(await this.readFileBuffer(path));
|
|
2033
2076
|
}
|
|
@@ -2045,10 +2088,12 @@ var OpfsFs = class {
|
|
|
2045
2088
|
const [target, existed] = await this.writeTarget(path);
|
|
2046
2089
|
const [dir, name2] = await this.parent(target);
|
|
2047
2090
|
try {
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2091
|
+
const handle = await this.inDir(
|
|
2092
|
+
dir,
|
|
2093
|
+
(parent) => parent.getFileHandle(name2, { create: true })
|
|
2094
|
+
);
|
|
2095
|
+
if (!existed) this.touch(parentOf2(target));
|
|
2096
|
+
return [handle, existed];
|
|
2052
2097
|
} catch (err) {
|
|
2053
2098
|
const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
|
|
2054
2099
|
throw code ? fsError(code, path) : translate(err, path);
|
|
@@ -2106,6 +2151,7 @@ var OpfsFs = class {
|
|
|
2106
2151
|
const parent = await this.locate(parentOf2(path), true);
|
|
2107
2152
|
if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
|
|
2108
2153
|
await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
|
|
2154
|
+
this.touch(parent.path);
|
|
2109
2155
|
}
|
|
2110
2156
|
async rm(path, options = {}) {
|
|
2111
2157
|
let found;
|
|
@@ -2129,6 +2175,7 @@ var OpfsFs = class {
|
|
|
2129
2175
|
this.changed(found.path);
|
|
2130
2176
|
}
|
|
2131
2177
|
await this.meta.remove([found.path]);
|
|
2178
|
+
this.touch(parentOf2(found.path));
|
|
2132
2179
|
}
|
|
2133
2180
|
async rename(from, to) {
|
|
2134
2181
|
if (from === to) return;
|
|
@@ -2160,6 +2207,7 @@ var OpfsFs = class {
|
|
|
2160
2207
|
if (source.link) {
|
|
2161
2208
|
if (target?.handle) await this.rm(destination);
|
|
2162
2209
|
await this.meta.move(source.path, destination, carry);
|
|
2210
|
+
this.touch(parentOf2(source.path), parent.path);
|
|
2163
2211
|
return;
|
|
2164
2212
|
}
|
|
2165
2213
|
const dir = parent.handle;
|
|
@@ -2181,6 +2229,7 @@ var OpfsFs = class {
|
|
|
2181
2229
|
}
|
|
2182
2230
|
if (copied) await this.restamp(destination, timed);
|
|
2183
2231
|
this.changed(source.path, destination);
|
|
2232
|
+
this.touch(parentOf2(source.path), parent.path);
|
|
2184
2233
|
}
|
|
2185
2234
|
async modified(path) {
|
|
2186
2235
|
const handle = await this.handle(path);
|
|
@@ -2244,6 +2293,7 @@ var OpfsFs = class {
|
|
|
2244
2293
|
mtimeMs: now,
|
|
2245
2294
|
ctimeMs: now
|
|
2246
2295
|
}));
|
|
2296
|
+
this.touch(parent.path);
|
|
2247
2297
|
}
|
|
2248
2298
|
async realpath(path, follow) {
|
|
2249
2299
|
return (await this.locate(path, follow)).path;
|
|
@@ -2284,6 +2334,7 @@ var OpfsFs = class {
|
|
|
2284
2334
|
}
|
|
2285
2335
|
const handle = found.handle;
|
|
2286
2336
|
const file = handle.kind === "file" ? await handle.getFile() : void 0;
|
|
2337
|
+
this.dirTimes.delete(found.path);
|
|
2287
2338
|
const ctimeMs = Date.now();
|
|
2288
2339
|
await this.meta.update(found.path, (entry) => ({
|
|
2289
2340
|
...entry,
|
|
@@ -3198,6 +3249,35 @@ async function scanFilesystems(fs, modules) {
|
|
|
3198
3249
|
}
|
|
3199
3250
|
return found;
|
|
3200
3251
|
}
|
|
3252
|
+
var SUFFIX = /^\.[A-Za-z0-9_+-][A-Za-z0-9._+-]*$/;
|
|
3253
|
+
async function scanBinfmts(fs, modules) {
|
|
3254
|
+
const found = /* @__PURE__ */ new Map();
|
|
3255
|
+
for (const pkg of await packagesIn(fs, modules)) {
|
|
3256
|
+
let manifest;
|
|
3257
|
+
try {
|
|
3258
|
+
manifest = JSON.parse(await fs.readFile(`${pkg}/package.json`));
|
|
3259
|
+
} catch {
|
|
3260
|
+
continue;
|
|
3261
|
+
}
|
|
3262
|
+
const declared = manifest.slicc?.binfmt;
|
|
3263
|
+
if (!declared || typeof declared !== "object") continue;
|
|
3264
|
+
for (const [suffix, command] of Object.entries(declared)) {
|
|
3265
|
+
if (!SUFFIX.test(suffix) || typeof command !== "string" || !NAME.test(command)) continue;
|
|
3266
|
+
if (!found.has(suffix)) found.set(suffix, command);
|
|
3267
|
+
}
|
|
3268
|
+
}
|
|
3269
|
+
return found;
|
|
3270
|
+
}
|
|
3271
|
+
function binfmtOf(binfmts, path) {
|
|
3272
|
+
const name2 = path.slice(path.lastIndexOf("/") + 1);
|
|
3273
|
+
let best;
|
|
3274
|
+
for (const suffix of binfmts.keys()) {
|
|
3275
|
+
if (name2.length > suffix.length && name2.endsWith(suffix)) {
|
|
3276
|
+
if (best === void 0 || suffix.length > best.length) best = suffix;
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
return best === void 0 ? void 0 : binfmts.get(best);
|
|
3280
|
+
}
|
|
3201
3281
|
|
|
3202
3282
|
// src/fs/commands.ts
|
|
3203
3283
|
var S_IFDIR2 = 16384;
|
|
@@ -3703,18 +3783,36 @@ function parseSize(text) {
|
|
|
3703
3783
|
return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
|
|
3704
3784
|
}
|
|
3705
3785
|
var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
3786
|
+
var HeldSet = class extends Set {
|
|
3787
|
+
idle;
|
|
3788
|
+
constructor(idle) {
|
|
3789
|
+
super();
|
|
3790
|
+
this.idle = idle;
|
|
3791
|
+
}
|
|
3792
|
+
add(holds) {
|
|
3793
|
+
holds.onIdle = this.idle;
|
|
3794
|
+
return super.add(holds);
|
|
3795
|
+
}
|
|
3796
|
+
delete(holds) {
|
|
3797
|
+
const removed = super.delete(holds);
|
|
3798
|
+
for (const path of holds.keys()) this.idle(path);
|
|
3799
|
+
return removed;
|
|
3800
|
+
}
|
|
3801
|
+
};
|
|
3706
3802
|
function heldUnder(held, target) {
|
|
3707
3803
|
for (const paths of held) for (const path of paths.keys()) if (within2(path, target)) return true;
|
|
3708
3804
|
return false;
|
|
3709
3805
|
}
|
|
3710
3806
|
var HeldPaths = class extends Map {
|
|
3711
3807
|
revoked = /* @__PURE__ */ new Set();
|
|
3808
|
+
onIdle;
|
|
3712
3809
|
hold(path, on) {
|
|
3713
3810
|
const count = (this.get(path) ?? 0) + (on ? 1 : -1);
|
|
3714
3811
|
if (count > 0) this.set(path, count);
|
|
3715
3812
|
else {
|
|
3716
3813
|
this.delete(path);
|
|
3717
3814
|
this.revoked.delete(path);
|
|
3815
|
+
this.onIdle?.(path);
|
|
3718
3816
|
}
|
|
3719
3817
|
}
|
|
3720
3818
|
revoke(prefix) {
|
|
@@ -3782,6 +3880,8 @@ function spliced(bytes, offset, part) {
|
|
|
3782
3880
|
}
|
|
3783
3881
|
var MountTable = class {
|
|
3784
3882
|
mounts = /* @__PURE__ */ new Map();
|
|
3883
|
+
sessions = /* @__PURE__ */ new Map();
|
|
3884
|
+
committing = /* @__PURE__ */ new Map();
|
|
3785
3885
|
devices = 0;
|
|
3786
3886
|
pending = /* @__PURE__ */ new Set();
|
|
3787
3887
|
notes = /* @__PURE__ */ new Map();
|
|
@@ -3906,8 +4006,69 @@ var MountTable = class {
|
|
|
3906
4006
|
if (!detach && this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
|
|
3907
4007
|
this.notes.delete(at);
|
|
3908
4008
|
this.mounts.delete(at);
|
|
3909
|
-
mount.
|
|
3910
|
-
mount.
|
|
4009
|
+
const waits = [...this.sessions].filter(([, s]) => s.mount === mount).map(([real]) => this.commit(real));
|
|
4010
|
+
for (const c of this.committing.values()) if (c.mount === mount) waits.push(c.done);
|
|
4011
|
+
const close = () => {
|
|
4012
|
+
mount.conn.close();
|
|
4013
|
+
mount.dispose();
|
|
4014
|
+
};
|
|
4015
|
+
if (waits.length === 0) close();
|
|
4016
|
+
else void Promise.allSettled(waits).then(close);
|
|
4017
|
+
}
|
|
4018
|
+
session(mount, real, rel2) {
|
|
4019
|
+
let session = this.sessions.get(real);
|
|
4020
|
+
if (!session) {
|
|
4021
|
+
const open = () => mount.conn.call({
|
|
4022
|
+
op: "open",
|
|
4023
|
+
path: rel2,
|
|
4024
|
+
write: true,
|
|
4025
|
+
create: true,
|
|
4026
|
+
truncate: false,
|
|
4027
|
+
exclusive: false
|
|
4028
|
+
});
|
|
4029
|
+
const before = this.committing.get(real)?.done ?? Promise.resolve();
|
|
4030
|
+
const fh = before.then(open, open);
|
|
4031
|
+
session = { mount, fh, extents: [], truncated: false };
|
|
4032
|
+
this.sessions.set(real, session);
|
|
4033
|
+
fh.catch(() => this.sessions.delete(real));
|
|
4034
|
+
}
|
|
4035
|
+
return session;
|
|
4036
|
+
}
|
|
4037
|
+
writing(real) {
|
|
4038
|
+
return this.sessions.get(real);
|
|
4039
|
+
}
|
|
4040
|
+
async commit(real) {
|
|
4041
|
+
const session = this.sessions.get(real);
|
|
4042
|
+
if (!session) return this.committing.get(real)?.done;
|
|
4043
|
+
this.sessions.delete(real);
|
|
4044
|
+
const done4 = (async () => {
|
|
4045
|
+
try {
|
|
4046
|
+
await session.mount.conn.call({ op: "release", fh: await session.fh });
|
|
4047
|
+
} finally {
|
|
4048
|
+
this.forget(session.mount, real);
|
|
4049
|
+
}
|
|
4050
|
+
})();
|
|
4051
|
+
const entry = { mount: session.mount, done: done4 };
|
|
4052
|
+
this.committing.set(real, entry);
|
|
4053
|
+
const settle = () => {
|
|
4054
|
+
if (this.committing.get(real) === entry) this.committing.delete(real);
|
|
4055
|
+
};
|
|
4056
|
+
done4.then(settle, settle);
|
|
4057
|
+
return done4;
|
|
4058
|
+
}
|
|
4059
|
+
async commitUnder(prefix) {
|
|
4060
|
+
const at = normalizePath(prefix);
|
|
4061
|
+
await Promise.all(
|
|
4062
|
+
[...this.sessions.keys()].filter((p) => within2(p, at)).map((p) => this.commit(p))
|
|
4063
|
+
);
|
|
4064
|
+
}
|
|
4065
|
+
async commitOverlapping(real, offset, length2) {
|
|
4066
|
+
const session = this.sessions.get(real);
|
|
4067
|
+
if (!session) return;
|
|
4068
|
+
const end = offset + length2;
|
|
4069
|
+
if (session.truncated || session.extents.some(([a, b]) => a < end && offset < b)) {
|
|
4070
|
+
await this.commit(real);
|
|
4071
|
+
}
|
|
3911
4072
|
}
|
|
3912
4073
|
crashed(mount, error) {
|
|
3913
4074
|
mount.state = "failed";
|
|
@@ -3966,6 +4127,13 @@ var MountTable = class {
|
|
|
3966
4127
|
return new MountFs(this, base);
|
|
3967
4128
|
}
|
|
3968
4129
|
};
|
|
4130
|
+
function noteExtent(extents, from, to) {
|
|
4131
|
+
const last = extents.at(-1);
|
|
4132
|
+
if (last && from <= last[1] && to >= last[0]) {
|
|
4133
|
+
last[0] = Math.min(last[0], from);
|
|
4134
|
+
last[1] = Math.max(last[1], to);
|
|
4135
|
+
} else extents.push([from, to]);
|
|
4136
|
+
}
|
|
3969
4137
|
var MountFs = class {
|
|
3970
4138
|
table;
|
|
3971
4139
|
base;
|
|
@@ -4025,6 +4193,7 @@ var MountFs = class {
|
|
|
4025
4193
|
}
|
|
4026
4194
|
}
|
|
4027
4195
|
async readWhole(mount, real, rel2) {
|
|
4196
|
+
await this.table.commit(real);
|
|
4028
4197
|
const attr = await this.table.getattr(mount, real, rel2);
|
|
4029
4198
|
if (attr.kind === "directory") throw errnoError("EISDIR", real);
|
|
4030
4199
|
if (mount.maxFile > 0 && attr.size > mount.maxFile) {
|
|
@@ -4074,6 +4243,7 @@ var MountFs = class {
|
|
|
4074
4243
|
}
|
|
4075
4244
|
}
|
|
4076
4245
|
async writeWhole(mount, real, rel2, bytes) {
|
|
4246
|
+
await this.table.commit(real);
|
|
4077
4247
|
this.table.forget(mount, real);
|
|
4078
4248
|
const fh = await this.open(mount, rel2, true);
|
|
4079
4249
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
@@ -4096,6 +4266,7 @@ var MountFs = class {
|
|
|
4096
4266
|
if ((await this.table.getattr(mount, real, rel2)).kind === "directory") {
|
|
4097
4267
|
throw errnoError("EISDIR", real);
|
|
4098
4268
|
}
|
|
4269
|
+
await this.table.commitOverlapping(real, offset, length2);
|
|
4099
4270
|
const fh = await mount.conn.call({
|
|
4100
4271
|
op: "open",
|
|
4101
4272
|
path: rel2,
|
|
@@ -4149,6 +4320,14 @@ var MountFs = class {
|
|
|
4149
4320
|
this.writable(mount, real);
|
|
4150
4321
|
this.fits(mount, real, offset + bytes.length);
|
|
4151
4322
|
this.table.forget(mount, real);
|
|
4323
|
+
if (mount.caps.sessions) {
|
|
4324
|
+
const session = this.table.session(mount, real, rel2);
|
|
4325
|
+
const end = offset + bytes.length;
|
|
4326
|
+
await this.writeRuns(mount, await session.fh, offset, bytes, transfer);
|
|
4327
|
+
noteExtent(session.extents, offset, end);
|
|
4328
|
+
this.table.forget(mount, real);
|
|
4329
|
+
return;
|
|
4330
|
+
}
|
|
4152
4331
|
const fh = await mount.conn.call({
|
|
4153
4332
|
op: "open",
|
|
4154
4333
|
path: rel2,
|
|
@@ -4157,17 +4336,20 @@ var MountFs = class {
|
|
|
4157
4336
|
truncate: false,
|
|
4158
4337
|
exclusive: false
|
|
4159
4338
|
});
|
|
4160
|
-
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
4161
4339
|
try {
|
|
4162
|
-
|
|
4163
|
-
const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
|
|
4164
|
-
await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
|
|
4165
|
-
}
|
|
4340
|
+
await this.writeRuns(mount, fh, offset, bytes, transfer);
|
|
4166
4341
|
} finally {
|
|
4167
4342
|
await mount.conn.call({ op: "release", fh });
|
|
4168
4343
|
this.table.forget(mount, real);
|
|
4169
4344
|
}
|
|
4170
4345
|
}
|
|
4346
|
+
async writeRuns(mount, fh, offset, bytes, transfer) {
|
|
4347
|
+
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
4348
|
+
for (let at = 0; at < bytes.length; at += chunk2) {
|
|
4349
|
+
const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
|
|
4350
|
+
await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4171
4353
|
async truncate(path, size) {
|
|
4172
4354
|
const real = await this.follow(path);
|
|
4173
4355
|
const found = this.at(real);
|
|
@@ -4179,6 +4361,8 @@ var MountFs = class {
|
|
|
4179
4361
|
this.writable(mount, real);
|
|
4180
4362
|
this.fits(mount, real, size);
|
|
4181
4363
|
this.table.forget(mount, real);
|
|
4364
|
+
const session = this.table.writing(real);
|
|
4365
|
+
if (session) session.truncated = true;
|
|
4182
4366
|
try {
|
|
4183
4367
|
await mount.conn.call({ op: "setattr", path: rel2, size });
|
|
4184
4368
|
} finally {
|
|
@@ -4200,7 +4384,16 @@ var MountFs = class {
|
|
|
4200
4384
|
const found = this.at(path);
|
|
4201
4385
|
if (!found) return this.base.lstat(path);
|
|
4202
4386
|
const real = normalizePath(path);
|
|
4203
|
-
|
|
4387
|
+
if (this.table.writing(real)?.truncated) await this.table.commit(real);
|
|
4388
|
+
return this.statWriting(
|
|
4389
|
+
await this.table.getattr(found.mount, real, found.rel),
|
|
4390
|
+
real,
|
|
4391
|
+
found.mount
|
|
4392
|
+
);
|
|
4393
|
+
}
|
|
4394
|
+
statWriting(attr, real, mount) {
|
|
4395
|
+
const ends = (this.table.writing(real)?.extents ?? []).map(([, end]) => end);
|
|
4396
|
+
return statOf({ ...attr, size: Math.max(attr.size, ...ends) }, real, mount);
|
|
4204
4397
|
}
|
|
4205
4398
|
async stat(path) {
|
|
4206
4399
|
const real = await this.follow(path);
|
|
@@ -4227,7 +4420,7 @@ var MountFs = class {
|
|
|
4227
4420
|
list.map(async (entry) => {
|
|
4228
4421
|
const child = normalizePath(`${real}/${entry.name}`);
|
|
4229
4422
|
if (entry.attr && mount.caps.listingStats) {
|
|
4230
|
-
return [entry.name,
|
|
4423
|
+
return [entry.name, this.statWriting(entry.attr, child, mount)];
|
|
4231
4424
|
}
|
|
4232
4425
|
return [entry.name, await this.lstat(child).catch(() => null)];
|
|
4233
4426
|
})
|
|
@@ -4263,6 +4456,7 @@ var MountFs = class {
|
|
|
4263
4456
|
const real = normalizePath(path);
|
|
4264
4457
|
if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
|
|
4265
4458
|
this.writable(mount, real);
|
|
4459
|
+
await this.table.commitUnder(real);
|
|
4266
4460
|
let attr;
|
|
4267
4461
|
try {
|
|
4268
4462
|
attr = await this.table.getattr(mount, real, rel2);
|
|
@@ -4286,6 +4480,8 @@ var MountFs = class {
|
|
|
4286
4480
|
if (!a && !b) return this.base.rename(from, to);
|
|
4287
4481
|
if (!a || !b || a.mount !== b.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
|
|
4288
4482
|
this.writable(a.mount, normalizePath(to));
|
|
4483
|
+
await this.table.commitUnder(from);
|
|
4484
|
+
await this.table.commitUnder(to);
|
|
4289
4485
|
this.table.forget(a.mount, normalizePath(from));
|
|
4290
4486
|
this.table.forget(a.mount, normalizePath(to));
|
|
4291
4487
|
try {
|
|
@@ -7094,6 +7290,7 @@ var VfsNode = class {
|
|
|
7094
7290
|
writeBack;
|
|
7095
7291
|
writeBackCost = 0;
|
|
7096
7292
|
opens = 0;
|
|
7293
|
+
writers = 0;
|
|
7097
7294
|
missing = false;
|
|
7098
7295
|
revoked = false;
|
|
7099
7296
|
fs;
|
|
@@ -7269,8 +7466,10 @@ var VfsNodes = class {
|
|
|
7269
7466
|
byPath = /* @__PURE__ */ new Map();
|
|
7270
7467
|
fs;
|
|
7271
7468
|
entries = /* @__PURE__ */ new Map();
|
|
7272
|
-
|
|
7469
|
+
onClosed;
|
|
7470
|
+
constructor(fs, onClosed) {
|
|
7273
7471
|
this.fs = fs;
|
|
7472
|
+
this.onClosed = onClosed;
|
|
7274
7473
|
}
|
|
7275
7474
|
async onEntry(path, op) {
|
|
7276
7475
|
const key = await this.entryKey(path);
|
|
@@ -7308,16 +7507,17 @@ var VfsNodes = class {
|
|
|
7308
7507
|
await this.fs.writeFile(path, new Uint8Array(0));
|
|
7309
7508
|
});
|
|
7310
7509
|
}
|
|
7311
|
-
open(path, pin) {
|
|
7510
|
+
open(path, pin, writable = false) {
|
|
7312
7511
|
let node = this.byPath.get(path);
|
|
7313
7512
|
if (!node) {
|
|
7314
7513
|
node = new VfsNode(this.fs, path, void 0, false, false, pin);
|
|
7315
7514
|
this.byPath.set(path, node);
|
|
7316
7515
|
}
|
|
7317
7516
|
node.opens++;
|
|
7517
|
+
if (writable) node.writers++;
|
|
7318
7518
|
return node;
|
|
7319
7519
|
}
|
|
7320
|
-
adopt(path, contents2, dirty) {
|
|
7520
|
+
adopt(path, contents2, dirty, writable = false) {
|
|
7321
7521
|
let node = this.byPath.get(path);
|
|
7322
7522
|
if (!node) {
|
|
7323
7523
|
node = new VfsNode(this.fs, path, contents2, false, dirty);
|
|
@@ -7326,15 +7526,27 @@ var VfsNodes = class {
|
|
|
7326
7526
|
node.replace(contents2);
|
|
7327
7527
|
}
|
|
7328
7528
|
node.opens++;
|
|
7529
|
+
if (writable) node.writers++;
|
|
7329
7530
|
return node;
|
|
7330
7531
|
}
|
|
7331
7532
|
holds(prefix) {
|
|
7332
7533
|
for (const path of this.byPath.keys()) if (within4(path, prefix)) return true;
|
|
7333
7534
|
return false;
|
|
7334
7535
|
}
|
|
7335
|
-
|
|
7536
|
+
writes(prefix) {
|
|
7537
|
+
for (const [path, node] of this.byPath)
|
|
7538
|
+
if (node.writers > 0 && within4(path, prefix)) return true;
|
|
7539
|
+
return false;
|
|
7540
|
+
}
|
|
7541
|
+
closed(node, writable = false) {
|
|
7336
7542
|
node.opens--;
|
|
7337
|
-
if (
|
|
7543
|
+
if (writable) node.writers--;
|
|
7544
|
+
if (node.opens > 0) {
|
|
7545
|
+
if (writable && node.writers === 0) this.onClosed?.(node.path);
|
|
7546
|
+
return;
|
|
7547
|
+
}
|
|
7548
|
+
if (this.byPath.get(node.path) === node) this.byPath.delete(node.path);
|
|
7549
|
+
this.onClosed?.(node.path);
|
|
7338
7550
|
}
|
|
7339
7551
|
async flush(path) {
|
|
7340
7552
|
for (const [p, node] of this.byPath) if (within4(p, path)) await node.serial(() => node.flush());
|
|
@@ -7480,7 +7692,7 @@ function vfsFile(fs, opts, nodes) {
|
|
|
7480
7692
|
const access = opts.flags & O_ACCMODE;
|
|
7481
7693
|
const readable = access !== O_WRONLY;
|
|
7482
7694
|
const writable = access === O_WRONLY || access === O_RDWR;
|
|
7483
|
-
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true) : nodes.open(opts.path, opts.pin);
|
|
7695
|
+
const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable) : nodes.open(opts.path, opts.pin, writable);
|
|
7484
7696
|
let openError;
|
|
7485
7697
|
const atOpen = (op) => {
|
|
7486
7698
|
void node.serial(op).catch((err) => {
|
|
@@ -7543,7 +7755,7 @@ function vfsFile(fs, opts, nodes) {
|
|
|
7543
7755
|
takeOpenError();
|
|
7544
7756
|
await node.flush();
|
|
7545
7757
|
} finally {
|
|
7546
|
-
nodes?.closed(node);
|
|
7758
|
+
nodes?.closed(node, writable);
|
|
7547
7759
|
}
|
|
7548
7760
|
})
|
|
7549
7761
|
});
|
|
@@ -8890,7 +9102,11 @@ function mediumSlot(lost, onLost) {
|
|
|
8890
9102
|
}
|
|
8891
9103
|
|
|
8892
9104
|
// src/mount/fsa.ts
|
|
8893
|
-
var FSA_CAPABILITIES = {
|
|
9105
|
+
var FSA_CAPABILITIES = {
|
|
9106
|
+
listingStats: true,
|
|
9107
|
+
ranges: true,
|
|
9108
|
+
sessions: true
|
|
9109
|
+
};
|
|
8894
9110
|
var codeOf2 = (err) => err?.code;
|
|
8895
9111
|
var denied = (err) => codeOf2(err) === "EACCES" || err?.name === "NotAllowedError";
|
|
8896
9112
|
function attrOf(st) {
|
|
@@ -8930,30 +9146,20 @@ function kernelFsHandlers(fs) {
|
|
|
8930
9146
|
if (st && flags.exclusive) throw fsError2("EEXIST", path);
|
|
8931
9147
|
if (st?.isDirectory && flags.write) throw fsError2("EISDIR", path);
|
|
8932
9148
|
if (!st && !flags.create) throw fsError2("ENOENT", path);
|
|
8933
|
-
|
|
8934
|
-
handles.set(++nextFh, { path
|
|
9149
|
+
if (!st || flags.truncate) await fs.writeFile(path, new Uint8Array(0));
|
|
9150
|
+
handles.set(++nextFh, { path });
|
|
8935
9151
|
return nextFh;
|
|
8936
9152
|
},
|
|
8937
|
-
async
|
|
8938
|
-
const h = handle(fh);
|
|
8939
|
-
return h.data.slice(offset, Math.min(h.length, offset + size));
|
|
8940
|
-
},
|
|
9153
|
+
read: async (fh, offset, size) => fs.pread(handle(fh).path, offset, size),
|
|
8941
9154
|
async write(fh, offset, bytes) {
|
|
8942
9155
|
const h = handle(fh);
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
const grown = new Uint8Array(Math.max(end, h.data.length * 2));
|
|
8946
|
-
grown.set(h.data.subarray(0, h.length));
|
|
8947
|
-
h.data = grown;
|
|
8948
|
-
}
|
|
8949
|
-
h.data.set(bytes, offset);
|
|
8950
|
-
h.length = Math.max(h.length, end);
|
|
8951
|
-
h.dirty = true;
|
|
9156
|
+
h.session ??= fs.writeSession(h.path, false);
|
|
9157
|
+
await (await h.session).write(offset, bytes);
|
|
8952
9158
|
},
|
|
8953
9159
|
async release(fh) {
|
|
8954
9160
|
const h = handle(fh);
|
|
8955
9161
|
handles.delete(fh);
|
|
8956
|
-
if (h.
|
|
9162
|
+
if (h.session !== void 0) await (await h.session).close();
|
|
8957
9163
|
},
|
|
8958
9164
|
mkdir: (path) => fs.mkdir(path),
|
|
8959
9165
|
async rmdir(path) {
|
|
@@ -8969,6 +9175,13 @@ function kernelFsHandlers(fs) {
|
|
|
8969
9175
|
symlink: (target, path) => fs.symlink(target, path),
|
|
8970
9176
|
readlink: (path) => fs.readlink(path),
|
|
8971
9177
|
async setattr(path, change) {
|
|
9178
|
+
if (change.size !== void 0) {
|
|
9179
|
+
const open = [...handles.values()].find(
|
|
9180
|
+
(h) => h.path === path && h.session !== void 0
|
|
9181
|
+
)?.session;
|
|
9182
|
+
if (open !== void 0) await (await open).truncate(change.size);
|
|
9183
|
+
else await fs.truncate?.(path, change.size);
|
|
9184
|
+
}
|
|
8972
9185
|
if (change.mode !== void 0) await fs.chmod(path, change.mode);
|
|
8973
9186
|
if (change.mtime !== void 0) {
|
|
8974
9187
|
await fs.utimes(path, new Date(change.mtime), new Date(change.mtime));
|
|
@@ -9693,6 +9906,7 @@ function tmpfs() {
|
|
|
9693
9906
|
const { dir, name: name2 } = parent(path);
|
|
9694
9907
|
if (dir.children.has(name2)) throw fsError2("EEXIST", path);
|
|
9695
9908
|
dir.children.set(name2, make("directory", 16877));
|
|
9909
|
+
dir.mtime = Date.now();
|
|
9696
9910
|
},
|
|
9697
9911
|
async rmdir(path) {
|
|
9698
9912
|
const { dir, name: name2 } = parent(path);
|
|
@@ -9701,6 +9915,7 @@ function tmpfs() {
|
|
|
9701
9915
|
if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
|
|
9702
9916
|
if (node.children.size > 0) throw fsError2("ENOTEMPTY", path);
|
|
9703
9917
|
dir.children.delete(name2);
|
|
9918
|
+
dir.mtime = Date.now();
|
|
9704
9919
|
},
|
|
9705
9920
|
async unlink(path) {
|
|
9706
9921
|
const { dir, name: name2 } = parent(path);
|
|
@@ -9708,6 +9923,7 @@ function tmpfs() {
|
|
|
9708
9923
|
if (!node) throw fsError2("ENOENT", path);
|
|
9709
9924
|
if (node.kind === "directory") throw fsError2("EISDIR", path);
|
|
9710
9925
|
dir.children.delete(name2);
|
|
9926
|
+
dir.mtime = Date.now();
|
|
9711
9927
|
},
|
|
9712
9928
|
async rename(from, to) {
|
|
9713
9929
|
const a = `/${parts(from).join("/")}`;
|
|
@@ -9724,6 +9940,8 @@ function tmpfs() {
|
|
|
9724
9940
|
}
|
|
9725
9941
|
source.dir.children.delete(source.name);
|
|
9726
9942
|
target.dir.children.set(target.name, node);
|
|
9943
|
+
source.dir.mtime = Date.now();
|
|
9944
|
+
target.dir.mtime = source.dir.mtime;
|
|
9727
9945
|
},
|
|
9728
9946
|
async symlink(target, path) {
|
|
9729
9947
|
const { dir, name: name2 } = parent(path);
|
|
@@ -9731,6 +9949,7 @@ function tmpfs() {
|
|
|
9731
9949
|
const node = make("symlink", 41471);
|
|
9732
9950
|
node.target = target;
|
|
9733
9951
|
dir.children.set(name2, node);
|
|
9952
|
+
dir.mtime = Date.now();
|
|
9734
9953
|
},
|
|
9735
9954
|
async readlink(path) {
|
|
9736
9955
|
const node = find(path);
|
|
@@ -9920,6 +10139,7 @@ var PACKAGE_ROOT = /^(.*\/node_modules\/(?:@[^/]+\/)?[^/]+)\//;
|
|
|
9920
10139
|
var ENV_REFERENCE = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
9921
10140
|
var WASM_MAGIC = [0, 97, 115, 109];
|
|
9922
10141
|
var SHEBANG_MAX = 256;
|
|
10142
|
+
var MAX_INTERPRETERS = 4;
|
|
9923
10143
|
var NOT_FOUND = 127;
|
|
9924
10144
|
var DECIDE_MS = 100;
|
|
9925
10145
|
var INIT_PID = 1;
|
|
@@ -9971,6 +10191,17 @@ function targetOf(command) {
|
|
|
9971
10191
|
...command.preopenRoot ? { preopenRoot: true } : {}
|
|
9972
10192
|
};
|
|
9973
10193
|
}
|
|
10194
|
+
function withScriptEnv(target, command) {
|
|
10195
|
+
if (!command) return target;
|
|
10196
|
+
return {
|
|
10197
|
+
...target,
|
|
10198
|
+
env: { ...target.env, ...command.env },
|
|
10199
|
+
unset: [
|
|
10200
|
+
...(target.unset ?? []).filter((key) => !Object.hasOwn(command.env ?? {}, key)),
|
|
10201
|
+
...command.unset ?? []
|
|
10202
|
+
]
|
|
10203
|
+
};
|
|
10204
|
+
}
|
|
9974
10205
|
function childHandle(handle) {
|
|
9975
10206
|
return {
|
|
9976
10207
|
pid: handle.pid,
|
|
@@ -9995,6 +10226,7 @@ var Launcher = class {
|
|
|
9995
10226
|
modulesDir;
|
|
9996
10227
|
env;
|
|
9997
10228
|
catalog;
|
|
10229
|
+
binfmts;
|
|
9998
10230
|
compiled = /* @__PURE__ */ new Map();
|
|
9999
10231
|
processes = /* @__PURE__ */ new Map();
|
|
10000
10232
|
zombies = /* @__PURE__ */ new Set();
|
|
@@ -10029,7 +10261,7 @@ var Launcher = class {
|
|
|
10029
10261
|
deciding = /* @__PURE__ */ new Map();
|
|
10030
10262
|
identities = { asked: 0, timedOut: 0 };
|
|
10031
10263
|
nodes;
|
|
10032
|
-
held =
|
|
10264
|
+
held = new HeldSet((path) => this.released(path));
|
|
10033
10265
|
processMounts;
|
|
10034
10266
|
booting = new AbortController();
|
|
10035
10267
|
fstab = Promise.resolve([]);
|
|
@@ -10071,11 +10303,12 @@ var Launcher = class {
|
|
|
10071
10303
|
});
|
|
10072
10304
|
enableCdp(this.net, this.cdp);
|
|
10073
10305
|
const fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
|
|
10074
|
-
this.nodes = new VfsNodes(fs);
|
|
10306
|
+
this.nodes = new VfsNodes(fs, (path) => this.released(path));
|
|
10075
10307
|
this.openFiles.add(this.nodes);
|
|
10076
10308
|
this.fs = keepingOpen(fs, this.nodes);
|
|
10077
10309
|
this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
|
|
10078
10310
|
this.catalog = void 0;
|
|
10311
|
+
this.binfmts = void 0;
|
|
10079
10312
|
});
|
|
10080
10313
|
}
|
|
10081
10314
|
setRoutes(table) {
|
|
@@ -10085,6 +10318,10 @@ var Launcher = class {
|
|
|
10085
10318
|
this.catalog ??= this.roots().then((roots) => scanCommands(this.base, roots));
|
|
10086
10319
|
return this.catalog;
|
|
10087
10320
|
}
|
|
10321
|
+
interpreters() {
|
|
10322
|
+
this.binfmts ??= this.roots().then((roots) => scanBinfmts(this.base, roots));
|
|
10323
|
+
return this.binfmts;
|
|
10324
|
+
}
|
|
10088
10325
|
async roots() {
|
|
10089
10326
|
return [this.modulesDir, ...await pnpmGlobalRoots(this.base, this.pnpmHome)];
|
|
10090
10327
|
}
|
|
@@ -10130,30 +10367,28 @@ var Launcher = class {
|
|
|
10130
10367
|
const command = (await this.commands()).get(name2);
|
|
10131
10368
|
return command?.script ? command : void 0;
|
|
10132
10369
|
}
|
|
10133
|
-
async interpreted(file, argv, cwd) {
|
|
10370
|
+
async interpreted(file, argv, cwd, depth = 0) {
|
|
10134
10371
|
const command = await this.scriptCommand(file);
|
|
10135
10372
|
const script = command?.script ?? this.fs.resolvePath(cwd, file);
|
|
10136
|
-
const
|
|
10373
|
+
const head = await this.head(script);
|
|
10374
|
+
if (!head) return void 0;
|
|
10375
|
+
const binfmt = isWasm(head) ? void 0 : binfmtOf(await this.interpreters(), script);
|
|
10376
|
+
const words = shebang(head) ?? (binfmt ? [binfmt] : void 0);
|
|
10137
10377
|
if (!words) return void 0;
|
|
10138
10378
|
if (baseName(words[0]) === "env") words.shift();
|
|
10139
10379
|
const [interp, ...rest] = words;
|
|
10140
|
-
const found = interp ? await this.resolve(interp, interp, cwd) : void 0;
|
|
10141
|
-
if (!found) return void 0;
|
|
10142
|
-
const target = command ? {
|
|
10143
|
-
...found,
|
|
10144
|
-
env: { ...found.env, ...command.env },
|
|
10145
|
-
unset: [...found.unset ?? [], ...command.unset ?? []]
|
|
10146
|
-
} : found;
|
|
10147
10380
|
const arg = rest.join(" ");
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
}
|
|
10381
|
+
const passed = [...arg ? [arg] : [], command ? script : file, ...argv.slice(1)];
|
|
10382
|
+
const found = interp ? await this.resolve(interp, interp, cwd) : void 0;
|
|
10383
|
+
if (!found) {
|
|
10384
|
+
if (!interp || depth >= MAX_INTERPRETERS || !await this.scriptCommand(interp)) {
|
|
10385
|
+
return void 0;
|
|
10386
|
+
}
|
|
10387
|
+
const inner = await this.interpreted(interp, [interp, ...passed], cwd, depth + 1);
|
|
10388
|
+
return inner && { ...inner, target: withScriptEnv(inner.target, command) };
|
|
10389
|
+
}
|
|
10390
|
+
const target = withScriptEnv(found, command);
|
|
10391
|
+
return { target, args: [...target.prefix ?? [], ...passed] };
|
|
10157
10392
|
}
|
|
10158
10393
|
async plan(file, argv, cwd) {
|
|
10159
10394
|
const direct = await this.resolve(file, argv[0] ?? file, cwd);
|
|
@@ -10161,8 +10396,10 @@ var Launcher = class {
|
|
|
10161
10396
|
return this.interpreted(file, argv, cwd);
|
|
10162
10397
|
}
|
|
10163
10398
|
async unrunnable(file, cwd) {
|
|
10164
|
-
const
|
|
10165
|
-
|
|
10399
|
+
const path = this.fs.resolvePath(cwd, file);
|
|
10400
|
+
const head = await this.base.readFileBuffer(path).catch(() => null);
|
|
10401
|
+
if (!head || head[0] === 35 && head[1] === 33) return "ENOENT";
|
|
10402
|
+
return binfmtOf(await this.interpreters(), path) ? "ENOENT" : "ENOEXEC";
|
|
10166
10403
|
}
|
|
10167
10404
|
module(path) {
|
|
10168
10405
|
return this.base.stat(path).then((st) => {
|
|
@@ -10433,6 +10670,11 @@ var Launcher = class {
|
|
|
10433
10670
|
umaskOf(pid) {
|
|
10434
10671
|
return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
|
|
10435
10672
|
}
|
|
10673
|
+
released(path) {
|
|
10674
|
+
const at = normalizePath(path);
|
|
10675
|
+
const open = [...this.openFiles].some((nodes) => nodes.writes(at)) || heldUnder(this.held, at);
|
|
10676
|
+
if (!open) void this.mounts.commit(at).catch(() => void 0);
|
|
10677
|
+
}
|
|
10436
10678
|
ignoredBy(pid, fork = false) {
|
|
10437
10679
|
return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
|
|
10438
10680
|
}
|
|
@@ -10617,6 +10859,7 @@ var Launcher = class {
|
|
|
10617
10859
|
}
|
|
10618
10860
|
async starting(argv, dir) {
|
|
10619
10861
|
this.catalog = void 0;
|
|
10862
|
+
this.binfmts = void 0;
|
|
10620
10863
|
const cwd = this.fs.resolvePath("/", dir ?? "/");
|
|
10621
10864
|
const [file = ""] = argv;
|
|
10622
10865
|
await this.base.mkdir(cwd, { recursive: true });
|