ssh2-sftp-client 9.0.0 → 9.0.3

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.
package/README.org CHANGED
@@ -9,16 +9,34 @@ convenience abstraction as well as a Promise based API.
9
9
  Documentation on the methods and available options in the underlying modules can
10
10
  be found on the [[https://github.com/mscdex/ssh2][SSH2]] project pages.
11
11
 
12
- Current stable release is *v9.0.0*.
12
+ Current stable release is *v9.0.2*.
13
13
 
14
14
  Code has been tested against Node versions 14.19.1, 16.15.0 and 18.1.0
15
15
 
16
- Node versions < 12.x are not supported. However, node v10.x should still work,
17
- although some tests will fail due to changes in file system functions used in
18
- test setup and tear down.
16
+ Node versions < 14.x are not supported.
19
17
 
20
18
  ** Version 9.x Changes
21
-
19
+ - Fix bug in ~end()~ method where it was possible for the module to attempt calling
20
+ the underlying ssh2 ~end()~ method when ssh2 has not been initialised. This could
21
+ lead to undefined reference errors.
22
+ - Fix bug in ~get()~ method where supplied destination streams were not close, creating
23
+ a possible resource leak. If the remote file did not exist, the method would return
24
+ an error, but failed to close any passed in stream supplied as the destination for
25
+ the data in the ~get()~ call.
26
+ - Change the default end and close handlers not to throw error or reject
27
+ promises. Previously, an end or close event would cause an error to be raised or a
28
+ promise to be rejected if the event was deemed to be /unexpected/. However,
29
+ classification of events as being unexpected was unreliable and didn't add much real
30
+ value. Both these handlers will now invalidate the sftp connection object and log that
31
+ the event fired and nothing else.
32
+ - Changed when event handled flags are reset. Now they are reset after a new set of
33
+ temporary listeners are added.
34
+ - Don't throw an error when calling end() if there is no active sftp connection. It does
35
+ no harm to call end() when there is no connection, so no need to raise an error.
36
+ - Use nullish coalescing when setting retry parameters instead of or'ing with
37
+ defaults. Allows setting values to 0.
38
+ - *Breaking Change*: This version uses syntax not supported in node versions
39
+ prior to v14. Therefore, node versions less than v14 will not work.
22
40
  - *Breaking Change*: This ~list()~ method no longer accepts a regular expression
23
41
  for filtering the entries to be returned. You can now specify a filter
24
42
  function instead. The function is called for each item in the list of items
@@ -331,21 +349,6 @@ The objects in the array returned by ~list()~ have the following properties;
331
349
  }
332
350
  #+end_src
333
351
 
334
- **** Pattern Filter
335
-
336
- The filter options can be a regular expression (most powerful option) or a
337
- simple /glob/-like string where * will match any number of characters, e.g.
338
-
339
- #+begin_example
340
- foo* => foo, foobar, foobaz
341
- ,*bar => bar, foobar, tabbar
342
- ,*oo* => foo, foobar, look, book
343
- #+end_example
344
-
345
- The /glob/-style matching is very simple. In most cases, you are best off using
346
- a real regular expression which will allow you to do more powerful matching and
347
- anchor matches to the beginning/end of the string etc.
348
-
349
352
  *** exists(path) ==> boolean
350
353
 
351
354
  Tests to see if remote file or directory exists. Returns type of remote object
@@ -1078,7 +1081,6 @@ services.
1078
1081
 
1079
1082
  #+end_src
1080
1083
 
1081
-
1082
1084
  *** createReadStream(remotePath, options)) ==> stream object
1083
1085
 
1084
1086
  Returns a read stream object which is attached to the remote file specified by
@@ -1191,6 +1193,25 @@ the ~end()~ method automatically removes all listeners from the client object.
1191
1193
  additional steps to simulate missing functionality etc. You want to use a CLI
1192
1194
  program which does as little as possible.
1193
1195
 
