@volter/tabnode 0.3.0 → 0.4.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.
@@ -6635,6 +6635,7 @@ function pidIsLive(pid) {
6635
6635
  return false;
6636
6636
  }
6637
6637
  const owners = /* @__PURE__ */ new Map();
6638
+ const allOwned = /* @__PURE__ */ new Map();
6638
6639
  const heldBy = /* @__PURE__ */ new WeakMap();
6639
6640
  const refHeld = /* @__PURE__ */ new WeakSet();
6640
6641
  const activeHandles = /* @__PURE__ */ new WeakSet();
@@ -6665,6 +6666,15 @@ function ownerOf(handle) {
6665
6666
  }
6666
6667
  function registerHandle(handle) {
6667
6668
  heldBy.set(handle, currentOwner());
6669
+ const token = heldBy.get(handle);
6670
+ if (token != null) {
6671
+ let owned = allOwned.get(token);
6672
+ if (!owned) {
6673
+ owned = /* @__PURE__ */ new Set();
6674
+ allOwned.set(token, owned);
6675
+ }
6676
+ owned.add(handle);
6677
+ }
6668
6678
  refHeld.add(handle);
6669
6679
  activeHandles.add(handle);
6670
6680
  hold(handle);
@@ -6686,6 +6696,12 @@ function handleHasRef(handle) {
6686
6696
  }
6687
6697
  function releaseHandle(handle) {
6688
6698
  drop(handle);
6699
+ const token = heldBy.get(handle);
6700
+ if (token != null) {
6701
+ const owned = allOwned.get(token);
6702
+ owned == null ? void 0 : owned.delete(handle);
6703
+ if ((owned == null ? void 0 : owned.size) === 0) allOwned.delete(token);
6704
+ }
6689
6705
  heldBy.delete(handle);
6690
6706
  refHeld.delete(handle);
6691
6707
  activeHandles.delete(handle);
@@ -6719,8 +6735,9 @@ function __releaseOwnedServers(token, stopTimers = true, stopProcess) {
6719
6735
  }
6720
6736
  }
6721
6737
  function __releaseOwnedHandles(token) {
6722
- const held = owners.get(token);
6738
+ const held = allOwned.get(token);
6723
6739
  if (!held) return;
6740
+ allOwned.delete(token);
6724
6741
  owners.delete(token);
6725
6742
  for (const handle of [...held]) {
6726
6743
  try {
@@ -6732,7 +6749,21 @@ function __releaseOwnedHandles(token) {
6732
6749
  function __adoptHandle(handle, token) {
6733
6750
  if (!handle) return;
6734
6751
  drop(handle);
6752
+ const previous = heldBy.get(handle);
6753
+ if (previous != null) {
6754
+ const owned = allOwned.get(previous);
6755
+ owned == null ? void 0 : owned.delete(handle);
6756
+ if ((owned == null ? void 0 : owned.size) === 0) allOwned.delete(previous);
6757
+ }
6735
6758
  heldBy.set(handle, token);
6759
+ if (token !== null) {
6760
+ let owned = allOwned.get(token);
6761
+ if (!owned) {
6762
+ owned = /* @__PURE__ */ new Set();
6763
+ allOwned.set(token, owned);
6764
+ }
6765
+ owned.add(handle);
6766
+ }
6736
6767
  hold(handle);
6737
6768
  }
6738
6769
  function ownedBytes(view) {
@@ -7913,11 +7944,11 @@ function answerFilesystem(method, args) {
7913
7944
  return answer2.apply(hostVfs, plain) ?? null;
7914
7945
  }
7915
7946
  const inodes = /* @__PURE__ */ new Map();
7916
- let nextInode = 1;
7947
+ let nextInode$1 = 1;
7917
7948
  function inodeOf(path2) {
7918
7949
  let inode = inodes.get(path2);
7919
7950
  if (inode === void 0) {
7920
- inode = nextInode++;
7951
+ inode = nextInode$1++;
7921
7952
  inodes.set(path2, inode);
7922
7953
  }
7923
7954
  return inode;
@@ -9077,7 +9108,9 @@ function refuseLinks(syscall, path2) {
9077
9108
  { code: "EPERM", errno: -1, syscall, path: path2 }
9078
9109
  );
9079
9110
  }
9080
- const pathModes = /* @__PURE__ */ new Map();
9111
+ function unsupportedMetadata(syscall) {
9112
+ throw Object.assign(new Error(`ENOTSUP: filesystem does not support ${syscall}`), { code: "ENOTSUP", syscall });
9113
+ }
9081
9114
  function treeHoldsLinks() {
9082
9115
  return vfs$1().holdsLinks === true;
9083
9116
  }
@@ -9141,19 +9174,18 @@ function fileFor(fd2) {
9141
9174
  return file;
9142
9175
  }
9143
9176
  function statArray(stats, bigint, path2) {
9144
- var _a3, _b3;
9177
+ var _a3, _b3, _c3;
9145
9178
  const mtime = stats.mtime instanceof Date ? stats.mtime.getTime() : Number(stats.mtimeMs ?? 0);
9146
9179
  const atime = stats.atime instanceof Date ? stats.atime.getTime() : Number(stats.atimeMs ?? mtime);
9147
9180
  const ctime = stats.ctime instanceof Date ? stats.ctime.getTime() : Number(stats.ctimeMs ?? mtime);
9148
9181
  const birth = stats.birthtime instanceof Date ? stats.birthtime.getTime() : Number(stats.birthtimeMs ?? mtime);
9149
9182
  const seconds = (ms2) => Math.floor(ms2 / 1e3);
9150
9183
  const nanos = (ms2) => Math.floor(ms2 % 1e3 * 1e6);
9151
- const stored = path2 !== void 0 ? pathModes.get(path2) : void 0;
9152
- const permissions = (stored !== void 0 ? stored : Number(stats.mode ?? 0)) & 4095;
9153
- const type2 = ((_a3 = stats.isDirectory) == null ? void 0 : _a3.call(stats)) ? S_IFDIR : ((_b3 = stats.isSymbolicLink) == null ? void 0 : _b3.call(stats)) ? S_IFLNK : S_IFREG;
9184
+ const permissions = Number(stats.mode ?? (((_a3 = stats.isDirectory) == null ? void 0 : _a3.call(stats)) ? 493 : 420)) & 4095;
9185
+ const type2 = ((_b3 = stats.isDirectory) == null ? void 0 : _b3.call(stats)) ? S_IFDIR : ((_c3 = stats.isSymbolicLink) == null ? void 0 : _c3.call(stats)) ? S_IFLNK : S_IFREG;
9154
9186
  const values = [
9155
9187
  Number(stats.dev ?? 0),
9156
- type2 | (permissions || (type2 === S_IFDIR ? 493 : 420)),
9188
+ type2 | permissions,
9157
9189
  Number(stats.nlink ?? 1),
9158
9190
  Number(stats.uid ?? 0),
9159
9191
  Number(stats.gid ?? 0),
@@ -9299,15 +9331,23 @@ class FSEvent {
9299
9331
  __privateAdd(this, _watcher, null);
9300
9332
  __publicField(this, "initialized", false);
9301
9333
  }
9302
- start(path2, _persistent, recursive, _encoding2) {
9334
+ start(path2, persistent, recursive, _encoding2) {
9303
9335
  const name = asPath(path2);
9304
9336
  const tree2 = vfs$1();
9337
+ const cwd = callingCwd();
9338
+ const owner = currentOwner();
9305
9339
  try {
9306
9340
  __privateSet(this, _watcher, tree2.watch(name, { recursive: Boolean(recursive) }, (event, filename) => {
9307
- var _a3;
9308
- (_a3 = this.onchange) == null ? void 0 : _a3.call(this, 0, event === "rename" ? "rename" : "change", filename ?? "");
9341
+ const notify = () => withFilesystem(tree2, () => withCwd(cwd, () => {
9342
+ var _a3;
9343
+ return (_a3 = this.onchange) == null ? void 0 : _a3.call(this, 0, event === "rename" ? "rename" : "change", filename ?? "");
9344
+ }));
9345
+ if (owner === null) notify();
9346
+ else enterRun(owner, notify);
9309
9347
  }));
9310
9348
  this.initialized = true;
9349
+ registerHandle(this);
9350
+ if (persistent === false) unrefHandle(this);
9311
9351
  return 0;
9312
9352
  } catch (error) {
9313
9353
  const code2 = error.code;
@@ -9316,6 +9356,7 @@ class FSEvent {
9316
9356
  }
9317
9357
  close() {
9318
9358
  var _a3;
9359
+ releaseHandle(this);
9319
9360
  this.initialized = false;
9320
9361
  try {
9321
9362
  (_a3 = __privateGet(this, _watcher)) == null ? void 0 : _a3.close();
@@ -9324,14 +9365,16 @@ class FSEvent {
9324
9365
  __privateSet(this, _watcher, null);
9325
9366
  }
9326
9367
  ref() {
9368
+ if (this.initialized) refHandle(this);
9327
9369
  }
9328
9370
  unref() {
9371
+ unrefHandle(this);
9329
9372
  }
9330
9373
  getAsyncId() {
9331
9374
  return 0;
9332
9375
  }
9333
9376
  hasRef() {
9334
- return this.initialized;
9377
+ return this.initialized && handleHasRef(this);
9335
9378
  }
9336
9379
  }
9337
9380
  _watcher = new WeakMap();
@@ -9428,9 +9471,14 @@ const fsBinding = {
9428
9471
  const tree2 = vfs$1();
9429
9472
  const bits = flagBits();
9430
9473
  const exists = tree2.existsSync(name);
9474
+ if (exists && ((flags & 3) !== 0 || (flags & (bits.create | bits.truncate | bits.append)) !== 0)) {
9475
+ tree2.accessSync(name, 2);
9476
+ }
9431
9477
  if ((flags & bits.excl) !== 0 && (flags & bits.create) !== 0 && exists) throw createNodeError$1("EEXIST", "open", name);
9432
9478
  if (!exists) {
9433
9479
  if ((flags & bits.create) === 0) throw createNodeError$1("ENOENT", "open", name);
9480
+ const parent = name.slice(0, name.lastIndexOf("/")) || "/";
9481
+ if (!tree2.statSync(parent).isDirectory()) throw createNodeError$1("ENOTDIR", "open", name);
9434
9482
  tree2.writeFileSync(name, "");
9435
9483
  } else if ((flags & bits.truncate) !== 0) {
9436
9484
  tree2.writeFileSync(name, "");
@@ -9443,6 +9491,7 @@ const fsBinding = {
9443
9491
  },
9444
9492
  close(fd2, req) {
9445
9493
  return answer(req, () => {
9494
+ fileFor(fd2);
9446
9495
  openFiles.delete(fd2);
9447
9496
  return void 0;
9448
9497
  });
@@ -9451,6 +9500,7 @@ const fsBinding = {
9451
9500
  read(fd2, buffer2, offset2, length, position, req) {
9452
9501
  return answer(req, () => {
9453
9502
  const file = fileFor(fd2);
9503
+ if ((file.flags & 3) === 1) throw createNodeError$1("EBADF", "read", file.path);
9454
9504
  if (file.cached === void 0) file.cached = file.tree.readFileSync(file.path);
9455
9505
  const bytes2 = file.cached;
9456
9506
  const from = position === null || position === void 0 || position < 0 ? file.position : position;
@@ -9473,17 +9523,27 @@ const fsBinding = {
9473
9523
  });
9474
9524
  },
9475
9525
  /** Node's fast path for `readFile` with an encoding it can decode itself. */
9476
- readFileUtf8(path2, _flags) {
9477
- return vfs$1().readFileSync(asPath(path2), "utf8");
9526
+ readFileUtf8(path2, flags) {
9527
+ const owned = typeof path2 !== "number";
9528
+ const fd2 = owned ? fsBinding.open(path2, flags, 438) : path2;
9529
+ try {
9530
+ const file = fileFor(fd2);
9531
+ const bytes2 = new Uint8Array(file.tree.statSync(file.path).size);
9532
+ const length = fsBinding.read(fd2, bytes2, 0, bytes2.length, -1);
9533
+ return new TextDecoder().decode(bytes2.subarray(0, length));
9534
+ } finally {
9535
+ if (owned) fsBinding.close(fd2);
9536
+ }
9478
9537
  },
9479
9538
  // ---- writing ------------------------------------------------------------
9480
9539
  writeBuffer(fd2, buffer2, offset2, length, position, req) {
9481
9540
  return answer(req, () => {
9482
9541
  const file = fileFor(fd2);
9542
+ if ((file.flags & 3) === 0) throw createNodeError$1("EBADF", "write", file.path);
9483
9543
  const tree2 = file.tree;
9484
9544
  const slice = buffer2.subarray(offset2, offset2 + length);
9485
9545
  const existing = file.cached ?? (tree2.existsSync(file.path) ? tree2.readFileSync(file.path) : new Uint8Array(0));
9486
- const at3 = position === null || position === void 0 || position < 0 ? file.position : position;
9546
+ const at3 = (file.flags & flagBits().append) !== 0 ? existing.length : position === null || position === void 0 || position < 0 ? file.position : position;
9487
9547
  const size = Math.max(existing.length, at3 + slice.length);
9488
9548
  const next2 = new Uint8Array(size);
9489
9549
  next2.set(existing, 0);
@@ -9513,15 +9573,14 @@ const fsBinding = {
9513
9573
  const bytes2 = encoding === "utf8" || encoding === void 0 || encoding === "utf-8" ? new TextEncoder().encode(value) : Uint8Array.from(value, (character) => character.charCodeAt(0) & 255);
9514
9574
  return fsBinding.writeBuffer(fd2, bytes2, 0, bytes2.length, position ?? null, req);
9515
9575
  },
9516
- writeFileUtf8(path2, data2, flags, _mode) {
9517
- const tree2 = vfs$1();
9518
- const name = asPath(path2);
9519
- if ((flags & flagBits().append) !== 0 && tree2.existsSync(name)) {
9520
- const existing = tree2.readFileSync(name, "utf8");
9521
- tree2.writeFileSync(name, existing + data2);
9522
- return void 0;
9576
+ writeFileUtf8(path2, data2, flags, mode) {
9577
+ const owned = typeof path2 !== "number";
9578
+ const fd2 = owned ? fsBinding.open(path2, flags, mode) : path2;
9579
+ try {
9580
+ fsBinding.writeString(fd2, data2, null, "utf8");
9581
+ } finally {
9582
+ if (owned) fsBinding.close(fd2);
9523
9583
  }
9524
- tree2.writeFileSync(name, data2);
9525
9584
  return void 0;
9526
9585
  },
9527
9586
  // ---- metadata -----------------------------------------------------------
@@ -9566,10 +9625,10 @@ const fsBinding = {
9566
9625
  return bigint ? BigInt64Array.from(values.map((value) => BigInt(value))) : Float64Array.from(values);
9567
9626
  });
9568
9627
  },
9569
- access(path2, _mode, req) {
9628
+ access(path2, mode, req) {
9570
9629
  return answer(req, () => {
9571
9630
  const name = asPath(path2);
9572
- if (!vfs$1().existsSync(name)) throw createNodeError$1("ENOENT", "access", name);
9631
+ vfs$1().accessSync(name, mode);
9573
9632
  return void 0;
9574
9633
  });
9575
9634
  },
@@ -9674,13 +9733,10 @@ const fsBinding = {
9674
9733
  if (tree2.existsSync(name)) {
9675
9734
  const stats = tree2.statSync(name);
9676
9735
  if (!recursive || !stats.isDirectory()) throw createNodeError$1("EEXIST", "mkdir", name);
9677
- const stored = pathModes.get(name);
9678
- if (stored !== void 0 && (stored & 511) !== wanted) throw createNodeError$1("EEXIST", "mkdir", name);
9679
- pathModes.set(name, wanted);
9680
9736
  return void 0;
9681
9737
  }
9682
9738
  tree2.mkdirSync(name, { recursive });
9683
- pathModes.set(name, wanted);
9739
+ tree2.chmodSync(name, wanted);
9684
9740
  return void 0;
9685
9741
  });
9686
9742
  },
@@ -9752,46 +9808,50 @@ const fsBinding = {
9752
9808
  }
9753
9809
  });
9754
9810
  },
9755
- // ---- the calls a page's filesystem has nothing to do -------------------
9756
- // Ownership and times are a kernel's bookkeeping. Permissions `chmod` sets
9757
- // are kept here, because a program that sets a mode and stats it back must
9758
- // see what it set, and a missing path is ENOENT as Node's is.
9811
+ // Metadata belongs to the filesystem, including a host's permission view.
9812
+ // Never keep a realm-global mode table or bypass that view for descriptors.
9759
9813
  chmod(path2, mode, req) {
9760
9814
  return answer(req, () => {
9761
9815
  const name = asPath(path2);
9762
- if (!vfs$1().existsSync(name)) throw createNodeError$1("ENOENT", "chmod", name);
9763
- pathModes.set(name, mode & 4095);
9816
+ vfs$1().chmodSync(name, mode);
9764
9817
  return void 0;
9765
9818
  });
9766
9819
  },
9767
9820
  fchmod(fd2, mode, req) {
9768
9821
  return answer(req, () => {
9769
9822
  const file = fileFor(fd2);
9770
- pathModes.set(file.path, mode & 4095);
9823
+ file.tree.chmodSync(file.path, mode);
9771
9824
  return void 0;
9772
9825
  });
9773
9826
  },
9774
- chown(path2, _uid, _gid, req) {
9775
- return answer(req, () => {
9776
- const name = asPath(path2);
9777
- if (!vfs$1().existsSync(name)) throw createNodeError$1("ENOENT", "chown", name);
9778
- return void 0;
9779
- });
9827
+ chown(_path, _uid, _gid, req) {
9828
+ return answer(req, () => unsupportedMetadata("chown"));
9780
9829
  },
9781
9830
  fchown(_fd, _uid, _gid, req) {
9782
- return answer(req, () => void 0);
9831
+ return answer(req, () => unsupportedMetadata("fchown"));
9783
9832
  },
9784
9833
  lchown(_path, _uid, _gid, req) {
9785
- return answer(req, () => void 0);
9834
+ return answer(req, () => unsupportedMetadata("lchown"));
9786
9835
  },
9787
- utimes(_path, _atime, _mtime, req) {
9788
- return answer(req, () => void 0);
9836
+ utimes(path2, atime, mtime, req) {
9837
+ return answer(req, () => {
9838
+ vfs$1().utimesSync(asPath(path2), new Date(atime * 1e3), new Date(mtime * 1e3));
9839
+ return void 0;
9840
+ });
9789
9841
  },
9790
- futimes(_fd, _atime, _mtime, req) {
9791
- return answer(req, () => void 0);
9842
+ futimes(fd2, atime, mtime, req) {
9843
+ return answer(req, () => {
9844
+ const file = fileFor(fd2);
9845
+ file.tree.utimesSync(file.path, new Date(atime * 1e3), new Date(mtime * 1e3));
9846
+ return void 0;
9847
+ });
9792
9848
  },
9793
- lutimes(_path, _atime, _mtime, req) {
9794
- return answer(req, () => void 0);
9849
+ lutimes(path2, atime, mtime, req) {
9850
+ return answer(req, () => {
9851
+ if (treeHoldsLinks()) return unsupportedMetadata("lutimes");
9852
+ vfs$1().utimesSync(asPath(path2), new Date(atime * 1e3), new Date(mtime * 1e3));
9853
+ return void 0;
9854
+ });
9795
9855
  },
9796
9856
  fsync(_fd, req) {
9797
9857
  return answer(req, () => void 0);
@@ -9802,6 +9862,7 @@ const fsBinding = {
9802
9862
  ftruncate(fd2, length, req) {
9803
9863
  return answer(req, () => {
9804
9864
  const file = fileFor(fd2);
9865
+ if ((file.flags & 3) === 0) throw createNodeError$1("EBADF", "ftruncate", file.path);
9805
9866
  const tree2 = file.tree;
9806
9867
  const bytes2 = file.cached ?? tree2.readFileSync(file.path);
9807
9868
  const next2 = new Uint8Array(length);
@@ -26098,6 +26159,16 @@ lazyExport("stream", "pipeline");
26098
26159
  lazyExport("stream", "finished");
26099
26160
  lazyModule("stream/promises");
26100
26161
  const streamPromisesModule = lazyModule("stream/promises");
26162
+ const nodeInodes = /* @__PURE__ */ new WeakMap();
26163
+ let nextInode = 1;
26164
+ function inodeFor(node) {
26165
+ let inode = nodeInodes.get(node);
26166
+ if (inode === void 0) {
26167
+ inode = nextInode++;
26168
+ nodeInodes.set(node, inode);
26169
+ }
26170
+ return inode;
26171
+ }
26101
26172
  function createNodeError(code2, syscall, path2, message) {
26102
26173
  const errno = {
26103
26174
  ENOENT: -2,
@@ -26383,6 +26454,17 @@ class VirtualFS {
26383
26454
  /**
26384
26455
  * Get stats for path
26385
26456
  */
26457
+ chmodSync(path2, mode) {
26458
+ const node = this.getNode(path2);
26459
+ if (!node) throw createNodeError("ENOENT", "chmod", path2);
26460
+ node.mode = mode & 4095;
26461
+ }
26462
+ utimesSync(path2, atime, mtime) {
26463
+ const node = this.getNode(path2);
26464
+ if (!node) throw createNodeError("ENOENT", "utimes", path2);
26465
+ node.atime = atime instanceof Date ? atime.getTime() : atime * 1e3;
26466
+ node.mtime = mtime instanceof Date ? mtime.getTime() : mtime * 1e3;
26467
+ }
26386
26468
  statSync(path2) {
26387
26469
  var _a3;
26388
26470
  const node = this.getNode(path2);
@@ -26401,7 +26483,7 @@ class VirtualFS {
26401
26483
  isFIFO: () => false,
26402
26484
  isSocket: () => false,
26403
26485
  size,
26404
- mode: node.type === "directory" ? 493 : 420,
26486
+ mode: node.mode ?? (node.type === "directory" ? 493 : 420),
26405
26487
  mtime: new Date(mtime),
26406
26488
  atime: new Date(atime),
26407
26489
  ctime: new Date(mtime),
@@ -26414,7 +26496,7 @@ class VirtualFS {
26414
26496
  uid: 1e3,
26415
26497
  gid: 1e3,
26416
26498
  dev: 0,
26417
- ino: 0,
26499
+ ino: inodeFor(node),
26418
26500
  rdev: 0,
26419
26501
  blksize: 4096,
26420
26502
  blocks: Math.ceil(size / 512)
@@ -26453,7 +26535,7 @@ class VirtualFS {
26453
26535
  uid: 1e3,
26454
26536
  gid: 1e3,
26455
26537
  dev: 0,
26456
- ino: 0,
26538
+ ino: inodeFor(node),
26457
26539
  rdev: 0,
26458
26540
  blksize: 4096,
26459
26541
  blocks: 0
@@ -68748,7 +68830,7 @@ function initChildProcess(vfs2) {
68748
68830
  };
68749
68831
  };
68750
68832
  const nodeCommand = uA("node", async (args, ctx) => {
68751
- var _a3, _b3;
68833
+ var _a3, _b3, _c3, _d3;
68752
68834
  if (!tree2) {
68753
68835
  return { stdout: "", stderr: "VFS not initialized\n", exitCode: 1 };
68754
68836
  }
@@ -68835,7 +68917,7 @@ function initChildProcess(vfs2) {
68835
68917
  // open, as a pipe whose writer has not closed.
68836
68918
  stdin: typeof ctx.stdin === "string" ? ctx.stdin : "",
68837
68919
  ...(streams == null ? void 0 : streams.held) || (streams == null ? void 0 : streams.stdinOpen) ? { stdinHeld: true } : {},
68838
- ...(streams == null ? void 0 : streams.held) ? { tty: true } : {},
68920
+ ...(streams == null ? void 0 : streams.held) || (streams == null ? void 0 : streams.terminal) ? { tty: true } : {},
68839
68921
  // The numbers this run was started with, so the guest's `process.pid`
68840
68922
  // is the one its parent's handle carries.
68841
68923
  ...runPid(runToken) ? { pid: runPid(runToken).pid, ppid: runPid(runToken).ppid } : {}
@@ -68877,12 +68959,28 @@ function initChildProcess(vfs2) {
68877
68959
  proc.argv0 = "node";
68878
68960
  proc.execArgv = execArgv;
68879
68961
  if (streams) streams.stdin = proc.stdin;
68880
- if (streams == null ? void 0 : streams.held) {
68962
+ if ((streams == null ? void 0 : streams.held) || (streams == null ? void 0 : streams.terminal)) {
68881
68963
  proc.stdout.isTTY = true;
68882
68964
  proc.stderr.isTTY = true;
68883
68965
  proc.stdin.isTTY = true;
68884
68966
  proc.stdin.setRawMode = () => proc.stdin;
68885
68967
  }
68968
+ const terminal = streams == null ? void 0 : streams.terminal;
68969
+ const resize = (columns, rows) => {
68970
+ for (const stream of [proc.stdout, proc.stderr]) {
68971
+ stream.columns = columns;
68972
+ stream.rows = rows;
68973
+ stream.emit("resize");
68974
+ }
68975
+ };
68976
+ let stopResize;
68977
+ let inputActive = true;
68978
+ let inputIterator;
68979
+ const readableInput = proc.stdin;
68980
+ const inputReading = () => {
68981
+ var _a4, _b4;
68982
+ return readableInput.readableFlowing === true || ((_a4 = readableInput._readableState) == null ? void 0 : _a4.reading) === true || ((_b4 = readableInput._readableState) == null ? void 0 : _b4.needReadable) === true || readableInput.listenerCount("data") > 0 || readableInput.listenerCount("readable") > 0;
68983
+ };
68886
68984
  if (Number.isInteger(channelFd) && channelFd >= 0 && runToken !== null) {
68887
68985
  enterRun(runToken, () => {
68888
68986
  attachChannel(proc, channelFd, channelSerialization);
@@ -68940,6 +69038,39 @@ ${reason.stack || ""}` : String(reason);
68940
69038
  };
68941
69039
  const detachUncaught = __onUncaughtException(proc, onUncaughtException);
68942
69040
  try {
69041
+ if (streams == null ? void 0 : streams.stdinStream) {
69042
+ inputIterator = streams.stdinStream[Symbol.asyncIterator]();
69043
+ const input = inputIterator;
69044
+ void (async () => {
69045
+ var _a4, _b4, _c4, _d4, _e3;
69046
+ const push = (chunk) => {
69047
+ if (runToken === null) proc.stdin.push(chunk);
69048
+ else enterRun(runToken, () => proc.stdin.push(chunk));
69049
+ };
69050
+ try {
69051
+ while (inputActive && !((_a4 = streams.signal) == null ? void 0 : _a4.aborted)) {
69052
+ while (inputActive && !((_b4 = streams.signal) == null ? void 0 : _b4.aborted) && (!inputReading() || readableInput.readableLength >= readableInput.readableHighWaterMark)) {
69053
+ await new Promise((resolve2) => (globalThis.__browserRuntimeNativeSetTimeout ?? setTimeout).call(globalThis, resolve2, 10));
69054
+ }
69055
+ if (!inputActive || ((_c4 = streams.signal) == null ? void 0 : _c4.aborted)) break;
69056
+ const next2 = await input.next();
69057
+ if (next2.done) break;
69058
+ if (!inputActive || ((_d4 = streams.signal) == null ? void 0 : _d4.aborted)) break;
69059
+ push(next2.value);
69060
+ }
69061
+ if (inputActive && !((_e3 = streams.signal) == null ? void 0 : _e3.aborted)) push(null);
69062
+ } catch (error) {
69063
+ if (inputActive) onUncaughtException(error);
69064
+ } finally {
69065
+ streams.stdinOpen = false;
69066
+ }
69067
+ })();
69068
+ }
69069
+ if (terminal) resize(terminal.columns, terminal.rows);
69070
+ stopResize = (_a3 = terminal == null ? void 0 : terminal.onResize) == null ? void 0 : _a3.call(terminal, (columns, rows) => {
69071
+ if (runToken === null) resize(columns, rows);
69072
+ else enterRun(runToken, () => resize(columns, rows));
69073
+ });
68943
69074
  let entrySettling;
68944
69075
  try {
68945
69076
  const runEntry = () => runtime2.runFile(resolvedPath);
@@ -68977,7 +69108,7 @@ ${error.stack || ""}` : String(error);
68977
69108
  }
68978
69109
  }
68979
69110
  const __ownsHandles = () => runToken !== null && (__ownedServerPorts(runToken).length > 0 || __ownedHandleCount(runToken) > 0);
68980
- const __printedThenWorking = () => pendingGuestTimers(proc) > 0 || heldWork().count > 0 || __ownsHandles();
69111
+ const __printedThenWorking = () => (streams == null ? void 0 : streams.stdinOpen) === true && inputReading() || pendingGuestTimers(proc) > 0 || heldWork().count > 0 || __ownsHandles();
68981
69112
  if ((stdout.length > 0 || stderr.length > 0) && !__printedThenWorking()) {
68982
69113
  await new Promise((r10) => (globalThis.__browserRuntimeNativeSetTimeout ?? setTimeout).call(globalThis, r10, 0));
68983
69114
  if (exitCalled) return { stdout, stderr, exitCode };
@@ -69000,16 +69131,16 @@ ${error.stack || ""}` : String(error);
69000
69131
  const startTime = Date.now();
69001
69132
  let lastOutputLen = stdout.length + stderr.length;
69002
69133
  let idleMs = 0;
69003
- const stillWorking = () => pendingGuestTimers(proc) > 0 || __ownsHandles();
69134
+ const stillWorking = () => (streams == null ? void 0 : streams.stdinOpen) === true && inputReading() || pendingGuestTimers(proc) > 0 || __ownsHandles();
69004
69135
  const isLongRunning = !!(streams == null ? void 0 : streams.held);
69005
69136
  while (!exitCalled) {
69006
- if ((_a3 = streams == null ? void 0 : streams.signal) == null ? void 0 : _a3.aborted) break;
69137
+ if ((_b3 = streams == null ? void 0 : streams.signal) == null ? void 0 : _b3.aborted) break;
69007
69138
  const raceResult = await Promise.race([
69008
69139
  exitPromise.then(() => "exit"),
69009
69140
  new Promise((r10) => (globalThis.__browserRuntimeNativeSetTimeout ?? setTimeout).call(globalThis, () => r10("tick"), CHECK_MS))
69010
69141
  ]);
69011
69142
  if (raceResult === "exit" || exitCalled) break;
69012
- if ((_b3 = streams == null ? void 0 : streams.signal) == null ? void 0 : _b3.aborted) break;
69143
+ if ((_c3 = streams == null ? void 0 : streams.signal) == null ? void 0 : _c3.aborted) break;
69013
69144
  const currentLen = stdout.length + stderr.length;
69014
69145
  if (currentLen > lastOutputLen) {
69015
69146
  lastOutputLen = currentLen;
@@ -69034,6 +69165,16 @@ ${error.stack || ""}` : String(error);
69034
69165
  } finally {
69035
69166
  detachRejections();
69036
69167
  detachUncaught();
69168
+ inputActive = false;
69169
+ try {
69170
+ void Promise.resolve((_d3 = inputIterator == null ? void 0 : inputIterator.return) == null ? void 0 : _d3.call(inputIterator)).catch(() => {
69171
+ });
69172
+ } catch {
69173
+ }
69174
+ try {
69175
+ stopResize == null ? void 0 : stopResize();
69176
+ } catch {
69177
+ }
69037
69178
  if (runToken !== null) {
69038
69179
  __releaseOwnedServers(runToken, false);
69039
69180
  __releaseOwnedHandles(runToken);
package/dist/index.d.ts CHANGED
@@ -55,6 +55,12 @@ export interface RunOptions {
55
55
  env?: Record<string, string>;
56
56
  /** What the shell reads on stdin, so a builtin reads what was piped to it. */
57
57
  stdin?: string;
58
+ stdinStream?: AsyncIterable<Uint8Array>;
59
+ terminal?: {
60
+ columns: number;
61
+ rows: number;
62
+ onResize?: (listener: (columns: number, rows: number) => void) => () => void;
63
+ };
58
64
  /** Callback for streaming stdout chunks as they arrive (for long-running commands like vitest watch) */
59
65
  onStdout?: (data: string) => void;
60
66
  /** Callback for streaming stderr chunks as they arrive */