@ai-ecoverse/slicc-kernel 1.32.3 → 1.34.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.
@@ -4548,7 +4548,7 @@ function argsOf(pkg, raw) {
4548
4548
  }
4549
4549
  function abiOf(raw, fallback) {
4550
4550
  const abi = raw ?? fallback;
4551
- return abi === "emscripten" || abi === "wasi" ? abi : void 0;
4551
+ return abi === "emscripten" || abi === "wasi" || abi === "js" ? abi : void 0;
4552
4552
  }
4553
4553
  function commandOf(pkg, name2, raw, packageAbi, shared) {
4554
4554
  const withEnv = envFields({ ...shared, ...envOf(pkg, raw.env) });
@@ -4557,11 +4557,25 @@ function commandOf(pkg, name2, raw, packageAbi, shared) {
4557
4557
  return { name: name2, abi: packageAbi, glue: script, wasm: script, argv0: name2, script, ...withEnv };
4558
4558
  }
4559
4559
  const abi = abiOf(raw.abi, packageAbi);
4560
+ const argv0 = typeof raw.argv0 === "string" && raw.argv0 ? raw.argv0 : name2;
4561
+ const args = argsOf(pkg, raw.args);
4562
+ if (abi === "js") {
4563
+ const module = inside(pkg, raw.module);
4564
+ if (!module) return void 0;
4565
+ return {
4566
+ name: name2,
4567
+ abi,
4568
+ glue: module,
4569
+ wasm: module,
4570
+ argv0,
4571
+ ...raw.argv0Path === true ? { argv0Path: true } : {},
4572
+ ...args ? { args } : {},
4573
+ ...withEnv
4574
+ };
4575
+ }
4560
4576
  const wasm = inside(pkg, raw.wasm);
4561
4577
  const glue = abi === "wasi" ? wasm : inside(pkg, raw.glue);
4562
4578
  if (!abi || !glue || !wasm) return void 0;
4563
- const argv0 = typeof raw.argv0 === "string" && raw.argv0 ? raw.argv0 : name2;
4564
- const args = argsOf(pkg, raw.args);
4565
4579
  const imports = abi === "wasi" ? inside(pkg, raw.imports) : void 0;
4566
4580
  return {
4567
4581
  name: name2,
@@ -5268,12 +5282,12 @@ var MountTable = class {
5268
5282
  }
5269
5283
  list() {
5270
5284
  const mounted = [...this.mounts.values()].map(
5271
- ({ type, source, target, options, state, error, present }) => ({
5285
+ ({ type, source, target, options, state, error, present: present2 }) => ({
5272
5286
  type,
5273
5287
  source,
5274
5288
  target,
5275
5289
  options: { ...options },
5276
- state: state === "ok" && present?.() === false ? "nomedium" : state,
5290
+ state: state === "ok" && present2?.() === false ? "nomedium" : state,
5277
5291
  ...error ? { error } : {}
5278
5292
  })
5279
5293
  );
@@ -5982,6 +5996,8 @@ var SAB_I_MEMORY = 10;
5982
5996
  var SAB_I_TOTAL_HI = 11;
5983
5997
  var SAB_I_OFFSET_HI = 12;
5984
5998
  var SAB_I_KILLED = 13;
5999
+ var SAB_I_ASYNC = 14;
6000
+ var SAB_I_STOP = 15;
5985
6001
  var U32 = 2 ** 32;
5986
6002
  function storeU53(header2, lo, hi, value) {
5987
6003
  Atomics.store(header2, hi, Math.floor(value / U32));
@@ -6163,8 +6179,10 @@ var AsyncOps = class {
6163
6179
  waiters = /* @__PURE__ */ new Set();
6164
6180
  closed = false;
6165
6181
  run;
6166
- constructor(run2) {
6182
+ onReady;
6183
+ constructor(run2, onReady) {
6167
6184
  this.run = run2;
6185
+ this.onReady = onReady;
6168
6186
  }
6169
6187
  submit(req) {
6170
6188
  const id = this.hold();
@@ -6241,6 +6259,7 @@ var AsyncOps = class {
6241
6259
  this.results.set(id, result);
6242
6260
  this.ready.push(id);
6243
6261
  this.wake();
6262
+ this.onReady?.();
6244
6263
  }
6245
6264
  wake() {
6246
6265
  for (const wake of [...this.waiters]) wake();
@@ -6549,6 +6568,8 @@ var KernelTty = class {
6549
6568
  waiters = [];
6550
6569
  hungUp = false;
6551
6570
  name;
6571
+ onRead;
6572
+ readers = 0;
6552
6573
  screen;
6553
6574
  signal;
6554
6575
  constructor(screen, signal) {
@@ -6699,7 +6720,19 @@ var KernelTty = class {
6699
6720
  }
6700
6721
  this.screen.write(Uint8Array.from(out));
6701
6722
  }
6723
+ get reading() {
6724
+ return this.readers > 0;
6725
+ }
6702
6726
  async read(max, signal) {
6727
+ this.onRead?.();
6728
+ this.readers++;
6729
+ try {
6730
+ return await this.take(max, signal);
6731
+ } finally {
6732
+ this.readers--;
6733
+ }
6734
+ }
6735
+ async take(max, signal) {
6703
6736
  while (this.readable.length === 0) {
6704
6737
  if (this.hungUp) return new Uint8Array(0);
6705
6738
  await this.changed(signal);
@@ -6764,13 +6797,14 @@ var PtyPair = class {
6764
6797
  slaveSeen = false;
6765
6798
  index;
6766
6799
  onFree;
6767
- constructor(index, signal, onFree) {
6800
+ constructor(index, signal, onFree, onRead) {
6768
6801
  this.index = index;
6769
6802
  this.onFree = onFree;
6770
6803
  this.slave = new KernelTty(
6771
6804
  { write: (bytes) => this.fromSlave(bytes) },
6772
6805
  (sig) => signal(this.slave, sig)
6773
6806
  );
6807
+ if (onRead) this.slave.onRead = () => onRead(this.slave);
6774
6808
  this.slave.name = `/dev/pts/${index}`;
6775
6809
  }
6776
6810
  master() {
@@ -6868,13 +6902,15 @@ var PtyPair = class {
6868
6902
  var PtyTable = class {
6869
6903
  pairs = /* @__PURE__ */ new Map();
6870
6904
  signal;
6871
- constructor(signal) {
6905
+ onRead;
6906
+ constructor(signal, onRead) {
6872
6907
  this.signal = signal;
6908
+ this.onRead = onRead;
6873
6909
  }
6874
6910
  open() {
6875
6911
  let index = 0;
6876
6912
  while (this.pairs.has(index)) index += 1;
6877
- const pair = new PtyPair(index, this.signal, (p) => this.pairs.delete(p.index));
6913
+ const pair = new PtyPair(index, this.signal, (p) => this.pairs.delete(p.index), this.onRead);
6878
6914
  this.pairs.set(index, pair);
6879
6915
  return { pair, master: pair.master() };
6880
6916
  }
@@ -7399,6 +7435,9 @@ var VfsNode = class {
7399
7435
  }
7400
7436
  return this.data;
7401
7437
  }
7438
+ holds() {
7439
+ return this.dirty;
7440
+ }
7402
7441
  replace(contents2) {
7403
7442
  this.ranged = void 0;
7404
7443
  this.data = new Uint8Array(contents2);
@@ -7509,9 +7548,34 @@ var VfsNode = class {
7509
7548
  var VfsNodes = class {
7510
7549
  byPath = /* @__PURE__ */ new Map();
7511
7550
  fs;
7551
+ entries = /* @__PURE__ */ new Map();
7512
7552
  constructor(fs) {
7513
7553
  this.fs = fs;
7514
7554
  }
7555
+ async onEntry(path, op) {
7556
+ const key = await this.entryKey(path);
7557
+ const run2 = (this.entries.get(key) ?? Promise.resolve()).then(op);
7558
+ const tail = run2.catch(() => void 0);
7559
+ this.entries.set(key, tail);
7560
+ void tail.then(() => {
7561
+ if (this.entries.get(key) === tail) this.entries.delete(key);
7562
+ });
7563
+ return run2;
7564
+ }
7565
+ async entryKey(path) {
7566
+ const parent = parentOf5(path);
7567
+ const dir = await realDir(this.fs, parent).catch(() => parent);
7568
+ const name2 = path.slice(path.lastIndexOf("/") + 1);
7569
+ return dir === "/" ? `/${name2}` : `${dir}/${name2}`;
7570
+ }
7571
+ createExclusive(path) {
7572
+ return this.onEntry(path, async () => {
7573
+ const node = this.byPath.get(path);
7574
+ const held = node ? await node.serial(async () => node.holds()) : false;
7575
+ if (held || await present(this.fs, path)) throw fsError("EEXIST", path);
7576
+ await this.fs.writeFile(path, new Uint8Array(0));
7577
+ });
7578
+ }
7515
7579
  open(path, pin) {
7516
7580
  let node = this.byPath.get(path);
7517
7581
  if (!node) {
@@ -7580,6 +7644,16 @@ var VfsNodes = class {
7580
7644
  }
7581
7645
  }
7582
7646
  };
7647
+ async function present(fs, path, follow = false) {
7648
+ const look = (follow ? void 0 : fs.lstat) ?? fs.stat;
7649
+ try {
7650
+ await (look ? look.call(fs, path) : fs.readFileBuffer(path));
7651
+ return true;
7652
+ } catch (err) {
7653
+ if (err?.code === "ENOENT") return false;
7654
+ throw err;
7655
+ }
7656
+ }
7583
7657
  var MAX_LINKS5 = 40;
7584
7658
  function parentOf5(path) {
7585
7659
  return path.slice(0, path.lastIndexOf("/")) || "/";
@@ -7644,12 +7718,17 @@ function keepingOpen(fs, nodes) {
7644
7718
  await fs.rm(path, options);
7645
7719
  nodes.unlinked(at);
7646
7720
  },
7647
- async rename(from, to) {
7721
+ rename(from, to) {
7648
7722
  const [a, b] = [fs.resolvePath("/", from), fs.resolvePath("/", to)];
7649
- await nodes.unlinking(b);
7650
- await fs.rename(from, to);
7651
- nodes.renamed(a, b);
7652
- }
7723
+ return nodes.onEntry(b, async () => {
7724
+ await nodes.unlinking(b);
7725
+ await fs.rename(from, to);
7726
+ nodes.renamed(a, b);
7727
+ });
7728
+ },
7729
+ writeFile: (path, content) => nodes.onEntry(fs.resolvePath("/", path), () => fs.writeFile(path, content)),
7730
+ symlink: (target, path) => nodes.onEntry(fs.resolvePath("/", path), () => fs.symlink(target, path)),
7731
+ mkdir: (path, options) => nodes.onEntry(fs.resolvePath("/", path), () => fs.mkdir(path, options))
7653
7732
  };
7654
7733
  }
7655
7734
  function vfsFile(fs, opts, nodes) {
@@ -7988,6 +8067,21 @@ var WasmProcess = class {
7988
8067
  this.options.hasPending?.() ? AbortSignal.abort() : this.interrupt.signal
7989
8068
  );
7990
8069
  }
8070
+ async openVfs(req) {
8071
+ await refuseReadonly(this.options.fs, req.path, req.flags, req);
8072
+ const fd = this.fds.install(heldFile(), 3);
8073
+ try {
8074
+ if (req.exclusive) await this.nodes.createExclusive(req.path);
8075
+ this.fds.installAt(fd, this.openVfsFile(req));
8076
+ if (req.existing && !await present(this.options.fs, req.path, true)) {
8077
+ throw new KernelError("ENOENT");
8078
+ }
8079
+ } catch (err) {
8080
+ await Promise.resolve(this.fds.close(fd));
8081
+ throw err;
8082
+ }
8083
+ return fd;
8084
+ }
7991
8085
  openVfsFile(req) {
7992
8086
  return vfsFile(
7993
8087
  this.options.fs,
@@ -8030,8 +8124,7 @@ var WasmProcess = class {
8030
8124
  case "fd-poll":
8031
8125
  return { ok: true, kind: "json", json: pollFile(this.fds.get(req.fd).file) };
8032
8126
  case "fd-open-vfs":
8033
- await refuseReadonly(this.options.fs, req.path, req.flags, req);
8034
- return { ok: true, kind: "json", json: this.fds.install(this.openVfsFile(req), 3) };
8127
+ return { ok: true, kind: "json", json: await this.openVfs(req) };
8035
8128
  case "fd-info":
8036
8129
  return { ok: true, kind: "json", json: this.fdInfo(req.fd) };
8037
8130
  case "fd-list":
@@ -8309,6 +8402,7 @@ var WasmProcess = class {
8309
8402
  case "sig-mask":
8310
8403
  this.caught = req.caught;
8311
8404
  this.ignored = req.ignored;
8405
+ this.inherited &= ~(req.defaults ?? 0);
8312
8406
  return { ok: true, kind: "void" };
8313
8407
  case "sig-pause":
8314
8408
  return this.pause();
@@ -8338,7 +8432,10 @@ var WasmProcess = class {
8338
8432
  }
8339
8433
  alarm;
8340
8434
  dlLog = [];
8341
- asyncOps = new AsyncOps((req) => this.syscall(req));
8435
+ asyncOps = new AsyncOps(
8436
+ (req) => this.syscall(req),
8437
+ () => this.options.onAsync?.()
8438
+ );
8342
8439
  alarmEvery;
8343
8440
  setAlarm(sig, first, every, timer) {
8344
8441
  if (!isSignal(sig)) throw new KernelError("EINVAL");
@@ -8366,6 +8463,7 @@ var WasmProcess = class {
8366
8463
  async exit() {
8367
8464
  if (this.exited) return;
8368
8465
  this.exited = true;
8466
+ this.die();
8369
8467
  this.clearAlarm();
8370
8468
  this.interrupt.abort();
8371
8469
  this.options.locks?.release(this.pid);
@@ -8447,6 +8545,26 @@ var Dying = class {
8447
8545
  return [typeof reported === "number" ? reported : CRASHED, void 0];
8448
8546
  }
8449
8547
  };
8548
+ function processInit(opts, sab) {
8549
+ const base = {
8550
+ type: WASM_PROCESS_INIT,
8551
+ pid: opts.shownPid ?? opts.pid,
8552
+ argv0: opts.argv0,
8553
+ args: opts.args,
8554
+ env: opts.env,
8555
+ cwd: opts.cwd,
8556
+ sab,
8557
+ ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {}
8558
+ };
8559
+ if (opts.program.abi === "js") {
8560
+ return { ...base, program: opts.program, fds: inheritedFds(opts.fds) };
8561
+ }
8562
+ return {
8563
+ ...base,
8564
+ program: opts.program,
8565
+ ...opts.fork ? { fork: opts.fork } : { fds: inheritedFds(opts.fds) }
8566
+ };
8567
+ }
8450
8568
  function spawnWasmProcess(opts) {
8451
8569
  const sab = new SharedArrayBuffer(SAB_HEADER_BYTES + SAB_DEFAULT_WINDOW_BYTES);
8452
8570
  const header2 = new Int32Array(sab, 0, SAB_HEADER_I32);
@@ -8472,11 +8590,22 @@ function spawnWasmProcess(opts) {
8472
8590
  locks: opts.locks,
8473
8591
  onReap: opts.onReap,
8474
8592
  resolver: opts.resolver,
8475
- onPending: (sig) => void Atomics.or(header2, SAB_I_SIGNALS, sigbit(sig)),
8593
+ onPending: (sig) => {
8594
+ Atomics.or(header2, SAB_I_SIGNALS, sigbit(sig));
8595
+ Atomics.notify(header2, SAB_I_SIGNALS);
8596
+ },
8597
+ onAsync: () => {
8598
+ Atomics.add(header2, SAB_I_ASYNC, 1);
8599
+ Atomics.notify(header2, SAB_I_ASYNC);
8600
+ },
8476
8601
  onTimer: (which) => void Atomics.or(header2, SAB_I_TIMERS, 1 << which),
8477
8602
  hasPending: () => Atomics.load(header2, SAB_I_SIGNALS) !== 0 || Atomics.load(header2, SAB_I_TIMERS) !== 0,
8478
8603
  raise: (sig) => signal(sig),
8479
- ...opts.program.abi === "wasi" ? { pendingBits: () => Atomics.load(header2, SAB_I_SIGNALS) } : {}
8604
+ ...opts.program.abi === "wasi" || opts.program.abi === "js" ? { pendingBits: () => Atomics.load(header2, SAB_I_SIGNALS) } : {}
8605
+ });
8606
+ process.onState((state) => {
8607
+ Atomics.store(header2, SAB_I_STOP, state === "stopped" ? 1 : 0);
8608
+ Atomics.notify(header2, SAB_I_STOP);
8480
8609
  });
8481
8610
  const holds = new HeldPaths();
8482
8611
  opts.held?.add(holds);
@@ -8537,7 +8666,8 @@ function spawnWasmProcess(opts) {
8537
8666
  end?.();
8538
8667
  };
8539
8668
  const spawnThread = (thread) => {
8540
- if (done4) return;
8669
+ const { program } = opts;
8670
+ if (done4 || program.abi === "js") return;
8541
8671
  if (threads.size >= WASM_MAX_THREADS - 1) {
8542
8672
  opts.onError?.(`more than ${WASM_MAX_THREADS} threads`);
8543
8673
  finish(CRASHED);
@@ -8562,7 +8692,7 @@ function spawnWasmProcess(opts) {
8562
8692
  const tinit = {
8563
8693
  type: WASM_THREAD_INIT,
8564
8694
  pid: opts.shownPid ?? opts.pid,
8565
- program: opts.program,
8695
+ program,
8566
8696
  argv0: opts.argv0,
8567
8697
  args: opts.args,
8568
8698
  env: opts.env,
@@ -8573,18 +8703,7 @@ function spawnWasmProcess(opts) {
8573
8703
  };
8574
8704
  tw.postMessage(tinit);
8575
8705
  };
8576
- const init = {
8577
- type: WASM_PROCESS_INIT,
8578
- pid: opts.shownPid ?? opts.pid,
8579
- program: opts.program,
8580
- argv0: opts.argv0,
8581
- args: opts.args,
8582
- env: opts.env,
8583
- cwd: opts.cwd,
8584
- sab,
8585
- ...opts.ppid !== void 0 ? { ppid: opts.ppid } : {},
8586
- ...opts.fork ? { fork: opts.fork } : { fds: inheritedFds(opts.fds) }
8587
- };
8706
+ const init = processInit(opts, sab);
8588
8707
  worker.postMessage(init, opts.fork ? [opts.fork.memory.buffer] : []);
8589
8708
  const signal = (sig) => {
8590
8709
  if (process.signal(sig) !== "terminate") return;
@@ -8757,13 +8876,16 @@ var SETUP_OPS = /* @__PURE__ */ new Set([
8757
8876
  "fd-info",
8758
8877
  "sig-mask",
8759
8878
  "proc-setpgid",
8760
- "tty-pgrp-set"
8879
+ "tty-pgrp-set",
8880
+ "proc-exec",
8881
+ "proc-spawn"
8761
8882
  ]);
8762
- var SETTLE_MS = 250;
8883
+ var SETTLE_MS = 2e3;
8763
8884
  var KEY_SIGNALS = /* @__PURE__ */ new Set([2, 3, 20]);
8764
8885
  var SettlingChildren = class {
8765
8886
  children = /* @__PURE__ */ new Map();
8766
8887
  held = [];
8888
+ parked = [];
8767
8889
  pgidOf;
8768
8890
  settleMs;
8769
8891
  constructor(pgidOf, settleMs = SETTLE_MS) {
@@ -8771,7 +8893,15 @@ var SettlingChildren = class {
8771
8893
  this.settleMs = settleMs;
8772
8894
  }
8773
8895
  started(pid, ppid) {
8774
- this.children.set(pid, { ppid });
8896
+ const parent = this.children.get(ppid);
8897
+ this.children.set(pid, parent ? { ppid: parent.ppid, execer: ppid } : { ppid });
8898
+ const group = this.pgidOf(ppid);
8899
+ for (const p of this.parked) if (p.pgid === group) this.held.push({ ...p, copy: true });
8900
+ this.parked = this.parked.filter((p) => p.pgid !== group);
8901
+ }
8902
+ reading(pgid) {
8903
+ this.parked = this.parked.filter((p) => p.pgid !== pgid);
8904
+ this.held = this.held.filter((h) => !(h.copy && h.pgid === pgid));
8775
8905
  }
8776
8906
  syscall(pid, op) {
8777
8907
  const child = this.children.get(pid);
@@ -8787,13 +8917,25 @@ var SettlingChildren = class {
8787
8917
  if (!child) return;
8788
8918
  clearTimeout(child.timer);
8789
8919
  this.children.delete(pid);
8920
+ if (child.execer !== void 0) this.settled(child.execer);
8790
8921
  const ready2 = this.held.filter((h) => !this.blocking(h.pgid));
8791
8922
  this.held = this.held.filter((h) => this.blocking(h.pgid));
8792
8923
  for (const h of ready2) h.send();
8793
8924
  }
8794
- deliver(sig, pgid, send) {
8795
- if (KEY_SIGNALS.has(sig) && this.blocking(pgid)) this.held.push({ pgid, send });
8796
- else send();
8925
+ deliver(sig, pgid, send, foreground) {
8926
+ if (!KEY_SIGNALS.has(sig)) {
8927
+ send();
8928
+ } else if (this.blocking(pgid)) {
8929
+ this.held.push({ pgid, send });
8930
+ } else if (foreground) {
8931
+ send();
8932
+ const moved2 = () => {
8933
+ if (foreground() !== pgid) send();
8934
+ };
8935
+ this.parked.push({ pgid, send: moved2 });
8936
+ } else {
8937
+ send();
8938
+ }
8797
8939
  }
8798
8940
  blocking(pgid) {
8799
8941
  for (const { ppid } of this.children.values()) if (this.pgidOf(ppid) === pgid) return true;
@@ -10063,11 +10205,8 @@ var Launcher = class {
10063
10205
  locks = new LockTable();
10064
10206
  settling = new SettlingChildren((pid) => this.jobs.pgidOf(pid));
10065
10207
  ptys = new PtyTable(
10066
- (tty, sig) => this.settling.deliver(
10067
- sig,
10068
- this.jobs.tcgetpgrp(tty, 0),
10069
- () => this.jobs.signalOwnedForeground(tty, sig)
10070
- )
10208
+ (tty, sig) => this.keySignal(tty, 0, sig, () => this.jobs.signalOwnedForeground(tty, sig)),
10209
+ (tty) => this.settling.reading(this.jobs.tcgetpgrp(tty, 0))
10071
10210
  );
10072
10211
  net = new LoopbackNet();
10073
10212
  cdp;
@@ -10257,6 +10396,9 @@ var Launcher = class {
10257
10396
  return await this.base.exists(optional) ? optional : void 0;
10258
10397
  }
10259
10398
  async program(target, env) {
10399
+ if (target.abi === "js") {
10400
+ return { abi: "js", glue: await this.base.readFile(target.wasm), path: target.wasm };
10401
+ }
10260
10402
  if (target.abi !== "wasi") {
10261
10403
  const [glue, { module: module2 }] = await Promise.all([
10262
10404
  this.base.readFile(target.glue),
@@ -10480,6 +10622,11 @@ var Launcher = class {
10480
10622
  })
10481
10623
  );
10482
10624
  }
10625
+ keySignal(tty, fallback, sig, send) {
10626
+ const pgid = this.jobs.tcgetpgrp(tty, fallback);
10627
+ const foreground = () => this.jobs.tcgetpgrp(tty, fallback);
10628
+ this.settling.deliver(sig, pgid, send, tty.reading ? void 0 : foreground);
10629
+ }
10483
10630
  ignoredBy(pid, fork = false) {
10484
10631
  return this.processes.get(pid)?.ignoredSignals(fork) ?? 0;
10485
10632
  }
@@ -10675,12 +10822,9 @@ var Launcher = class {
10675
10822
  let leader = 0;
10676
10823
  const tty = new KernelTty(
10677
10824
  { write: (bytes) => options.onData(bytes.slice()) },
10678
- (sig) => this.settling.deliver(
10679
- sig,
10680
- this.jobs.tcgetpgrp(tty, leader),
10681
- () => this.jobs.signalForeground(tty, leader, sig)
10682
- )
10825
+ (sig) => this.keySignal(tty, leader, sig, () => this.jobs.signalForeground(tty, leader, sig))
10683
10826
  );
10827
+ tty.onRead = () => this.settling.reading(this.jobs.tcgetpgrp(tty, leader));
10684
10828
  tty.name = `/dev/tty${++this.terminals}`;
10685
10829
  tty.setSize(options.cols ?? 80, options.rows ?? 24);
10686
10830
  const fds = new FdTable();