@volter/tabnode 0.4.1 → 0.5.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.
Files changed (80) hide show
  1. package/dist/__sw__.js +259 -5
  2. package/dist/assets/{runtime-worker-LWfW6VWE.js → runtime-worker-B9buriZs.js} +4004 -2955
  3. package/dist/fetch-transport.d.ts +20 -0
  4. package/dist/guest-timers.d.ts +15 -0
  5. package/dist/http-client-transport.d.ts +19 -0
  6. package/dist/index.d.ts +11 -0
  7. package/dist/index.mjs +5187 -3051
  8. package/dist/native-stream-binding.d.ts +57 -0
  9. package/dist/native-stream-owner.d.ts +121 -0
  10. package/dist/node-lib/binding/duplex_stream.d.ts +26 -0
  11. package/dist/node-lib/binding/fds.d.ts +8 -0
  12. package/dist/node-lib/binding/fs.d.ts +311 -78
  13. package/dist/node-lib/binding/misc.d.ts +1 -0
  14. package/dist/node-lib/binding/pipe_wrap.d.ts +4 -4
  15. package/dist/node-lib/binding/process_wrap.d.ts +12 -1
  16. package/dist/node-lib/binding/stream_wrap.d.ts +27 -18
  17. package/dist/node-lib/binding/tcp_wrap.d.ts +0 -1
  18. package/dist/node-lib/binding/tty_wrap.d.ts +43 -2
  19. package/dist/node-lib/binding/util.d.ts +83 -0
  20. package/dist/node-lib/binding/uv.d.ts +4 -0
  21. package/dist/node-lib/fs-module.d.ts +1 -0
  22. package/dist/node-lib/internals/buffer-and-streams.d.ts +30 -17
  23. package/dist/node-lib/internals/index.d.ts +1 -1
  24. package/dist/node-lib/load.d.ts +17 -3
  25. package/dist/node-lib/module-hooks.d.ts +1 -0
  26. package/dist/node-lib/public-modules.d.ts +1 -1
  27. package/dist/node-lib/timers.d.ts +38 -0
  28. package/dist/node-process-host.d.ts +41 -0
  29. package/dist/process-registry.d.ts +39 -0
  30. package/dist/process-tokens.d.ts +11 -0
  31. package/dist/promise-ownership.d.ts +3 -0
  32. package/dist/runtime.d.ts +10 -9
  33. package/dist/shims/child_process.d.ts +2 -0
  34. package/dist/shims/crypto.d.ts +660 -119
  35. package/dist/shims/dns.d.ts +76 -2
  36. package/dist/shims/process.d.ts +1 -0
  37. package/dist/shims/string_decoder.d.ts +19 -3
  38. package/dist/shims/tls.d.ts +853 -31
  39. package/dist/source-edits.d.ts +7 -0
  40. package/package.json +1 -1
  41. package/src/code-transforms.ts +2 -3
  42. package/src/fetch-transport.ts +98 -0
  43. package/src/guest-timers.ts +89 -0
  44. package/src/http-client-transport.ts +169 -0
  45. package/src/index.ts +12 -0
  46. package/src/native-stream-binding.ts +393 -0
  47. package/src/native-stream-owner.ts +317 -0
  48. package/src/node-lib/binding/duplex_stream.ts +95 -0
  49. package/src/node-lib/binding/fds.ts +18 -1
  50. package/src/node-lib/binding/fs.ts +45 -17
  51. package/src/node-lib/binding/misc.ts +2 -0
  52. package/src/node-lib/binding/pipe_wrap.ts +35 -12
  53. package/src/node-lib/binding/process_wrap.ts +50 -9
  54. package/src/node-lib/binding/stream_wrap.ts +196 -72
  55. package/src/node-lib/binding/tcp_wrap.ts +43 -8
  56. package/src/node-lib/binding/tty_wrap.ts +135 -8
  57. package/src/node-lib/binding/util.ts +24 -15
  58. package/src/node-lib/binding/uv.ts +4 -0
  59. package/src/node-lib/fs-module.ts +2 -18
  60. package/src/node-lib/internal/navigator.js +149 -0
  61. package/src/node-lib/internals/buffer-and-streams.ts +50 -11
  62. package/src/node-lib/internals/events-util.ts +5 -0
  63. package/src/node-lib/internals/index.ts +12 -2
  64. package/src/node-lib/load.ts +170 -62
  65. package/src/node-lib/module-hooks.ts +5 -2
  66. package/src/node-lib/public-modules.ts +12 -48
  67. package/src/node-lib/sources.ts +4 -0
  68. package/src/node-lib/timers.ts +93 -0
  69. package/src/node-process-host.ts +124 -0
  70. package/src/process-registry.ts +144 -0
  71. package/src/process-tokens.ts +55 -6
  72. package/src/promise-ownership.ts +28 -0
  73. package/src/runtime.ts +90 -214
  74. package/src/shims/child_process.ts +310 -80
  75. package/src/shims/crypto.ts +115 -100
  76. package/src/shims/dns.ts +17 -1
  77. package/src/shims/process.ts +33 -11
  78. package/src/shims/string_decoder.ts +10 -6
  79. package/src/shims/tls.ts +49 -19
  80. package/src/source-edits.ts +20 -0
