@ai-ecoverse/slicc-kernel 1.161.0 → 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/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.161.0" : "0.0.0-dev";
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 replace(target, async (gated, sealed) => {
4760
- const pinned = await keeper.pin(target, gated, sealed);
4761
- if (pinned.moved.has(target)) pinned.commit();
4762
- else await keeper.settle(pinned, () => fs.rm(path, options));
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.marks.around(
4770
- [source],
4771
- () => replace(target, async (gated, sealed) => {
4772
- const pinned = await keeper.pin(target, gated, sealed);
4773
- await keeper.settle(pinned, () => fs.rename(from, to));
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, { recursive: true }).catch(() => void 0);
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);
@@ -9022,12 +9151,25 @@ var HeldPaths = class extends Map {
9022
9151
  for (const file of this.unlinked.values()) if (file.hidden === hidden) return true;
9023
9152
  return false;
9024
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
+ }
9025
9164
  keep(path, file) {
9026
9165
  const fresh3 = this.handlesAt(path).filter((id2) => !this.keptHandles.has(id2));
9027
9166
  for (const id2 of fresh3) this.keptHandles.set(id2, file);
9028
9167
  this.uses.set(file, Math.max(fresh3.length, 1));
9029
9168
  this.unlinked.set(path, file);
9030
9169
  }
9170
+ unkeepAt(path, hidden) {
9171
+ for (const file of [...this.uses.keys()]) if (file.hidden === hidden) this.unkeep(path, file);
9172
+ }
9031
9173
  unkeep(path, file) {
9032
9174
  for (const [id2, kept] of this.keptHandles) if (kept === file) this.keptHandles.delete(id2);
9033
9175
  this.uses.delete(file);
@@ -9596,12 +9738,12 @@ var MountFs = class _MountFs {
9596
9738
  const target = await found.mount.conn.call({ op: "readlink", path: found.rel });
9597
9739
  return this.follow(this.base.resolvePath(parentOf5(real), target), hops + 1, create);
9598
9740
  }
9599
- async open(mount, rel2, write, owner) {
9741
+ async open(mount, rel2, write, owner, create = write) {
9600
9742
  return await mount.conn.call({
9601
9743
  op: "open",
9602
9744
  path: rel2,
9603
9745
  write,
9604
- create: write,
9746
+ create,
9605
9747
  truncate: write,
9606
9748
  exclusive: false,
9607
9749
  ...owner
@@ -9698,10 +9840,10 @@ var MountFs = class _MountFs {
9698
9840
  `${real} would be larger than this mount's maxfile (${mount.maxFile})`
9699
9841
  );
9700
9842
  }
9701
- const made2 = await this.driverOwner(found, real, owner, false);
9843
+ const made2 = owner?.existing ? void 0 : await this.driverOwner(found, real, owner, false);
9702
9844
  for (let attempt = 1; ; attempt++) {
9703
9845
  try {
9704
- return await this.writeWhole(mount, real, rel2, bytes, made2);
9846
+ return await this.writeWhole(mount, real, rel2, bytes, made2, owner?.existing !== true);
9705
9847
  } catch (err) {
9706
9848
  if (err.code !== "ESTALE" || attempt >= STALE_RETRIES) throw err;
9707
9849
  }
@@ -9719,10 +9861,10 @@ var MountFs = class _MountFs {
9719
9861
  this.table.forget(mount, real);
9720
9862
  }
9721
9863
  }
9722
- async writeWhole(mount, real, rel2, bytes, owner) {
9864
+ async writeWhole(mount, real, rel2, bytes, owner, create = true) {
9723
9865
  await this.table.commit(real);
9724
9866
  this.table.forget(mount, real);
9725
- const fh = await this.open(mount, rel2, true, owner);
9867
+ const fh = await this.open(mount, rel2, true, owner, create);
9726
9868
  const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
9727
9869
  try {
9728
9870
  for (let offset = 0; offset < bytes.length; offset += chunk2) {
@@ -10459,6 +10601,11 @@ function ownerFor(entry, mode) {
10459
10601
  if (!cred) return void 0;
10460
10602
  return ownerOf(cred, mode === void 0 ? void 0 : mode & ~(entry.umask?.() ?? 18));
10461
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
+ }
10462
10609
  function plainDirMode(entry, mode) {
10463
10610
  if (entry.cred || mode === void 0) return void 0;
10464
10611
  const wanted = mode & 4095 & ~(entry.umask?.() ?? 18);
@@ -10529,7 +10676,7 @@ async function run(entry, path, req) {
10529
10676
  case "read":
10530
10677
  return { ok: true, kind: "bytes", bytes: await readSource(entry, path) };
10531
10678
  case "write":
10532
- await fs.writeFile(path, req.body ?? new Uint8Array(0), ownerFor(entry, 438));
10679
+ await fs.writeFile(path, req.body ?? new Uint8Array(0), writeOwner(entry, req));
10533
10680
  return done;
10534
10681
  case "exists":
10535
10682
  return json2(await fs.exists(path));
@@ -23425,6 +23572,9 @@ var RemoteWatch = class {
23425
23572
  holdsSent = 0;
23426
23573
  silent = /* @__PURE__ */ new Set();
23427
23574
  holders = /* @__PURE__ */ new Set();
23575
+ instances = /* @__PURE__ */ new Map();
23576
+ instance;
23577
+ keeps;
23428
23578
  listeners = /* @__PURE__ */ new Set();
23429
23579
  waiting = /* @__PURE__ */ new Map();
23430
23580
  locks;
@@ -23544,8 +23694,9 @@ var RemoteWatch = class {
23544
23694
  this.met?.();
23545
23695
  }
23546
23696
  if (data.tab !== void 0 && data.paths !== void 0) this.announced(data);
23547
- if (data.tab !== void 0 && data.holds !== void 0) this.heldBy(data);
23697
+ this.holdsHeard(data);
23548
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);
23549
23700
  this.acked(data.ack, data.from);
23550
23701
  }
23551
23702
  if (data.changed !== void 0) this.emit({ paths: data.changed, trees: [], content: true });
@@ -23572,6 +23723,7 @@ var RemoteWatch = class {
23572
23723
  this.mourned.delete(peer);
23573
23724
  this.others.delete(peer);
23574
23725
  this.peerHolds.delete(peer);
23726
+ this.keeps?.left(peer);
23575
23727
  this.peers.delete(peer);
23576
23728
  this.ackers.delete(peer);
23577
23729
  this.holders.delete(peer);
@@ -23612,9 +23764,38 @@ var RemoteWatch = class {
23612
23764
  this.rebuild();
23613
23765
  this.channel.postMessage({ ack: seq2, from: this.tab, to: from });
23614
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
+ }
23615
23795
  heldBy({ tab, holds: holds2, seq: seq2 }) {
23616
23796
  const from = tab;
23617
23797
  const { add, drop } = holds2;
23798
+ if (holds2?.instance !== void 0) this.instances.set(from, holds2.instance);
23618
23799
  const held = this.peerHolds.get(from) ?? /* @__PURE__ */ new Map();
23619
23800
  for (const path of drop) held.delete(path);
23620
23801
  for (const [path, fid] of add) held.set(path, fid);
@@ -23656,9 +23837,38 @@ var RemoteWatch = class {
23656
23837
  sendHolds(add, drop) {
23657
23838
  const seq2 = ++this.seq;
23658
23839
  this.holdsSent++;
23659
- this.channel.postMessage({ tab: this.tab, holds: { add: [...add], drop: [...drop] }, seq: seq2 });
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
+ });
23660
23846
  return seq2;
23661
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
+ }
23662
23872
  peerHeld(path) {
23663
23873
  const out = [];
23664
23874
  for (const [tab, held] of this.peerHolds)
@@ -23694,6 +23904,7 @@ var RemoteWatch = class {
23694
23904
  if (this.peers.delete(tab)) left = true;
23695
23905
  this.others.delete(tab);
23696
23906
  this.peerHolds.delete(tab);
23907
+ this.keeps?.left(tab);
23697
23908
  this.ackers.delete(tab);
23698
23909
  this.holders.delete(tab);
23699
23910
  }
@@ -25169,6 +25380,7 @@ function translate(err, path, op = "lookup", mapped = true) {
25169
25380
  return error;
25170
25381
  }
25171
25382
  var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
25383
+ var FILE_ID_TRIES = 3;
25172
25384
  function sidecar(meta) {
25173
25385
  const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
25174
25386
  const guarded3 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
@@ -25408,18 +25620,48 @@ var OpfsFs = class {
25408
25620
  this.channel?.postMessage(paths);
25409
25621
  }
25410
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) {
25411
25633
  const found = await this.locate(path, true);
25412
25634
  const [entry] = await (this.meta.fresh ?? this.meta.get).call(this.meta, [found.path]);
25413
25635
  if (entry?.fid !== void 0) return entry.fid;
25414
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;
25415
25647
  this.idWrites++;
25416
- await this.meta.update(found.path, (now) => ({
25417
- ...occupied(now),
25418
- path: found.path,
25419
- fid: now?.fid ?? fid
25420
- }));
25421
- const [stored] = await this.meta.get([found.path]);
25422
- return stored?.fid ?? fid;
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;
25423
25665
  }
25424
25666
  heldUnder = () => [];
25425
25667
  heldBy(held) {
@@ -25445,6 +25687,20 @@ var OpfsFs = class {
25445
25687
  const remote = this.remote;
25446
25688
  return remote && { ...remote.holdCounts(), held: remote.peerHeldPaths() };
25447
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
+ }
25448
25704
  rehold(from, to) {
25449
25705
  this.remote?.rehold(from, to);
25450
25706
  }
@@ -26392,7 +26648,7 @@ var OpfsFs = class {
26392
26648
  }
26393
26649
  }
26394
26650
  async writeFile(path, content, owner) {
26395
- const [file, existed, target] = await this.fileFor(path);
26651
+ const [file, existed, target] = await this.fileFor(path, owner?.existing !== true);
26396
26652
  if (!existed) await this.stampNew(target, owner, false);
26397
26653
  if (!existed && content.length === 0) return;
26398
26654
  let writable2;
@@ -30283,7 +30539,7 @@ var Launcher = class {
30283
30539
  busy: (path) => [...this.openFiles].some((nodes) => nodes.holds(path)),
30284
30540
  owner: options.unlinkOwner,
30285
30541
  marks: this.pathMarks,
30286
- ...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
30542
+ ...this.peerOptions(options.fs)
30287
30543
  }
30288
30544
  );
30289
30545
  this.fs = this.processChain(fs, this.nodes, this.unlinked);
@@ -31117,7 +31373,7 @@ var Launcher = class {
31117
31373
  const assign = this.mounts.locate(target.wasm) === void 0 ? this.rootFs.fileId : void 0;
31118
31374
  return this.base.stat(target.wasm).then(
31119
31375
  async (st) => {
31120
- 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);
31121
31377
  if (fid === void 0) return exeIdOf(fileIdentity(st.ino, st.mtime.getTime()));
31122
31378
  return { exeId: `fid:${fid}:${st.mtime.getTime()}` };
31123
31379
  },
@@ -32279,8 +32535,82 @@ var Launcher = class {
32279
32535
  this.writers ??= this.writerStack();
32280
32536
  return this.writers;
32281
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
+ }
32282
32612
  rootFileId(path) {
32283
- return this.mounts.locate(path) === void 0 ? this.rootFs.fileId?.(path) : void 0;
32613
+ return this.mounts.locate(path) === void 0 ? this.rootFs.bindId?.(path) : void 0;
32284
32614
  }
32285
32615
  flushingPeers(fs) {
32286
32616
  const peers = fs.peers?.();
@@ -32326,6 +32656,7 @@ var Launcher = class {
32326
32656
  busy: (path) => [...this.openFiles].some((open) => open.holds(path)),
32327
32657
  owner: this.unlinkOwnerOption,
32328
32658
  marks: this.pathMarks,
32659
+ peers: this.rootFs.peerKeeps?.(),
32329
32660
  ...this.owners ? { dirOwner: { uid: 0, gid: 0, mode: 448 } } : {}
32330
32661
  }
32331
32662
  );