@ai-ecoverse/slicc-kernel 1.34.1 → 1.36.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.
@@ -448,11 +448,41 @@ var OpfsFs = class {
448
448
  const fallback = directory || EXECUTABLE.test(path) ? 493 : 420;
449
449
  return (directory ? S_IFDIR : S_IFREG) | (meta?.mode ?? fallback);
450
450
  }
451
+ dirTimes = /* @__PURE__ */ new Map();
452
+ dirDirty = /* @__PURE__ */ new Set();
453
+ dirFlush;
454
+ touch(...dirs) {
455
+ const now = Date.now();
456
+ for (const dir of dirs) {
457
+ this.dirTimes.set(dir, now);
458
+ this.dirDirty.add(dir);
459
+ }
460
+ this.dirFlush ??= Promise.resolve().then(() => this.flushDirTimes()).catch(() => void 0).finally(() => {
461
+ this.dirFlush = void 0;
462
+ });
463
+ }
464
+ async flushDirTimes() {
465
+ for (const path of this.dirDirty) {
466
+ this.dirDirty.delete(path);
467
+ const ms = this.dirTimes.get(path);
468
+ if (ms === void 0) continue;
469
+ await this.meta.update(path, (entry) => ({
470
+ ...entry,
471
+ path,
472
+ mtimeMs: ms,
473
+ mtimeFor: 0,
474
+ ctimeMs: ms
475
+ }));
476
+ if (this.dirTimes.get(path) === ms) this.dirTimes.delete(path);
477
+ }
478
+ }
451
479
  async statOf(path, handle, meta) {
452
480
  const directory = handle.kind === "directory";
453
481
  const file = directory ? void 0 : await handle.getFile();
454
482
  const modified = file?.lastModified ?? 0;
455
- const mtime = meta?.mtimeMs !== void 0 && meta.mtimeFor === modified ? meta.mtimeMs : modified;
483
+ const touched = directory ? this.dirTimes.get(path) : void 0;
484
+ const kept = meta?.mtimeMs !== void 0 && meta.mtimeFor === modified ? meta.mtimeMs : modified;
485
+ const mtime = touched ?? kept;
456
486
  return {
457
487
  isFile: !directory,
458
488
  isDirectory: directory,
@@ -461,7 +491,7 @@ var OpfsFs = class {
461
491
  mode: this.mode(path, directory, meta),
462
492
  mtime: new Date(mtime),
463
493
  atime: new Date(meta?.atimeMs ?? mtime),
464
- ctime: new Date(Math.max(meta?.ctimeMs ?? mtime, modified)),
494
+ ctime: new Date(Math.max(touched ?? meta?.ctimeMs ?? mtime, modified)),
465
495
  ino: meta?.ino ?? inodeOf(path),
466
496
  ...this.ranged && !directory ? { ranged: true } : {}
467
497
  };
@@ -538,6 +568,19 @@ var OpfsFs = class {
538
568
  const handle = await this.existing(path);
539
569
  await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
540
570
  }
571
+ async writeSession(path, truncate) {
572
+ const handle = (await this.fileFor(path))[0];
573
+ const writable = await this.retrying(
574
+ path,
575
+ () => handle.createWritable({ keepExistingData: !truncate }),
576
+ true
577
+ );
578
+ return {
579
+ write: (at, bytes) => writable.write({ type: "write", position: at, data: bytes }),
580
+ truncate: (size) => writable.truncate(size),
581
+ close: () => writable.close()
582
+ };
583
+ }
541
584
  async readFile(path) {
542
585
  return new TextDecoder().decode(await this.readFileBuffer(path));
543
586
  }
@@ -555,10 +598,12 @@ var OpfsFs = class {
555
598
  const [target, existed] = await this.writeTarget(path);
556
599
  const [dir, name2] = await this.parent(target);
557
600
  try {
558
- return [
559
- await this.inDir(dir, (parent) => parent.getFileHandle(name2, { create: true })),
560
- existed
561
- ];
601
+ const handle = await this.inDir(
602
+ dir,
603
+ (parent) => parent.getFileHandle(name2, { create: true })
604
+ );
605
+ if (!existed) this.touch(parentOf2(target));
606
+ return [handle, existed];
562
607
  } catch (err) {
563
608
  const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
564
609
  throw code ? fsError(code, path) : translate(err, path);
@@ -616,6 +661,7 @@ var OpfsFs = class {
616
661
  const parent = await this.locate(parentOf2(path), true);
617
662
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
618
663
  await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
664
+ this.touch(parent.path);
619
665
  }
620
666
  async rm(path, options = {}) {
621
667
  let found;
@@ -639,6 +685,7 @@ var OpfsFs = class {
639
685
  this.changed(found.path);
640
686
  }
641
687
  await this.meta.remove([found.path]);
688
+ this.touch(parentOf2(found.path));
642
689
  }
643
690
  async rename(from, to) {
644
691
  if (from === to) return;
@@ -670,6 +717,7 @@ var OpfsFs = class {
670
717
  if (source.link) {
671
718
  if (target?.handle) await this.rm(destination);
672
719
  await this.meta.move(source.path, destination, carry);
720
+ this.touch(parentOf2(source.path), parent.path);
673
721
  return;
674
722
  }
675
723
  const dir = parent.handle;
@@ -691,6 +739,7 @@ var OpfsFs = class {
691
739
  }
692
740
  if (copied) await this.restamp(destination, timed);
693
741
  this.changed(source.path, destination);
742
+ this.touch(parentOf2(source.path), parent.path);
694
743
  }
695
744
  async modified(path) {
696
745
  const handle = await this.handle(path);
@@ -754,6 +803,10 @@ var OpfsFs = class {
754
803
  mtimeMs: now,
755
804
  ctimeMs: now
756
805
  }));
806
+ this.touch(parent.path);
807
+ }
808
+ async realpath(path, follow) {
809
+ return (await this.locate(path, follow)).path;
757
810
  }
758
811
  async readlink(path) {
759
812
  const found = await this.locate(path, false);
@@ -791,6 +844,7 @@ var OpfsFs = class {
791
844
  }
792
845
  const handle = found.handle;
793
846
  const file = handle.kind === "file" ? await handle.getFile() : void 0;
847
+ this.dirTimes.delete(found.path);
794
848
  const ctimeMs = Date.now();
795
849
  await this.meta.update(found.path, (entry) => ({
796
850
  ...entry,
@@ -4787,6 +4841,7 @@ function withCommandDirs(fs, names) {
4787
4841
  rename: (from, to) => fs.rename(from, to),
4788
4842
  symlink: (target, path) => fs.symlink(target, path),
4789
4843
  readlink: (path) => fs.readlink(path),
4844
+ ...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
4790
4845
  chmod: (path, mode) => fs.chmod(path, mode),
4791
4846
  utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
4792
4847
  lutimes: (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
@@ -5183,18 +5238,36 @@ function parseSize(text) {
5183
5238
  return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
5184
5239
  }
5185
5240
  var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
5241
+ var HeldSet = class extends Set {
5242
+ idle;
5243
+ constructor(idle) {
5244
+ super();
5245
+ this.idle = idle;
5246
+ }
5247
+ add(holds) {
5248
+ holds.onIdle = this.idle;
5249
+ return super.add(holds);
5250
+ }
5251
+ delete(holds) {
5252
+ const removed = super.delete(holds);
5253
+ for (const path of holds.keys()) this.idle(path);
5254
+ return removed;
5255
+ }
5256
+ };
5186
5257
  function heldUnder(held, target) {
5187
5258
  for (const paths of held) for (const path of paths.keys()) if (within3(path, target)) return true;
5188
5259
  return false;
5189
5260
  }
5190
5261
  var HeldPaths = class extends Map {
5191
5262
  revoked = /* @__PURE__ */ new Set();
5263
+ onIdle;
5192
5264
  hold(path, on) {
5193
5265
  const count = (this.get(path) ?? 0) + (on ? 1 : -1);
5194
5266
  if (count > 0) this.set(path, count);
5195
5267
  else {
5196
5268
  this.delete(path);
5197
5269
  this.revoked.delete(path);
5270
+ this.onIdle?.(path);
5198
5271
  }
5199
5272
  }
5200
5273
  revoke(prefix) {
@@ -5262,6 +5335,8 @@ function spliced(bytes, offset, part) {
5262
5335
  }
5263
5336
  var MountTable = class {
5264
5337
  mounts = /* @__PURE__ */ new Map();
5338
+ sessions = /* @__PURE__ */ new Map();
5339
+ committing = /* @__PURE__ */ new Map();
5265
5340
  devices = 0;
5266
5341
  pending = /* @__PURE__ */ new Set();
5267
5342
  notes = /* @__PURE__ */ new Map();
@@ -5386,8 +5461,69 @@ var MountTable = class {
5386
5461
  if (!detach && this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
5387
5462
  this.notes.delete(at);
5388
5463
  this.mounts.delete(at);
5389
- mount.conn.close();
5390
- mount.dispose();
5464
+ const waits = [...this.sessions].filter(([, s]) => s.mount === mount).map(([real]) => this.commit(real));
5465
+ for (const c of this.committing.values()) if (c.mount === mount) waits.push(c.done);
5466
+ const close = () => {
5467
+ mount.conn.close();
5468
+ mount.dispose();
5469
+ };
5470
+ if (waits.length === 0) close();
5471
+ else void Promise.allSettled(waits).then(close);
5472
+ }
5473
+ session(mount, real, rel2) {
5474
+ let session = this.sessions.get(real);
5475
+ if (!session) {
5476
+ const open = () => mount.conn.call({
5477
+ op: "open",
5478
+ path: rel2,
5479
+ write: true,
5480
+ create: true,
5481
+ truncate: false,
5482
+ exclusive: false
5483
+ });
5484
+ const before = this.committing.get(real)?.done ?? Promise.resolve();
5485
+ const fh = before.then(open, open);
5486
+ session = { mount, fh, extents: [], truncated: false };
5487
+ this.sessions.set(real, session);
5488
+ fh.catch(() => this.sessions.delete(real));
5489
+ }
5490
+ return session;
5491
+ }
5492
+ writing(real) {
5493
+ return this.sessions.get(real);
5494
+ }
5495
+ async commit(real) {
5496
+ const session = this.sessions.get(real);
5497
+ if (!session) return this.committing.get(real)?.done;
5498
+ this.sessions.delete(real);
5499
+ const done4 = (async () => {
5500
+ try {
5501
+ await session.mount.conn.call({ op: "release", fh: await session.fh });
5502
+ } finally {
5503
+ this.forget(session.mount, real);
5504
+ }
5505
+ })();
5506
+ const entry = { mount: session.mount, done: done4 };
5507
+ this.committing.set(real, entry);
5508
+ const settle3 = () => {
5509
+ if (this.committing.get(real) === entry) this.committing.delete(real);
5510
+ };
5511
+ done4.then(settle3, settle3);
5512
+ return done4;
5513
+ }
5514
+ async commitUnder(prefix) {
5515
+ const at = normalizePath(prefix);
5516
+ await Promise.all(
5517
+ [...this.sessions.keys()].filter((p) => within3(p, at)).map((p) => this.commit(p))
5518
+ );
5519
+ }
5520
+ async commitOverlapping(real, offset, length2) {
5521
+ const session = this.sessions.get(real);
5522
+ if (!session) return;
5523
+ const end = offset + length2;
5524
+ if (session.truncated || session.extents.some(([a, b]) => a < end && offset < b)) {
5525
+ await this.commit(real);
5526
+ }
5391
5527
  }
5392
5528
  crashed(mount, error) {
5393
5529
  mount.state = "failed";
@@ -5446,6 +5582,13 @@ var MountTable = class {
5446
5582
  return new MountFs(this, base);
5447
5583
  }
5448
5584
  };
5585
+ function noteExtent(extents, from, to) {
5586
+ const last = extents.at(-1);
5587
+ if (last && from <= last[1] && to >= last[0]) {
5588
+ last[0] = Math.min(last[0], from);
5589
+ last[1] = Math.max(last[1], to);
5590
+ } else extents.push([from, to]);
5591
+ }
5449
5592
  var MountFs = class {
5450
5593
  table;
5451
5594
  base;
@@ -5505,6 +5648,7 @@ var MountFs = class {
5505
5648
  }
5506
5649
  }
5507
5650
  async readWhole(mount, real, rel2) {
5651
+ await this.table.commit(real);
5508
5652
  const attr = await this.table.getattr(mount, real, rel2);
5509
5653
  if (attr.kind === "directory") throw errnoError("EISDIR", real);
5510
5654
  if (mount.maxFile > 0 && attr.size > mount.maxFile) {
@@ -5554,6 +5698,7 @@ var MountFs = class {
5554
5698
  }
5555
5699
  }
5556
5700
  async writeWhole(mount, real, rel2, bytes) {
5701
+ await this.table.commit(real);
5557
5702
  this.table.forget(mount, real);
5558
5703
  const fh = await this.open(mount, rel2, true);
5559
5704
  const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
@@ -5576,6 +5721,7 @@ var MountFs = class {
5576
5721
  if ((await this.table.getattr(mount, real, rel2)).kind === "directory") {
5577
5722
  throw errnoError("EISDIR", real);
5578
5723
  }
5724
+ await this.table.commitOverlapping(real, offset, length2);
5579
5725
  const fh = await mount.conn.call({
5580
5726
  op: "open",
5581
5727
  path: rel2,
@@ -5629,6 +5775,14 @@ var MountFs = class {
5629
5775
  this.writable(mount, real);
5630
5776
  this.fits(mount, real, offset + bytes.length);
5631
5777
  this.table.forget(mount, real);
5778
+ if (mount.caps.sessions) {
5779
+ const session = this.table.session(mount, real, rel2);
5780
+ const end = offset + bytes.length;
5781
+ await this.writeRuns(mount, await session.fh, offset, bytes, transfer);
5782
+ noteExtent(session.extents, offset, end);
5783
+ this.table.forget(mount, real);
5784
+ return;
5785
+ }
5632
5786
  const fh = await mount.conn.call({
5633
5787
  op: "open",
5634
5788
  path: rel2,
@@ -5637,17 +5791,20 @@ var MountFs = class {
5637
5791
  truncate: false,
5638
5792
  exclusive: false
5639
5793
  });
5640
- const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
5641
5794
  try {
5642
- for (let at = 0; at < bytes.length; at += chunk2) {
5643
- const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
5644
- await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
5645
- }
5795
+ await this.writeRuns(mount, fh, offset, bytes, transfer);
5646
5796
  } finally {
5647
5797
  await mount.conn.call({ op: "release", fh });
5648
5798
  this.table.forget(mount, real);
5649
5799
  }
5650
5800
  }
5801
+ async writeRuns(mount, fh, offset, bytes, transfer) {
5802
+ const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
5803
+ for (let at = 0; at < bytes.length; at += chunk2) {
5804
+ const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
5805
+ await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
5806
+ }
5807
+ }
5651
5808
  async truncate(path, size) {
5652
5809
  const real = await this.follow(path);
5653
5810
  const found = this.at(real);
@@ -5659,6 +5816,8 @@ var MountFs = class {
5659
5816
  this.writable(mount, real);
5660
5817
  this.fits(mount, real, size);
5661
5818
  this.table.forget(mount, real);
5819
+ const session = this.table.writing(real);
5820
+ if (session) session.truncated = true;
5662
5821
  try {
5663
5822
  await mount.conn.call({ op: "setattr", path: rel2, size });
5664
5823
  } finally {
@@ -5680,7 +5839,16 @@ var MountFs = class {
5680
5839
  const found = this.at(path);
5681
5840
  if (!found) return this.base.lstat(path);
5682
5841
  const real = normalizePath(path);
5683
- return statOf(await this.table.getattr(found.mount, real, found.rel), real, found.mount);
5842
+ if (this.table.writing(real)?.truncated) await this.table.commit(real);
5843
+ return this.statWriting(
5844
+ await this.table.getattr(found.mount, real, found.rel),
5845
+ real,
5846
+ found.mount
5847
+ );
5848
+ }
5849
+ statWriting(attr, real, mount) {
5850
+ const ends = (this.table.writing(real)?.extents ?? []).map(([, end]) => end);
5851
+ return statOf({ ...attr, size: Math.max(attr.size, ...ends) }, real, mount);
5684
5852
  }
5685
5853
  async stat(path) {
5686
5854
  const real = await this.follow(path);
@@ -5707,7 +5875,7 @@ var MountFs = class {
5707
5875
  list.map(async (entry) => {
5708
5876
  const child = normalizePath(`${real}/${entry.name}`);
5709
5877
  if (entry.attr && mount.caps.listingStats) {
5710
- return [entry.name, statOf(entry.attr, child, mount)];
5878
+ return [entry.name, this.statWriting(entry.attr, child, mount)];
5711
5879
  }
5712
5880
  return [entry.name, await this.lstat(child).catch(() => null)];
5713
5881
  })
@@ -5743,6 +5911,7 @@ var MountFs = class {
5743
5911
  const real = normalizePath(path);
5744
5912
  if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
5745
5913
  this.writable(mount, real);
5914
+ await this.table.commitUnder(real);
5746
5915
  let attr;
5747
5916
  try {
5748
5917
  attr = await this.table.getattr(mount, real, rel2);
@@ -5766,6 +5935,8 @@ var MountFs = class {
5766
5935
  if (!a && !b) return this.base.rename(from, to);
5767
5936
  if (!a || !b || a.mount !== b.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
5768
5937
  this.writable(a.mount, normalizePath(to));
5938
+ await this.table.commitUnder(from);
5939
+ await this.table.commitUnder(to);
5769
5940
  this.table.forget(a.mount, normalizePath(from));
5770
5941
  this.table.forget(a.mount, normalizePath(to));
5771
5942
  try {
@@ -5784,6 +5955,12 @@ var MountFs = class {
5784
5955
  this.table.forget(found.mount, normalizePath(path));
5785
5956
  await found.mount.conn.call({ op: "symlink", target, path: found.rel });
5786
5957
  }
5958
+ async realpath(path, follow) {
5959
+ if (this.at(path) || !this.base.realpath) throw errnoError("ENOSYS", path);
5960
+ const real = await this.base.realpath(path, follow);
5961
+ if (this.at(real)) throw errnoError("ENOSYS", path);
5962
+ return real;
5963
+ }
5787
5964
  async readlink(path) {
5788
5965
  const found = this.at(path);
5789
5966
  if (!found) return this.base.readlink(path);
@@ -7366,6 +7543,7 @@ var VfsNode = class {
7366
7543
  writeBack;
7367
7544
  writeBackCost = 0;
7368
7545
  opens = 0;
7546
+ writers = 0;
7369
7547
  missing = false;
7370
7548
  revoked = false;
7371
7549
  fs;
@@ -7541,8 +7719,10 @@ var VfsNodes = class {
7541
7719
  byPath = /* @__PURE__ */ new Map();
7542
7720
  fs;
7543
7721
  entries = /* @__PURE__ */ new Map();
7544
- constructor(fs) {
7722
+ onClosed;
7723
+ constructor(fs, onClosed) {
7545
7724
  this.fs = fs;
7725
+ this.onClosed = onClosed;
7546
7726
  }
7547
7727
  async onEntry(path, op) {
7548
7728
  const key = await this.entryKey(path);
@@ -7556,9 +7736,21 @@ var VfsNodes = class {
7556
7736
  }
7557
7737
  async entryKey(path) {
7558
7738
  const parent = parentOf5(path);
7559
- const dir = await realDir(this.fs, parent).catch(() => parent);
7560
- const name2 = path.slice(path.lastIndexOf("/") + 1);
7561
- return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
7739
+ const dir = await this.real(parent, true, () => realDir(this.fs, parent));
7740
+ return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
7741
+ }
7742
+ targetKey(path) {
7743
+ return this.real(path, true, () => realTarget(this.fs, path));
7744
+ }
7745
+ async real(path, follow, walk) {
7746
+ if (this.fs.realpath) {
7747
+ try {
7748
+ return await this.fs.realpath(path, follow);
7749
+ } catch (err) {
7750
+ if (errCode(err) === "ELOOP") throw err;
7751
+ }
7752
+ }
7753
+ return walk();
7562
7754
  }
7563
7755
  createExclusive(path) {
7564
7756
  return this.onEntry(path, async () => {
@@ -7568,16 +7760,17 @@ var VfsNodes = class {
7568
7760
  await this.fs.writeFile(path, new Uint8Array(0));
7569
7761
  });
7570
7762
  }
7571
- open(path, pin) {
7763
+ open(path, pin, writable = false) {
7572
7764
  let node = this.byPath.get(path);
7573
7765
  if (!node) {
7574
7766
  node = new VfsNode(this.fs, path, void 0, false, false, pin);
7575
7767
  this.byPath.set(path, node);
7576
7768
  }
7577
7769
  node.opens++;
7770
+ if (writable) node.writers++;
7578
7771
  return node;
7579
7772
  }
7580
- adopt(path, contents2, dirty) {
7773
+ adopt(path, contents2, dirty, writable = false) {
7581
7774
  let node = this.byPath.get(path);
7582
7775
  if (!node) {
7583
7776
  node = new VfsNode(this.fs, path, contents2, false, dirty);
@@ -7586,15 +7779,27 @@ var VfsNodes = class {
7586
7779
  node.replace(contents2);
7587
7780
  }
7588
7781
  node.opens++;
7782
+ if (writable) node.writers++;
7589
7783
  return node;
7590
7784
  }
7591
7785
  holds(prefix) {
7592
7786
  for (const path of this.byPath.keys()) if (within4(path, prefix)) return true;
7593
7787
  return false;
7594
7788
  }
7595
- closed(node) {
7789
+ writes(prefix) {
7790
+ for (const [path, node] of this.byPath)
7791
+ if (node.writers > 0 && within4(path, prefix)) return true;
7792
+ return false;
7793
+ }
7794
+ closed(node, writable = false) {
7596
7795
  node.opens--;
7597
- if (node.opens === 0 && this.byPath.get(node.path) === node) this.byPath.delete(node.path);
7796
+ if (writable) node.writers--;
7797
+ if (node.opens > 0) {
7798
+ if (writable && node.writers === 0) this.onClosed?.(node.path);
7799
+ return;
7800
+ }
7801
+ if (this.byPath.get(node.path) === node) this.byPath.delete(node.path);
7802
+ this.onClosed?.(node.path);
7598
7803
  }
7599
7804
  async flush(path) {
7600
7805
  for (const [p, node] of this.byPath) if (within4(p, path)) await node.serial(() => node.flush());
@@ -7653,10 +7858,9 @@ function parentOf5(path) {
7653
7858
  async function linkOf(fs, path) {
7654
7859
  return fs.readlink?.(path).catch(() => void 0);
7655
7860
  }
7656
- async function realDir(fs, dir) {
7861
+ async function realDir(fs, dir, budget = { hops: 0 }) {
7657
7862
  const queue = dir.split("/").filter(Boolean);
7658
7863
  let at = "/";
7659
- let hops = 0;
7660
7864
  while (queue.length > 0) {
7661
7865
  const part = queue.shift();
7662
7866
  if (part === ".") continue;
@@ -7665,28 +7869,41 @@ async function realDir(fs, dir) {
7665
7869
  continue;
7666
7870
  }
7667
7871
  const next = at === "/" ? `/${part}` : `${at}/${part}`;
7668
- const link2 = hops < MAX_LINKS5 ? await linkOf(fs, next) : void 0;
7872
+ const link2 = await linkOf(fs, next);
7669
7873
  if (link2 === void 0) {
7670
7874
  at = next;
7671
7875
  continue;
7672
7876
  }
7673
- hops++;
7877
+ if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", dir);
7878
+ budget.hops++;
7674
7879
  queue.unshift(...link2.split("/").filter(Boolean));
7675
7880
  if (link2.startsWith("/")) at = "/";
7676
7881
  }
7677
7882
  return at;
7678
7883
  }
7679
- async function targetDir(fs, path) {
7680
- let dir = await realDir(fs, parentOf5(path));
7884
+ function errCode(err) {
7885
+ return err?.code;
7886
+ }
7887
+ function joinName(dir, name2) {
7888
+ return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
7889
+ }
7890
+ async function realTarget(fs, path) {
7891
+ const budget = { hops: 0 };
7892
+ let dir = await realDir(fs, parentOf5(path), budget);
7681
7893
  let name2 = path.slice(path.lastIndexOf("/") + 1);
7682
- for (let hops = 0; hops < MAX_LINKS5; hops++) {
7683
- const link2 = await linkOf(fs, dir === "/" ? `/${name2}` : `${dir}/${name2}`);
7684
- if (link2 === void 0) return dir;
7685
- const target = link2.startsWith("/") ? link2 : `${dir}/${link2}`;
7686
- dir = await realDir(fs, parentOf5(target));
7894
+ for (; ; ) {
7895
+ const link2 = await linkOf(fs, joinName(dir, name2));
7896
+ if (link2 === void 0) break;
7897
+ if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", path);
7898
+ budget.hops++;
7899
+ const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
7900
+ dir = await realDir(fs, parentOf5(target), budget);
7687
7901
  name2 = target.slice(target.lastIndexOf("/") + 1);
7688
7902
  }
7689
- return dir;
7903
+ return joinName(dir, name2);
7904
+ }
7905
+ async function targetDir(fs, path) {
7906
+ return parentOf5(await realTarget(fs, path).catch(() => path));
7690
7907
  }
7691
7908
  async function refuseReadonly(fs, path, flags, opts = {}) {
7692
7909
  const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
@@ -7705,13 +7922,14 @@ function keepingOpen(fs, nodes) {
7705
7922
  return {
7706
7923
  ...fs,
7707
7924
  async rm(path, options) {
7708
- const at = fs.resolvePath("/", path);
7925
+ const at = await nodes.entryKey(fs.resolvePath("/", path));
7709
7926
  await nodes.unlinking(at);
7710
7927
  await fs.rm(path, options);
7711
7928
  nodes.unlinked(at);
7712
7929
  },
7713
- rename(from, to) {
7714
- const [a, b] = [fs.resolvePath("/", from), fs.resolvePath("/", to)];
7930
+ async rename(from, to) {
7931
+ const a = await nodes.entryKey(fs.resolvePath("/", from));
7932
+ const b = await nodes.entryKey(fs.resolvePath("/", to));
7715
7933
  return nodes.onEntry(b, async () => {
7716
7934
  await nodes.unlinking(b);
7717
7935
  await fs.rename(from, to);
@@ -7727,7 +7945,7 @@ function vfsFile(fs, opts, nodes) {
7727
7945
  const access = opts.flags & O_ACCMODE;
7728
7946
  const readable = access !== O_WRONLY;
7729
7947
  const writable = access === O_WRONLY || access === O_RDWR;
7730
- const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true) : nodes.open(opts.path, opts.pin);
7948
+ const node = !nodes || opts.orphan ? new VfsNode(fs, opts.path, opts.contents, opts.orphan === true, opts.dirty === true) : opts.contents !== void 0 ? nodes.adopt(opts.path, opts.contents, opts.dirty === true, writable) : nodes.open(opts.path, opts.pin, writable);
7731
7949
  let openError;
7732
7950
  const atOpen = (op) => {
7733
7951
  void node.serial(op).catch((err) => {
@@ -7790,7 +8008,7 @@ function vfsFile(fs, opts, nodes) {
7790
8008
  takeOpenError();
7791
8009
  await node.flush();
7792
8010
  } finally {
7793
- nodes?.closed(node);
8011
+ nodes?.closed(node, writable);
7794
8012
  }
7795
8013
  })
7796
8014
  });
@@ -7857,10 +8075,12 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
7857
8075
  "fd-cloexec",
7858
8076
  "fd-list",
7859
8077
  "proc-alarm",
8078
+ "proc-umask",
7860
8079
  "dl-log",
7861
8080
  "fd-renumber",
7862
8081
  "fd-promote",
7863
8082
  "fd-open-tty",
8083
+ "fd-tty-names",
7864
8084
  "tty-get",
7865
8085
  "tty-set",
7866
8086
  "tty-winsz",
@@ -7894,6 +8114,7 @@ function isWasmSyscall(req) {
7894
8114
  return typeof op === "string" && SYSCALL_OPS.has(op);
7895
8115
  }
7896
8116
  var MAX_READ = 1024 * 1024;
8117
+ var DEFAULT_UMASK = 18;
7897
8118
  var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
7898
8119
  var HOSTS_ONLY = new Resolver({ routes: new Routes() });
7899
8120
  var WasmProcess = class {
@@ -7904,6 +8125,7 @@ var WasmProcess = class {
7904
8125
  inherited = 0;
7905
8126
  interrupt = new AbortController();
7906
8127
  dying = false;
8128
+ umask = DEFAULT_UMASK;
7907
8129
  execChild;
7908
8130
  execTermsig;
7909
8131
  stopped = 0;
@@ -7921,6 +8143,7 @@ var WasmProcess = class {
7921
8143
  this.fds = fds;
7922
8144
  this.options = options;
7923
8145
  this.inherited = options.ignored ?? 0;
8146
+ this.umask = options.umask ?? DEFAULT_UMASK;
7924
8147
  this.children = new ChildTable(fds, options.spawner, options.forker);
7925
8148
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
7926
8149
  if (!options.nodes) options.openFiles?.add(this.nodes);
@@ -7993,6 +8216,11 @@ var WasmProcess = class {
7993
8216
  return result;
7994
8217
  }
7995
8218
  }
8219
+ setUmask(mask) {
8220
+ const old = this.umask;
8221
+ if (mask !== void 0) this.umask = mask & 511;
8222
+ return old;
8223
+ }
7996
8224
  die() {
7997
8225
  if (this.dying) return;
7998
8226
  this.dying = true;
@@ -8063,9 +8291,21 @@ var WasmProcess = class {
8063
8291
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
8064
8292
  const fd = this.fds.install(heldFile(), 3);
8065
8293
  try {
8066
- if (req.exclusive) await this.nodes.createExclusive(req.path);
8067
- this.fds.installAt(fd, this.openVfsFile(req));
8068
- if (req.existing && !await present(this.options.fs, req.path, true)) {
8294
+ const nodes = this.nodes;
8295
+ const path = req.orphan ? req.path : req.exclusive ? await nodes.entryKey(req.path) : await nodes.targetKey(req.path);
8296
+ const open = { ...req, path };
8297
+ if (req.exclusive) {
8298
+ await nodes.createExclusive(path);
8299
+ this.fds.installAt(fd, this.openVfsFile(open));
8300
+ } else if (req.create) {
8301
+ await nodes.onEntry(path, async () => {
8302
+ this.fds.installAt(fd, this.openVfsFile(open));
8303
+ await this.fds.get(fd).file.stat?.();
8304
+ });
8305
+ } else {
8306
+ this.fds.installAt(fd, this.openVfsFile(open));
8307
+ }
8308
+ if (req.existing && !await present(this.options.fs, path, true)) {
8069
8309
  throw new KernelError("ENOENT");
8070
8310
  }
8071
8311
  } catch (err) {
@@ -8144,7 +8384,7 @@ var WasmProcess = class {
8144
8384
  case "fd-reserve":
8145
8385
  return { ok: true, kind: "json", json: this.reserve(req.fd, req.min, req.meta) };
8146
8386
  case "fd-promote":
8147
- this.promote(req);
8387
+ await this.promote(req);
8148
8388
  return { ok: true, kind: "void" };
8149
8389
  case "fd-renumber":
8150
8390
  if (req.from !== req.to) {
@@ -8157,6 +8397,8 @@ var WasmProcess = class {
8157
8397
  if (!tty) throw new KernelError("ENXIO");
8158
8398
  return { ok: true, kind: "json", json: this.fds.install(tty.file(), 3) };
8159
8399
  }
8400
+ case "fd-tty-names":
8401
+ return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
8160
8402
  case "fd-select": {
8161
8403
  const { read, write, timeoutMs } = req;
8162
8404
  const signal = this.blockingSignal();
@@ -8178,10 +8420,14 @@ var WasmProcess = class {
8178
8420
  async vfsSyscall(req) {
8179
8421
  if ("path" in req || "from" in req) {
8180
8422
  const nodes = this.nodes;
8181
- if (req.op === "fd-path-flush") await nodes.flush(req.path);
8182
- else if (req.op === "fd-path-unlinking") await nodes.unlinking(req.path);
8183
- else if (req.op === "fd-path-unlinked") nodes.unlinked(req.path);
8184
- else if (req.op === "fd-path-renamed") nodes.renamed(req.from, req.to);
8423
+ if (req.op === "fd-path-flush") {
8424
+ await nodes.flush(await nodes.targetKey(req.path).catch(() => req.path));
8425
+ } else if (req.op === "fd-path-unlinking")
8426
+ await nodes.unlinking(await nodes.entryKey(req.path));
8427
+ else if (req.op === "fd-path-unlinked") nodes.unlinked(await nodes.entryKey(req.path));
8428
+ else if (req.op === "fd-path-renamed") {
8429
+ nodes.renamed(await nodes.entryKey(req.from), await nodes.entryKey(req.to));
8430
+ }
8185
8431
  return { ok: true, kind: "void" };
8186
8432
  }
8187
8433
  const file = this.fds.get(req.fd).file;
@@ -8202,17 +8448,19 @@ var WasmProcess = class {
8202
8448
  return { ok: true, kind: "json", json: await file.stat() };
8203
8449
  }
8204
8450
  }
8205
- promote(req) {
8451
+ async promote(req) {
8206
8452
  if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
8207
8453
  if (req.share !== void 0) {
8208
8454
  this.fds.dup2(req.share, req.fd);
8209
8455
  return;
8210
8456
  }
8211
8457
  if (req.path === void 0) throw new KernelError("EINVAL");
8458
+ const path = req.orphan ? req.path : await this.nodes.targetKey(req.path);
8459
+ if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
8212
8460
  const file = vfsFile(
8213
8461
  this.options.fs,
8214
8462
  {
8215
- path: req.path,
8463
+ path,
8216
8464
  flags: req.flags ?? 0,
8217
8465
  position: req.position ?? 0,
8218
8466
  ...req.contents !== void 0 ? { contents: req.contents } : {},
@@ -8388,6 +8636,8 @@ var WasmProcess = class {
8388
8636
  case "proc-alarm":
8389
8637
  this.setAlarm(req.sig, req.firstMs ?? req.ms, req.repeat ? req.ms : 0, req.timer);
8390
8638
  return { ok: true, kind: "void" };
8639
+ case "proc-umask":
8640
+ return { ok: true, kind: "json", json: this.setUmask(req.mask) };
8391
8641
  case "dl-log":
8392
8642
  if (req.append) this.dlLog.push(req.append);
8393
8643
  return { ok: true, kind: "json", json: this.dlLog.slice(req.from) };
@@ -8537,7 +8787,7 @@ var Dying = class {
8537
8787
  return [typeof reported === "number" ? reported : CRASHED, void 0];
8538
8788
  }
8539
8789
  };
8540
- function processInit(opts, sab) {
8790
+ function processInit(opts, sab, umask) {
8541
8791
  const base = {
8542
8792
  type: WASM_PROCESS_INIT,
8543
8793
  pid: opts.shownPid ?? opts.pid,
@@ -8546,6 +8796,7 @@ function processInit(opts, sab) {
8546
8796
  env: opts.env,
8547
8797
  cwd: opts.cwd,
8548
8798
  sab,
8799
+ umask,
8549
8800
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {}
8550
8801
  };
8551
8802
  if (opts.program.abi === "js") {
@@ -8557,11 +8808,17 @@ function processInit(opts, sab) {
8557
8808
  ...opts.fork ? { fork: opts.fork } : { fds: inheritedFds(opts.fds) }
8558
8809
  };
8559
8810
  }
8811
+ function inherited(opts) {
8812
+ return {
8813
+ ...opts.ignored ? { ignored: opts.ignored } : {},
8814
+ ...opts.umask !== void 0 ? { umask: opts.umask } : {}
8815
+ };
8816
+ }
8560
8817
  function spawnWasmProcess(opts) {
8561
8818
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
8562
8819
  const header2 = new Int32Array(sab, 0, SAB_HEADER_I32);
8563
8820
  const process = new WasmProcess(opts.pid, opts.fds, {
8564
- ...opts.ignored ? { ignored: opts.ignored } : {},
8821
+ ...inherited(opts),
8565
8822
  ...opts.identity ? { identity: opts.identity } : {},
8566
8823
  ...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
8567
8824
  spawner: opts.spawner,
@@ -8695,7 +8952,7 @@ function spawnWasmProcess(opts) {
8695
8952
  };
8696
8953
  tw.postMessage(tinit);
8697
8954
  };
8698
- const init = processInit(opts, sab);
8955
+ const init = processInit(opts, sab, process.umask);
8699
8956
  worker.postMessage(init, opts.fork ? [opts.fork.memory.buffer] : []);
8700
8957
  const signal = (sig) => {
8701
8958
  if (process.signal(sig) !== "terminate") return;
@@ -8713,6 +8970,7 @@ function spawnWasmProcess(opts) {
8713
8970
  onState: (listener) => process.onState(listener),
8714
8971
  memory: () => Atomics.load(header2, SAB_I_MEMORY) * 65536,
8715
8972
  ignoredSignals: (fork) => process.inheritable(fork),
8973
+ umask: () => process.umask,
8716
8974
  writesBack: () => opts.program.abi === "wasi" && !done4
8717
8975
  };
8718
8976
  }
@@ -8818,8 +9076,13 @@ var JobTable = class {
8818
9076
  return id;
8819
9077
  }
8820
9078
  terminalNamed(name2) {
8821
- const known = /* @__PURE__ */ new Set([...this.terminals.values(), ...this.foreground.keys()]);
8822
- return [...known].find((tty) => tty.name === name2);
9079
+ return this.knownTerminals().find((tty) => tty.name === name2);
9080
+ }
9081
+ terminalNames() {
9082
+ return this.knownTerminals().flatMap((tty) => tty.name ? [tty.name] : []);
9083
+ }
9084
+ knownTerminals() {
9085
+ return [.../* @__PURE__ */ new Set([...this.terminals.values(), ...this.foreground.keys()])];
8823
9086
  }
8824
9087
  controllingTerminal(pid) {
8825
9088
  const member = this.members.get(pid);
@@ -9092,7 +9355,11 @@ function mediumSlot(lost, onLost) {
9092
9355
  }
9093
9356
 
9094
9357
  // src/mount/fsa.ts
9095
- var FSA_CAPABILITIES = { listingStats: true };
9358
+ var FSA_CAPABILITIES = {
9359
+ listingStats: true,
9360
+ ranges: true,
9361
+ sessions: true
9362
+ };
9096
9363
  var codeOf2 = (err) => err?.code;
9097
9364
  var denied = (err) => codeOf2(err) === "EACCES" || err?.name === "NotAllowedError";
9098
9365
  function attrOf(st) {
@@ -9132,30 +9399,20 @@ function kernelFsHandlers(fs) {
9132
9399
  if (st && flags.exclusive) throw fsError2("EEXIST", path);
9133
9400
  if (st?.isDirectory && flags.write) throw fsError2("EISDIR", path);
9134
9401
  if (!st && !flags.create) throw fsError2("ENOENT", path);
9135
- const data = st && !flags.truncate ? await fs.readFileBuffer(path) : new Uint8Array(0);
9136
- handles.set(++nextFh, { path, data, length: data.length, dirty: !st || flags.truncate });
9402
+ if (!st || flags.truncate) await fs.writeFile(path, new Uint8Array(0));
9403
+ handles.set(++nextFh, { path });
9137
9404
  return nextFh;
9138
9405
  },
9139
- async read(fh, offset, size) {
9140
- const h = handle(fh);
9141
- return h.data.slice(offset, Math.min(h.length, offset + size));
9142
- },
9406
+ read: async (fh, offset, size) => fs.pread(handle(fh).path, offset, size),
9143
9407
  async write(fh, offset, bytes) {
9144
9408
  const h = handle(fh);
9145
- const end = offset + bytes.length;
9146
- if (end > h.data.length) {
9147
- const grown = new Uint8Array(Math.max(end, h.data.length * 2));
9148
- grown.set(h.data.subarray(0, h.length));
9149
- h.data = grown;
9150
- }
9151
- h.data.set(bytes, offset);
9152
- h.length = Math.max(h.length, end);
9153
- h.dirty = true;
9409
+ h.session ??= fs.writeSession(h.path, false);
9410
+ await (await h.session).write(offset, bytes);
9154
9411
  },
9155
9412
  async release(fh) {
9156
9413
  const h = handle(fh);
9157
9414
  handles.delete(fh);
9158
- if (h.dirty) await fs.writeFile(h.path, h.data.slice(0, h.length));
9415
+ if (h.session !== void 0) await (await h.session).close();
9159
9416
  },
9160
9417
  mkdir: (path) => fs.mkdir(path),
9161
9418
  async rmdir(path) {
@@ -9171,6 +9428,13 @@ function kernelFsHandlers(fs) {
9171
9428
  symlink: (target, path) => fs.symlink(target, path),
9172
9429
  readlink: (path) => fs.readlink(path),
9173
9430
  async setattr(path, change) {
9431
+ if (change.size !== void 0) {
9432
+ const open = [...handles.values()].find(
9433
+ (h) => h.path === path && h.session !== void 0
9434
+ )?.session;
9435
+ if (open !== void 0) await (await open).truncate(change.size);
9436
+ else await fs.truncate?.(path, change.size);
9437
+ }
9174
9438
  if (change.mode !== void 0) await fs.chmod(path, change.mode);
9175
9439
  if (change.mtime !== void 0) {
9176
9440
  await fs.utimes(path, new Date(change.mtime), new Date(change.mtime));
@@ -9886,6 +10150,7 @@ function tmpfs() {
9886
10150
  const { dir, name: name2 } = parent(path);
9887
10151
  if (dir.children.has(name2)) throw fsError2("EEXIST", path);
9888
10152
  dir.children.set(name2, make("directory", 16877));
10153
+ dir.mtime = Date.now();
9889
10154
  },
9890
10155
  async rmdir(path) {
9891
10156
  const { dir, name: name2 } = parent(path);
@@ -9894,6 +10159,7 @@ function tmpfs() {
9894
10159
  if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
9895
10160
  if (node.children.size > 0) throw fsError2("ENOTEMPTY", path);
9896
10161
  dir.children.delete(name2);
10162
+ dir.mtime = Date.now();
9897
10163
  },
9898
10164
  async unlink(path) {
9899
10165
  const { dir, name: name2 } = parent(path);
@@ -9901,6 +10167,7 @@ function tmpfs() {
9901
10167
  if (!node) throw fsError2("ENOENT", path);
9902
10168
  if (node.kind === "directory") throw fsError2("EISDIR", path);
9903
10169
  dir.children.delete(name2);
10170
+ dir.mtime = Date.now();
9904
10171
  },
9905
10172
  async rename(from, to) {
9906
10173
  const a = `/${parts(from).join("/")}`;
@@ -9917,6 +10184,8 @@ function tmpfs() {
9917
10184
  }
9918
10185
  source.dir.children.delete(source.name);
9919
10186
  target.dir.children.set(target.name, node);
10187
+ source.dir.mtime = Date.now();
10188
+ target.dir.mtime = source.dir.mtime;
9920
10189
  },
9921
10190
  async symlink(target, path) {
9922
10191
  const { dir, name: name2 } = parent(path);
@@ -9924,6 +10193,7 @@ function tmpfs() {
9924
10193
  const node = make("symlink", 41471);
9925
10194
  node.target = target;
9926
10195
  dir.children.set(name2, node);
10196
+ dir.mtime = Date.now();
9927
10197
  },
9928
10198
  async readlink(path) {
9929
10199
  const node = find(path);
@@ -10222,7 +10492,7 @@ var Launcher = class {
10222
10492
  deciding = /* @__PURE__ */ new Map();
10223
10493
  identities = { asked: 0, timedOut: 0 };
10224
10494
  nodes;
10225
- held = /* @__PURE__ */ new Set();
10495
+ held = new HeldSet((path) => this.released(path));
10226
10496
  processMounts;
10227
10497
  booting = new AbortController();
10228
10498
  fstab = Promise.resolve([]);
@@ -10264,7 +10534,7 @@ var Launcher = class {
10264
10534
  });
10265
10535
  enableCdp(this.net, this.cdp);
10266
10536
  const fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
10267
- this.nodes = new VfsNodes(fs);
10537
+ this.nodes = new VfsNodes(fs, (path) => this.released(path));
10268
10538
  this.openFiles.add(this.nodes);
10269
10539
  this.fs = keepingOpen(fs, this.nodes);
10270
10540
  this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
@@ -10453,6 +10723,7 @@ var Launcher = class {
10453
10723
  this.settling.syscall(pid, call.op);
10454
10724
  },
10455
10725
  ...req.ignored ? { ignored: req.ignored } : {},
10726
+ ...req.umask !== void 0 ? { umask: req.umask } : {},
10456
10727
  program: req.program,
10457
10728
  argv0: req.argv0,
10458
10729
  args: req.args,
@@ -10544,7 +10815,8 @@ var Launcher = class {
10544
10815
  ...described,
10545
10816
  started: (this.described.get(root.pid) ?? described).started,
10546
10817
  state: this.processes.has(member.pid) ? "S" : "Z",
10547
- memory: this.processes.get(member.pid)?.memory() ?? 0
10818
+ memory: this.processes.get(member.pid)?.memory() ?? 0,
10819
+ umask: this.umaskOf(member.pid)
10548
10820
  });
10549
10821
  }
10550
10822
  return listed;
@@ -10566,6 +10838,7 @@ var Launcher = class {
10566
10838
  report,
10567
10839
  ppid,
10568
10840
  ignored: this.ignoredBy(ppid),
10841
+ umask: this.umaskOf(ppid),
10569
10842
  ...exec
10570
10843
  });
10571
10844
  deciding?.attach();
@@ -10610,7 +10883,8 @@ var Launcher = class {
10610
10883
  fds,
10611
10884
  ppid,
10612
10885
  fork: state,
10613
- ignored: this.ignoredBy(ppid, true)
10886
+ ignored: this.ignoredBy(ppid, true),
10887
+ umask: this.umaskOf(ppid)
10614
10888
  })
10615
10889
  );
10616
10890
  }
@@ -10619,6 +10893,14 @@ var Launcher = class {
10619
10893
  const foreground = () => this.jobs.tcgetpgrp(tty, fallback);
10620
10894
  this.settling.deliver(sig, pgid, send, tty.reading ? void 0 : foreground);
10621
10895
  }
10896
+ umaskOf(pid) {
10897
+ return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
10898
+ }
10899
+ released(path) {
10900
+ const at = normalizePath(path);
10901
+ const open = [...this.openFiles].some((nodes) => nodes.writes(at)) || heldUnder(this.held, at);
10902
+ if (!open) void this.mounts.commit(at).catch(() => void 0);
10903
+ }
10622
10904
  ignoredBy(pid, fork = false) {
10623
10905
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
10624
10906
  }