@ai-ecoverse/slicc-kernel 1.161.0 → 1.162.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 +2 -2
- package/dist/index.js.map +1 -1
- package/dist/kernel-worker.js +368 -35
- package/dist/kernel-worker.js.map +3 -3
- package/dist/meta.js.map +2 -2
- package/dist/node-process-worker.js +1 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +390 -44
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +1 -1
- package/dist/process-worker.js.map +2 -2
- package/dist/types/fs/opfs.d.ts +6 -2
- package/dist/types/fs/remote.d.ts +56 -0
- package/dist/types/fs/types.d.ts +36 -0
- package/dist/types/fs/unlinked.d.ts +21 -1
- package/dist/types/kernel/host.d.ts +1 -1
- package/dist/types/launcher.d.ts +5 -0
- package/dist/types/mount/mount-fs.d.ts +5 -2
- package/dist/types/node.d.ts +2 -2
- package/dist/types/realm/sync-fs-token-registry.d.ts +1 -1
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/node.ts
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { lstatSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { lstatSync, mkdirSync, mkdtempSync, readdirSync, rmSync } from "node:fs";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { isAbsolute, join } from "node:path";
|
|
6
6
|
import { MessageChannel as MessageChannel2, Worker } from "node:worker_threads";
|
|
@@ -2212,7 +2212,9 @@ var KernelSocket = class _KernelSocket {
|
|
|
2212
2212
|
throw new KernelError("ECONNRESET", "read ECONNRESET");
|
|
2213
2213
|
return bytes.length > 0 ? bytes : null;
|
|
2214
2214
|
},
|
|
2215
|
-
write: (bytes) => raw2.tx.write(bytes)
|
|
2215
|
+
write: (bytes) => raw2.tx.write(bytes).catch((err) => {
|
|
2216
|
+
throw this.refused ? new KernelError("ECONNRESET", "read ECONNRESET") : err;
|
|
2217
|
+
}),
|
|
2216
2218
|
closeWrite: () => raw2.tx.closeWrite(),
|
|
2217
2219
|
close: () => {
|
|
2218
2220
|
raw2.rx.closeRead();
|
|
@@ -3675,7 +3677,7 @@ function satisfies(version, range2) {
|
|
|
3675
3677
|
}
|
|
3676
3678
|
|
|
3677
3679
|
// src/version.ts
|
|
3678
|
-
var KERNEL_VERSION = true ? "1.
|
|
3680
|
+
var KERNEL_VERSION = true ? "1.162.1" : "0.0.0-dev";
|
|
3679
3681
|
var OS_RELEASE = "6.0.0-slicc";
|
|
3680
3682
|
function procVersion() {
|
|
3681
3683
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -4638,6 +4640,9 @@ var LOOKUP_OPS = [
|
|
|
4638
4640
|
var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
4639
4641
|
var TOUCHED_MAX = 4096;
|
|
4640
4642
|
var parentOf2 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
4643
|
+
var PEER_RM_MS = 1e3;
|
|
4644
|
+
var PEER_RENAME_MS = 1e4;
|
|
4645
|
+
var PEER_GATE_MS = PEER_RENAME_MS + 5e3;
|
|
4641
4646
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
4642
4647
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
4643
4648
|
var PathGate = class {
|
|
@@ -4680,7 +4685,9 @@ var UnlinkedKeeper = class {
|
|
|
4680
4685
|
links = /* @__PURE__ */ new Map();
|
|
4681
4686
|
linked = /* @__PURE__ */ new Map();
|
|
4682
4687
|
dirOwner;
|
|
4688
|
+
peers;
|
|
4683
4689
|
constructor(fs, holders, rootOf, options = {}) {
|
|
4690
|
+
this.peers = options.peers;
|
|
4684
4691
|
this.marks = options.marks ?? new PathMarks();
|
|
4685
4692
|
const { locks, owner } = options;
|
|
4686
4693
|
this.dirOwner = options.dirOwner;
|
|
@@ -4756,22 +4763,29 @@ var UnlinkedKeeper = class {
|
|
|
4756
4763
|
lstat: (path) => unreplaced(path, async () => counted3(await fs.lstat(path))),
|
|
4757
4764
|
async rm(path, options) {
|
|
4758
4765
|
const target = fs.resolvePath("/", path);
|
|
4759
|
-
await
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4766
|
+
const kept = await keeper.peerKeep(target, false, () => keeper.removable(target, options));
|
|
4767
|
+
if (kept.whole) return;
|
|
4768
|
+
await kept.undoing(
|
|
4769
|
+
() => replace(target, async (gated, sealed) => {
|
|
4770
|
+
const pinned = await keeper.pin(target, gated, sealed);
|
|
4771
|
+
if (pinned.moved.has(target)) pinned.commit();
|
|
4772
|
+
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
4773
|
+
})
|
|
4774
|
+
);
|
|
4764
4775
|
},
|
|
4765
4776
|
async rename(from, to) {
|
|
4766
4777
|
const target = fs.resolvePath("/", to);
|
|
4767
4778
|
const source = fs.resolvePath("/", from);
|
|
4768
4779
|
if (target === source) return fs.rename(from, to);
|
|
4769
|
-
await keeper.
|
|
4770
|
-
|
|
4771
|
-
() =>
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4780
|
+
const kept = await keeper.peerKeep(target, true, () => keeper.renamable(source, target));
|
|
4781
|
+
await kept.undoing(
|
|
4782
|
+
() => keeper.marks.around(
|
|
4783
|
+
[source],
|
|
4784
|
+
() => replace(target, async (gated, sealed) => {
|
|
4785
|
+
const pinned = await keeper.pin(target, gated, sealed);
|
|
4786
|
+
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
4787
|
+
})
|
|
4788
|
+
)
|
|
4775
4789
|
);
|
|
4776
4790
|
},
|
|
4777
4791
|
symlink: (target, path, owner) => keeper.marks.around([fs.resolvePath("/", path)], () => fs.symlink(target, path, owner)),
|
|
@@ -4916,6 +4930,114 @@ var UnlinkedKeeper = class {
|
|
|
4916
4930
|
(err) => err?.code !== "ENOENT" && err?.code !== "ENOTDIR"
|
|
4917
4931
|
);
|
|
4918
4932
|
}
|
|
4933
|
+
get id() {
|
|
4934
|
+
return this.instance;
|
|
4935
|
+
}
|
|
4936
|
+
adopt(hidden, refs) {
|
|
4937
|
+
return this.keptFile(hidden, { refs });
|
|
4938
|
+
}
|
|
4939
|
+
async peerKeep(target, over, valid2 = async () => true) {
|
|
4940
|
+
const none = { whole: false, undoing: (op) => op() };
|
|
4941
|
+
const peers = this.peers;
|
|
4942
|
+
if (!peers?.shared() || this.rootOf(target) !== "/") return none;
|
|
4943
|
+
const held = peers.heldWithin(target).filter((holder) => holder.instance !== void 0);
|
|
4944
|
+
if (held.length === 0 || !await valid2()) return none;
|
|
4945
|
+
const tabs = /* @__PURE__ */ new Map();
|
|
4946
|
+
for (const holder of held) tabs.set(holder.tab, [...tabs.get(holder.tab) ?? [], holder]);
|
|
4947
|
+
const asked = [...tabs].map(([tab, holders]) => {
|
|
4948
|
+
const paths = holders.map((holder) => holder.path);
|
|
4949
|
+
return { tab, holders, paths };
|
|
4950
|
+
});
|
|
4951
|
+
const heard = await Promise.all(
|
|
4952
|
+
asked.map(
|
|
4953
|
+
({ tab, paths }) => peers.ask(tab, { keeping: { paths } }, over ? PEER_RENAME_MS : PEER_RM_MS)
|
|
4954
|
+
)
|
|
4955
|
+
);
|
|
4956
|
+
if (over && heard.includes(false)) {
|
|
4957
|
+
for (const { tab, paths } of asked)
|
|
4958
|
+
void peers.ask(tab, { kept: { items: [], paths } }, PEER_RM_MS);
|
|
4959
|
+
throw fsError("EBUSY", `${target}: open in another tab that did not answer`);
|
|
4960
|
+
}
|
|
4961
|
+
const { whole, moved: moved2 } = await this.setAside(peers, target, asked, heard);
|
|
4962
|
+
return { whole, undoing: (op) => this.undoing(op, moved2) };
|
|
4963
|
+
}
|
|
4964
|
+
async setAside(peers, target, asked, heard) {
|
|
4965
|
+
const mine = this.heldWithin(target);
|
|
4966
|
+
const taken = /* @__PURE__ */ new Set();
|
|
4967
|
+
let whole = false;
|
|
4968
|
+
const told = [];
|
|
4969
|
+
const moved2 = [];
|
|
4970
|
+
for (const [i, { tab, holders, paths }] of asked.entries()) {
|
|
4971
|
+
const items = [];
|
|
4972
|
+
for (const holder of holders) {
|
|
4973
|
+
const free = !mine.has(holder.path) && !taken.has(holder.path);
|
|
4974
|
+
const hidden = free ? await this.asideFor(holder.path, holder.instance) : void 0;
|
|
4975
|
+
if (hidden !== void 0) {
|
|
4976
|
+
taken.add(holder.path);
|
|
4977
|
+
moved2.push({ tab, path: holder.path, hidden, fid: holder.fid });
|
|
4978
|
+
if (holder.path === target) whole = true;
|
|
4979
|
+
}
|
|
4980
|
+
items.push({ path: holder.path, hidden, fid: holder.fid });
|
|
4981
|
+
}
|
|
4982
|
+
const answer2 = peers.ask(tab, { kept: { items, paths } }, PEER_RM_MS);
|
|
4983
|
+
if (heard[i]) told.push(answer2);
|
|
4984
|
+
}
|
|
4985
|
+
await Promise.all(told);
|
|
4986
|
+
return { whole, moved: moved2 };
|
|
4987
|
+
}
|
|
4988
|
+
async undoing(op, moved2) {
|
|
4989
|
+
try {
|
|
4990
|
+
return await op();
|
|
4991
|
+
} catch (err) {
|
|
4992
|
+
for (const { tab, path, hidden, fid } of moved2) {
|
|
4993
|
+
const back = await this.serial(async () => {
|
|
4994
|
+
if (await this.present(path)) return false;
|
|
4995
|
+
return this.fs.rename(hidden, path).then(
|
|
4996
|
+
() => true,
|
|
4997
|
+
() => false
|
|
4998
|
+
);
|
|
4999
|
+
});
|
|
5000
|
+
if (!back) continue;
|
|
5001
|
+
void this.peers?.ask(
|
|
5002
|
+
tab,
|
|
5003
|
+
{ kept: { items: [{ path, hidden: void 0, fid, undo: hidden }], paths: [] } },
|
|
5004
|
+
PEER_RM_MS
|
|
5005
|
+
);
|
|
5006
|
+
}
|
|
5007
|
+
throw err;
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
async removable(target, options) {
|
|
5011
|
+
const st = await this.fs.lstat(target).catch(() => void 0);
|
|
5012
|
+
return st !== void 0 && (!st.isDirectory || options?.recursive === true);
|
|
5013
|
+
}
|
|
5014
|
+
async renamable(source, target) {
|
|
5015
|
+
const from = await this.fs.lstat(source);
|
|
5016
|
+
const to = await this.fs.lstat(target).catch(() => void 0);
|
|
5017
|
+
if (!to) return true;
|
|
5018
|
+
if (to.isDirectory && !from.isDirectory) throw fsError("EISDIR", target);
|
|
5019
|
+
if (!to.isDirectory && from.isDirectory) throw fsError("ENOTDIR", target);
|
|
5020
|
+
if (to.isDirectory && (await this.fs.readdir(target)).length > 0)
|
|
5021
|
+
throw fsError("ENOTEMPTY", target);
|
|
5022
|
+
return true;
|
|
5023
|
+
}
|
|
5024
|
+
async asideFor(path, instance) {
|
|
5025
|
+
return await this.hideFor(path, instance) ?? this.hideFor(path, instance);
|
|
5026
|
+
}
|
|
5027
|
+
hideFor(path, instance) {
|
|
5028
|
+
return this.serial(async () => {
|
|
5029
|
+
const dir = dirOf("/");
|
|
5030
|
+
const hidden = `${dir}/${instance}.p${crypto.randomUUID().replaceAll("-", "")}`;
|
|
5031
|
+
try {
|
|
5032
|
+
const owner = this.dirOwner;
|
|
5033
|
+
await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
|
|
5034
|
+
await this.fs.rename(path, hidden);
|
|
5035
|
+
return hidden;
|
|
5036
|
+
} catch {
|
|
5037
|
+
return void 0;
|
|
5038
|
+
}
|
|
5039
|
+
});
|
|
5040
|
+
}
|
|
4919
5041
|
keptFile(hidden, entry) {
|
|
4920
5042
|
return {
|
|
4921
5043
|
hidden,
|
|
@@ -4970,7 +5092,7 @@ var UnlinkedKeeper = class {
|
|
|
4970
5092
|
}
|
|
4971
5093
|
async tidy(dir) {
|
|
4972
5094
|
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
4973
|
-
if (left.length === 0) await this.fs.rm(dir
|
|
5095
|
+
if (left.length === 0) await this.fs.rm(dir).catch(() => void 0);
|
|
4974
5096
|
}
|
|
4975
5097
|
sweep(root) {
|
|
4976
5098
|
const query = this.locks?.query?.bind(this.locks);
|
|
@@ -6380,6 +6502,11 @@ async function shownTarget(fs, target) {
|
|
|
6380
6502
|
const st = await quiet2(fs.stat(target));
|
|
6381
6503
|
return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
|
|
6382
6504
|
}
|
|
6505
|
+
async function madeFor(owner, virtual) {
|
|
6506
|
+
if (!owner?.existing || await virtual() === void 0) return owner;
|
|
6507
|
+
const { existing: _, ...made2 } = owner;
|
|
6508
|
+
return made2;
|
|
6509
|
+
}
|
|
6383
6510
|
function withCommandDirs(fs, names2, userDir = async () => void 0, owners = false, targetOf2 = async () => void 0) {
|
|
6384
6511
|
async function nearest(path) {
|
|
6385
6512
|
const st = await fs.stat(path).catch(() => void 0);
|
|
@@ -6477,7 +6604,7 @@ function withCommandDirs(fs, names2, userDir = async () => void 0, owners = fals
|
|
|
6477
6604
|
}),
|
|
6478
6605
|
readFileBuffer: (path) => fs.readFileBuffer(path).catch((err) => content(path, err)),
|
|
6479
6606
|
commandTarget: async (path) => (await entryTarget(path))?.path,
|
|
6480
|
-
writeFile: (path, content2, owner) => fs.writeFile(path, content2, owner),
|
|
6607
|
+
writeFile: async (path, content2, owner) => fs.writeFile(path, content2, await madeFor(owner, () => entryTarget(path))),
|
|
6481
6608
|
link: (from, to, follow2) => fs.link(from, to, follow2),
|
|
6482
6609
|
links: (path) => mayLink(fs, path),
|
|
6483
6610
|
async exists(path) {
|
|
@@ -6642,6 +6769,10 @@ function withEtcFiles(fs, files) {
|
|
|
6642
6769
|
}
|
|
6643
6770
|
} : {},
|
|
6644
6771
|
readFile: (path) => fs.readFile(path).catch(fallback(path, (b) => new TextDecoder().decode(b))),
|
|
6772
|
+
writeFile: async (path, content, owner) => {
|
|
6773
|
+
if (owner?.existing && files.has(path)) await materialize(path);
|
|
6774
|
+
return fs.writeFile(path, content, owner);
|
|
6775
|
+
},
|
|
6645
6776
|
...fs.append ? {
|
|
6646
6777
|
append: async (path, bytes, options) => {
|
|
6647
6778
|
if (files.has(path)) await materialize(path);
|
|
@@ -9022,12 +9153,25 @@ var HeldPaths = class extends Map {
|
|
|
9022
9153
|
for (const file of this.unlinked.values()) if (file.hidden === hidden) return true;
|
|
9023
9154
|
return false;
|
|
9024
9155
|
}
|
|
9156
|
+
boundAt(path, fid) {
|
|
9157
|
+
return this.handlesAt(path).filter(
|
|
9158
|
+
(id2) => !this.keptHandles.has(id2) && this.bound.get(id2)?.fid === fid
|
|
9159
|
+
);
|
|
9160
|
+
}
|
|
9161
|
+
keepBound(ids, path, file) {
|
|
9162
|
+
for (const id2 of ids) this.keptHandles.set(id2, file);
|
|
9163
|
+
this.uses.set(file, ids.length);
|
|
9164
|
+
this.unlinked.set(path, file);
|
|
9165
|
+
}
|
|
9025
9166
|
keep(path, file) {
|
|
9026
9167
|
const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
9027
9168
|
for (const id2 of fresh3) this.keptHandles.set(id2, file);
|
|
9028
9169
|
this.uses.set(file, Math.max(fresh3.length, 1));
|
|
9029
9170
|
this.unlinked.set(path, file);
|
|
9030
9171
|
}
|
|
9172
|
+
unkeepAt(path, hidden) {
|
|
9173
|
+
for (const file of [...this.uses.keys()]) if (file.hidden === hidden) this.unkeep(path, file);
|
|
9174
|
+
}
|
|
9031
9175
|
unkeep(path, file) {
|
|
9032
9176
|
for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
|
|
9033
9177
|
this.uses.delete(file);
|
|
@@ -9596,12 +9740,12 @@ var MountFs = class _MountFs {
|
|
|
9596
9740
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
9597
9741
|
return this.follow(this.base.resolvePath(parentOf5(real), target), hops + 1, create);
|
|
9598
9742
|
}
|
|
9599
|
-
async open(mount, rel2, write, owner) {
|
|
9743
|
+
async open(mount, rel2, write, owner, create = write) {
|
|
9600
9744
|
return await mount.conn.call({
|
|
9601
9745
|
op: "open",
|
|
9602
9746
|
path: rel2,
|
|
9603
9747
|
write,
|
|
9604
|
-
create
|
|
9748
|
+
create,
|
|
9605
9749
|
truncate: write,
|
|
9606
9750
|
exclusive: false,
|
|
9607
9751
|
...owner
|
|
@@ -9698,10 +9842,10 @@ var MountFs = class _MountFs {
|
|
|
9698
9842
|
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
9699
9843
|
);
|
|
9700
9844
|
}
|
|
9701
|
-
const made2 = await this.driverOwner(found, real, owner, false);
|
|
9845
|
+
const made2 = owner?.existing ? void 0 : await this.driverOwner(found, real, owner, false);
|
|
9702
9846
|
for (let attempt = 1; ; attempt++) {
|
|
9703
9847
|
try {
|
|
9704
|
-
return await this.writeWhole(mount, real, rel2, bytes, made2);
|
|
9848
|
+
return await this.writeWhole(mount, real, rel2, bytes, made2, owner?.existing !== true);
|
|
9705
9849
|
} catch (err) {
|
|
9706
9850
|
if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
|
|
9707
9851
|
}
|
|
@@ -9719,10 +9863,10 @@ var MountFs = class _MountFs {
|
|
|
9719
9863
|
this.table.forget(mount, real);
|
|
9720
9864
|
}
|
|
9721
9865
|
}
|
|
9722
|
-
async writeWhole(mount, real, rel2, bytes, owner) {
|
|
9866
|
+
async writeWhole(mount, real, rel2, bytes, owner, create = true) {
|
|
9723
9867
|
await this.table.commit(real);
|
|
9724
9868
|
this.table.forget(mount, real);
|
|
9725
|
-
const fh = await this.open(mount, rel2, true, owner);
|
|
9869
|
+
const fh = await this.open(mount, rel2, true, owner, create);
|
|
9726
9870
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
9727
9871
|
try {
|
|
9728
9872
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
@@ -10459,6 +10603,11 @@ function ownerFor(entry, mode) {
|
|
|
10459
10603
|
if (!cred) return void 0;
|
|
10460
10604
|
return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
|
|
10461
10605
|
}
|
|
10606
|
+
function writeOwner(entry, req) {
|
|
10607
|
+
const owner = ownerFor(entry, 438);
|
|
10608
|
+
if (req.handle === void 0 || req.mode !== 1) return owner;
|
|
10609
|
+
return { ...owner ?? {}, existing: true };
|
|
10610
|
+
}
|
|
10462
10611
|
function plainDirMode(entry, mode) {
|
|
10463
10612
|
if (entry.cred || mode === void 0) return void 0;
|
|
10464
10613
|
const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
|
|
@@ -10529,7 +10678,7 @@ async function run(entry, path, req) {
|
|
|
10529
10678
|
case "read":
|
|
10530
10679
|
return { ok: true, kind: "bytes", bytes: await readSource(entry, path) };
|
|
10531
10680
|
case "write":
|
|
10532
|
-
await fs.writeFile(path, req.body ?? new Uint8Array(0),
|
|
10681
|
+
await fs.writeFile(path, req.body ?? new Uint8Array(0), writeOwner(entry, req));
|
|
10533
10682
|
return done;
|
|
10534
10683
|
case "exists":
|
|
10535
10684
|
return json2(await fs.exists(path));
|
|
@@ -23425,6 +23574,9 @@ var RemoteWatch = class {
|
|
|
23425
23574
|
holdsSent = 0;
|
|
23426
23575
|
silent = /* @__PURE__ */ new Set();
|
|
23427
23576
|
holders = /* @__PURE__ */ new Set();
|
|
23577
|
+
instances = /* @__PURE__ */ new Map();
|
|
23578
|
+
instance;
|
|
23579
|
+
keeps;
|
|
23428
23580
|
listeners = /* @__PURE__ */ new Set();
|
|
23429
23581
|
waiting = /* @__PURE__ */ new Map();
|
|
23430
23582
|
locks;
|
|
@@ -23544,8 +23696,9 @@ var RemoteWatch = class {
|
|
|
23544
23696
|
this.met?.();
|
|
23545
23697
|
}
|
|
23546
23698
|
if (data.tab !== void 0 && data.paths !== void 0) this.announced(data);
|
|
23547
|
-
|
|
23699
|
+
this.holdsHeard(data);
|
|
23548
23700
|
if (data.ack !== void 0 && data.to === this.tab && data.from !== void 0) {
|
|
23701
|
+
if (data.instance !== void 0) this.instances.set(data.from, data.instance);
|
|
23549
23702
|
this.acked(data.ack, data.from);
|
|
23550
23703
|
}
|
|
23551
23704
|
if (data.changed !== void 0) this.emit({ paths: data.changed, trees: [], content: true });
|
|
@@ -23572,6 +23725,7 @@ var RemoteWatch = class {
|
|
|
23572
23725
|
this.mourned.delete(peer);
|
|
23573
23726
|
this.others.delete(peer);
|
|
23574
23727
|
this.peerHolds.delete(peer);
|
|
23728
|
+
this.keeps?.left(peer);
|
|
23575
23729
|
this.peers.delete(peer);
|
|
23576
23730
|
this.ackers.delete(peer);
|
|
23577
23731
|
this.holders.delete(peer);
|
|
@@ -23612,9 +23766,38 @@ var RemoteWatch = class {
|
|
|
23612
23766
|
this.rebuild();
|
|
23613
23767
|
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
23614
23768
|
}
|
|
23769
|
+
holdsHeard(data) {
|
|
23770
|
+
if (data.tab === void 0) return;
|
|
23771
|
+
if (data.holds !== void 0) this.heldBy(data);
|
|
23772
|
+
if (data.to === this.tab && (data.keeping || data.kept)) void this.keepAsked(data);
|
|
23773
|
+
}
|
|
23774
|
+
keepChains = /* @__PURE__ */ new Map();
|
|
23775
|
+
keepAsked(data) {
|
|
23776
|
+
const from = data.tab;
|
|
23777
|
+
const next3 = (this.keepChains.get(from) ?? Promise.resolve()).then(() => this.keepOne(data));
|
|
23778
|
+
this.keepChains.set(from, next3);
|
|
23779
|
+
return next3;
|
|
23780
|
+
}
|
|
23781
|
+
async keepOne({ tab, seq: seq2, keeping, kept }) {
|
|
23782
|
+
const from = tab;
|
|
23783
|
+
const keeps = this.keeps;
|
|
23784
|
+
try {
|
|
23785
|
+
if (keeping) await keeps?.keeping(from, keeping.paths);
|
|
23786
|
+
if (kept)
|
|
23787
|
+
await keeps?.kept(from, kept.items, kept.paths ?? kept.items.map((item) => item.path));
|
|
23788
|
+
} finally {
|
|
23789
|
+
this.channel.postMessage({
|
|
23790
|
+
ack: seq2,
|
|
23791
|
+
from: this.tab,
|
|
23792
|
+
to: from,
|
|
23793
|
+
...this.instance === void 0 ? {} : { instance: this.instance }
|
|
23794
|
+
});
|
|
23795
|
+
}
|
|
23796
|
+
}
|
|
23615
23797
|
heldBy({ tab, holds: holds2, seq: seq2 }) {
|
|
23616
23798
|
const from = tab;
|
|
23617
23799
|
const { add, drop } = holds2;
|
|
23800
|
+
if (holds2?.instance !== void 0) this.instances.set(from, holds2.instance);
|
|
23618
23801
|
const held = this.peerHolds.get(from) ?? /* @__PURE__ */ new Map();
|
|
23619
23802
|
for (const path of drop) held.delete(path);
|
|
23620
23803
|
for (const [path, fid] of add) held.set(path, fid);
|
|
@@ -23656,9 +23839,38 @@ var RemoteWatch = class {
|
|
|
23656
23839
|
sendHolds(add, drop) {
|
|
23657
23840
|
const seq2 = ++this.seq;
|
|
23658
23841
|
this.holdsSent++;
|
|
23659
|
-
|
|
23842
|
+
const instance = this.instance === void 0 ? {} : { instance: this.instance };
|
|
23843
|
+
this.channel.postMessage({
|
|
23844
|
+
tab: this.tab,
|
|
23845
|
+
holds: { add: [...add], drop: [...drop], ...instance },
|
|
23846
|
+
seq: seq2
|
|
23847
|
+
});
|
|
23660
23848
|
return seq2;
|
|
23661
23849
|
}
|
|
23850
|
+
heldWithin(target) {
|
|
23851
|
+
const out = [];
|
|
23852
|
+
for (const [tab, held] of this.peerHolds) {
|
|
23853
|
+
for (const [path, fid] of held) {
|
|
23854
|
+
if (path !== target && !path.startsWith(`${target}/`)) continue;
|
|
23855
|
+
out.push({ tab, path, fid, instance: this.instances.get(tab) });
|
|
23856
|
+
}
|
|
23857
|
+
}
|
|
23858
|
+
return out;
|
|
23859
|
+
}
|
|
23860
|
+
ask(to, body, cap) {
|
|
23861
|
+
const seq2 = ++this.seq;
|
|
23862
|
+
this.channel.postMessage({ tab: this.tab, to, seq: seq2, ...body });
|
|
23863
|
+
return new Promise((resolve2) => {
|
|
23864
|
+
const done4 = () => {
|
|
23865
|
+
clearTimeout(timer);
|
|
23866
|
+
this.waiting.delete(seq2);
|
|
23867
|
+
resolve2(left.size === 0);
|
|
23868
|
+
};
|
|
23869
|
+
const left = /* @__PURE__ */ new Set([to]);
|
|
23870
|
+
const timer = setTimeout(done4, cap);
|
|
23871
|
+
this.waiting.set(seq2, { left, done: done4 });
|
|
23872
|
+
});
|
|
23873
|
+
}
|
|
23662
23874
|
peerHeld(path) {
|
|
23663
23875
|
const out = [];
|
|
23664
23876
|
for (const [tab, held] of this.peerHolds)
|
|
@@ -23694,6 +23906,7 @@ var RemoteWatch = class {
|
|
|
23694
23906
|
if (this.peers.delete(tab)) left = true;
|
|
23695
23907
|
this.others.delete(tab);
|
|
23696
23908
|
this.peerHolds.delete(tab);
|
|
23909
|
+
this.keeps?.left(tab);
|
|
23697
23910
|
this.ackers.delete(tab);
|
|
23698
23911
|
this.holders.delete(tab);
|
|
23699
23912
|
}
|
|
@@ -25169,6 +25382,7 @@ function translate(err, path, op = "lookup", mapped = true) {
|
|
|
25169
25382
|
return error;
|
|
25170
25383
|
}
|
|
25171
25384
|
var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
|
|
25385
|
+
var FILE_ID_TRIES = 3;
|
|
25172
25386
|
function sidecar(meta) {
|
|
25173
25387
|
const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
|
|
25174
25388
|
const guarded3 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
|
|
@@ -25408,18 +25622,48 @@ var OpfsFs = class {
|
|
|
25408
25622
|
this.channel?.postMessage(paths);
|
|
25409
25623
|
}
|
|
25410
25624
|
async fileId(path) {
|
|
25625
|
+
for (let tries = 0; tries < FILE_ID_TRIES; tries++) {
|
|
25626
|
+
const fid = await this.assignId(path);
|
|
25627
|
+
if (fid !== void 0) return fid;
|
|
25628
|
+
}
|
|
25629
|
+
throw fsError("EAGAIN", `${path}: moved while its file id was assigned`);
|
|
25630
|
+
}
|
|
25631
|
+
bindId(path) {
|
|
25632
|
+
return this.assignId(path);
|
|
25633
|
+
}
|
|
25634
|
+
async assignId(path) {
|
|
25411
25635
|
const found = await this.locate(path, true);
|
|
25412
25636
|
const [entry] = await (this.meta.fresh ?? this.meta.get).call(this.meta, [found.path]);
|
|
25413
25637
|
if (entry?.fid !== void 0) return entry.fid;
|
|
25414
25638
|
const fid = crypto.randomUUID();
|
|
25639
|
+
let moved2 = false;
|
|
25640
|
+
await this.meta.update(found.path, (now) => {
|
|
25641
|
+
const vacatedSince = now?.vacated && (!entry?.vacated || now.ino !== entry.ino);
|
|
25642
|
+
if (vacatedSince || entry !== void 0 && now === void 0) {
|
|
25643
|
+
moved2 = true;
|
|
25644
|
+
return now;
|
|
25645
|
+
}
|
|
25646
|
+
return { ...occupied(now), path: found.path, fid: now?.fid ?? fid };
|
|
25647
|
+
});
|
|
25648
|
+
if (moved2) return void 0;
|
|
25415
25649
|
this.idWrites++;
|
|
25416
|
-
await this.
|
|
25417
|
-
|
|
25418
|
-
|
|
25419
|
-
|
|
25420
|
-
|
|
25421
|
-
const
|
|
25422
|
-
|
|
25650
|
+
const there = await this.locate(path, true).then(
|
|
25651
|
+
(at) => at.path === found.path,
|
|
25652
|
+
() => false
|
|
25653
|
+
);
|
|
25654
|
+
if (there) return (await this.meta.get([found.path]))[0]?.fid ?? fid;
|
|
25655
|
+
const gone = await this.locate(found.path, true).then(
|
|
25656
|
+
() => false,
|
|
25657
|
+
(err) => missing4(err)
|
|
25658
|
+
);
|
|
25659
|
+
if (gone) {
|
|
25660
|
+
await this.meta.update(found.path, (now) => {
|
|
25661
|
+
if (now?.fid !== fid || now.vacated) return now;
|
|
25662
|
+
const { fid: _, ...rest2 } = now;
|
|
25663
|
+
return rest2;
|
|
25664
|
+
});
|
|
25665
|
+
}
|
|
25666
|
+
return void 0;
|
|
25423
25667
|
}
|
|
25424
25668
|
heldUnder = () => [];
|
|
25425
25669
|
heldBy(held) {
|
|
@@ -25445,6 +25689,20 @@ var OpfsFs = class {
|
|
|
25445
25689
|
const remote = this.remote;
|
|
25446
25690
|
return remote && { ...remote.holdCounts(), held: remote.peerHeldPaths() };
|
|
25447
25691
|
}
|
|
25692
|
+
peerKeeps() {
|
|
25693
|
+
const remote = this.remote;
|
|
25694
|
+
if (!remote) return void 0;
|
|
25695
|
+
return {
|
|
25696
|
+
shared: () => this.shared(),
|
|
25697
|
+
heldWithin: (path) => remote.heldWithin(path),
|
|
25698
|
+
ask: (to, body, cap) => remote.ask(to, body, cap)
|
|
25699
|
+
};
|
|
25700
|
+
}
|
|
25701
|
+
servePeerKeeps(instance, handlers) {
|
|
25702
|
+
if (!this.remote) return;
|
|
25703
|
+
this.remote.instance = instance;
|
|
25704
|
+
this.remote.keeps = handlers;
|
|
25705
|
+
}
|
|
25448
25706
|
rehold(from, to) {
|
|
25449
25707
|
this.remote?.rehold(from, to);
|
|
25450
25708
|
}
|
|
@@ -26392,7 +26650,7 @@ var OpfsFs = class {
|
|
|
26392
26650
|
}
|
|
26393
26651
|
}
|
|
26394
26652
|
async writeFile(path, content, owner) {
|
|
26395
|
-
const [file, existed, target] = await this.fileFor(path);
|
|
26653
|
+
const [file, existed, target] = await this.fileFor(path, owner?.existing !== true);
|
|
26396
26654
|
if (!existed) await this.stampNew(target, owner, false);
|
|
26397
26655
|
if (!existed && content.length === 0) return;
|
|
26398
26656
|
let writable2;
|
|
@@ -30283,7 +30541,7 @@ var Launcher = class {
|
|
|
30283
30541
|
busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
|
|
30284
30542
|
owner: options.unlinkOwner,
|
|
30285
30543
|
marks: this.pathMarks,
|
|
30286
|
-
...this.
|
|
30544
|
+
...this.peerOptions(options.fs)
|
|
30287
30545
|
}
|
|
30288
30546
|
);
|
|
30289
30547
|
this.fs = this.processChain(fs, this.nodes, this.unlinked);
|
|
@@ -31117,7 +31375,7 @@ var Launcher = class {
|
|
|
31117
31375
|
const assign = this.mounts.locate(target.wasm) === void 0 ? this.rootFs.fileId : void 0;
|
|
31118
31376
|
return this.base.stat(target.wasm).then(
|
|
31119
31377
|
async (st) => {
|
|
31120
|
-
const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm);
|
|
31378
|
+
const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm)?.catch(() => void 0);
|
|
31121
31379
|
if (fid === void 0) return exeIdOf(fileIdentity(st.ino, st.mtime.getTime()));
|
|
31122
31380
|
return { exeId: `fid:${fid}:${st.mtime.getTime()}` };
|
|
31123
31381
|
},
|
|
@@ -32279,8 +32537,82 @@ var Launcher = class {
|
|
|
32279
32537
|
this.writers ??= this.writerStack();
|
|
32280
32538
|
return this.writers;
|
|
32281
32539
|
}
|
|
32540
|
+
peerGates = /* @__PURE__ */ new Map();
|
|
32541
|
+
peerOptions(fs) {
|
|
32542
|
+
const peers = fs.peerKeeps?.();
|
|
32543
|
+
queueMicrotask(() => fs.servePeerKeeps?.(this.unlinked.id, this.peerKept()));
|
|
32544
|
+
return {
|
|
32545
|
+
...peers ? { peers } : {},
|
|
32546
|
+
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
32547
|
+
};
|
|
32548
|
+
}
|
|
32549
|
+
peerKept() {
|
|
32550
|
+
const gates = this.peerGates;
|
|
32551
|
+
const gone = /* @__PURE__ */ new Set();
|
|
32552
|
+
const instance = this.unlinked.id;
|
|
32553
|
+
const ours = (hidden) => hidden.startsWith(`/${UNLINKED_DIR}/${instance}.p`) && /^[0-9a-f]+$/.test(hidden.slice(`/${UNLINKED_DIR}/${instance}.p`.length));
|
|
32554
|
+
const open = (tab, paths) => {
|
|
32555
|
+
for (const [key, release] of [...gates]) {
|
|
32556
|
+
const [owner, path] = key.split("\0");
|
|
32557
|
+
if (owner !== tab || paths && !paths.includes(path)) continue;
|
|
32558
|
+
gates.delete(key);
|
|
32559
|
+
release();
|
|
32560
|
+
}
|
|
32561
|
+
};
|
|
32562
|
+
return {
|
|
32563
|
+
keeping: async (tab, paths) => {
|
|
32564
|
+
for (const path of paths) {
|
|
32565
|
+
if (gone.has(tab)) return;
|
|
32566
|
+
const release = await this.unlinked.gate.acquire(path);
|
|
32567
|
+
const key = `${tab}\0${path}`;
|
|
32568
|
+
let done4 = false;
|
|
32569
|
+
const once2 = () => {
|
|
32570
|
+
if (done4) return;
|
|
32571
|
+
done4 = true;
|
|
32572
|
+
clearTimeout(timer);
|
|
32573
|
+
release();
|
|
32574
|
+
};
|
|
32575
|
+
const timer = setTimeout(() => {
|
|
32576
|
+
if (gates.get(key) === once2) gates.delete(key);
|
|
32577
|
+
once2();
|
|
32578
|
+
}, PEER_GATE_MS);
|
|
32579
|
+
if (gone.has(tab)) once2();
|
|
32580
|
+
else gates.set(key, once2);
|
|
32581
|
+
}
|
|
32582
|
+
},
|
|
32583
|
+
kept: async (tab, items, paths) => {
|
|
32584
|
+
for (const { path, hidden, fid, undo } of items) {
|
|
32585
|
+
if (undo !== void 0) this.unadopt(path, undo, fid);
|
|
32586
|
+
else this.adoptKept(path, hidden, fid, ours);
|
|
32587
|
+
}
|
|
32588
|
+
open(tab, paths);
|
|
32589
|
+
},
|
|
32590
|
+
left: (tab) => {
|
|
32591
|
+
gone.add(tab);
|
|
32592
|
+
open(tab);
|
|
32593
|
+
}
|
|
32594
|
+
};
|
|
32595
|
+
}
|
|
32596
|
+
unadopt(path, undo, fid) {
|
|
32597
|
+
for (const holds2 of this.held) holds2.unkeepAt(path, undo);
|
|
32598
|
+
void this.rootFs.announceHolds?.([[path, fid]], [], false);
|
|
32599
|
+
}
|
|
32600
|
+
adoptKept(path, hidden, fid, ours) {
|
|
32601
|
+
if (hidden === void 0 || !ours(hidden)) {
|
|
32602
|
+
this.held.doubt([path]);
|
|
32603
|
+
return;
|
|
32604
|
+
}
|
|
32605
|
+
const bound3 = [...this.held].map(
|
|
32606
|
+
(holds2) => [holds2, [...holds2.boundAt(path, fid), ...holds2.boundAt(hidden, fid)]]
|
|
32607
|
+
);
|
|
32608
|
+
const keeping = bound3.filter(([, ids]) => ids.length > 0);
|
|
32609
|
+
const file = this.unlinked.adopt(hidden, Math.max(keeping.length, 1));
|
|
32610
|
+
if (keeping.length === 0) file.release();
|
|
32611
|
+
for (const [holds2, ids] of keeping) holds2.keepBound(ids, path, file);
|
|
32612
|
+
void this.rootFs.announceHolds?.([], [path, hidden], false);
|
|
32613
|
+
}
|
|
32282
32614
|
rootFileId(path) {
|
|
32283
|
-
return this.mounts.locate(path) === void 0 ? this.rootFs.
|
|
32615
|
+
return this.mounts.locate(path) === void 0 ? this.rootFs.bindId?.(path) : void 0;
|
|
32284
32616
|
}
|
|
32285
32617
|
flushingPeers(fs) {
|
|
32286
32618
|
const peers = fs.peers?.();
|
|
@@ -32326,6 +32658,7 @@ var Launcher = class {
|
|
|
32326
32658
|
busy: (path) => [...this.openFiles].some((open) => open.holds(path)),
|
|
32327
32659
|
owner: this.unlinkOwnerOption,
|
|
32328
32660
|
marks: this.pathMarks,
|
|
32661
|
+
peers: this.rootFs.peerKeeps?.(),
|
|
32329
32662
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
32330
32663
|
}
|
|
32331
32664
|
);
|
|
@@ -35024,25 +35357,38 @@ function ownCacheDir(make = privateCacheDir) {
|
|
|
35024
35357
|
}
|
|
35025
35358
|
}
|
|
35026
35359
|
var swept = false;
|
|
35027
|
-
function privateCacheDir(base = tmpdir(), alive = pidAlive) {
|
|
35360
|
+
function privateCacheDir(base = tmpdir(), alive = pidAlive, uid = process.getuid?.()) {
|
|
35361
|
+
const parent = cacheParent(base, uid);
|
|
35028
35362
|
if (!swept) {
|
|
35029
35363
|
swept = true;
|
|
35030
|
-
sweepCacheDirs(
|
|
35364
|
+
sweepCacheDirs(parent, alive, uid);
|
|
35365
|
+
}
|
|
35366
|
+
return mkdtempSync(join(parent, `${process.pid}-`));
|
|
35367
|
+
}
|
|
35368
|
+
function cacheParent(base, uid) {
|
|
35369
|
+
const parent = join(base, uid === void 0 ? "slicc-kernel-cc" : `${CACHE_PREFIX}u${uid}`);
|
|
35370
|
+
try {
|
|
35371
|
+
mkdirSync(parent, { mode: 448 });
|
|
35372
|
+
} catch (err) {
|
|
35373
|
+
if (err.code !== "EEXIST") throw err;
|
|
35374
|
+
}
|
|
35375
|
+
const st = lstatSync(parent);
|
|
35376
|
+
if (!st.isDirectory() || uid !== void 0 && (st.uid !== uid || (st.mode & 63) !== 0)) {
|
|
35377
|
+
throw new Error(`slicc-kernel: ${parent} is not a private directory of this user`);
|
|
35031
35378
|
}
|
|
35032
|
-
return
|
|
35379
|
+
return parent;
|
|
35033
35380
|
}
|
|
35034
|
-
function sweepCacheDirs(
|
|
35381
|
+
function sweepCacheDirs(parent, alive = pidAlive, uid = process.getuid?.()) {
|
|
35035
35382
|
let names2;
|
|
35036
35383
|
try {
|
|
35037
|
-
names2 = readdirSync(
|
|
35384
|
+
names2 = readdirSync(parent);
|
|
35038
35385
|
} catch {
|
|
35039
35386
|
return;
|
|
35040
35387
|
}
|
|
35041
35388
|
for (const name2 of names2) {
|
|
35042
|
-
|
|
35043
|
-
const pid = Number(name2.slice(CACHE_PREFIX.length).split("-")[0]);
|
|
35389
|
+
const pid = Number(name2.split("-")[0]);
|
|
35044
35390
|
if (!Number.isInteger(pid) || pid <= 0 || alive(pid)) continue;
|
|
35045
|
-
removeOurs(join(
|
|
35391
|
+
removeOurs(join(parent, name2), uid);
|
|
35046
35392
|
}
|
|
35047
35393
|
}
|
|
35048
35394
|
function removeOurs(path, uid) {
|