@volter/tabnode 0.4.0 → 0.4.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.
@@ -7475,8 +7475,12 @@ function signalNameOf(signal) {
7475
7475
  if (typeof signal === "string") return signal;
7476
7476
  return signalNames[signal] ?? "SIGTERM";
7477
7477
  }
7478
- function spawningDirectory() {
7479
- const realm = globalThis.process;
7478
+ function parentProcess(token) {
7479
+ var _a3;
7480
+ return token !== null ? (_a3 = __runFor(token)) == null ? void 0 : _a3.process : globalThis.process;
7481
+ }
7482
+ function spawningDirectory(token) {
7483
+ const realm = parentProcess(token);
7480
7484
  if (typeof (realm == null ? void 0 : realm.cwd) !== "function") return void 0;
7481
7485
  try {
7482
7486
  return realm.cwd();
@@ -7484,9 +7488,13 @@ function spawningDirectory() {
7484
7488
  return void 0;
7485
7489
  }
7486
7490
  }
7487
- function inheritedWriter$1(fd2) {
7491
+ function inheritedWriter$1(fd2, token) {
7488
7492
  if (fd2 !== 1 && fd2 !== 2) return null;
7489
- const realm = globalThis.process;
7493
+ if (token !== null) {
7494
+ const parent = __runFor(token);
7495
+ return (fd2 === 1 ? parent == null ? void 0 : parent.stdout : parent == null ? void 0 : parent.stderr) ?? null;
7496
+ }
7497
+ const realm = parentProcess(token);
7490
7498
  const stream = fd2 === 1 ? realm == null ? void 0 : realm.stdout : realm == null ? void 0 : realm.stderr;
7491
7499
  if (!stream || typeof stream.write !== "function") return null;
7492
7500
  return (text2) => {
@@ -7538,7 +7546,7 @@ class Process {
7538
7546
  const request = {
7539
7547
  file: options.file,
7540
7548
  args: options.args ?? [options.file],
7541
- cwd: options.cwd ?? spawningDirectory(),
7549
+ cwd: options.cwd ?? spawningDirectory(ownerOf(this)),
7542
7550
  env,
7543
7551
  detached: options.detached === true,
7544
7552
  stdout: null,
@@ -7581,7 +7589,7 @@ class Process {
7581
7589
  request.stderrIsPipe = true;
7582
7590
  }
7583
7591
  } else if (entry.type === "inherit" || entry.type === "fd") {
7584
- const inherited = inheritedWriter$1(entry.fd ?? index2);
7592
+ const inherited = inheritedWriter$1(entry.fd ?? index2, ownerOf(this));
7585
7593
  if (index2 === 1) request.stdout = inherited;
7586
7594
  else request.stderr = inherited;
7587
7595
  }
@@ -68925,6 +68933,9 @@ function initChildProcess(vfs2) {
68925
68933
  const proc = runtime2.getProcess();
68926
68934
  if (runToken !== null) setRunPid(runToken, proc.pid, proc.ppid);
68927
68935
  const releaseRun = runToken === null ? null : __recordRun(runToken, {
68936
+ process: proc,
68937
+ stdout: appendStdout,
68938
+ stderr: appendStderr,
68928
68939
  pendingTimers: () => pendingGuestTimers(proc),
68929
68940
  stopTimers: () => stopGuestTimers(proc),
68930
68941
  reportUncaught: (error) => __reportUncaughtException(proc, error)
package/dist/index.mjs CHANGED
@@ -7188,8 +7188,12 @@ function signalNameOf(signal) {
7188
7188
  if (typeof signal === "string") return signal;
7189
7189
  return signalNames[signal] ?? "SIGTERM";
7190
7190
  }
7191
- function spawningDirectory() {
7192
- const realm = globalThis.process;
7191
+ function parentProcess(token) {
7192
+ var _a2;
7193
+ return token !== null ? (_a2 = __runFor(token)) == null ? void 0 : _a2.process : globalThis.process;
7194
+ }
7195
+ function spawningDirectory(token) {
7196
+ const realm = parentProcess(token);
7193
7197
  if (typeof (realm == null ? void 0 : realm.cwd) !== "function") return void 0;
7194
7198
  try {
7195
7199
  return realm.cwd();
@@ -7197,9 +7201,13 @@ function spawningDirectory() {
7197
7201
  return void 0;
7198
7202
  }
7199
7203
  }
7200
- function inheritedWriter$1(fd) {
7204
+ function inheritedWriter$1(fd, token) {
7201
7205
  if (fd !== 1 && fd !== 2) return null;
7202
- const realm = globalThis.process;
7206
+ if (token !== null) {
7207
+ const parent = __runFor(token);
7208
+ return (fd === 1 ? parent == null ? void 0 : parent.stdout : parent == null ? void 0 : parent.stderr) ?? null;
7209
+ }
7210
+ const realm = parentProcess(token);
7203
7211
  const stream = fd === 1 ? realm == null ? void 0 : realm.stdout : realm == null ? void 0 : realm.stderr;
7204
7212
  if (!stream || typeof stream.write !== "function") return null;
7205
7213
  return (text2) => {
@@ -7251,7 +7259,7 @@ class Process {
7251
7259
  const request = {
7252
7260
  file: options.file,
7253
7261
  args: options.args ?? [options.file],
7254
- cwd: options.cwd ?? spawningDirectory(),
7262
+ cwd: options.cwd ?? spawningDirectory(ownerOf(this)),
7255
7263
  env,
7256
7264
  detached: options.detached === true,
7257
7265
  stdout: null,
@@ -7294,7 +7302,7 @@ class Process {
7294
7302
  request.stderrIsPipe = true;
7295
7303
  }
7296
7304
  } else if (entry.type === "inherit" || entry.type === "fd") {
7297
- const inherited = inheritedWriter$1(entry.fd ?? index2);
7305
+ const inherited = inheritedWriter$1(entry.fd ?? index2, ownerOf(this));
7298
7306
  if (index2 === 1) request.stdout = inherited;
7299
7307
  else request.stderr = inherited;
7300
7308
  }
@@ -28045,6 +28053,9 @@ function initChildProcess(vfs2) {
28045
28053
  const proc = runtime.getProcess();
28046
28054
  if (runToken !== null) setRunPid(runToken, proc.pid, proc.ppid);
28047
28055
  const releaseRun = runToken === null ? null : __recordRun(runToken, {
28056
+ process: proc,
28057
+ stdout: appendStdout,
28058
+ stderr: appendStderr,
28048
28059
  pendingTimers: () => pendingGuestTimers(proc),
28049
28060
  stopTimers: () => stopGuestTimers(proc),
28050
28061
  reportUncaught: (error) => __reportUncaughtException(proc, error)
@@ -33927,7 +33938,7 @@ class WorkerRuntime {
33927
33938
  this.worker = new Worker(
33928
33939
  new URL(
33929
33940
  /* @vite-ignore */
33930
- "/assets/runtime-worker-DoV8lqBn.js",
33941
+ "/assets/runtime-worker-LWfW6VWE.js",
33931
33942
  import.meta.url
33932
33943
  ),
33933
33944
  { type: "module" }
@@ -21,7 +21,7 @@
21
21
  import { LibuvStreamWrap } from './stream_wrap';
22
22
  import { Pipe } from './pipe_wrap';
23
23
  import { type OwnedHandle } from './handles';
24
- import type { ProcessToken } from '../../process-tokens';
24
+ import { type ProcessToken } from '../../process-tokens';
25
25
  /** One entry of Node's `options.stdio`, as `getValidStdio` builds it. */
26
26
  export interface StdioEntry {
27
27
  type: 'pipe' | 'overlapped' | 'ignore' | 'inherit' | 'fd' | 'wrap';
@@ -14,12 +14,17 @@
14
14
  * for both. The guest never sees the variable: the environment the `node`
15
15
  * command gives its process has it removed.
16
16
  */
17
+ import type { Process } from './shims/process';
17
18
  /** Whatever the embedding runtime uses to name one guest process. */
18
19
  export type ProcessToken = string;
19
20
  /** The environment variable a run's name travels in, from `exec` to `node`. */
20
21
  export declare const PROCESS_TOKEN_ENV = "__TABNODE_PROCESS_TOKEN";
21
22
  /** What the engine can answer about one named run. */
22
23
  export interface OwnedRun {
24
+ /** The guest whose cwd and inherited streams belong to this run. */
25
+ process: Process;
26
+ stdout(text: string): void;
27
+ stderr(text: string): void;
23
28
  /** The timers the run's guest still holds, as Node's loop counts them. */
24
29
  pendingTimers(): number;
25
30
  /** Clear those timers, as ending the process clears them. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/tabnode",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Node's own library, in a browser tab.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -25,9 +25,9 @@ import { Pipe, constants as pipeConstants } from './pipe_wrap';
25
25
  import { UV_ENOENT, UV_ESRCH } from './uv';
26
26
  import {
27
27
  registerHandle, refHandle, unrefHandle, handleHasRef, releaseHandle, __adoptHandle,
28
- type OwnedHandle,
28
+ ownerOf, type OwnedHandle,
29
29
  } from './handles';
30
- import type { ProcessToken } from '../../process-tokens';
30
+ import { __runFor, type ProcessToken } from '../../process-tokens';
31
31
 
32
32
  /** One entry of Node's `options.stdio`, as `getValidStdio` builds it. */
33
33
  export interface StdioEntry {
@@ -154,18 +154,31 @@ function signalNameOf(signal: number | string): string {
154
154
  * `/work` means `/work/child.js`, and resolved against the engine's shell's
155
155
  * own root it meant `/child.js` and a child that exited 1.
156
156
  */
157
- function spawningDirectory(): string | undefined {
158
- const realm = (globalThis as unknown as { process?: { cwd?: () => string } }).process;
157
+ // A shared realm's process global is whichever guest ran last. The spawning
158
+ // handle belongs to the parent: inherited stdio and cwd must come from that
159
+ // parent's process, even while a sibling starts or exits. A failed fork was
160
+ // replaying stderr into its own closed IPC-backed stream (write EBADF).
161
+ function parentProcess(token: ProcessToken | null) {
162
+ return token !== null ? __runFor(token)?.process
163
+ : (globalThis as unknown as { process?: { cwd?: () => string; stdout?: { write(text: string): unknown }; stderr?: { write(text: string): unknown } } }).process;
164
+ }
165
+
166
+ function spawningDirectory(token: ProcessToken | null): string | undefined {
167
+ const realm = parentProcess(token);
159
168
  if (typeof realm?.cwd !== 'function') return undefined;
160
169
  try { return realm.cwd(); } catch { return undefined; }
161
170
  }
162
171
 
163
172
  /** Where an `inherit` entry's bytes go: the spawning program's own stream. */
164
- function inheritedWriter(fd: number): ((text: string) => void) | null {
173
+ function inheritedWriter(fd: number, token: ProcessToken | null): ((text: string) => void) | null {
165
174
  if (fd !== 1 && fd !== 2) return null;
166
- const realm = (globalThis as unknown as {
167
- process?: { stdout?: { write(text: string): unknown }; stderr?: { write(text: string): unknown } };
168
- }).process;
175
+ // Inherit the descriptor's original sink, not a guest replacement of
176
+ // process.stderr.write (a child commonly redirects its console over IPC).
177
+ if (token !== null) {
178
+ const parent = __runFor(token);
179
+ return (fd === 1 ? parent?.stdout : parent?.stderr) ?? null;
180
+ }
181
+ const realm = parentProcess(token);
169
182
  const stream = fd === 1 ? realm?.stdout : realm?.stderr;
170
183
  if (!stream || typeof stream.write !== 'function') return null;
171
184
  return (text: string) => {
@@ -230,7 +243,7 @@ export class Process implements OwnedHandle {
230
243
  const request: RunRequest = {
231
244
  file: options.file,
232
245
  args: options.args ?? [options.file],
233
- cwd: options.cwd ?? spawningDirectory(),
246
+ cwd: options.cwd ?? spawningDirectory(ownerOf(this)),
234
247
  env,
235
248
  detached: options.detached === true,
236
249
  stdout: null,
@@ -270,7 +283,7 @@ export class Process implements OwnedHandle {
270
283
  request.stderrIsPipe = true;
271
284
  }
272
285
  } else if (entry.type === 'inherit' || entry.type === 'fd') {
273
- const inherited = inheritedWriter(entry.fd ?? index);
286
+ const inherited = inheritedWriter(entry.fd ?? index, ownerOf(this));
274
287
  if (index === 1) request.stdout = inherited;
275
288
  else request.stderr = inherited;
276
289
  }
@@ -15,6 +15,7 @@
15
15
  * command gives its process has it removed.
16
16
  */
17
17
 
18
+ import type { Process } from './shims/process';
18
19
  import { AsyncLocalStorage } from './shims/async_hooks';
19
20
 
20
21
  /** Whatever the embedding runtime uses to name one guest process. */
@@ -25,6 +26,10 @@ export const PROCESS_TOKEN_ENV = '__TABNODE_PROCESS_TOKEN';
25
26
 
26
27
  /** What the engine can answer about one named run. */
27
28
  export interface OwnedRun {
29
+ /** The guest whose cwd and inherited streams belong to this run. */
30
+ process: Process;
31
+ stdout(text: string): void;
32
+ stderr(text: string): void;
28
33
  /** The timers the run's guest still holds, as Node's loop counts them. */
29
34
  pendingTimers(): number;
30
35
  /** Clear those timers, as ending the process clears them. */
@@ -485,6 +485,9 @@ export function initChildProcess(vfs: VirtualFS): void {
485
485
  // whether a pid is a live process.
486
486
  if (runToken !== null) setRunPid(runToken, proc.pid, proc.ppid);
487
487
  const releaseRun = runToken === null ? null : __recordRun(runToken, {
488
+ process: proc,
489
+ stdout: appendStdout,
490
+ stderr: appendStderr,
488
491
  pendingTimers: () => pendingGuestTimers(proc),
489
492
  stopTimers: () => stopGuestTimers(proc),
490
493
  reportUncaught: (error: unknown) => __reportUncaughtException(proc, error),