node-pty 1.1.0-beta4 → 1.1.0-beta41

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 (84) hide show
  1. package/README.md +6 -4
  2. package/binding.gyp +10 -20
  3. package/deps/winpty/src/agent/Agent.cc +11 -3
  4. package/deps/winpty/src/winpty.gyp +29 -1
  5. package/lib/conpty_console_list_agent.js +3 -7
  6. package/lib/conpty_console_list_agent.js.map +1 -1
  7. package/lib/index.js +2 -1
  8. package/lib/index.js.map +1 -1
  9. package/lib/terminal.js.map +1 -1
  10. package/lib/terminal.test.js.map +1 -1
  11. package/lib/unixTerminal.js +7 -44
  12. package/lib/unixTerminal.js.map +1 -1
  13. package/lib/unixTerminal.test.js +3 -5
  14. package/lib/unixTerminal.test.js.map +1 -1
  15. package/lib/utils.js +23 -1
  16. package/lib/utils.js.map +1 -1
  17. package/lib/windowsConoutConnection.js +6 -3
  18. package/lib/windowsConoutConnection.js.map +1 -1
  19. package/lib/windowsPtyAgent.js +45 -48
  20. package/lib/windowsPtyAgent.js.map +1 -1
  21. package/lib/windowsTerminal.js +1 -1
  22. package/lib/windowsTerminal.js.map +1 -1
  23. package/lib/windowsTerminal.test.js +40 -26
  24. package/lib/windowsTerminal.test.js.map +1 -1
  25. package/lib/worker/conoutSocketWorker.js.map +1 -1
  26. package/package.json +7 -2
  27. package/prebuilds/darwin-arm64/pty.node +0 -0
  28. package/prebuilds/darwin-arm64/spawn-helper +0 -0
  29. package/prebuilds/darwin-x64/pty.node +0 -0
  30. package/prebuilds/darwin-x64/spawn-helper +0 -0
  31. package/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
  32. package/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
  33. package/prebuilds/win32-arm64/conpty.node +0 -0
  34. package/prebuilds/win32-arm64/conpty.pdb +0 -0
  35. package/prebuilds/win32-arm64/conpty_console_list.node +0 -0
  36. package/prebuilds/win32-arm64/conpty_console_list.pdb +0 -0
  37. package/prebuilds/win32-arm64/pty.node +0 -0
  38. package/prebuilds/win32-arm64/pty.pdb +0 -0
  39. package/prebuilds/win32-arm64/winpty-agent.exe +0 -0
  40. package/prebuilds/win32-arm64/winpty-agent.pdb +0 -0
  41. package/prebuilds/win32-arm64/winpty.dll +0 -0
  42. package/prebuilds/win32-arm64/winpty.pdb +0 -0
  43. package/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
  44. package/prebuilds/win32-x64/conpty/conpty.dll +0 -0
  45. package/prebuilds/win32-x64/conpty.node +0 -0
  46. package/prebuilds/win32-x64/conpty.pdb +0 -0
  47. package/prebuilds/win32-x64/conpty_console_list.node +0 -0
  48. package/prebuilds/win32-x64/conpty_console_list.pdb +0 -0
  49. package/prebuilds/win32-x64/pty.node +0 -0
  50. package/prebuilds/win32-x64/pty.pdb +0 -0
  51. package/prebuilds/win32-x64/winpty-agent.exe +0 -0
  52. package/prebuilds/win32-x64/winpty-agent.pdb +0 -0
  53. package/prebuilds/win32-x64/winpty.dll +0 -0
  54. package/prebuilds/win32-x64/winpty.pdb +0 -0
  55. package/scripts/gen-compile-commands.js +8 -0
  56. package/scripts/{publish.js → increment-version.js} +7 -15
  57. package/scripts/post-install.js +48 -10
  58. package/scripts/prebuild.js +34 -0
  59. package/src/conpty_console_list_agent.ts +2 -6
  60. package/src/index.ts +2 -1
  61. package/src/interfaces.ts +2 -1
  62. package/src/native.d.ts +5 -5
  63. package/src/terminal.test.ts +1 -1
  64. package/src/terminal.ts +2 -2
  65. package/src/unix/pty.cc +244 -284
  66. package/src/unixTerminal.test.ts +3 -6
  67. package/src/unixTerminal.ts +9 -36
  68. package/src/utils.ts +20 -0
  69. package/src/win/conpty.cc +268 -187
  70. package/src/win/conpty.h +41 -0
  71. package/src/win/conpty_console_list.cc +16 -15
  72. package/src/win/path_util.cc +39 -36
  73. package/src/win/path_util.h +5 -2
  74. package/src/win/winpty.cc +108 -151
  75. package/src/windowsConoutConnection.ts +6 -3
  76. package/src/windowsPtyAgent.ts +42 -43
  77. package/src/windowsTerminal.test.ts +46 -33
  78. package/src/windowsTerminal.ts +3 -3
  79. package/src/worker/conoutSocketWorker.ts +2 -2
  80. package/third_party/conpty/1.23.251008001/win10-arm64/OpenConsole.exe +0 -0
  81. package/third_party/conpty/1.23.251008001/win10-arm64/conpty.dll +0 -0
  82. package/third_party/conpty/1.23.251008001/win10-x64/OpenConsole.exe +0 -0
  83. package/third_party/conpty/1.23.251008001/win10-x64/conpty.dll +0 -0
  84. package/typings/node-pty.d.ts +9 -1
