node-pty 1.1.0-beta3 → 1.1.0-beta30

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 (42) hide show
  1. package/README.md +6 -5
  2. package/binding.gyp +4 -15
  3. package/deps/winpty/src/agent/Agent.cc +11 -3
  4. package/lib/unixTerminal.js +4 -27
  5. package/lib/unixTerminal.js.map +1 -1
  6. package/lib/unixTerminal.test.js +3 -5
  7. package/lib/unixTerminal.test.js.map +1 -1
  8. package/lib/windowsConoutConnection.js +0 -4
  9. package/lib/windowsConoutConnection.js.map +1 -1
  10. package/lib/windowsPtyAgent.js +41 -21
  11. package/lib/windowsPtyAgent.js.map +1 -1
  12. package/lib/windowsTerminal.js +1 -1
  13. package/lib/windowsTerminal.js.map +1 -1
  14. package/lib/windowsTerminal.test.js +38 -26
  15. package/lib/windowsTerminal.test.js.map +1 -1
  16. package/lib/worker/conoutSocketWorker.js +13 -10
  17. package/lib/worker/conoutSocketWorker.js.map +1 -1
  18. package/package.json +4 -2
  19. package/scripts/increment-version.js +54 -0
  20. package/scripts/post-install.js +51 -10
  21. package/scripts/publish.js +7 -4
  22. package/src/interfaces.ts +1 -0
  23. package/src/native.d.ts +5 -5
  24. package/src/unix/pty.cc +241 -284
  25. package/src/unixTerminal.test.ts +3 -6
  26. package/src/unixTerminal.ts +4 -18
  27. package/src/win/conpty.cc +264 -185
  28. package/src/win/conpty.h +41 -0
  29. package/src/win/conpty_console_list.cc +16 -15
  30. package/src/win/path_util.cc +39 -36
  31. package/src/win/path_util.h +5 -2
  32. package/src/win/winpty.cc +108 -151
  33. package/src/windowsConoutConnection.ts +0 -4
  34. package/src/windowsPtyAgent.ts +38 -20
  35. package/src/windowsTerminal.test.ts +43 -32
  36. package/src/windowsTerminal.ts +1 -1
  37. package/src/worker/conoutSocketWorker.ts +14 -11
  38. package/third_party/conpty/1.22.250204002/win10-arm64/OpenConsole.exe +0 -0
  39. package/third_party/conpty/1.22.250204002/win10-arm64/conpty.dll +0 -0
  40. package/third_party/conpty/1.22.250204002/win10-x64/OpenConsole.exe +0 -0
  41. package/third_party/conpty/1.22.250204002/win10-x64/conpty.dll +0 -0
  42. package/typings/node-pty.d.ts +8 -0
@@ -7,9 +7,9 @@
7
7
  import * as fs from 'fs';
8
8
  import * as os from 'os';
9
9
  import * as path from 'path';
10
+ import { fork } from 'child_process';
10
11
  import { Socket } from 'net';
11
12
  import { ArgvOrCommandLine } from './types';
12
- import { fork } from 'child_process';
13
13
  import { ConoutConnection } from './windowsConoutConnection';
14
14
 
15
15
  let conptyNative: IConptyNative;
