appium-chromedriver 5.2.17 → 5.3.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/lib/install.js CHANGED
@@ -7,7 +7,10 @@ import {
7
7
 
8
8
  const DOWNLOAD_TIMEOUT_MS = 15 * 1000;
9
9
  const LATEST_VERSION = 'LATEST';
10
-
10
+ /**
11
+ *
12
+ * @param {string} ver
13
+ */
11
14
  async function formatCdVersion (ver) {
12
15
  return ver === LATEST_VERSION
13
16
  ? (await retrieveData(`${CD_CDN}/LATEST_RELEASE`, {
@@ -17,6 +20,10 @@ async function formatCdVersion (ver) {
17
20
  : ver;
18
21
  }
19
22
 
23
+ /**
24
+ *
25
+ * @param {string} platformName
26
+ */
20
27
  async function prepareChromedriverDir (platformName) {
21
28
  const chromedriverDir = getChromedriverDir(platformName);
22
29
  if (!await fs.exists(chromedriverDir)) {
@@ -3,12 +3,23 @@ import { isStandardCap } from '@appium/base-driver';
3
3
 
4
4
  const W3C_PREFIX = 'goog:';
5
5
 
6
+ /**
7
+ *
8
+ * @param {string} capName
9
+ */
6
10
  function toW3cCapName (capName) {
7
11
  return (_.isString(capName) && !capName.includes(':') && !isStandardCap(capName))
8
12
  ? `${W3C_PREFIX}${capName}`
9
13
  : capName;
10
14
  }
11
15
 
16
+ /**
17
+ *
18
+ * @param {Record<string,any>} allCaps
19
+ * @param {string} rawCapName
20
+ * @param {any} defaultValue
21
+ * @returns
22
+ */
12
23
  function getCapValue (allCaps = {}, rawCapName, defaultValue) {
13
24
  for (const [capName, capValue] of _.toPairs(allCaps)) {
14
25
  if (toW3cCapName(capName) === toW3cCapName(rawCapName)) {
@@ -18,11 +29,16 @@ function getCapValue (allCaps = {}, rawCapName, defaultValue) {
18
29
  return defaultValue;
19
30
  }
20
31
 
32
+ /**
33
+ *
34
+ * @param {any} originalCaps
35
+ * @returns {Record<string,any>}
36
+ */
21
37
  function toW3cCapNames (originalCaps = {}) {
22
38
  return _.reduce(originalCaps, (acc, value, key) => {
23
39
  acc[toW3cCapName(key)] = value;
24
40
  return acc;
25
- }, {});
41
+ }, /** @type {Record<string,any>} */({}));
26
42
  }
27
43
 
28
44
  export { toW3cCapNames, getCapValue };
@@ -1,28 +1,45 @@
1
1
  import {
2
- getChromedriverDir, CD_CDN, retrieveData, getOsInfo,
3
- OS, X64, X86, APPLE_ARM_SUFFIXES,
2
+ getChromedriverDir,
3
+ CD_CDN,
4
+ retrieveData,
5
+ getOsInfo,
6
+ OS,
7
+ X64,
8
+ X86,
9
+ APPLE_ARM_SUFFIXES,
4
10
  } from './utils';
5
11
  import _ from 'lodash';
6
12
  import xpath from 'xpath';
7
- import { DOMParser } from '@xmldom/xmldom';
13
+ import {DOMParser} from '@xmldom/xmldom';
8
14
  import B from 'bluebird';
9
15
  import path from 'path';
10
16
  import os from 'os';
11
- import { system, fs, logger, tempDir, zip, util, net } from '@appium/support';
12
-
17
+ import {system, fs, logger, tempDir, zip, util, net} from '@appium/support';
13
18
 
14
19
  const TIMEOUT_MS = 15000;
15
20
  const MAX_PARALLEL_DOWNLOADS = 5;
16
21
 
17
22
  const log = logger.getLogger('ChromedriverStorageClient');
18
23
 
19
-
20
- async function isCrcOk (src, checksum) {
24
+ /**
25
+ *
26
+ * @param {string} src
27
+ * @param {string} checksum
28
+ * @returns {Promise<boolean>}
29
+ */
30
+ async function isCrcOk(src, checksum) {
21
31
  const md5 = await fs.hash(src, 'md5');
22
32
  return _.toLower(md5) === _.toLower(checksum);
23
33
  }
24
34
 
25
- function findChildNode (parent, childName = null, text = null) {
35
+ /**
36
+ *
37
+ * @param {Node|Attr} parent
38
+ * @param {string?} childName
39
+ * @param {string?} text
40
+ * @returns
41
+ */
42
+ function findChildNode(parent, childName = null, text = null) {
26
43
  if (!childName && !text) {
27
44
  return null;
28
45
  }
@@ -31,7 +48,7 @@ function findChildNode (parent, childName = null, text = null) {
31
48
  }
32
49
 
33
50
  for (let childNodeIdx = 0; childNodeIdx < parent.childNodes.length; childNodeIdx++) {
34
- const childNode = parent.childNodes[childNodeIdx];
51
+ const childNode = /** @type {Element|Attr} */ (parent.childNodes[childNodeIdx]);
35
52
  if (childName && !text && childName === childNode.localName) {
36
53
  return childNode;
37
54
  }
@@ -51,29 +68,35 @@ function findChildNode (parent, childName = null, text = null) {
51
68
  return null;
52
69
  }
53
70
 
54
- function extractNodeText (node) {
55
- return (!node || !node.firstChild || !util.hasValue(node.firstChild.nodeValue))
71
+ /**
72
+ *
73
+ * @param {Node?} node
74
+ * @returns
75
+ */
76
+ function extractNodeText(node) {
77
+ return !node || !node.firstChild || !util.hasValue(node.firstChild.nodeValue)
56
78
  ? null
57
79
  : node.firstChild.nodeValue;
58
80
  }
59
81
 
60
-
61
82
  class ChromedriverStorageClient {
62
- constructor (args = {}) {
63
- const {
64
- chromedriverDir = getChromedriverDir(),
65
- timeout = TIMEOUT_MS,
66
- } = args;
83
+ /**
84
+ *
85
+ * @param {import('./types').ChromedriverStorageClientOpts} args
86
+ */
87
+ constructor(args = {}) {
88
+ const {chromedriverDir = getChromedriverDir(), timeout = TIMEOUT_MS} = args;
67
89
  this.chromedriverDir = chromedriverDir;
68
90
  this.timeout = timeout;
91
+ /** @type {ChromedriverDetailsMapping} */
69
92
  this.mapping = {};
70
93
  }
71
94
 
72
95
  /**
73
96
  * @typedef {Object} AdditionalDriverDetails
74
- * @property {?string} version - Chromedriver version
97
+ * @property {string?} version - Chromedriver version
75
98
  * or `null` if it cannot be found
76
- * @property {?string} minBrowserVersion - The minimum browser version
99
+ * @property {string?} minBrowserVersion - The minimum browser version
77
100
  * supported by chromedriver or `null` if it cannot be found
78
101
  */
79
102
 
@@ -84,7 +107,7 @@ class ChromedriverStorageClient {
84
107
  * @param {string} content - Release notes of the corresponding chromedriver
85
108
  * @returns {AdditionalDriverDetails}
86
109
  */
87
- parseNotes (content) {
110
+ parseNotes(content) {
88
111
  const result = {};
89
112
  const versionMatch = /^\s*[-]+ChromeDriver[\D]+([\d.]+)/im.exec(content);
90
113
  if (versionMatch) {
@@ -101,21 +124,27 @@ class ChromedriverStorageClient {
101
124
  * Downloads chromedriver release notes and puts them
102
125
  * into the dictionary argument
103
126
  *
127
+ * The method call mutates by merging `AdditionalDriverDetails`
104
128
  * @param {string} driverKey - Driver version plus archive name
105
129
  * @param {string} notesUrl - The URL of chromedriver notes
106
- * @param {Object} infoDict - The dictionary containing driver info.
107
- * The method call mutates by merging `AdditionalDriverDetails`
130
+ * @param {ChromedriverDetails} infoDict - The dictionary containing driver info.
108
131
  * @throws {Error} if the release notes cannot be downloaded
109
132
  */
110
- async retrieveAdditionalDriverInfo (driverKey, notesUrl, infoDict) {
111
- const notes = await retrieveData(notesUrl, {
112
- 'user-agent': 'appium',
113
- accept: '*/*',
114
- }, { timeout: this.timeout });
115
- const { minBrowserVersion } = this.parseNotes(notes);
133
+ async retrieveAdditionalDriverInfo(driverKey, notesUrl, infoDict) {
134
+ const notes = await retrieveData(
135
+ notesUrl,
136
+ {
137
+ 'user-agent': 'appium',
138
+ accept: '*/*',
139
+ },
140
+ {timeout: this.timeout}
141
+ );
142
+ const {minBrowserVersion} = this.parseNotes(notes);
116
143
  if (!minBrowserVersion) {
117
- log.debug(`The driver '${driverKey}' does not contain valid release notes at ${notesUrl}. ` +
118
- `Skipping it`);
144
+ log.debug(
145
+ `The driver '${driverKey}' does not contain valid release notes at ${notesUrl}. ` +
146
+ `Skipping it`
147
+ );
119
148
  return;
120
149
  }
121
150
  infoDict.minBrowserVersion = minBrowserVersion;
@@ -125,14 +154,16 @@ class ChromedriverStorageClient {
125
154
  * Parses chromedriver storage XML and stores
126
155
  * the parsed results into `this.mapping`
127
156
  *
128
- * @param {DOMDocument} doc - The DOM representation
157
+ * @param {Document} doc - The DOM representation
129
158
  * of the chromedriver storage XML
130
159
  * @param {boolean} shouldParseNotes [true] - If set to `true`
131
160
  * then additional drivers information is going to be parsed
132
161
  * and assigned to `this.mapping`
133
162
  */
134
- async parseStorageXml (doc, shouldParseNotes = true) {
135
- const driverNodes = xpath.select(`//*[local-name(.)='Contents']`, doc);
163
+ async parseStorageXml(doc, shouldParseNotes = true) {
164
+ const driverNodes = /** @type {Array<Node|Attr>} */ (
165
+ xpath.select(`//*[local-name(.)='Contents']`, doc)
166
+ );
136
167
  log.debug(`Parsed ${driverNodes.length} entries from storage XML`);
137
168
  if (_.isEmpty(driverNodes)) {
138
169
  return;
@@ -140,10 +171,11 @@ class ChromedriverStorageClient {
140
171
 
141
172
  const promises = [];
142
173
  for (const driverNode of driverNodes) {
143
- const key = extractNodeText(findChildNode(driverNode, 'Key'));
144
- if (!_.includes(key, '/chromedriver_')) {
174
+ const k = extractNodeText(findChildNode(driverNode, 'Key'));
175
+ if (!_.includes(k, '/chromedriver_')) {
145
176
  continue;
146
177
  }
178
+ const key = String(k);
147
179
 
148
180
  const etag = extractNodeText(findChildNode(driverNode, 'ETag'));
149
181
  if (!etag) {
@@ -151,16 +183,20 @@ class ChromedriverStorageClient {
151
183
  continue;
152
184
  }
153
185
 
186
+ /** @type {ChromedriverDetails} */
154
187
  const cdInfo = {
155
188
  url: `${CD_CDN}/${key}`,
156
189
  etag: _.trim(etag, '"'),
157
- version: _.first(key.split('/')),
190
+ version: /** @type {string} */ (_.first(key.split('/'))),
191
+ minBrowserVersion: null,
158
192
  };
159
193
  this.mapping[key] = cdInfo;
160
194
 
161
195
  const notesPath = `${cdInfo.version}/notes.txt`;
162
- const isNotesPresent = !!driverNodes
163
- .reduce((acc, node) => acc || findChildNode(node, 'Key', notesPath), false);
196
+ const isNotesPresent = !!driverNodes.reduce(
197
+ (acc, node) => Boolean(acc || findChildNode(node, 'Key', notesPath)),
198
+ false
199
+ );
164
200
  if (!isNotesPresent) {
165
201
  cdInfo.minBrowserVersion = null;
166
202
  if (shouldParseNotes) {
@@ -180,34 +216,23 @@ class ChromedriverStorageClient {
180
216
  log.info(`The total count of entries in the mapping: ${_.size(this.mapping)}`);
181
217
  }
182
218
 
183
- /**
184
- * @typedef {Object} DriverDetails
185
- * @property {string} url - The full url to the corresponding driver in
186
- * the remote storage
187
- * @property {string} etag - The CRC of the driver archive
188
- * @property {string} version - Chromedriver version
189
- */
190
-
191
- /**
192
- * @typedef {Object} ChromedriversMapping
193
- * @property {DriverDetails} - The keys are unique driver identifiers
194
- * (version/archive name). The corresponding values have `DriverDetails`
195
- * containing chromedriver details
196
- */
197
-
198
219
  /**
199
220
  * Retrieves chromedriver mapping from the storage
200
221
  *
201
222
  * @param {boolean} shouldParseNotes [true] - if set to `true`
202
223
  * then additional chromedrivers info is going to be retrieved and
203
224
  * parsed from release notes
204
- * @returns {ChromedriversMapping}
225
+ * @returns {Promise<ChromedriverDetailsMapping>}
205
226
  */
206
- async retrieveMapping (shouldParseNotes = true) {
207
- const xml = await retrieveData(CD_CDN, {
208
- 'user-agent': 'appium',
209
- accept: 'application/xml, */*',
210
- }, { timeout: this.timeout });
227
+ async retrieveMapping(shouldParseNotes = true) {
228
+ const xml = await retrieveData(
229
+ CD_CDN,
230
+ {
231
+ 'user-agent': 'appium',
232
+ accept: 'application/xml, */*',
233
+ },
234
+ {timeout: this.timeout}
235
+ );
211
236
  const doc = new DOMParser().parseFromString(xml);
212
237
  await this.parseStorageXml(doc, shouldParseNotes);
213
238
  return _.cloneDeep(this.mapping);
@@ -220,54 +245,50 @@ class ChromedriverStorageClient {
220
245
  * @param {string} src - The source archive path
221
246
  * @param {string} dst - The destination chromedriver path
222
247
  */
223
- async unzipDriver (src, dst) {
248
+ async unzipDriver(src, dst) {
224
249
  const tmpRoot = await tempDir.openDir();
225
250
  try {
226
251
  await zip.extractAllTo(src, tmpRoot);
227
- const chromedriverPath = await fs.walkDir(tmpRoot, true, (itemPath, isDirectory) =>
228
- !isDirectory && _.toLower(path.parse(itemPath).name) === 'chromedriver');
252
+ const chromedriverPath = await fs.walkDir(
253
+ tmpRoot,
254
+ true,
255
+ (itemPath, isDirectory) =>
256
+ !isDirectory && _.toLower(path.parse(itemPath).name) === 'chromedriver'
257
+ );
229
258
  if (!chromedriverPath) {
230
- throw new Error('The archive was unzipped properly, but we could not find any chromedriver executable');
259
+ throw new Error(
260
+ 'The archive was unzipped properly, but we could not find any chromedriver executable'
261
+ );
231
262
  }
232
263
  log.debug(`Moving the extracted '${path.basename(chromedriverPath)}' to '${dst}'`);
233
264
  await fs.mv(chromedriverPath, dst, {
234
- mkdirp: true
265
+ mkdirp: true,
235
266
  });
236
267
  } finally {
237
268
  await fs.rimraf(tmpRoot);
238
269
  }
239
270
  }
240
271
 
241
- /**
242
- * @typedef {Object} OSInfo
243
- * @property {string} name - The name of the host OS
244
- * Can be either `mac`, `windows` or `linux`
245
- * @property {string} arch - The architecture of the host OS.
246
- * Can be either `32` or `64`
247
- */
248
-
249
272
  /**
250
273
  * Filters `this.mapping` to only select matching
251
274
  * chromedriver entries by operating system information
252
275
  * and/or additional synchronization options (if provided)
253
276
  *
254
- * @param {?OSInfo} osInfo
255
- * @param {?SyncOptions} opts
277
+ * @param {OSInfo} osInfo
278
+ * @param {SyncOptions} opts
256
279
  * @returns {Array<String>} The list of filtered chromedriver
257
280
  * entry names (version/archive name)
258
281
  */
259
- selectMatchingDrivers (osInfo, opts = {}) {
260
- const {
261
- minBrowserVersion,
262
- versions = [],
263
- } = opts;
282
+ selectMatchingDrivers(osInfo, opts = {}) {
283
+ const {minBrowserVersion, versions = []} = opts;
264
284
  let driversToSync = _.keys(this.mapping);
265
285
 
266
286
  if (!_.isEmpty(versions)) {
267
287
  // Handle only selected versions if requested
268
288
  log.debug(`Selecting chromedrivers whose versions match to ${versions}`);
269
- driversToSync = driversToSync
270
- .filter((cdName) => versions.includes(`${this.mapping[cdName].version}`));
289
+ driversToSync = driversToSync.filter((cdName) =>
290
+ versions.includes(`${this.mapping[cdName].version}`)
291
+ );
271
292
 
272
293
  log.debug(`Got ${util.pluralize('item', driversToSync.length, true)}`);
273
294
  if (_.isEmpty(driversToSync)) {
@@ -275,29 +296,41 @@ class ChromedriverStorageClient {
275
296
  }
276
297
  }
277
298
 
278
- if (!isNaN(minBrowserVersion)) {
299
+ if (_.isString(minBrowserVersion) && !Number.isNaN(minBrowserVersion)) {
279
300
  // Only select drivers that support the current browser whose major version number equals to `minBrowserVersion`
280
301
  const minBrowserVersionInt = parseInt(minBrowserVersion, 10);
281
- log.debug(`Selecting chromedrivers whose minimum supported browser version matches to ${minBrowserVersionInt}`);
302
+ log.debug(
303
+ `Selecting chromedrivers whose minimum supported browser version matches to ${minBrowserVersionInt}`
304
+ );
282
305
  let closestMatchedVersionNumber = 0;
283
306
  // Select the newest available and compatible chromedriver
284
307
  for (const cdName of driversToSync) {
285
- const currentMinBrowserVersion = parseInt(this.mapping[cdName].minBrowserVersion, 10);
286
- if (!isNaN(currentMinBrowserVersion)
287
- && currentMinBrowserVersion <= minBrowserVersionInt
288
- && closestMatchedVersionNumber < currentMinBrowserVersion) {
308
+ const currentMinBrowserVersion = parseInt(
309
+ String(this.mapping[cdName].minBrowserVersion),
310
+ 10
311
+ );
312
+ if (
313
+ !Number.isNaN(currentMinBrowserVersion) &&
314
+ currentMinBrowserVersion <= minBrowserVersionInt &&
315
+ closestMatchedVersionNumber < currentMinBrowserVersion
316
+ ) {
289
317
  closestMatchedVersionNumber = currentMinBrowserVersion;
290
318
  }
291
319
  }
292
- driversToSync = driversToSync.filter((cdName) => `${this.mapping[cdName].minBrowserVersion}` ===
293
- `${closestMatchedVersionNumber > 0 ? closestMatchedVersionNumber : minBrowserVersionInt}`);
320
+ driversToSync = driversToSync.filter(
321
+ (cdName) =>
322
+ `${this.mapping[cdName].minBrowserVersion}` ===
323
+ `${closestMatchedVersionNumber > 0 ? closestMatchedVersionNumber : minBrowserVersionInt}`
324
+ );
294
325
 
295
326
  log.debug(`Got ${util.pluralize('item', driversToSync.length, true)}`);
296
327
  if (_.isEmpty(driversToSync)) {
297
328
  return [];
298
329
  }
299
- log.debug(`Will select candidate ${util.pluralize('driver', driversToSync.length)} ` +
300
- `versioned as '${_.uniq(driversToSync.map((cdName) => this.mapping[cdName].version))}'`);
330
+ log.debug(
331
+ `Will select candidate ${util.pluralize('driver', driversToSync.length)} ` +
332
+ `versioned as '${_.uniq(driversToSync.map((cdName) => this.mapping[cdName].version))}'`
333
+ );
301
334
  }
302
335
 
303
336
  if (!_.isEmpty(osInfo)) {
@@ -338,27 +371,28 @@ class ChromedriverStorageClient {
338
371
  * or return a boolean result if the driver retrieval process fails
339
372
  * @throws {Error} if there was a failure while retrieving the driver
340
373
  * and `isStrict` is set to `true`
341
- * @returns {boolean} if `true` then the chromedriver is successfully
374
+ * @returns {Promise<boolean>} if `true` then the chromedriver is successfully
342
375
  * downloaded and extracted.
343
376
  */
344
- async retrieveDriver (index, driverKey, archivesRoot, isStrict = false) {
345
- const { url, etag, version } = this.mapping[driverKey];
377
+ async retrieveDriver(index, driverKey, archivesRoot, isStrict = false) {
378
+ const {url, etag, version} = this.mapping[driverKey];
346
379
  const archivePath = path.resolve(archivesRoot, `${index}.zip`);
347
380
  log.debug(`Retrieving '${url}' to '${archivePath}'`);
348
381
  try {
349
382
  await net.downloadFile(url, archivePath, {
350
383
  isMetered: false,
351
- timeout: TIMEOUT_MS
384
+ timeout: TIMEOUT_MS,
352
385
  });
353
386
  } catch (e) {
354
- const msg = `Cannot download chromedriver archive. Original error: ${e.message}`;
387
+ const err = /** @type {Error} */ (e);
388
+ const msg = `Cannot download chromedriver archive. Original error: ${err.message}`;
355
389
  if (isStrict) {
356
390
  throw new Error(msg);
357
391
  }
358
392
  log.error(msg);
359
393
  return false;
360
394
  }
361
- if (!await isCrcOk(archivePath, etag)) {
395
+ if (!(await isCrcOk(archivePath, etag))) {
362
396
  const msg = `The checksum for the downloaded chromedriver '${driverKey}' did not match`;
363
397
  if (isStrict) {
364
398
  throw new Error(msg);
@@ -366,46 +400,33 @@ class ChromedriverStorageClient {
366
400
  log.error(msg);
367
401
  return false;
368
402
  }
369
- const fileName = `${path.parse(url).name}_v${version}` +
370
- (system.isWindows() ? '.exe' : '');
403
+ const fileName = `${path.parse(url).name}_v${version}` + (system.isWindows() ? '.exe' : '');
371
404
  const targetPath = path.resolve(this.chromedriverDir, fileName);
372
405
  try {
373
406
  await this.unzipDriver(archivePath, targetPath);
374
407
  await fs.chmod(targetPath, 0o755);
375
408
  log.debug(`Permissions of the file '${targetPath}' have been changed to 755`);
376
409
  } catch (e) {
410
+ const err = /** @type {Error} */ (e);
377
411
  if (isStrict) {
378
- throw e;
412
+ throw err;
379
413
  }
380
- log.error(e.message);
414
+ log.error(err.message);
381
415
  return false;
382
416
  }
383
417
  return true;
384
418
  }
385
419
 
386
- /**
387
- * @typedef {Object} SyncOptions
388
- * @property {Array<String>} versions - The list of chromedriver
389
- * versions to sync. If empty (the default value) then all available
390
- * chromedrivers are going to be downloaded and extracted
391
- * @property {string|number} minBrowserVersion - The minumum supported
392
- * Chrome version that downloaded chromedrivers should support. Can match
393
- * multiple drivers.
394
- * @property {?OSInfo} osInfo - System information used to filter out
395
- * the list of the retrieved drivers. If not provided then the script
396
- * will try to retrieve it.
397
- */
398
-
399
420
  /**
400
421
  * Retrieves chromedrivers from the remote storage
401
422
  * to the local file system
402
423
  *
403
- * @param {?SyncOptions} opts
424
+ * @param {SyncOptions} opts
404
425
  * @throws {Error} if there was a problem while retrieving
405
426
  * the drivers
406
- * @returns {Array<String} The list of successfully synchronized driver keys
427
+ * @returns {Promise<string[]>} The list of successfully synchronized driver keys
407
428
  */
408
- async syncDrivers (opts = {}) {
429
+ async syncDrivers(opts = {}) {
409
430
  if (_.isEmpty(this.mapping)) {
410
431
  await this.retrieveMapping(!!opts.minBrowserVersion);
411
432
  }
@@ -413,24 +434,31 @@ class ChromedriverStorageClient {
413
434
  throw new Error('Cannot retrieve chromedrivers mapping from Google storage');
414
435
  }
415
436
 
416
- const driversToSync = this.selectMatchingDrivers(opts.osInfo ?? await getOsInfo(), opts);
437
+ const driversToSync = this.selectMatchingDrivers(opts.osInfo ?? (await getOsInfo()), opts);
417
438
  if (_.isEmpty(driversToSync)) {
418
439
  log.debug(`There are no drivers to sync. Exiting`);
419
440
  return [];
420
441
  }
421
- log.debug(`Got ${util.pluralize('driver', driversToSync.length, true)} to sync: ` +
422
- JSON.stringify(driversToSync, null, 2));
423
-
442
+ log.debug(
443
+ `Got ${util.pluralize('driver', driversToSync.length, true)} to sync: ` +
444
+ JSON.stringify(driversToSync, null, 2)
445
+ );
446
+
447
+ /**
448
+ * @type {string[]}
449
+ */
424
450
  const synchronizedDrivers = [];
425
451
  const promises = [];
426
452
  const archivesRoot = await tempDir.openDir();
427
453
  try {
428
454
  for (const [idx, driverKey] of driversToSync.entries()) {
429
- promises.push((async () => {
430
- if (await this.retrieveDriver(idx, driverKey, archivesRoot, !_.isEmpty(opts))) {
431
- synchronizedDrivers.push(driverKey);
432
- }
433
- })());
455
+ promises.push(
456
+ (async () => {
457
+ if (await this.retrieveDriver(idx, driverKey, archivesRoot, !_.isEmpty(opts))) {
458
+ synchronizedDrivers.push(driverKey);
459
+ }
460
+ })()
461
+ );
434
462
 
435
463
  if (promises.length % MAX_PARALLEL_DOWNLOADS === 0) {
436
464
  await B.all(promises);
@@ -441,8 +469,10 @@ class ChromedriverStorageClient {
441
469
  await fs.rimraf(archivesRoot);
442
470
  }
443
471
  if (!_.isEmpty(synchronizedDrivers)) {
444
- log.info(`Successfully synchronized ` +
445
- `${util.pluralize('chromedriver', synchronizedDrivers.length, true)}`);
472
+ log.info(
473
+ `Successfully synchronized ` +
474
+ `${util.pluralize('chromedriver', synchronizedDrivers.length, true)}`
475
+ );
446
476
  } else {
447
477
  log.info(`No chromedrivers were synchronized`);
448
478
  }
@@ -450,5 +480,11 @@ class ChromedriverStorageClient {
450
480
  }
451
481
  }
452
482
 
453
-
454
483
  export default ChromedriverStorageClient;
484
+
485
+ /**
486
+ * @typedef {import('./types').SyncOptions} SyncOptions
487
+ * @typedef {import('./types').OSInfo} OSInfo
488
+ * @typedef {import('./types').ChromedriverDetails} ChromedriverDetails
489
+ * @typedef {import('./types').ChromedriverDetailsMapping} ChromedriverDetailsMapping
490
+ */