@ai-ecoverse/slicc-kernel 1.160.3 → 1.162.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/dist/index.js.map +1 -1
- package/dist/kernel-worker.js +487 -41
- package/dist/kernel-worker.js.map +3 -3
- package/dist/meta.js.map +2 -2
- package/dist/node-process-worker.js +5 -1
- package/dist/node-process-worker.js.map +2 -2
- package/dist/node.js +487 -41
- package/dist/node.js.map +3 -3
- package/dist/process-worker.js +5 -1
- package/dist/process-worker.js.map +2 -2
- package/dist/types/fs/opfs.d.ts +14 -2
- package/dist/types/fs/remote.d.ts +75 -0
- package/dist/types/fs/types.d.ts +44 -0
- package/dist/types/fs/unlinked.d.ts +21 -1
- package/dist/types/kernel/host.d.ts +2 -1
- package/dist/types/kernel/proc-info.d.ts +6 -0
- package/dist/types/launcher.d.ts +5 -0
- package/dist/types/mount/mount-fs.d.ts +8 -3
- package/dist/types/realm/sync-fs-token-registry.d.ts +2 -1
- package/package.json +2 -2
package/dist/node.js
CHANGED
|
@@ -3675,7 +3675,7 @@ function satisfies(version, range2) {
|
|
|
3675
3675
|
}
|
|
3676
3676
|
|
|
3677
3677
|
// src/version.ts
|
|
3678
|
-
var KERNEL_VERSION = true ? "1.
|
|
3678
|
+
var KERNEL_VERSION = true ? "1.162.0" : "0.0.0-dev";
|
|
3679
3679
|
var OS_RELEASE = "6.0.0-slicc";
|
|
3680
3680
|
function procVersion() {
|
|
3681
3681
|
return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
|
|
@@ -4638,6 +4638,9 @@ var LOOKUP_OPS = [
|
|
|
4638
4638
|
var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
4639
4639
|
var TOUCHED_MAX = 4096;
|
|
4640
4640
|
var parentOf2 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
|
|
4641
|
+
var PEER_RM_MS = 1e3;
|
|
4642
|
+
var PEER_RENAME_MS = 1e4;
|
|
4643
|
+
var PEER_GATE_MS = PEER_RENAME_MS + 5e3;
|
|
4641
4644
|
var lockName = (instance) => `slicc-unlinked:${instance}`;
|
|
4642
4645
|
var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
|
|
4643
4646
|
var PathGate = class {
|
|
@@ -4680,7 +4683,9 @@ var UnlinkedKeeper = class {
|
|
|
4680
4683
|
links = /* @__PURE__ */ new Map();
|
|
4681
4684
|
linked = /* @__PURE__ */ new Map();
|
|
4682
4685
|
dirOwner;
|
|
4686
|
+
peers;
|
|
4683
4687
|
constructor(fs, holders, rootOf, options = {}) {
|
|
4688
|
+
this.peers = options.peers;
|
|
4684
4689
|
this.marks = options.marks ?? new PathMarks();
|
|
4685
4690
|
const { locks, owner } = options;
|
|
4686
4691
|
this.dirOwner = options.dirOwner;
|
|
@@ -4756,22 +4761,29 @@ var UnlinkedKeeper = class {
|
|
|
4756
4761
|
lstat: (path) => unreplaced(path, async () => counted3(await fs.lstat(path))),
|
|
4757
4762
|
async rm(path, options) {
|
|
4758
4763
|
const target = fs.resolvePath("/", path);
|
|
4759
|
-
await
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
+
const kept = await keeper.peerKeep(target, false, () => keeper.removable(target, options));
|
|
4765
|
+
if (kept.whole) return;
|
|
4766
|
+
await kept.undoing(
|
|
4767
|
+
() => replace(target, async (gated, sealed) => {
|
|
4768
|
+
const pinned = await keeper.pin(target, gated, sealed);
|
|
4769
|
+
if (pinned.moved.has(target)) pinned.commit();
|
|
4770
|
+
else await keeper.settle(pinned, () => fs.rm(path, options));
|
|
4771
|
+
})
|
|
4772
|
+
);
|
|
4764
4773
|
},
|
|
4765
4774
|
async rename(from, to) {
|
|
4766
4775
|
const target = fs.resolvePath("/", to);
|
|
4767
4776
|
const source = fs.resolvePath("/", from);
|
|
4768
4777
|
if (target === source) return fs.rename(from, to);
|
|
4769
|
-
await keeper.
|
|
4770
|
-
|
|
4771
|
-
() =>
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4778
|
+
const kept = await keeper.peerKeep(target, true, () => keeper.renamable(source, target));
|
|
4779
|
+
await kept.undoing(
|
|
4780
|
+
() => keeper.marks.around(
|
|
4781
|
+
[source],
|
|
4782
|
+
() => replace(target, async (gated, sealed) => {
|
|
4783
|
+
const pinned = await keeper.pin(target, gated, sealed);
|
|
4784
|
+
await keeper.settle(pinned, () => fs.rename(from, to));
|
|
4785
|
+
})
|
|
4786
|
+
)
|
|
4775
4787
|
);
|
|
4776
4788
|
},
|
|
4777
4789
|
symlink: (target, path, owner) => keeper.marks.around([fs.resolvePath("/", path)], () => fs.symlink(target, path, owner)),
|
|
@@ -4916,6 +4928,114 @@ var UnlinkedKeeper = class {
|
|
|
4916
4928
|
(err) => err?.code !== "ENOENT" && err?.code !== "ENOTDIR"
|
|
4917
4929
|
);
|
|
4918
4930
|
}
|
|
4931
|
+
get id() {
|
|
4932
|
+
return this.instance;
|
|
4933
|
+
}
|
|
4934
|
+
adopt(hidden, refs) {
|
|
4935
|
+
return this.keptFile(hidden, { refs });
|
|
4936
|
+
}
|
|
4937
|
+
async peerKeep(target, over, valid2 = async () => true) {
|
|
4938
|
+
const none = { whole: false, undoing: (op) => op() };
|
|
4939
|
+
const peers = this.peers;
|
|
4940
|
+
if (!peers?.shared() || this.rootOf(target) !== "/") return none;
|
|
4941
|
+
const held = peers.heldWithin(target).filter((holder) => holder.instance !== void 0);
|
|
4942
|
+
if (held.length === 0 || !await valid2()) return none;
|
|
4943
|
+
const tabs = /* @__PURE__ */ new Map();
|
|
4944
|
+
for (const holder of held) tabs.set(holder.tab, [...tabs.get(holder.tab) ?? [], holder]);
|
|
4945
|
+
const asked = [...tabs].map(([tab, holders]) => {
|
|
4946
|
+
const paths = holders.map((holder) => holder.path);
|
|
4947
|
+
return { tab, holders, paths };
|
|
4948
|
+
});
|
|
4949
|
+
const heard = await Promise.all(
|
|
4950
|
+
asked.map(
|
|
4951
|
+
({ tab, paths }) => peers.ask(tab, { keeping: { paths } }, over ? PEER_RENAME_MS : PEER_RM_MS)
|
|
4952
|
+
)
|
|
4953
|
+
);
|
|
4954
|
+
if (over && heard.includes(false)) {
|
|
4955
|
+
for (const { tab, paths } of asked)
|
|
4956
|
+
void peers.ask(tab, { kept: { items: [], paths } }, PEER_RM_MS);
|
|
4957
|
+
throw fsError("EBUSY", `${target}: open in another tab that did not answer`);
|
|
4958
|
+
}
|
|
4959
|
+
const { whole, moved: moved2 } = await this.setAside(peers, target, asked, heard);
|
|
4960
|
+
return { whole, undoing: (op) => this.undoing(op, moved2) };
|
|
4961
|
+
}
|
|
4962
|
+
async setAside(peers, target, asked, heard) {
|
|
4963
|
+
const mine = this.heldWithin(target);
|
|
4964
|
+
const taken = /* @__PURE__ */ new Set();
|
|
4965
|
+
let whole = false;
|
|
4966
|
+
const told = [];
|
|
4967
|
+
const moved2 = [];
|
|
4968
|
+
for (const [i, { tab, holders, paths }] of asked.entries()) {
|
|
4969
|
+
const items = [];
|
|
4970
|
+
for (const holder of holders) {
|
|
4971
|
+
const free = !mine.has(holder.path) && !taken.has(holder.path);
|
|
4972
|
+
const hidden = free ? await this.asideFor(holder.path, holder.instance) : void 0;
|
|
4973
|
+
if (hidden !== void 0) {
|
|
4974
|
+
taken.add(holder.path);
|
|
4975
|
+
moved2.push({ tab, path: holder.path, hidden, fid: holder.fid });
|
|
4976
|
+
if (holder.path === target) whole = true;
|
|
4977
|
+
}
|
|
4978
|
+
items.push({ path: holder.path, hidden, fid: holder.fid });
|
|
4979
|
+
}
|
|
4980
|
+
const answer2 = peers.ask(tab, { kept: { items, paths } }, PEER_RM_MS);
|
|
4981
|
+
if (heard[i]) told.push(answer2);
|
|
4982
|
+
}
|
|
4983
|
+
await Promise.all(told);
|
|
4984
|
+
return { whole, moved: moved2 };
|
|
4985
|
+
}
|
|
4986
|
+
async undoing(op, moved2) {
|
|
4987
|
+
try {
|
|
4988
|
+
return await op();
|
|
4989
|
+
} catch (err) {
|
|
4990
|
+
for (const { tab, path, hidden, fid } of moved2) {
|
|
4991
|
+
const back = await this.serial(async () => {
|
|
4992
|
+
if (await this.present(path)) return false;
|
|
4993
|
+
return this.fs.rename(hidden, path).then(
|
|
4994
|
+
() => true,
|
|
4995
|
+
() => false
|
|
4996
|
+
);
|
|
4997
|
+
});
|
|
4998
|
+
if (!back) continue;
|
|
4999
|
+
void this.peers?.ask(
|
|
5000
|
+
tab,
|
|
5001
|
+
{ kept: { items: [{ path, hidden: void 0, fid, undo: hidden }], paths: [] } },
|
|
5002
|
+
PEER_RM_MS
|
|
5003
|
+
);
|
|
5004
|
+
}
|
|
5005
|
+
throw err;
|
|
5006
|
+
}
|
|
5007
|
+
}
|
|
5008
|
+
async removable(target, options) {
|
|
5009
|
+
const st = await this.fs.lstat(target).catch(() => void 0);
|
|
5010
|
+
return st !== void 0 && (!st.isDirectory || options?.recursive === true);
|
|
5011
|
+
}
|
|
5012
|
+
async renamable(source, target) {
|
|
5013
|
+
const from = await this.fs.lstat(source);
|
|
5014
|
+
const to = await this.fs.lstat(target).catch(() => void 0);
|
|
5015
|
+
if (!to) return true;
|
|
5016
|
+
if (to.isDirectory && !from.isDirectory) throw fsError("EISDIR", target);
|
|
5017
|
+
if (!to.isDirectory && from.isDirectory) throw fsError("ENOTDIR", target);
|
|
5018
|
+
if (to.isDirectory && (await this.fs.readdir(target)).length > 0)
|
|
5019
|
+
throw fsError("ENOTEMPTY", target);
|
|
5020
|
+
return true;
|
|
5021
|
+
}
|
|
5022
|
+
async asideFor(path, instance) {
|
|
5023
|
+
return await this.hideFor(path, instance) ?? this.hideFor(path, instance);
|
|
5024
|
+
}
|
|
5025
|
+
hideFor(path, instance) {
|
|
5026
|
+
return this.serial(async () => {
|
|
5027
|
+
const dir = dirOf("/");
|
|
5028
|
+
const hidden = `${dir}/${instance}.p${crypto.randomUUID().replaceAll("-", "")}`;
|
|
5029
|
+
try {
|
|
5030
|
+
const owner = this.dirOwner;
|
|
5031
|
+
await this.fs.mkdir(dir, { recursive: true, ...owner ? { owner } : {} });
|
|
5032
|
+
await this.fs.rename(path, hidden);
|
|
5033
|
+
return hidden;
|
|
5034
|
+
} catch {
|
|
5035
|
+
return void 0;
|
|
5036
|
+
}
|
|
5037
|
+
});
|
|
5038
|
+
}
|
|
4919
5039
|
keptFile(hidden, entry) {
|
|
4920
5040
|
return {
|
|
4921
5041
|
hidden,
|
|
@@ -4970,7 +5090,7 @@ var UnlinkedKeeper = class {
|
|
|
4970
5090
|
}
|
|
4971
5091
|
async tidy(dir) {
|
|
4972
5092
|
const left = await this.fs.readdir(dir).catch(() => [UNLINKED_DIR]);
|
|
4973
|
-
if (left.length === 0) await this.fs.rm(dir
|
|
5093
|
+
if (left.length === 0) await this.fs.rm(dir).catch(() => void 0);
|
|
4974
5094
|
}
|
|
4975
5095
|
sweep(root) {
|
|
4976
5096
|
const query = this.locks?.query?.bind(this.locks);
|
|
@@ -6380,6 +6500,11 @@ async function shownTarget(fs, target) {
|
|
|
6380
6500
|
const st = await quiet2(fs.stat(target));
|
|
6381
6501
|
return st?.isFile && await readsAs2(fs, NOBODY, target) ? st : void 0;
|
|
6382
6502
|
}
|
|
6503
|
+
async function madeFor(owner, virtual) {
|
|
6504
|
+
if (!owner?.existing || await virtual() === void 0) return owner;
|
|
6505
|
+
const { existing: _, ...made2 } = owner;
|
|
6506
|
+
return made2;
|
|
6507
|
+
}
|
|
6383
6508
|
function withCommandDirs(fs, names2, userDir = async () => void 0, owners = false, targetOf2 = async () => void 0) {
|
|
6384
6509
|
async function nearest(path) {
|
|
6385
6510
|
const st = await fs.stat(path).catch(() => void 0);
|
|
@@ -6477,7 +6602,7 @@ function withCommandDirs(fs, names2, userDir = async () => void 0, owners = fals
|
|
|
6477
6602
|
}),
|
|
6478
6603
|
readFileBuffer: (path) => fs.readFileBuffer(path).catch((err) => content(path, err)),
|
|
6479
6604
|
commandTarget: async (path) => (await entryTarget(path))?.path,
|
|
6480
|
-
writeFile: (path, content2, owner) => fs.writeFile(path, content2, owner),
|
|
6605
|
+
writeFile: async (path, content2, owner) => fs.writeFile(path, content2, await madeFor(owner, () => entryTarget(path))),
|
|
6481
6606
|
link: (from, to, follow2) => fs.link(from, to, follow2),
|
|
6482
6607
|
links: (path) => mayLink(fs, path),
|
|
6483
6608
|
async exists(path) {
|
|
@@ -6642,6 +6767,10 @@ function withEtcFiles(fs, files) {
|
|
|
6642
6767
|
}
|
|
6643
6768
|
} : {},
|
|
6644
6769
|
readFile: (path) => fs.readFile(path).catch(fallback(path, (b) => new TextDecoder().decode(b))),
|
|
6770
|
+
writeFile: async (path, content, owner) => {
|
|
6771
|
+
if (owner?.existing && files.has(path)) await materialize(path);
|
|
6772
|
+
return fs.writeFile(path, content, owner);
|
|
6773
|
+
},
|
|
6645
6774
|
...fs.append ? {
|
|
6646
6775
|
append: async (path, bytes, options) => {
|
|
6647
6776
|
if (files.has(path)) await materialize(path);
|
|
@@ -8919,12 +9048,15 @@ function linkedTo(mount, path) {
|
|
|
8919
9048
|
var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
|
|
8920
9049
|
var HeldSet = class extends Set {
|
|
8921
9050
|
idle;
|
|
8922
|
-
|
|
9051
|
+
moved;
|
|
9052
|
+
constructor(idle2, moved2) {
|
|
8923
9053
|
super();
|
|
8924
9054
|
this.idle = idle2;
|
|
9055
|
+
this.moved = moved2;
|
|
8925
9056
|
}
|
|
8926
9057
|
add(holds2) {
|
|
8927
9058
|
holds2.onIdle = this.idle;
|
|
9059
|
+
holds2.onMoved = this.moved;
|
|
8928
9060
|
return super.add(holds2);
|
|
8929
9061
|
}
|
|
8930
9062
|
doubt(paths, over) {
|
|
@@ -8961,6 +9093,7 @@ var HeldPaths = class extends Map {
|
|
|
8961
9093
|
doubted = /* @__PURE__ */ new Map();
|
|
8962
9094
|
doubts = 0;
|
|
8963
9095
|
onIdle;
|
|
9096
|
+
onMoved;
|
|
8964
9097
|
keyOf(path) {
|
|
8965
9098
|
return this.aliases.get(path) ?? path;
|
|
8966
9099
|
}
|
|
@@ -9018,12 +9151,25 @@ var HeldPaths = class extends Map {
|
|
|
9018
9151
|
for (const file of this.unlinked.values()) if (file.hidden === hidden) return true;
|
|
9019
9152
|
return false;
|
|
9020
9153
|
}
|
|
9154
|
+
boundAt(path, fid) {
|
|
9155
|
+
return this.handlesAt(path).filter(
|
|
9156
|
+
(id2) => !this.keptHandles.has(id2) && this.bound.get(id2)?.fid === fid
|
|
9157
|
+
);
|
|
9158
|
+
}
|
|
9159
|
+
keepBound(ids, path, file) {
|
|
9160
|
+
for (const id2 of ids) this.keptHandles.set(id2, file);
|
|
9161
|
+
this.uses.set(file, ids.length);
|
|
9162
|
+
this.unlinked.set(path, file);
|
|
9163
|
+
}
|
|
9021
9164
|
keep(path, file) {
|
|
9022
9165
|
const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
|
|
9023
9166
|
for (const id2 of fresh3) this.keptHandles.set(id2, file);
|
|
9024
9167
|
this.uses.set(file, Math.max(fresh3.length, 1));
|
|
9025
9168
|
this.unlinked.set(path, file);
|
|
9026
9169
|
}
|
|
9170
|
+
unkeepAt(path, hidden) {
|
|
9171
|
+
for (const file of [...this.uses.keys()]) if (file.hidden === hidden) this.unkeep(path, file);
|
|
9172
|
+
}
|
|
9027
9173
|
unkeep(path, file) {
|
|
9028
9174
|
for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
|
|
9029
9175
|
this.uses.delete(file);
|
|
@@ -9121,6 +9267,7 @@ var HeldPaths = class extends Map {
|
|
|
9121
9267
|
for (const [id2, key] of this.handles) {
|
|
9122
9268
|
if (within5(key, from)) this.handles.set(id2, to + key.slice(from.length));
|
|
9123
9269
|
}
|
|
9270
|
+
this.onMoved?.(from, to);
|
|
9124
9271
|
}
|
|
9125
9272
|
isRevoked(path) {
|
|
9126
9273
|
return this.revoked.has(path);
|
|
@@ -9591,12 +9738,12 @@ var MountFs = class _MountFs {
|
|
|
9591
9738
|
const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
|
|
9592
9739
|
return this.follow(this.base.resolvePath(parentOf5(real), target), hops + 1, create);
|
|
9593
9740
|
}
|
|
9594
|
-
async open(mount, rel2, write, owner) {
|
|
9741
|
+
async open(mount, rel2, write, owner, create = write) {
|
|
9595
9742
|
return await mount.conn.call({
|
|
9596
9743
|
op: "open",
|
|
9597
9744
|
path: rel2,
|
|
9598
9745
|
write,
|
|
9599
|
-
create
|
|
9746
|
+
create,
|
|
9600
9747
|
truncate: write,
|
|
9601
9748
|
exclusive: false,
|
|
9602
9749
|
...owner
|
|
@@ -9693,10 +9840,10 @@ var MountFs = class _MountFs {
|
|
|
9693
9840
|
`${real} would be larger than this mount's maxfile (${mount.maxFile})`
|
|
9694
9841
|
);
|
|
9695
9842
|
}
|
|
9696
|
-
const made2 = await this.driverOwner(found, real, owner, false);
|
|
9843
|
+
const made2 = owner?.existing ? void 0 : await this.driverOwner(found, real, owner, false);
|
|
9697
9844
|
for (let attempt = 1; ; attempt++) {
|
|
9698
9845
|
try {
|
|
9699
|
-
return await this.writeWhole(mount, real, rel2, bytes, made2);
|
|
9846
|
+
return await this.writeWhole(mount, real, rel2, bytes, made2, owner?.existing !== true);
|
|
9700
9847
|
} catch (err) {
|
|
9701
9848
|
if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
|
|
9702
9849
|
}
|
|
@@ -9714,10 +9861,10 @@ var MountFs = class _MountFs {
|
|
|
9714
9861
|
this.table.forget(mount, real);
|
|
9715
9862
|
}
|
|
9716
9863
|
}
|
|
9717
|
-
async writeWhole(mount, real, rel2, bytes, owner) {
|
|
9864
|
+
async writeWhole(mount, real, rel2, bytes, owner, create = true) {
|
|
9718
9865
|
await this.table.commit(real);
|
|
9719
9866
|
this.table.forget(mount, real);
|
|
9720
|
-
const fh = await this.open(mount, rel2, true, owner);
|
|
9867
|
+
const fh = await this.open(mount, rel2, true, owner, create);
|
|
9721
9868
|
const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
|
|
9722
9869
|
try {
|
|
9723
9870
|
for (let offset = 0; offset < bytes.length; offset += chunk2) {
|
|
@@ -10454,6 +10601,11 @@ function ownerFor(entry, mode) {
|
|
|
10454
10601
|
if (!cred) return void 0;
|
|
10455
10602
|
return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
|
|
10456
10603
|
}
|
|
10604
|
+
function writeOwner(entry, req) {
|
|
10605
|
+
const owner = ownerFor(entry, 438);
|
|
10606
|
+
if (req.handle === void 0 || req.mode !== 1) return owner;
|
|
10607
|
+
return { ...owner ?? {}, existing: true };
|
|
10608
|
+
}
|
|
10457
10609
|
function plainDirMode(entry, mode) {
|
|
10458
10610
|
if (entry.cred || mode === void 0) return void 0;
|
|
10459
10611
|
const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
|
|
@@ -10524,7 +10676,7 @@ async function run(entry, path, req) {
|
|
|
10524
10676
|
case "read":
|
|
10525
10677
|
return { ok: true, kind: "bytes", bytes: await readSource(entry, path) };
|
|
10526
10678
|
case "write":
|
|
10527
|
-
await fs.writeFile(path, req.body ?? new Uint8Array(0),
|
|
10679
|
+
await fs.writeFile(path, req.body ?? new Uint8Array(0), writeOwner(entry, req));
|
|
10528
10680
|
return done;
|
|
10529
10681
|
case "exists":
|
|
10530
10682
|
return json2(await fs.exists(path));
|
|
@@ -10748,12 +10900,18 @@ async function holdOp(entry, path, req) {
|
|
|
10748
10900
|
if (req.nofollow && !await ownEntry(entry, path, real ?? path)) continue;
|
|
10749
10901
|
const fid = req.ino === void 0 ? void 0 : await idOf(entry, real ?? path);
|
|
10750
10902
|
if (!r || !r.missed(path) && !r.changedSince(path, gen) && !r.inFlight?.(path))
|
|
10751
|
-
return json2(entry
|
|
10903
|
+
return json2(await heldOpen(entry, path, real, req, fid));
|
|
10752
10904
|
}
|
|
10753
10905
|
}
|
|
10754
10906
|
entry.hold?.(path, on, open, void 0, req.handle);
|
|
10755
10907
|
return done;
|
|
10756
10908
|
}
|
|
10909
|
+
async function heldOpen(entry, path, real, req, fid) {
|
|
10910
|
+
const handle = entry.hold?.(path, true, true, real, void 0, req.ino, fid);
|
|
10911
|
+
if (handle === void 0) return null;
|
|
10912
|
+
await entry.announceHold?.(real ?? path, fid, ((req.mode ?? 0) & HOLD_CREATED) !== 0);
|
|
10913
|
+
return handle;
|
|
10914
|
+
}
|
|
10757
10915
|
async function idOf(entry, path) {
|
|
10758
10916
|
return await entry.fileId?.(path)?.catch(() => void 0) ?? void 0;
|
|
10759
10917
|
}
|
|
@@ -11771,6 +11929,10 @@ var SYS_TREE = {
|
|
|
11771
11929
|
`,
|
|
11772
11930
|
slicc_file_ids: (listing) => `${listing.fileIds ?? 0}
|
|
11773
11931
|
`,
|
|
11932
|
+
slicc_peer_holds: ({ holds: holds2 }) => `${holds2?.mine ?? 0} ${holds2?.peers ?? 0} ${holds2?.sent ?? 0}
|
|
11933
|
+
`,
|
|
11934
|
+
slicc_peer_held: ({ holds: holds2 }) => (holds2?.held ?? []).map((path) => `${path}
|
|
11935
|
+
`).join(""),
|
|
11774
11936
|
slicc_rename_acks: (listing) => {
|
|
11775
11937
|
const samples = [...listing.renameAcks?.ackMs ?? []].sort((a, b) => a - b);
|
|
11776
11938
|
const median = samples[Math.floor(samples.length / 2)] ?? 0;
|
|
@@ -22853,6 +23015,7 @@ function holdingToken(opts, holds2, caller) {
|
|
|
22853
23015
|
...opts.statfs ? { statfs: opts.statfs } : {},
|
|
22854
23016
|
...opts.renames ? { renames: opts.renames } : {},
|
|
22855
23017
|
...opts.fileId ? { fileId: opts.fileId } : {},
|
|
23018
|
+
...opts.announceHold ? { announceHold: opts.announceHold } : {},
|
|
22856
23019
|
...opts.renameSeq ? { renameSeq: opts.renameSeq } : {},
|
|
22857
23020
|
hold: (path, on, open, real, handle, ino, fid) => holds2.hold(path, on, open, real, handle, ino, fid),
|
|
22858
23021
|
boundIno: holds2.boundIno.bind(holds2),
|
|
@@ -23404,6 +23567,14 @@ var RemoteWatch = class {
|
|
|
23404
23567
|
mourned = /* @__PURE__ */ new Set();
|
|
23405
23568
|
others = /* @__PURE__ */ new Map();
|
|
23406
23569
|
union = /* @__PURE__ */ new Set();
|
|
23570
|
+
myHolds = /* @__PURE__ */ new Map();
|
|
23571
|
+
peerHolds = /* @__PURE__ */ new Map();
|
|
23572
|
+
holdsSent = 0;
|
|
23573
|
+
silent = /* @__PURE__ */ new Set();
|
|
23574
|
+
holders = /* @__PURE__ */ new Set();
|
|
23575
|
+
instances = /* @__PURE__ */ new Map();
|
|
23576
|
+
instance;
|
|
23577
|
+
keeps;
|
|
23407
23578
|
listeners = /* @__PURE__ */ new Set();
|
|
23408
23579
|
waiting = /* @__PURE__ */ new Map();
|
|
23409
23580
|
locks;
|
|
@@ -23447,7 +23618,7 @@ var RemoteWatch = class {
|
|
|
23447
23618
|
this.settled = true;
|
|
23448
23619
|
this.recount();
|
|
23449
23620
|
});
|
|
23450
|
-
this.channel.postMessage({ hello: this.tab, renames: true });
|
|
23621
|
+
this.channel.postMessage({ hello: this.tab, renames: true, keeps: true });
|
|
23451
23622
|
}
|
|
23452
23623
|
async join() {
|
|
23453
23624
|
if (!this.locks) return;
|
|
@@ -23507,8 +23678,12 @@ var RemoteWatch = class {
|
|
|
23507
23678
|
return `${this.name}:${tab}`;
|
|
23508
23679
|
}
|
|
23509
23680
|
receive(data) {
|
|
23681
|
+
const spoke = data.hello ?? data.here ?? data.tab ?? data.from;
|
|
23682
|
+
if (spoke !== void 0) this.silent.delete(spoke);
|
|
23510
23683
|
const sender = data.hello ?? data.here ?? (data.paths !== void 0 ? data.tab : void 0);
|
|
23511
23684
|
if (sender !== void 0 && data.renames) this.ackers.add(sender);
|
|
23685
|
+
const keeper = data.hello ?? data.here;
|
|
23686
|
+
if (keeper !== void 0 && data.keeps) this.holders.add(keeper);
|
|
23512
23687
|
if (data.hello !== void 0) this.greeted(data.hello);
|
|
23513
23688
|
if (data.renaming !== void 0 && data.tab !== void 0) this.heard(data);
|
|
23514
23689
|
if (data.renamed !== void 0) this.renamed?.(data.renamed);
|
|
@@ -23519,7 +23694,9 @@ var RemoteWatch = class {
|
|
|
23519
23694
|
this.met?.();
|
|
23520
23695
|
}
|
|
23521
23696
|
if (data.tab !== void 0 && data.paths !== void 0) this.announced(data);
|
|
23697
|
+
this.holdsHeard(data);
|
|
23522
23698
|
if (data.ack !== void 0 && data.to === this.tab && data.from !== void 0) {
|
|
23699
|
+
if (data.instance !== void 0) this.instances.set(data.from, data.instance);
|
|
23523
23700
|
this.acked(data.ack, data.from);
|
|
23524
23701
|
}
|
|
23525
23702
|
if (data.changed !== void 0) this.emit({ paths: data.changed, trees: [], content: true });
|
|
@@ -23545,8 +23722,11 @@ var RemoteWatch = class {
|
|
|
23545
23722
|
void this.locks.request(name2, async () => {
|
|
23546
23723
|
this.mourned.delete(peer);
|
|
23547
23724
|
this.others.delete(peer);
|
|
23725
|
+
this.peerHolds.delete(peer);
|
|
23726
|
+
this.keeps?.left(peer);
|
|
23548
23727
|
this.peers.delete(peer);
|
|
23549
23728
|
this.ackers.delete(peer);
|
|
23729
|
+
this.holders.delete(peer);
|
|
23550
23730
|
this.flushedAt(peer);
|
|
23551
23731
|
this.rebuild();
|
|
23552
23732
|
this.recount();
|
|
@@ -23559,8 +23739,9 @@ var RemoteWatch = class {
|
|
|
23559
23739
|
greeted(peer) {
|
|
23560
23740
|
this.arrived(peer);
|
|
23561
23741
|
const answer2 = () => {
|
|
23562
|
-
this.channel.postMessage({ here: this.tab, to: peer, renames: true });
|
|
23742
|
+
this.channel.postMessage({ here: this.tab, to: peer, renames: true, keeps: true });
|
|
23563
23743
|
if (this.mine.length > 0) void this.announce(this.mine);
|
|
23744
|
+
if (this.myHolds.size > 0) this.sendHolds([...this.myHolds], []);
|
|
23564
23745
|
};
|
|
23565
23746
|
if (!this.quiet) {
|
|
23566
23747
|
answer2();
|
|
@@ -23583,6 +23764,127 @@ var RemoteWatch = class {
|
|
|
23583
23764
|
this.rebuild();
|
|
23584
23765
|
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
23585
23766
|
}
|
|
23767
|
+
holdsHeard(data) {
|
|
23768
|
+
if (data.tab === void 0) return;
|
|
23769
|
+
if (data.holds !== void 0) this.heldBy(data);
|
|
23770
|
+
if (data.to === this.tab && (data.keeping || data.kept)) void this.keepAsked(data);
|
|
23771
|
+
}
|
|
23772
|
+
keepChains = /* @__PURE__ */ new Map();
|
|
23773
|
+
keepAsked(data) {
|
|
23774
|
+
const from = data.tab;
|
|
23775
|
+
const next3 = (this.keepChains.get(from) ?? Promise.resolve()).then(() => this.keepOne(data));
|
|
23776
|
+
this.keepChains.set(from, next3);
|
|
23777
|
+
return next3;
|
|
23778
|
+
}
|
|
23779
|
+
async keepOne({ tab, seq: seq2, keeping, kept }) {
|
|
23780
|
+
const from = tab;
|
|
23781
|
+
const keeps = this.keeps;
|
|
23782
|
+
try {
|
|
23783
|
+
if (keeping) await keeps?.keeping(from, keeping.paths);
|
|
23784
|
+
if (kept)
|
|
23785
|
+
await keeps?.kept(from, kept.items, kept.paths ?? kept.items.map((item) => item.path));
|
|
23786
|
+
} finally {
|
|
23787
|
+
this.channel.postMessage({
|
|
23788
|
+
ack: seq2,
|
|
23789
|
+
from: this.tab,
|
|
23790
|
+
to: from,
|
|
23791
|
+
...this.instance === void 0 ? {} : { instance: this.instance }
|
|
23792
|
+
});
|
|
23793
|
+
}
|
|
23794
|
+
}
|
|
23795
|
+
heldBy({ tab, holds: holds2, seq: seq2 }) {
|
|
23796
|
+
const from = tab;
|
|
23797
|
+
const { add, drop } = holds2;
|
|
23798
|
+
if (holds2?.instance !== void 0) this.instances.set(from, holds2.instance);
|
|
23799
|
+
const held = this.peerHolds.get(from) ?? /* @__PURE__ */ new Map();
|
|
23800
|
+
for (const path of drop) held.delete(path);
|
|
23801
|
+
for (const [path, fid] of add) held.set(path, fid);
|
|
23802
|
+
if (held.size > 0) this.peerHolds.set(from, held);
|
|
23803
|
+
else this.peerHolds.delete(from);
|
|
23804
|
+
this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
|
|
23805
|
+
}
|
|
23806
|
+
hold(add, drop, wait2) {
|
|
23807
|
+
const fresh3 = add.filter(
|
|
23808
|
+
([path, fid]) => !this.myHolds.has(path) || this.myHolds.get(path) !== fid
|
|
23809
|
+
);
|
|
23810
|
+
const gone = drop.filter((path) => this.myHolds.has(path));
|
|
23811
|
+
for (const path of gone) this.myHolds.delete(path);
|
|
23812
|
+
for (const [path, fid] of fresh3) this.myHolds.set(path, fid);
|
|
23813
|
+
if (fresh3.length === 0 && gone.length === 0 || this.peers.size === 0)
|
|
23814
|
+
return Promise.resolve();
|
|
23815
|
+
const seq2 = this.sendHolds(fresh3, gone);
|
|
23816
|
+
if (!wait2) return Promise.resolve();
|
|
23817
|
+
const heard = new Set(
|
|
23818
|
+
[...this.peers].filter((peer) => this.holders.has(peer) && !this.silent.has(peer))
|
|
23819
|
+
);
|
|
23820
|
+
return this.acks(seq2, heard, (left) => {
|
|
23821
|
+
for (const peer of left) this.silent.add(peer);
|
|
23822
|
+
});
|
|
23823
|
+
}
|
|
23824
|
+
rehold(from, to) {
|
|
23825
|
+
const add = [];
|
|
23826
|
+
const drop = [];
|
|
23827
|
+
for (const [path, fid] of [...this.myHolds]) {
|
|
23828
|
+
if (path !== from && !path.startsWith(`${from}/`)) continue;
|
|
23829
|
+
const moved2 = to + path.slice(from.length);
|
|
23830
|
+
this.myHolds.delete(path);
|
|
23831
|
+
this.myHolds.set(moved2, fid);
|
|
23832
|
+
drop.push(path);
|
|
23833
|
+
add.push([moved2, fid]);
|
|
23834
|
+
}
|
|
23835
|
+
if (drop.length > 0 && this.peers.size > 0) this.sendHolds(add, drop);
|
|
23836
|
+
}
|
|
23837
|
+
sendHolds(add, drop) {
|
|
23838
|
+
const seq2 = ++this.seq;
|
|
23839
|
+
this.holdsSent++;
|
|
23840
|
+
const instance = this.instance === void 0 ? {} : { instance: this.instance };
|
|
23841
|
+
this.channel.postMessage({
|
|
23842
|
+
tab: this.tab,
|
|
23843
|
+
holds: { add: [...add], drop: [...drop], ...instance },
|
|
23844
|
+
seq: seq2
|
|
23845
|
+
});
|
|
23846
|
+
return seq2;
|
|
23847
|
+
}
|
|
23848
|
+
heldWithin(target) {
|
|
23849
|
+
const out = [];
|
|
23850
|
+
for (const [tab, held] of this.peerHolds) {
|
|
23851
|
+
for (const [path, fid] of held) {
|
|
23852
|
+
if (path !== target && !path.startsWith(`${target}/`)) continue;
|
|
23853
|
+
out.push({ tab, path, fid, instance: this.instances.get(tab) });
|
|
23854
|
+
}
|
|
23855
|
+
}
|
|
23856
|
+
return out;
|
|
23857
|
+
}
|
|
23858
|
+
ask(to, body, cap) {
|
|
23859
|
+
const seq2 = ++this.seq;
|
|
23860
|
+
this.channel.postMessage({ tab: this.tab, to, seq: seq2, ...body });
|
|
23861
|
+
return new Promise((resolve2) => {
|
|
23862
|
+
const done4 = () => {
|
|
23863
|
+
clearTimeout(timer);
|
|
23864
|
+
this.waiting.delete(seq2);
|
|
23865
|
+
resolve2(left.size === 0);
|
|
23866
|
+
};
|
|
23867
|
+
const left = /* @__PURE__ */ new Set([to]);
|
|
23868
|
+
const timer = setTimeout(done4, cap);
|
|
23869
|
+
this.waiting.set(seq2, { left, done: done4 });
|
|
23870
|
+
});
|
|
23871
|
+
}
|
|
23872
|
+
peerHeld(path) {
|
|
23873
|
+
const out = [];
|
|
23874
|
+
for (const [tab, held] of this.peerHolds)
|
|
23875
|
+
if (held.has(path)) out.push({ tab, fid: held.get(path) });
|
|
23876
|
+
return out;
|
|
23877
|
+
}
|
|
23878
|
+
peerHeldPaths() {
|
|
23879
|
+
const out = /* @__PURE__ */ new Set();
|
|
23880
|
+
for (const held of this.peerHolds.values()) for (const path of held.keys()) out.add(path);
|
|
23881
|
+
return [...out].sort();
|
|
23882
|
+
}
|
|
23883
|
+
holdCounts() {
|
|
23884
|
+
let peers = 0;
|
|
23885
|
+
for (const held of this.peerHolds.values()) peers += held.size;
|
|
23886
|
+
return { mine: this.myHolds.size, peers, sent: this.holdsSent };
|
|
23887
|
+
}
|
|
23586
23888
|
acked(seq2, from) {
|
|
23587
23889
|
const wait2 = this.waiting.get(seq2);
|
|
23588
23890
|
if (!wait2) return;
|
|
@@ -23597,11 +23899,14 @@ var RemoteWatch = class {
|
|
|
23597
23899
|
if (!this.locks) return;
|
|
23598
23900
|
const held = new Set(((await this.locks.query()).held ?? []).map((lock) => lock.name));
|
|
23599
23901
|
let left = false;
|
|
23600
|
-
for (const tab of /* @__PURE__ */ new Set([...this.others.keys(), ...this.peers])) {
|
|
23902
|
+
for (const tab of /* @__PURE__ */ new Set([...this.others.keys(), ...this.peers, ...this.peerHolds.keys()])) {
|
|
23601
23903
|
if (!held.has(this.lockOf(tab))) {
|
|
23602
23904
|
if (this.peers.delete(tab)) left = true;
|
|
23603
23905
|
this.others.delete(tab);
|
|
23906
|
+
this.peerHolds.delete(tab);
|
|
23907
|
+
this.keeps?.left(tab);
|
|
23604
23908
|
this.ackers.delete(tab);
|
|
23909
|
+
this.holders.delete(tab);
|
|
23605
23910
|
}
|
|
23606
23911
|
}
|
|
23607
23912
|
this.rebuild();
|
|
@@ -23649,8 +23954,9 @@ var RemoteWatch = class {
|
|
|
23649
23954
|
renamedTree(lock) {
|
|
23650
23955
|
this.channel.postMessage({ renamed: lock });
|
|
23651
23956
|
}
|
|
23652
|
-
acks(seq2, from) {
|
|
23957
|
+
acks(seq2, from, missed) {
|
|
23653
23958
|
if (from.size === 0) return Promise.resolve();
|
|
23959
|
+
const left = new Set(from);
|
|
23654
23960
|
return new Promise((resolve2) => {
|
|
23655
23961
|
const done4 = () => {
|
|
23656
23962
|
clearTimeout(timer);
|
|
@@ -23658,10 +23964,11 @@ var RemoteWatch = class {
|
|
|
23658
23964
|
resolve2();
|
|
23659
23965
|
};
|
|
23660
23966
|
const timer = setTimeout(() => {
|
|
23967
|
+
missed?.(left);
|
|
23661
23968
|
done4();
|
|
23662
23969
|
void this.prune();
|
|
23663
23970
|
}, ANNOUNCE_WAIT_MS);
|
|
23664
|
-
this.waiting.set(seq2, { left
|
|
23971
|
+
this.waiting.set(seq2, { left, done: done4 });
|
|
23665
23972
|
});
|
|
23666
23973
|
}
|
|
23667
23974
|
emit(change) {
|
|
@@ -25073,6 +25380,7 @@ function translate(err, path, op = "lookup", mapped = true) {
|
|
|
25073
25380
|
return error;
|
|
25074
25381
|
}
|
|
25075
25382
|
var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
|
|
25383
|
+
var FILE_ID_TRIES = 3;
|
|
25076
25384
|
function sidecar(meta) {
|
|
25077
25385
|
const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
|
|
25078
25386
|
const guarded3 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
|
|
@@ -25312,18 +25620,48 @@ var OpfsFs = class {
|
|
|
25312
25620
|
this.channel?.postMessage(paths);
|
|
25313
25621
|
}
|
|
25314
25622
|
async fileId(path) {
|
|
25623
|
+
for (let tries = 0; tries < FILE_ID_TRIES; tries++) {
|
|
25624
|
+
const fid = await this.assignId(path);
|
|
25625
|
+
if (fid !== void 0) return fid;
|
|
25626
|
+
}
|
|
25627
|
+
throw fsError("EAGAIN", `${path}: moved while its file id was assigned`);
|
|
25628
|
+
}
|
|
25629
|
+
bindId(path) {
|
|
25630
|
+
return this.assignId(path);
|
|
25631
|
+
}
|
|
25632
|
+
async assignId(path) {
|
|
25315
25633
|
const found = await this.locate(path, true);
|
|
25316
25634
|
const [entry] = await (this.meta.fresh ?? this.meta.get).call(this.meta, [found.path]);
|
|
25317
25635
|
if (entry?.fid !== void 0) return entry.fid;
|
|
25318
25636
|
const fid = crypto.randomUUID();
|
|
25637
|
+
let moved2 = false;
|
|
25638
|
+
await this.meta.update(found.path, (now) => {
|
|
25639
|
+
const vacatedSince = now?.vacated && (!entry?.vacated || now.ino !== entry.ino);
|
|
25640
|
+
if (vacatedSince || entry !== void 0 && now === void 0) {
|
|
25641
|
+
moved2 = true;
|
|
25642
|
+
return now;
|
|
25643
|
+
}
|
|
25644
|
+
return { ...occupied(now), path: found.path, fid: now?.fid ?? fid };
|
|
25645
|
+
});
|
|
25646
|
+
if (moved2) return void 0;
|
|
25319
25647
|
this.idWrites++;
|
|
25320
|
-
await this.
|
|
25321
|
-
|
|
25322
|
-
|
|
25323
|
-
|
|
25324
|
-
|
|
25325
|
-
const
|
|
25326
|
-
|
|
25648
|
+
const there = await this.locate(path, true).then(
|
|
25649
|
+
(at) => at.path === found.path,
|
|
25650
|
+
() => false
|
|
25651
|
+
);
|
|
25652
|
+
if (there) return (await this.meta.get([found.path]))[0]?.fid ?? fid;
|
|
25653
|
+
const gone = await this.locate(found.path, true).then(
|
|
25654
|
+
() => false,
|
|
25655
|
+
(err) => missing4(err)
|
|
25656
|
+
);
|
|
25657
|
+
if (gone) {
|
|
25658
|
+
await this.meta.update(found.path, (now) => {
|
|
25659
|
+
if (now?.fid !== fid || now.vacated) return now;
|
|
25660
|
+
const { fid: _, ...rest2 } = now;
|
|
25661
|
+
return rest2;
|
|
25662
|
+
});
|
|
25663
|
+
}
|
|
25664
|
+
return void 0;
|
|
25327
25665
|
}
|
|
25328
25666
|
heldUnder = () => [];
|
|
25329
25667
|
heldBy(held) {
|
|
@@ -25342,6 +25680,30 @@ var OpfsFs = class {
|
|
|
25342
25680
|
return this.remote?.listen(listener) ?? (() => {
|
|
25343
25681
|
});
|
|
25344
25682
|
}
|
|
25683
|
+
announceHolds(add, drop, wait2) {
|
|
25684
|
+
return this.remote?.hold(add, drop, wait2) ?? Promise.resolve();
|
|
25685
|
+
}
|
|
25686
|
+
holdCounts() {
|
|
25687
|
+
const remote = this.remote;
|
|
25688
|
+
return remote && { ...remote.holdCounts(), held: remote.peerHeldPaths() };
|
|
25689
|
+
}
|
|
25690
|
+
peerKeeps() {
|
|
25691
|
+
const remote = this.remote;
|
|
25692
|
+
if (!remote) return void 0;
|
|
25693
|
+
return {
|
|
25694
|
+
shared: () => this.shared(),
|
|
25695
|
+
heldWithin: (path) => remote.heldWithin(path),
|
|
25696
|
+
ask: (to, body, cap) => remote.ask(to, body, cap)
|
|
25697
|
+
};
|
|
25698
|
+
}
|
|
25699
|
+
servePeerKeeps(instance, handlers) {
|
|
25700
|
+
if (!this.remote) return;
|
|
25701
|
+
this.remote.instance = instance;
|
|
25702
|
+
this.remote.keeps = handlers;
|
|
25703
|
+
}
|
|
25704
|
+
rehold(from, to) {
|
|
25705
|
+
this.remote?.rehold(from, to);
|
|
25706
|
+
}
|
|
25345
25707
|
announceWatch(paths) {
|
|
25346
25708
|
return this.remote?.announce(paths) ?? Promise.resolve();
|
|
25347
25709
|
}
|
|
@@ -26286,7 +26648,7 @@ var OpfsFs = class {
|
|
|
26286
26648
|
}
|
|
26287
26649
|
}
|
|
26288
26650
|
async writeFile(path, content, owner) {
|
|
26289
|
-
const [file, existed, target] = await this.fileFor(path);
|
|
26651
|
+
const [file, existed, target] = await this.fileFor(path, owner?.existing !== true);
|
|
26290
26652
|
if (!existed) await this.stampNew(target, owner, false);
|
|
26291
26653
|
if (!existed && content.length === 0) return;
|
|
26292
26654
|
let writable2;
|
|
@@ -30034,7 +30396,12 @@ var Launcher = class {
|
|
|
30034
30396
|
identities = { asked: 0, timedOut: 0 };
|
|
30035
30397
|
nodes;
|
|
30036
30398
|
unlinked;
|
|
30037
|
-
held = new HeldSet(
|
|
30399
|
+
held = new HeldSet(
|
|
30400
|
+
(path) => this.released(path),
|
|
30401
|
+
(from, to) => {
|
|
30402
|
+
if (this.mounts.locate(to) === void 0) this.rootFs.rehold?.(from, to);
|
|
30403
|
+
}
|
|
30404
|
+
);
|
|
30038
30405
|
processMounts;
|
|
30039
30406
|
booting = new AbortController();
|
|
30040
30407
|
fstab = Promise.resolve([]);
|
|
@@ -30172,7 +30539,7 @@ var Launcher = class {
|
|
|
30172
30539
|
busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
|
|
30173
30540
|
owner: options.unlinkOwner,
|
|
30174
30541
|
marks: this.pathMarks,
|
|
30175
|
-
...this.
|
|
30542
|
+
...this.peerOptions(options.fs)
|
|
30176
30543
|
}
|
|
30177
30544
|
);
|
|
30178
30545
|
this.fs = this.processChain(fs, this.nodes, this.unlinked);
|
|
@@ -31006,7 +31373,7 @@ var Launcher = class {
|
|
|
31006
31373
|
const assign = this.mounts.locate(target.wasm) === void 0 ? this.rootFs.fileId : void 0;
|
|
31007
31374
|
return this.base.stat(target.wasm).then(
|
|
31008
31375
|
async (st) => {
|
|
31009
|
-
const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm);
|
|
31376
|
+
const fid = st.fid ?? await assign?.call(this.rootFs, target.wasm)?.catch(() => void 0);
|
|
31010
31377
|
if (fid === void 0) return exeIdOf(fileIdentity(st.ino, st.mtime.getTime()));
|
|
31011
31378
|
return { exeId: `fid:${fid}:${st.mtime.getTime()}` };
|
|
31012
31379
|
},
|
|
@@ -31252,7 +31619,8 @@ var Launcher = class {
|
|
|
31252
31619
|
cwdChecks: this.cwds.checks,
|
|
31253
31620
|
renameAcks: this.rootFs.renameWaits?.(),
|
|
31254
31621
|
staleFds: staleFdCount(),
|
|
31255
|
-
fileIds: this.rootFs.fileIdWrites?.() ?? 0
|
|
31622
|
+
fileIds: this.rootFs.fileIdWrites?.() ?? 0,
|
|
31623
|
+
holds: this.rootFs.holdCounts?.()
|
|
31256
31624
|
}),
|
|
31257
31625
|
sudo: (call, signal) => this.sudo(pid, call, signal),
|
|
31258
31626
|
crontab: (call) => this.crontab(pid, call),
|
|
@@ -31264,6 +31632,7 @@ var Launcher = class {
|
|
|
31264
31632
|
statfs: (path) => this.mounts.statfs(path),
|
|
31265
31633
|
renames: (since2, held) => this.renameLog.since(since2, held),
|
|
31266
31634
|
fileId: (path) => this.locks.shared?.() === true ? this.rootFileId(path) : void 0,
|
|
31635
|
+
announceHold: (path, fid, created4) => this.locks.shared?.() === true && this.mounts.locate(path) === void 0 ? this.rootFs.announceHolds?.([[path, fid]], [], !created4) : void 0,
|
|
31267
31636
|
renameSeq: (from, to) => this.renameLog.seqOf(from, to),
|
|
31268
31637
|
mounts: () => this.mounts.list(),
|
|
31269
31638
|
mount: (call, signal) => this.processMount(pid, call, signal),
|
|
@@ -31705,6 +32074,8 @@ var Launcher = class {
|
|
|
31705
32074
|
const at = normalizePath(path);
|
|
31706
32075
|
this.unlinked.closed(at);
|
|
31707
32076
|
this.writers?.unlinked.closed(at);
|
|
32077
|
+
if (![...this.held].some((holds2) => holds2.holding(at)))
|
|
32078
|
+
void this.rootFs.announceHolds?.([], [at], false);
|
|
31708
32079
|
const open = [...this.openFiles].some((nodes) => nodes.writes(at)) || heldUnder(this.held, at);
|
|
31709
32080
|
if (!open) void this.mounts.commit(at).catch(() => void 0);
|
|
31710
32081
|
}
|
|
@@ -32164,8 +32535,82 @@ var Launcher = class {
|
|
|
32164
32535
|
this.writers ??= this.writerStack();
|
|
32165
32536
|
return this.writers;
|
|
32166
32537
|
}
|
|
32538
|
+
peerGates = /* @__PURE__ */ new Map();
|
|
32539
|
+
peerOptions(fs) {
|
|
32540
|
+
const peers = fs.peerKeeps?.();
|
|
32541
|
+
queueMicrotask(() => fs.servePeerKeeps?.(this.unlinked.id, this.peerKept()));
|
|
32542
|
+
return {
|
|
32543
|
+
...peers ? { peers } : {},
|
|
32544
|
+
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
32545
|
+
};
|
|
32546
|
+
}
|
|
32547
|
+
peerKept() {
|
|
32548
|
+
const gates = this.peerGates;
|
|
32549
|
+
const gone = /* @__PURE__ */ new Set();
|
|
32550
|
+
const instance = this.unlinked.id;
|
|
32551
|
+
const ours = (hidden) => hidden.startsWith(`/${UNLINKED_DIR}/${instance}.p`) && /^[0-9a-f]+$/.test(hidden.slice(`/${UNLINKED_DIR}/${instance}.p`.length));
|
|
32552
|
+
const open = (tab, paths) => {
|
|
32553
|
+
for (const [key, release] of [...gates]) {
|
|
32554
|
+
const [owner, path] = key.split("\0");
|
|
32555
|
+
if (owner !== tab || paths && !paths.includes(path)) continue;
|
|
32556
|
+
gates.delete(key);
|
|
32557
|
+
release();
|
|
32558
|
+
}
|
|
32559
|
+
};
|
|
32560
|
+
return {
|
|
32561
|
+
keeping: async (tab, paths) => {
|
|
32562
|
+
for (const path of paths) {
|
|
32563
|
+
if (gone.has(tab)) return;
|
|
32564
|
+
const release = await this.unlinked.gate.acquire(path);
|
|
32565
|
+
const key = `${tab}\0${path}`;
|
|
32566
|
+
let done4 = false;
|
|
32567
|
+
const once2 = () => {
|
|
32568
|
+
if (done4) return;
|
|
32569
|
+
done4 = true;
|
|
32570
|
+
clearTimeout(timer);
|
|
32571
|
+
release();
|
|
32572
|
+
};
|
|
32573
|
+
const timer = setTimeout(() => {
|
|
32574
|
+
if (gates.get(key) === once2) gates.delete(key);
|
|
32575
|
+
once2();
|
|
32576
|
+
}, PEER_GATE_MS);
|
|
32577
|
+
if (gone.has(tab)) once2();
|
|
32578
|
+
else gates.set(key, once2);
|
|
32579
|
+
}
|
|
32580
|
+
},
|
|
32581
|
+
kept: async (tab, items, paths) => {
|
|
32582
|
+
for (const { path, hidden, fid, undo } of items) {
|
|
32583
|
+
if (undo !== void 0) this.unadopt(path, undo, fid);
|
|
32584
|
+
else this.adoptKept(path, hidden, fid, ours);
|
|
32585
|
+
}
|
|
32586
|
+
open(tab, paths);
|
|
32587
|
+
},
|
|
32588
|
+
left: (tab) => {
|
|
32589
|
+
gone.add(tab);
|
|
32590
|
+
open(tab);
|
|
32591
|
+
}
|
|
32592
|
+
};
|
|
32593
|
+
}
|
|
32594
|
+
unadopt(path, undo, fid) {
|
|
32595
|
+
for (const holds2 of this.held) holds2.unkeepAt(path, undo);
|
|
32596
|
+
void this.rootFs.announceHolds?.([[path, fid]], [], false);
|
|
32597
|
+
}
|
|
32598
|
+
adoptKept(path, hidden, fid, ours) {
|
|
32599
|
+
if (hidden === void 0 || !ours(hidden)) {
|
|
32600
|
+
this.held.doubt([path]);
|
|
32601
|
+
return;
|
|
32602
|
+
}
|
|
32603
|
+
const bound3 = [...this.held].map(
|
|
32604
|
+
(holds2) => [holds2, [...holds2.boundAt(path, fid), ...holds2.boundAt(hidden, fid)]]
|
|
32605
|
+
);
|
|
32606
|
+
const keeping = bound3.filter(([, ids]) => ids.length > 0);
|
|
32607
|
+
const file = this.unlinked.adopt(hidden, Math.max(keeping.length, 1));
|
|
32608
|
+
if (keeping.length === 0) file.release();
|
|
32609
|
+
for (const [holds2, ids] of keeping) holds2.keepBound(ids, path, file);
|
|
32610
|
+
void this.rootFs.announceHolds?.([], [path, hidden], false);
|
|
32611
|
+
}
|
|
32167
32612
|
rootFileId(path) {
|
|
32168
|
-
return this.mounts.locate(path) === void 0 ? this.rootFs.
|
|
32613
|
+
return this.mounts.locate(path) === void 0 ? this.rootFs.bindId?.(path) : void 0;
|
|
32169
32614
|
}
|
|
32170
32615
|
flushingPeers(fs) {
|
|
32171
32616
|
const peers = fs.peers?.();
|
|
@@ -32211,6 +32656,7 @@ var Launcher = class {
|
|
|
32211
32656
|
busy: (path) => [...this.openFiles].some((open) => open.holds(path)),
|
|
32212
32657
|
owner: this.unlinkOwnerOption,
|
|
32213
32658
|
marks: this.pathMarks,
|
|
32659
|
+
peers: this.rootFs.peerKeeps?.(),
|
|
32214
32660
|
...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
|
|
32215
32661
|
}
|
|
32216
32662
|
);
|