package/src/unix/pty.cc CHANGED
@@ -17,11 +17,14 @@
17
17
  * Includes
18
18
  */
19
19
 
20
- #include <nan.h>
20
+ #define NODE_ADDON_API_DISABLE_DEPRECATED
21
+ #include <napi.h>
22
+ #include <assert.h>
21
23
  #include <errno.h>
22
24
  #include <string.h>
23
25
  #include <stdlib.h>
24
26
  #include <unistd.h>
27
+ #include <thread>
25
28
 
26
29
  #include <sys/types.h>
27
30
  #include <sys/stat.h>
@@ -38,6 +41,10 @@
38
41
  #include <util.h>
39
42
  #elif defined(__FreeBSD__)
40
43
  #include <libutil.h>
44
+ #include <termios.h>
45
+ #elif defined(__OpenBSD__)
46
+ #include <util.h>
47
+ #include <termios.h>
41
48
  #endif
42
49
 
43
50
  /* Some platforms name VWERASE and VDISCARD differently */
@@ -99,27 +106,120 @@ int pthread_fchdir_np(int fd) API_AVAILABLE(macosx(10.12));
99
106
  })
100
107
  #endif
101
108
 
102
- /**
103
- * Structs
104
- */
105
-
106
- struct pty_baton {
107
- Nan::Persistent<v8::Function> cb;
108
- int exit_code;
109
- int signal_code;
110
- pid_t pid;
111
- uv_async_t async;
112
- uv_thread_t tid;
109
+ struct ExitEvent {
110
+ int exit_code = 0, signal_code = 0;
113
111
  };
114
112
 
113
+ void SetupExitCallback(Napi::Env env, Napi::Function cb, pid_t pid) {
114
+ std::thread *th = new std::thread;
115
+ // Don't use Napi::AsyncWorker which is limited by UV_THREADPOOL_SIZE.
116
+ auto tsfn = Napi::ThreadSafeFunction::New(
117
+ env,
118
+ cb, // JavaScript function called asynchronously
119
+ "SetupExitCallback_resource", // Name
120
+ 0, // Unlimited queue
121
+ 1, // Only one thread will use this initially
122
+ [th](Napi::Env) { // Finalizer used to clean threads up
123
+ th->join();
124
+ delete th;
125
+ });
126
+ *th = std::thread([tsfn = std::move(tsfn), pid] {
127
+ auto callback = [](Napi::Env env, Napi::Function cb, ExitEvent *exit_event) {
128
+ cb.Call({Napi::Number::New(env, exit_event->exit_code),
129
+ Napi::Number::New(env, exit_event->signal_code)});
130
+ delete exit_event;
131
+ };
132
+
133
+ int ret;
134
+ int stat_loc;
135
+ #if defined(__APPLE__)
136
+ // Based on
137
+ // https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69?
138
+ int kq = HANDLE_EINTR(kqueue());
139
+ struct kevent change = {0};
140
+ EV_SET(&change, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
141
+ ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
142
+ if (ret == -1) {
143
+ if (errno == ESRCH) {
144
+ // At this point, one of the following has occurred:
145
+ // 1. The process has died but has not yet been reaped.
146
+ // 2. The process has died and has already been reaped.
147
+ // 3. The process is in the process of dying. It's no longer
148
+ // kqueueable, but it may not be waitable yet either. Mark calls
149
+ // this case the "zombie death race".
150
+ ret = HANDLE_EINTR(waitpid(pid, &stat_loc, WNOHANG));
151
+ if (ret == 0) {
152
+ ret = kill(pid, SIGKILL);
153
+ if (ret != -1) {
154
+ HANDLE_EINTR(waitpid(pid, &stat_loc, 0));
155
+ }
156
+ }
157
+ }
158
+ } else {
159
+ struct kevent event = {0};
160
+ ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL));
161
+ if (ret == 1) {
162
+ if ((event.fflags & NOTE_EXIT) &&
163
+ (event.ident == static_cast<uintptr_t>(pid))) {
164
+ // The process is dead or dying. This won't block for long, if at
165
+ // all.
166
+ HANDLE_EINTR(waitpid(pid, &stat_loc, 0));
167
+ }
168
+ }
169
+ }
170
+ #else
171
+ while (true) {
172
+ errno = 0;
173
+ if ((ret = waitpid(pid, &stat_loc, 0)) != pid) {
174
+ if (ret == -1 && errno == EINTR) {
175
+ continue;
176
+ }
177
+ if (ret == -1 && errno == ECHILD) {
178
+ // XXX node v0.8.x seems to have this problem.
179
+ // waitpid is already handled elsewhere.
180
+ ;
181
+ } else {
182
+ assert(false);
183
+ }
184
+ }
185
+ break;
186
+ }
187
+ #endif
188
+ ExitEvent *exit_event = new ExitEvent;
189
+ if (WIFEXITED(stat_loc)) {
190
+ exit_event->exit_code = WEXITSTATUS(stat_loc); // errno?
191
+ }
192
+ if (WIFSIGNALED(stat_loc)) {
193
+ exit_event->signal_code = WTERMSIG(stat_loc);
194
+ }
195
+ auto status = tsfn.BlockingCall(exit_event, callback); // In main thread
196
+ switch (status) {
197
+ case napi_closing:
198
+ break;
199
+
200
+ case napi_queue_full:
201
+ Napi::Error::Fatal("SetupExitCallback", "Queue was full");
202
+
203
+ case napi_ok:
204
+ if (tsfn.Release() != napi_ok) {
205
+ Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.Release() failed");
206
+ }
207
+ break;
208
+
209
+ default:
210
+ Napi::Error::Fatal("SetupExitCallback", "ThreadSafeFunction.BlockingCall() failed");
211
+ }
212
+ });
213
+ }
214
+
115
215
  /**
116
216
  * Methods
117
217
  */
