@ai-ecoverse/slicc-kernel 1.83.0 → 1.84.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/node.js CHANGED
@@ -448,8 +448,8 @@ async function inParent(g, op, path, old = false) {
448
448
  if (!old) return void await need(g, op, parentOf(path), W_OK | X_OK, DIR, path);
449
449
  const dir = await access(g.fs, g.cred, parentOf(path), W_OK | X_OK, DIR);
450
450
  if (dir.denied === void 0) return stickyEntry(g, op, path, dir);
451
- const lookup2 = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
452
- if (!lookup2 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
451
+ const lookup3 = dir.denied === dir.path && dir.perm && permits(g.cred, dir.perm, X_OK);
452
+ if (!lookup3 || await present(g.fs, entryIn(dir, path), false)) deny(g, op, path);
453
453
  }
454
454
  async function makeIn(g, op, path) {
455
455
  for (let dir = parentOf(path); ; dir = parentOf(dir)) {
@@ -3022,6 +3022,9 @@ var MemoryMeta = class {
3022
3022
  async remove(paths) {
3023
3023
  for (const path of paths) this.entries.deleteTree(path);
3024
3024
  }
3025
+ absent(path) {
3026
+ return !this.entries.has(path);
3027
+ }
3025
3028
  async links(dir) {
3026
3029
  return [...this.entries.entries()].map(([, e]) => e).filter((entry) => entry.dir === dir);
3027
3030
  }
@@ -3467,10 +3470,17 @@ function translate(err, path, op = "lookup", mapped = true) {
3467
3470
  var META_OPS = ["get", "update", "move", "remove", "links", "under", "all"];
3468
3471
  function sidecar(meta) {
3469
3472
  const pathOf = ([first]) => [first, "/"].flat().find((p) => typeof p === "string");
3470
- const guarded2 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
3473
+ const guarded3 = (op) => (...args) => meta[op].apply(meta, args).catch((err) => {
3471
3474
  throw translate(err, pathOf(args), "sidecar", false);
3472
3475
  });
3473
- return Object.fromEntries(META_OPS.map((op) => [op, guarded2(op)]));
3476
+ const ops = Object.fromEntries(META_OPS.map((op) => [op, guarded3(op)]));
3477
+ if (meta.absent) ops.absent = meta.absent.bind(meta);
3478
+ const { removeFile } = meta;
3479
+ if (removeFile)
3480
+ ops.removeFile = (path) => removeFile.call(meta, path).catch((err) => {
3481
+ throw translate(err, path, "sidecar", false);
3482
+ });
3483
+ return ops;
3474
3484
  }
3475
3485
  function missing2(err) {
3476
3486
  const code = err?.code;
@@ -4163,6 +4173,7 @@ var OpfsFs = class {
4163
4173
  throw error;
4164
4174
  }
4165
4175
  this.refuse(found.path, true);
4176
+ if (options.file && found.handle?.kind === "directory") throw fsError("EISDIR", path);
4166
4177
  if (found.handle) {
4167
4178
  const [dir, name2] = await this.parent(found.path);
4168
4179
  try {
@@ -4173,8 +4184,19 @@ var OpfsFs = class {
4173
4184
  } catch (err) {
4174
4185
  throw translate(err, path);
4175
4186
  }
4176
- this.changed(found.path);
4187
+ const file = found.handle.kind === "file";
4188
+ if (!file || this.remote?.watched()) this.changed(found.path);
4189
+ else this.forget(found.path);
4177
4190
  this.movePending(found.path);
4191
+ if (file && this.meta.absent?.(found.path)) {
4192
+ this.touch(parentOf4(found.path));
4193
+ return;
4194
+ }
4195
+ if (file && this.meta.removeFile) {
4196
+ await this.meta.removeFile(found.path);
4197
+ this.touch(parentOf4(found.path));
4198
+ return;
4199
+ }
4178
4200
  }
4179
4201
  await this.meta.remove([found.path]);
4180
4202
  this.touch(parentOf4(found.path));
@@ -5907,9 +5929,13 @@ function withCommandDirs(fs, names, userDir = async () => void 0, owners = false
5907
5929
  // src/fs/fresh-modules.ts
5908
5930
  var MAGIC = [0, 97, 115, 109];
5909
5931
  var MAX_FRESH = 65536;
5932
+ var DEFAULT_UMASK = 18;
5910
5933
  function isModule(bytes) {
5911
5934
  return MAGIC.every((b, i) => bytes[i] === b);
5912
5935
  }
5936
+ function within4(path, dir) {
5937
+ return path.startsWith(dir === "/" ? "/" : `${dir}/`);
5938
+ }
5913
5939
  var FreshModules = class {
5914
5940
  fresh = /* @__PURE__ */ new Map();
5915
5941
  add(path, umask, pid) {
@@ -5917,17 +5943,40 @@ var FreshModules = class {
5917
5943
  this.fresh.delete(key);
5918
5944
  const oldest = this.fresh.size >= MAX_FRESH ? this.fresh.keys().next().value : void 0;
5919
5945
  if (oldest !== void 0) this.fresh.delete(oldest);
5920
- this.fresh.set(key, { umask: umask & 511, pid, added: 0 });
5946
+ const mask = umask & 511;
5947
+ this.fresh.set(key, { umask: mask, pid, added: 0, setup: mask !== DEFAULT_UMASK });
5921
5948
  }
5922
5949
  forget(pid) {
5923
5950
  for (const [path, fresh2] of this.fresh) if (fresh2.pid === pid) this.fresh.delete(path);
5924
5951
  }
5952
+ drop(fresh2) {
5953
+ for (const [key, note] of [...this.fresh]) if (note === fresh2) this.fresh.delete(key);
5954
+ }
5955
+ moved(from, to) {
5956
+ const source = normalizePath(from);
5957
+ const target = normalizePath(to);
5958
+ if (source === target) return;
5959
+ for (const key of [...this.fresh.keys()]) {
5960
+ if (key === target || within4(key, target)) this.fresh.delete(key);
5961
+ }
5962
+ for (const [key, fresh2] of [...this.fresh]) {
5963
+ if (key !== source && !within4(key, source)) continue;
5964
+ this.fresh.delete(key);
5965
+ this.fresh.set(target + key.slice(source.length), fresh2);
5966
+ }
5967
+ }
5925
5968
  wrap(fs) {
5926
- const decide = async (given, bytes) => {
5969
+ const head = async (path) => {
5970
+ if (fs.pread) return fs.pread(path, 0, MAGIC.length);
5971
+ return (await fs.readFileBuffer(path)).subarray(0, MAGIC.length);
5972
+ };
5973
+ const decide = async (given, known) => {
5927
5974
  const path = normalizePath(given);
5928
5975
  const fresh2 = this.fresh.get(path);
5929
- if (!fresh2 || typeof bytes === "string" || bytes.length < MAGIC.length) return;
5976
+ if (!fresh2) return;
5977
+ const bytes = known && known.length >= MAGIC.length ? known : await head(path);
5930
5978
  const want = isModule(bytes) ? 73 & ~fresh2.umask : 0;
5979
+ fresh2.setup = false;
5931
5980
  if (want === fresh2.added) return;
5932
5981
  const mode = (await fs.stat(path)).mode & 4095;
5933
5982
  await fs.chmod(path, mode & ~fresh2.added | want);
@@ -5936,32 +5985,47 @@ var FreshModules = class {
5936
5985
  const overrides = {
5937
5986
  writeFile: async (path, content, owner) => {
5938
5987
  await fs.writeFile(path, content, owner);
5939
- await decide(path, content);
5988
+ if (this.fresh.has(normalizePath(path))) {
5989
+ await decide(
5990
+ path,
5991
+ typeof content === "string" ? new TextEncoder().encode(content) : content
5992
+ );
5993
+ }
5940
5994
  },
5941
5995
  chmod: async (path, mode) => {
5942
5996
  const fresh2 = this.fresh.get(normalizePath(path));
5943
- const creating = fresh2?.added === 0 && (mode & 4095) === (438 & ~fresh2.umask);
5944
- if (!creating) this.fresh.delete(normalizePath(path));
5997
+ if (fresh2?.setup && (mode & 4095) === (438 & ~fresh2.umask)) fresh2.setup = false;
5998
+ else if (fresh2) this.drop(fresh2);
5945
5999
  await fs.chmod(path, mode);
5946
6000
  },
5947
6001
  rm: async (path, options) => {
5948
- this.fresh.delete(normalizePath(path));
5949
6002
  await fs.rm(path, options);
6003
+ const at = normalizePath(path);
6004
+ for (const key of [...this.fresh.keys()]) {
6005
+ if (key === at || within4(key, at)) this.fresh.delete(key);
6006
+ }
5950
6007
  },
5951
6008
  rename: async (from, to) => {
5952
6009
  await fs.rename(from, to);
5953
- const fresh2 = this.fresh.get(normalizePath(from));
5954
- this.fresh.delete(normalizePath(from));
5955
- this.fresh.delete(normalizePath(to));
6010
+ this.moved(from, to);
6011
+ },
6012
+ link: async (from, to, follow2) => {
6013
+ await fs.link(from, to, follow2);
6014
+ const source = follow2 && fs.realpath ? await fs.realpath(from, true) : from;
6015
+ const fresh2 = this.fresh.get(normalizePath(source));
5956
6016
  if (fresh2) this.fresh.set(normalizePath(to), fresh2);
5957
6017
  }
5958
6018
  };
5959
6019
  const ranged2 = rangedOps(fs);
5960
6020
  if (ranged2) {
5961
6021
  overrides.pwrite = async (path, at, bytes, transfer) => {
5962
- const head = at === 0 && this.fresh.has(normalizePath(path)) ? bytes.slice(0, 4) : void 0;
6022
+ const touches2 = at < MAGIC.length && this.fresh.has(normalizePath(path));
5963
6023
  await ranged2.pwrite(path, at, bytes, transfer);
5964
- if (head) await decide(path, head);
6024
+ if (touches2) await decide(path);
6025
+ };
6026
+ overrides.truncate = async (path, size) => {
6027
+ await ranged2.truncate(path, size);
6028
+ if (size < MAGIC.length && this.fresh.has(normalizePath(path))) await decide(path);
5965
6029
  };
5966
6030
  }
5967
6031
  const bound2 = /* @__PURE__ */ new WeakMap();
@@ -6701,7 +6765,7 @@ function parseSize(text) {
6701
6765
  if (!match) throw errnoError("EINVAL", `not a size: ${text}`);
6702
6766
  return Math.floor(Number(match[1]) * UNITS[match[2].toLowerCase()]);
6703
6767
  }
6704
- var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
6768
+ var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
6705
6769
  var HeldSet = class extends Set {
6706
6770
  idle;
6707
6771
  constructor(idle) {
@@ -6720,7 +6784,7 @@ var HeldSet = class extends Set {
6720
6784
  }
6721
6785
  };
6722
6786
  function heldUnder(held, target) {
6723
- for (const paths of held) for (const path of paths.keys()) if (within4(path, target)) return true;
6787
+ for (const paths of held) for (const path of paths.keys()) if (within5(path, target)) return true;
6724
6788
  return false;
6725
6789
  }
6726
6790
  var HeldPaths = class extends Map {
@@ -6836,24 +6900,24 @@ var HeldPaths = class extends Map {
6836
6900
  return this.owning === path;
6837
6901
  }
6838
6902
  revoke(prefix) {
6839
- for (const path of this.keys()) if (within4(path, prefix)) this.revoked.add(path);
6903
+ for (const path of this.keys()) if (within5(path, prefix)) this.revoked.add(path);
6840
6904
  }
6841
6905
  renamed(from, to) {
6842
6906
  for (const [path, count2] of [...this]) {
6843
- if (!within4(path, from)) continue;
6907
+ if (!within5(path, from)) continue;
6844
6908
  const moved2 = to + path.slice(from.length);
6845
6909
  this.delete(path);
6846
6910
  this.set(moved2, (this.get(moved2) ?? 0) + count2);
6847
6911
  if (this.revoked.delete(path)) this.revoked.add(moved2);
6848
6912
  }
6849
6913
  for (const [path, count2] of [...this.opens]) {
6850
- if (!within4(path, from)) continue;
6914
+ if (!within5(path, from)) continue;
6851
6915
  const moved2 = to + path.slice(from.length);
6852
6916
  this.opens.delete(path);
6853
6917
  this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count2);
6854
6918
  }
6855
6919
  for (const [id2, key] of this.handles) {
6856
- if (within4(key, from)) this.handles.set(id2, to + key.slice(from.length));
6920
+ if (within5(key, from)) this.handles.set(id2, to + key.slice(from.length));
6857
6921
  }
6858
6922
  }
6859
6923
  isRevoked(path) {
@@ -6957,10 +7021,14 @@ var MountTable = class {
6957
7021
  ...error ? { error } : {}
6958
7022
  });
6959
7023
  }
7024
+ under(path) {
7025
+ for (const mount of this.mounts.values()) if (within5(mount.target, path)) return true;
7026
+ return false;
7027
+ }
6960
7028
  locate(path) {
6961
7029
  let best;
6962
7030
  for (const mount of this.mounts.values()) {
6963
- if (within4(path, mount.target) && (!best || mount.target.length > best.target.length))
7031
+ if (within5(path, mount.target) && (!best || mount.target.length > best.target.length))
6964
7032
  best = mount;
6965
7033
  }
6966
7034
  if (!best) return void 0;
@@ -7103,7 +7171,7 @@ var MountTable = class {
7103
7171
  async commitUnder(prefix) {
7104
7172
  const at = normalizePath(prefix);
7105
7173
  await Promise.all(
7106
- [...this.sessions.keys()].filter((p) => within4(p, at)).map((p) => this.commit(p))
7174
+ [...this.sessions.keys()].filter((p) => within5(p, at)).map((p) => this.commit(p))
7107
7175
  );
7108
7176
  }
7109
7177
  async commitOverlapping(real, offset, length2) {
@@ -7138,10 +7206,10 @@ var MountTable = class {
7138
7206
  }
7139
7207
  forget(mount, path) {
7140
7208
  mount.epoch++;
7141
- for (const key of [...mount.attrs.keys()]) if (within4(key, path)) mount.attrs.delete(key);
7209
+ for (const key of [...mount.attrs.keys()]) if (within5(key, path)) mount.attrs.delete(key);
7142
7210
  mount.lists.delete(path);
7143
7211
  mount.lists.delete(parentOf7(path));
7144
- for (const key of [...mount.lists.keys()]) if (within4(key, path)) mount.lists.delete(key);
7212
+ for (const key of [...mount.lists.keys()]) if (within5(key, path)) mount.lists.delete(key);
7145
7213
  }
7146
7214
  async getattr(mount, path, rel2, settled = false) {
7147
7215
  for (let round = 1; ; round++) {
@@ -7526,9 +7594,12 @@ var MountFs = class {
7526
7594
  }
7527
7595
  async rm(path, options = {}) {
7528
7596
  const found = this.at(path);
7597
+ if (!found && options.recursive && this.table.under(normalizePath(path)))
7598
+ return this.rmAround(normalizePath(path), options);
7529
7599
  if (!found) return this.base.rm(path, options);
7530
7600
  const { mount, rel: rel2 } = found;
7531
7601
  const real = normalizePath(path);
7602
+ if (rel2 === "/" && options.recursive) await this.rmEach(real, mount, rel2, options);
7532
7603
  if (rel2 === "/") throw errnoError("EBUSY", `${real} is a mount point`);
7533
7604
  this.writable(mount, real);
7534
7605
  await this.table.commitUnder(real);
@@ -7539,16 +7610,33 @@ var MountFs = class {
7539
7610
  if (options.force && err.code === "ENOENT") return;
7540
7611
  throw err;
7541
7612
  }
7613
+ if (options.file && attr2.kind === "directory") throw errnoError("EISDIR", real);
7542
7614
  this.table.forget(mount, real);
7543
7615
  if (attr2.kind === "directory" && options.recursive) {
7544
- for (const entry of await this.table.readdir(mount, real, rel2)) {
7545
- await this.rm(`${real}/${entry.name}`, { recursive: true, force: options.force ?? false });
7546
- }
7616
+ await this.rmEach(real, mount, rel2, { recursive: true, force: options.force ?? false });
7547
7617
  this.table.forget(mount, real);
7548
7618
  }
7549
7619
  await mount.conn.call({ op: attr2.kind === "directory" ? "rmdir" : "unlink", path: rel2 });
7550
7620
  this.table.forget(mount, real);
7551
7621
  }
7622
+ async rmEach(real, mount, rel2, options) {
7623
+ let first;
7624
+ for (const entry of await this.table.readdir(mount, real, rel2)) {
7625
+ await this.rm(joinName(real, entry.name), options).catch((err) => {
7626
+ first ??= err;
7627
+ });
7628
+ }
7629
+ if (first) throw first;
7630
+ }
7631
+ async rmAround(path, options) {
7632
+ let first;
7633
+ for (const name2 of await this.base.readdir(path)) {
7634
+ await this.rm(joinName(path, name2), options).catch((err) => {
7635
+ first ??= err;
7636
+ });
7637
+ }
7638
+ throw first ?? errnoError("EBUSY", `${path} holds a mount point`);
7639
+ }
7552
7640
  async rename(from, to) {
7553
7641
  const a = this.at(from);
7554
7642
  const b = this.at(to);
@@ -7776,6 +7864,11 @@ function statJson(s) {
7776
7864
  async function readdirStat(fs, dir) {
7777
7865
  return (await fs.readdirStat(dir)).map(([name2, stat]) => [name2, stat && statJson(stat)]);
7778
7866
  }
7867
+ async function usageAt(entry, path) {
7868
+ if (!entry.statfs) return void 0;
7869
+ const real = await realTarget(entry.fs, path).catch(() => path);
7870
+ return entry.statfs(real);
7871
+ }
7779
7872
  async function storageUsage() {
7780
7873
  const estimate = await globalThis.navigator?.storage?.estimate?.();
7781
7874
  if (typeof estimate?.quota !== "number") return null;
@@ -7847,8 +7940,7 @@ async function run(entry, path, req) {
7847
7940
  await fs.link(path, fs.resolvePath(cwd, req.arg2 ?? ""), req.mode === 1);
7848
7941
  return done;
7849
7942
  case "unlink":
7850
- if ((await fs.lstat(path)).isDirectory) throw syncError("EISDIR", path);
7851
- await fs.rm(path);
7943
+ await fs.rm(path, { file: true });
7852
7944
  return done;
7853
7945
  case "rmdir":
7854
7946
  if (!(await fs.lstat(path)).isDirectory) throw syncError("ENOTDIR", path);
@@ -7996,8 +8088,8 @@ async function guarded(entry, path, req) {
7996
8088
  await guard(g, path, req, entry.cwd);
7997
8089
  return void 0;
7998
8090
  } catch (err) {
7999
- const refusal = err.code === "EACCES";
8000
- return req.op === "exists" && refusal ? json2(false) : toErrno(err);
8091
+ const refusal2 = err.code === "EACCES";
8092
+ return req.op === "exists" && refusal2 ? json2(false) : toErrno(err);
8001
8093
  }
8002
8094
  }
8003
8095
  async function createExclusive(fs, path, owner) {
@@ -8082,6 +8174,26 @@ async function dispatchSyncFs(req) {
8082
8174
  return toErrno(err);
8083
8175
  }
8084
8176
  }
8177
+ var DENIED = /* @__PURE__ */ new Set(["EACCES", "EPERM"]);
8178
+ async function rmPermitted(entry, path, req, refused2) {
8179
+ const st = await entry.fs.lstat(path).catch(() => void 0);
8180
+ if (!st?.isDirectory) return refused2;
8181
+ const unlisted = await guarded(entry, path, { ...req, op: "readdir" });
8182
+ if (unlisted) return unlisted;
8183
+ const names = await entry.fs.readdir(path).catch(() => void 0);
8184
+ if (!names) return refused2;
8185
+ let first;
8186
+ for (const name2 of names) {
8187
+ const child = await dispatchOne(entry, {
8188
+ ...req,
8189
+ path: joinName(path, name2)
8190
+ });
8191
+ if (!child.ok) first ??= child;
8192
+ }
8193
+ if (first) return first;
8194
+ const again = await guarded(entry, path, req);
8195
+ return again ?? await run(entry, path, req);
8196
+ }
8085
8197
  async function dispatchOne(entry, req) {
8086
8198
  const { fs, cwd } = entry;
8087
8199
  try {
@@ -8091,8 +8203,11 @@ async function dispatchOne(entry, req) {
8091
8203
  if (req.op === "access") return await accessOp(entry, path, req);
8092
8204
  if (req.op === "hold") return await holdOp(entry, path, req);
8093
8205
  const refused2 = await guarded(entry, path, req);
8206
+ if (refused2 && req.op === "rm" && !refused2.ok && DENIED.has(refused2.errno)) {
8207
+ return await rmPermitted(entry, path, req, refused2);
8208
+ }
8094
8209
  if (refused2) return refused2;
8095
- if (req.op === "statfs") return json2(await entry.statfs?.(path) ?? await storageUsage());
8210
+ if (req.op === "statfs") return json2(await usageAt(entry, path) ?? await storageUsage());
8096
8211
  if (entry.revoked?.(path)) return { ok: false, errno: "EIO", message: `${path} was unmounted` };
8097
8212
  if (req.op === "write" && req.mode === 1 || UNLINKED_OPS.has(req.op)) {
8098
8213
  const serve = async () => run(entry, await keptOr(entry, path, req.handle), req);
@@ -10776,7 +10891,7 @@ function parsePrefix(text) {
10776
10891
  if (length2 < 0 || length2 > max) return void 0;
10777
10892
  return { ...parsed2, length: length2 };
10778
10893
  }
10779
- function within5(prefix, addr) {
10894
+ function within6(prefix, addr) {
10780
10895
  if (prefix.v6 !== addr.v6) return false;
10781
10896
  const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
10782
10897
  return addr.bits >> shift === prefix.bits >> shift;
@@ -10816,9 +10931,9 @@ var Routes = class {
10816
10931
  if (!addr) return "unreachable";
10817
10932
  if (addr.v6 && addr.bits === 1n) return "loopback";
10818
10933
  if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
10819
- if (RESERVED.some((p) => within5(p, addr))) return "reserved";
10934
+ if (RESERVED.some((p) => within6(p, addr))) return "reserved";
10820
10935
  if (addr.v6 && !this.ipv6) return "unreachable";
10821
- if (this.exit || this.table.some((p) => within5(p, addr))) return "uplink";
10936
+ if (this.exit || this.table.some((p) => within6(p, addr))) return "uplink";
10822
10937
  return "unreachable";
10823
10938
  }
10824
10939
  };
@@ -10884,14 +10999,14 @@ var Resolver = class {
10884
10999
  if (hit && hit.expires > this.now()) return hit.addresses;
10885
11000
  const signal = blocking?.();
10886
11001
  if (signal?.aborted) throw new KernelError("EINTR");
10887
- let lookup2 = this.pending.get(id2);
10888
- if (!lookup2) {
11002
+ let lookup3 = this.pending.get(id2);
11003
+ if (!lookup3) {
10889
11004
  const abort = new AbortController();
10890
- lookup2 = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
10891
- this.pending.set(id2, lookup2);
10892
- void lookup2.work.then(() => this.pending.delete(id2));
11005
+ lookup3 = { work: this.lookup(key, asked, id2, abort.signal), abort, waiters: 0 };
11006
+ this.pending.set(id2, lookup3);
11007
+ void lookup3.work.then(() => this.pending.delete(id2));
10893
11008
  }
10894
- return waitFor(lookup2, signal);
11009
+ return waitFor(lookup3, signal);
10895
11010
  }
10896
11011
  async lookup(name2, family, id2, abandoned) {
10897
11012
  const uplink = this.uplink;
@@ -10932,17 +11047,17 @@ var Resolver = class {
10932
11047
  }
10933
11048
  }
10934
11049
  };
10935
- function waitFor(lookup2, signal) {
10936
- lookup2.waiters++;
11050
+ function waitFor(lookup3, signal) {
11051
+ lookup3.waiters++;
10937
11052
  return new Promise((resolve2, reject) => {
10938
11053
  const abort = () => {
10939
- if (--lookup2.waiters === 0) lookup2.abort.abort(new KernelError("EINTR"));
11054
+ if (--lookup3.waiters === 0) lookup3.abort.abort(new KernelError("EINTR"));
10940
11055
  reject(new KernelError("EINTR"));
10941
11056
  };
10942
11057
  signal?.addEventListener("abort", abort, { once: true });
10943
- void lookup2.work.then((value) => {
11058
+ void lookup3.work.then((value) => {
10944
11059
  if (signal?.aborted) return;
10945
- lookup2.waiters--;
11060
+ lookup3.waiters--;
10946
11061
  signal?.removeEventListener("abort", abort);
10947
11062
  resolve2(value);
10948
11063
  });
@@ -11674,7 +11789,7 @@ function isMissing(err) {
11674
11789
  if (err?.code === "ENOENT") return true;
11675
11790
  return err instanceof Error && err.message.startsWith("ENOENT");
11676
11791
  }
11677
- function within6(path, root) {
11792
+ function within7(path, root) {
11678
11793
  return path === root || path.startsWith(root.endsWith("/") ? root : `${root}/`);
11679
11794
  }
11680
11795
  var VfsNode = class {
@@ -11995,12 +12110,12 @@ var VfsNodes = class {
11995
12110
  return node;
11996
12111
  }
11997
12112
  holds(prefix) {
11998
- for (const path of this.byPath.keys()) if (within6(path, prefix)) return true;
12113
+ for (const path of this.byPath.keys()) if (within7(path, prefix)) return true;
11999
12114
  return false;
12000
12115
  }
12001
12116
  writes(prefix) {
12002
12117
  for (const [path, node] of this.byPath)
12003
- if (node.writers > 0 && within6(path, prefix)) return true;
12118
+ if (node.writers > 0 && within7(path, prefix)) return true;
12004
12119
  return false;
12005
12120
  }
12006
12121
  closed(node, writable2 = false) {
@@ -12044,11 +12159,11 @@ var VfsNodes = class {
12044
12159
  this.kept.delete(node);
12045
12160
  }
12046
12161
  async flush(path) {
12047
- for (const [p, node] of this.byPath) if (within6(p, path)) await node.serial(() => node.flush());
12162
+ for (const [p, node] of this.byPath) if (within7(p, path)) await node.serial(() => node.flush());
12048
12163
  }
12049
12164
  async unlinking(path) {
12050
12165
  for (const [held, node] of [...this.byPath]) {
12051
- if (!within6(held, path)) continue;
12166
+ if (!within7(held, path)) continue;
12052
12167
  await node.serial(async () => {
12053
12168
  await Promise.all([this.keepsOnDisk || node.keep(), node.limit()]);
12054
12169
  });
@@ -12056,14 +12171,14 @@ var VfsNodes = class {
12056
12171
  }
12057
12172
  revoke(prefix) {
12058
12173
  for (const [path, node] of [...this.byPath]) {
12059
- if (!within6(path, prefix)) continue;
12174
+ if (!within7(path, prefix)) continue;
12060
12175
  node.revoke();
12061
12176
  this.forget(node);
12062
12177
  }
12063
12178
  }
12064
12179
  unlinked(path) {
12065
12180
  for (const [held, node] of [...this.byPath]) {
12066
- if (!within6(held, path) || this.byPath.get(held) !== node) continue;
12181
+ if (!within7(held, path) || this.byPath.get(held) !== node) continue;
12067
12182
  if (!this.dropName(held, node)) this.orphan(node);
12068
12183
  }
12069
12184
  }
@@ -12071,8 +12186,8 @@ var VfsNodes = class {
12071
12186
  if (from === to) return;
12072
12187
  const moved2 = [];
12073
12188
  for (const [p, node] of [...this.byPath]) {
12074
- if (within6(p, from)) moved2.push([p, node]);
12075
- else if (within6(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
12189
+ if (within7(p, from)) moved2.push([p, node]);
12190
+ else if (within7(p, to) && this.byPath.get(p) === node && !this.dropName(p, node)) {
12076
12191
  this.orphan(node);
12077
12192
  }
12078
12193
  }
@@ -12351,7 +12466,7 @@ function isWasmSyscall(req) {
12351
12466
  var MAX_READ = 1024 * 1024;
12352
12467
  var WRITE_ACCESS = 3;
12353
12468
  var KEYBOARD = /* @__PURE__ */ new Set([SIG.INT, SIG.QUIT, SIG.TSTP]);
12354
- var DEFAULT_UMASK = 18;
12469
+ var DEFAULT_UMASK2 = 18;
12355
12470
  function words(value) {
12356
12471
  if (!Array.isArray(value) || !value.every((w) => typeof w === "string")) {
12357
12472
  throw new KernelError("EINVAL");
@@ -12379,7 +12494,7 @@ var WasmProcess = class {
12379
12494
  interrupt = new AbortController();
12380
12495
  gone = new AbortController();
12381
12496
  dying = false;
12382
- umask = DEFAULT_UMASK;
12497
+ umask = DEFAULT_UMASK2;
12383
12498
  cred;
12384
12499
  execChild;
12385
12500
  killed = false;
@@ -12401,7 +12516,7 @@ var WasmProcess = class {
12401
12516
  this.fds = fds;
12402
12517
  this.options = options;
12403
12518
  this.inherited = options.ignored ?? 0;
12404
- this.umask = options.umask ?? DEFAULT_UMASK;
12519
+ this.umask = options.umask ?? DEFAULT_UMASK2;
12405
12520
  this.cred = copyCred(options.cred ?? ROOT);
12406
12521
  this.children = new ChildTable(fds, options.spawner, options.forker);
12407
12522
  this.nodes = options.nodes ?? new VfsNodes(options.fs);
@@ -18087,7 +18202,7 @@ var Launcher = class {
18087
18202
  }
18088
18203
  }
18089
18204
  umaskOf(pid) {
18090
- return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK;
18205
+ return this.processes.get(pid)?.umask?.() ?? DEFAULT_UMASK2;
18091
18206
  }
18092
18207
  imageOf(pid) {
18093
18208
  const members = this.jobs.list();
@@ -18597,6 +18712,111 @@ var Launcher = class {
18597
18712
  }
18598
18713
  };
18599
18714
 
18715
+ // src/node/loopback.ts
18716
+ import { lookup } from "node:dns/promises";
18717
+ import { isIP } from "node:net";
18718
+ var DEVTOOLS_PORTS = [9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229];
18719
+ var METADATA_V6 = [64768, 3778, 0, 0, 0, 0, 0, 596].join(":");
18720
+ var resolveHost = async (host) => (await lookup(host, { all: true, verbatim: true })).map((a) => a.address);
18721
+ function ipv4Place(address) {
18722
+ const [a, b] = address.split(".").map(Number);
18723
+ if (a === 127 || a === 0) return "loopback";
18724
+ if (a === 169 && b === 254) return "link-local";
18725
+ return void 0;
18726
+ }
18727
+ function placeOf(address) {
18728
+ const plain = address.replace(/^\[(.*)\]$/, "$1").toLowerCase();
18729
+ const kind = isIP(plain);
18730
+ if (kind === 4) return ipv4Place(plain);
18731
+ if (kind !== 6) return void 0;
18732
+ const words2 = hextets2(plain);
18733
+ const embedded2 = words2.slice(0, 5).every((w) => w === 0) && (words2[5] === 65535 || words2[5] === 0);
18734
+ if (embedded2 && (words2[5] === 65535 || words2[6] !== 0)) return ipv4Place(ipv4Of(words2));
18735
+ if (words2.slice(0, 7).every((w) => w === 0) && (words2[7] ?? 0) <= 1) return "loopback";
18736
+ if (((words2[0] ?? 0) & 65472) === 65152) return "link-local";
18737
+ if (words2.join(":") === METADATA_V6) return "link-local";
18738
+ return void 0;
18739
+ }
18740
+ function ipv4Of(words2) {
18741
+ const [high = 0, low = 0] = words2.slice(6);
18742
+ return [high >> 8, high & 255, low >> 8, low & 255].join(".");
18743
+ }
18744
+ function hextets2(address) {
18745
+ const dotted = /(\d+\.\d+\.\d+\.\d+)$/.exec(address)?.[1];
18746
+ let text = address;
18747
+ if (dotted) {
18748
+ const [a, b, c, d] = dotted.split(".").map(Number);
18749
+ text = `${address.slice(0, -dotted.length)}${(a << 8 | b).toString(16)}:${(c << 8 | d).toString(16)}`;
18750
+ }
18751
+ const [head = "", tail] = text.split("::");
18752
+ const part = (side) => side ? side.split(":").map((w) => Number.parseInt(w, 16)) : [];
18753
+ const left = part(head);
18754
+ const right = tail === void 0 ? [] : part(tail);
18755
+ const gap = Array(8 - left.length - right.length).fill(0);
18756
+ return [...left, ...gap, ...right];
18757
+ }
18758
+ function loopbackName2(host) {
18759
+ const name2 = host.toLowerCase().replace(/\.$/, "");
18760
+ return name2 === "localhost" || name2.endsWith(".localhost");
18761
+ }
18762
+ function portOf(url) {
18763
+ if (url.port) return Number(url.port);
18764
+ return url.protocol === "https:" || url.protocol === "wss:" ? 443 : 80;
18765
+ }
18766
+ function allows(policy, port) {
18767
+ if (Array.isArray(policy)) return policy.includes(port);
18768
+ return policy === true && !DEVTOOLS_PORTS.includes(port);
18769
+ }
18770
+ function refusal(url, address, place) {
18771
+ const where = `${address}:${portOf(url)}`;
18772
+ const why = place === "link-local" ? `nodeTransport refuses link-local ${where}` : `nodeTransport refuses the host's loopback ${where} (see nodeTransport({ hostLoopback }))`;
18773
+ return Object.assign(new Error(why), { status: 403, code: "EACCES" });
18774
+ }
18775
+ async function addressesOf(host, resolve2) {
18776
+ const plain = host.replace(/^\[(.*)\]$/, "$1");
18777
+ if (isIP(plain)) return [plain];
18778
+ if (loopbackName2(plain)) return ["127.0.0.1"];
18779
+ return resolve2(plain).catch(() => []);
18780
+ }
18781
+ async function checkDestination(target, policy, resolve2 = resolveHost) {
18782
+ const url = new URL(target);
18783
+ for (const address of await addressesOf(url.hostname, resolve2)) {
18784
+ const place = placeOf(address);
18785
+ if (!place) continue;
18786
+ if (place === "loopback" && allows(policy, portOf(url))) continue;
18787
+ throw refusal(url, address, place);
18788
+ }
18789
+ }
18790
+ function guarded2(check, signal) {
18791
+ if (!signal) return check;
18792
+ signal.throwIfAborted();
18793
+ let stop = () => {
18794
+ };
18795
+ const aborted2 = new Promise((_, reject) => {
18796
+ stop = () => reject(signal.reason);
18797
+ signal.addEventListener("abort", stop, { once: true });
18798
+ });
18799
+ return Promise.race([check, aborted2]).finally(() => signal.removeEventListener("abort", stop));
18800
+ }
18801
+ function guardedLoopback(transport, policy, resolve2) {
18802
+ const open = transport.websocket?.bind(transport);
18803
+ return {
18804
+ get traits() {
18805
+ return transport.traits;
18806
+ },
18807
+ fetch: async (request, owner) => {
18808
+ await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
18809
+ return transport.fetch(request, owner);
18810
+ },
18811
+ ...open ? {
18812
+ websocket: async (request) => {
18813
+ await guarded2(checkDestination(request.url, policy, resolve2), request.signal);
18814
+ return open(request);
18815
+ }
18816
+ } : {}
18817
+ };
18818
+ }
18819
+
18600
18820
  // src/node/memory-root.ts
18601
18821
  var fail2 = (name2) => {
18602
18822
  throw new DOMException(name2, name2);
@@ -18803,8 +19023,8 @@ function isMemoryRoot(handle) {
18803
19023
  }
18804
19024
 
18805
19025
  // src/node/ws-diagnose.ts
18806
- import { lookup } from "node:dns/promises";
18807
- import { isIP, connect as tcp } from "node:net";
19026
+ import { lookup as lookup2 } from "node:dns/promises";
19027
+ import { isIP as isIP2, connect as tcp } from "node:net";
18808
19028
  import { connect as tls } from "node:tls";
18809
19029
  var PROBE_MS = 3e3;
18810
19030
  var PASSED = /* @__PURE__ */ new Set(["ECONNREFUSED", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT", "ECONNRESET"]);
@@ -18815,7 +19035,7 @@ function codeOf5(e) {
18815
19035
  }
18816
19036
  function probe(secure, host, port, ms) {
18817
19037
  return new Promise((resolve2) => {
18818
- const socket = secure ? tls({ host, port, ...isIP(host) ? {} : { servername: host } }) : tcp({ host, port });
19038
+ const socket = secure ? tls({ host, port, ...isIP2(host) ? {} : { servername: host } }) : tcp({ host, port });
18819
19039
  const done4 = (code) => {
18820
19040
  clearTimeout(timer);
18821
19041
  socket.destroy();
@@ -18835,7 +19055,7 @@ async function diagnose(url, ms = PROBE_MS) {
18835
19055
  const secure = at.protocol === "wss:";
18836
19056
  const port = Number(at.port) || (secure ? 443 : 80);
18837
19057
  try {
18838
- await lookup(host);
19058
+ await lookup2(host);
18839
19059
  } catch (e) {
18840
19060
  return NO_NAME.has(codeOf5(e) ?? "") ? "EAI_NONAME" : void 0;
18841
19061
  }
@@ -19684,9 +19904,10 @@ function hostfsFetchOf(options) {
19684
19904
  }
19685
19905
  };
19686
19906
  }
19687
- function nodeTransport() {
19907
+ function nodeTransport(options = {}) {
19688
19908
  const transport = fetchTransport({ webSocket: true, webSocketHeaders: true, redirect: "manual" });
19689
- return diagnosing({ ...transport, traits: { ...transport.traits, crossOrigin: "any" } });
19909
+ const open = { ...transport, traits: { ...transport.traits, crossOrigin: "any" } };
19910
+ return diagnosing(guardedLoopback(open, options.hostLoopback ?? false));
19690
19911
  }
19691
19912
  var TERMINATED = "the kernel is terminated";
19692
19913
  var EXIT_GRACE_MS = 50;