@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.
package/dist/node.js CHANGED
@@ -1938,11 +1938,41 @@ var OpfsFs = class {
1938
1938
  const fallback = directory || EXECUTABLE.test(path) ? 493 : 420;
1939
1939
  return (directory ? S_IFDIR : S_IFREG) | (meta?.mode ?? fallback);
1940
1940
  }
1941
+ dirTimes = /* @__PURE__ */ new Map();
1942
+ dirDirty = /* @__PURE__ */ new Set();
1943
+ dirFlush;
1944
+ touch(...dirs) {
1945
+ const now = Date.now();
1946
+ for (const dir of dirs) {
1947
+ this.dirTimes.set(dir, now);
1948
+ this.dirDirty.add(dir);
1949
+ }
1950
+ this.dirFlush ??= Promise.resolve().then(() => this.flushDirTimes()).catch(() => void 0).finally(() => {
1951
+ this.dirFlush = void 0;
1952
+ });
1953
+ }
1954
+ async flushDirTimes() {
1955
+ for (const path of this.dirDirty) {
1956
+ this.dirDirty.delete(path);
1957
+ const ms = this.dirTimes.get(path);
1958
+ if (ms === void 0) continue;
1959
+ await this.meta.update(path, (entry) => ({
1960
+ ...entry,
1961
+ path,
1962
+ mtimeMs: ms,
1963
+ mtimeFor: 0,
1964
+ ctimeMs: ms
1965
+ }));
1966
+ if (this.dirTimes.get(path) === ms) this.dirTimes.delete(path);
1967
+ }
1968
+ }
1941
1969
  async statOf(path, handle, meta) {
1942
1970
  const directory = handle.kind === "directory";
1943
1971
  const file = directory ? void 0 : await handle.getFile();
1944
1972
  const modified = file?.lastModified ?? 0;
1945
- const mtime = meta?.mtimeMs !== void 0 && meta.mtimeFor === modified ? meta.mtimeMs : modified;
1973
+ const touched = directory ? this.dirTimes.get(path) : void 0;
1974
+ const kept = meta?.mtimeMs !== void 0 && meta.mtimeFor === modified ? meta.mtimeMs : modified;
1975
+ const mtime = touched ?? kept;
1946
1976
  return {
1947
1977
  isFile: !directory,
1948
1978
  isDirectory: directory,
@@ -1951,7 +1981,7 @@ var OpfsFs = class {
1951
1981
  mode: this.mode(path, directory, meta),
1952
1982
  mtime: new Date(mtime),
1953
1983
  atime: new Date(meta?.atimeMs ?? mtime),
1954
- ctime: new Date(Math.max(meta?.ctimeMs ?? mtime, modified)),
1984
+ ctime: new Date(Math.max(touched ?? meta?.ctimeMs ?? mtime, modified)),
1955
1985
  ino: meta?.ino ?? inodeOf(path),
1956
1986
  ...this.ranged && !directory ? { ranged: true } : {}
1957
1987
  };
@@ -2028,6 +2058,19 @@ var OpfsFs = class {
2028
2058
  const handle = await this.existing(path);
2029
2059
  await this.retrying(path, () => positioned(handle, (io) => io.truncate(size)), true);
2030
2060
  }
2061
+ async writeSession(path, truncate) {
2062
+ const handle = (await this.fileFor(path))[0];
2063
+ const writable = await this.retrying(
2064
+ path,
2065
+ () => handle.createWritable({ keepExistingData: !truncate }),
2066
+ true
2067
+ );
2068
+ return {
2069
+ write: (at, bytes) => writable.write({ type: "write", position: at, data: bytes }),
2070
+ truncate: (size) => writable.truncate(size),
2071
+ close: () => writable.close()
2072
+ };
2073
+ }
2031
2074
  async readFile(path) {
2032
2075
  return new TextDecoder().decode(await this.readFileBuffer(path));
2033
2076
  }
@@ -2045,10 +2088,12 @@ var OpfsFs = class {
2045
2088
  const [target, existed] = await this.writeTarget(path);
2046
2089
  const [dir, name2] = await this.parent(target);
2047
2090
  try {
2048
- return [
2049
- await this.inDir(dir, (parent) => parent.getFileHandle(name2, { create: true })),
2050
- existed
2051
- ];
2091
+ const handle = await this.inDir(
2092
+ dir,
2093
+ (parent) => parent.getFileHandle(name2, { create: true })
2094
+ );
2095
+ if (!existed) this.touch(parentOf2(target));
2096
+ return [handle, existed];
2052
2097
  } catch (err) {
2053
2098
  const code = err?.name === "TypeMismatchError" ? "EISDIR" : null;
2054
2099
  throw code ? fsError(code, path) : translate(err, path);
@@ -2106,6 +2151,7 @@ var OpfsFs = class {
2106
2151
  const parent = await this.locate(parentOf2(path), true);
2107
2152
  if (parent.handle?.kind !== "directory") throw fsError("ENOTDIR", path);
2108
2153
  await parent.handle.getDirectoryHandle(baseOf(path), { create: true });
2154
+ this.touch(parent.path);
2109
2155
  }
2110
2156
  async rm(path, options = {}) {
2111
2157
  let found;
@@ -2129,6 +2175,7 @@ var OpfsFs = class {
2129
2175
  this.changed(found.path);
2130
2176
  }
2131
2177
  await this.meta.remove([found.path]);
2178
+ this.touch(parentOf2(found.path));
2132
2179
  }
2133
2180
  async rename(from, to) {
2134
2181
  if (from === to) return;
@@ -2160,6 +2207,7 @@ var OpfsFs = class {
2160
2207
  if (source.link) {
2161
2208
  if (target?.handle) await this.rm(destination);
2162
2209
  await this.meta.move(source.path, destination, carry);
2210
+ this.touch(parentOf2(source.path), parent.path);
2163
2211
  return;
2164
2212
  }
2165
2213
  const dir = parent.handle;
@@ -2181,6 +2229,7 @@ var OpfsFs = class {
2181
2229
  }
2182
2230
  if (copied) await this.restamp(destination, timed);
2183
2231
  this.changed(source.path, destination);
2232
+ this.touch(parentOf2(source.path), parent.path);
2184
2233
  }
2185
2234
  async modified(path) {
2186
2235
  const handle = await this.handle(path);
@@ -2244,6 +2293,10 @@ var OpfsFs = class {
2244
2293
  mtimeMs: now,
2245
2294
  ctimeMs: now
2246
2295
  }));
2296
+ this.touch(parent.path);
2297
+ }
2298
+ async realpath(path, follow) {
2299
+ return (await this.locate(path, follow)).path;
2247
2300
  }
2248
2301
  async readlink(path) {
2249
2302
  const found = await this.locate(path, false);
@@ -2281,6 +2334,7 @@ var OpfsFs = class {
2281
2334
  }
2282
2335
  const handle = found.handle;
2283
2336
  const file = handle.kind === "file" ? await handle.getFile() : void 0;
2337
+ this.dirTimes.delete(found.path);
2284
2338
  const ctimeMs = Date.now();
2285
2339
  await this.meta.update(found.path, (entry) => ({
2286
2340
  ...entry,
@@ -3303,6 +3357,7 @@ function withCommandDirs(fs, names) {
3303
3357
  rename: (from, to) => fs.rename(from, to),
3304
3358
  symlink: (target, path) => fs.symlink(target, path),
3305
3359
  readlink: (path) => fs.readlink(path),
3360
+ ...fs.realpath ? { realpath: fs.realpath.bind(fs) } : {},
3306
3361
  chmod: (path, mode) => fs.chmod(path, mode),
3307
3362
  utimes: (path, atime, mtime) => fs.utimes(path, atime, mtime),
3308
3363
  lutimes: (path, atime, mtime) => lutimesOf(fs, path, atime, mtime),
@@ -3699,18 +3754,36 @@ function parseSize(text) {
3699
3754
  return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
3700
3755
  }
3701
3756
  var within2 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
3757
+ var HeldSet = class extends Set {
3758
+ idle;
3759
+ constructor(idle) {
3760
+ super();
3761
+ this.idle = idle;
3762
+ }
3763
+ add(holds) {
3764
+ holds.onIdle = this.idle;
3765
+ return super.add(holds);
3766
+ }
3767
+ delete(holds) {
3768
+ const removed = super.delete(holds);
3769
+ for (const path of holds.keys()) this.idle(path);
3770
+ return removed;
3771
+ }
3772
+ };
3702
3773
  function heldUnder(held, target) {
3703
3774
  for (const paths of held) for (const path of paths.keys()) if (within2(path, target)) return true;
3704
3775
  return false;
3705
3776
  }
3706
3777
  var HeldPaths = class extends Map {
3707
3778
  revoked = /* @__PURE__ */ new Set();
3779
+ onIdle;
3708
3780
  hold(path, on) {
3709
3781
  const count = (this.get(path) ?? 0) + (on ? 1 : -1);
3710
3782
  if (count > 0) this.set(path, count);
3711
3783
  else {
3712
3784
  this.delete(path);
3713
3785
  this.revoked.delete(path);
3786
+ this.onIdle?.(path);
3714
3787
  }
3715
3788
  }
3716
3789
  revoke(prefix) {
@@ -3778,6 +3851,8 @@ function spliced(bytes, offset, part) {
3778
3851
  }
3779
3852
  var MountTable = class {
3780
3853
  mounts = /* @__PURE__ */ new Map();
3854
+ sessions = /* @__PURE__ */ new Map();
3855
+ committing = /* @__PURE__ */ new Map();
3781
3856
  devices = 0;
3782
3857
  pending = /* @__PURE__ */ new Set();
3783
3858
  notes = /* @__PURE__ */ new Map();
@@ -3902,8 +3977,69 @@ var MountTable = class {
3902
3977
  if (!detach && this.deps.busy(at)) throw errnoError("EBUSY", `${at} has open files`);
3903
3978
  this.notes.delete(at);
3904
3979
  this.mounts.delete(at);
3905
- mount.conn.close();
3906
- mount.dispose();
3980
+ const waits = [...this.sessions].filter(([, s]) => s.mount === mount).map(([real]) => this.commit(real));
3981
+ for (const c of this.committing.values()) if (c.mount === mount) waits.push(c.done);
3982
+ const close = () => {
3983
+ mount.conn.close();
3984
+ mount.dispose();
3985
+ };
3986
+ if (waits.length === 0) close();
3987
+ else void Promise.allSettled(waits).then(close);
3988
+ }
3989
+ session(mount, real, rel2) {
3990
+ let session = this.sessions.get(real);
3991
+ if (!session) {
3992
+ const open = () => mount.conn.call({
3993
+ op: "open",
3994
+ path: rel2,
3995
+ write: true,
3996
+ create: true,
3997
+ truncate: false,
3998
+ exclusive: false
3999
+ });
4000
+ const before = this.committing.get(real)?.done ?? Promise.resolve();
4001
+ const fh = before.then(open, open);
4002
+ session = { mount, fh, extents: [], truncated: false };
4003
+ this.sessions.set(real, session);
4004
+ fh.catch(() => this.sessions.delete(real));
4005
+ }
4006
+ return session;
4007
+ }
4008
+ writing(real) {
4009
+ return this.sessions.get(real);
4010
+ }
4011
+ async commit(real) {
4012
+ const session = this.sessions.get(real);
4013
+ if (!session) return this.committing.get(real)?.done;
4014
+ this.sessions.delete(real);
4015
+ const done4 = (async () => {
4016
+ try {
4017
+ await session.mount.conn.call({ op: "release", fh: await session.fh });
4018
+ } finally {
4019
+ this.forget(session.mount, real);
4020
+ }
4021
+ })();
4022
+ const entry = { mount: session.mount, done: done4 };
4023
+ this.committing.set(real, entry);
4024
+ const settle = () => {
4025
+ if (this.committing.get(real) === entry) this.committing.delete(real);
4026
+ };
4027
+ done4.then(settle, settle);
4028
+ return done4;
4029
+ }
4030
+ async commitUnder(prefix) {
4031
+ const at = normalizePath(prefix);
4032
+ await Promise.all(
4033
+ [...this.sessions.keys()].filter((p) => within2(p, at)).map((p) => this.commit(p))
4034
+ );
4035
+ }
4036
+ async commitOverlapping(real, offset, length2) {
4037
+ const session = this.sessions.get(real);
4038
+ if (!session) return;
4039
+ const end = offset + length2;
4040
+ if (session.truncated || session.extents.some(([a, b]) => a < end && offset < b)) {
4041
+ await this.commit(real);
4042
+ }
3907
4043
  }
3908
4044
  crashed(mount, error) {
3909
4045
  mount.state = "failed";
@@ -3962,6 +4098,13 @@ var MountTable = class {
3962
4098
  return new MountFs(this, base);
3963
4099
  }
3964
4100
  };
4101
+ function noteExtent(extents, from, to) {
4102
+ const last = extents.at(-1);
4103
+ if (last && from <= last[1] && to >= last[0]) {
4104
+ last[0] = Math.min(last[0], from);
4105
+ last[1] = Math.max(last[1], to);
4106
+ } else extents.push([from, to]);
4107
+ }
3965
4108
  var MountFs = class {
3966
4109
  table;
3967
4110
  base;
@@ -4021,6 +4164,7 @@ var MountFs = class {
4021
4164
  }
4022
4165
  }
4023
4166
  async readWhole(mount, real, rel2) {
4167
+ await this.table.commit(real);
4024
4168
  const attr = await this.table.getattr(mount, real, rel2);
4025
4169
  if (attr.kind === "directory") throw errnoError("EISDIR", real);
4026
4170
  if (mount.maxFile > 0 && attr.size > mount.maxFile) {
@@ -4070,6 +4214,7 @@ var MountFs = class {
4070
4214
  }
4071
4215
  }
4072
4216
  async writeWhole(mount, real, rel2, bytes) {
4217
+ await this.table.commit(real);
4073
4218
  this.table.forget(mount, real);
4074
4219
  const fh = await this.open(mount, rel2, true);
4075
4220
  const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
@@ -4092,6 +4237,7 @@ var MountFs = class {
4092
4237
  if ((await this.table.getattr(mount, real, rel2)).kind === "directory") {
4093
4238
  throw errnoError("EISDIR", real);
4094
4239
  }
4240
+ await this.table.commitOverlapping(real, offset, length2);
4095
4241
  const fh = await mount.conn.call({
4096
4242
  op: "open",
4097
4243
  path: rel2,
@@ -4145,6 +4291,14 @@ var MountFs = class {
4145
4291
  this.writable(mount, real);
4146
4292
  this.fits(mount, real, offset + bytes.length);
4147
4293
  this.table.forget(mount, real);
4294
+ if (mount.caps.sessions) {
4295
+ const session = this.table.session(mount, real, rel2);
4296
+ const end = offset + bytes.length;
4297
+ await this.writeRuns(mount, await session.fh, offset, bytes, transfer);
4298
+ noteExtent(session.extents, offset, end);
4299
+ this.table.forget(mount, real);
4300
+ return;
4301
+ }
4148
4302
  const fh = await mount.conn.call({
4149
4303
  op: "open",
4150
4304
  path: rel2,
@@ -4153,17 +4307,20 @@ var MountFs = class {
4153
4307
  truncate: false,
4154
4308
  exclusive: false
4155
4309
  });
4156
- const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
4157
4310
  try {
4158
- for (let at = 0; at < bytes.length; at += chunk2) {
4159
- const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
4160
- await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
4161
- }
4311
+ await this.writeRuns(mount, fh, offset, bytes, transfer);
4162
4312
  } finally {
4163
4313
  await mount.conn.call({ op: "release", fh });
4164
4314
  this.table.forget(mount, real);
4165
4315
  }
4166
4316
  }
4317
+ async writeRuns(mount, fh, offset, bytes, transfer) {
4318
+ const chunk2 = mount.caps.maxIo ?? DEFAULT_IO;
4319
+ for (let at = 0; at < bytes.length; at += chunk2) {
4320
+ const part = handed(bytes, transfer, chunk2) ? bytes : bytes.slice(at, at + chunk2);
4321
+ await mount.conn.call({ op: "write", fh, offset: offset + at, bytes: part }, [part.buffer]);
4322
+ }
4323
+ }
4167
4324
  async truncate(path, size) {
4168
4325
  const real = await this.follow(path);
4169
4326
  const found = this.at(real);
@@ -4175,6 +4332,8 @@ var MountFs = class {
4175
4332
  this.writable(mount, real);
4176
4333
  this.fits(mount, real, size);
4177
4334
  this.table.forget(mount, real);
4335
+ const session = this.table.writing(real);
4336
+ if (session) session.truncated = true;
4178
4337
  try {
4179
4338
  await mount.conn.call({ op: "setattr", path: rel2, size });
4180
4339
  } finally {
@@ -4196,7 +4355,16 @@ var MountFs = class {
4196
4355
  const found = this.at(path);
4197
4356
  if (!found) return this.base.lstat(path);
4198
4357
  const real = normalizePath(path);
4199
- return statOf(await this.table.getattr(found.mount, real, found.rel), real, found.mount);
4358
+ if (this.table.writing(real)?.truncated) await this.table.commit(real);
4359
+ return this.statWriting(
4360
+ await this.table.getattr(found.mount, real, found.rel),
4361
+ real,
4362
+ found.mount
4363
+ );
4364
+ }
4365
+ statWriting(attr, real, mount) {
4366
+ const ends = (this.table.writing(real)?.extents ?? []).map(([, end]) => end);
4367
+ return statOf({ ...attr, size: Math.max(attr.size, ...ends) }, real, mount);
4200
4368
  }
4201
4369
  async stat(path) {
4202
4370
  const real = await this.follow(path);
@@ -4223,7 +4391,7 @@ var MountFs = class {
4223
4391
  list.map(async (entry) => {
4224
4392
  const child = normalizePath(`${real}/${entry.name}`);
4225
4393
  if (entry.attr && mount.caps.listingStats) {
4226
- return [entry.name, statOf(entry.attr, child, mount)];
4394
+ return [entry.name, this.statWriting(entry.attr, child, mount)];
4227
4395
  }
4228
4396
  return [entry.name, await this.lstat(child).catch(() => null)];
4229
4397
  })
@@ -4259,6 +4427,7 @@ var MountFs = class {
4259
4427
  const real = normalizePath(path);
4260
4428
  if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
4261
4429
  this.writable(mount, real);
4430
+ await this.table.commitUnder(real);
4262
4431
  let attr;
4263
4432
  try {
4264
4433
  attr = await this.table.getattr(mount, real, rel2);
@@ -4282,6 +4451,8 @@ var MountFs = class {
4282
4451
  if (!a && !b) return this.base.rename(from, to);
4283
4452
  if (!a || !b || a.mount !== b.mount) throw errnoError("EXDEV", `${from} -> ${to}`);
4284
4453
  this.writable(a.mount, normalizePath(to));
4454
+ await this.table.commitUnder(from);
4455
+ await this.table.commitUnder(to);
4285
4456
  this.table.forget(a.mount, normalizePath(from));
4286
4457
  this.table.forget(a.mount, normalizePath(to));
4287
4458
  try {
@@ -4300,6 +4471,12 @@ var MountFs = class {
4300
4471
  this.table.forget(found.mount, normalizePath(path));
4301
4472
  await found.mount.conn.call({ op: "symlink", target, path: found.rel });
4302
4473
  }
4474
+ async realpath(path, follow) {
4475
+ if (this.at(path) || !this.base.realpath) throw errnoError("ENOSYS", path);
4476
+ const real = await this.base.realpath(path, follow);
4477
+ if (this.at(real)) throw errnoError("ENOSYS", path);
4478
+ return real;
4479
+ }
4303
4480
  async readlink(path) {
4304
4481
  const found = this.at(path);
4305
4482
  if (!found) return this.base.readlink(path);
@@ -7084,6 +7261,7 @@ var VfsNode = class {
7084
7261
  writeBack;
7085
7262
  writeBackCost = 0;
7086
7263
  opens = 0;
7264
+ writers = 0;
7087
7265
  missing = false;
7088
7266
  revoked = false;
7089
7267
  fs;
@@ -7259,8 +7437,10 @@ var VfsNodes = class {
7259
7437
  byPath = /* @__PURE__ */ new Map();
7260
7438
  fs;
7261
7439
  entries = /* @__PURE__ */ new Map();
7262
- constructor(fs) {
7440
+ onClosed;
7441
+ constructor(fs, onClosed) {
7263
7442
  this.fs = fs;
7443
+ this.onClosed = onClosed;
7264
7444
  }
7265
7445
  async onEntry(path, op) {
7266
7446
  const key = await this.entryKey(path);
@@ -7274,9 +7454,21 @@ var VfsNodes = class {
7274
7454
  }
7275
7455
  async entryKey(path) {
7276
7456
  const parent = parentOf5(path);
7277
- const dir = await realDir(this.fs, parent).catch(() => parent);
7278
- const name2 = path.slice(path.lastIndexOf("/") + 1);
7279
- return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
7457
+ const dir = await this.real(parent, true, () => realDir(this.fs, parent));
7458
+ return joinName(dir, path.slice(path.lastIndexOf("/") + 1));
7459
+ }
7460
+ targetKey(path) {
7461
+ return this.real(path, true, () => realTarget(this.fs, path));
7462
+ }
7463
+ async real(path, follow, walk) {
7464
+ if (this.fs.realpath) {
7465
+ try {
7466
+ return await this.fs.realpath(path, follow);
7467
+ } catch (err) {
7468
+ if (errCode(err) === "ELOOP") throw err;
7469
+ }
7470
+ }
7471
+ return walk();
7280
7472
  }
7281
7473
  createExclusive(path) {
7282
7474
  return this.onEntry(path, async () => {
@@ -7286,16 +7478,17 @@ var VfsNodes = class {
7286
7478
  await this.fs.writeFile(path, new Uint8Array(0));
7287
7479
  });
7288
7480
  }
7289
- open(path, pin) {
7481
+ open(path, pin, writable = false) {
7290
7482
  let node = this.byPath.get(path);
7291
7483
  if (!node) {
7292
7484
  node = new VfsNode(this.fs, path, void 0, false, false, pin);
7293
7485
  this.byPath.set(path, node);
7294
7486
  }
7295
7487
  node.opens++;
7488
+ if (writable) node.writers++;
7296
7489
  return node;
7297
7490
  }
7298
- adopt(path, contents2, dirty) {
7491
+ adopt(path, contents2, dirty, writable = false) {
7299
7492
  let node = this.byPath.get(path);
7300
7493
  if (!node) {
7301
7494
  node = new VfsNode(this.fs, path, contents2, false, dirty);
@@ -7304,15 +7497,27 @@ var VfsNodes = class {
7304
7497
  node.replace(contents2);
7305
7498
  }
7306
7499
  node.opens++;
7500
+ if (writable) node.writers++;
7307
7501
  return node;
7308
7502
  }
7309
7503
  holds(prefix) {
7310
7504
  for (const path of this.byPath.keys()) if (within4(path, prefix)) return true;
7311
7505
  return false;
7312
7506
  }
7313
- closed(node) {
7507
+ writes(prefix) {
7508
+ for (const [path, node] of this.byPath)
7509
+ if (node.writers > 0 && within4(path, prefix)) return true;
7510
+ return false;
7511
+ }
7512
+ closed(node, writable = false) {
7314
7513
  node.opens--;
7315
- if (node.opens === 0 && this.byPath.get(node.path) === node) this.byPath.delete(node.path);
7514
+ if (writable) node.writers--;
7515
+ if (node.opens > 0) {
7516
+ if (writable && node.writers === 0) this.onClosed?.(node.path);
7517
+ return;
7518
+ }
7519
+ if (this.byPath.get(node.path) === node) this.byPath.delete(node.path);
7520
+ this.onClosed?.(node.path);
7316
7521
  }
7317
7522
  async flush(path) {
7318
7523
  for (const [p, node] of this.byPath) if (within4(p, path)) await node.serial(() => node.flush());
@@ -7371,10 +7576,9 @@ function parentOf5(path) {
7371
7576
  async function linkOf(fs, path) {
7372
7577
  return fs.readlink?.(path).catch(() => void 0);
7373
7578
  }
7374
- async function realDir(fs, dir) {
7579
+ async function realDir(fs, dir, budget = { hops: 0 }) {
7375
7580
  const queue = dir.split("/").filter(Boolean);
7376
7581
  let at = "/";
7377
- let hops = 0;
7378
7582
  while (queue.length > 0) {
7379
7583
  const part = queue.shift();
7380
7584
  if (part === ".") continue;
@@ -7383,28 +7587,41 @@ async function realDir(fs, dir) {
7383
7587
  continue;
7384
7588
  }
7385
7589
  const next = at === "/" ? `/${part}` : `${at}/${part}`;
7386
- const link2 = hops < MAX_LINKS5 ? await linkOf(fs, next) : void 0;
7590
+ const link2 = await linkOf(fs, next);
7387
7591
  if (link2 === void 0) {
7388
7592
  at = next;
7389
7593
  continue;
7390
7594
  }
7391
- hops++;
7595
+ if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", dir);
7596
+ budget.hops++;
7392
7597
  queue.unshift(...link2.split("/").filter(Boolean));
7393
7598
  if (link2.startsWith("/")) at = "/";
7394
7599
  }
7395
7600
  return at;
7396
7601
  }
7397
- async function targetDir(fs, path) {
7398
- let dir = await realDir(fs, parentOf5(path));
7602
+ function errCode(err) {
7603
+ return err?.code;
7604
+ }
7605
+ function joinName(dir, name2) {
7606
+ return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
7607
+ }
7608
+ async function realTarget(fs, path) {
7609
+ const budget = { hops: 0 };
7610
+ let dir = await realDir(fs, parentOf5(path), budget);
7399
7611
  let name2 = path.slice(path.lastIndexOf("/") + 1);
7400
- for (let hops = 0; hops < MAX_LINKS5; hops++) {
7401
- const link2 = await linkOf(fs, dir === "/" ? `/${name2}` : `${dir}/${name2}`);
7402
- if (link2 === void 0) return dir;
7403
- const target = link2.startsWith("/") ? link2 : `${dir}/${link2}`;
7404
- dir = await realDir(fs, parentOf5(target));
7612
+ for (; ; ) {
7613
+ const link2 = await linkOf(fs, joinName(dir, name2));
7614
+ if (link2 === void 0) break;
7615
+ if (budget.hops >= MAX_LINKS5) throw fsError("ELOOP", path);
7616
+ budget.hops++;
7617
+ const target = link2.startsWith("/") ? link2 : joinName(dir, link2);
7618
+ dir = await realDir(fs, parentOf5(target), budget);
7405
7619
  name2 = target.slice(target.lastIndexOf("/") + 1);
7406
7620
  }
7407
- return dir;
7621
+ return joinName(dir, name2);
7622
+ }
7623
+ async function targetDir(fs, path) {
7624
+ return parentOf5(await realTarget(fs, path).catch(() => path));
7408
7625
  }
7409
7626
  async function refuseReadonly(fs, path, flags, opts = {}) {
7410
7627
  const writes = (flags & O_ACCMODE) !== 0 || opts.truncate === true;
@@ -7423,13 +7640,14 @@ function keepingOpen(fs, nodes) {
7423
7640
  return {
7424
7641
  ...fs,
7425
7642
  async rm(path, options) {
7426
- const at = fs.resolvePath("/", path);
7643
+ const at = await nodes.entryKey(fs.resolvePath("/", path));
7427
7644
  await nodes.unlinking(at);
7428
7645
  await fs.rm(path, options);
7429
7646
  nodes.unlinked(at);
7430
7647
  },
7431
- rename(from, to) {
7432
- const [a, b] = [fs.resolvePath("/", from), fs.resolvePath("/", to)];
7648
+ async rename(from, to) {
7649
+ const a = await nodes.entryKey(fs.resolvePath("/", from));
7650
+ const b = await nodes.entryKey(fs.resolvePath("/", to));
7433
7651
  return nodes.onEntry(b, async () => {
7434
7652
  await nodes.unlinking(b);
7435
7653
  await fs.rename(from, to);
@@ -7445,7 +7663,7 @@ function vfsFile(fs, opts, nodes) {
7445
7663
  const access = opts.flags & O_ACCMODE;
7446
7664
  const readable = access !== O_WRONLY;
7447
7665
  const writable = access === O_WRONLY || access === O_RDWR;
7448
- 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);
7666
+ 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);
7449
7667
  let openError;
7450
7668
  const atOpen = (op) => {
7451
7669
  void node.serial(op).catch((err) => {
@@ -7508,7 +7726,7 @@ function vfsFile(fs, opts, nodes) {
7508
7726
  takeOpenError();
7509
7727
  await node.flush();
7510
7728
  } finally {
7511
- nodes?.closed(node);
7729
+ nodes?.closed(node, writable);
7512
7730
  }
7513
7731
  })
7514
7732
  });
@@ -7575,10 +7793,12 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
7575
7793
  "fd-cloexec",
7576
7794
  "fd-list",
7577
7795
  "proc-alarm",
7796
+ "proc-umask",
7578
7797
  "dl-log",
7579
7798
  "fd-renumber",
7580
7799
  "fd-promote",
7581
7800
  "fd-open-tty",
7801
+ "fd-tty-names",
7582
7802
  "tty-get",
7583
7803
  "tty-set",
7584
7804
  "tty-winsz",
@@ -7612,6 +7832,7 @@ function isWasmSyscall(req) {
7612
7832
  return typeof op === "string" && SYSCALL_OPS.has(op);
7613
7833
  }
7614
7834
  var MAX_READ = 1024 * 1024;
7835
+ var DEFAULT_UMASK = 18;
7615
7836
  var DYING = { ok: false, errno: "EINTR", message: "the process is being killed" };
7616
7837
  var HOSTS_ONLY = new Resolver({ routes: new Routes() });
7617
7838
  var WasmProcess = class {
@@ -7622,6 +7843,7 @@ var WasmProcess = class {
7622
7843
  inherited = 0;
7623
7844
  interrupt = new AbortController();
7624
7845
  dying = false;
7846
+ umask = DEFAULT_UMASK;
7625
7847
  execChild;
7626
7848
  execTermsig;
7627
7849
  stopped = 0;
@@ -7639,6 +7861,7 @@ var WasmProcess = class {
7639
7861
  this.fds = fds;
7640
7862
  this.options = options;
7641
7863
  this.inherited = options.ignored ?? 0;
7864
+ this.umask = options.umask ?? DEFAULT_UMASK;
7642
7865
  this.children = new ChildTable(fds, options.spawner, options.forker);
7643
7866
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
7644
7867
  if (!options.nodes) options.openFiles?.add(this.nodes);
@@ -7711,6 +7934,11 @@ var WasmProcess = class {
7711
7934
  return result;
7712
7935
  }
7713
7936
  }
7937
+ setUmask(mask) {
7938
+ const old = this.umask;
7939
+ if (mask !== void 0) this.umask = mask & 511;
7940
+ return old;
7941
+ }
7714
7942
  die() {
7715
7943
  if (this.dying) return;
7716
7944
  this.dying = true;
@@ -7781,9 +8009,21 @@ var WasmProcess = class {
7781
8009
  await refuseReadonly(this.options.fs, req.path, req.flags, req);
7782
8010
  const fd = this.fds.install(heldFile(), 3);
7783
8011
  try {
7784
- if (req.exclusive) await this.nodes.createExclusive(req.path);
7785
- this.fds.installAt(fd, this.openVfsFile(req));
7786
- if (req.existing && !await present(this.options.fs, req.path, true)) {
8012
+ const nodes = this.nodes;
8013
+ const path = req.orphan ? req.path : req.exclusive ? await nodes.entryKey(req.path) : await nodes.targetKey(req.path);
8014
+ const open = { ...req, path };
8015
+ if (req.exclusive) {
8016
+ await nodes.createExclusive(path);
8017
+ this.fds.installAt(fd, this.openVfsFile(open));
8018
+ } else if (req.create) {
8019
+ await nodes.onEntry(path, async () => {
8020
+ this.fds.installAt(fd, this.openVfsFile(open));
8021
+ await this.fds.get(fd).file.stat?.();
8022
+ });
8023
+ } else {
8024
+ this.fds.installAt(fd, this.openVfsFile(open));
8025
+ }
8026
+ if (req.existing && !await present(this.options.fs, path, true)) {
7787
8027
  throw new KernelError("ENOENT");
7788
8028
  }
7789
8029
  } catch (err) {
@@ -7862,7 +8102,7 @@ var WasmProcess = class {
7862
8102
  case "fd-reserve":
7863
8103
  return { ok: true, kind: "json", json: this.reserve(req.fd, req.min, req.meta) };
7864
8104
  case "fd-promote":
7865
- this.promote(req);
8105
+ await this.promote(req);
7866
8106
  return { ok: true, kind: "void" };
7867
8107
  case "fd-renumber":
7868
8108
  if (req.from !== req.to) {
@@ -7875,6 +8115,8 @@ var WasmProcess = class {
7875
8115
  if (!tty) throw new KernelError("ENXIO");
7876
8116
  return { ok: true, kind: "json", json: this.fds.install(tty.file(), 3) };
7877
8117
  }
8118
+ case "fd-tty-names":
8119
+ return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
7878
8120
  case "fd-select": {
7879
8121
  const { read, write, timeoutMs } = req;
7880
8122
  const signal = this.blockingSignal();
@@ -7896,10 +8138,14 @@ var WasmProcess = class {
7896
8138
  async vfsSyscall(req) {
7897
8139
  if ("path" in req || "from" in req) {
7898
8140
  const nodes = this.nodes;
7899
- if (req.op === "fd-path-flush") await nodes.flush(req.path);
7900
- else if (req.op === "fd-path-unlinking") await nodes.unlinking(req.path);
7901
- else if (req.op === "fd-path-unlinked") nodes.unlinked(req.path);
7902
- else if (req.op === "fd-path-renamed") nodes.renamed(req.from, req.to);
8141
+ if (req.op === "fd-path-flush") {
8142
+ await nodes.flush(await nodes.targetKey(req.path).catch(() => req.path));
8143
+ } else if (req.op === "fd-path-unlinking")
8144
+ await nodes.unlinking(await nodes.entryKey(req.path));
8145
+ else if (req.op === "fd-path-unlinked") nodes.unlinked(await nodes.entryKey(req.path));
8146
+ else if (req.op === "fd-path-renamed") {
8147
+ nodes.renamed(await nodes.entryKey(req.from), await nodes.entryKey(req.to));
8148
+ }
7903
8149
  return { ok: true, kind: "void" };
7904
8150
  }
7905
8151
  const file = this.fds.get(req.fd).file;
@@ -7920,17 +8166,19 @@ var WasmProcess = class {
7920
8166
  return { ok: true, kind: "json", json: await file.stat() };
7921
8167
  }
7922
8168
  }
7923
- promote(req) {
8169
+ async promote(req) {
7924
8170
  if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
7925
8171
  if (req.share !== void 0) {
7926
8172
  this.fds.dup2(req.share, req.fd);
7927
8173
  return;
7928
8174
  }
7929
8175
  if (req.path === void 0) throw new KernelError("EINVAL");
8176
+ const path = req.orphan ? req.path : await this.nodes.targetKey(req.path);
8177
+ if (!this.fds.get(req.fd).file.held) throw new KernelError("EBADF");
7930
8178
  const file = vfsFile(
7931
8179
  this.options.fs,
7932
8180
  {
7933
- path: req.path,
8181
+ path,
7934
8182
  flags: req.flags ?? 0,
7935
8183
  position: req.position ?? 0,
7936
8184
  ...req.contents !== void 0 ? { contents: req.contents } : {},
@@ -8106,6 +8354,8 @@ var WasmProcess = class {
8106
8354
  case "proc-alarm":
8107
8355
  this.setAlarm(req.sig, req.firstMs ?? req.ms, req.repeat ? req.ms : 0, req.timer);
8108
8356
  return { ok: true, kind: "void" };
8357
+ case "proc-umask":
8358
+ return { ok: true, kind: "json", json: this.setUmask(req.mask) };
8109
8359
  case "dl-log":
8110
8360
  if (req.append) this.dlLog.push(req.append);
8111
8361
  return { ok: true, kind: "json", json: this.dlLog.slice(req.from) };
@@ -8255,7 +8505,7 @@ var Dying = class {
8255
8505
  return [typeof reported === "number" ? reported : CRASHED, void 0];
8256
8506
  }
8257
8507
  };
8258
- function processInit(opts, sab) {
8508
+ function processInit(opts, sab, umask) {
8259
8509
  const base = {
8260
8510
  type: WASM_PROCESS_INIT,
8261
8511
  pid: opts.shownPid ?? opts.pid,
@@ -8264,6 +8514,7 @@ function processInit(opts, sab) {
8264
8514
  env: opts.env,
8265
8515
  cwd: opts.cwd,
8266
8516
  sab,
8517
+ umask,
8267
8518
  ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {}
8268
8519
  };
8269
8520
  if (opts.program.abi === "js") {
@@ -8275,11 +8526,17 @@ function processInit(opts, sab) {
8275
8526
  ...opts.fork ? { fork: opts.fork } : { fds: inheritedFds(opts.fds) }
8276
8527
  };
8277
8528
  }
8529
+ function inherited(opts) {
8530
+ return {
8531
+ ...opts.ignored ? { ignored: opts.ignored } : {},
8532
+ ...opts.umask !== void 0 ? { umask: opts.umask } : {}
8533
+ };
8534
+ }
8278
8535
  function spawnWasmProcess(opts) {
8279
8536
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
8280
8537
  const header3 = new Int32Array(sab, 0, SAB_HEADER_I32);
8281
8538
  const process = new WasmProcess(opts.pid, opts.fds, {
8282
- ...opts.ignored ? { ignored: opts.ignored } : {},
8539
+ ...inherited(opts),
8283
8540
  ...opts.identity ? { identity: opts.identity } : {},
8284
8541
  ...opts.onSyscall ? { onSyscall: opts.onSyscall } : {},
8285
8542
  spawner: opts.spawner,
@@ -8413,7 +8670,7 @@ function spawnWasmProcess(opts) {
8413
8670
  };
8414
8671
  tw.postMessage(tinit);
8415
8672
  };
8416
- const init = processInit(opts, sab);
8673
+ const init = processInit(opts, sab, process.umask);
8417
8674
  worker.postMessage(init, opts.fork ? [opts.fork.memory.buffer] : []);
8418
8675
  const signal = (sig) => {
8419
8676
  if (process.signal(sig) !== "terminate") return;
@@ -8431,6 +8688,7 @@ function spawnWasmProcess(opts) {
8431
8688
  onState: (listener) => process.onState(listener),
8432
8689
  memory: () => Atomics.load(header3, SAB_I_MEMORY) * 65536,
8433
8690
  ignoredSignals: (fork) => process.inheritable(fork),
8691
+ umask: () => process.umask,
8434
8692
  writesBack: () => opts.program.abi === "wasi" && !done4
8435
8693
  };
8436
8694
  }
@@ -8536,8 +8794,13 @@ var JobTable = class {
8536
8794
  return id;
8537
8795
  }
8538
8796
  terminalNamed(name2) {
8539
- const known = /* @__PURE__ */ new Set([...this.terminals.values(), ...this.foreground.keys()]);
8540
- return [...known].find((tty) => tty.name === name2);
8797
+ return this.knownTerminals().find((tty) => tty.name === name2);
8798
+ }
8799
+ terminalNames() {
8800
+ return this.knownTerminals().flatMap((tty) => tty.name ? [tty.name] : []);
8801
+ }
8802
+ knownTerminals() {
8803
+ return [.../* @__PURE__ */ new Set([...this.terminals.values(), ...this.foreground.keys()])];
8541
8804
  }
8542
8805
  controllingTerminal(pid) {
8543
8806
  const member = this.members.get(pid);
@@ -8810,7 +9073,11 @@ function mediumSlot(lost, onLost) {
8810
9073
  }
8811
9074
 
8812
9075
  // src/mount/fsa.ts
8813
- var FSA_CAPABILITIES = { listingStats: true };
9076
+ var FSA_CAPABILITIES = {
9077
+ listingStats: true,
9078
+ ranges: true,
9079
+ sessions: true
9080
+ };
8814
9081
  var codeOf2 = (err) => err?.code;
8815
9082
  var denied = (err) => codeOf2(err) === "EACCES" || err?.name === "NotAllowedError";
8816
9083
  function attrOf(st) {
@@ -8850,30 +9117,20 @@ function kernelFsHandlers(fs) {
8850
9117
  if (st && flags.exclusive) throw fsError2("EEXIST", path);
8851
9118
  if (st?.isDirectory && flags.write) throw fsError2("EISDIR", path);
8852
9119
  if (!st && !flags.create) throw fsError2("ENOENT", path);
8853
- const data = st && !flags.truncate ? await fs.readFileBuffer(path) : new Uint8Array(0);
8854
- handles.set(++nextFh, { path, data, length: data.length, dirty: !st || flags.truncate });
9120
+ if (!st || flags.truncate) await fs.writeFile(path, new Uint8Array(0));
9121
+ handles.set(++nextFh, { path });
8855
9122
  return nextFh;
8856
9123
  },
8857
- async read(fh, offset, size) {
8858
- const h = handle(fh);
8859
- return h.data.slice(offset, Math.min(h.length, offset + size));
8860
- },
9124
+ read: async (fh, offset, size) => fs.pread(handle(fh).path, offset, size),
8861
9125
  async write(fh, offset, bytes) {
8862
9126
  const h = handle(fh);
8863
- const end = offset + bytes.length;
8864
- if (end > h.data.length) {
8865
- const grown = new Uint8Array(Math.max(end, h.data.length * 2));
8866
- grown.set(h.data.subarray(0, h.length));
8867
- h.data = grown;
8868
- }
8869
- h.data.set(bytes, offset);
8870
- h.length = Math.max(h.length, end);
8871
- h.dirty = true;
9127
+ h.session ??= fs.writeSession(h.path, false);
9128
+ await (await h.session).write(offset, bytes);
8872
9129
  },
8873
9130
  async release(fh) {
8874
9131
  const h = handle(fh);
8875
9132
  handles.delete(fh);
8876
- if (h.dirty) await fs.writeFile(h.path, h.data.slice(0, h.length));
9133
+ if (h.session !== void 0) await (await h.session).close();
8877
9134
  },
8878
9135
  mkdir: (path) => fs.mkdir(path),
8879
9136
  async rmdir(path) {
@@ -8889,6 +9146,13 @@ function kernelFsHandlers(fs) {
8889
9146
  symlink: (target, path) => fs.symlink(target, path),
8890
9147
  readlink: (path) => fs.readlink(path),
8891
9148
  async setattr(path, change) {
9149
+ if (change.size !== void 0) {
9150
+ const open = [...handles.values()].find(
9151
+ (h) => h.path === path && h.session !== void 0
9152
+ )?.session;
9153
+ if (open !== void 0) await (await open).truncate(change.size);
9154
+ else await fs.truncate?.(path, change.size);
9155
+ }
8892
9156
  if (change.mode !== void 0) await fs.chmod(path, change.mode);
8893
9157
  if (change.mtime !== void 0) {
8894
9158
  await fs.utimes(path, new Date(change.mtime), new Date(change.mtime));
@@ -9613,6 +9877,7 @@ function tmpfs() {
9613
9877
  const { dir, name: name2 } = parent(path);
9614
9878
  if (dir.children.has(name2)) throw fsError2("EEXIST", path);
9615
9879
  dir.children.set(name2, make("directory", 16877));
9880
+ dir.mtime = Date.now();
9616
9881
  },
9617
9882
  async rmdir(path) {
9618
9883
  const { dir, name: name2 } = parent(path);
@@ -9621,6 +9886,7 @@ function tmpfs() {
9621
9886
  if (node.kind !== "directory") throw fsError2("ENOTDIR", path);
9622
9887
  if (node.children.size > 0) throw fsError2("ENOTEMPTY", path);
9623
9888
  dir.children.delete(name2);
9889
+ dir.mtime = Date.now();
9624
9890
  },
9625
9891
  async unlink(path) {
9626
9892
  const { dir, name: name2 } = parent(path);
@@ -9628,6 +9894,7 @@ function tmpfs() {
9628
9894
  if (!node) throw fsError2("ENOENT", path);
9629
9895
  if (node.kind === "directory") throw fsError2("EISDIR", path);
9630
9896
  dir.children.delete(name2);
9897
+ dir.mtime = Date.now();
9631
9898
  },
9632
9899
  async rename(from, to) {
9633
9900
  const a = `/${parts(from).join("/")}`;
@@ -9644,6 +9911,8 @@ function tmpfs() {
9644
9911
  }
9645
9912
  source.dir.children.delete(source.name);
9646
9913
  target.dir.children.set(target.name, node);
9914
+ source.dir.mtime = Date.now();
9915
+ target.dir.mtime = source.dir.mtime;
9647
9916
  },
9648
9917
  async symlink(target, path) {
9649
9918
  const { dir, name: name2 } = parent(path);
@@ -9651,6 +9920,7 @@ function tmpfs() {
9651
9920
  const node = make("symlink", 41471);
9652
9921
  node.target = target;
9653
9922
  dir.children.set(name2, node);
9923
+ dir.mtime = Date.now();
9654
9924
  },
9655
9925
  async readlink(path) {
9656
9926
  const node = find(path);
@@ -9949,7 +10219,7 @@ var Launcher = class {
9949
10219
  deciding = /* @__PURE__ */ new Map();
9950
10220
  identities = { asked: 0, timedOut: 0 };
9951
10221
  nodes;
9952
- held = /* @__PURE__ */ new Set();
10222
+ held = new HeldSet((path) => this.released(path));
9953
10223
  processMounts;
9954
10224
  booting = new AbortController();
9955
10225
  fstab = Promise.resolve([]);
@@ -9991,7 +10261,7 @@ var Launcher = class {
9991
10261
  });
9992
10262
  enableCdp(this.net, this.cdp);
9993
10263
  const fs = withCommandDirs(this.base, async () => new Set((await this.commands()).keys()));
9994
- this.nodes = new VfsNodes(fs);
10264
+ this.nodes = new VfsNodes(fs, (path) => this.released(path));
9995
10265
  this.openFiles.add(this.nodes);
9996
10266
  this.fs = keepingOpen(fs, this.nodes);
9997
10267
  this.watchers.watch([this.modulesDir, this.pnpmHome], { recursive: true }, () => {
@@ -10180,6 +10450,7 @@ var Launcher = class {
10180
10450
  this.settling.syscall(pid, call.op);
10181
10451
  },
10182
10452
  ...req.ignored ? { ignored: req.ignored } : {},
10453
+ ...req.umask !== void 0 ? { umask: req.umask } : {},
10183
10454
  program: req.program,
10184
10455
  argv0: req.argv0,
10185
10456
  args: req.args,
@@ -10271,7 +10542,8 @@ var Launcher = class {
10271
10542
  ...described,
10272
10543
  started: (this.described.get(root.pid) ?? described).started,
10273
10544
  state: this.processes.has(member.pid) ? "S" : "Z",
10274
- memory: this.processes.get(member.pid)?.memory() ?? 0
10545
+ memory: this.processes.get(member.pid)?.memory() ?? 0,
10546
+ umask: this.umaskOf(member.pid)
10275
10547
  });
10276
10548
  }
10277
10549
  return listed;
@@ -10293,6 +10565,7 @@ var Launcher = class {
10293
10565
  report,
10294
10566
  ppid,
10295
10567
  ignored: this.ignoredBy(ppid),
10568
+ umask: this.umaskOf(ppid),
10296
10569
  ...exec
10297
10570
  });
10298
10571
  deciding?.attach();
@@ -10337,7 +10610,8 @@ var Launcher = class {
10337
10610
  fds,
10338
10611
  ppid,
10339
10612
  fork: state,
10340
- ignored: this.ignoredBy(ppid, true)
10613
+ ignored: this.ignoredBy(ppid, true),
10614
+ umask: this.umaskOf(ppid)
10341
10615
  })
10342
10616
  );
10343
10617
  }
@@ -10346,6 +10620,14 @@ var Launcher = class {
10346
10620
  const foreground = () => this.jobs.tcgetpgrp(tty, fallback);
10347
10621
  this.settling.deliver(sig, pgid, send, tty.reading ? void 0 : foreground);
10348
10622
  }
10623
+ umaskOf(pid) {
10624
+ return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
10625
+ }
10626
+ released(path) {
10627
+ const at = normalizePath(path);
10628
+ const open = [...this.openFiles].some((nodes) => nodes.writes(at)) || heldUnder(this.held, at);
10629
+ if (!open) void this.mounts.commit(at).catch(() => void 0);
10630
+ }
10349
10631
  ignoredBy(pid, fork = false) {
10350
10632
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
10351
10633
  }