118
218
 
119
- NAN_METHOD(PtyFork);
120
- NAN_METHOD(PtyOpen);
121
- NAN_METHOD(PtyResize);
122
- NAN_METHOD(PtyGetProc);
219
+ Napi::Value PtyFork(const Napi::CallbackInfo& info);
220
+ Napi::Value PtyOpen(const Napi::CallbackInfo& info);
221
+ Napi::Value PtyResize(const Napi::CallbackInfo& info);
222
+ Napi::Value PtyGetProc(const Napi::CallbackInfo& info);
123
223
 
124
224
  /**
125
225
  * Functions
@@ -136,15 +236,6 @@ static char *
136
236
  pty_getproc(int, char *);
137
237
  #endif
138
238
 
139
- static void
140
- pty_waitpid(void *);
141
-
142
- static void
143
- pty_after_waitpid(uv_async_t *);
144
-
145
- static void
146
- pty_after_close(uv_handle_t *);
147
-
148
239
  #if defined(__APPLE__) || defined(__OpenBSD__)
149
240
  static void
150
241
  pty_posix_spawn(char** argv, char** env,
@@ -155,62 +246,75 @@ pty_posix_spawn(char** argv, char** env,
155
246
  int* err);
156
247
  #endif
157
248
 
158
- NAN_METHOD(PtyFork) {
159
- Nan::HandleScope scope;
249
+ struct DelBuf {
250
+ int len;
251
+ DelBuf(int len) : len(len) {}
252
+ void operator()(char **p) {
253
+ if (p == nullptr)
254
+ return;
255
+ for (int i = 0; i < len; i++)
256
+ free(p[i]);
257
+ delete[] p;
258
+ }
259
+ };
260
+
261
+ Napi::Value PtyFork(const Napi::CallbackInfo& info) {
262
+ Napi::Env napiEnv(info.Env());
263
+ Napi::HandleScope scope(napiEnv);
160
264
 
161
265
  if (info.Length() != 11 ||
162
- !info[0]->IsString() ||
163
- !info[1]->IsArray() ||
164
- !info[2]->IsArray() ||
165
- !info[3]->IsString() ||
166
- !info[4]->IsNumber() ||
167
- !info[5]->IsNumber() ||
168
- !info[6]->IsNumber() ||
169
- !info[7]->IsNumber() ||
170
- !info[8]->IsBoolean() ||
171
- !info[9]->IsString() ||
172
- !info[10]->IsFunction()) {
173
- return Nan::ThrowError(
174
- "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)");
266
+ !info[0].IsString() ||
267
+ !info[1].IsArray() ||
268
+ !info[2].IsArray() ||
269
+ !info[3].IsString() ||
270
+ !info[4].IsNumber() ||
271
+ !info[5].IsNumber() ||
272
+ !info[6].IsNumber() ||
273
+ !info[7].IsNumber() ||
274
+ !info[8].IsBoolean() ||
275
+ !info[9].IsString() ||
276
+ !info[10].IsFunction()) {
277
+ throw Napi::Error::New(napiEnv, "Usage: pty.fork(file, args, env, cwd, cols, rows, uid, gid, utf8, helperPath, onexit)");
175
278
  }
176
279
 
177
280
  // file
178
- Nan::Utf8String file(info[0]);
281
+ std::string file = info[0].As<Napi::String>();
179
282
 
180
283
  // args
181
- v8::Local<v8::Array> argv_ = v8::Local<v8::Array>::Cast(info[1]);
284
+ Napi::Array argv_ = info[1].As<Napi::Array>();
182
285
 
183
286
  // env
184
- v8::Local<v8::Array> env_ = v8::Local<v8::Array>::Cast(info[2]);
185
- int envc = env_->Length();
186
- char **env = new char*[envc+1];
287
+ Napi::Array env_ = info[2].As<Napi::Array>();
288
+ int envc = env_.Length();
289
+ std::unique_ptr<char *, DelBuf> env_unique_ptr(new char *[envc + 1], DelBuf(envc + 1));
290
+ char **env = env_unique_ptr.get();
187
291
  env[envc] = NULL;
188
292
  for (int i = 0; i < envc; i++) {
189
- Nan::Utf8String pair(Nan::Get(env_, i).ToLocalChecked());
190
- env[i] = strdup(*pair);
293
+ std::string pair = env_.Get(i).As<Napi::String>();
294
+ env[i] = strdup(pair.c_str());
191
295
  }
192
296
 
193
297
  // cwd
194
- Nan::Utf8String cwd_(info[3]);
298
+ std::string cwd_ = info[3].As<Napi::String>();
195
299
 
196
300
  // size
197
301
  struct winsize winp;
198
- winp.ws_col = info[4]->IntegerValue(Nan::GetCurrentContext()).FromJust();
199
- winp.ws_row = info[5]->IntegerValue(Nan::GetCurrentContext()).FromJust();
302
+ winp.ws_col = info[4].As<Napi::Number>().Int32Value();
303
+ winp.ws_row = info[5].As<Napi::Number>().Int32Value();
200
304
  winp.ws_xpixel = 0;
201
305
  winp.ws_ypixel = 0;
202
306
 
203
307
  #if !defined(__APPLE__)
204
308
  // uid / gid
205
- int uid = info[6]->IntegerValue(Nan::GetCurrentContext()).FromJust();
206
- int gid = info[7]->IntegerValue(Nan::GetCurrentContext()).FromJust();
309
+ int uid = info[6].As<Napi::Number>().Int32Value();
310
+ int gid = info[7].As<Napi::Number>().Int32Value();
207
311
  #endif
208
312
 
209
313
  // termios
210
314
  struct termios t = termios();
211
315
  struct termios *term = &t;
212
316
  term->c_iflag = ICRNL | IXON | IXANY | IMAXBEL | BRKINT;
213
- if (Nan::To<bool>(info[8]).FromJust()) {
317
+ if (info[8].As<Napi::Boolean>().Value()) {
214
318
  #if defined(IUTF8)
215
319
  term->c_iflag |= IUTF8;
216
320
  #endif
@@ -245,45 +349,44 @@ NAN_METHOD(PtyFork) {
245
349
  cfsetospeed(term, B38400);
246
350
 
247
351
  // helperPath
248
- Nan::Utf8String helper_path(info[9]);
352
+ std::string helper_path = info[9].As<Napi::String>();
249
353
 
250
354
  pid_t pid;
251
355
  int master;
252
356
  #if defined(__APPLE__)
253
- int argc = argv_->Length();
357
+ int argc = argv_.Length();
254
358
  int argl = argc + 4;
255
- char **argv = new char*[argl];
256
- argv[0] = strdup(*helper_path);
257
- argv[1] = strdup(*cwd_);
258
- argv[2] = strdup(*file);
359
+ std::unique_ptr<char *, DelBuf> argv_unique_ptr(new char *[argl], DelBuf(argl));
360
+ char **argv = argv_unique_ptr.get();
361
+ argv[0] = strdup(helper_path.c_str());
362
+ argv[1] = strdup(cwd_.c_str());
363
+ argv[2] = strdup(file.c_str());
259
364
  argv[argl - 1] = NULL;
260
365
  for (int i = 0; i < argc; i++) {
261
- Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
262
- argv[i + 3] = strdup(*arg);
366
+ std::string arg = argv_.Get(i).As<Napi::String>();
367
+ argv[i + 3] = strdup(arg.c_str());
263
368
  }
264
369
 
265
370
  int err = -1;
266
371
  pty_posix_spawn(argv, env, term, &winp, &master, &pid, &err);
267
372
  if (err != 0) {
268
- Nan::ThrowError("posix_spawnp failed.");
269
- goto done;
373
+ throw Napi::Error::New(napiEnv, "posix_spawnp failed.");
270
374
  }
271
375
  if (pty_nonblock(master) == -1) {
272
- Nan::ThrowError("Could not set master fd to nonblocking.");
273
- goto done;
376
+ throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
274
377
  }
275
378
  #else
276
- int argc = argv_->Length();
379
+ int argc = argv_.Length();
277
380
  int argl = argc + 2;
278
- char **argv = new char*[argl];
279
- argv[0] = strdup(*file);
381
+ std::unique_ptr<char *, DelBuf> argv_unique_ptr(new char *[argl], DelBuf(argl));
382
+ char** argv = argv_unique_ptr.get();
383
+ argv[0] = strdup(file.c_str());
280
384
  argv[argl - 1] = NULL;
281
385
  for (int i = 0; i < argc; i++) {
282
- Nan::Utf8String arg(Nan::Get(argv_, i).ToLocalChecked());
283
- argv[i + 1] = strdup(*arg);
386
+ std::string arg = argv_.Get(i).As<Napi::String>();
387
+ argv[i + 1] = strdup(arg.c_str());
284
388
  }
285
389
 
286
- char* cwd = strdup(*cwd_);
287
390
  sigset_t newmask, oldmask;
288
391
  struct sigaction sig_action;
289
392
  // temporarily block all signals
@@ -303,14 +406,6 @@ NAN_METHOD(PtyFork) {
303
406
  for (int i = 0 ; i < NSIG ; i++) { // NSIG is a macro for all signals + 1
304
407
  sigaction(i, &sig_action, NULL);
305
408
  }
306
- } else {
307
- for (int i = 0; i < argl; i++) free(argv[i]);
308
- delete[] argv;
309
-
310
- for (int i = 0; i < envc; i++) free(env[i]);
311
- delete[] env;
312
-
313
- free(cwd);
314
409
  }
315
410
 
316
411
  // reenable signals
@@ -318,11 +413,10 @@ NAN_METHOD(PtyFork) {
318
413
 
319
414
  switch (pid) {
320
415
  case -1:
321
- Nan::ThrowError("forkpty(3) failed.");
322
- goto done;
416
+ throw Napi::Error::New(napiEnv, "forkpty(3) failed.");
323
417
  case 0:
324
- if (strlen(cwd)) {
325
- if (chdir(cwd) == -1) {
418
+ if (strlen(cwd_.c_str())) {
419
+ if (chdir(cwd_.c_str()) == -1) {
326
420
  perror("chdir(2) failed.");
327
421
  _exit(1);
328
422
  }
@@ -349,62 +443,36 @@ NAN_METHOD(PtyFork) {
349
443
  }
350
444
  default:
351
445
  if (pty_nonblock(master) == -1) {
352
- Nan::ThrowError("Could not set master fd to nonblocking.");
353
- goto done;
446
+ throw Napi::Error::New(napiEnv, "Could not set master fd to nonblocking.");
354
447
  }
355
448
  }
356
449
  #endif
357
450
 
358
- {
359
- v8::Local<v8::Object> obj = Nan::New<v8::Object>();
360
- Nan::Set(obj,
361
- Nan::New<v8::String>("fd").ToLocalChecked(),
362
- Nan::New<v8::Number>(master));
363
- Nan::Set(obj,
364
- Nan::New<v8::String>("pid").ToLocalChecked(),
365
- Nan::New<v8::Number>(pid));
366
- Nan::Set(obj,
367
- Nan::New<v8::String>("pty").ToLocalChecked(),
368
- Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
451
+ Napi::Object obj = Napi::Object::New(napiEnv);
452
+ obj.Set("fd", Napi::Number::New(napiEnv, master));
453
+ obj.Set("pid", Napi::Number::New(napiEnv, pid));
454
+ obj.Set("pty", Napi::String::New(napiEnv, ptsname(master)));
369
455
 
370
- pty_baton *baton = new pty_baton();
371
- baton->exit_code = 0;
372
- baton->signal_code = 0;
373
- baton->cb.Reset(v8::Local<v8::Function>::Cast(info[10]));
374
- baton->pid = pid;
375
- baton->async.data = baton;
376
-
377
- uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);
378
-
379
- uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));
380
-
381
- return info.GetReturnValue().Set(obj);
382
- }
383
-
384
- done:
385
- #if defined(__APPLE__)
386
- for (int i = 0; i < argl; i++) free(argv[i]);
387
- delete[] argv;
388
-
389
- for (int i = 0; i < envc; i++) free(env[i]);
390
- delete[] env;
391
- #endif
392
- return info.GetReturnValue().SetUndefined();
456
+ // Set up process exit callback.
457
+ Napi::Function cb = info[10].As<Napi::Function>();
458
+ SetupExitCallback(napiEnv, cb, pid);
459
+ return obj;
393
460
  }
394
461
 
395
- NAN_METHOD(PtyOpen) {
396
- Nan::HandleScope scope;
462
+ Napi::Value PtyOpen(const Napi::CallbackInfo& info) {
463
+ Napi::Env env(info.Env());
464
+ Napi::HandleScope scope(env);
397
465
 
398
466
  if (info.Length() != 2 ||
399
- !info[0]->IsNumber() ||
400
- !info[1]->IsNumber()) {
401
- return Nan::ThrowError("Usage: pty.open(cols, rows)");
467
+ !info[0].IsNumber() ||
468
+ !info[1].IsNumber()) {
469
+ throw Napi::Error::New(env, "Usage: pty.open(cols, rows)");
402
470
  }
403
471
 
404
472
  // size
405
473
  struct winsize winp;
406
- winp.ws_col = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
407
- winp.ws_row = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
474
+ winp.ws_col = info[0].As<Napi::Number>().Int32Value();
475
+ winp.ws_row = info[1].As<Napi::Number>().Int32Value();
408
476
  winp.ws_xpixel = 0;
409
477
  winp.ws_ypixel = 0;
410
478
 
@@ -413,98 +481,98 @@ NAN_METHOD(PtyOpen) {
413
481
  int ret = openpty(&master, &slave, nullptr, NULL, static_cast<winsize*>(&winp));
414
482
 
415
483
  if (ret == -1) {
416
- return Nan::ThrowError("openpty(3) failed.");
484
+ throw Napi::Error::New(env, "openpty(3) failed.");
417
485
  }
418
486
 
419
487
  if (pty_nonblock(master) == -1) {
420
- return Nan::ThrowError("Could not set master fd to nonblocking.");
488
+ throw Napi::Error::New(env, "Could not set master fd to nonblocking.");
421
489
  }
422
490
 
423
491
  if (pty_nonblock(slave) == -1) {
424
- return Nan::ThrowError("Could not set slave fd to nonblocking.");
492
+ throw Napi::Error::New(env, "Could not set slave fd to nonblocking.");
425
493
  }
426
494
 
427
- v8::Local<v8::Object> obj = Nan::New<v8::Object>();
428
- Nan::Set(obj,
429
- Nan::New<v8::String>("master").ToLocalChecked(),
430
- Nan::New<v8::Number>(master));
431
- Nan::Set(obj,
432
- Nan::New<v8::String>("slave").ToLocalChecked(),
433
- Nan::New<v8::Number>(slave));
434
- Nan::Set(obj,
435
- Nan::New<v8::String>("pty").ToLocalChecked(),
436
- Nan::New<v8::String>(ptsname(master)).ToLocalChecked());
495
+ Napi::Object obj = Napi::Object::New(env);
496
+ obj.Set("master", Napi::Number::New(env, master));
497
+ obj.Set("slave", Napi::Number::New(env, slave));
498
+ obj.Set("pty", Napi::String::New(env, ptsname(master)));
437
499
 
438
- return info.GetReturnValue().Set(obj);
500
+ return obj;
439
501
  }
440
502
 
441
- NAN_METHOD(PtyResize) {
442
- Nan::HandleScope scope;
503
+ Napi::Value PtyResize(const Napi::CallbackInfo& info) {
504
+ Napi::Env env(info.Env());
505
+ Napi::HandleScope scope(env);
443
506
 
444
507
  if (info.Length() != 3 ||
445
- !info[0]->IsNumber() ||
446
- !info[1]->IsNumber() ||
447
- !info[2]->IsNumber()) {
448
- return Nan::ThrowError("Usage: pty.resize(fd, cols, rows)");
508
+ !info[0].IsNumber() ||
509
+ !info[1].IsNumber() ||
510
+ !info[2].IsNumber()) {
511
+ throw Napi::Error::New(env, "Usage: pty.resize(fd, cols, rows)");
449
512
  }
450
513
 
451
- int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
514
+ int fd = info[0].As<Napi::Number>().Int32Value();
452
515
 
453
516
  struct winsize winp;
454
- winp.ws_col = info[1]->IntegerValue(Nan::GetCurrentContext()).FromJust();
455
- winp.ws_row = info[2]->IntegerValue(Nan::GetCurrentContext()).FromJust();
517
+ winp.ws_col = info[1].As<Napi::Number>().Int32Value();
518
+ winp.ws_row = info[2].As<Napi::Number>().Int32Value();
456
519
  winp.ws_xpixel = 0;
457
520
  winp.ws_ypixel = 0;
458
521
 
459
522
  if (ioctl(fd, TIOCSWINSZ, &winp) == -1) {
460
523
  switch (errno) {
461
- case EBADF: return Nan::ThrowError("ioctl(2) failed, EBADF");
462
- case EFAULT: return Nan::ThrowError("ioctl(2) failed, EFAULT");
463
- case EINVAL: return Nan::ThrowError("ioctl(2) failed, EINVAL");
464
- case ENOTTY: return Nan::ThrowError("ioctl(2) failed, ENOTTY");
524
+ case EBADF:
525
+ throw Napi::Error::New(env, "ioctl(2) failed, EBADF");
526
+ case EFAULT:
527
+ throw Napi::Error::New(env, "ioctl(2) failed, EFAULT");
528
+ case EINVAL:
529
+ throw Napi::Error::New(env, "ioctl(2) failed, EINVAL");
530
+ case ENOTTY:
531
+ throw Napi::Error::New(env, "ioctl(2) failed, ENOTTY");
465
532
  }
466
- return Nan::ThrowError("ioctl(2) failed");
533
+ throw Napi::Error::New(env, "ioctl(2) failed");
467
534
  }
468
535
 
469
- return info.GetReturnValue().SetUndefined();
536
+ return env.Undefined();
470
537
  }
471
538
 
472
539
  /**
473
540
  * Foreground Process Name
474
541
  */