@@ -54,6 +54,7 @@ export class WindowsPtyAgent {
54
54
  rows: number,
55
55
  debug: boolean,
56
56
  private _useConpty: boolean | undefined,
57
+ private _useConptyDll: boolean = false,
57
58
  conptyInheritCursor: boolean = false
58
59
  ) {
59
60
  if (this._useConpty === undefined || this._useConpty === true) {
@@ -99,7 +100,7 @@ export class WindowsPtyAgent {
99
100
  // Open pty session.
100
101
  let term: IConptyProcess | IWinptyProcess;
101
102
  if (this._useConpty) {
102
- term = (this._ptyNative as IConptyNative).startProcess(file, cols, rows, debug, this._generatePipeName(), conptyInheritCursor);
103
+ term = (this._ptyNative as IConptyNative).startProcess(file, cols, rows, debug, this._generatePipeName(), conptyInheritCursor, this._useConptyDll);
103
104
  } else {
104
105
  term = (this._ptyNative as IWinptyNative).startProcess(file, commandLine, env, cwd, cols, rows, debug);
105
106
  this._pid = (term as IWinptyProcess).pid;
@@ -134,7 +135,7 @@ export class WindowsPtyAgent {
134
135
  this._inSocket.setEncoding('utf8');
135
136
 
136
137
  if (this._useConpty) {
137
- const connect = (this._ptyNative as IConptyNative).connect(this._pty, commandLine, cwd, env, c => this._$onProcessExit(c));
138
+ const connect = (this._ptyNative as IConptyNative).connect(this._pty, commandLine, cwd, env, this._useConptyDll, c => this._$onProcessExit(c));
138
139
  this._innerPid = connect.pid;
139
140
  }
140
141
  }
@@ -144,33 +145,43 @@ export class WindowsPtyAgent {
144
145
  if (this._exitCode !== undefined) {
145
146
  throw new Error('Cannot resize a pty that has already exited');
146
147
  }
147
- this._ptyNative.resize(this._pty, cols, rows);
148
+ (this._ptyNative as IConptyNative).resize(this._pty, cols, rows, this._useConptyDll);
148
149
  return;
149
150
  }
150
- this._ptyNative.resize(this._pid, cols, rows);
151
+ (this._ptyNative as IWinptyNative).resize(this._pid, cols, rows);
151
152
  }
152
153
 
153
154
  public clear(): void {
154
155
  if (this._useConpty) {
155
- (this._ptyNative as IConptyNative).clear(this._pty);
156
+ (this._ptyNative as IConptyNative).clear(this._pty, this._useConptyDll);
156
157
  }
157
158
  }
158
159
 
159
160
  public kill(): void {
160
- this._inSocket.readable = false;
161
- this._outSocket.readable = false;
162
161
  // Tell the agent to kill the pty, this releases handles to the process
163
162
  if (this._useConpty) {
164
- this._getConsoleProcessList().then(consoleProcessList => {
165
- consoleProcessList.forEach((pid: number) => {
166
- try {
167
- process.kill(pid);
168
- } catch (e) {
169
- // Ignore if process cannot be found (kill ESRCH error)
170
- }
163
+ if (!this._useConptyDll) {
164
+ this._inSocket.readable = false;
165
+ this._outSocket.readable = false;
166
+ this._getConsoleProcessList().then(consoleProcessList => {
167
+ consoleProcessList.forEach((pid: number) => {
168
+ try {
169
+ process.kill(pid);
170
+ } catch (e) {
171
+ // Ignore if process cannot be found (kill ESRCH error)
172
+ }
173
+ });
171
174
  });
172
- (this._ptyNative as IConptyNative).kill(this._pty);
173
- });
175
+ (this._ptyNative as IConptyNative).kill(this._pty, this._useConptyDll);
176
+ this._conoutSocketWorker.dispose();
177
+ } else {
178
+ // Close the input write handle to signal the end of session.
179
+ this._inSocket.destroy();
180
+ (this._ptyNative as IConptyNative).kill(this._pty, this._useConptyDll);
181
+ this._outSocket.on('data', () => {
182
+ this._conoutSocketWorker.dispose();
183
+ });
184
+ }
174
185
  } else {
175
186
  // Because pty.kill closes the handle, it will kill most processes by itself.
176
187
  // Process IDs can be reused as soon as all handles to them are
@@ -188,7 +199,6 @@ export class WindowsPtyAgent {
188
199
  }
189
200
  });
190
201
  }
191
- this._conoutSocketWorker.dispose();
192
202
  }
193
203
 
194
204
  private _getConsoleProcessList(): Promise<number[]> {
@@ -232,11 +242,16 @@ export class WindowsPtyAgent {
232
242
  */
233
243
  private _$onProcessExit(exitCode: number): void {
234
244
  this._exitCode = exitCode;
235
- this._flushDataAndCleanUp();
236
- this._outSocket.on('data', () => this._flushDataAndCleanUp());
245
+ if (!this._useConptyDll) {
246
+ this._flushDataAndCleanUp();
247
+ this._outSocket.on('data', () => this._flushDataAndCleanUp());
248
+ }
237
249
  }
238
250
 
239
251
  private _flushDataAndCleanUp(): void {
252
+ if (this._useConptyDll) {
253
+ return;
254
+ }
240
255
  if (this._closeTimeout) {
241
256
  clearTimeout(this._closeTimeout);
242
257
  }
@@ -244,6 +259,9 @@ export class WindowsPtyAgent {
244
259
  }
245
260
 
246
261
  private _cleanUpProcess(): void {
262
+ if (this._useConptyDll) {
263
+ return;
264
+ }
247
265
  this._inSocket.readable = false;
248
266
  this._outSocket.readable = false;
249
267
  this._outSocket.destroy();
@@ -26,13 +26,16 @@ function pollForProcessState(desiredState: IProcessState, intervalMs: number = 1
26
26
  psList({ all: true }).then(ps => {
27
27
  let success = true;
28
28
  const pids = Object.keys(desiredState).map(k => parseInt(k, 10));
29
+ console.log('expected pids', JSON.stringify(pids));
29
30
  pids.forEach(pid => {
30
31
  if (desiredState[pid]) {
31
32
  if (!ps.some(p => p.pid === pid)) {
33
+ console.log(`pid ${pid} does not exist`);
32
34
  success = false;
33
35
  }
34
36
  } else {
35
37
  if (ps.some(p => p.pid === pid)) {
38
+ console.log(`pid ${pid} still exists`);
36
39
  success = false;
37
40
  }
38
41
  }
@@ -71,6 +74,7 @@ function pollForProcessTreeSize(pid: number, size: number, intervalMs: number =
71
74
  }).forEach(p => openList.push(p));
72
75
  list.push(current);
73
76
  }
77
+ console.log('list', JSON.stringify(list));
74
78
  const success = list.length === size;
75
79
  if (success) {
76
80
  clearInterval(interval);
@@ -88,53 +92,55 @@ function pollForProcessTreeSize(pid: number, size: number, intervalMs: number =
88
92
  }
89
93
 
90
94
  if (process.platform === 'win32') {
91
- [true, false].forEach((useConpty) => {
92
- describe(`WindowsTerminal (useConpty = ${useConpty})`, () => {
95
+ [[false, false], [true, false], [true, true]].forEach(([useConpty, useConptyDll]) => {
96
+ describe(`WindowsTerminal (useConpty = ${useConpty}, useConptyDll = ${useConptyDll})`, () => {
93
97
  describe('kill', () => {
94
- it('should not crash parent process', (done) => {
95
- const term = new WindowsTerminal('cmd.exe', [], { useConpty });
98
+ it('should not crash parent process', function (done) {
99
+ this.timeout(20000);
100
+ const term = new WindowsTerminal('cmd.exe', [], { useConpty, useConptyDll });
101
+ term.on('exit', () => done());
96
102
  term.kill();
97
- // Add done call to deferred function queue to ensure the kill call has completed
98
- (<any>term)._defer(done);
99
103
  });
100
104
  it('should kill the process tree', function (done: Mocha.Done): void {
101
- this.timeout(5000);
102
- const term = new WindowsTerminal('cmd.exe', [], { useConpty });
105
+ this.timeout(20000);
106
+ const term = new WindowsTerminal('cmd.exe', [], { useConpty, useConptyDll });
103
107
  // Start sub-processes
104
108
  term.write('powershell.exe\r');
105
- term.write('notepad.exe\r');
106
109
  term.write('node.exe\r');
107
- pollForProcessTreeSize(term.pid, 4, 500, 5000).then(list => {
110
+ console.log('start poll for tree size');
111
+ pollForProcessTreeSize(term.pid, 3, 500, 5000).then(list => {
108
112
  assert.strictEqual(list[0].name.toLowerCase(), 'cmd.exe');
109
113
  assert.strictEqual(list[1].name.toLowerCase(), 'powershell.exe');
110
- assert.strictEqual(list[2].name.toLowerCase(), 'notepad.exe');
111
- assert.strictEqual(list[3].name.toLowerCase(), 'node.exe');
114
+ assert.strictEqual(list[2].name.toLowerCase(), 'node.exe');
112
115
  term.kill();
113
116
  const desiredState: IProcessState = {};
114
117
  desiredState[list[0].pid] = false;
115
118
  desiredState[list[1].pid] = false;
116
- desiredState[list[2].pid] = true;
117
- desiredState[list[3].pid] = false;
118
- pollForProcessState(desiredState).then(() => {
119
- // Kill notepad before done
120
- process.kill(list[2].pid);
121
- done();
119
+ desiredState[list[2].pid] = false;
120
+ term.on('exit', () => {
121
+ pollForProcessState(desiredState, 1000, 5000).then(() => {
122
+ done();
123
+ });
122
124
  });
123
125
  });
124
126
  });
125
127
  });
126
128
 
127
129
  describe('resize', () => {
128
- it('should throw a non-native exception when resizing an invalid value', () => {
129
- const term = new WindowsTerminal('cmd.exe', [], { useConpty });
130
+ it('should throw a non-native exception when resizing an invalid value', (done) => {
131
+ const term = new WindowsTerminal('cmd.exe', [], { useConpty, useConptyDll });
130
132
  assert.throws(() => term.resize(-1, -1));
131
133
  assert.throws(() => term.resize(0, 0));
132
134
  assert.doesNotThrow(() => term.resize(1, 1));
135
+ term.on('exit', () => {
136
+ done();
137
+ });
138
+ term.kill();
133
139
  });
134
140
  it('should throw a non-native exception when resizing a killed terminal', (done) => {
135
- const term = new WindowsTerminal('cmd.exe', [], { useConpty });
141
+ const term = new WindowsTerminal('cmd.exe', [], { useConpty, useConptyDll });
136
142
  (<any>term)._defer(() => {
137
- term.on('exit', () => {
143
+ term.once('exit', () => {
138
144
  assert.throws(() => term.resize(1, 1));
139
145
  done();
140
146
  });
@@ -144,7 +150,8 @@ if (process.platform === 'win32') {
144
150
  });
145
151
 
146
152
  describe('Args as CommandLine', () => {
147
- it('should not fail running a file containing a space in the path', (done) => {
153
+ it('should not fail running a file containing a space in the path', function (done) {
154
+ this.timeout(10000);
148
155
  const spaceFolder = path.resolve(__dirname, '..', 'fixtures', 'space folder');
149
156
  if (!fs.existsSync(spaceFolder)) {
150
157
  fs.mkdirSync(spaceFolder);
@@ -158,7 +165,7 @@ if (process.platform === 'win32') {
158
165
  // Skip test if git bash isn't installed
159
166
  return;
160
167
  }
161
- const term = new WindowsTerminal(cmdCopiedPath, '/c echo "hello world"', { useConpty });
168
+ const term = new WindowsTerminal(cmdCopiedPath, '/c echo "hello world"', { useConpty, useConptyDll });
162
169
  let result = '';
163
170
  term.on('data', (data) => {
164
171
  result += data;
@@ -171,8 +178,9 @@ if (process.platform === 'win32') {
171
178
  });
172
179
 
173
180
  describe('env', () => {
174
- it('should set environment variables of the shell', (done) => {
175
- const term = new WindowsTerminal('cmd.exe', '/C echo %FOO%', { env: { FOO: 'BAR' }});
181
+ it('should set environment variables of the shell', function (done) {
182
+ this.timeout(10000);
183
+ const term = new WindowsTerminal('cmd.exe', '/C echo %FOO%', { useConpty, useConptyDll, env: { FOO: 'BAR' }});
176
184
  let result = '';
177
185
  term.on('data', (data) => {
178
186
  result += data;
@@ -185,16 +193,18 @@ if (process.platform === 'win32') {
185
193
  });
186
194
 
187
195
  describe('On close', () => {
188
- it('should return process zero exit codes', (done) => {
189
- const term = new WindowsTerminal('cmd.exe', '/C exit', { useConpty });
196
+ it('should return process zero exit codes', function (done) {
197
+ this.timeout(10000);
198
+ const term = new WindowsTerminal('cmd.exe', '/C exit', { useConpty, useConptyDll });
190
199
  term.on('exit', (code) => {
191
200
  assert.strictEqual(code, 0);
192
201
  done();
193
202
  });
194
203
  });
195
204
 
196
- it('should return process non-zero exit codes', (done) => {
197
- const term = new WindowsTerminal('cmd.exe', '/C exit 2', { useConpty });
205
+ it('should return process non-zero exit codes', function (done) {
206
+ this.timeout(10000);
207
+ const term = new WindowsTerminal('cmd.exe', '/C exit 2', { useConpty, useConptyDll });
198
208
  term.on('exit', (code) => {
199
209
  assert.strictEqual(code, 2);
200
210
  done();
@@ -203,8 +213,9 @@ if (process.platform === 'win32') {
203
213
  });
204
214
 
205
215
  describe('Write', () => {
206
- it('should accept input', (done) => {
207
- const term = new WindowsTerminal('cmd.exe', '', { useConpty });
216
+ it('should accept input', function (done) {
217
+ this.timeout(10000);
218
+ const term = new WindowsTerminal('cmd.exe', '', { useConpty, useConptyDll });
208
219
  term.write('exit\r');
209
220
  term.on('exit', () => {
210
221
  done();
@@ -48,7 +48,7 @@ export class WindowsTerminal extends Terminal {
48
48
  this._deferreds = [];
49
49
 
50
50
  // Create new termal.
51
- this._agent = new WindowsPtyAgent(file, args, parsedEnv, cwd, this._cols, this._rows, false, opt.useConpty, opt.conptyInheritCursor);
51
+ this._agent = new WindowsPtyAgent(file, args, parsedEnv, cwd, this._cols, this._rows, false, opt.useConpty, opt.useConptyDll, opt.conptyInheritCursor);
52
52
  this._socket = this._agent.outSocket;
53
53
 
54
54
  // Not available until `ready` event emitted.
@@ -4,19 +4,22 @@
4
4
 
5
5
  import { parentPort, workerData } from 'worker_threads';
6
6
  import { Socket, createServer } from 'net';
7
+ import * as fs from 'fs';
7
8
  import { ConoutWorkerMessage, IWorkerData, getWorkerPipeName } from '../shared/conout';
8
9
 
9
10
  const conoutPipeName = (workerData as IWorkerData).conoutPipeName;
10
- const conoutSocket = new Socket();
11
+ const outSocketFD = fs.openSync(conoutPipeName, 'r');
12
+ const conoutSocket = new Socket({
13
+ fd: outSocketFD,
14
+ readable: true
15
+ });
11
16
  conoutSocket.setEncoding('utf8');
12
- conoutSocket.connect(conoutPipeName, () => {
13
- const server = createServer(workerSocket => {
14
- conoutSocket.pipe(workerSocket);
15
- });
16
- server.listen(getWorkerPipeName(conoutPipeName));
17
-
18
- if (!parentPort) {
19
- throw new Error('worker_threads parentPort is null');
20
- }
21
- parentPort.postMessage(ConoutWorkerMessage.READY);
17
+ const server = createServer(workerSocket => {
18
+ conoutSocket.pipe(workerSocket);
22
19
  });
20
+ server.listen(getWorkerPipeName(conoutPipeName));
21
+
22
+ if (!parentPort) {
23
+ throw new Error('worker_threads parentPort is null');
24
+ }
25
+ parentPort.postMessage(ConoutWorkerMessage.READY);
@@ -95,6 +95,14 @@ declare module 'node-pty' {
95
95
  */
96
96
  useConpty?: boolean;
97
97
 
98
+ /**
99
+ * (EXPERIMENTAL)
100
+ *
101
+ * Whether to use the conpty.dll shipped with the node-pty package instead of the one built into
102
+ * Windows. Defaults to false.
103
+ */
104
+ useConptyDll?: boolean;
105
+
98
106
  /**
99
107
  * Whether to use PSEUDOCONSOLE_INHERIT_CURSOR in conpty.
100
108
  * @see https://docs.microsoft.com/en-us/windows/console/createpseudoconsole