1196
+ ** Issues with ~fastPut()~ and ~fastGet()~ Methods
1197
+
1198
+ The ~fastPut()~ and ~fastGet()~ methods are known to be somewhat dependent on
1199
+ SFTP server capabilities. Some SFTP servers just do not work correctly with
1200
+ concurrent connections and some are known to have issues with negotiating
1201
+ packet sizes. These issues can sometimes be resolved by tweaking the options
1202
+ supplied to the methods, such as setting number of concurrent connections or
1203
+ a psecific packet size.
1204
+
1205
+ To see an example of the type of issues you can observe with ~fastPut()~ or
1206
+ ~fastGet()~, have a look at [[https://github.com/theophilusx/ssh2-sftp-client/issues/407][issue 407]], which describes the experiences of one
1207
+ user. Bottom line, when it works, it tends to work well and be significantly
1208
+ faster than using just ~get()~ or ~put()~. However, when developing code to
1209
+ run against different SFTP servers, especially where you are unable to test
1210
+ against each server, you are likely better off just using ~get()~ and ~put()~
1211
+ or structuring your code so that users can select which method to use (this
1212
+ is what =ssh2-sftp-client= does - for example, see the ~!downloadDir()~ and
1213
+ ~uploadDir()~ methods.
1214
+
1194
1215
  ** Promises, Events & Managing Exceptions
1195
1216
 
1196
1217
  One of the challenges in providing a Promise based API over a module like
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssh2-sftp-client",
3
- "version": "9.0.0",
3
+ "version": "9.0.3",
4
4
  "description": "ssh2 sftp client for node",
5
5
  "main": "src/index.js",
6
6
  "repository": {
@@ -40,7 +40,7 @@
40
40
  "eslint-plugin-mocha": "^10.0.3",
41
41
  "eslint-plugin-node": "^11.1.0",
42
42
  "eslint-plugin-promise": "^6.0.0",
43
- "eslint-plugin-unicorn": "^42.0.0",
43
+ "eslint-plugin-unicorn": "^43.0.2",
44
44
  "mocha": "^10.0.0",
45
45
  "moment": "^2.29.1",
46
46
  "nyc": "^15.1.0",
package/src/index.js CHANGED
@@ -18,7 +18,7 @@ const { errorCode } = require('./constants');
18
18
 
19
19
  class SftpClient {
20
20
  constructor(clientName) {
21
- this.version = '9.0.0';
21
+ this.version = '9.0.2';
22
22
  this.client = new Client();
23
23
  this.sftp = undefined;
24
24
  this.clientName = clientName ? clientName : 'sftp';
@@ -31,7 +31,7 @@ class SftpClient {
31
31
  this.debug = undefined;
32
32
 
33
33
  this.client.on('close', () => {
34
- if (this.endCalled || this.closeHandled) {
34
+ if (this.endCalled || this.errorHandled || this.closeHandled) {
35
35
  // we are processing an expected end event or close event handled elsewhere
36
36
  this.debugMsg('Global: Ignoring handled close event');
37
37
  } else {
@@ -41,7 +41,7 @@ class SftpClient {
41
41
  });
42
42
 
43
43
  this.client.on('end', () => {
44
- if (this.endCalled || this.endHandled) {
44
+ if (this.endCalled || this.errorHandled || this.endHandled) {
45
45
  // end event expected or handled elsewhere
46
46
  this.debugMsg('Global: Ignoring hanlded end event');
47
47
  } else {
@@ -95,26 +95,20 @@ class SftpClient {
95
95
  } else {
96
96
  switch (err.code) {
97
97
  case 'ENOTFOUND':
98
- msg =
99
- `${name}: ${err.level} error. ` +
100
- `Address lookup failed for host ${err.hostname}${retry}`;
98
+ msg = `${name}: Address lookup failed for host${retry}`;
101
99
  break;
102
100
  case 'ECONNREFUSED':
103
- msg =
104
- `${name}: ${err.level} error. Remote host at ` +
105
- `${err.address} refused connection${retry}`;
101
+ msg = `${name}: Remote host refused connection${retry}`;
106
102
  break;
107
103
  case 'ECONNRESET':
108
- msg =
109
- `${name}: Remote host has reset the connection: ` +
110
- `${err.message}${retry}`;
104
+ msg = `${name}: Remote host has reset the connection: ${err.message}${retry}`;
111
105
  break;
112
106
  default:
113
107
  msg = `${name}: ${err.message}${retry}`;
114
108
  }
115
109
  code = err.code ? err.code : errorCode.generic;
116
110
  }
117
- let newError = new Error(msg);
111
+ const newError = new Error(msg);
118
112
  newError.code = code;
119
113
  newError.custom = true;
120
114
  this.debugMsg(`${newError.message} (${newError.code})`);
@@ -159,9 +153,7 @@ class SftpClient {
159
153
  return new Promise((resolve, reject) => {
160
154
  listeners = addTempListeners(this, 'getConnection', reject);
161
155
  doReady = () => {
162
- this.debugMsg(
163
- 'getConnection ready listener: got connection - promise resolved'
164
- );
156
+ this.debugMsg('getConnection ready listener: got connection - promise resolved');
165
157
  resolve(true);
166
158
  };
167
159
  this.on('ready', doReady);
@@ -169,7 +161,6 @@ class SftpClient {
169
161
  }).finally(() => {
170
162
  this.removeListener('ready', doReady);
171
163
  removeTempListeners(this, listeners, 'getConnection');
172
- this._resetEventFlags();
173
164
  });
174
165
  }
175
166
 
@@ -177,7 +168,6 @@ class SftpClient {
177
168
  return new Promise((resolve, reject) => {
178
169
  this.client.sftp((err, sftp) => {
179
170
  if (err) {
180
- this.client.end();
181
171
  reject(this.fmtError(err, 'getSftpChannel', err.code));
182
172
  } else {
183
173
  this.debugMsg('getSftpChannel: SFTP channel established');
@@ -208,10 +198,7 @@ class SftpClient {
208
198
  if (config.debug) {
209
199
  this.debug = config.debug;
210
200
  this.debugMsg('connect: Debugging turned on');
211
- this.debugMsg(
212
- `ssh2-sftp-client Version: ${this.version} `,
213
- process.versions
214
- );
201
+ this.debugMsg(`ssh2-sftp-client Version: ${this.version} `, process.versions);
215
202
  }
216
203
  if (this.sftp) {
217
204
  throw this.fmtError(
@@ -220,10 +207,10 @@ class SftpClient {
220
207
  errorCode.connect
221
208
  );
222
209
  }
223
- let retryOpts = {
224
- retries: config.retries || 1,
225
- factor: config.factor || 2,
226
- minTimeout: config.retry_minTimeout || 25000,
210
+ const retryOpts = {
211
+ retries: config.retries ?? 1,
212
+ factor: config.factor ?? 2,
213
+ minTimeout: config.retry_minTimeout ?? 25000,
227
214
  };
228
215
  await promiseRetry(retryOpts, async (retry, attempt) => {
229
216
  try {
@@ -236,16 +223,8 @@ class SftpClient {
236
223
  case 'ERR_SOCKET_BAD_PORT':
237
224
  throw err;
238
225
  case undefined: {
239
- if (
240
- err.message.endsWith(
241
- 'All configured authentication methods failed'
242
- )
243
- ) {
244
- throw this.fmtError(
245
- err.message,
246
- 'getConnection',
247
- errorCode.badAuth
248
- );
226
+ if (err.message.endsWith('All configured authentication methods failed')) {
227
+ throw this.fmtError(err.message, 'getConnection', errorCode.badAuth);
249
228
  }
250
229
  retry(err);
251
230
  break;
@@ -255,14 +234,13 @@ class SftpClient {
255
234
  }
256
235
  }
257
236
  });
258
- let sftp = await this.getSftpChannel();
237
+ const sftp = await this.getSftpChannel();
259
238
  return sftp;
260
239
  } catch (err) {
261
- this.end();
240
+ await this.end();
262
241
  throw err.custom ? err : this.fmtError(err, 'connect');
263
242
  } finally {
264
243
  removeTempListeners(this, listeners, 'connect');
265
- this._resetEventFlags();
266
244
  }
267
245
  }
268
246
 
@@ -286,9 +264,7 @@ class SftpClient {
286
264
  this.debugMsg('_realPath <- ""');
287
265
  resolve('');
288
266
  } else {
289
- reject(
290
- this.fmtError(`${err.message} ${rPath}`, 'realPath', err.code)
291
- );
267
+ reject(this.fmtError(`${err.message} ${rPath}`, 'realPath', err.code));
292
268
  }
293
269
  }
294
270
  this.debugMsg(`_realPath <- ${absPath}`);
@@ -309,7 +285,6 @@ class SftpClient {
309
285
  : this.fmtError(`${e.message} ${remotePath}`, 'realPath', e.code);
310
286
  } finally {
311
287
  removeTempListeners(this, listeners, 'realPath');
312
- this._resetEventFlags();
313
288
  }
314
289
  }
315
290
 
@@ -336,13 +311,7 @@ class SftpClient {
336
311
  this.sftp.stat(aPath, (err, stats) => {
337
312
  if (err) {
338
313
  if (err.code === 2 || err.code === 4) {
339
- reject(
340
- this.fmtError(
341
- `No such file: ${aPath}`,
342
- '_stat',
343
- errorCode.notexist
344
- )
345
- );
314
+ reject(this.fmtError(`No such file: ${aPath}`, '_stat', errorCode.notexist));
346
315
  } else {
347
316
  reject(this.fmtError(`${err.message} ${aPath}`, '_stat', err.code));
348
317
  }
@@ -380,7 +349,6 @@ class SftpClient {
380
349
  throw err.custom ? err : this.fmtError(err, 'stat', err.code);
381
350
  } finally {
382
351
  removeTempListeners(this, listeners, 'stat');
383
- this._resetEventFlags();
384
352
  }
385
353
  }
386
354
 
@@ -437,7 +405,6 @@ class SftpClient {
437
405
  throw err.custom ? err : this.fmtError(err, 'exists', err.code);
438
406
  } finally {
439
407
  removeTempListeners(this, listeners, 'exists');
440
- this._resetEventFlags();
441
408
  }
442
409
  }
443
410
 
@@ -447,7 +414,7 @@ class SftpClient {
447
414
  * List contents of a remote directory. If a pattern is provided,
448
415
  * filter the results to only include files with names that match
449
416
  * the supplied pattern. Return value is an array of file entry
450
- * objects that include properties for type, name, size, modifiyTime,
417
+ * objects that include properties for type, name, size, modifyTime,
451
418
  * accessTime, rights {user, group other}, owner and group.
452
419
  *
453
420
  * @param {String} remotePath - path to remote directory
@@ -458,9 +425,7 @@ class SftpClient {
458
425
  return new Promise((resolve, reject) => {
459
426
  this.sftp.readdir(remotePath, (err, fileList) => {
460
427
  if (err) {
461
- reject(
462
- this.fmtError(`${err.message} ${remotePath}`, 'list', err.code)
463
- );
428
+ reject(this.fmtError(`${err.message} ${remotePath}`, 'list', err.code));
464
429
  } else {
465
430
  const reg = /-/gi;
466
431
  const newList = fileList.map((item) => {
@@ -497,12 +462,9 @@ class SftpClient {
497
462
  haveConnection(this, 'list');
498
463
  return await this._list(remotePath, filter);
499
464
  } catch (e) {
500
- throw e.custom
501
- ? e
502
- : this.fmtError(`${e.message} ${remotePath}`, 'list', e.code);
465
+ throw e.custom ? e : this.fmtError(`${e.message} ${remotePath}`, 'list', e.code);
503
466
  } finally {
504
467
  removeTempListeners(this, listeners, 'list');
505
- this._resetEventFlags();
506
468
  }
507
469
  }
508
470
 
@@ -536,11 +498,14 @@ class SftpClient {
536
498
  };
537
499
  rdr = this.sftp.createReadStream(rPath, opts.readStreamOptions);
538
500
  rdr.once('error', (err) => {
501
+ if (dst && typeof dst !== 'string' && !dst.destroyed) {
502
+ dst.destroy();
503
+ }
539
504
  reject(this.fmtError(`${err.message} ${rPath}`, '_get', err.code));
540
505
  });
541
506
  if (dst === undefined) {
542
507
  // no dst specified, return buffer of data
543
- this.debugMsg('get returning buffer of data');
508
+ this.debugMsg('get resolving buffer of data');
544
509
  wtr = concat((buff) => {
545
510
  resolve(buff);
546
511
  });
@@ -575,8 +540,10 @@ class SftpClient {
575
540
  });
576
541
  rdr.once('end', () => {
577
542
  if (typeof dst === 'string') {
543
+ this.debugMsg('get: resolving with dst filename');
578
544
  resolve(dst);
579
- } else {
545
+ } else if (dst !== undefined) {
546
+ this.debugMsg('get: resolving with writer stream object');
580
547
  resolve(wtr);
581
548
  }
582
549
  });
@@ -591,12 +558,9 @@ class SftpClient {
591
558
  haveConnection(this, 'get');
592
559
  return await this._get(remotePath, dst, options);
593
560
  } catch (e) {
594
- throw e.custom
595
- ? e
596
- : this.fmtError(`${e.message} ${remotePath}`, 'get', e.code);
561
+ throw e.custom ? e : this.fmtError(`${e.message} ${remotePath}`, 'get', e.code);
597
562
  } finally {
598
563
  removeTempListeners(this, listeners, 'get');
599
- this._resetEventFlags();
600
564
  }
601
565
  }
602
566
 
@@ -614,9 +578,7 @@ class SftpClient {
614
578
  return new Promise((resolve, reject) => {
615
579
  this.sftp.fastGet(rPath, lPath, opts, (err) => {
616
580
  if (err) {
617
- reject(
618
- this.fmtError(`${err.message} Remote: ${rPath} Local: ${lPath}`)
619
- );
581
+ reject(this.fmtError(`${err.message} Remote: ${rPath} Local: ${lPath}`));
620
582
  }
621
583
  resolve(`${rPath} was successfully download to ${lPath}!`);
622
584
  });
@@ -630,9 +592,7 @@ class SftpClient {
630
592
  haveConnection(this, 'fastGet');
631
593
  const ftype = await this.exists(remotePath);
632
594
  if (ftype !== '-') {
633
- const msg = `${
634
- !ftype ? 'No such file ' : 'Not a regular file'
635
- } ${remotePath}`;
595
+ const msg = `${!ftype ? 'No such file ' : 'Not a regular file'} ${remotePath}`;
636
596
  throw this.fmtError(msg, 'fastGet', errorCode.badPath);
637
597
  }
638
598
  const localCheck = haveLocalCreate(localPath);
@@ -648,7 +608,6 @@ class SftpClient {
648
608
  throw this.fmtError(err, 'fastGet');
649
609
  } finally {
650
610
  removeTempListeners(this, listeners, 'fastGet');
651
- this._resetEventFlags();
652
611
  }
653
612
  }
654
613
 
@@ -707,7 +666,6 @@ class SftpClient {
707
666
  throw e.custom ? e : this.fmtError(e.message, 'fastPut', e.code);
708
667
  } finally {
709
668
  removeTempListeners(this, listeners, 'fastPut');
710
- this._resetEventFlags();
711
669
  }
712
670
  }
713
671
 
@@ -755,9 +713,7 @@ class SftpClient {
755
713
  rdr.once('error', (err) => {
756
714
  reject(
757
715
  this.fmtError(
758
- `${err.message} ${
759
- typeof lPath === 'string' ? lPath : '<stream>'
760
- }`,
716
+ `${err.message} ${typeof lPath === 'string' ? lPath : '<stream>'}`,
761
717
  '_put',
762
718
  err.code
763
719
  )
@@ -788,7 +744,6 @@ class SftpClient {
788
744
  throw e.custom ? e : this.fmtError(e.message, 'put', e.code);
789
745
  } finally {
790
746
  removeTempListeners(this, listeners, 'put');
791
- this._resetEventFlags();
792
747
  }
793
748
  }
794
749
 
@@ -845,7 +800,6 @@ class SftpClient {
845
800
  throw e.custom ? e : this.fmtError(e.message, 'append', e.code);
846
801
  } finally {
847
802
  removeTempListeners(this, listeners, 'append');
848
- this._resetEventFlags();
849
803
  }
850
804
  }
851
805
 
@@ -936,7 +890,6 @@ class SftpClient {
936
890
  throw this.fmtError(`${err.message}`, 'mkdir', err.code);
937
891
  } finally {
938
892
  removeTempListeners(this, listeners, 'append');
939
- this._resetEventFlags();
940
893
  }
941
894
  }
942
895
 
@@ -977,9 +930,7 @@ class SftpClient {
977
930
  }
978
931
  const promiseList = [];
979
932
  for (const f of files) {
980
- promiseList.push(
981
- this._delete(`${p}${this.remotePathSep}${f.name}`)
982
- );
933
+ promiseList.push(this._delete(`${p}${this.remotePathSep}${f.name}`));
983
934
  }
984
935
  await Promise.all(promiseList);
985
936
  }
@@ -1015,7 +966,6 @@ class SftpClient {
1015
966
  throw err.custom ? err : this.fmtError(err.message, 'rmdir', err.code);
1016
967
  } finally {
1017
968
  removeTempListeners(this, listeners, 'rmdir');
1018
- this._resetEventFlags();
1019
969
  }
1020
970
  }
1021
971
 
@@ -1037,9 +987,7 @@ class SftpClient {
1037
987
  if (notFoundOK && err.code === 2) {
1038
988
  resolve(`Successfully deleted ${rPath}`);
1039
989
  } else {
1040
- reject(
1041
- this.fmtError(`${err.message} ${rPath}`, 'delete', err.code)
1042
- );
990
+ reject(this.fmtError(`${err.message} ${rPath}`, 'delete', err.code));
1043
991
  }
1044
992
  }
1045
993
  resolve(`Successfully deleted ${rPath}`);
@@ -1057,7 +1005,6 @@ class SftpClient {
1057
1005
  throw err.custom ? err : this.fmtError(err.message, 'delete', err.code);
1058
1006
  } finally {
1059
1007
  removeTempListeners(this, listeners, 'delete');
1060
- this._resetEventFlags();
1061
1008
  }
1062
1009
  }
1063
1010
 
@@ -1098,14 +1045,9 @@ class SftpClient {
1098
1045
  } catch (err) {
1099
1046
  throw err.custom
1100
1047
  ? err
1101
- : this.fmtError(
1102
- `${err.message} ${fromPath} ${toPath}`,
1103
- 'rename',
1104
- err.code
1105
- );
1048
+ : this.fmtError(`${err.message} ${fromPath} ${toPath}`, 'rename', err.code);
1106
1049
  } finally {
1107
1050
  removeTempListeners(this, listeners, 'rename');
1108
- this._resetEventFlags();
1109
1051
  }
1110
1052
  }
1111
1053
 
@@ -1147,14 +1089,9 @@ class SftpClient {
1147
1089
  } catch (err) {
1148
1090
  throw err.custom
1149
1091
  ? err
1150
- : this.fmtError(
1151
- `${err.message} ${fromPath} ${toPath}`,
1152
- 'posixRename',
1153
- err.code
1154
- );
1092
+ : this.fmtError(`${err.message} ${fromPath} ${toPath}`, 'posixRename', err.code);
1155
1093
  } finally {
1156
1094
  removeTempListeners(this, listeners, 'posixRename');
1157
- this._resetEventFlags();
1158
1095
  }
1159
1096
  }
1160
1097
 
@@ -1191,7 +1128,6 @@ class SftpClient {
1191
1128
  : this.fmtError(`${err.message} ${remotePath}`, 'chmod', err.code);
1192
1129
  } finally {
1193
1130
  removeTempListeners(this, listeners, 'chmod');
1194
- this._resetEventFlags();
1195
1131
  }
1196
1132
  }
1197
1133
 
@@ -1266,9 +1202,7 @@ class SftpClient {
1266
1202
  }
1267
1203
  this.client.emit('upload', { source: newSrc, destination: newDst });
1268
1204
  } else {
1269
- this.debugMsg(
1270
- `uploadDir: File ignored: ${e.name} not a regular file`
1271
- );
1205
+ this.debugMsg(`uploadDir: File ignored: ${e.name} not a regular file`);
1272
1206
  }
1273
1207
  await Promise.all(fileUploads);
1274
1208
  }
@@ -1291,7 +1225,6 @@ class SftpClient {
1291
1225
  throw err.custom ? err : this.fmtError(err, 'uploadDir');
1292
1226
  } finally {
1293
1227
  removeTempListeners(this, listeners, 'chmod');
1294
- this._resetEventFlags();
1295
1228
  }
1296
1229
  }
1297
1230
 
@@ -1353,9 +1286,7 @@ class SftpClient {
1353
1286
  }
1354
1287
  this.client.emit('download', { source: newSrc, destination: newDst });
1355
1288
  } else {
1356
- this.debugMsg(
1357
- `downloadDir: File ignored: ${f.name} not regular file`
1358
- );
1289
+ this.debugMsg(`downloadDir: File ignored: ${f.name} not regular file`);
1359
1290
  }
1360
1291
  }
1361
1292
  await Promise.all(downloadFiles);
@@ -1377,7 +1308,6 @@ class SftpClient {
1377
1308
  throw err.custom ? err : this.fmtError(err, 'downloadDir', err.code);
1378
1309
  } finally {
1379
1310
  removeTempListeners(this, listeners, 'downloadDir');
1380
- this._resetEventFlags();
1381
1311
  }
1382
1312
  }
1383
1313
 
@@ -1402,12 +1332,9 @@ class SftpClient {
1402
1332
  const stream = this.sftp.createReadStream(remotePath, options);
1403
1333
  return stream;
1404
1334
  } catch (err) {
1405
- throw err.custom
1406
- ? err
1407
- : this.fmtError(err.message, 'createReadStream', err.code);
1335
+ throw err.custom ? err : this.fmtError(err.message, 'createReadStream', err.code);
1408
1336
  } finally {
1409
1337
  removeTempListeners(this, listeners, 'createReadStreame');
1410
- this._resetEventFlags();
1411
1338
  }
1412
1339
  }
1413
1340
 
@@ -1432,12 +1359,9 @@ class SftpClient {
1432
1359
  const stream = this.sftp.createWriteStream(remotePath, options);
1433
1360
  return stream;
1434
1361
  } catch (err) {
1435
- throw err.custom
1436
- ? err
1437
- : this.fmtError(err.message, 'createWriteStream', err.code);
1362
+ throw err.custom ? err : this.fmtError(err.message, 'createWriteStream', err.code);
1438
1363
  } finally {
1439
1364
  removeTempListeners(this, listeners, 'createWriteStream');
1440
- this._resetEventFlags();
1441
1365
  }
1442
1366
  }
1443
1367
 
@@ -1486,11 +1410,7 @@ class SftpClient {
1486
1410
  );
1487
1411
  }
1488
1412
  if (srcExists !== '-') {
1489
- throw this.fmtError(
1490
- `Source not a file ${srcPath}`,
1491
- 'rcopy',
1492
- errorCode.badPath
1493
- );
1413
+ throw this.fmtError(`Source not a file ${srcPath}`, 'rcopy', errorCode.badPath);
1494
1414
  }
1495
1415
  const dstPath = await normalizeRemotePath(this, dst);
1496
1416
  const dstExists = await this.exists(dstPath);
@@ -1501,12 +1421,11 @@ class SftpClient {
1501
1421
  errorCode.badPath
1502
1422
  );
1503
1423
  }
1504
- return await this._rcopy(srcPath, dstPath);
1424
+ return this._rcopy(srcPath, dstPath);
1505
1425
  } catch (err) {
1506
1426
  throw err.custom ? err : this.fmtError(err, 'rcopy');
1507
1427
  } finally {
1508
1428
  removeTempListeners(this, listeners, 'rcopy');
1509
- this._resetEventFlags();
1510
1429
  }
1511
1430
  }
1512
1431
  /**
@@ -1527,14 +1446,17 @@ class SftpClient {
1527
1446
  resolve(true);
1528
1447
  };
1529
1448
  this.on('close', endCloseHandler);
1530
- if (haveConnection(this, 'end', reject)) {
1449
+ if (this.client.sftp) {
1531
1450
  this.client.end();
1451
+ } else {
1452
+ // no actual connection exists - just resolve
1453
+ this.debugMsg('end: Called when no connection active');
1454
+ resolve(true);
1532
1455
  }
1533
1456
  }).finally(() => {
1534
1457
  removeTempListeners(this, listeners, 'end');
1535
1458
  this.removeListener('close', endCloseHandler);
1536
1459
  this.endCalled = false;
1537
- this._resetEventFlags();
1538
1460
  });
1539
1461
  }
1540
1462
  }