475
- NAN_METHOD(PtyGetProc) {
476
- Nan::HandleScope scope;
542
+ Napi::Value PtyGetProc(const Napi::CallbackInfo& info) {
543
+ Napi::Env env(info.Env());
544
+ Napi::HandleScope scope(env);
477
545
 
478
546
  #if defined(__APPLE__)
479
547
  if (info.Length() != 1 ||
480
- !info[0]->IsNumber()) {
481
- return Nan::ThrowError("Usage: pty.process(pid)");
548
+ !info[0].IsNumber()) {
549
+ throw Napi::Error::New(env, "Usage: pty.process(pid)");
482
550
  }
483
551
 
484
- int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
552
+ int fd = info[0].As<Napi::Number>().Int32Value();
485
553
  char *name = pty_getproc(fd);
486
554
  #else
487
555
  if (info.Length() != 2 ||
488
- !info[0]->IsNumber() ||
489
- !info[1]->IsString()) {
490
- return Nan::ThrowError("Usage: pty.process(fd, tty)");
556
+ !info[0].IsNumber() ||
557
+ !info[1].IsString()) {
558
+ throw Napi::Error::New(env, "Usage: pty.process(fd, tty)");
491
559
  }
492
560
 
493
- int fd = info[0]->IntegerValue(Nan::GetCurrentContext()).FromJust();
561
+ int fd = info[0].As<Napi::Number>().Int32Value();
494
562
 
495
- Nan::Utf8String tty_(info[1]);
496
- char *tty = strdup(*tty_);
563
+ std::string tty_ = info[1].As<Napi::String>();
564
+ char *tty = strdup(tty_.c_str());
497
565
  char *name = pty_getproc(fd, tty);
498
566
  free(tty);
499
567
  #endif
500
568
 
501
569
  if (name == NULL) {
502
- return info.GetReturnValue().SetUndefined();
570
+ return env.Undefined();
503
571
  }
504
572
 
505
- v8::Local<v8::String> name_ = Nan::New<v8::String>(name).ToLocalChecked();
573
+ Napi::String name_ = Napi::String::New(env, name);
506
574
  free(name);
507
- return info.GetReturnValue().Set(name_);
575
+ return name_;
508
576
  }
509
577
 
510
578
  /**
@@ -518,114 +586,6 @@ pty_nonblock(int fd) {
518
586
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
519
587
  }
520
588
 
521
- /**
522
- * pty_waitpid
523
- * Wait for SIGCHLD to read exit status.
524
- */
525
-
526
- static void
527
- pty_waitpid(void *data) {
528
- int ret;
529
- int stat_loc;
530
- pty_baton *baton = static_cast<pty_baton*>(data);
531
- errno = 0;
532
- #if defined(__APPLE__)
533
- // Based on
534
- // https://source.chromium.org/chromium/chromium/src/+/main:base/process/kill_mac.cc;l=35-69?
535
- int kq = HANDLE_EINTR(kqueue());
536
- struct kevent change = {0};
537
- EV_SET(&change, baton->pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
538
- ret = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
539
- if (ret == -1) {
540
- if (errno == ESRCH) {
541
- // At this point, one of the following has occurred:
542
- // 1. The process has died but has not yet been reaped.
543
- // 2. The process has died and has already been reaped.
544
- // 3. The process is in the process of dying. It's no longer
545
- // kqueueable, but it may not be waitable yet either. Mark calls
546
- // this case the "zombie death race".
547
- ret = HANDLE_EINTR(waitpid(baton->pid, &stat_loc, WNOHANG));
548
- if (ret == 0) {
549
- ret = kill(baton->pid, SIGKILL);
550
- if (ret != -1) {
551
- HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
552
- }
553
- }
554
- }
555
- } else {
556
- struct kevent event = {0};
557
- ret = HANDLE_EINTR(kevent(kq, NULL, 0, &event, 1, NULL));
558
- if (ret == 1) {
559
- if ((event.fflags & NOTE_EXIT) &&
560
- (event.ident == static_cast<uintptr_t>(baton->pid))) {
561
- // The process is dead or dying. This won't block for long, if at
562
- // all.
563
- HANDLE_EINTR(waitpid(baton->pid, &stat_loc, 0));
564
- }
565
- }
566
- }
567
- #else
568
- if ((ret = waitpid(baton->pid, &stat_loc, 0)) != baton->pid) {
569
- if (ret == -1 && errno == EINTR) {
570
- return pty_waitpid(baton);
571
- }
572
- if (ret == -1 && errno == ECHILD) {
573
- // XXX node v0.8.x seems to have this problem.
574
- // waitpid is already handled elsewhere.
575
- ;
576
- } else {
577
- assert(false);
578
- }
579
- }
580
- #endif
581
-
582
- if (WIFEXITED(stat_loc)) {
583
- baton->exit_code = WEXITSTATUS(stat_loc); // errno?
584
- }
585
-
586
- if (WIFSIGNALED(stat_loc)) {
587
- baton->signal_code = WTERMSIG(stat_loc);
588
- }
589
-
590
- uv_async_send(&baton->async);
591
- }
592
-
593
- /**
594
- * pty_after_waitpid
595
- * Callback after exit status has been read.
596
- */
597
-
598
- static void
599
- pty_after_waitpid(uv_async_t *async) {
600
- Nan::HandleScope scope;
601
- pty_baton *baton = static_cast<pty_baton*>(async->data);
602
-
603
- v8::Local<v8::Value> argv[] = {
604
- Nan::New<v8::Integer>(baton->exit_code),
605
- Nan::New<v8::Integer>(baton->signal_code),
606
- };
607
-
608
- v8::Local<v8::Function> cb = Nan::New<v8::Function>(baton->cb);
609
- baton->cb.Reset();
610
- memset(&baton->cb, -1, sizeof(baton->cb));
611
- Nan::AsyncResource resource("pty_after_waitpid");
612
- resource.runInAsyncScope(Nan::GetCurrentContext()->Global(), cb, 2, argv);
613
-
614
- uv_close((uv_handle_t *)async, pty_after_close);
615
- }
616
-
617
- /**
618
- * pty_after_close
619
- * uv_close() callback - free handle data
620
- */
621
-
622
- static void
623
- pty_after_close(uv_handle_t *handle) {
624
- uv_async_t *async = (uv_async_t *)handle;
625
- pty_baton *baton = static_cast<pty_baton*>(async->data);
626
- delete baton;
627
- }
628
-
629
589
  /**
630
590
  * pty_getproc
631
591
  * Taken from tmux.
@@ -828,12 +788,12 @@ done:
828
788
  * Init
829
789
  */
830
790
 
831
- NAN_MODULE_INIT(init) {
832
- Nan::HandleScope scope;
833
- Nan::Export(target, "fork", PtyFork);
834
- Nan::Export(target, "open", PtyOpen);
835
- Nan::Export(target, "resize", PtyResize);
836
- Nan::Export(target, "process", PtyGetProc);
791
+ Napi::Object init(Napi::Env env, Napi::Object exports) {
792
+ exports.Set("fork", Napi::Function::New(env, PtyFork));
793
+ exports.Set("open", Napi::Function::New(env, PtyOpen));
794
+ exports.Set("resize", Napi::Function::New(env, PtyResize));
795
+ exports.Set("process", Napi::Function::New(env, PtyGetProc));
796
+ return exports;
837
797
  }
838
798
 
839
- NODE_MODULE(pty, init)
799
+ NODE_API_MODULE(NODE_GYP_MODULE_NAME, init)