@appium/support 2.55.4 → 2.57.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/build/lib/env.d.ts +54 -0
- package/build/lib/env.d.ts.map +1 -0
- package/build/lib/env.js +102 -0
- package/build/lib/fs.d.ts +221 -0
- package/build/lib/fs.d.ts.map +1 -0
- package/build/lib/fs.js +74 -56
- package/build/lib/image-util.d.ts +56 -0
- package/build/lib/image-util.d.ts.map +1 -0
- package/build/lib/image-util.js +3 -6
- package/build/lib/index.d.ts +38 -0
- package/build/lib/index.d.ts.map +1 -0
- package/build/lib/index.js +36 -18
- package/build/lib/log-internal.d.ts +74 -0
- package/build/lib/log-internal.d.ts.map +1 -0
- package/build/lib/log-internal.js +11 -21
- package/build/lib/logger.d.ts +3 -0
- package/build/lib/logger.d.ts.map +1 -0
- package/build/lib/logger.js +2 -4
- package/build/lib/logging.d.ts +45 -0
- package/build/lib/logging.d.ts.map +1 -0
- package/build/lib/logging.js +12 -16
- package/build/lib/mjpeg.d.ts +65 -0
- package/build/lib/mjpeg.d.ts.map +1 -0
- package/build/lib/mjpeg.js +12 -7
- package/build/lib/mkdirp.d.ts +3 -0
- package/build/lib/mkdirp.d.ts.map +1 -0
- package/build/lib/mkdirp.js +7 -11
- package/build/lib/net.d.ts +95 -0
- package/build/lib/net.d.ts.map +1 -0
- package/build/lib/net.js +42 -26
- package/build/lib/node.d.ts +26 -0
- package/build/lib/node.d.ts.map +1 -0
- package/build/lib/node.js +102 -3
- package/build/lib/npm.d.ts +123 -0
- package/build/lib/npm.d.ts.map +1 -0
- package/build/lib/npm.js +217 -0
- package/build/lib/plist.d.ts +43 -0
- package/build/lib/plist.d.ts.map +1 -0
- package/build/lib/plist.js +2 -4
- package/build/lib/process.d.ts +3 -0
- package/build/lib/process.d.ts.map +1 -0
- package/build/lib/process.js +2 -4
- package/build/lib/system.d.ts +7 -0
- package/build/lib/system.d.ts.map +1 -0
- package/build/lib/system.js +2 -4
- package/build/lib/tempdir.d.ts +63 -0
- package/build/lib/tempdir.d.ts.map +1 -0
- package/build/lib/tempdir.js +4 -9
- package/build/lib/timing.d.ts +46 -0
- package/build/lib/timing.d.ts.map +1 -0
- package/build/lib/timing.js +2 -4
- package/build/lib/util.d.ts +183 -0
- package/build/lib/util.d.ts.map +1 -0
- package/build/lib/util.js +9 -15
- package/build/lib/zip.d.ts +180 -0
- package/build/lib/zip.d.ts.map +1 -0
- package/build/lib/zip.js +9 -9
- package/build/tsconfig.tsbuildinfo +1 -0
- package/lib/env.js +162 -0
- package/lib/fs.js +198 -69
- package/lib/image-util.js +23 -7
- package/lib/index.js +6 -6
- package/lib/log-internal.js +31 -38
- package/lib/logging.js +41 -17
- package/lib/mjpeg.js +14 -5
- package/lib/mkdirp.js +3 -6
- package/lib/net.js +116 -60
- package/lib/node.js +107 -4
- package/lib/npm.js +278 -0
- package/lib/plist.js +3 -1
- package/lib/tempdir.js +14 -13
- package/lib/util.js +36 -33
- package/lib/zip.js +31 -21
- package/package.json +31 -13
- package/build/test/assets/sample_binary.plist +0 -0
- package/build/test/assets/sample_text.plist +0 -28
- package/build/test/fs-specs.js +0 -264
- package/build/test/helpers.js +0 -35
- package/build/test/image-util-e2e-specs.js +0 -78
- package/build/test/index-specs.js +0 -49
- package/build/test/log-internals-specs.js +0 -97
- package/build/test/logger/helpers.js +0 -71
- package/build/test/logger/logger-force-specs.js +0 -41
- package/build/test/logger/logger-normal-specs.js +0 -113
- package/build/test/logger/logger-test-specs.js +0 -40
- package/build/test/mjpeg-e2e-specs.js +0 -96
- package/build/test/net-e2e-specs.js +0 -32
- package/build/test/node-e2e-specs.js +0 -22
- package/build/test/plist-specs.js +0 -54
- package/build/test/process-specs.js +0 -104
- package/build/test/system-specs.js +0 -136
- package/build/test/tempdir-specs.js +0 -86
- package/build/test/timing-specs.js +0 -125
- package/build/test/util-e2e-specs.js +0 -136
- package/build/test/util-specs.js +0 -537
- package/build/test/zip-e2e-specs.js +0 -233
package/lib/logging.js
CHANGED
|
@@ -22,10 +22,14 @@ function patchLogger (logger) {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
*
|
|
27
|
+
* @returns {[npmlog.Logger, boolean]}
|
|
28
|
+
*/
|
|
25
29
|
function _getLogger () {
|
|
26
30
|
// check if the user set the `_TESTING` or `_FORCE_LOGS` flag
|
|
27
|
-
const testingMode =
|
|
28
|
-
const forceLogMode =
|
|
31
|
+
const testingMode = process.env._TESTING === '1';
|
|
32
|
+
const forceLogMode = process.env._FORCE_LOGS === '1';
|
|
29
33
|
|
|
30
34
|
// if is possible that there is a logger instance that is already around,
|
|
31
35
|
// in which case we want t o use that
|
|
@@ -44,19 +48,32 @@ function _getLogger () {
|
|
|
44
48
|
return [logger, usingGlobalLog];
|
|
45
49
|
}
|
|
46
50
|
|
|
51
|
+
/**
|
|
52
|
+
* @param {Prefix?} prefix
|
|
53
|
+
* @param {boolean} logTimestamp whether to include timestamps into log prefixes
|
|
54
|
+
* @returns {string}
|
|
55
|
+
*/
|
|
47
56
|
function getActualPrefix (prefix, logTimestamp = false) {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
return actualPrefix;
|
|
57
|
+
const result = (_.isFunction(prefix) ? prefix() : prefix) ?? '';
|
|
58
|
+
return logTimestamp
|
|
59
|
+
? `[${moment().format(PREFIX_TIMESTAMP_FORMAT)}] ${result}`
|
|
60
|
+
: result;
|
|
53
61
|
}
|
|
54
62
|
|
|
63
|
+
/**
|
|
64
|
+
*
|
|
65
|
+
* @param {Prefix?} prefix
|
|
66
|
+
* @returns {AppiumLogger}
|
|
67
|
+
*/
|
|
55
68
|
function getLogger (prefix = null) {
|
|
56
69
|
let [logger, usingGlobalLog] = _getLogger();
|
|
57
70
|
|
|
58
71
|
// wrap the logger so that we can catch and modify any logging
|
|
59
|
-
let wrappedLogger = {
|
|
72
|
+
let wrappedLogger = {
|
|
73
|
+
unwrap: () => logger,
|
|
74
|
+
levels: NPM_LEVELS,
|
|
75
|
+
prefix,
|
|
76
|
+
};
|
|
60
77
|
|
|
61
78
|
// allow access to the level of the underlying logger
|
|
62
79
|
Object.defineProperty(wrappedLogger, 'level', {
|
|
@@ -70,12 +87,12 @@ function getLogger (prefix = null) {
|
|
|
70
87
|
configurable: true
|
|
71
88
|
});
|
|
72
89
|
|
|
73
|
-
const logTimestamp =
|
|
90
|
+
const logTimestamp = process.env._LOG_TIMESTAMP === '1';
|
|
74
91
|
|
|
75
92
|
// add all the levels from `npmlog`, and map to the underlying logger
|
|
76
93
|
for (const level of NPM_LEVELS) {
|
|
77
94
|
wrappedLogger[level] = function (...args) {
|
|
78
|
-
const actualPrefix = getActualPrefix(prefix, logTimestamp);
|
|
95
|
+
const actualPrefix = getActualPrefix(this.prefix, logTimestamp);
|
|
79
96
|
for (const arg of args) {
|
|
80
97
|
const out = (_.isError(arg) && arg.stack) ? arg.stack : `${arg}`;
|
|
81
98
|
for (const line of out.split('\n')) {
|
|
@@ -99,15 +116,14 @@ function getLogger (prefix = null) {
|
|
|
99
116
|
// package set the log level
|
|
100
117
|
wrappedLogger.level = 'verbose';
|
|
101
118
|
}
|
|
102
|
-
|
|
103
|
-
return wrappedLogger;
|
|
119
|
+
return /** @type {AppiumLogger} */(wrappedLogger);
|
|
104
120
|
}
|
|
105
121
|
|
|
106
122
|
/**
|
|
107
|
-
* @typedef
|
|
108
|
-
* @property {
|
|
123
|
+
* @typedef LoadResult
|
|
124
|
+
* @property {string[]} issues The list of rule parsing issues (one item per rule).
|
|
109
125
|
* Rules with issues are skipped. An empty list is returned if no parsing issues exist.
|
|
110
|
-
* @property {
|
|
126
|
+
* @property {import('./log-internal').SecureValuePreprocessingRule[]} rules The list of successfully loaded
|
|
111
127
|
* replacement rules. The list could be empty if no rules were loaded.
|
|
112
128
|
*/
|
|
113
129
|
|
|
@@ -117,12 +133,12 @@ function getLogger (prefix = null) {
|
|
|
117
133
|
* appear in Appium logs.
|
|
118
134
|
* Each call to this method replaces the previously loaded rules if any existed.
|
|
119
135
|
*
|
|
120
|
-
* @param {string|string[]|Rule[]} rulesJsonPath The full path to the JSON file containing
|
|
136
|
+
* @param {string|string[]|import('./log-internal').Rule[]} rulesJsonPath The full path to the JSON file containing
|
|
121
137
|
* the replacement rules. Each rule could either be a string to be replaced
|
|
122
138
|
* or an object with predefined properties. See the `Rule` type definition in
|
|
123
139
|
* `log-internals.js` to get more details on its format.
|
|
124
140
|
* @throws {Error} If the given file cannot be loaded
|
|
125
|
-
* @returns {LoadResult}
|
|
141
|
+
* @returns {Promise<LoadResult>}
|
|
126
142
|
*/
|
|
127
143
|
async function loadSecureValuesPreprocessingRules (rulesJsonPath) {
|
|
128
144
|
const issues = await SECURE_VALUES_PREPROCESSOR.loadRules(rulesJsonPath);
|
|
@@ -137,3 +153,11 @@ const log = getLogger();
|
|
|
137
153
|
|
|
138
154
|
export { log, patchLogger, getLogger, loadSecureValuesPreprocessingRules };
|
|
139
155
|
export default log;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @typedef {import('@appium/types').Prefix} Prefix
|
|
159
|
+
*/
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @typedef {import('@appium/types').AppiumLogger} AppiumLogger
|
|
163
|
+
*/
|
package/lib/mjpeg.js
CHANGED
|
@@ -30,6 +30,11 @@ const MJPEG_SERVER_TIMEOUT_MS = 10000;
|
|
|
30
30
|
/** Class which stores the last bit of data streamed into it */
|
|
31
31
|
class MJpegStream extends Writable {
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* @type {number}
|
|
35
|
+
*/
|
|
36
|
+
updateCount = 0;
|
|
37
|
+
|
|
33
38
|
/**
|
|
34
39
|
* Create an MJpegStream
|
|
35
40
|
* @param {string} mJpegUrl - URL of MJPEG-over-HTTP stream
|
|
@@ -52,24 +57,26 @@ class MJpegStream extends Writable {
|
|
|
52
57
|
* or `null` if no image can be parsed
|
|
53
58
|
*/
|
|
54
59
|
get lastChunkBase64 () {
|
|
60
|
+
const lastChunk = /** @type {Buffer} */(this.lastChunk);
|
|
55
61
|
return !_.isEmpty(this.lastChunk) && _.isBuffer(this.lastChunk)
|
|
56
|
-
?
|
|
62
|
+
? lastChunk.toString('base64')
|
|
57
63
|
: null;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
66
|
/**
|
|
61
67
|
* Get the PNG version of the JPEG buffer
|
|
62
68
|
*
|
|
63
|
-
* @returns {
|
|
69
|
+
* @returns {Promise<Buffer?>} PNG image data or `null` if no PNG
|
|
64
70
|
* image can be parsed
|
|
65
71
|
*/
|
|
66
72
|
async lastChunkPNG () {
|
|
67
|
-
|
|
73
|
+
const lastChunk = /** @type {Buffer} */(this.lastChunk);
|
|
74
|
+
if (_.isEmpty(lastChunk) || !_.isBuffer(lastChunk)) {
|
|
68
75
|
return null;
|
|
69
76
|
}
|
|
70
77
|
|
|
71
78
|
try {
|
|
72
|
-
const jpg = await getJimpImage(
|
|
79
|
+
const jpg = await getJimpImage(lastChunk);
|
|
73
80
|
return await jpg.getBuffer(MIME_PNG);
|
|
74
81
|
} catch (e) {
|
|
75
82
|
return null;
|
|
@@ -79,7 +86,7 @@ class MJpegStream extends Writable {
|
|
|
79
86
|
/**
|
|
80
87
|
* Get the base64-encoded version of the PNG
|
|
81
88
|
*
|
|
82
|
-
* @returns {
|
|
89
|
+
* @returns {Promise<string?>} base64-encoded PNG image data
|
|
83
90
|
* or `null` if no image can be parsed
|
|
84
91
|
*/
|
|
85
92
|
async lastChunkPNGBase64 () {
|
|
@@ -185,6 +192,8 @@ class MJpegStream extends Writable {
|
|
|
185
192
|
this.registerStartSuccess();
|
|
186
193
|
this.registerStartSuccess = null;
|
|
187
194
|
}
|
|
195
|
+
|
|
196
|
+
return true;
|
|
188
197
|
}
|
|
189
198
|
}
|
|
190
199
|
|
package/lib/mkdirp.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { fs } from './fs';
|
|
3
2
|
/**
|
|
4
|
-
*
|
|
5
|
-
* of fs.mkdir(dir, {recursive: true});
|
|
3
|
+
* @deprecated Use `fs.mkdirp` instead.
|
|
6
4
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
const { mkdirp } = fs;
|
|
9
6
|
export { mkdirp };
|
package/lib/net.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import fs from './fs';
|
|
3
|
-
import url from 'url';
|
|
4
3
|
import B from 'bluebird';
|
|
5
4
|
import { toReadableSizeString } from './util';
|
|
6
5
|
import log from './logger';
|
|
@@ -11,19 +10,29 @@ import FormData from 'form-data';
|
|
|
11
10
|
|
|
12
11
|
const DEFAULT_TIMEOUT_MS = 4 * 60 * 1000;
|
|
13
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Converts {@linkcode AuthCredentials} to credentials understood by {@linkcode axios}.
|
|
15
|
+
* @param {AuthCredentials | import('axios').AxiosBasicCredentials} auth
|
|
16
|
+
* @returns {import('axios').AxiosBasicCredentials?}
|
|
17
|
+
*/
|
|
14
18
|
function toAxiosAuth (auth) {
|
|
15
19
|
if (!_.isPlainObject(auth)) {
|
|
16
20
|
return null;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
const axiosAuth = {
|
|
20
|
-
username: auth
|
|
21
|
-
password: auth
|
|
24
|
+
username: _.get(auth, 'username', _.get(auth, 'user')),
|
|
25
|
+
password: _.get(auth, 'password', _.get(auth, 'pass')),
|
|
22
26
|
};
|
|
23
27
|
return (axiosAuth.username && axiosAuth.password) ? axiosAuth : null;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
/**
|
|
31
|
+
* @param {NodeJS.ReadableStream} localFileStream
|
|
32
|
+
* @param {URL} parsedUri
|
|
33
|
+
* @param {HttpUploadOptions & NetOptions} [uploadOptions]
|
|
34
|
+
*/
|
|
35
|
+
async function uploadFileToHttp (localFileStream, parsedUri, uploadOptions = /** @type {HttpUploadOptions & NetOptions} */({})) {
|
|
27
36
|
const {
|
|
28
37
|
method = 'POST',
|
|
29
38
|
timeout = DEFAULT_TIMEOUT_MS,
|
|
@@ -34,6 +43,7 @@ async function uploadFileToHttp (localFileStream, parsedUri, uploadOptions = {})
|
|
|
34
43
|
} = uploadOptions;
|
|
35
44
|
const { href } = parsedUri;
|
|
36
45
|
|
|
46
|
+
/** @type {import('axios').AxiosRequestConfig} */
|
|
37
47
|
const requestOpts = {
|
|
38
48
|
url: href,
|
|
39
49
|
method,
|
|
@@ -61,8 +71,10 @@ async function uploadFileToHttp (localFileStream, parsedUri, uploadOptions = {})
|
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
73
|
}
|
|
64
|
-
requestOpts.headers =
|
|
65
|
-
|
|
74
|
+
requestOpts.headers = {
|
|
75
|
+
...(_.isPlainObject(headers) ? headers : {}),
|
|
76
|
+
...form.getHeaders()
|
|
77
|
+
};
|
|
66
78
|
requestOpts.data = form;
|
|
67
79
|
} else {
|
|
68
80
|
if (_.isPlainObject(headers)) {
|
|
@@ -77,11 +89,14 @@ async function uploadFileToHttp (localFileStream, parsedUri, uploadOptions = {})
|
|
|
77
89
|
log.info(`Server response: ${status} ${statusText}`);
|
|
78
90
|
}
|
|
79
91
|
|
|
80
|
-
|
|
92
|
+
/**
|
|
93
|
+
* @param {string | Buffer | NodeJS.ReadableStream} localFileStream
|
|
94
|
+
* @param {URL} parsedUri
|
|
95
|
+
* @param {NotHttpUploadOptions & NetOptions} [uploadOptions]
|
|
96
|
+
*/
|
|
97
|
+
async function uploadFileToFtp (localFileStream, parsedUri, uploadOptions = /** @type {NotHttpUploadOptions & NetOptions} */({})) {
|
|
81
98
|
const {
|
|
82
99
|
auth,
|
|
83
|
-
user,
|
|
84
|
-
pass,
|
|
85
100
|
} = uploadOptions;
|
|
86
101
|
const {
|
|
87
102
|
hostname,
|
|
@@ -92,11 +107,11 @@ async function uploadFileToFtp (localFileStream, parsedUri, uploadOptions = {})
|
|
|
92
107
|
|
|
93
108
|
const ftpOpts = {
|
|
94
109
|
host: hostname,
|
|
95
|
-
port: port
|
|
110
|
+
port: !_.isUndefined(port) ? _.parseInt(port) : 21,
|
|
96
111
|
};
|
|
97
|
-
if (
|
|
98
|
-
ftpOpts.user = auth
|
|
99
|
-
ftpOpts.pass = auth
|
|
112
|
+
if (auth?.user && auth?.pass) {
|
|
113
|
+
ftpOpts.user = auth.user;
|
|
114
|
+
ftpOpts.pass = auth.pass;
|
|
100
115
|
}
|
|
101
116
|
log.debug(`${protocol} upload options: ${JSON.stringify(ftpOpts)}`);
|
|
102
117
|
return await new B((resolve, reject) => {
|
|
@@ -111,42 +126,44 @@ async function uploadFileToFtp (localFileStream, parsedUri, uploadOptions = {})
|
|
|
111
126
|
}
|
|
112
127
|
|
|
113
128
|
/**
|
|
114
|
-
* @
|
|
115
|
-
* @
|
|
116
|
-
* @
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* @typedef {Object} FtpUploadOptions
|
|
121
|
-
* @property {boolean} isMetered [true] - Whether to log the actual upload performance
|
|
122
|
-
* (e.g. timings and speed)
|
|
123
|
-
* @property {AuthCredentials} auth
|
|
129
|
+
* Returns `true` if params are valid for {@linkcode uploadFileToHttp}.
|
|
130
|
+
* @param {any} opts
|
|
131
|
+
* @param {URL} url
|
|
132
|
+
* @returns {opts is HttpUploadOptions & NetOptions}
|
|
124
133
|
*/
|
|
134
|
+
function isHttpUploadOptions (opts, url) {
|
|
135
|
+
try {
|
|
136
|
+
const {protocol} = new URL(url);
|
|
137
|
+
return protocol === 'http:' || protocol === 'https:';
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
125
142
|
|
|
126
143
|
/**
|
|
127
|
-
* @
|
|
128
|
-
* @
|
|
129
|
-
*
|
|
130
|
-
* @
|
|
131
|
-
* @property {AuthCredentials} auth
|
|
132
|
-
* @property {number} timeout [240000] - The actual request timeout in milliseconds
|
|
133
|
-
* @property {Object} headers - Additional request headers mapping
|
|
134
|
-
* @property {?string} fileFieldName [file] - The name of the form field containing the file
|
|
135
|
-
* content to be uploaded. Any falsy value make the request to use non-multipart upload
|
|
136
|
-
* @property {Array<Pair>|Object} formFields - The additional form fields
|
|
137
|
-
* to be included into the upload request. This property is only considered if
|
|
138
|
-
* `fileFieldName` is set
|
|
144
|
+
* Returns `true` if params are valid for {@linkcode uploadFileToFtp}.
|
|
145
|
+
* @param {any} opts
|
|
146
|
+
* @param {URL} url
|
|
147
|
+
* @returns {opts is NotHttpUploadOptions & NetOptions}
|
|
139
148
|
*/
|
|
140
|
-
|
|
149
|
+
function isNotHttpUploadOptions (opts, url) {
|
|
150
|
+
try {
|
|
151
|
+
const {protocol} = new URL(url);
|
|
152
|
+
return protocol === 'ftp:';
|
|
153
|
+
} catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
141
157
|
/**
|
|
142
158
|
* Uploads the given file to a remote location. HTTP(S) and FTP
|
|
143
159
|
* protocols are supported.
|
|
144
160
|
*
|
|
145
161
|
* @param {string} localPath - The path to a file on the local storage.
|
|
146
162
|
* @param {string} remoteUri - The remote URI to upload the file to.
|
|
147
|
-
* @param {
|
|
163
|
+
* @param {(HttpUploadOptions|NotHttpUploadOptions) & NetOptions} [uploadOptions]
|
|
164
|
+
* @returns {Promise<void>}
|
|
148
165
|
*/
|
|
149
|
-
async function uploadFile (localPath, remoteUri, uploadOptions = {}) {
|
|
166
|
+
async function uploadFile (localPath, remoteUri, uploadOptions = /** @type {(HttpUploadOptions|NotHttpUploadOptions) & NetOptions} */({})) {
|
|
150
167
|
if (!await fs.exists(localPath)) {
|
|
151
168
|
throw new Error (`'${localPath}' does not exists or is not accessible`);
|
|
152
169
|
}
|
|
@@ -154,26 +171,25 @@ async function uploadFile (localPath, remoteUri, uploadOptions = {}) {
|
|
|
154
171
|
const {
|
|
155
172
|
isMetered = true,
|
|
156
173
|
} = uploadOptions;
|
|
157
|
-
|
|
158
|
-
const parsedUri = url.parse(remoteUri);
|
|
174
|
+
const url = new URL(remoteUri);
|
|
159
175
|
const {size} = await fs.stat(localPath);
|
|
160
176
|
if (isMetered) {
|
|
161
177
|
log.info(`Uploading '${localPath}' of ${toReadableSizeString(size)} size to '${remoteUri}'`);
|
|
162
178
|
}
|
|
163
179
|
const timer = new Timer().start();
|
|
164
|
-
if (
|
|
180
|
+
if (isHttpUploadOptions(uploadOptions, url)) {
|
|
165
181
|
if (!uploadOptions.fileFieldName) {
|
|
166
|
-
uploadOptions.headers =
|
|
167
|
-
_.isPlainObject(uploadOptions.headers) ? uploadOptions.headers : {},
|
|
168
|
-
|
|
169
|
-
|
|
182
|
+
uploadOptions.headers = {
|
|
183
|
+
...(_.isPlainObject(uploadOptions.headers) ? uploadOptions.headers : {}),
|
|
184
|
+
'Content-Length': size
|
|
185
|
+
};
|
|
170
186
|
}
|
|
171
|
-
await uploadFileToHttp(fs.createReadStream(localPath),
|
|
172
|
-
} else if (
|
|
173
|
-
await uploadFileToFtp(fs.createReadStream(localPath),
|
|
187
|
+
await uploadFileToHttp(fs.createReadStream(localPath), url, uploadOptions);
|
|
188
|
+
} else if (isNotHttpUploadOptions(uploadOptions, url)) {
|
|
189
|
+
await uploadFileToFtp(fs.createReadStream(localPath), url, uploadOptions);
|
|
174
190
|
} else {
|
|
175
191
|
throw new Error(`Cannot upload the file at '${localPath}' to '${remoteUri}'. ` +
|
|
176
|
-
`Unsupported remote protocol '${
|
|
192
|
+
`Unsupported remote protocol '${url.protocol}'. ` +
|
|
177
193
|
`Only http/https and ftp/ftps protocols are supported.`);
|
|
178
194
|
}
|
|
179
195
|
if (isMetered) {
|
|
@@ -182,24 +198,15 @@ async function uploadFile (localPath, remoteUri, uploadOptions = {}) {
|
|
|
182
198
|
}
|
|
183
199
|
}
|
|
184
200
|
|
|
185
|
-
/**
|
|
186
|
-
* @typedef {Object} DownloadOptions
|
|
187
|
-
* @property {boolean} isMetered [true] - Whether to log the actual download performance
|
|
188
|
-
* (e.g. timings and speed)
|
|
189
|
-
* @property {AuthCredentials} auth
|
|
190
|
-
* @property {number} timeout [240000] - The actual request timeout in milliseconds
|
|
191
|
-
* @property {Object} headers - Request headers mapping
|
|
192
|
-
*/
|
|
193
|
-
|
|
194
201
|
/**
|
|
195
202
|
* Downloads the given file via HTTP(S)
|
|
196
203
|
*
|
|
197
204
|
* @param {string} remoteUrl - The remote url
|
|
198
205
|
* @param {string} dstPath - The local path to download the file to
|
|
199
|
-
* @param {
|
|
206
|
+
* @param {DownloadOptions & NetOptions} [downloadOptions]
|
|
200
207
|
* @throws {Error} If download operation fails
|
|
201
208
|
*/
|
|
202
|
-
async function downloadFile (remoteUrl, dstPath, downloadOptions = {}) {
|
|
209
|
+
async function downloadFile (remoteUrl, dstPath, downloadOptions = /** @type {DownloadOptions & NetOptions} */({})) {
|
|
203
210
|
const {
|
|
204
211
|
isMetered = true,
|
|
205
212
|
auth,
|
|
@@ -207,6 +214,9 @@ async function downloadFile (remoteUrl, dstPath, downloadOptions = {}) {
|
|
|
207
214
|
headers,
|
|
208
215
|
} = downloadOptions;
|
|
209
216
|
|
|
217
|
+
/**
|
|
218
|
+
* @type {import('axios').AxiosRequestConfig}
|
|
219
|
+
*/
|
|
210
220
|
const requestOpts = {
|
|
211
221
|
url: remoteUrl,
|
|
212
222
|
responseType: 'stream',
|
|
@@ -261,3 +271,49 @@ async function downloadFile (remoteUrl, dstPath, downloadOptions = {}) {
|
|
|
261
271
|
}
|
|
262
272
|
|
|
263
273
|
export { uploadFile, downloadFile };
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Common options for {@linkcode uploadFile} and {@linkcode downloadFile}.
|
|
277
|
+
* @typedef NetOptions
|
|
278
|
+
* @property {boolean} [isMetered=true] - Whether to log the actual download performance
|
|
279
|
+
* (e.g. timings and speed)
|
|
280
|
+
* @property {AuthCredentials} auth
|
|
281
|
+
*/
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Specific options for {@linkcode downloadFile}.
|
|
285
|
+
* @typedef DownloadOptions
|
|
286
|
+
* @property {number} [timeout] - The actual request timeout in milliseconds; defaults to {@linkcode DEFAULT_TIMEOUT_MS}
|
|
287
|
+
* @property {Record<string,any>} headers - Request headers mapping
|
|
288
|
+
*/
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Basic auth credentials; used by {@linkcode NetOptions}.
|
|
292
|
+
* @typedef AuthCredentials
|
|
293
|
+
* @property {string} user - Non-empty user name
|
|
294
|
+
* @property {string} pass - Non-empty password
|
|
295
|
+
*/
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* This type is used in {@linkcode uploadFile} if the remote location uses the `ftp` protocol, and distinguishes the type from {@linkcode HttpUploadOptions}.
|
|
299
|
+
* @typedef NotHttpUploadOptions
|
|
300
|
+
* @property {never} headers
|
|
301
|
+
* @property {never} method
|
|
302
|
+
* @property {never} timeout
|
|
303
|
+
* @property {never} fileFieldName
|
|
304
|
+
* @property {never} formFields
|
|
305
|
+
*/
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Specific options for {@linkcode uploadFile} if the remote location uses the `http(s)` protocol
|
|
309
|
+
* @typedef HttpUploadOptions
|
|
310
|
+
* @property {Record<string,any>} headers - Additional request headers mapping
|
|
311
|
+
* @property {import('axios').Method} [method='POST'] - The HTTP method used for file upload
|
|
312
|
+
* @property {number} [timeout] - The actual request timeout in milliseconds; defaults to {@linkcode DEFAULT_TIMEOUT_MS}
|
|
313
|
+
* @property {string} [fileFieldName='file'] - The name of the form field containing the file
|
|
314
|
+
* content to be uploaded. Any falsy value make the request to use non-multipart upload
|
|
315
|
+
* @property {Record<string,any>} [formFields] - The additional form fields
|
|
316
|
+
* to be included into the upload request. This property is only considered if
|
|
317
|
+
* `fileFieldName` is set
|
|
318
|
+
*/
|
|
319
|
+
|
package/lib/node.js
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import { isWindows } from './system';
|
|
2
2
|
import log from './logger';
|
|
3
|
+
import _ from 'lodash';
|
|
3
4
|
import { exec } from 'teen_process';
|
|
4
5
|
import path from 'path';
|
|
6
|
+
import { v4 as uuidV4 } from 'uuid';
|
|
7
|
+
|
|
8
|
+
const ECMA_SIZES = Object.freeze({
|
|
9
|
+
STRING: 2,
|
|
10
|
+
BOOLEAN: 4,
|
|
11
|
+
NUMBER: 8,
|
|
12
|
+
});
|
|
5
13
|
|
|
6
14
|
/**
|
|
7
15
|
* Internal utility to link global package to local context
|
|
8
16
|
*
|
|
9
|
-
* @
|
|
17
|
+
* @param {string} packageName - name of the package to link
|
|
10
18
|
* @throws {Error} If the command fails
|
|
11
19
|
*/
|
|
12
20
|
async function linkGlobalPackage (packageName) {
|
|
@@ -32,7 +40,7 @@ async function linkGlobalPackage (packageName) {
|
|
|
32
40
|
* this will attempt to link the package and then re-require it
|
|
33
41
|
*
|
|
34
42
|
* @param {string} packageName - the name of the package to be required
|
|
35
|
-
* @returns {
|
|
43
|
+
* @returns {Promise<unknown>} - the package object
|
|
36
44
|
* @throws {Error} If the package is not found locally or globally
|
|
37
45
|
*/
|
|
38
46
|
async function requirePackage (packageName) {
|
|
@@ -46,7 +54,7 @@ async function requirePackage (packageName) {
|
|
|
46
54
|
|
|
47
55
|
// second, get it from where it ought to be in the global node_modules
|
|
48
56
|
try {
|
|
49
|
-
const globalPackageName = path.resolve(process.env.npm_config_prefix, 'lib', 'node_modules', packageName);
|
|
57
|
+
const globalPackageName = path.resolve(process.env.npm_config_prefix ?? '', 'lib', 'node_modules', packageName);
|
|
50
58
|
log.debug(`Loading global package '${globalPackageName}'`);
|
|
51
59
|
return require(globalPackageName);
|
|
52
60
|
} catch (err) {
|
|
@@ -63,4 +71,99 @@ async function requirePackage (packageName) {
|
|
|
63
71
|
}
|
|
64
72
|
}
|
|
65
73
|
|
|
66
|
-
|
|
74
|
+
function extractAllProperties (obj) {
|
|
75
|
+
const stringProperties = [];
|
|
76
|
+
for (const prop in obj) {
|
|
77
|
+
stringProperties.push(prop);
|
|
78
|
+
}
|
|
79
|
+
if (_.isFunction(Object.getOwnPropertySymbols)) {
|
|
80
|
+
stringProperties.push(...(Object.getOwnPropertySymbols(obj)));
|
|
81
|
+
}
|
|
82
|
+
return stringProperties;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function _getSizeOfObject (seen, object) {
|
|
86
|
+
if (_.isNil(object)) {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let bytes = 0;
|
|
91
|
+
const properties = extractAllProperties(object);
|
|
92
|
+
for (const key of properties) {
|
|
93
|
+
// Do not recalculate circular references
|
|
94
|
+
if (typeof object[key] === 'object' && !_.isNil(object[key])) {
|
|
95
|
+
if (seen.has(object[key])) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
seen.add(object[key]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
bytes += getCalculator(seen)(key);
|
|
102
|
+
try {
|
|
103
|
+
bytes += getCalculator(seen)(object[key]);
|
|
104
|
+
} catch (ex) {
|
|
105
|
+
if (ex instanceof RangeError) {
|
|
106
|
+
// circular reference detected, final result might be incorrect
|
|
107
|
+
// let's be nice and not throw an exception
|
|
108
|
+
bytes = 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return bytes;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getCalculator (seen) {
|
|
117
|
+
return function calculator (obj) {
|
|
118
|
+
if (_.isBuffer(obj)) {
|
|
119
|
+
return obj.length;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
switch (typeof (obj)) {
|
|
123
|
+
case 'string':
|
|
124
|
+
return obj.length * ECMA_SIZES.STRING;
|
|
125
|
+
case 'boolean':
|
|
126
|
+
return ECMA_SIZES.BOOLEAN;
|
|
127
|
+
case 'number':
|
|
128
|
+
return ECMA_SIZES.NUMBER;
|
|
129
|
+
case 'symbol':
|
|
130
|
+
return _.isFunction(Symbol.keyFor) && Symbol.keyFor(obj)
|
|
131
|
+
? /** @type {string} */(Symbol.keyFor(obj)).length * ECMA_SIZES.STRING
|
|
132
|
+
: (obj.toString().length - 8) * ECMA_SIZES.STRING;
|
|
133
|
+
case 'object':
|
|
134
|
+
return _.isArray(obj)
|
|
135
|
+
? obj.map(getCalculator(seen)).reduce((acc, curr) => acc + curr, 0)
|
|
136
|
+
: _getSizeOfObject(seen, obj);
|
|
137
|
+
default:
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Calculate the in-depth size in memory of the provided object.
|
|
145
|
+
* The original implementation is borrowed from https://github.com/miktam/sizeof.
|
|
146
|
+
*
|
|
147
|
+
* @param {*} obj An object whose size should be calculated
|
|
148
|
+
* @returns {number} Object size in bytes.
|
|
149
|
+
*/
|
|
150
|
+
function getObjectSize (obj) {
|
|
151
|
+
return getCalculator(new WeakSet())(obj);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const OBJECTS_MAPPING = new WeakMap();
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Calculates a unique object identifier
|
|
158
|
+
*
|
|
159
|
+
* @param {object} object Any valid ECMA object
|
|
160
|
+
* @returns {string} A uuidV4 string that uniquely identifies given object
|
|
161
|
+
*/
|
|
162
|
+
function getObjectId (object) {
|
|
163
|
+
if (!OBJECTS_MAPPING.has(object)) {
|
|
164
|
+
OBJECTS_MAPPING.set(object, uuidV4());
|
|
165
|
+
}
|
|
166
|
+
return OBJECTS_MAPPING.get(object);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export { requirePackage, getObjectSize, getObjectId };
|