ssh2-sftp-client 7.0.1 → 7.1.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.
- package/README.md +26 -6
- package/README.org +55 -5
- package/package.json +21 -7
- package/src/constants.js +3 -5
- package/src/index.js +111 -136
- package/src/utils.js +57 -42
- package/CHANGELOG.org +0 -245
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
- [Specifying Paths](#sec-5-1)
|
|
7
7
|
- [Methods](#sec-5-2)
|
|
8
8
|
- [new SftpClient(name) ===> SFTP client object](#sec-5-2-1)
|
|
9
|
-
- [connect(config) ===>
|
|
9
|
+
- [connect(config) ===> SFTP object](#sec-5-2-2)
|
|
10
10
|
- [list(path, pattern) ==> Array[object]](#sec-5-2-3)
|
|
11
11
|
- [exists(path) ==> boolean](#sec-5-2-4)
|
|
12
12
|
- [stat(path) ==> object](#sec-5-2-5)
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
- [How can I connect through a Socks Proxy](#sec-7-4)
|
|
41
41
|
- [Timeout while waiting for handshake or handshake errors](#sec-7-5)
|
|
42
42
|
- [How can I limit upload/download speed](#sec-7-6)
|
|
43
|
+
- [Connection hangs or fails for larger files](#sec-7-7)
|
|
43
44
|
- [Examples](#sec-8)
|
|
44
45
|
- [Troubleshooting](#sec-9)
|
|
45
46
|
- [Common Errors](#sec-9-1)
|
|
@@ -60,9 +61,9 @@ an SFTP client for node.js, a wrapper around [SSH2](https://github.com/mscdex/ss
|
|
|
60
61
|
|
|
61
62
|
Documentation on the methods and available options in the underlying modules can be found on the [SSH2](https://github.com/mscdex/ssh2) project pages.
|
|
62
63
|
|
|
63
|
-
Current stable release is **v7.
|
|
64
|
+
Current stable release is **v7.1.0**.
|
|
64
65
|
|
|
65
|
-
Code has been tested against Node versions 12.22.
|
|
66
|
+
Code has been tested against Node versions 12.22.6, 14.17.6 and 16.10.0
|
|
66
67
|
|
|
67
68
|
Node versions < 10.x are not supported.
|
|
68
69
|
|
|
@@ -94,10 +95,12 @@ sftp.connect({
|
|
|
94
95
|
|
|
95
96
|
# Version 7.x Changes<a id="sec-4"></a>
|
|
96
97
|
|
|
97
|
-
- This version is based on version 1.
|
|
98
|
+
- This version is based on version 1.x.x of `ssh2`. This version of `ssh2` is a complete re-write of the `ssh2` library. This re-write addresses issues encountered when using node v14 as well as some design weaknesses in the previous 0.8.x version.
|
|
98
99
|
|
|
99
100
|
- **Breaking Change** Expanded option handling for `get()` and `put()` methods. A number of use cases were identified where setting specific options on the read and write streams and the pipe operation are necessary. For example, disabling `autoClose` on streams or the `end` event in pipes. The `options` argument for `get()` and `put()` calls now supports properties for `readStreamOptions`, `writeStreamOptions` and `pipeOptions`. Note that options are only applied to streams created by the `get()` and `put()` methods. Streams passed into these methods are under the control of the client code and therefore cannot have options supplied in arguments to those streams (you would apply such options when you create the streams). Options are typically only necessary in special use cases. Most of the time, no options are required. However, if you are currently using options to either `put()` or `get()`, you will need to update your code to map these options to the new structure.
|
|
100
101
|
|
|
102
|
+
- **Breaking Change 7.1.0** A race condition was identified when using a put() call with a writeStream option of `autoClose: false`. In some situations, the promise would be resolved before the final close of the write stream. This could result in errors if you immediately attempt to access the uploaded file. To avoid this situatioin, the promise is now resolved once a `close` event is emitted. This means that setting `autoClose: false` can no longer be supported. The write stream for `put()` will autoClose once data writing has completed.
|
|
103
|
+
|
|
101
104
|
- Improved event handling. A listener for a global error event is now defined to catch errors which occur in-between method calls i.e. connection lost in-between calls to the library methods. A new mechanism has also been added for removal of listeners when no longer required.
|
|
102
105
|
|
|
103
106
|
# Documentation<a id="sec-5"></a>
|
|
@@ -174,7 +177,7 @@ Constructor to create a new `ssh2-sftp-client` object. An optional `name` string
|
|
|
174
177
|
});
|
|
175
178
|
```
|
|
176
179
|
|
|
177
|
-
### connect(config) ===>
|
|
180
|
+
### connect(config) ===> SFTP object<a id="sec-5-2-2"></a>
|
|
178
181
|
|
|
179
182
|
Connect to an sftp server. Full documentation for connection options is available [here](https://github.com/mscdex/ssh2#user-content-client-methods)
|
|
180
183
|
|
|
@@ -496,11 +499,12 @@ Upload data from local system to remote server. If the `src` argument is a strin
|
|
|
496
499
|
flags: 'w', // w - write and a - append
|
|
497
500
|
encoding: null, // use null for binary files
|
|
498
501
|
mode: 0o666, // mode to use for created file (rwx)
|
|
499
|
-
autoClose: true // automatically close the write stream when finished
|
|
500
502
|
}}
|
|
501
503
|
```
|
|
502
504
|
|
|
503
505
|
The most common options to use are mode and encoding. The values shown above are the defaults. You do not have to set encoding to utf-8 for text files, null is fine for all file types. However, using utf-8 encoding for binary files will often result in data corruption.
|
|
506
|
+
|
|
507
|
+
Note that you cannot set `autoClose: false` for `writeStreamOptions`. If you attempt to set this property to false, it will be ignored. This is necessary to avoid a race condition which may exist when setting `autoClose` to false on the writeStream. As there is no easy way to access the writeStream once the promise has been resolved, setting this to autoClose false is not terribly useful as there is no easy way to manually close the stream after the promise has been resolved.
|
|
504
508
|
|
|
505
509
|
2. Example Use
|
|
506
510
|
|
|
@@ -1106,6 +1110,8 @@ client.connect({
|
|
|
1106
1110
|
|
|
1107
1111
|
Some users have encountered the error 'Timeout while waiting for handshake' or 'Handshake failed, no matching client->server ciphers. This is often due to the client not having the correct configuration for the transport layer algorithms used by ssh2. One of the connect options provided by the ssh2 module is `algorithm`, which is an object that allows you to explicitly set the key exchange, ciphers, hmac and compression algorithms as well as server host key used to establish the initial secure connection. See the SSH2 documentation for details. Getting these parameters correct usually resolves the issue.
|
|
1108
1112
|
|
|
1113
|
+
When encountering this type of problem, one worthwhile approach is to use openSSH's CLI sftp program with the `-v` switch to raise loggin levels. This will show you what algorithms the CLI is using. You can then use this information to match the names with the accepted algorithm names documented in the `ssh2` README to set the properties in the `algorithms` object.
|
|
1114
|
+
|
|
1109
1115
|
## How can I limit upload/download speed<a id="sec-7-6"></a>
|
|
1110
1116
|
|
|
1111
1117
|
If you want to limit the amount of bandwidth used during upload/download of data, you can use a stream to limit throughput. The following example was provided by *kennylbj*. Note that there is a caveat that we must set the `autoClose` flag to false to avoid calling an extra `_read()` on a closed stream that may cause \_get Permission Denied error in ssh2-streams.
|
|
@@ -1143,6 +1149,14 @@ try {
|
|
|
1143
1149
|
}
|
|
1144
1150
|
```
|
|
1145
1151
|
|
|
1152
|
+
## Connection hangs or fails for larger files<a id="sec-7-7"></a>
|
|
1153
|
+
|
|
1154
|
+
This was contributed by Ladislav Jacho. Thanks.
|
|
1155
|
+
|
|
1156
|
+
A symptom of this issue is that you are able to upload small files, but uploading larger ones fail. You probably have an MTU/fragmentation problem. For each network interface on both client and server set the MTU to 576, e.g. `ifconfig eth0 mtu 576`. If that works, you need to find the largest MTU which will work for your network. An MTU which is too small will adversely affect throughput speed. A common value to use is an MTU of 1400.
|
|
1157
|
+
|
|
1158
|
+
For more explanation, see [issue #342](https://github.com/theophilusx/ssh2-sftp-client/issues/342).
|
|
1159
|
+
|
|
1146
1160
|
# Examples<a id="sec-8"></a>
|
|
1147
1161
|
|
|
1148
1162
|
I have started collecting example scripts in the example directory of the repository. These are mainly scripts I have put together in order to investigate issues or provide samples for users. They are not robust, lack adequate error handling and may contain errors. However, I think they are still useful for helping developers see how the module and API can be used.
|
|
@@ -1320,6 +1334,8 @@ Perhaps the best assistance is a minimal reproducible example of the issue. Once
|
|
|
1320
1334
|
|
|
1321
1335
|
Pull requests are always welcomed. However, please ensure your changes pass all tests and if your adding a new feature, that tests for that feature are included. Likewise, for new features or enhancements, please include any relevant documentation updates.
|
|
1322
1336
|
|
|
1337
|
+
**Note**: The `README.md` file is generated from the `README.org` file. Therefore, any documentation updates or fixes need to be made to the `README.org` file. This file is *tangled* using `Emacs` org mode. If you don't use Emacs or org-mode, don't be too concerned. The org-mode syntax is straight-forward and similar to *markdown*. I will verify any updates to `README.org` and generate a new `README.md` when necessary. The main point to note is that any changes made directly to `README.md` will not persist and will be lost when a new version is generated, so don't modify that file.
|
|
1338
|
+
|
|
1323
1339
|
This module will adopt a standard semantic versioning policy. Please indicate in your pull request what level of change it represents i.e.
|
|
1324
1340
|
|
|
1325
1341
|
- **Major:** Change to API or major change in functionality which will require an increase in major version number.
|
|
@@ -1341,3 +1357,7 @@ Thanks to the following for their contributions -
|
|
|
1341
1357
|
- **teenangst:** Contributed fix for error code 4 in stat() method
|
|
1342
1358
|
- **kennylbj:** Contributed example of using a throttle stream to limit upload/download bandwidth.
|
|
1343
1359
|
- **anton-erofeev:** Documentation fix
|
|
1360
|
+
- **Ladislav Jacho:** Contributed solution explanation for connections hanging when transferring larger files.
|
|
1361
|
+
- **Emma Milner:** Contributed fix for put() bug
|
|
1362
|
+
- **Witni Davis:** Contributed PR to fix put() RCE when using 'finish' rather than 'close' to resolve promise
|
|
1363
|
+
- **Maik Marschner:** Contributed fix for connect() not returning sftp object. Also included test to check for this regression in future.
|
package/README.org
CHANGED
|
@@ -9,9 +9,9 @@ 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 *v7.
|
|
12
|
+
Current stable release is *v7.1.0*.
|
|
13
13
|
|
|
14
|
-
Code has been tested against Node versions 12.22.
|
|
14
|
+
Code has been tested against Node versions 12.22.6, 14.17.6 and 16.10.0
|
|
15
15
|
|
|
16
16
|
Node versions < 10.x are not supported.
|
|
17
17
|
|
|
@@ -43,7 +43,7 @@ npm install ssh2-sftp-client
|
|
|
43
43
|
|
|
44
44
|
* Version 7.x Changes
|
|
45
45
|
|
|
46
|
-
- This version is based on version 1.
|
|
46
|
+
- This version is based on version 1.x.x of ~ssh2~. This version of ~ssh2~ is a
|
|
47
47
|
complete re-write of the ~ssh2~ library. This re-write addresses issues
|
|
48
48
|
encountered when using node v14 as well as some design weaknesses in the
|
|
49
49
|
previous 0.8.x version.
|
|
@@ -62,6 +62,14 @@ npm install ssh2-sftp-client
|
|
|
62
62
|
currently using options to either ~put()~ or ~get()~, you will need to update
|
|
63
63
|
your code to map these options to the new structure.
|
|
64
64
|
|
|
65
|
+
- *Breaking Change 7.1.0* A race condition was identified when using a put()
|
|
66
|
+
call with a writeStream option of ~autoClose: false~. In some situations, the
|
|
67
|
+
promise would be resolved before the final close of the write stream. This
|
|
68
|
+
could result in errors if you immediately attempt to access the uploaded
|
|
69
|
+
file. To avoid this situatioin, the promise is now resolved once a ~close~
|
|
70
|
+
event is emitted. This means that setting ~autoClose: false~ can no longer be
|
|
71
|
+
supported. The write stream for ~put()~ will autoClose once data writing has completed.
|
|
72
|
+
|
|
65
73
|
- Improved event handling. A listener for a global error event is now defined to
|
|
66
74
|
catch errors which occur in-between method calls i.e. connection lost
|
|
67
75
|
in-between calls to the library methods. A new mechanism has also been added
|
|
@@ -175,7 +183,7 @@ client has thrown the error.
|
|
|
175
183
|
});
|
|
176
184
|
#+end_src
|
|
177
185
|
|
|
178
|
-
*** connect(config) ===>
|
|
186
|
+
*** connect(config) ===> SFTP object
|
|
179
187
|
|
|
180
188
|
Connect to an sftp server. Full documentation for connection options is
|
|
181
189
|
available [[https://github.com/mscdex/ssh2#user-content-client-methods][here]]
|
|
@@ -546,7 +554,6 @@ option value. For example, you might use the following to set ~writeStream~ opti
|
|
|
546
554
|
flags: 'w', // w - write and a - append
|
|
547
555
|
encoding: null, // use null for binary files
|
|
548
556
|
mode: 0o666, // mode to use for created file (rwx)
|
|
549
|
-
autoClose: true // automatically close the write stream when finished
|
|
550
557
|
}}
|
|
551
558
|
#+end_src
|
|
552
559
|
|
|
@@ -555,6 +562,14 @@ the defaults. You do not have to set encoding to utf-8 for text files, null is
|
|
|
555
562
|
fine for all file types. However, using utf-8 encoding for binary files will
|
|
556
563
|
often result in data corruption.
|
|
557
564
|
|
|
565
|
+
Note that you cannot set ~autoClose: false~ for ~writeStreamOptions~. If you
|
|
566
|
+
attempt to set this property to false, it will be ignored. This is necessary to
|
|
567
|
+
avoid a race condition which may exist when setting ~autoClose~ to false on the
|
|
568
|
+
writeStream. As there is no easy way to access the writeStream once the promise
|
|
569
|
+
has been resolved, setting this to autoClose false is not terribly useful as
|
|
570
|
+
there is no easy way to manually close the stream after the promise has been
|
|
571
|
+
resolved.
|
|
572
|
+
|
|
558
573
|
**** Example Use
|
|
559
574
|
|
|
560
575
|
#+begin_src javascript
|
|
@@ -1355,6 +1370,12 @@ host key used to establish the initial secure connection. See the SSH2
|
|
|
1355
1370
|
documentation for details. Getting these parameters correct usually resolves the
|
|
1356
1371
|
issue.
|
|
1357
1372
|
|
|
1373
|
+
When encountering this type of problem, one worthwhile approach is to use
|
|
1374
|
+
openSSH's CLI sftp program with the =-v= switch to raise loggin levels. This
|
|
1375
|
+
will show you what algorithms the CLI is using. You can then use this
|
|
1376
|
+
information to match the names with the accepted algorithm names documented in
|
|
1377
|
+
the =ssh2= README to set the properties in the =algorithms= object.
|
|
1378
|
+
|
|
1358
1379
|
** How can I limit upload/download speed
|
|
1359
1380
|
|
|
1360
1381
|
If you want to limit the amount of bandwidth used during upload/download of
|
|
@@ -1396,6 +1417,19 @@ issue.
|
|
|
1396
1417
|
await client.end();
|
|
1397
1418
|
}
|
|
1398
1419
|
#+end_src
|
|
1420
|
+
** Connection hangs or fails for larger files
|
|
1421
|
+
|
|
1422
|
+
This was contributed by Ladislav Jacho. Thanks.
|
|
1423
|
+
|
|
1424
|
+
A symptom of this issue is that you are able to upload small files, but
|
|
1425
|
+
uploading larger ones fail. You probably have an MTU/fragmentation problem. For
|
|
1426
|
+
each network interface on both client and server set the MTU to 576, e.g.
|
|
1427
|
+
=ifconfig eth0 mtu 576=. If that works, you need to find the largest MTU which
|
|
1428
|
+
will work for your network. An MTU which is too small will adversely affect
|
|
1429
|
+
throughput speed. A common value to use is an MTU of 1400.
|
|
1430
|
+
|
|
1431
|
+
For more explanation, see [[https://github.com/theophilusx/ssh2-sftp-client/issues/342][issue #342]].
|
|
1432
|
+
|
|
1399
1433
|
* Examples
|
|
1400
1434
|
|
|
1401
1435
|
I have started collecting example scripts in the example directory of the
|
|
@@ -1705,6 +1739,15 @@ tests and if your adding a new feature, that tests for that feature are
|
|
|
1705
1739
|
included. Likewise, for new features or enhancements, please include any
|
|
1706
1740
|
relevant documentation updates.
|
|
1707
1741
|
|
|
1742
|
+
*Note*: The =README.md= file is generated from the =README.org= file. Therefore,
|
|
1743
|
+
any documentation updates or fixes need to be made to the =README.org= file.
|
|
1744
|
+
This file is /tangled/ using =Emacs= org mode. If you don't use Emacs or
|
|
1745
|
+
org-mode, don't be too concerned. The org-mode syntax is straight-forward and
|
|
1746
|
+
similar to /markdown/. I will verify any updates to =README.org= and generate a
|
|
1747
|
+
new =README.md= when necessary. The main point to note is that any changes made
|
|
1748
|
+
directly to =README.md= will not persist and will be lost when a new version is
|
|
1749
|
+
generated, so don't modify that file.
|
|
1750
|
+
|
|
1708
1751
|
This module will adopt a standard semantic versioning policy. Please indicate in
|
|
1709
1752
|
your pull request what level of change it represents i.e.
|
|
1710
1753
|
|
|
@@ -1734,3 +1777,10 @@ Thanks to the following for their contributions -
|
|
|
1734
1777
|
- kennylbj :: Contributed example of using a throttle stream to limit
|
|
1735
1778
|
upload/download bandwidth.
|
|
1736
1779
|
- anton-erofeev :: Documentation fix
|
|
1780
|
+
- Ladislav Jacho :: Contributed solution explanation for connections hanging
|
|
1781
|
+
when transferring larger files.
|
|
1782
|
+
- Emma Milner :: Contributed fix for put() bug
|
|
1783
|
+
- Witni Davis :: Contributed PR to fix put() RCE when using 'finish' rather than
|
|
1784
|
+
'close' to resolve promise
|
|
1785
|
+
- Maik Marschner :: Contributed fix for connect() not returning sftp object.
|
|
1786
|
+
Also included test to check for this regression in future.
|
package/package.json
CHANGED
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssh2-sftp-client",
|
|
3
|
-
"version": "7.0
|
|
3
|
+
"version": "7.1.0",
|
|
4
4
|
"description": "ssh2 sftp client for node",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/theophilusx/ssh2-sftp-client"
|
|
9
9
|
},
|
|
10
|
-
"keywords": [
|
|
10
|
+
"keywords": [
|
|
11
|
+
"sftp",
|
|
12
|
+
"nodejs",
|
|
13
|
+
"promises"
|
|
14
|
+
],
|
|
11
15
|
"scripts": {
|
|
12
16
|
"test": "mocha",
|
|
13
17
|
"coverage": "nyc npm run test",
|
|
14
|
-
"lint": "eslint \"src/**/*.js\"
|
|
18
|
+
"lint": "eslint \"src/**/*.js\" \"test/**/*.js\""
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=10.24.1"
|
|
15
22
|
},
|
|
16
23
|
"author": "Tim Cross",
|
|
17
24
|
"email": "theophilusx@gmail.com",
|
|
@@ -25,17 +32,24 @@
|
|
|
25
32
|
"dependencies": {
|
|
26
33
|
"concat-stream": "^2.0.0",
|
|
27
34
|
"promise-retry": "^2.0.1",
|
|
28
|
-
"ssh2": "^1.
|
|
35
|
+
"ssh2": "^1.5.0"
|
|
29
36
|
},
|
|
30
37
|
"devDependencies": {
|
|
31
|
-
"chai": "^4.
|
|
38
|
+
"chai": "^4.3.4",
|
|
32
39
|
"chai-as-promised": "^7.1.1",
|
|
33
40
|
"chai-subset": "^1.6.0",
|
|
34
|
-
"checksum": "^0.
|
|
41
|
+
"checksum": "^1.0.0",
|
|
35
42
|
"dotenv": "^10.0.0",
|
|
36
|
-
"
|
|
43
|
+
"eslint": "^7.32.0",
|
|
44
|
+
"eslint-config-prettier": "^8.3.0",
|
|
45
|
+
"eslint-plugin-mocha": "^9.0.0",
|
|
46
|
+
"eslint-plugin-node": "^11.1.0",
|
|
47
|
+
"eslint-plugin-promise": "^5.1.0",
|
|
48
|
+
"eslint-plugin-unicorn": "^36.0.0",
|
|
49
|
+
"mocha": "^9.1.2",
|
|
37
50
|
"moment": "^2.29.1",
|
|
38
51
|
"nyc": "^15.1.0",
|
|
52
|
+
"prettier": "^2.4.1",
|
|
39
53
|
"through2": "^4.0.2",
|
|
40
54
|
"winston": "^3.3.3"
|
|
41
55
|
}
|
package/src/constants.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const errorCode = {
|
|
4
2
|
generic: 'ERR_GENERIC_CLIENT',
|
|
5
3
|
connect: 'ERR_NOT_CONNECTED',
|
|
6
4
|
badPath: 'ERR_BAD_PATH',
|
|
7
5
|
permission: 'EACCES',
|
|
8
6
|
notexist: 'ENOENT',
|
|
9
|
-
notdir: 'ENOTDIR'
|
|
7
|
+
notdir: 'ENOTDIR',
|
|
10
8
|
};
|
|
11
9
|
|
|
12
10
|
const targetType = {
|
|
@@ -15,10 +13,10 @@ const targetType = {
|
|
|
15
13
|
writeDir: 3,
|
|
16
14
|
readDir: 4,
|
|
17
15
|
readObj: 5,
|
|
18
|
-
writeObj: 6
|
|
16
|
+
writeObj: 6,
|
|
19
17
|
};
|
|
20
18
|
|
|
21
19
|
module.exports = {
|
|
22
20
|
errorCode,
|
|
23
|
-
targetType
|
|
21
|
+
targetType,
|
|
24
22
|
};
|
package/src/index.js
CHANGED
|
@@ -114,7 +114,7 @@ class SftpClient {
|
|
|
114
114
|
*
|
|
115
115
|
* @param {Object} config - an SFTP configuration object
|
|
116
116
|
*
|
|
117
|
-
* @return {Promise} which will resolve to an sftp client object
|
|
117
|
+
* @return {Promise<Object>} which will resolve to an sftp client object
|
|
118
118
|
*
|
|
119
119
|
*/
|
|
120
120
|
getConnection(config) {
|
|
@@ -133,13 +133,12 @@ class SftpClient {
|
|
|
133
133
|
// .catch((err) => {
|
|
134
134
|
// return Promise.reject(err);
|
|
135
135
|
// })
|
|
136
|
-
.finally(async (
|
|
136
|
+
.finally(async () => {
|
|
137
137
|
this.debugMsg('getConnection: finally clause fired');
|
|
138
138
|
await sleep(500);
|
|
139
139
|
this.removeListener('ready', doReady);
|
|
140
140
|
removeTempListeners(this, 'getConnection');
|
|
141
141
|
this._resetEventFlags();
|
|
142
|
-
return resp;
|
|
143
142
|
})
|
|
144
143
|
);
|
|
145
144
|
}
|
|
@@ -158,7 +157,7 @@ class SftpClient {
|
|
|
158
157
|
resolve(sftp);
|
|
159
158
|
}
|
|
160
159
|
});
|
|
161
|
-
}).finally((
|
|
160
|
+
}).finally(() => {
|
|
162
161
|
this.debugMsg('getSftpChannel: finally clause fired');
|
|
163
162
|
removeTempListeners(this, 'getSftpChannel');
|
|
164
163
|
this._resetEventFlags();
|
|
@@ -174,7 +173,7 @@ class SftpClient {
|
|
|
174
173
|
*
|
|
175
174
|
* @param {Object} config - an SFTP configuration object
|
|
176
175
|
*
|
|
177
|
-
* @return {Promise} which will resolve to an sftp client object
|
|
176
|
+
* @return {Promise<Object>} which will resolve to an sftp client object
|
|
178
177
|
*
|
|
179
178
|
*/
|
|
180
179
|
async connect(config) {
|
|
@@ -205,7 +204,7 @@ class SftpClient {
|
|
|
205
204
|
minTimeout: config.retry_minTimeout || 1000,
|
|
206
205
|
}
|
|
207
206
|
);
|
|
208
|
-
|
|
207
|
+
return this.getSftpChannel();
|
|
209
208
|
} catch (err) {
|
|
210
209
|
this.debugMsg(`connect: Error ${err.message}`);
|
|
211
210
|
this._resetEventFlags();
|
|
@@ -222,7 +221,7 @@ class SftpClient {
|
|
|
222
221
|
* Returns undefined if the path does not exists.
|
|
223
222
|
*
|
|
224
223
|
* @param {String} remotePath - remote path, may be relative
|
|
225
|
-
* @returns {Promise} - remote absolute path or
|
|
224
|
+
* @returns {Promise<String>} - remote absolute path or ''
|
|
226
225
|
*/
|
|
227
226
|
realPath(remotePath) {
|
|
228
227
|
return new Promise((resolve, reject) => {
|
|
@@ -244,13 +243,19 @@ class SftpClient {
|
|
|
244
243
|
resolve(absPath);
|
|
245
244
|
});
|
|
246
245
|
}
|
|
247
|
-
}).finally((
|
|
246
|
+
}).finally(() => {
|
|
248
247
|
removeTempListeners(this, 'realPath');
|
|
249
248
|
this._resetEventFlags();
|
|
250
|
-
return rsp;
|
|
251
249
|
});
|
|
252
250
|
}
|
|
253
251
|
|
|
252
|
+
/**
|
|
253
|
+
* @async
|
|
254
|
+
*
|
|
255
|
+
* Return the current workding directory path
|
|
256
|
+
*
|
|
257
|
+
* @returns {Promise<String>} - current remote working directory
|
|
258
|
+
*/
|
|
254
259
|
cwd() {
|
|
255
260
|
return this.realPath('.');
|
|
256
261
|
}
|
|
@@ -259,7 +264,7 @@ class SftpClient {
|
|
|
259
264
|
* Retrieves attributes for path
|
|
260
265
|
*
|
|
261
266
|
* @param {String} remotePath - a string containing the path to a file
|
|
262
|
-
* @return {Promise} stats - attributes info
|
|
267
|
+
* @return {Promise<Object>} stats - attributes info
|
|
263
268
|
*/
|
|
264
269
|
async stat(remotePath) {
|
|
265
270
|
const _stat = (aPath) => {
|
|
@@ -302,9 +307,8 @@ class SftpClient {
|
|
|
302
307
|
resolve(result);
|
|
303
308
|
}
|
|
304
309
|
});
|
|
305
|
-
}).finally((
|
|
310
|
+
}).finally(() => {
|
|
306
311
|
removeTempListeners(this, 'stat');
|
|
307
|
-
return rsp;
|
|
308
312
|
});
|
|
309
313
|
};
|
|
310
314
|
|
|
@@ -314,11 +318,7 @@ class SftpClient {
|
|
|
314
318
|
return _stat(absPath);
|
|
315
319
|
} catch (err) {
|
|
316
320
|
this._resetEventFlags();
|
|
317
|
-
|
|
318
|
-
throw err;
|
|
319
|
-
} else {
|
|
320
|
-
throw fmtError(err, 'stat', err.code);
|
|
321
|
-
}
|
|
321
|
+
throw err.custom ? err : fmtError(err, 'stat', err.code);
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
@@ -330,7 +330,7 @@ class SftpClient {
|
|
|
330
330
|
*
|
|
331
331
|
* @param {string} remotePath - path to the object on the sftp server.
|
|
332
332
|
*
|
|
333
|
-
* @return {Promise} returns false if object does not exist. Returns type of
|
|
333
|
+
* @return {Promise<Boolean|String>} returns false if object does not exist. Returns type of
|
|
334
334
|
* object if it does
|
|
335
335
|
*/
|
|
336
336
|
async exists(remotePath) {
|
|
@@ -369,17 +369,12 @@ class SftpClient {
|
|
|
369
369
|
this.debugMsg(`exists: throw error ${err.message} ${err.code}`);
|
|
370
370
|
throw err;
|
|
371
371
|
}
|
|
372
|
-
} else {
|
|
373
|
-
this.debugMsg(`exists: ${remotePath} = false`);
|
|
374
|
-
return false;
|
|
375
372
|
}
|
|
373
|
+
this.debugMsg(`exists: default ${remotePath} = false`);
|
|
374
|
+
return false;
|
|
376
375
|
} catch (err) {
|
|
377
376
|
this._resetEventFlags();
|
|
378
|
-
|
|
379
|
-
throw err;
|
|
380
|
-
} else {
|
|
381
|
-
throw fmtError(err, 'exists', err.code);
|
|
382
|
-
}
|
|
377
|
+
throw err.custom ? err : fmtError(err, 'exists', err.code);
|
|
383
378
|
}
|
|
384
379
|
}
|
|
385
380
|
|
|
@@ -394,8 +389,7 @@ class SftpClient {
|
|
|
394
389
|
*
|
|
395
390
|
* @param {String} remotePath - path to remote directory
|
|
396
391
|
* @param {RegExp} pattern - regular expression to match filenames
|
|
397
|
-
* @returns {Promise} array of file description objects
|
|
398
|
-
* @throws {Error}
|
|
392
|
+
* @returns {Promise<Array>} array of file description objects
|
|
399
393
|
*/
|
|
400
394
|
list(remotePath, pattern = /.*/) {
|
|
401
395
|
return new Promise((resolve, reject) => {
|
|
@@ -413,15 +407,15 @@ class SftpClient {
|
|
|
413
407
|
if (fileList) {
|
|
414
408
|
newList = fileList.map((item) => {
|
|
415
409
|
return {
|
|
416
|
-
type: item.longname.
|
|
410
|
+
type: item.longname.slice(0, 1),
|
|
417
411
|
name: item.filename,
|
|
418
412
|
size: item.attrs.size,
|
|
419
413
|
modifyTime: item.attrs.mtime * 1000,
|
|
420
414
|
accessTime: item.attrs.atime * 1000,
|
|
421
415
|
rights: {
|
|
422
|
-
user: item.longname.
|
|
423
|
-
group: item.longname.
|
|
424
|
-
other: item.longname.
|
|
416
|
+
user: item.longname.slice(1, 4).replace(reg, ''),
|
|
417
|
+
group: item.longname.slice(4, 7).replace(reg, ''),
|
|
418
|
+
other: item.longname.slice(7, 10).replace(reg, ''),
|
|
425
419
|
},
|
|
426
420
|
owner: item.attrs.uid,
|
|
427
421
|
group: item.attrs.gid,
|
|
@@ -442,10 +436,9 @@ class SftpClient {
|
|
|
442
436
|
}
|
|
443
437
|
});
|
|
444
438
|
}
|
|
445
|
-
}).finally((
|
|
439
|
+
}).finally(() => {
|
|
446
440
|
removeTempListeners(this, 'list');
|
|
447
441
|
this._resetEventFlags();
|
|
448
|
-
return rsp;
|
|
449
442
|
});
|
|
450
443
|
}
|
|
451
444
|
|
|
@@ -462,7 +455,7 @@ class SftpClient {
|
|
|
462
455
|
* @param {Object} options - options object with supported properties of readStreamOptions,
|
|
463
456
|
* writeStreamOptions and pipeOptions.
|
|
464
457
|
*
|
|
465
|
-
* @return {Promise}
|
|
458
|
+
* @return {Promise<String|Stream|Buffer>}
|
|
466
459
|
*/
|
|
467
460
|
get(
|
|
468
461
|
remotePath,
|
|
@@ -530,7 +523,7 @@ class SftpClient {
|
|
|
530
523
|
}
|
|
531
524
|
rdr.pipe(wtr, options.pipeOptions ? options.pipeOptions : {});
|
|
532
525
|
}
|
|
533
|
-
}).finally((
|
|
526
|
+
}).finally(() => {
|
|
534
527
|
removeTempListeners(this, 'get');
|
|
535
528
|
this._resetEventFlags();
|
|
536
529
|
if (
|
|
@@ -548,7 +541,6 @@ class SftpClient {
|
|
|
548
541
|
) {
|
|
549
542
|
wtr.destroy();
|
|
550
543
|
}
|
|
551
|
-
return rsp;
|
|
552
544
|
});
|
|
553
545
|
}
|
|
554
546
|
|
|
@@ -563,7 +555,7 @@ class SftpClient {
|
|
|
563
555
|
* @param {String} remotePath
|
|
564
556
|
* @param {String} localPath
|
|
565
557
|
* @param {Object} options
|
|
566
|
-
* @return {Promise} the result of downloading the file
|
|
558
|
+
* @return {Promise<String>} the result of downloading the file
|
|
567
559
|
*/
|
|
568
560
|
async fastGet(remotePath, localPath, options) {
|
|
569
561
|
try {
|
|
@@ -583,7 +575,7 @@ class SftpClient {
|
|
|
583
575
|
err.code = errorCode.badPath;
|
|
584
576
|
throw err;
|
|
585
577
|
}
|
|
586
|
-
await new Promise((resolve, reject) => {
|
|
578
|
+
let rslt = await new Promise((resolve, reject) => {
|
|
587
579
|
if (haveConnection(this, 'fastGet', reject)) {
|
|
588
580
|
this.debugMsg(
|
|
589
581
|
`fastGet -> remote: ${remotePath} local: ${localPath} `,
|
|
@@ -598,10 +590,10 @@ class SftpClient {
|
|
|
598
590
|
resolve(`${remotePath} was successfully download to ${localPath}!`);
|
|
599
591
|
});
|
|
600
592
|
}
|
|
601
|
-
}).finally((
|
|
593
|
+
}).finally(() => {
|
|
602
594
|
removeTempListeners(this, 'fastGet');
|
|
603
|
-
return rsp;
|
|
604
595
|
});
|
|
596
|
+
return rslt;
|
|
605
597
|
} catch (err) {
|
|
606
598
|
this._resetEventFlags();
|
|
607
599
|
throw fmtError(err, 'fastGet');
|
|
@@ -619,7 +611,7 @@ class SftpClient {
|
|
|
619
611
|
* @param {String} localPath
|
|
620
612
|
* @param {String} remotePath
|
|
621
613
|
* @param {Object} options
|
|
622
|
-
* @return {Promise} the result of downloading the file
|
|
614
|
+
* @return {Promise<String>} the result of downloading the file
|
|
623
615
|
*/
|
|
624
616
|
fastPut(localPath, remotePath, options) {
|
|
625
617
|
this.debugMsg(`fastPut -> local ${localPath} remote ${remotePath}`);
|
|
@@ -663,10 +655,9 @@ class SftpClient {
|
|
|
663
655
|
resolve(`${localPath} was successfully uploaded to ${remotePath}!`);
|
|
664
656
|
});
|
|
665
657
|
}
|
|
666
|
-
}).finally((
|
|
658
|
+
}).finally(() => {
|
|
667
659
|
removeTempListeners(this, 'fastPut');
|
|
668
660
|
this._resetEventFlags();
|
|
669
|
-
return rsp;
|
|
670
661
|
});
|
|
671
662
|
}
|
|
672
663
|
|
|
@@ -680,12 +671,16 @@ class SftpClient {
|
|
|
680
671
|
* @param {Object} options - options used for read, write stream and pipe configuration
|
|
681
672
|
* value supported by node. Allowed properties are readStreamOptions,
|
|
682
673
|
* writeStreamOptions and pipeOptions.
|
|
683
|
-
* @return {Promise}
|
|
674
|
+
* @return {Promise<String>}
|
|
684
675
|
*/
|
|
685
676
|
put(
|
|
686
677
|
localSrc,
|
|
687
678
|
remotePath,
|
|
688
|
-
options = {
|
|
679
|
+
options = {
|
|
680
|
+
readStreamOptions: {},
|
|
681
|
+
writeStreamOptions: { autoClose: true },
|
|
682
|
+
pipeOptions: {},
|
|
683
|
+
}
|
|
689
684
|
) {
|
|
690
685
|
let wtr, rdr;
|
|
691
686
|
|
|
@@ -693,6 +688,7 @@ class SftpClient {
|
|
|
693
688
|
if (typeof localSrc === 'string') {
|
|
694
689
|
const localCheck = haveLocalAccess(localSrc);
|
|
695
690
|
if (!localCheck.status) {
|
|
691
|
+
this.debugMsg(`put: local source check error ${localCheck.details}`);
|
|
696
692
|
return reject(
|
|
697
693
|
fmtError(
|
|
698
694
|
`Bad path: ${localSrc}: ${localCheck.details}`,
|
|
@@ -706,12 +702,16 @@ class SftpClient {
|
|
|
706
702
|
addTempListeners(this, 'put', reject);
|
|
707
703
|
wtr = this.sftp.createWriteStream(
|
|
708
704
|
remotePath,
|
|
709
|
-
options.writeStreamOptions
|
|
705
|
+
options.writeStreamOptions
|
|
706
|
+
? { ...options.writeStreamOptions, autoClose: true }
|
|
707
|
+
: {}
|
|
710
708
|
);
|
|
711
709
|
wtr.once('error', (err) => {
|
|
710
|
+
this.debugMsg(`put: write stream error ${err.message}`);
|
|
712
711
|
reject(fmtError(`${err.message} ${remotePath}`, 'put', err.code));
|
|
713
712
|
});
|
|
714
|
-
wtr.once('
|
|
713
|
+
wtr.once('close', () => {
|
|
714
|
+
this.debugMsg('put: promise resolved');
|
|
715
715
|
resolve(`Uploaded data stream to ${remotePath}`);
|
|
716
716
|
});
|
|
717
717
|
if (localSrc instanceof Buffer) {
|
|
@@ -722,13 +722,14 @@ class SftpClient {
|
|
|
722
722
|
this.debugMsg(`put source is a file path: ${localSrc}`);
|
|
723
723
|
rdr = fs.createReadStream(
|
|
724
724
|
localSrc,
|
|
725
|
-
options.readStreamOptions ? options.
|
|
725
|
+
options.readStreamOptions ? options.readStreamOptions : {}
|
|
726
726
|
);
|
|
727
727
|
} else {
|
|
728
728
|
this.debugMsg('put source is a stream');
|
|
729
729
|
rdr = localSrc;
|
|
730
730
|
}
|
|
731
731
|
rdr.once('error', (err) => {
|
|
732
|
+
this.debugMsg(`put: read stream error ${err.message}`);
|
|
732
733
|
reject(
|
|
733
734
|
fmtError(
|
|
734
735
|
`${err.message} ${
|
|
@@ -742,7 +743,7 @@ class SftpClient {
|
|
|
742
743
|
rdr.pipe(wtr, options.pipeOptions ? options.pipeOptions : {});
|
|
743
744
|
}
|
|
744
745
|
}
|
|
745
|
-
}).finally((
|
|
746
|
+
}).finally(() => {
|
|
746
747
|
removeTempListeners(this, 'put');
|
|
747
748
|
this._resetEventFlags();
|
|
748
749
|
if (
|
|
@@ -753,14 +754,6 @@ class SftpClient {
|
|
|
753
754
|
) {
|
|
754
755
|
rdr.destroy();
|
|
755
756
|
}
|
|
756
|
-
if (
|
|
757
|
-
wtr &&
|
|
758
|
-
options.writeStreamOptions &&
|
|
759
|
-
options.writeStreamOptions.autoClose === false
|
|
760
|
-
) {
|
|
761
|
-
wtr.destroy();
|
|
762
|
-
}
|
|
763
|
-
return resp;
|
|
764
757
|
});
|
|
765
758
|
}
|
|
766
759
|
|
|
@@ -770,49 +763,45 @@ class SftpClient {
|
|
|
770
763
|
* @param {Buffer|stream} input
|
|
771
764
|
* @param {String} remotePath
|
|
772
765
|
* @param {Object} options
|
|
773
|
-
* @return {Promise}
|
|
766
|
+
* @return {Promise<String>}
|
|
774
767
|
*/
|
|
775
|
-
append(input, remotePath, options = {}) {
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
768
|
+
async append(input, remotePath, options = {}) {
|
|
769
|
+
const fileType = await this.exists(remotePath);
|
|
770
|
+
if (fileType && fileType === 'd') {
|
|
771
|
+
throw fmtError(
|
|
772
|
+
`Bad path: ${remotePath}: cannot append to a directory`,
|
|
773
|
+
'append',
|
|
774
|
+
errorCode.badPath
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
return await new Promise((resolve, reject) => {
|
|
778
|
+
if (haveConnection(this, 'append', reject)) {
|
|
779
|
+
if (typeof input === 'string') {
|
|
780
|
+
reject(fmtError('Cannot append one file to another', 'append'));
|
|
781
|
+
} else {
|
|
782
|
+
this.debugMsg(`append -> remote: ${remotePath} `, options);
|
|
783
|
+
addTempListeners(this, 'append', reject);
|
|
784
|
+
options.flags = 'a';
|
|
785
|
+
let stream = this.sftp.createWriteStream(remotePath, options);
|
|
786
|
+
stream.on('error', (err_1) => {
|
|
787
|
+
reject(
|
|
788
|
+
fmtError(`${err_1.message} ${remotePath}`, 'append', err_1.code)
|
|
789
|
+
);
|
|
790
|
+
});
|
|
791
|
+
stream.on('finish', () => {
|
|
792
|
+
resolve(`Appended data to ${remotePath}`);
|
|
793
|
+
});
|
|
794
|
+
if (input instanceof Buffer) {
|
|
795
|
+
stream.write(input);
|
|
796
|
+
stream.end();
|
|
790
797
|
} else {
|
|
791
|
-
|
|
792
|
-
addTempListeners(this, 'append', reject);
|
|
793
|
-
options.flags = 'a';
|
|
794
|
-
let stream = this.sftp.createWriteStream(remotePath, options);
|
|
795
|
-
stream.on('error', (err) => {
|
|
796
|
-
reject(
|
|
797
|
-
fmtError(`${err.message} ${remotePath}`, 'append', err.code)
|
|
798
|
-
);
|
|
799
|
-
});
|
|
800
|
-
stream.on('finish', () => {
|
|
801
|
-
resolve(`Appended data to ${remotePath}`);
|
|
802
|
-
});
|
|
803
|
-
if (input instanceof Buffer) {
|
|
804
|
-
stream.write(input);
|
|
805
|
-
stream.end();
|
|
806
|
-
} else {
|
|
807
|
-
input.pipe(stream);
|
|
808
|
-
}
|
|
798
|
+
input.pipe(stream);
|
|
809
799
|
}
|
|
810
800
|
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
});
|
|
801
|
+
}
|
|
802
|
+
}).finally(() => {
|
|
803
|
+
removeTempListeners(this, 'append');
|
|
804
|
+
this._resetEventFlags();
|
|
816
805
|
});
|
|
817
806
|
}
|
|
818
807
|
|
|
@@ -823,7 +812,7 @@ class SftpClient {
|
|
|
823
812
|
*
|
|
824
813
|
* @param {string} remotePath - remote directory path.
|
|
825
814
|
* @param {boolean} recursive - if true, recursively create directories
|
|
826
|
-
* @return {Promise}
|
|
815
|
+
* @return {Promise<String>}
|
|
827
816
|
*/
|
|
828
817
|
async mkdir(remotePath, recursive = false) {
|
|
829
818
|
const _mkdir = (p) => {
|
|
@@ -838,6 +827,12 @@ class SftpClient {
|
|
|
838
827
|
let error = new Error(`Bad path: ${p} permission denied`);
|
|
839
828
|
error.code = errorCode.badPath;
|
|
840
829
|
reject(error);
|
|
830
|
+
} else if (err.code === 2) {
|
|
831
|
+
let error = new Error(
|
|
832
|
+
`Bad path: ${p} parent not a directory or not exist`
|
|
833
|
+
);
|
|
834
|
+
error.code = errorCode.badPath;
|
|
835
|
+
reject(error);
|
|
841
836
|
} else {
|
|
842
837
|
reject(err);
|
|
843
838
|
}
|
|
@@ -846,10 +841,9 @@ class SftpClient {
|
|
|
846
841
|
resolve(`${p} directory created`);
|
|
847
842
|
}
|
|
848
843
|
});
|
|
849
|
-
}).finally((
|
|
844
|
+
}).finally(() => {
|
|
850
845
|
removeTempListeners(this, '_mkdir');
|
|
851
846
|
this._resetEventFlags();
|
|
852
|
-
return rsp;
|
|
853
847
|
});
|
|
854
848
|
};
|
|
855
849
|
|
|
@@ -884,7 +878,7 @@ class SftpClient {
|
|
|
884
878
|
* @param {string} remotePath - path to directory to be removed
|
|
885
879
|
* @param {boolean} recursive - if true, remove directories/files in target
|
|
886
880
|
* directory
|
|
887
|
-
* @return {Promise}
|
|
881
|
+
* @return {Promise<String>}
|
|
888
882
|
*/
|
|
889
883
|
async rmdir(remotePath, recursive = false) {
|
|
890
884
|
const _rmdir = (p) => {
|
|
@@ -898,9 +892,8 @@ class SftpClient {
|
|
|
898
892
|
}
|
|
899
893
|
resolve('Successfully removed directory');
|
|
900
894
|
});
|
|
901
|
-
}).finally((
|
|
895
|
+
}).finally(() => {
|
|
902
896
|
removeTempListeners(this, 'rmdir');
|
|
903
|
-
return rsp;
|
|
904
897
|
});
|
|
905
898
|
};
|
|
906
899
|
|
|
@@ -926,11 +919,7 @@ class SftpClient {
|
|
|
926
919
|
return _rmdir(absPath);
|
|
927
920
|
} catch (err) {
|
|
928
921
|
this._resetEventFlags();
|
|
929
|
-
|
|
930
|
-
throw err;
|
|
931
|
-
} else {
|
|
932
|
-
throw fmtError(err, 'rmdir', err.code);
|
|
933
|
-
}
|
|
922
|
+
throw err.custom ? err : fmtError(err, 'rmdir', err.code);
|
|
934
923
|
}
|
|
935
924
|
}
|
|
936
925
|
|
|
@@ -942,7 +931,7 @@ class SftpClient {
|
|
|
942
931
|
* @param {string} remotePath - path to the file to delete
|
|
943
932
|
* @param {boolean} notFoundOK - if true, ignore errors for missing target.
|
|
944
933
|
* Default is false.
|
|
945
|
-
* @return {Promise} with string 'Successfully deleted file' once resolved
|
|
934
|
+
* @return {Promise<String>} with string 'Successfully deleted file' once resolved
|
|
946
935
|
*
|
|
947
936
|
*/
|
|
948
937
|
delete(remotePath, notFoundOK = false) {
|
|
@@ -965,10 +954,9 @@ class SftpClient {
|
|
|
965
954
|
resolve(`Successfully deleted ${remotePath}`);
|
|
966
955
|
});
|
|
967
956
|
}
|
|
968
|
-
}).finally((
|
|
957
|
+
}).finally(() => {
|
|
969
958
|
removeTempListeners(this, 'delete');
|
|
970
959
|
this._resetEventFlags();
|
|
971
|
-
return rsp;
|
|
972
960
|
});
|
|
973
961
|
}
|
|
974
962
|
|
|
@@ -980,7 +968,7 @@ class SftpClient {
|
|
|
980
968
|
* @param {string} fromPath - path to the file to be renamed.
|
|
981
969
|
* @param {string} toPath - path to the new name.
|
|
982
970
|
*
|
|
983
|
-
* @return {Promise}
|
|
971
|
+
* @return {Promise<String>}
|
|
984
972
|
*
|
|
985
973
|
*/
|
|
986
974
|
rename(fromPath, toPath) {
|
|
@@ -1002,10 +990,9 @@ class SftpClient {
|
|
|
1002
990
|
resolve(`Successfully renamed ${fromPath} to ${toPath}`);
|
|
1003
991
|
});
|
|
1004
992
|
}
|
|
1005
|
-
}).finally((
|
|
993
|
+
}).finally(() => {
|
|
1006
994
|
removeTempListeners(this, 'rename');
|
|
1007
995
|
this._resetEventFlags();
|
|
1008
|
-
return rsp;
|
|
1009
996
|
});
|
|
1010
997
|
}
|
|
1011
998
|
|
|
@@ -1018,7 +1005,7 @@ class SftpClient {
|
|
|
1018
1005
|
* @param {string} fromPath - path to the file to be renamed.
|
|
1019
1006
|
* @param {string} toPath - path the new name.
|
|
1020
1007
|
*
|
|
1021
|
-
* @return {Promise}
|
|
1008
|
+
* @return {Promise<String>}
|
|
1022
1009
|
*
|
|
1023
1010
|
*/
|
|
1024
1011
|
posixRename(fromPath, toPath) {
|
|
@@ -1040,10 +1027,9 @@ class SftpClient {
|
|
|
1040
1027
|
resolve(`Successful POSIX rename ${fromPath} to ${toPath}`);
|
|
1041
1028
|
});
|
|
1042
1029
|
}
|
|
1043
|
-
}).finally((
|
|
1030
|
+
}).finally(() => {
|
|
1044
1031
|
removeTempListeners(this, 'posixRename');
|
|
1045
1032
|
this._resetEventFlags();
|
|
1046
|
-
return rsp;
|
|
1047
1033
|
});
|
|
1048
1034
|
}
|
|
1049
1035
|
|
|
@@ -1055,7 +1041,7 @@ class SftpClient {
|
|
|
1055
1041
|
* @param {string} remotePath - path to the remote target object.
|
|
1056
1042
|
* @param {number | string} mode - the new octal mode to set
|
|
1057
1043
|
*
|
|
1058
|
-
* @return {Promise}
|
|
1044
|
+
* @return {Promise<String>}
|
|
1059
1045
|
*/
|
|
1060
1046
|
chmod(remotePath, mode) {
|
|
1061
1047
|
return new Promise((resolve, reject) => {
|
|
@@ -1067,10 +1053,9 @@ class SftpClient {
|
|
|
1067
1053
|
}
|
|
1068
1054
|
resolve('Successfully change file mode');
|
|
1069
1055
|
});
|
|
1070
|
-
}).finally((
|
|
1056
|
+
}).finally(() => {
|
|
1071
1057
|
removeTempListeners(this, 'chmod');
|
|
1072
1058
|
this._resetEventFlags();
|
|
1073
|
-
return rsp;
|
|
1074
1059
|
});
|
|
1075
1060
|
}
|
|
1076
1061
|
|
|
@@ -1084,8 +1069,7 @@ class SftpClient {
|
|
|
1084
1069
|
* @param {String} dstDir - remote destination directory
|
|
1085
1070
|
* @param {RegExp} filter - (Optional) a regular expression used to select
|
|
1086
1071
|
* files and directories to upload
|
|
1087
|
-
* @returns {String}
|
|
1088
|
-
* @throws {Error}
|
|
1072
|
+
* @returns {Promise<String>}
|
|
1089
1073
|
*/
|
|
1090
1074
|
async uploadDir(srcDir, dstDir, filter = /.*/) {
|
|
1091
1075
|
try {
|
|
@@ -1130,11 +1114,7 @@ class SftpClient {
|
|
|
1130
1114
|
return `${srcDir} uploaded to ${dstDir}`;
|
|
1131
1115
|
} catch (err) {
|
|
1132
1116
|
this._resetEventFlags();
|
|
1133
|
-
|
|
1134
|
-
throw err;
|
|
1135
|
-
} else {
|
|
1136
|
-
throw fmtError(err, 'uploadDir');
|
|
1137
|
-
}
|
|
1117
|
+
throw err.custom ? err : fmtError(err, 'uploadDir');
|
|
1138
1118
|
}
|
|
1139
1119
|
}
|
|
1140
1120
|
|
|
@@ -1148,8 +1128,7 @@ class SftpClient {
|
|
|
1148
1128
|
* @param {String} dstDir - local destination directory
|
|
1149
1129
|
* @param {RegExp} filter - (Optional) a regular expression used to select
|
|
1150
1130
|
* files and directories to upload
|
|
1151
|
-
* @returns {Promise}
|
|
1152
|
-
* @throws {Error}
|
|
1131
|
+
* @returns {Promise<String>}
|
|
1153
1132
|
*/
|
|
1154
1133
|
async downloadDir(srcDir, dstDir, filter = /.*/) {
|
|
1155
1134
|
try {
|
|
@@ -1191,11 +1170,7 @@ class SftpClient {
|
|
|
1191
1170
|
return `${srcDir} downloaded to ${dstDir}`;
|
|
1192
1171
|
} catch (err) {
|
|
1193
1172
|
this._resetEventFlags();
|
|
1194
|
-
|
|
1195
|
-
throw err;
|
|
1196
|
-
} else {
|
|
1197
|
-
throw fmtError(err, 'downloadDir', err.code);
|
|
1198
|
-
}
|
|
1173
|
+
throw err.custom ? err : fmtError(err, 'downloadDir', err.code);
|
|
1199
1174
|
}
|
|
1200
1175
|
}
|
|
1201
1176
|
|
|
@@ -1204,6 +1179,7 @@ class SftpClient {
|
|
|
1204
1179
|
*
|
|
1205
1180
|
* End the SFTP connection
|
|
1206
1181
|
*
|
|
1182
|
+
* @returns {Promise<Boolean>}
|
|
1207
1183
|
*/
|
|
1208
1184
|
end() {
|
|
1209
1185
|
let endCloseHandler;
|
|
@@ -1220,13 +1196,12 @@ class SftpClient {
|
|
|
1220
1196
|
this.debugMsg('end: Have connection - calling end()');
|
|
1221
1197
|
this.client.end();
|
|
1222
1198
|
}
|
|
1223
|
-
}).finally((
|
|
1199
|
+
}).finally(() => {
|
|
1224
1200
|
this.debugMsg('end: finally clause fired');
|
|
1225
1201
|
removeTempListeners(this, 'end');
|
|
1226
1202
|
this.removeListener('close', endCloseHandler);
|
|
1227
1203
|
this.endCalled = false;
|
|
1228
1204
|
this._resetEventFlags();
|
|
1229
|
-
return resp;
|
|
1230
1205
|
});
|
|
1231
1206
|
}
|
|
1232
1207
|
}
|
package/src/utils.js
CHANGED
|
@@ -99,7 +99,7 @@ function endListener(client, name, reject) {
|
|
|
99
99
|
client.debugMsg(`${name}: handling end event with reject'`);
|
|
100
100
|
reject(fmtError('Unexpected end event raised', name));
|
|
101
101
|
} else {
|
|
102
|
-
client.
|
|
102
|
+
client.debugMsg(`${name}: handling end event with throw`);
|
|
103
103
|
throw fmtError('Unexpected end event raised', name);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -157,26 +157,16 @@ function removeTempListeners(obj, name) {
|
|
|
157
157
|
* @returns {string | boolean} returns a string for object type if it exists, false otherwise
|
|
158
158
|
*/
|
|
159
159
|
function localExists(filePath) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
} else if (stats.isFile()) {
|
|
169
|
-
return '-';
|
|
170
|
-
} else {
|
|
171
|
-
throw fmtError(
|
|
172
|
-
`Bad path: ${filePath}: unsupported file type`,
|
|
173
|
-
'localExists',
|
|
174
|
-
errorCode.badPath
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
} catch (err) {
|
|
160
|
+
const stats = fs.statSync(filePath, { throwIfNoEntry: false });
|
|
161
|
+
if (!stats) {
|
|
162
|
+
return false;
|
|
163
|
+
} else if (stats.isDirectory()) {
|
|
164
|
+
return 'd';
|
|
165
|
+
} else if (stats.isFile()) {
|
|
166
|
+
return '-';
|
|
167
|
+
} else {
|
|
178
168
|
throw fmtError(
|
|
179
|
-
`Bad path: ${filePath}:
|
|
169
|
+
`Bad path: ${filePath}: target must be a file or directory`,
|
|
180
170
|
'localExists',
|
|
181
171
|
errorCode.badPath
|
|
182
172
|
);
|
|
@@ -213,23 +203,33 @@ function haveLocalAccess(filePath, mode = 'r') {
|
|
|
213
203
|
code: 0,
|
|
214
204
|
};
|
|
215
205
|
} catch (err) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
206
|
+
switch (err.errno) {
|
|
207
|
+
case -2:
|
|
208
|
+
return {
|
|
209
|
+
status: false,
|
|
210
|
+
type: null,
|
|
211
|
+
details: 'not exist',
|
|
212
|
+
code: -2,
|
|
213
|
+
};
|
|
214
|
+
case -13:
|
|
215
|
+
return {
|
|
216
|
+
status: false,
|
|
217
|
+
type: localExists(filePath),
|
|
218
|
+
details: 'permission denied',
|
|
219
|
+
code: -13,
|
|
220
|
+
};
|
|
221
|
+
case -20:
|
|
222
|
+
return {
|
|
223
|
+
status: false,
|
|
224
|
+
type: null,
|
|
225
|
+
details: 'parent not a directory',
|
|
226
|
+
};
|
|
227
|
+
default:
|
|
228
|
+
return {
|
|
229
|
+
status: false,
|
|
230
|
+
type: null,
|
|
231
|
+
details: err.message,
|
|
232
|
+
};
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
}
|
|
@@ -246,14 +246,29 @@ function haveLocalAccess(filePath, mode = 'r') {
|
|
|
246
246
|
function haveLocalCreate(filePath) {
|
|
247
247
|
const { status, details, type } = haveLocalAccess(filePath, 'w');
|
|
248
248
|
if (!status && details === 'permission denied') {
|
|
249
|
-
throw new Error(`Bad path: ${filePath}: permission denied`);
|
|
249
|
+
//throw new Error(`Bad path: ${filePath}: permission denied`);
|
|
250
|
+
return {
|
|
251
|
+
status,
|
|
252
|
+
details,
|
|
253
|
+
type,
|
|
254
|
+
};
|
|
250
255
|
} else if (!status) {
|
|
251
256
|
const dirPath = path.dirname(filePath);
|
|
252
257
|
const localCheck = haveLocalAccess(dirPath, 'w');
|
|
253
258
|
if (localCheck.status && localCheck.type !== 'd') {
|
|
254
|
-
throw new Error(`Bad path: ${dirPath}: not a directory`);
|
|
259
|
+
//throw new Error(`Bad path: ${dirPath}: not a directory`);
|
|
260
|
+
return {
|
|
261
|
+
status: false,
|
|
262
|
+
details: `${dirPath}: not a directory`,
|
|
263
|
+
type: null,
|
|
264
|
+
};
|
|
255
265
|
} else if (!localCheck.status) {
|
|
256
|
-
throw new Error(`Bad path: ${dirPath}: ${localCheck.details}`);
|
|
266
|
+
//throw new Error(`Bad path: ${dirPath}: ${localCheck.details}`);
|
|
267
|
+
return {
|
|
268
|
+
status: localCheck.status,
|
|
269
|
+
details: `${dirPath}: ${localCheck.details}`,
|
|
270
|
+
type: null,
|
|
271
|
+
};
|
|
257
272
|
} else {
|
|
258
273
|
return {
|
|
259
274
|
status: true,
|
|
@@ -270,10 +285,10 @@ async function normalizeRemotePath(client, aPath) {
|
|
|
270
285
|
try {
|
|
271
286
|
if (aPath.startsWith('..')) {
|
|
272
287
|
let root = await client.realPath('..');
|
|
273
|
-
return root + client.remotePathSep + aPath.
|
|
288
|
+
return root + client.remotePathSep + aPath.slice(3);
|
|
274
289
|
} else if (aPath.startsWith('.')) {
|
|
275
290
|
let root = await client.realPath('.');
|
|
276
|
-
return root + client.remotePathSep + aPath.
|
|
291
|
+
return root + client.remotePathSep + aPath.slice(2);
|
|
277
292
|
}
|
|
278
293
|
return aPath;
|
|
279
294
|
} catch (err) {
|
package/CHANGELOG.org
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
* Change Logging
|
|
2
|
-
|
|
3
|
-
** v7.0.1
|
|
4
|
-
- New version based on ssh2 version 1.2.0
|
|
5
|
-
- Added some additional path checks to deal with poor error messages and
|
|
6
|
-
unreliable behaviour when connecting to an sftp server on Windows based
|
|
7
|
-
platforms.
|
|
8
|
-
- Added additional tests for local resources (files/directories) to provide
|
|
9
|
-
better error handling and informative error messages.
|
|
10
|
-
- Updated README to provide more info on managing exceptions
|
|
11
|
-
- Changed behaviour of global error handler. This error handler now just logs
|
|
12
|
-
the error to the console and sets the client.sftp property to undefined to
|
|
13
|
-
ensure any subsequent calls to client methods fail immediately with an
|
|
14
|
-
error indicating there is no active connection.
|
|
15
|
-
|
|
16
|
-
** V7.0.0
|
|
17
|
-
- New version based on new SSH2 version 1.1.0.
|
|
18
|
-
- Expand option handling for get() and put() methods *Breaking Change*
|
|
19
|
-
- Re-factored the retry code in the connect() method
|
|
20
|
-
- Improve error reporting for adding/removing listeners
|
|
21
|
-
- Extend localExists() method to also verify read or write access
|
|
22
|
-
|
|
23
|
-
** V6.0.1
|
|
24
|
-
- Fix issue with connect retry not releasing 'ready' listeners
|
|
25
|
-
- Add finally clauses to all promises to ensure temporary listeners are deleted
|
|
26
|
-
- Add nyc module to report on code test coverage
|
|
27
|
-
- Add additional utils tests to increase test coverage
|
|
28
|
-
- Removed some dead code and unused utility functions to reduce download size
|
|
29
|
-
- Cleanup tests and reduce inter-test dependencies
|
|
30
|
-
|
|
31
|
-
** V6.0.0.0
|
|
32
|
-
- Update connection retry code to use the promise-retry module instead of
|
|
33
|
-
plain rety module
|
|
34
|
-
- Added optional filter argument for uploadDir/downlDir to select which files
|
|
35
|
-
and directories are included
|
|
36
|
-
- Added an optional boolean argument to delete to turn off raising an error
|
|
37
|
-
when delete target does not exists
|
|
38
|
-
- Reduced/simplified argument verification code to reduce package size and
|
|
39
|
-
increase performance
|
|
40
|
-
- Refactored handling of events and add default close and error listeners to
|
|
41
|
-
catch connections closed abruptly without an error being raised.
|
|
42
|
-
|
|
43
|
-
** V5.3.2
|
|
44
|
-
- Minor README typo fixes
|
|
45
|
-
- Fix error in local file path checks (#294)
|
|
46
|
-
|
|
47
|
-
** V5.3.1
|
|
48
|
-
- Fix bug in handling of relative local paths
|
|
49
|
-
- Change handling of stream closures in ~get()~ and ~put()~ methods
|
|
50
|
-
|
|
51
|
-
** v5.3.0
|
|
52
|
-
- Refine event handler management
|
|
53
|
-
- Fix path processing for win32 based sftp servers
|
|
54
|
-
- Update documentation
|
|
55
|
-
** v5.2.2
|
|
56
|
-
- Bug fix release. Add error code 4 check to stat() method.
|
|
57
|
-
- bump Mocha version for tests
|
|
58
|
-
|
|
59
|
-
** v5.2.1
|
|
60
|
-
- Move some dependencies into dev-Dependencies
|
|
61
|
-
** v5.2.0
|
|
62
|
-
- Add new method posixRename() which uses the openSSH POSIX rename extension.
|
|
63
|
-
** v5.1.3
|
|
64
|
-
- Fix bug when writing to root directory and failure due to not being able to
|
|
65
|
-
determine parent
|
|
66
|
-
- Refactor some tests to eliminate need to have artificial delays between
|
|
67
|
-
tests
|
|
68
|
-
- Bumped some dependency versions to latest version
|
|
69
|
-
** v5.1.2
|
|
70
|
-
- Added back global close handler
|
|
71
|
-
- Added dumpListeners() method
|
|
72
|
-
|
|
73
|
-
** v5.1.1
|
|
74
|
-
- Added separate close handlers to each method.
|
|
75
|
-
- Added missing return statement in connect method
|
|
76
|
-
- Added additional troubleshooting documentation for
|
|
77
|
-
common errors.
|
|
78
|
-
|
|
79
|
-
** v5.1.0
|
|
80
|
-
- Fix bug in checkRemotePath() relating to handling of badly
|
|
81
|
-
specified paths (issue #213)
|
|
82
|
-
- Added additional debugging support
|
|
83
|
-
- Add missing test for valid connection in end() method.
|
|
84
|
-
- Bump ssh2 version to v0.8.8
|
|
85
|
-
|
|
86
|
-
** v5.0.2
|
|
87
|
-
- Fix bugs related to win32 platform and local tests for valid directories
|
|
88
|
-
- Fix problem with parsing of file paths
|
|
89
|
-
|
|
90
|
-
** v5.0.1
|
|
91
|
-
- Turn down error checking to be less stringent and handle situations
|
|
92
|
-
where user does not have read permission on parent directory.
|
|
93
|
-
|
|
94
|
-
** v5.0.0
|
|
95
|
-
- Added two new methods ~uploadDir()~ and ~downloadDir()~
|
|
96
|
-
- Removed deprecated ~auxList()~ method
|
|
97
|
-
- Improved error message consistency
|
|
98
|
-
- Added additional error checking to enable more accurate and useful error
|
|
99
|
-
messages.
|
|
100
|
-
- Added default error handler to deal with event errors which fire outside of
|
|
101
|
-
active SftpClient methods (i.e. connection unexpectedly reset by remote host).
|
|
102
|
-
- Modified event handlers to ensure that only event handlers added by the
|
|
103
|
-
module are removed by the module (users now responsible for removing any
|
|
104
|
-
custom event handlers they add).
|
|
105
|
-
- Module error handlers added using ~prependListener~ to ensure they are
|
|
106
|
-
called before any additional custom handlers added by client code.
|
|
107
|
-
- Any error events fired during an ~end()~ call are now ignored.
|
|
108
|
-
|
|
109
|
-
** v4.3.1
|
|
110
|
-
- Updated end() method to resolve once close event fires
|
|
111
|
-
- Added errorListener to error event in each promise to catch error events
|
|
112
|
-
and reject the promise. This should resolve the issue of some error events
|
|
113
|
-
causing uncaughtException erros and causing the process to exit.
|
|
114
|
-
|
|
115
|
-
** v4.3.0
|
|
116
|
-
- Ensure errors include an err.code property and pass through the error code
|
|
117
|
-
from the originating error
|
|
118
|
-
- Change tests for error type to use ~error.code~ instead of matching on
|
|
119
|
-
~error.message~.
|
|
120
|
-
|
|
121
|
-
** v4.2.4
|
|
122
|
-
- Bumped ssh2 to v0.8.6
|
|
123
|
-
- Added exists() usage example to examples directory
|
|
124
|
-
- Clarify documentation on get() method
|
|
125
|
-
** v4.2.3
|
|
126
|
-
- Fix bug in ~exist()~ where tests on root directory returned false
|
|
127
|
-
- Minor documentation fixes
|
|
128
|
-
- Clean up mkdir example
|
|
129
|
-
|
|
130
|
-
** v4.2.2
|
|
131
|
-
- Minor documentation fixes
|
|
132
|
-
- Added additional examples in the ~example~ directory
|
|
133
|
-
|
|
134
|
-
** v4.2.1
|
|
135
|
-
- Remove default close listener. changes in ssh2 API removed the utility of a
|
|
136
|
-
default close listener
|
|
137
|
-
- Fix path handling. Under mixed environments (where client platform and
|
|
138
|
-
server platform were different i.e. one windows the other unix), path
|
|
139
|
-
handling was broken due tot he use of path.join().
|
|
140
|
-
- Ensure error messages include path details. Instead of errors such as "No
|
|
141
|
-
such file" now report "No such file /path/to/missing/file" to help with
|
|
142
|
-
debugging
|
|
143
|
-
|
|
144
|
-
** v4.2.0
|
|
145
|
-
- Work-around for SSH2 =end= event bug
|
|
146
|
-
- Added ability to set client name in constructor method
|
|
147
|
-
- Added additional error checking to prevent ~connect()~ being called on
|
|
148
|
-
already connected client
|
|
149
|
-
- Added additional examples in =example= directory
|
|
150
|
-
|
|
151
|
-
** v4.1.0
|
|
152
|
-
- move ~end()~ call to resolve into close hook
|
|
153
|
-
- Prevent ~put()~ and ~get()~ from creating empty files in destination when
|
|
154
|
-
unable to read source
|
|
155
|
-
- Expand tests for operations when lacking required permissions
|
|
156
|
-
- Add additional data checks for ~append()~
|
|
157
|
-
- Verify file exists
|
|
158
|
-
- Verify file is writeable
|
|
159
|
-
- Verify file is a regular file
|
|
160
|
-
- Fix handling of relative paths
|
|
161
|
-
- Add ~realPath()~ method
|
|
162
|
-
- Add ~cwd()~ method
|
|
163
|
-
|
|
164
|
-
** v4.0.4
|
|
165
|
-
- Minor documentation fix
|
|
166
|
-
- Fix return value from ~get()~
|
|
167
|
-
|
|
168
|
-
** v4.0.3
|
|
169
|
-
- Fix bug in mkdir() relating to handling of relative paths
|
|
170
|
-
- Modify exists() to always return 'd' if path is '.'
|
|
171
|
-
|
|
172
|
-
** v4.0.2
|
|
173
|
-
- Fix some minor packaging issues
|
|
174
|
-
|
|
175
|
-
** v4.0.0
|
|
176
|
-
- Remove support for node < 8.x
|
|
177
|
-
- Fix connection retry feature
|
|
178
|
-
- sftp connection object set to null when 'end' signal is raised
|
|
179
|
-
- Removed 'connectMethod' argument from connect method.
|
|
180
|
-
- Refined adding/removing of listeners in connect() and end() methods to enable
|
|
181
|
-
errors to be adequately caught and reported.
|
|
182
|
-
- Deprecate auxList() and add pattern/regexp filter option to list()
|
|
183
|
-
- Refactored handling of event signals to provide better feedback to clients
|
|
184
|
-
- Removed pointless 'permissions' property from objects returned by ~stat()~
|
|
185
|
-
(same as mode property). Added additional properties describing the type of
|
|
186
|
-
object.
|
|
187
|
-
- Added the ~removeListener()~ method to compliment the existing ~on()~ method.
|
|
188
|
-
|
|
189
|
-
** Older Versions
|
|
190
|
-
*** v2.5.2
|
|
191
|
-
- Repository transferred to theophilusx
|
|
192
|
-
- Fix error in package.json pointing to wrong repository
|
|
193
|
-
|
|
194
|
-
*** v2.5.1
|
|
195
|
-
- Apply 4 pull requests to address minor issues prior to transfer
|
|
196
|
-
|
|
197
|
-
*** v2.5.0
|
|
198
|
-
- ???
|
|
199
|
-
|
|
200
|
-
*** v2.4.3
|
|
201
|
-
- merge #108, #110
|
|
202
|
-
- fix connect promise if connection ends
|
|
203
|
-
|
|
204
|
-
*** v2.4.2
|
|
205
|
-
- merge #105
|
|
206
|
-
- fix windows path
|
|
207
|
-
|
|
208
|
-
*** v2.4.1
|
|
209
|
-
- merge pr #99, #100
|
|
210
|
-
- bug fix
|
|
211
|
-
|
|
212
|
-
*** v2.4.0
|
|
213
|
-
- Requires node.js v7.5.0 or above.
|
|
214
|
-
- merge pr #97, thanks for @theophilusx
|
|
215
|
-
- Remove emitter.maxListener warnings
|
|
216
|
-
- Upgraded ssh2 dependency from 0.5.5 to 0.6.1
|
|
217
|
-
- Enhanced error messages to provide more context and to be more consistent
|
|
218
|
-
- re-factored test
|
|
219
|
-
- Added new 'exists' method and re-factored mkdir/rmdir
|
|
220
|
-
|
|
221
|
-
*** v2.3.0
|
|
222
|
-
- add: ~stat~ method
|
|
223
|
-
- add ~fastGet~ and ~fastPut~ method.
|
|
224
|
-
- fix: ~mkdir~ file exists decision logic
|
|
225
|
-
|
|
226
|
-
*** v3.0.0 -- deprecate this version
|
|
227
|
-
- change: ~sftp.get~ will return chunk not stream anymore
|
|
228
|
-
- fix: get readable not emitting data events in node 10.0.0
|
|
229
|
-
|
|
230
|
-
*** v2.1.1
|
|
231
|
-
- add: event listener. [[https://github.com/jyu213/ssh2-sftp-client#Event][doc]]
|
|
232
|
-
- add: ~get~ or ~put~ method add extra options [[https://github.com/jyu213/ssh2-sftp-client/pull/52][pr#52]]
|
|
233
|
-
|
|
234
|
-
*** v2.0.1
|
|
235
|
-
- add: ~chmod~ method [[https://github.com/jyu213/ssh2-sftp-client/pull/33][pr#33]]
|
|
236
|
-
- update: upgrade ssh2 to V0.5.0 [[https://github.com/jyu213/ssh2-sftp-client/pull/30][pr#30]]
|
|
237
|
-
- fix: get method stream error reject unwork [[https://github.com/jyu213/ssh2-sftp-client/issues/22][#22]]
|
|
238
|
-
- fix: return Error object on promise rejection [[https://github.com/jyu213/ssh2-sftp-client/pull/20][pr#20]]
|
|
239
|
-
|
|
240
|
-
*** v1.1.0
|
|
241
|
-
- fix: add encoding control support for binary stream
|
|
242
|
-
|
|
243
|
-
*** v1.0.5:
|
|
244
|
-
- fix: multi image upload
|
|
245
|
-
- change: remove ~this.client.sftp~ to ~connect~ function
|