@@ -15,6 +15,7 @@ import { LibuvStreamWrap, type WriteWrap } from './stream_wrap';
15
15
  import { UV_EADDRINUSE, UV_ENOENT, UV_EBADF } from './uv';
16
16
  import { handleForFd, registerFd, releaseFd } from './fds';
17
17
  import { __adoptHandle, ownerOf } from './handles';
18
+ import { nativeStreamFor, registerNativeStreamConstructor } from '../../native-stream-binding';
18
19
 
19
20
  /** libuv's `uv_pipe_t` flavours, and the two chmod bits `listen` reads. */
20
21
  export const constants = {
@@ -51,9 +52,12 @@ export class Pipe extends LibuvStreamWrap {
51
52
  constructor(type: number = constants.SOCKET) {
52
53
  super();
53
54
  this.type = type;
55
+ this.attachNative('pipe', type);
54
56
  }
55
57
 
56
58
  bind(path: string): number {
59
+ const native = nativeStreamFor(this);
60
+ if (native) return native.call({ operation: 'bind', id: native.descriptor.id, address: path }).status;
57
61
  const held = boundPaths.get(path);
58
62
  if (held && held !== this) return UV_EADDRINUSE;
59
63
  this.path = path;
@@ -62,12 +66,20 @@ export class Pipe extends LibuvStreamWrap {
62
66
  }
63
67
 
64
68
  listen(_backlog: number): number {
69
+ const native = nativeStreamFor(this);
70
+ if (native) {
71
+ const status = native.call({ operation: 'listen', id: native.descriptor.id, backlog: _backlog }).status;
72
+ if (status === 0) this.listening = true;
73
+ return status;
74
+ }
65
75
  if (this.path === null) return UV_EBADF;
66
76
  this.listening = true;
67
77
  return 0;
68
78
  }
69
79
 
70
80
  connect(req: PipeConnectWrap, path: string): number {
81
+ const native = nativeStreamFor(this);
82
+ if (native) return native.connect(path, undefined, false, status => req.oncomplete?.(status, this, req, true, true));
71
83
  // libuv answers a connect through the request, never from the call.
72
84
  queueMicrotask(() => {
73
85
  if (this.closed) return;
@@ -76,7 +88,7 @@ export class Pipe extends LibuvStreamWrap {
76
88
  req.oncomplete?.(UV_ENOENT, this, req, true, true);
77
89
  return;
78
90
  }
79
- const accepted = new Pipe(server.type === constants.IPC ? constants.IPC : constants.SOCKET);
91
+ const accepted = new (server.constructor as typeof Pipe)(server.type === constants.IPC ? constants.IPC : constants.SOCKET);
80
92
  accepted.path = path;
81
93
  // The serving run owns what it accepted, not the connecting one.
82
94
  __adoptHandle(accepted, ownerOf(server));
@@ -95,6 +107,12 @@ export class Pipe extends LibuvStreamWrap {
95
107
  * opened it, because a child's channel is written to before the child runs.
96
108
  */
97
109
  open(fd: number): number {
110
+ const native = nativeStreamFor(this);
111
+ if (native) {
112
+ const status = native.call({ operation: 'open', id: native.descriptor.id, fd }).status;
113
+ if (status === 0) this.fd = fd;
114
+ return status;
115
+ }
98
116
  const held = handleForFd(fd) as LibuvStreamWrap | undefined;
99
117
  if (!held) return UV_EBADF;
100
118
  this.takeOverFrom(held);
@@ -111,8 +129,9 @@ export class Pipe extends LibuvStreamWrap {
111
129
 
112
130
  /** libuv's `dup()`: the same pairing, under the same flavour. */
113
131
  override duplicate(): Pipe {
114
- const copy = new Pipe(this.type);
115
- copy.takeOverFrom(this);
132
+ const native = nativeStreamFor(this);
133
+ const copy = native ? native.duplicate() as Pipe : new (this.constructor as typeof Pipe)(this.type);
134
+ if (!native) copy.shareConnectionFrom(this);
116
135
  return copy;
117
136
  }
118
137
 
@@ -133,14 +152,16 @@ export class Pipe extends LibuvStreamWrap {
133
152
  *
134
153
  * The handle becomes the receiving run's. Node's `send(message, socket)`
135
154
  * duplicates the sender's descriptor into the other process, and from then
136
- * on the socket is an active handle of that process's loop; here parent and
137
- * child are one realm and there is nothing to duplicate, so the move is the
138
- * run ownership the engine counts. Without it, openvscode-server's extension
155
+ * on the socket is an active handle of that process's loop. The binding
156
+ * duplicates the wrapper and assigns that reference to the receiver.
157
+ * Without that ownership, openvscode-server's extension
139
158
  * host -- a program whose only handle is the socket its parent sent it --
140
159
  * read as idle and was settled with exit 0.
141
160
  */
142
- override writeUtf8String(req: WriteWrap, text: string, handle?: unknown): number {
143
- if (handle === undefined || handle === null) return super.writeUtf8String(req, text);
161
+ protected override dispatchWrite(req: WriteWrap, bytes: Uint8Array, handle?: unknown): number {
162
+ const native = nativeStreamFor(this);
163
+ if (native) return native.write(req, [bytes], handle);
164
+ if (handle === undefined || handle === null) return super.dispatchWrite(req, bytes);
144
165
  // A descriptor crosses as a duplicate, as `SCM_RIGHTS` hands one over: the
145
166
  // receiver gets its own handle on the same connection, so the sender's
146
167
  // close on `NODE_HANDLE_ACK` -- which Node's `child_process.js` does the
@@ -148,11 +169,12 @@ export class Pipe extends LibuvStreamWrap {
148
169
  // a listening server's, has nothing to duplicate and crosses as it is.
149
170
  const sent = handle as LibuvStreamWrap;
150
171
  const given = sent.peer !== null ? sent.duplicate() : sent;
151
- // The receiving run owns it from here: parent and child are one realm and
152
- // there is no descriptor table to move it in, so the move is the count the
153
- // engine keeps of what holds a run open.
172
+ // The receiving run owns this reference; the sender retains its own until
173
+ // Node closes it on acknowledgement, or continues using it with keepOpen.
154
174
  __adoptHandle(given, ownerOf(this.peer ?? this));
155
- return super.writeUtf8String(req, text, given);
175
+ const status = super.dispatchWrite(req, bytes, given);
176
+ if (status !== 0 && given !== sent) given.close();
177
+ return status;
156
178
  }
157
179
 
158
180
  protected override onCloseHandle(): void {
@@ -163,4 +185,5 @@ export class Pipe extends LibuvStreamWrap {
163
185
  }
164
186
  }
165
187
 
188
+ registerNativeStreamConstructor('pipe', Pipe);
166
189
  export default { Pipe, PipeConnectWrap, constants };
@@ -27,7 +27,9 @@ import {
27
27
  registerHandle, refHandle, unrefHandle, handleHasRef, releaseHandle, __adoptHandle,
28
28
  ownerOf, type OwnedHandle,
29
29
  } from './handles';
30
- import { __runFor, type ProcessToken } from '../../process-tokens';
30
+ import { __runFor, mintPid, type ProcessToken } from '../../process-tokens';
31
+ import { handleForFd } from './fds';
32
+ import { TTY, type TerminalState } from './tty_wrap';
31
33
 
32
34
  /** One entry of Node's `options.stdio`, as `getValidStdio` builds it. */
33
35
  export interface StdioEntry {
@@ -85,8 +87,14 @@ export interface RunRequest {
85
87
  stderrIsPipe: boolean;
86
88
  /** True when the child's fd 0 is a pipe whose writer has not closed. */
87
89
  stdinIsPipe: boolean;
90
+ terminal?: TerminalState;
88
91
  /** The IPC channel the child is started with, where it has one. */
89
92
  channel?: RunChannel;
93
+ /**
94
+ * The child's pipes past fd 2 (`stdio: [..., 'pipe', 'pipe']`), each at the
95
+ * number the child reads or writes it by, with the child's end of it.
96
+ */
97
+ descriptors: { fd: number; pipe: Pipe }[];
90
98
  /** Called once, when the run has ended. */
91
99
  exit(code: number, signal: string | null): void;
92
100
  }
@@ -100,7 +108,7 @@ export interface StartedRun {
100
108
  /** libuv's `uv_process_kill`: 0, or `UV_ESRCH` for a run already over. */
101
109
  kill(signal: string): number;
102
110
  /** Bytes the parent wrote to the child's fd 0. */
103
- writeStdin(bytes: Uint8Array): void;
111
+ writeStdin(bytes: Uint8Array): void | Promise<void>;
104
112
  /** The parent closed the child's fd 0. */
105
113
  endStdin(): void;
106
114
  }
@@ -123,7 +131,6 @@ export function setProcessRunner(next: ProcessRunner): void {
123
131
  runner = next;
124
132
  }
125
133
 
126
- let nextPid = 1001 + Math.floor(Math.random() * 30000);
127
134
  let nextAsyncId = 1;
128
135
 
129
136
  /** `envPairs` is `KEY=value` strings; the engine's shell takes a record. */
@@ -208,6 +215,7 @@ export class Process implements OwnedHandle {
208
215
  private closed = false;
209
216
  /** The stdio entries this run was started with, and their far ends. */
210
217
  private stdio: StdioEntry[] = [];
218
+ private terminalEnds = new Map<number, TTY>();
211
219
  private readonly asyncId = nextAsyncId++;
212
220
 
213
221
  constructor() {
@@ -250,13 +258,30 @@ export class Process implements OwnedHandle {
250
258
  stderr: null,
251
259
  stderrIsPipe: false,
252
260
  stdinIsPipe: false,
261
+ descriptors: [],
253
262
  exit: (code, signal) => { this.reportExit(code, signal); },
254
263
  };
255
264
 
256
- let stdinFar: Pipe | null = null;
265
+ let stdinFar: LibuvStreamWrap | null = null;
257
266
  for (let index = 0; index < this.stdio.length; index += 1) {
258
267
  const entry = this.stdio[index];
259
268
  if (!entry) continue;
269
+ if (index < 3 && !entry.ipc) {
270
+ const descriptor = entry.type === 'fd' || entry.type === 'inherit'
271
+ ? handleForFd(entry.fd ?? index) : entry.handle;
272
+ if (descriptor instanceof TTY && descriptor.slave) {
273
+ // Inherit an independent descriptor reference, never the parent's
274
+ // wrapper. Failed spawn and child exit release only this copy.
275
+ const inherited = descriptor.duplicate();
276
+ __adoptHandle(inherited, null);
277
+ this.terminalEnds.set(index, inherited);
278
+ request.terminal = inherited.terminal;
279
+ if (index === 0) { stdinFar = inherited; request.stdinIsPipe = true; }
280
+ else if (index === 1) request.stdout = text => this.toPipe(index, text);
281
+ else request.stderr = text => this.toPipe(index, text);
282
+ continue;
283
+ }
284
+ }
260
285
  const far = this.pairFarEnd(entry);
261
286
  if (entry.ipc) {
262
287
  if (far) {
@@ -273,7 +298,12 @@ export class Process implements OwnedHandle {
273
298
  request.stdinIsPipe = far !== null;
274
299
  continue;
275
300
  }
276
- if (index !== 1 && index !== 2) continue;
301
+ if (index > 2) {
302
+ // A pipe past fd 2 is the child's too, at its own number: libuv
303
+ // hands a child every `UV_CREATE_PIPE` entry, not only stdio's three.
304
+ if (far) request.descriptors.push({ fd: index, pipe: far });
305
+ continue;
306
+ }
277
307
  if (far) {
278
308
  const to = index;
279
309
  const write = (text: string): void => { this.toPipe(to, text); };
@@ -297,7 +327,7 @@ export class Process implements OwnedHandle {
297
327
  this.run = runner.start(request);
298
328
  // The number the parent reads off the handle is the child's own
299
329
  // `process.pid`, as it is in Node; a runner that names none is given one.
300
- this.pid = this.run.pid ?? nextPid++;
330
+ this.pid = this.run.pid ?? mintPid();
301
331
  if (stdinFar) this.readStdinFrom(stdinFar);
302
332
  return 0;
303
333
  }
@@ -337,7 +367,7 @@ export class Process implements OwnedHandle {
337
367
 
338
368
  /** The far end of fd `index`, whoever holds it now. */
339
369
  private farEndAt(index: number): LibuvStreamWrap | null {
340
- return this.stdio[index]?.handle?.peer ?? null;
370
+ return this.terminalEnds.get(index) ?? this.stdio[index]?.handle?.peer ?? null;
341
371
  }
342
372
 
343
373
  private closeFarEnds(): void {
@@ -356,11 +386,22 @@ export class Process implements OwnedHandle {
356
386
  }
357
387
 
358
388
  /** Bytes the parent writes reach the child's fd 0; the parent's EOF ends it. */
359
- private readStdinFrom(far: Pipe): void {
389
+ private readStdinFrom(far: LibuvStreamWrap): void {
360
390
  far.onread = (arrayBuffer: ArrayBuffer | null): void => {
361
391
  const length = streamBaseState[kReadBytesOrError];
362
392
  if (length <= 0 || arrayBuffer === null) { this.run?.endStdin(); return; }
363
- this.run?.writeStdin(new Uint8Array(arrayBuffer, streamBaseState[kArrayBufferOffset], length).slice());
393
+ const run = this.run;
394
+ const consumed = run?.writeStdin(new Uint8Array(arrayBuffer, streamBaseState[kArrayBufferOffset], length).slice());
395
+ if (consumed) {
396
+ far.readStop();
397
+ void consumed.then(() => {
398
+ if (this.run === run && !this.ended && !far.closed) far.readStart();
399
+ }, () => {
400
+ // A stopped receiver closes its input pipe. Do not resume a reader
401
+ // or deliver its queued bytes to a later run.
402
+ if (this.run === run && !this.ended) { far.close(); run?.endStdin(); }
403
+ });
404
+ }
364
405
  };
365
406
  far.readStart();
366
407
  }