@ai-ecoverse/slicc-kernel 1.54.8 → 1.54.9
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 +4 -2
- package/dist/index.js +40 -10
- package/dist/index.js.map +2 -2
- package/dist/kernel-worker.js +180 -25
- package/dist/kernel-worker.js.map +3 -3
- package/dist/node.js +220 -35
- package/dist/node.js.map +3 -3
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -725,7 +725,7 @@ var OpfsFs = class {
|
|
|
725
725
|
const swap = targetStat?.isDirectory === true;
|
|
726
726
|
const staging = swap ? `.${name2}.${crypto.randomUUID()}` : name2;
|
|
727
727
|
this.forget(destination);
|
|
728
|
-
const
|
|
728
|
+
const timed2 = await this.explicitTimes(source.path);
|
|
729
729
|
const moved2 = await this.place(source.handle, dir, staging);
|
|
730
730
|
if (swap) await this.rm(destination, { recursive: true });
|
|
731
731
|
await this.meta.move(source.path, destination, carry);
|
|
@@ -737,7 +737,7 @@ var OpfsFs = class {
|
|
|
737
737
|
if (!placed) await dir.removeEntry(staging, { recursive: true });
|
|
738
738
|
copied ||= !placed;
|
|
739
739
|
}
|
|
740
|
-
if (copied) await this.restamp(destination,
|
|
740
|
+
if (copied) await this.restamp(destination, timed2);
|
|
741
741
|
this.changed(source.path, destination);
|
|
742
742
|
this.touch(parentOf2(source.path), parent.path);
|
|
743
743
|
}
|
|
@@ -746,16 +746,16 @@ var OpfsFs = class {
|
|
|
746
746
|
return handle.kind === "file" ? (await handle.getFile()).lastModified : 0;
|
|
747
747
|
}
|
|
748
748
|
async explicitTimes(path) {
|
|
749
|
-
const
|
|
749
|
+
const timed2 = [];
|
|
750
750
|
for (const entry of await this.meta.under(path)) {
|
|
751
751
|
if (entry.mtimeFor === void 0 || entry.link !== void 0) continue;
|
|
752
752
|
const current = await this.modified(entry.path).catch(() => void 0);
|
|
753
|
-
if (current === entry.mtimeFor)
|
|
753
|
+
if (current === entry.mtimeFor) timed2.push(entry.path.slice(path.length));
|
|
754
754
|
}
|
|
755
|
-
return
|
|
755
|
+
return timed2;
|
|
756
756
|
}
|
|
757
|
-
async restamp(path,
|
|
758
|
-
for (const rest2 of
|
|
757
|
+
async restamp(path, timed2) {
|
|
758
|
+
for (const rest2 of timed2) {
|
|
759
759
|
const mtimeFor = await this.modified(path + rest2);
|
|
760
760
|
await this.meta.update(path + rest2, (entry) => entry && { ...entry, mtimeFor });
|
|
761
761
|
}
|
|
@@ -11818,11 +11818,24 @@ var HostfsClient = class {
|
|
|
11818
11818
|
get maxIo() {
|
|
11819
11819
|
return this.capabilities.maxIo ?? HOSTFS_IO;
|
|
11820
11820
|
}
|
|
11821
|
-
|
|
11821
|
+
retired;
|
|
11822
|
+
stop = new AbortController();
|
|
11823
|
+
get gone() {
|
|
11824
|
+
return this.retired !== void 0;
|
|
11825
|
+
}
|
|
11826
|
+
retire(why) {
|
|
11827
|
+
this.retired ??= why;
|
|
11828
|
+
this.stop.abort();
|
|
11829
|
+
}
|
|
11830
|
+
async send(path, init, handle = false, retried = false, cleanup = false) {
|
|
11831
|
+
if (this.retired && !cleanup) throw fsError2("ENOTCONN", this.retired);
|
|
11832
|
+
const stop = cleanup ? void 0 : this.stop.signal;
|
|
11833
|
+
const signal = init.signal && stop ? AbortSignal.any([init.signal, stop]) : init.signal ?? stop;
|
|
11822
11834
|
let response;
|
|
11823
11835
|
try {
|
|
11824
11836
|
response = await this.fetch(`${this.grant.url}${path}`, {
|
|
11825
11837
|
...init,
|
|
11838
|
+
...signal ? { signal } : {},
|
|
11826
11839
|
credentials: "omit",
|
|
11827
11840
|
headers: {
|
|
11828
11841
|
...init.headers,
|
|
@@ -11859,6 +11872,20 @@ var HostfsClient = class {
|
|
|
11859
11872
|
);
|
|
11860
11873
|
return await response.json();
|
|
11861
11874
|
}
|
|
11875
|
+
async cleanup(op, body) {
|
|
11876
|
+
const response = await this.send(
|
|
11877
|
+
"/api/hostfs",
|
|
11878
|
+
{
|
|
11879
|
+
method: "POST",
|
|
11880
|
+
headers: { "Content-Type": "application/json" },
|
|
11881
|
+
body: JSON.stringify({ op, ...body })
|
|
11882
|
+
},
|
|
11883
|
+
true,
|
|
11884
|
+
false,
|
|
11885
|
+
true
|
|
11886
|
+
);
|
|
11887
|
+
await response.body?.cancel();
|
|
11888
|
+
}
|
|
11862
11889
|
async read(fh, offset, size, ifMatch) {
|
|
11863
11890
|
const response = await this.send(
|
|
11864
11891
|
"/api/hostfs",
|
|
@@ -11926,6 +11953,11 @@ function hostfsHandlers(current) {
|
|
|
11926
11953
|
return fresh(remote);
|
|
11927
11954
|
};
|
|
11928
11955
|
const call = (op, body = {}) => current().call(op, body);
|
|
11956
|
+
const settle3 = (client, restore) => client.call("setattr", restore).catch((err) => {
|
|
11957
|
+
if (!client.gone || !client.cleanup) throw err;
|
|
11958
|
+
void client.cleanup("setattr", restore).catch(() => {
|
|
11959
|
+
});
|
|
11960
|
+
});
|
|
11929
11961
|
async function writable(client, path, flags) {
|
|
11930
11962
|
const { ifMatch: _, ...asked } = flags;
|
|
11931
11963
|
const opening = () => client.call("open", { path: rel(path), ...asked });
|
|
@@ -11936,9 +11968,9 @@ function hostfsHandlers(current) {
|
|
|
11936
11968
|
const mode = (await client.call("stat", { path: rel(path) })).mode ?? 0;
|
|
11937
11969
|
if (mode & 128) throw err;
|
|
11938
11970
|
const restore = { path: rel(path), mode };
|
|
11939
|
-
await client.call("setattr", { path: rel(path), mode: mode | 128 })
|
|
11940
|
-
|
|
11941
|
-
|
|
11971
|
+
const opened2 = await client.call("setattr", { path: rel(path), mode: mode | 128 }).then(opening).catch(async (again) => {
|
|
11972
|
+
await settle3(client, restore).catch(() => {
|
|
11973
|
+
});
|
|
11942
11974
|
throw again;
|
|
11943
11975
|
});
|
|
11944
11976
|
return { opened: opened2, restore };
|
|
@@ -11993,7 +12025,7 @@ function hostfsHandlers(current) {
|
|
|
11993
12025
|
try {
|
|
11994
12026
|
await fresh(remote).client.call("release", { fh: remote.fh }, true);
|
|
11995
12027
|
} finally {
|
|
11996
|
-
if (remote.restore) await current()
|
|
12028
|
+
if (remote.restore) await settle3(current(), remote.restore);
|
|
11997
12029
|
}
|
|
11998
12030
|
},
|
|
11999
12031
|
mkdir: async (path) => void await call("mkdir", { path: rel(path) }),
|
|
@@ -12123,6 +12155,73 @@ function invalidation(event) {
|
|
|
12123
12155
|
return event.paths ?? true;
|
|
12124
12156
|
}
|
|
12125
12157
|
var PORT_REQUEST_MS = 6e4;
|
|
12158
|
+
var HOSTFS_CALL_MS = 2e4;
|
|
12159
|
+
var PORT_BEAT_MS = 1e3;
|
|
12160
|
+
var PORT_DEAD_MS = 1e4;
|
|
12161
|
+
var PORT_LINGER_MS = 1e3;
|
|
12162
|
+
function timed(transport, ms) {
|
|
12163
|
+
const inflight = /* @__PURE__ */ new Set();
|
|
12164
|
+
const guard = (name2, run2, late, expire) => new Promise((resolve, reject) => {
|
|
12165
|
+
let expired = false;
|
|
12166
|
+
const running = run2();
|
|
12167
|
+
inflight.add(running);
|
|
12168
|
+
const done4 = () => inflight.delete(running);
|
|
12169
|
+
running.then(done4, done4);
|
|
12170
|
+
const timer = setTimeout(() => {
|
|
12171
|
+
expired = true;
|
|
12172
|
+
const why = `the host folder did not answer ${name2} within ${ms} ms`;
|
|
12173
|
+
transport.retire?.(why);
|
|
12174
|
+
expire?.(running);
|
|
12175
|
+
reject(fsError2("ENOTCONN", why));
|
|
12176
|
+
}, ms);
|
|
12177
|
+
running.then(
|
|
12178
|
+
(value) => {
|
|
12179
|
+
clearTimeout(timer);
|
|
12180
|
+
if (expired) late?.(value);
|
|
12181
|
+
resolve(value);
|
|
12182
|
+
},
|
|
12183
|
+
(err) => {
|
|
12184
|
+
clearTimeout(timer);
|
|
12185
|
+
reject(err);
|
|
12186
|
+
}
|
|
12187
|
+
);
|
|
12188
|
+
});
|
|
12189
|
+
const cleanup = async (op, body, skip) => {
|
|
12190
|
+
await Promise.allSettled([...inflight].filter((work) => work !== skip));
|
|
12191
|
+
await transport.cleanup?.(op, body);
|
|
12192
|
+
};
|
|
12193
|
+
const release = (opened2) => {
|
|
12194
|
+
const fh = opened2?.fh;
|
|
12195
|
+
if (typeof fh === "number") void cleanup("release", { fh }).catch(() => {
|
|
12196
|
+
});
|
|
12197
|
+
};
|
|
12198
|
+
return {
|
|
12199
|
+
get capabilities() {
|
|
12200
|
+
return transport.capabilities;
|
|
12201
|
+
},
|
|
12202
|
+
get token() {
|
|
12203
|
+
return transport.token;
|
|
12204
|
+
},
|
|
12205
|
+
get maxIo() {
|
|
12206
|
+
return transport.maxIo;
|
|
12207
|
+
},
|
|
12208
|
+
get gone() {
|
|
12209
|
+
return transport.gone;
|
|
12210
|
+
},
|
|
12211
|
+
retire: (why) => transport.retire?.(why),
|
|
12212
|
+
...transport.cleanup ? { cleanup } : {},
|
|
12213
|
+
call: (op, body, handle) => guard(
|
|
12214
|
+
op,
|
|
12215
|
+
() => transport.call(op, body, handle),
|
|
12216
|
+
op === "open" ? release : void 0,
|
|
12217
|
+
op === "release" ? (running) => void cleanup("release", body ?? {}, running).catch(() => {
|
|
12218
|
+
}) : void 0
|
|
12219
|
+
),
|
|
12220
|
+
read: (fh, offset, size, ifMatch) => guard("read", () => transport.read(fh, offset, size, ifMatch)),
|
|
12221
|
+
write: (fh, offset, bytes) => guard("write", () => transport.write(fh, offset, bytes)),
|
|
12222
|
+
watch: (signal, onEvent, onOpen) => transport.watch(signal, onEvent, onOpen)
|
|
12223
|
+
};
|
|
12224
|
+
}
|
|
12126
12225
|
var PORT_MS_PER_MIB = 1e3;
|
|
12127
12226
|
var nextPortToken = 0;
|
|
12128
12227
|
var PortTransport = class {
|
|
@@ -12133,13 +12232,25 @@ var PortTransport = class {
|
|
|
12133
12232
|
waiting = /* @__PURE__ */ new Map();
|
|
12134
12233
|
next = 0;
|
|
12135
12234
|
dead;
|
|
12136
|
-
|
|
12235
|
+
beatMs;
|
|
12236
|
+
deadMs;
|
|
12237
|
+
lingerMs;
|
|
12238
|
+
beat;
|
|
12239
|
+
answers = false;
|
|
12240
|
+
pinged;
|
|
12241
|
+
constructor(grant, liveness = {}) {
|
|
12137
12242
|
this.port = grant.port;
|
|
12138
|
-
this.requestMs = requestMs;
|
|
12243
|
+
this.requestMs = liveness.requestMs ?? PORT_REQUEST_MS;
|
|
12244
|
+
this.beatMs = liveness.beatMs ?? PORT_BEAT_MS;
|
|
12245
|
+
this.deadMs = liveness.deadMs ?? PORT_DEAD_MS;
|
|
12246
|
+
this.lingerMs = liveness.lingerMs ?? PORT_LINGER_MS;
|
|
12139
12247
|
this.capabilities = grant.capabilities ?? {};
|
|
12140
12248
|
this.token = grant.token ?? `port-${++nextPortToken}`;
|
|
12141
12249
|
this.port.addEventListener("message", (event) => {
|
|
12142
|
-
|
|
12250
|
+
if (event.data?.pong) {
|
|
12251
|
+
this.answers = true;
|
|
12252
|
+
this.pinged = void 0;
|
|
12253
|
+
} else this.waiting.get(event.data?.id)?.(event.data);
|
|
12143
12254
|
});
|
|
12144
12255
|
this.port.addEventListener("close", () => this.fail("the folder's port closed"));
|
|
12145
12256
|
this.port.start();
|
|
@@ -12150,8 +12261,51 @@ var PortTransport = class {
|
|
|
12150
12261
|
get gone() {
|
|
12151
12262
|
return this.dead !== void 0;
|
|
12152
12263
|
}
|
|
12264
|
+
retire(why) {
|
|
12265
|
+
this.fail(why);
|
|
12266
|
+
}
|
|
12267
|
+
ping() {
|
|
12268
|
+
if (this.pinged !== void 0) return;
|
|
12269
|
+
this.pinged = Date.now();
|
|
12270
|
+
try {
|
|
12271
|
+
this.port.postMessage({ id: 0, op: "ping" });
|
|
12272
|
+
} catch {
|
|
12273
|
+
}
|
|
12274
|
+
}
|
|
12275
|
+
watchBeat() {
|
|
12276
|
+
if (this.beat || this.dead) return;
|
|
12277
|
+
this.ping();
|
|
12278
|
+
this.beat = setInterval(() => {
|
|
12279
|
+
if (this.waiting.size === 0) {
|
|
12280
|
+
clearInterval(this.beat);
|
|
12281
|
+
this.beat = void 0;
|
|
12282
|
+
return;
|
|
12283
|
+
}
|
|
12284
|
+
if (this.answers && this.pinged !== void 0 && Date.now() - this.pinged > this.deadMs) {
|
|
12285
|
+
this.fail("the folder's port stopped answering");
|
|
12286
|
+
return;
|
|
12287
|
+
}
|
|
12288
|
+
this.ping();
|
|
12289
|
+
}, this.beatMs);
|
|
12290
|
+
}
|
|
12291
|
+
async cleanup(op, body) {
|
|
12292
|
+
if (this.closed) throw fsError2("ENOTCONN", "the folder's port is closed");
|
|
12293
|
+
this.port.postMessage({ id: 0, op: "cleanup", name: op, body });
|
|
12294
|
+
}
|
|
12295
|
+
closed = false;
|
|
12153
12296
|
fail(why) {
|
|
12154
|
-
if (this.dead === void 0)
|
|
12297
|
+
if (this.dead === void 0) {
|
|
12298
|
+
setTimeout(() => {
|
|
12299
|
+
try {
|
|
12300
|
+
this.port.postMessage({ id: 0, op: "bye" });
|
|
12301
|
+
} catch {
|
|
12302
|
+
}
|
|
12303
|
+
this.closed = true;
|
|
12304
|
+
this.port.close();
|
|
12305
|
+
}, this.lingerMs);
|
|
12306
|
+
}
|
|
12307
|
+
clearInterval(this.beat);
|
|
12308
|
+
this.beat = void 0;
|
|
12155
12309
|
this.dead ??= why;
|
|
12156
12310
|
for (const settle3 of [...this.waiting.values()])
|
|
12157
12311
|
settle3({ id: 0, error: { code: "ENOTCONN", message: why } });
|
|
@@ -12168,6 +12322,7 @@ var PortTransport = class {
|
|
|
12168
12322
|
},
|
|
12169
12323
|
this.requestMs + Math.ceil(bytes / 1048576) * PORT_MS_PER_MIB
|
|
12170
12324
|
);
|
|
12325
|
+
this.watchBeat();
|
|
12171
12326
|
this.waiting.set(id2, (reply) => {
|
|
12172
12327
|
clearTimeout(timer);
|
|
12173
12328
|
this.waiting.delete(id2);
|
|
@@ -12214,6 +12369,7 @@ var PortTransport = class {
|
|
|
12214
12369
|
}
|
|
12215
12370
|
resolve();
|
|
12216
12371
|
};
|
|
12372
|
+
this.watchBeat();
|
|
12217
12373
|
this.waiting.set(id2, (reply) => {
|
|
12218
12374
|
if (reply.open) return onOpen();
|
|
12219
12375
|
if (reply.event) return onEvent(reply.event);
|
|
@@ -12231,10 +12387,10 @@ var PortTransport = class {
|
|
|
12231
12387
|
}).finally(() => this.fail("the folder's watch ended"));
|
|
12232
12388
|
}
|
|
12233
12389
|
};
|
|
12234
|
-
function grantConnect(hook, fetch2,
|
|
12390
|
+
function grantConnect(hook, fetch2, liveness = {}) {
|
|
12235
12391
|
return async (source, options) => {
|
|
12236
12392
|
const first = await hook(source, options);
|
|
12237
|
-
if ("port" in first) return new PortTransport(first,
|
|
12393
|
+
if ("port" in first) return new PortTransport(first, liveness);
|
|
12238
12394
|
const grant = async () => {
|
|
12239
12395
|
const again = await hook(source, options);
|
|
12240
12396
|
if ("port" in again)
|
|
@@ -12254,7 +12410,10 @@ async function openHostfs(spec, connect, timing = {}) {
|
|
|
12254
12410
|
const stop = new AbortController();
|
|
12255
12411
|
let active;
|
|
12256
12412
|
const handlers = hostfsHandlers(() => active);
|
|
12257
|
-
const ranged2 = () =>
|
|
12413
|
+
const ranged2 = () => ({
|
|
12414
|
+
...active?.capabilities.ranges === true ? { ranges: true } : {},
|
|
12415
|
+
...active ? { maxIo: Math.min(active.maxIo, HOSTFS_IO) } : {}
|
|
12416
|
+
});
|
|
12258
12417
|
const served = serveFilesystem(
|
|
12259
12418
|
port2,
|
|
12260
12419
|
{ ...slot.handlers, mount: ranged2 },
|
|
@@ -12270,7 +12429,7 @@ async function openHostfs(spec, connect, timing = {}) {
|
|
|
12270
12429
|
{
|
|
12271
12430
|
connect: () => connect(spec.source, { readonly: readonly2 }),
|
|
12272
12431
|
up(client) {
|
|
12273
|
-
active = client;
|
|
12432
|
+
active = timed(client, timing.callMs ?? HOSTFS_CALL_MS);
|
|
12274
12433
|
slot.insert(client, handlers);
|
|
12275
12434
|
served.invalidate(true);
|
|
12276
12435
|
},
|
|
@@ -13290,11 +13449,7 @@ var Launcher = class {
|
|
|
13290
13449
|
throw fsError("ENODEV", "this kernel has no hostfs hook (createKernel({ hostfs }))");
|
|
13291
13450
|
return openHostfs(
|
|
13292
13451
|
spec,
|
|
13293
|
-
grantConnect(
|
|
13294
|
-
hostfs,
|
|
13295
|
-
hostfsFetch ?? ((url, init) => fetch(url, init)),
|
|
13296
|
-
hostfsTiming?.requestMs
|
|
13297
|
-
),
|
|
13452
|
+
grantConnect(hostfs, hostfsFetch ?? ((url, init) => fetch(url, init)), hostfsTiming),
|
|
13298
13453
|
hostfsTiming
|
|
13299
13454
|
);
|
|
13300
13455
|
}
|