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.
@@ -1,300 +1,419 @@
1
1
  "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- Object.defineProperty(exports, "__esModule", {
5
- value: true
6
- });
7
- exports.default = void 0;
8
- require("source-map-support/register");
9
- var _utils = require("./utils");
10
- var _lodash = _interopRequireDefault(require("lodash"));
11
- var _xpath = _interopRequireDefault(require("xpath"));
12
- var _xmldom = require("@xmldom/xmldom");
13
- var _bluebird = _interopRequireDefault(require("bluebird"));
14
- var _path = _interopRequireDefault(require("path"));
15
- var _os = _interopRequireDefault(require("os"));
16
- var _support = require("@appium/support");
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const utils_1 = require("./utils");
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const xpath_1 = __importDefault(require("xpath"));
9
+ const xmldom_1 = require("@xmldom/xmldom");
10
+ const bluebird_1 = __importDefault(require("bluebird"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const os_1 = __importDefault(require("os"));
13
+ const support_1 = require("@appium/support");
17
14
  const TIMEOUT_MS = 15000;
18
15
  const MAX_PARALLEL_DOWNLOADS = 5;
19
- const log = _support.logger.getLogger('ChromedriverStorageClient');
16
+ const log = support_1.logger.getLogger('ChromedriverStorageClient');
17
+ /**
18
+ *
19
+ * @param {string} src
20
+ * @param {string} checksum
21
+ * @returns {Promise<boolean>}
22
+ */
20
23
  async function isCrcOk(src, checksum) {
21
- const md5 = await _support.fs.hash(src, 'md5');
22
- return _lodash.default.toLower(md5) === _lodash.default.toLower(checksum);
24
+ const md5 = await support_1.fs.hash(src, 'md5');
25
+ return lodash_1.default.toLower(md5) === lodash_1.default.toLower(checksum);
23
26
  }
27
+ /**
28
+ *
29
+ * @param {Node|Attr} parent
30
+ * @param {string?} childName
31
+ * @param {string?} text
32
+ * @returns
33
+ */
24
34
  function findChildNode(parent, childName = null, text = null) {
25
- if (!childName && !text) {
26
- return null;
27
- }
28
- if (!parent.hasChildNodes()) {
29
- return null;
30
- }
31
- for (let childNodeIdx = 0; childNodeIdx < parent.childNodes.length; childNodeIdx++) {
32
- const childNode = parent.childNodes[childNodeIdx];
33
- if (childName && !text && childName === childNode.localName) {
34
- return childNode;
35
+ if (!childName && !text) {
36
+ return null;
35
37
  }
36
- if (text) {
37
- const childText = extractNodeText(childNode);
38
- if (!childText) {
39
- continue;
40
- }
41
- if (childName && childName === childNode.localName && text === childText) {
42
- return childNode;
43
- }
44
- if (!childName && text === childText) {
45
- return childNode;
46
- }
38
+ if (!parent.hasChildNodes()) {
39
+ return null;
40
+ }
41
+ for (let childNodeIdx = 0; childNodeIdx < parent.childNodes.length; childNodeIdx++) {
42
+ const childNode = /** @type {Element|Attr} */ (parent.childNodes[childNodeIdx]);
43
+ if (childName && !text && childName === childNode.localName) {
44
+ return childNode;
45
+ }
46
+ if (text) {
47
+ const childText = extractNodeText(childNode);
48
+ if (!childText) {
49
+ continue;
50
+ }
51
+ if (childName && childName === childNode.localName && text === childText) {
52
+ return childNode;
53
+ }
54
+ if (!childName && text === childText) {
55
+ return childNode;
56
+ }
57
+ }
47
58
  }
48
- }
49
- return null;
59
+ return null;
50
60
  }
61
+ /**
62
+ *
63
+ * @param {Node?} node
64
+ * @returns
65
+ */
51
66
  function extractNodeText(node) {
52
- return !node || !node.firstChild || !_support.util.hasValue(node.firstChild.nodeValue) ? null : node.firstChild.nodeValue;
67
+ return !node || !node.firstChild || !support_1.util.hasValue(node.firstChild.nodeValue)
68
+ ? null
69
+ : node.firstChild.nodeValue;
53
70
  }
54
71
  class ChromedriverStorageClient {
55
- constructor(args = {}) {
56
- const {
57
- chromedriverDir = (0, _utils.getChromedriverDir)(),
58
- timeout = TIMEOUT_MS
59
- } = args;
60
- this.chromedriverDir = chromedriverDir;
61
- this.timeout = timeout;
62
- this.mapping = {};
63
- }
64
- parseNotes(content) {
65
- const result = {};
66
- const versionMatch = /^\s*[-]+ChromeDriver[\D]+([\d.]+)/im.exec(content);
67
- if (versionMatch) {
68
- result.version = versionMatch[1];
69
- }
70
- const minBrowserVersionMatch = /^\s*Supports Chrome[\D]+(\d+)/im.exec(content);
71
- if (minBrowserVersionMatch) {
72
- result.minBrowserVersion = minBrowserVersionMatch[1];
73
- }
74
- return result;
75
- }
76
- async retrieveAdditionalDriverInfo(driverKey, notesUrl, infoDict) {
77
- const notes = await (0, _utils.retrieveData)(notesUrl, {
78
- 'user-agent': 'appium',
79
- accept: '*/*'
80
- }, {
81
- timeout: this.timeout
82
- });
83
- const {
84
- minBrowserVersion
85
- } = this.parseNotes(notes);
86
- if (!minBrowserVersion) {
87
- log.debug(`The driver '${driverKey}' does not contain valid release notes at ${notesUrl}. ` + `Skipping it`);
88
- return;
72
+ /**
73
+ *
74
+ * @param {import('./types').ChromedriverStorageClientOpts} args
75
+ */
76
+ constructor(args = {}) {
77
+ const { chromedriverDir = (0, utils_1.getChromedriverDir)(), timeout = TIMEOUT_MS } = args;
78
+ this.chromedriverDir = chromedriverDir;
79
+ this.timeout = timeout;
80
+ /** @type {ChromedriverDetailsMapping} */
81
+ this.mapping = {};
89
82
  }
90
- infoDict.minBrowserVersion = minBrowserVersion;
91
- }
92
- async parseStorageXml(doc, shouldParseNotes = true) {
93
- const driverNodes = _xpath.default.select(`//*[local-name(.)='Contents']`, doc);
94
- log.debug(`Parsed ${driverNodes.length} entries from storage XML`);
95
- if (_lodash.default.isEmpty(driverNodes)) {
96
- return;
97
- }
98
- const promises = [];
99
- for (const driverNode of driverNodes) {
100
- const key = extractNodeText(findChildNode(driverNode, 'Key'));
101
- if (!_lodash.default.includes(key, '/chromedriver_')) {
102
- continue;
103
- }
104
- const etag = extractNodeText(findChildNode(driverNode, 'ETag'));
105
- if (!etag) {
106
- log.debug(`The entry '${key}' does not contain the checksum. Skipping it`);
107
- continue;
108
- }
109
- const cdInfo = {
110
- url: `${_utils.CD_CDN}/${key}`,
111
- etag: _lodash.default.trim(etag, '"'),
112
- version: _lodash.default.first(key.split('/'))
113
- };
114
- this.mapping[key] = cdInfo;
115
- const notesPath = `${cdInfo.version}/notes.txt`;
116
- const isNotesPresent = !!driverNodes.reduce((acc, node) => acc || findChildNode(node, 'Key', notesPath), false);
117
- if (!isNotesPresent) {
118
- cdInfo.minBrowserVersion = null;
119
- if (shouldParseNotes) {
120
- log.info(`The entry '${key}' does not contain any notes. Skipping it`);
83
+ /**
84
+ * @typedef {Object} AdditionalDriverDetails
85
+ * @property {string?} version - Chromedriver version
86
+ * or `null` if it cannot be found
87
+ * @property {string?} minBrowserVersion - The minimum browser version
88
+ * supported by chromedriver or `null` if it cannot be found
89
+ */
90
+ /**
91
+ * Gets additional chromedriver details from chromedriver
92
+ * release notes
93
+ *
94
+ * @param {string} content - Release notes of the corresponding chromedriver
95
+ * @returns {AdditionalDriverDetails}
96
+ */
97
+ parseNotes(content) {
98
+ const result = {};
99
+ const versionMatch = /^\s*[-]+ChromeDriver[\D]+([\d.]+)/im.exec(content);
100
+ if (versionMatch) {
101
+ result.version = versionMatch[1];
121
102
  }
122
- continue;
123
- } else if (!shouldParseNotes) {
124
- continue;
125
- }
126
- promises.push(this.retrieveAdditionalDriverInfo(key, `${_utils.CD_CDN}/${notesPath}`, cdInfo));
127
- if (promises.length % MAX_PARALLEL_DOWNLOADS === 0) {
128
- await _bluebird.default.all(promises);
129
- }
130
- }
131
- await _bluebird.default.all(promises);
132
- log.info(`The total count of entries in the mapping: ${_lodash.default.size(this.mapping)}`);
133
- }
134
- async retrieveMapping(shouldParseNotes = true) {
135
- const xml = await (0, _utils.retrieveData)(_utils.CD_CDN, {
136
- 'user-agent': 'appium',
137
- accept: 'application/xml, */*'
138
- }, {
139
- timeout: this.timeout
140
- });
141
- const doc = new _xmldom.DOMParser().parseFromString(xml);
142
- await this.parseStorageXml(doc, shouldParseNotes);
143
- return _lodash.default.cloneDeep(this.mapping);
144
- }
145
- async unzipDriver(src, dst) {
146
- const tmpRoot = await _support.tempDir.openDir();
147
- try {
148
- await _support.zip.extractAllTo(src, tmpRoot);
149
- const chromedriverPath = await _support.fs.walkDir(tmpRoot, true, (itemPath, isDirectory) => !isDirectory && _lodash.default.toLower(_path.default.parse(itemPath).name) === 'chromedriver');
150
- if (!chromedriverPath) {
151
- throw new Error('The archive was unzipped properly, but we could not find any chromedriver executable');
152
- }
153
- log.debug(`Moving the extracted '${_path.default.basename(chromedriverPath)}' to '${dst}'`);
154
- await _support.fs.mv(chromedriverPath, dst, {
155
- mkdirp: true
156
- });
157
- } finally {
158
- await _support.fs.rimraf(tmpRoot);
159
- }
160
- }
161
- selectMatchingDrivers(osInfo, opts = {}) {
162
- const {
163
- minBrowserVersion,
164
- versions = []
165
- } = opts;
166
- let driversToSync = _lodash.default.keys(this.mapping);
167
- if (!_lodash.default.isEmpty(versions)) {
168
- log.debug(`Selecting chromedrivers whose versions match to ${versions}`);
169
- driversToSync = driversToSync.filter(cdName => versions.includes(`${this.mapping[cdName].version}`));
170
- log.debug(`Got ${_support.util.pluralize('item', driversToSync.length, true)}`);
171
- if (_lodash.default.isEmpty(driversToSync)) {
172
- return [];
173
- }
174
- }
175
- if (!isNaN(minBrowserVersion)) {
176
- const minBrowserVersionInt = parseInt(minBrowserVersion, 10);
177
- log.debug(`Selecting chromedrivers whose minimum supported browser version matches to ${minBrowserVersionInt}`);
178
- let closestMatchedVersionNumber = 0;
179
- for (const cdName of driversToSync) {
180
- const currentMinBrowserVersion = parseInt(this.mapping[cdName].minBrowserVersion, 10);
181
- if (!isNaN(currentMinBrowserVersion) && currentMinBrowserVersion <= minBrowserVersionInt && closestMatchedVersionNumber < currentMinBrowserVersion) {
182
- closestMatchedVersionNumber = currentMinBrowserVersion;
103
+ const minBrowserVersionMatch = /^\s*Supports Chrome[\D]+(\d+)/im.exec(content);
104
+ if (minBrowserVersionMatch) {
105
+ result.minBrowserVersion = minBrowserVersionMatch[1];
183
106
  }
184
- }
185
- driversToSync = driversToSync.filter(cdName => `${this.mapping[cdName].minBrowserVersion}` === `${closestMatchedVersionNumber > 0 ? closestMatchedVersionNumber : minBrowserVersionInt}`);
186
- log.debug(`Got ${_support.util.pluralize('item', driversToSync.length, true)}`);
187
- if (_lodash.default.isEmpty(driversToSync)) {
188
- return [];
189
- }
190
- log.debug(`Will select candidate ${_support.util.pluralize('driver', driversToSync.length)} ` + `versioned as '${_lodash.default.uniq(driversToSync.map(cdName => this.mapping[cdName].version))}'`);
107
+ return result;
191
108
  }
192
- if (!_lodash.default.isEmpty(osInfo)) {
193
- let {
194
- name,
195
- arch
196
- } = osInfo;
197
- if (arch === _utils.X64 && !driversToSync.some(cdName => cdName.includes(`_${name}${_utils.X64}`))) {
198
- arch = _utils.X86;
199
- }
200
- if (name === _utils.OS.mac && _lodash.default.includes(_lodash.default.toLower(_os.default.cpus()[0].model), 'apple')) {
201
- for (const armSuffix of _utils.APPLE_ARM_SUFFIXES) {
202
- if (driversToSync.some(cdName => cdName.includes(armSuffix))) {
203
- arch = armSuffix;
204
- break;
205
- }
109
+ /**
110
+ * Downloads chromedriver release notes and puts them
111
+ * into the dictionary argument
112
+ *
113
+ * The method call mutates by merging `AdditionalDriverDetails`
114
+ * @param {string} driverKey - Driver version plus archive name
115
+ * @param {string} notesUrl - The URL of chromedriver notes
116
+ * @param {ChromedriverDetails} infoDict - The dictionary containing driver info.
117
+ * @throws {Error} if the release notes cannot be downloaded
118
+ */
119
+ async retrieveAdditionalDriverInfo(driverKey, notesUrl, infoDict) {
120
+ const notes = await (0, utils_1.retrieveData)(notesUrl, {
121
+ 'user-agent': 'appium',
122
+ accept: '*/*',
123
+ }, { timeout: this.timeout });
124
+ const { minBrowserVersion } = this.parseNotes(notes);
125
+ if (!minBrowserVersion) {
126
+ log.debug(`The driver '${driverKey}' does not contain valid release notes at ${notesUrl}. ` +
127
+ `Skipping it`);
128
+ return;
206
129
  }
207
- }
208
- log.debug(`Selecting chromedrivers whose platform matches to ${name}${arch}`);
209
- const platformRe = new RegExp(`(\\b|_)${name}${arch}\\b`);
210
- driversToSync = driversToSync.filter(cdName => platformRe.test(cdName));
211
- log.debug(`Got ${_support.util.pluralize('item', driversToSync.length, true)}`);
130
+ infoDict.minBrowserVersion = minBrowserVersion;
212
131
  }
213
- return driversToSync;
214
- }
215
- async retrieveDriver(index, driverKey, archivesRoot, isStrict = false) {
216
- const {
217
- url,
218
- etag,
219
- version
220
- } = this.mapping[driverKey];
221
- const archivePath = _path.default.resolve(archivesRoot, `${index}.zip`);
222
- log.debug(`Retrieving '${url}' to '${archivePath}'`);
223
- try {
224
- await _support.net.downloadFile(url, archivePath, {
225
- isMetered: false,
226
- timeout: TIMEOUT_MS
227
- });
228
- } catch (e) {
229
- const msg = `Cannot download chromedriver archive. Original error: ${e.message}`;
230
- if (isStrict) {
231
- throw new Error(msg);
232
- }
233
- log.error(msg);
234
- return false;
235
- }
236
- if (!(await isCrcOk(archivePath, etag))) {
237
- const msg = `The checksum for the downloaded chromedriver '${driverKey}' did not match`;
238
- if (isStrict) {
239
- throw new Error(msg);
240
- }
241
- log.error(msg);
242
- return false;
243
- }
244
- const fileName = `${_path.default.parse(url).name}_v${version}` + (_support.system.isWindows() ? '.exe' : '');
245
- const targetPath = _path.default.resolve(this.chromedriverDir, fileName);
246
- try {
247
- await this.unzipDriver(archivePath, targetPath);
248
- await _support.fs.chmod(targetPath, 0o755);
249
- log.debug(`Permissions of the file '${targetPath}' have been changed to 755`);
250
- } catch (e) {
251
- if (isStrict) {
252
- throw e;
253
- }
254
- log.error(e.message);
255
- return false;
132
+ /**
133
+ * Parses chromedriver storage XML and stores
134
+ * the parsed results into `this.mapping`
135
+ *
136
+ * @param {Document} doc - The DOM representation
137
+ * of the chromedriver storage XML
138
+ * @param {boolean} shouldParseNotes [true] - If set to `true`
139
+ * then additional drivers information is going to be parsed
140
+ * and assigned to `this.mapping`
141
+ */
142
+ async parseStorageXml(doc, shouldParseNotes = true) {
143
+ const driverNodes = /** @type {Array<Node|Attr>} */ (xpath_1.default.select(`//*[local-name(.)='Contents']`, doc));
144
+ log.debug(`Parsed ${driverNodes.length} entries from storage XML`);
145
+ if (lodash_1.default.isEmpty(driverNodes)) {
146
+ return;
147
+ }
148
+ const promises = [];
149
+ for (const driverNode of driverNodes) {
150
+ const k = extractNodeText(findChildNode(driverNode, 'Key'));
151
+ if (!lodash_1.default.includes(k, '/chromedriver_')) {
152
+ continue;
153
+ }
154
+ const key = String(k);
155
+ const etag = extractNodeText(findChildNode(driverNode, 'ETag'));
156
+ if (!etag) {
157
+ log.debug(`The entry '${key}' does not contain the checksum. Skipping it`);
158
+ continue;
159
+ }
160
+ /** @type {ChromedriverDetails} */
161
+ const cdInfo = {
162
+ url: `${utils_1.CD_CDN}/${key}`,
163
+ etag: lodash_1.default.trim(etag, '"'),
164
+ version: /** @type {string} */ (lodash_1.default.first(key.split('/'))),
165
+ minBrowserVersion: null,
166
+ };
167
+ this.mapping[key] = cdInfo;
168
+ const notesPath = `${cdInfo.version}/notes.txt`;
169
+ const isNotesPresent = !!driverNodes.reduce((acc, node) => Boolean(acc || findChildNode(node, 'Key', notesPath)), false);
170
+ if (!isNotesPresent) {
171
+ cdInfo.minBrowserVersion = null;
172
+ if (shouldParseNotes) {
173
+ log.info(`The entry '${key}' does not contain any notes. Skipping it`);
174
+ }
175
+ continue;
176
+ }
177
+ else if (!shouldParseNotes) {
178
+ continue;
179
+ }
180
+ promises.push(this.retrieveAdditionalDriverInfo(key, `${utils_1.CD_CDN}/${notesPath}`, cdInfo));
181
+ if (promises.length % MAX_PARALLEL_DOWNLOADS === 0) {
182
+ await bluebird_1.default.all(promises);
183
+ }
184
+ }
185
+ await bluebird_1.default.all(promises);
186
+ log.info(`The total count of entries in the mapping: ${lodash_1.default.size(this.mapping)}`);
256
187
  }
257
- return true;
258
- }
259
- async syncDrivers(opts = {}) {
260
- if (_lodash.default.isEmpty(this.mapping)) {
261
- await this.retrieveMapping(!!opts.minBrowserVersion);
188
+ /**
189
+ * Retrieves chromedriver mapping from the storage
190
+ *
191
+ * @param {boolean} shouldParseNotes [true] - if set to `true`
192
+ * then additional chromedrivers info is going to be retrieved and
193
+ * parsed from release notes
194
+ * @returns {Promise<ChromedriverDetailsMapping>}
195
+ */
196
+ async retrieveMapping(shouldParseNotes = true) {
197
+ const xml = await (0, utils_1.retrieveData)(utils_1.CD_CDN, {
198
+ 'user-agent': 'appium',
199
+ accept: 'application/xml, */*',
200
+ }, { timeout: this.timeout });
201
+ const doc = new xmldom_1.DOMParser().parseFromString(xml);
202
+ await this.parseStorageXml(doc, shouldParseNotes);
203
+ return lodash_1.default.cloneDeep(this.mapping);
262
204
  }
263
- if (_lodash.default.isEmpty(this.mapping)) {
264
- throw new Error('Cannot retrieve chromedrivers mapping from Google storage');
205
+ /**
206
+ * Extracts downloaded chromedriver archive
207
+ * into the given destination
208
+ *
209
+ * @param {string} src - The source archive path
210
+ * @param {string} dst - The destination chromedriver path
211
+ */
212
+ async unzipDriver(src, dst) {
213
+ const tmpRoot = await support_1.tempDir.openDir();
214
+ try {
215
+ await support_1.zip.extractAllTo(src, tmpRoot);
216
+ const chromedriverPath = await support_1.fs.walkDir(tmpRoot, true, (itemPath, isDirectory) => !isDirectory && lodash_1.default.toLower(path_1.default.parse(itemPath).name) === 'chromedriver');
217
+ if (!chromedriverPath) {
218
+ throw new Error('The archive was unzipped properly, but we could not find any chromedriver executable');
219
+ }
220
+ log.debug(`Moving the extracted '${path_1.default.basename(chromedriverPath)}' to '${dst}'`);
221
+ await support_1.fs.mv(chromedriverPath, dst, {
222
+ mkdirp: true,
223
+ });
224
+ }
225
+ finally {
226
+ await support_1.fs.rimraf(tmpRoot);
227
+ }
265
228
  }
266
- const driversToSync = this.selectMatchingDrivers(opts.osInfo ?? (await (0, _utils.getOsInfo)()), opts);
267
- if (_lodash.default.isEmpty(driversToSync)) {
268
- log.debug(`There are no drivers to sync. Exiting`);
269
- return [];
229
+ /**
230
+ * Filters `this.mapping` to only select matching
231
+ * chromedriver entries by operating system information
232
+ * and/or additional synchronization options (if provided)
233
+ *
234
+ * @param {OSInfo} osInfo
235
+ * @param {SyncOptions} opts
236
+ * @returns {Array<String>} The list of filtered chromedriver
237
+ * entry names (version/archive name)
238
+ */
239
+ selectMatchingDrivers(osInfo, opts = {}) {
240
+ const { minBrowserVersion, versions = [] } = opts;
241
+ let driversToSync = lodash_1.default.keys(this.mapping);
242
+ if (!lodash_1.default.isEmpty(versions)) {
243
+ // Handle only selected versions if requested
244
+ log.debug(`Selecting chromedrivers whose versions match to ${versions}`);
245
+ driversToSync = driversToSync.filter((cdName) => versions.includes(`${this.mapping[cdName].version}`));
246
+ log.debug(`Got ${support_1.util.pluralize('item', driversToSync.length, true)}`);
247
+ if (lodash_1.default.isEmpty(driversToSync)) {
248
+ return [];
249
+ }
250
+ }
251
+ if (lodash_1.default.isString(minBrowserVersion) && !Number.isNaN(minBrowserVersion)) {
252
+ // Only select drivers that support the current browser whose major version number equals to `minBrowserVersion`
253
+ const minBrowserVersionInt = parseInt(minBrowserVersion, 10);
254
+ log.debug(`Selecting chromedrivers whose minimum supported browser version matches to ${minBrowserVersionInt}`);
255
+ let closestMatchedVersionNumber = 0;
256
+ // Select the newest available and compatible chromedriver
257
+ for (const cdName of driversToSync) {
258
+ const currentMinBrowserVersion = parseInt(String(this.mapping[cdName].minBrowserVersion), 10);
259
+ if (!Number.isNaN(currentMinBrowserVersion) &&
260
+ currentMinBrowserVersion <= minBrowserVersionInt &&
261
+ closestMatchedVersionNumber < currentMinBrowserVersion) {
262
+ closestMatchedVersionNumber = currentMinBrowserVersion;
263
+ }
264
+ }
265
+ driversToSync = driversToSync.filter((cdName) => `${this.mapping[cdName].minBrowserVersion}` ===
266
+ `${closestMatchedVersionNumber > 0 ? closestMatchedVersionNumber : minBrowserVersionInt}`);
267
+ log.debug(`Got ${support_1.util.pluralize('item', driversToSync.length, true)}`);
268
+ if (lodash_1.default.isEmpty(driversToSync)) {
269
+ return [];
270
+ }
271
+ log.debug(`Will select candidate ${support_1.util.pluralize('driver', driversToSync.length)} ` +
272
+ `versioned as '${lodash_1.default.uniq(driversToSync.map((cdName) => this.mapping[cdName].version))}'`);
273
+ }
274
+ if (!lodash_1.default.isEmpty(osInfo)) {
275
+ // Filter out drivers for unsupported system architectures
276
+ let { name, arch } = osInfo;
277
+ if (arch === utils_1.X64 && !driversToSync.some((cdName) => cdName.includes(`_${name}${utils_1.X64}`))) {
278
+ // Fall back to x86 build if x64 one is not available for the given OS
279
+ arch = utils_1.X86;
280
+ }
281
+ // https://stackoverflow.com/questions/65146751/detecting-apple-silicon-mac-in-javascript
282
+ if (name === utils_1.OS.mac && lodash_1.default.includes(lodash_1.default.toLower(os_1.default.cpus()[0].model), 'apple')) {
283
+ for (const armSuffix of utils_1.APPLE_ARM_SUFFIXES) {
284
+ if (driversToSync.some((cdName) => cdName.includes(armSuffix))) {
285
+ // prefer executable for ARM arch if present
286
+ arch = armSuffix;
287
+ break;
288
+ }
289
+ }
290
+ }
291
+ log.debug(`Selecting chromedrivers whose platform matches to ${name}${arch}`);
292
+ const platformRe = new RegExp(`(\\b|_)${name}${arch}\\b`);
293
+ driversToSync = driversToSync.filter((cdName) => platformRe.test(cdName));
294
+ log.debug(`Got ${support_1.util.pluralize('item', driversToSync.length, true)}`);
295
+ }
296
+ return driversToSync;
270
297
  }
271
- log.debug(`Got ${_support.util.pluralize('driver', driversToSync.length, true)} to sync: ` + JSON.stringify(driversToSync, null, 2));
272
- const synchronizedDrivers = [];
273
- const promises = [];
274
- const archivesRoot = await _support.tempDir.openDir();
275
- try {
276
- for (const [idx, driverKey] of driversToSync.entries()) {
277
- promises.push((async () => {
278
- if (await this.retrieveDriver(idx, driverKey, archivesRoot, !_lodash.default.isEmpty(opts))) {
279
- synchronizedDrivers.push(driverKey);
280
- }
281
- })());
282
- if (promises.length % MAX_PARALLEL_DOWNLOADS === 0) {
283
- await _bluebird.default.all(promises);
298
+ /**
299
+ * Retrieves the given chromedriver from the storage
300
+ * and unpacks it into `this.chromedriverDir` folder
301
+ *
302
+ * @param {number} index - The unique driver index
303
+ * @param {string} driverKey - The driver key in `this.mapping`
304
+ * @param {string} archivesRoot - The temporary folder path to extract
305
+ * downloaded archives to
306
+ * @param {boolean} isStrict [true] - Whether to throw an error (`true`)
307
+ * or return a boolean result if the driver retrieval process fails
308
+ * @throws {Error} if there was a failure while retrieving the driver
309
+ * and `isStrict` is set to `true`
310
+ * @returns {Promise<boolean>} if `true` then the chromedriver is successfully
311
+ * downloaded and extracted.
312
+ */
313
+ async retrieveDriver(index, driverKey, archivesRoot, isStrict = false) {
314
+ const { url, etag, version } = this.mapping[driverKey];
315
+ const archivePath = path_1.default.resolve(archivesRoot, `${index}.zip`);
316
+ log.debug(`Retrieving '${url}' to '${archivePath}'`);
317
+ try {
318
+ await support_1.net.downloadFile(url, archivePath, {
319
+ isMetered: false,
320
+ timeout: TIMEOUT_MS,
321
+ });
322
+ }
323
+ catch (e) {
324
+ const err = /** @type {Error} */ (e);
325
+ const msg = `Cannot download chromedriver archive. Original error: ${err.message}`;
326
+ if (isStrict) {
327
+ throw new Error(msg);
328
+ }
329
+ log.error(msg);
330
+ return false;
331
+ }
332
+ if (!(await isCrcOk(archivePath, etag))) {
333
+ const msg = `The checksum for the downloaded chromedriver '${driverKey}' did not match`;
334
+ if (isStrict) {
335
+ throw new Error(msg);
336
+ }
337
+ log.error(msg);
338
+ return false;
339
+ }
340
+ const fileName = `${path_1.default.parse(url).name}_v${version}` + (support_1.system.isWindows() ? '.exe' : '');
341
+ const targetPath = path_1.default.resolve(this.chromedriverDir, fileName);
342
+ try {
343
+ await this.unzipDriver(archivePath, targetPath);
344
+ await support_1.fs.chmod(targetPath, 0o755);
345
+ log.debug(`Permissions of the file '${targetPath}' have been changed to 755`);
284
346
  }
285
- }
286
- await _bluebird.default.all(promises);
287
- } finally {
288
- await _support.fs.rimraf(archivesRoot);
347
+ catch (e) {
348
+ const err = /** @type {Error} */ (e);
349
+ if (isStrict) {
350
+ throw err;
351
+ }
352
+ log.error(err.message);
353
+ return false;
354
+ }
355
+ return true;
289
356
  }
290
- if (!_lodash.default.isEmpty(synchronizedDrivers)) {
291
- log.info(`Successfully synchronized ` + `${_support.util.pluralize('chromedriver', synchronizedDrivers.length, true)}`);
292
- } else {
293
- log.info(`No chromedrivers were synchronized`);
357
+ /**
358
+ * Retrieves chromedrivers from the remote storage
359
+ * to the local file system
360
+ *
361
+ * @param {SyncOptions} opts
362
+ * @throws {Error} if there was a problem while retrieving
363
+ * the drivers
364
+ * @returns {Promise<string[]>} The list of successfully synchronized driver keys
365
+ */
366
+ async syncDrivers(opts = {}) {
367
+ if (lodash_1.default.isEmpty(this.mapping)) {
368
+ await this.retrieveMapping(!!opts.minBrowserVersion);
369
+ }
370
+ if (lodash_1.default.isEmpty(this.mapping)) {
371
+ throw new Error('Cannot retrieve chromedrivers mapping from Google storage');
372
+ }
373
+ const driversToSync = this.selectMatchingDrivers(opts.osInfo ?? (await (0, utils_1.getOsInfo)()), opts);
374
+ if (lodash_1.default.isEmpty(driversToSync)) {
375
+ log.debug(`There are no drivers to sync. Exiting`);
376
+ return [];
377
+ }
378
+ log.debug(`Got ${support_1.util.pluralize('driver', driversToSync.length, true)} to sync: ` +
379
+ JSON.stringify(driversToSync, null, 2));
380
+ /**
381
+ * @type {string[]}
382
+ */
383
+ const synchronizedDrivers = [];
384
+ const promises = [];
385
+ const archivesRoot = await support_1.tempDir.openDir();
386
+ try {
387
+ for (const [idx, driverKey] of driversToSync.entries()) {
388
+ promises.push((async () => {
389
+ if (await this.retrieveDriver(idx, driverKey, archivesRoot, !lodash_1.default.isEmpty(opts))) {
390
+ synchronizedDrivers.push(driverKey);
391
+ }
392
+ })());
393
+ if (promises.length % MAX_PARALLEL_DOWNLOADS === 0) {
394
+ await bluebird_1.default.all(promises);
395
+ }
396
+ }
397
+ await bluebird_1.default.all(promises);
398
+ }
399
+ finally {
400
+ await support_1.fs.rimraf(archivesRoot);
401
+ }
402
+ if (!lodash_1.default.isEmpty(synchronizedDrivers)) {
403
+ log.info(`Successfully synchronized ` +
404
+ `${support_1.util.pluralize('chromedriver', synchronizedDrivers.length, true)}`);
405
+ }
406
+ else {
407
+ log.info(`No chromedrivers were synchronized`);
408
+ }
409
+ return synchronizedDrivers;
294
410
  }
295
- return synchronizedDrivers;
296
- }
297
411
  }
298
- var _default = ChromedriverStorageClient;
299
- exports.default = _default;
300
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJUSU1FT1VUX01TIiwiTUFYX1BBUkFMTEVMX0RPV05MT0FEUyIsImxvZyIsImxvZ2dlciIsImdldExvZ2dlciIsImlzQ3JjT2siLCJzcmMiLCJjaGVja3N1bSIsIm1kNSIsImZzIiwiaGFzaCIsIl8iLCJ0b0xvd2VyIiwiZmluZENoaWxkTm9kZSIsInBhcmVudCIsImNoaWxkTmFtZSIsInRleHQiLCJoYXNDaGlsZE5vZGVzIiwiY2hpbGROb2RlSWR4IiwiY2hpbGROb2RlcyIsImxlbmd0aCIsImNoaWxkTm9kZSIsImxvY2FsTmFtZSIsImNoaWxkVGV4dCIsImV4dHJhY3ROb2RlVGV4dCIsIm5vZGUiLCJmaXJzdENoaWxkIiwidXRpbCIsImhhc1ZhbHVlIiwibm9kZVZhbHVlIiwiQ2hyb21lZHJpdmVyU3RvcmFnZUNsaWVudCIsImNvbnN0cnVjdG9yIiwiYXJncyIsImNocm9tZWRyaXZlckRpciIsImdldENocm9tZWRyaXZlckRpciIsInRpbWVvdXQiLCJtYXBwaW5nIiwicGFyc2VOb3RlcyIsImNvbnRlbnQiLCJyZXN1bHQiLCJ2ZXJzaW9uTWF0Y2giLCJleGVjIiwidmVyc2lvbiIsIm1pbkJyb3dzZXJWZXJzaW9uTWF0Y2giLCJtaW5Ccm93c2VyVmVyc2lvbiIsInJldHJpZXZlQWRkaXRpb25hbERyaXZlckluZm8iLCJkcml2ZXJLZXkiLCJub3Rlc1VybCIsImluZm9EaWN0Iiwibm90ZXMiLCJyZXRyaWV2ZURhdGEiLCJhY2NlcHQiLCJkZWJ1ZyIsInBhcnNlU3RvcmFnZVhtbCIsImRvYyIsInNob3VsZFBhcnNlTm90ZXMiLCJkcml2ZXJOb2RlcyIsInhwYXRoIiwic2VsZWN0IiwiaXNFbXB0eSIsInByb21pc2VzIiwiZHJpdmVyTm9kZSIsImtleSIsImluY2x1ZGVzIiwiZXRhZyIsImNkSW5mbyIsInVybCIsIkNEX0NETiIsInRyaW0iLCJmaXJzdCIsInNwbGl0Iiwibm90ZXNQYXRoIiwiaXNOb3Rlc1ByZXNlbnQiLCJyZWR1Y2UiLCJhY2MiLCJpbmZvIiwicHVzaCIsIkIiLCJhbGwiLCJzaXplIiwicmV0cmlldmVNYXBwaW5nIiwieG1sIiwiRE9NUGFyc2VyIiwicGFyc2VGcm9tU3RyaW5nIiwiY2xvbmVEZWVwIiwidW56aXBEcml2ZXIiLCJkc3QiLCJ0bXBSb290IiwidGVtcERpciIsIm9wZW5EaXIiLCJ6aXAiLCJleHRyYWN0QWxsVG8iLCJjaHJvbWVkcml2ZXJQYXRoIiwid2Fsa0RpciIsIml0ZW1QYXRoIiwiaXNEaXJlY3RvcnkiLCJwYXRoIiwicGFyc2UiLCJuYW1lIiwiRXJyb3IiLCJiYXNlbmFtZSIsIm12IiwibWtkaXJwIiwicmltcmFmIiwic2VsZWN0TWF0Y2hpbmdEcml2ZXJzIiwib3NJbmZvIiwib3B0cyIsInZlcnNpb25zIiwiZHJpdmVyc1RvU3luYyIsImtleXMiLCJmaWx0ZXIiLCJjZE5hbWUiLCJwbHVyYWxpemUiLCJpc05hTiIsIm1pbkJyb3dzZXJWZXJzaW9uSW50IiwicGFyc2VJbnQiLCJjbG9zZXN0TWF0Y2hlZFZlcnNpb25OdW1iZXIiLCJjdXJyZW50TWluQnJvd3NlclZlcnNpb24iLCJ1bmlxIiwibWFwIiwiYXJjaCIsIlg2NCIsInNvbWUiLCJYODYiLCJPUyIsIm1hYyIsIm9zIiwiY3B1cyIsIm1vZGVsIiwiYXJtU3VmZml4IiwiQVBQTEVfQVJNX1NVRkZJWEVTIiwicGxhdGZvcm1SZSIsIlJlZ0V4cCIsInRlc3QiLCJyZXRyaWV2ZURyaXZlciIsImluZGV4IiwiYXJjaGl2ZXNSb290IiwiaXNTdHJpY3QiLCJhcmNoaXZlUGF0aCIsInJlc29sdmUiLCJuZXQiLCJkb3dubG9hZEZpbGUiLCJpc01ldGVyZWQiLCJlIiwibXNnIiwibWVzc2FnZSIsImVycm9yIiwiZmlsZU5hbWUiLCJzeXN0ZW0iLCJpc1dpbmRvd3MiLCJ0YXJnZXRQYXRoIiwiY2htb2QiLCJzeW5jRHJpdmVycyIsImdldE9zSW5mbyIsIkpTT04iLCJzdHJpbmdpZnkiLCJzeW5jaHJvbml6ZWREcml2ZXJzIiwiaWR4IiwiZW50cmllcyJdLCJzb3VyY2VzIjpbIi4uLy4uL2xpYi9zdG9yYWdlLWNsaWVudC5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBnZXRDaHJvbWVkcml2ZXJEaXIsIENEX0NETiwgcmV0cmlldmVEYXRhLCBnZXRPc0luZm8sXG4gIE9TLCBYNjQsIFg4NiwgQVBQTEVfQVJNX1NVRkZJWEVTLFxufSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgeHBhdGggZnJvbSAneHBhdGgnO1xuaW1wb3J0IHsgRE9NUGFyc2VyIH0gZnJvbSAnQHhtbGRvbS94bWxkb20nO1xuaW1wb3J0IEIgZnJvbSAnYmx1ZWJpcmQnO1xuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgb3MgZnJvbSAnb3MnO1xuaW1wb3J0IHsgc3lzdGVtLCBmcywgbG9nZ2VyLCB0ZW1wRGlyLCB6aXAsIHV0aWwsIG5ldCB9IGZyb20gJ0BhcHBpdW0vc3VwcG9ydCc7XG5cblxuY29uc3QgVElNRU9VVF9NUyA9IDE1MDAwO1xuY29uc3QgTUFYX1BBUkFMTEVMX0RPV05MT0FEUyA9IDU7XG5cbmNvbnN0IGxvZyA9IGxvZ2dlci5nZXRMb2dnZXIoJ0Nocm9tZWRyaXZlclN0b3JhZ2VDbGllbnQnKTtcblxuXG5hc3luYyBmdW5jdGlvbiBpc0NyY09rIChzcmMsIGNoZWNrc3VtKSB7XG4gIGNvbnN0IG1kNSA9IGF3YWl0IGZzLmhhc2goc3JjLCAnbWQ1Jyk7XG4gIHJldHVybiBfLnRvTG93ZXIobWQ1KSA9PT0gXy50b0xvd2VyKGNoZWNrc3VtKTtcbn1cblxuZnVuY3Rpb24gZmluZENoaWxkTm9kZSAocGFyZW50LCBjaGlsZE5hbWUgPSBudWxsLCB0ZXh0ID0gbnVsbCkge1xuICBpZiAoIWNoaWxkTmFtZSAmJiAhdGV4dCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGlmICghcGFyZW50Lmhhc0NoaWxkTm9kZXMoKSkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgZm9yIChsZXQgY2hpbGROb2RlSWR4ID0gMDsgY2hpbGROb2RlSWR4IDwgcGFyZW50LmNoaWxkTm9kZXMubGVuZ3RoOyBjaGlsZE5vZGVJZHgrKykge1xuICAgIGNvbnN0IGNoaWxkTm9kZSA9IHBhcmVudC5jaGlsZE5vZGVzW2NoaWxkTm9kZUlkeF07XG4gICAgaWYgKGNoaWxkTmFtZSAmJiAhdGV4dCAmJiBjaGlsZE5hbWUgPT09IGNoaWxkTm9kZS5sb2NhbE5hbWUpIHtcbiAgICAgIHJldHVybiBjaGlsZE5vZGU7XG4gICAgfVxuICAgIGlmICh0ZXh0KSB7XG4gICAgICBjb25zdCBjaGlsZFRleHQgPSBleHRyYWN0Tm9kZVRleHQoY2hpbGROb2RlKTtcbiAgICAgIGlmICghY2hpbGRUZXh0KSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuICAgICAgaWYgKGNoaWxkTmFtZSAmJiBjaGlsZE5hbWUgPT09IGNoaWxkTm9kZS5sb2NhbE5hbWUgJiYgdGV4dCA9PT0gY2hpbGRUZXh0KSB7XG4gICAgICAgIHJldHVybiBjaGlsZE5vZGU7XG4gICAgICB9XG4gICAgICBpZiAoIWNoaWxkTmFtZSAmJiB0ZXh0ID09PSBjaGlsZFRleHQpIHtcbiAgICAgICAgcmV0dXJuIGNoaWxkTm9kZTtcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbmZ1bmN0aW9uIGV4dHJhY3ROb2RlVGV4dCAobm9kZSkge1xuICByZXR1cm4gKCFub2RlIHx8ICFub2RlLmZpcnN0Q2hpbGQgfHwgIXV0aWwuaGFzVmFsdWUobm9kZS5maXJzdENoaWxkLm5vZGVWYWx1ZSkpXG4gICAgPyBudWxsXG4gICAgOiBub2RlLmZpcnN0Q2hpbGQubm9kZVZhbHVlO1xufVxuXG5cbmNsYXNzIENocm9tZWRyaXZlclN0b3JhZ2VDbGllbnQge1xuICBjb25zdHJ1Y3RvciAoYXJncyA9IHt9KSB7XG4gICAgY29uc3Qge1xuICAgICAgY2hyb21lZHJpdmVyRGlyID0gZ2V0Q2hyb21lZHJpdmVyRGlyKCksXG4gICAgICB0aW1lb3V0ID0gVElNRU9VVF9NUyxcbiAgICB9ID0gYXJncztcbiAgICB0aGlzLmNocm9tZWRyaXZlckRpciA9IGNocm9tZWRyaXZlckRpcjtcbiAgICB0aGlzLnRpbWVvdXQgPSB0aW1lb3V0O1xuICAgIHRoaXMubWFwcGluZyA9IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IEFkZGl0aW9uYWxEcml2ZXJEZXRhaWxzXG4gICAqIEBwcm9wZXJ0eSB7P3N0cmluZ30gdmVyc2lvbiAtIENocm9tZWRyaXZlciB2ZXJzaW9uXG4gICAqIG9yIGBudWxsYCBpZiBpdCBjYW5ub3QgYmUgZm91bmRcbiAgICogQHByb3BlcnR5IHs/c3RyaW5nfSBtaW5Ccm93c2VyVmVyc2lvbiAtIFRoZSBtaW5pbXVtIGJyb3dzZXIgdmVyc2lvblxuICAgKiBzdXBwb3J0ZWQgYnkgY2hyb21lZHJpdmVyIG9yIGBudWxsYCBpZiBpdCBjYW5ub3QgYmUgZm91bmRcbiAgICovXG5cbiAgLyoqXG4gICAqIEdldHMgYWRkaXRpb25hbCBjaHJvbWVkcml2ZXIgZGV0YWlscyBmcm9tIGNocm9tZWRyaXZlclxuICAgKiByZWxlYXNlIG5vdGVzXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjb250ZW50IC0gUmVsZWFzZSBub3RlcyBvZiB0aGUgY29ycmVzcG9uZGluZyBjaHJvbWVkcml2ZXJcbiAgICogQHJldHVybnMge0FkZGl0aW9uYWxEcml2ZXJEZXRhaWxzfVxuICAgKi9cbiAgcGFyc2VOb3RlcyAoY29udGVudCkge1xuICAgIGNvbnN0IHJlc3VsdCA9IHt9O1xuICAgIGNvbnN0IHZlcnNpb25NYXRjaCA9IC9eXFxzKlstXStDaHJvbWVEcml2ZXJbXFxEXSsoW1xcZC5dKykvaW0uZXhlYyhjb250ZW50KTtcbiAgICBpZiAodmVyc2lvbk1hdGNoKSB7XG4gICAgICByZXN1bHQudmVyc2lvbiA9IHZlcnNpb25NYXRjaFsxXTtcbiAgICB9XG4gICAgY29uc3QgbWluQnJvd3NlclZlcnNpb25NYXRjaCA9IC9eXFxzKlN1cHBvcnRzIENocm9tZVtcXERdKyhcXGQrKS9pbS5leGVjKGNvbnRlbnQpO1xuICAgIGlmIChtaW5Ccm93c2VyVmVyc2lvbk1hdGNoKSB7XG4gICAgICByZXN1bHQubWluQnJvd3NlclZlcnNpb24gPSBtaW5Ccm93c2VyVmVyc2lvbk1hdGNoWzFdO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG5cbiAgLyoqXG4gICAqIERvd25sb2FkcyBjaHJvbWVkcml2ZXIgcmVsZWFzZSBub3RlcyBhbmQgcHV0cyB0aGVtXG4gICAqIGludG8gdGhlIGRpY3Rpb25hcnkgYXJndW1lbnRcbiAgICpcbiAgICogQHBhcmFtIHtzdHJpbmd9IGRyaXZlcktleSAtIERyaXZlciB2ZXJzaW9uIHBsdXMgYXJjaGl2ZSBuYW1lXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBub3Rlc1VybCAtIFRoZSBVUkwgb2YgY2hyb21lZHJpdmVyIG5vdGVzXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBpbmZvRGljdCAtIFRoZSBkaWN0aW9uYXJ5IGNvbnRhaW5pbmcgZHJpdmVyIGluZm8uXG4gICAqIFRoZSBtZXRob2QgY2FsbCBtdXRhdGVzIGJ5IG1lcmdpbmcgYEFkZGl0aW9uYWxEcml2ZXJEZXRhaWxzYFxuICAgKiBAdGhyb3dzIHtFcnJvcn0gaWYgdGhlIHJlbGVhc2Ugbm90ZXMgY2Fubm90IGJlIGRvd25sb2FkZWRcbiAgICovXG4gIGFzeW5jIHJldHJpZXZlQWRkaXRpb25hbERyaXZlckluZm8gKGRyaXZlcktleSwgbm90ZXNVcmwsIGluZm9EaWN0KSB7XG4gICAgY29uc3Qgbm90ZXMgPSBhd2FpdCByZXRyaWV2ZURhdGEobm90ZXNVcmwsIHtcbiAgICAgICd1c2VyLWFnZW50JzogJ2FwcGl1bScsXG4gICAgICBhY2NlcHQ6ICcqLyonLFxuICAgIH0sIHsgdGltZW91dDogdGhpcy50aW1lb3V0IH0pO1xuICAgIGNvbnN0IHsgbWluQnJvd3NlclZlcnNpb24gfSA9IHRoaXMucGFyc2VOb3Rlcyhub3Rlcyk7XG4gICAgaWYgKCFtaW5Ccm93c2VyVmVyc2lvbikge1xuICAgICAgbG9nLmRlYnVnKGBUaGUgZHJpdmVyICcke2RyaXZlcktleX0nIGRvZXMgbm90IGNvbnRhaW4gdmFsaWQgcmVsZWFzZSBub3RlcyBhdCAke25vdGVzVXJsfS4gYCArXG4gICAgICAgIGBTa2lwcGluZyBpdGApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBpbmZvRGljdC5taW5Ccm93c2VyVmVyc2lvbiA9IG1pbkJyb3dzZXJWZXJzaW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIFBhcnNlcyBjaHJvbWVkcml2ZXIgc3RvcmFnZSBYTUwgYW5kIHN0b3Jlc1xuICAgKiB0aGUgcGFyc2VkIHJlc3VsdHMgaW50byBgdGhpcy5tYXBwaW5nYFxuICAgKlxuICAgKiBAcGFyYW0ge0RPTURvY3VtZW50fSBkb2MgLSBUaGUgRE9NIHJlcHJlc2VudGF0aW9uXG4gICAqIG9mIHRoZSBjaHJvbWVkcml2ZXIgc3RvcmFnZSBYTUxcbiAgICogQHBhcmFtIHtib29sZWFufSBzaG91bGRQYXJzZU5vdGVzIFt0cnVlXSAtIElmIHNldCB0byBgdHJ1ZWBcbiAgICogdGhlbiBhZGRpdGlvbmFsIGRyaXZlcnMgaW5mb3JtYXRpb24gaXMgZ29pbmcgdG8gYmUgcGFyc2VkXG4gICAqIGFuZCBhc3NpZ25lZCB0byBgdGhpcy5tYXBwaW5nYFxuICAgKi9cbiAgYXN5bmMgcGFyc2VTdG9yYWdlWG1sIChkb2MsIHNob3VsZFBhcnNlTm90ZXMgPSB0cnVlKSB7XG4gICAgY29uc3QgZHJpdmVyTm9kZXMgPSB4cGF0aC5zZWxlY3QoYC8vKltsb2NhbC1uYW1lKC4pPSdDb250ZW50cyddYCwgZG9jKTtcbiAgICBsb2cuZGVidWcoYFBhcnNlZCAke2RyaXZlck5vZGVzLmxlbmd0aH0gZW50cmllcyBmcm9tIHN0b3JhZ2UgWE1MYCk7XG4gICAgaWYgKF8uaXNFbXB0eShkcml2ZXJOb2RlcykpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBwcm9taXNlcyA9IFtdO1xuICAgIGZvciAoY29uc3QgZHJpdmVyTm9kZSBvZiBkcml2ZXJOb2Rlcykge1xuICAgICAgY29uc3Qga2V5ID0gZXh0cmFjdE5vZGVUZXh0KGZpbmRDaGlsZE5vZGUoZHJpdmVyTm9kZSwgJ0tleScpKTtcbiAgICAgIGlmICghXy5pbmNsdWRlcyhrZXksICcvY2hyb21lZHJpdmVyXycpKSB7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBldGFnID0gZXh0cmFjdE5vZGVUZXh0KGZpbmRDaGlsZE5vZGUoZHJpdmVyTm9kZSwgJ0VUYWcnKSk7XG4gICAgICBpZiAoIWV0YWcpIHtcbiAgICAgICAgbG9nLmRlYnVnKGBUaGUgZW50cnkgJyR7a2V5fScgZG9lcyBub3QgY29udGFpbiB0aGUgY2hlY2tzdW0uIFNraXBwaW5nIGl0YCk7XG4gICAgICAgIGNvbnRpbnVlO1xuICAgICAgfVxuXG4gICAgICBjb25zdCBjZEluZm8gPSB7XG4gICAgICAgIHVybDogYCR7Q0RfQ0ROfS8ke2tleX1gLFxuICAgICAgICBldGFnOiBfLnRyaW0oZXRhZywgJ1wiJyksXG4gICAgICAgIHZlcnNpb246IF8uZmlyc3Qoa2V5LnNwbGl0KCcvJykpLFxuICAgICAgfTtcbiAgICAgIHRoaXMubWFwcGluZ1trZXldID0gY2RJbmZvO1xuXG4gICAgICBjb25zdCBub3Rlc1BhdGggPSBgJHtjZEluZm8udmVyc2lvbn0vbm90ZXMudHh0YDtcbiAgICAgIGNvbnN0IGlzTm90ZXNQcmVzZW50ID0gISFkcml2ZXJOb2Rlc1xuICAgICAgICAucmVkdWNlKChhY2MsIG5vZGUpID0+IGFjYyB8fCBmaW5kQ2hpbGROb2RlKG5vZGUsICdLZXknLCBub3Rlc1BhdGgpLCBmYWxzZSk7XG4gICAgICBpZiAoIWlzTm90ZXNQcmVzZW50KSB7XG4gICAgICAgIGNkSW5mby5taW5Ccm93c2VyVmVyc2lvbiA9IG51bGw7XG4gICAgICAgIGlmIChzaG91bGRQYXJzZU5vdGVzKSB7XG4gICAgICAgICAgbG9nLmluZm8oYFRoZSBlbnRyeSAnJHtrZXl9JyBkb2VzIG5vdCBjb250YWluIGFueSBub3Rlcy4gU2tpcHBpbmcgaXRgKTtcbiAgICAgICAgfVxuICAgICAgICBjb250aW51ZTtcbiAgICAgIH0gZWxzZSBpZiAoIXNob3VsZFBhcnNlTm90ZXMpIHtcbiAgICAgICAgY29udGludWU7XG4gICAgICB9XG5cbiAgICAgIHByb21pc2VzLnB1c2godGhpcy5yZXRyaWV2ZUFkZGl0aW9uYWxEcml2ZXJJbmZvKGtleSwgYCR7Q0RfQ0ROfS8ke25vdGVzUGF0aH1gLCBjZEluZm8pKTtcbiAgICAgIGlmIChwcm9taXNlcy5sZW5ndGggJSBNQVhfUEFSQUxMRUxfRE9XTkxPQURTID09PSAwKSB7XG4gICAgICAgIGF3YWl0IEIuYWxsKHByb21pc2VzKTtcbiAgICAgIH1cbiAgICB9XG4gICAgYXdhaXQgQi5hbGwocHJvbWlzZXMpO1xuICAgIGxvZy5pbmZvKGBUaGUgdG90YWwgY291bnQgb2YgZW50cmllcyBpbiB0aGUgbWFwcGluZzogJHtfLnNpemUodGhpcy5tYXBwaW5nKX1gKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBEcml2ZXJEZXRhaWxzXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB1cmwgLSBUaGUgZnVsbCB1cmwgdG8gdGhlIGNvcnJlc3BvbmRpbmcgZHJpdmVyIGluXG4gICAqIHRoZSByZW1vdGUgc3RvcmFnZVxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gZXRhZyAtIFRoZSBDUkMgb2YgdGhlIGRyaXZlciBhcmNoaXZlXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB2ZXJzaW9uIC0gQ2hyb21lZHJpdmVyIHZlcnNpb25cbiAgICovXG5cbiAgLyoqXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IENocm9tZWRyaXZlcnNNYXBwaW5nXG4gICAqIEBwcm9wZXJ0eSB7RHJpdmVyRGV0YWlsc30gLSBUaGUga2V5cyBhcmUgdW5pcXVlIGRyaXZlciBpZGVudGlmaWVyc1xuICAgKiAodmVyc2lvbi9hcmNoaXZlIG5hbWUpLiBUaGUgY29ycmVzcG9uZGluZyB2YWx1ZXMgaGF2ZSBgRHJpdmVyRGV0YWlsc2BcbiAgICogY29udGFpbmluZyBjaHJvbWVkcml2ZXIgZGV0YWlsc1xuICAgKi9cblxuICAvKipcbiAgICogUmV0cmlldmVzIGNocm9tZWRyaXZlciBtYXBwaW5nIGZyb20gdGhlIHN0b3JhZ2VcbiAgICpcbiAgICogQHBhcmFtIHtib29sZWFufSBzaG91bGRQYXJzZU5vdGVzIFt0cnVlXSAtIGlmIHNldCB0byBgdHJ1ZWBcbiAgICogdGhlbiBhZGRpdGlvbmFsIGNocm9tZWRyaXZlcnMgaW5mbyBpcyBnb2luZyB0byBiZSByZXRyaWV2ZWQgYW5kXG4gICAqIHBhcnNlZCBmcm9tIHJlbGVhc2Ugbm90ZXNcbiAgICogQHJldHVybnMge0Nocm9tZWRyaXZlcnNNYXBwaW5nfVxuICAgKi9cbiAgYXN5bmMgcmV0cmlldmVNYXBwaW5nIChzaG91bGRQYXJzZU5vdGVzID0gdHJ1ZSkge1xuICAgIGNvbnN0IHhtbCA9IGF3YWl0IHJldHJpZXZlRGF0YShDRF9DRE4sIHtcbiAgICAgICd1c2VyLWFnZW50JzogJ2FwcGl1bScsXG4gICAgICBhY2NlcHQ6ICdhcHBsaWNhdGlvbi94bWwsICovKicsXG4gICAgfSwgeyB0aW1lb3V0OiB0aGlzLnRpbWVvdXQgfSk7XG4gICAgY29uc3QgZG9jID0gbmV3IERPTVBhcnNlcigpLnBhcnNlRnJvbVN0cmluZyh4bWwpO1xuICAgIGF3YWl0IHRoaXMucGFyc2VTdG9yYWdlWG1sKGRvYywgc2hvdWxkUGFyc2VOb3Rlcyk7XG4gICAgcmV0dXJuIF8uY2xvbmVEZWVwKHRoaXMubWFwcGluZyk7XG4gIH1cblxuICAvKipcbiAgICogRXh0cmFjdHMgZG93bmxvYWRlZCBjaHJvbWVkcml2ZXIgYXJjaGl2ZVxuICAgKiBpbnRvIHRoZSBnaXZlbiBkZXN0aW5hdGlvblxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3JjIC0gVGhlIHNvdXJjZSBhcmNoaXZlIHBhdGhcbiAgICogQHBhcmFtIHtzdHJpbmd9IGRzdCAtIFRoZSBkZXN0aW5hdGlvbiBjaHJvbWVkcml2ZXIgcGF0aFxuICAgKi9cbiAgYXN5bmMgdW56aXBEcml2ZXIgKHNyYywgZHN0KSB7XG4gICAgY29uc3QgdG1wUm9vdCA9IGF3YWl0IHRlbXBEaXIub3BlbkRpcigpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB6aXAuZXh0cmFjdEFsbFRvKHNyYywgdG1wUm9vdCk7XG4gICAgICBjb25zdCBjaHJvbWVkcml2ZXJQYXRoID0gYXdhaXQgZnMud2Fsa0Rpcih0bXBSb290LCB0cnVlLCAoaXRlbVBhdGgsIGlzRGlyZWN0b3J5KSA9PlxuICAgICAgICAhaXNEaXJlY3RvcnkgJiYgXy50b0xvd2VyKHBhdGgucGFyc2UoaXRlbVBhdGgpLm5hbWUpID09PSAnY2hyb21lZHJpdmVyJyk7XG4gICAgICBpZiAoIWNocm9tZWRyaXZlclBhdGgpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKCdUaGUgYXJjaGl2ZSB3YXMgdW56aXBwZWQgcHJvcGVybHksIGJ1dCB3ZSBjb3VsZCBub3QgZmluZCBhbnkgY2hyb21lZHJpdmVyIGV4ZWN1dGFibGUnKTtcbiAgICAgIH1cbiAgICAgIGxvZy5kZWJ1ZyhgTW92aW5nIHRoZSBleHRyYWN0ZWQgJyR7cGF0aC5iYXNlbmFtZShjaHJvbWVkcml2ZXJQYXRoKX0nIHRvICcke2RzdH0nYCk7XG4gICAgICBhd2FpdCBmcy5tdihjaHJvbWVkcml2ZXJQYXRoLCBkc3QsIHtcbiAgICAgICAgbWtkaXJwOiB0cnVlXG4gICAgICB9KTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgZnMucmltcmFmKHRtcFJvb3QpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBPU0luZm9cbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IG5hbWUgLSBUaGUgbmFtZSBvZiB0aGUgaG9zdCBPU1xuICAgKiBDYW4gYmUgZWl0aGVyIGBtYWNgLCBgd2luZG93c2Agb3IgYGxpbnV4YFxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gYXJjaCAtIFRoZSBhcmNoaXRlY3R1cmUgb2YgdGhlIGhvc3QgT1MuXG4gICAqIENhbiBiZSBlaXRoZXIgYDMyYCBvciBgNjRgXG4gICAqL1xuXG4gIC8qKlxuICAgKiBGaWx0ZXJzIGB0aGlzLm1hcHBpbmdgIHRvIG9ubHkgc2VsZWN0IG1hdGNoaW5nXG4gICAqIGNocm9tZWRyaXZlciBlbnRyaWVzIGJ5IG9wZXJhdGluZyBzeXN0ZW0gaW5mb3JtYXRpb25cbiAgICogYW5kL29yIGFkZGl0aW9uYWwgc3luY2hyb25pemF0aW9uIG9wdGlvbnMgKGlmIHByb3ZpZGVkKVxuICAgKlxuICAgKiBAcGFyYW0gez9PU0luZm99IG9zSW5mb1xuICAgKiBAcGFyYW0gez9TeW5jT3B0aW9uc30gb3B0c1xuICAgKiBAcmV0dXJucyB7QXJyYXk8U3RyaW5nPn0gVGhlIGxpc3Qgb2YgZmlsdGVyZWQgY2hyb21lZHJpdmVyXG4gICAqIGVudHJ5IG5hbWVzICh2ZXJzaW9uL2FyY2hpdmUgbmFtZSlcbiAgICovXG4gIHNlbGVjdE1hdGNoaW5nRHJpdmVycyAob3NJbmZvLCBvcHRzID0ge30pIHtcbiAgICBjb25zdCB7XG4gICAgICBtaW5Ccm93c2VyVmVyc2lvbixcbiAgICAgIHZlcnNpb25zID0gW10sXG4gICAgfSA9IG9wdHM7XG4gICAgbGV0IGRyaXZlcnNUb1N5bmMgPSBfLmtleXModGhpcy5tYXBwaW5nKTtcblxuICAgIGlmICghXy5pc0VtcHR5KHZlcnNpb25zKSkge1xuICAgICAgLy8gSGFuZGxlIG9ubHkgc2VsZWN0ZWQgdmVyc2lvbnMgaWYgcmVxdWVzdGVkXG4gICAgICBsb2cuZGVidWcoYFNlbGVjdGluZyBjaHJvbWVkcml2ZXJzIHdob3NlIHZlcnNpb25zIG1hdGNoIHRvICR7dmVyc2lvbnN9YCk7XG4gICAgICBkcml2ZXJzVG9TeW5jID0gZHJpdmVyc1RvU3luY1xuICAgICAgICAuZmlsdGVyKChjZE5hbWUpID0+IHZlcnNpb25zLmluY2x1ZGVzKGAke3RoaXMubWFwcGluZ1tjZE5hbWVdLnZlcnNpb259YCkpO1xuXG4gICAgICBsb2cuZGVidWcoYEdvdCAke3V0aWwucGx1cmFsaXplKCdpdGVtJywgZHJpdmVyc1RvU3luYy5sZW5ndGgsIHRydWUpfWApO1xuICAgICAgaWYgKF8uaXNFbXB0eShkcml2ZXJzVG9TeW5jKSkge1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKCFpc05hTihtaW5Ccm93c2VyVmVyc2lvbikpIHtcbiAgICAgIC8vIE9ubHkgc2VsZWN0IGRyaXZlcnMgdGhhdCBzdXBwb3J0IHRoZSBjdXJyZW50IGJyb3dzZXIgd2hvc2UgbWFqb3IgdmVyc2lvbiBudW1iZXIgZXF1YWxzIHRvIGBtaW5Ccm93c2VyVmVyc2lvbmBcbiAgICAgIGNvbnN0IG1pbkJyb3dzZXJWZXJzaW9uSW50ID0gcGFyc2VJbnQobWluQnJvd3NlclZlcnNpb24sIDEwKTtcbiAgICAgIGxvZy5kZWJ1ZyhgU2VsZWN0aW5nIGNocm9tZWRyaXZlcnMgd2hvc2UgbWluaW11bSBzdXBwb3J0ZWQgYnJvd3NlciB2ZXJzaW9uIG1hdGNoZXMgdG8gJHttaW5Ccm93c2VyVmVyc2lvbkludH1gKTtcbiAgICAgIGxldCBjbG9zZXN0TWF0Y2hlZFZlcnNpb25OdW1iZXIgPSAwO1xuICAgICAgLy8gU2VsZWN0IHRoZSBuZXdlc3QgYXZhaWxhYmxlIGFuZCBjb21wYXRpYmxlIGNocm9tZWRyaXZlclxuICAgICAgZm9yIChjb25zdCBjZE5hbWUgb2YgZHJpdmVyc1RvU3luYykge1xuICAgICAgICBjb25zdCBjdXJyZW50TWluQnJvd3NlclZlcnNpb24gPSBwYXJzZUludCh0aGlzLm1hcHBpbmdbY2ROYW1lXS5taW5Ccm93c2VyVmVyc2lvbiwgMTApO1xuICAgICAgICBpZiAoIWlzTmFOKGN1cnJlbnRNaW5Ccm93c2VyVmVyc2lvbilcbiAgICAgICAgICAgICYmIGN1cnJlbnRNaW5Ccm93c2VyVmVyc2lvbiA8PSBtaW5Ccm93c2VyVmVyc2lvbkludFxuICAgICAgICAgICAgJiYgY2xvc2VzdE1hdGNoZWRWZXJzaW9uTnVtYmVyIDwgY3VycmVudE1pbkJyb3dzZXJWZXJzaW9uKSB7XG4gICAgICAgICAgY2xvc2VzdE1hdGNoZWRWZXJzaW9uTnVtYmVyID0gY3VycmVudE1pbkJyb3dzZXJWZXJzaW9uO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBkcml2ZXJzVG9TeW5jID0gZHJpdmVyc1RvU3luYy5maWx0ZXIoKGNkTmFtZSkgPT4gYCR7dGhpcy5tYXBwaW5nW2NkTmFtZV0ubWluQnJvd3NlclZlcnNpb259YCA9PT1cbiAgICAgICAgYCR7Y2xvc2VzdE1hdGNoZWRWZXJzaW9uTnVtYmVyID4gMCA/IGNsb3Nlc3RNYXRjaGVkVmVyc2lvbk51bWJlciA6IG1pbkJyb3dzZXJWZXJzaW9uSW50fWApO1xuXG4gICAgICBsb2cuZGVidWcoYEdvdCAke3V0aWwucGx1cmFsaXplKCdpdGVtJywgZHJpdmVyc1RvU3luYy5sZW5ndGgsIHRydWUpfWApO1xuICAgICAgaWYgKF8uaXNFbXB0eShkcml2ZXJzVG9TeW5jKSkge1xuICAgICAgICByZXR1cm4gW107XG4gICAgICB9XG4gICAgICBsb2cuZGVidWcoYFdpbGwgc2VsZWN0IGNhbmRpZGF0ZSAke3V0aWwucGx1cmFsaXplKCdkcml2ZXInLCBkcml2ZXJzVG9TeW5jLmxlbmd0aCl9IGAgK1xuICAgICAgICBgdmVyc2lvbmVkIGFzICcke18udW5pcShkcml2ZXJzVG9TeW5jLm1hcCgoY2ROYW1lKSA9PiB0aGlzLm1hcHBpbmdbY2ROYW1lXS52ZXJzaW9uKSl9J2ApO1xuICAgIH1cblxuICAgIGlmICghXy5pc0VtcHR5KG9zSW5mbykpIHtcbiAgICAgIC8vIEZpbHRlciBvdXQgZHJpdmVycyBmb3IgdW5zdXBwb3J0ZWQgc3lzdGVtIGFyY2hpdGVjdHVyZXNcbiAgICAgIGxldCB7bmFtZSwgYXJjaH0gPSBvc0luZm87XG4gICAgICBpZiAoYXJjaCA9PT0gWDY0ICYmICFkcml2ZXJzVG9TeW5jLnNvbWUoKGNkTmFtZSkgPT4gY2ROYW1lLmluY2x1ZGVzKGBfJHtuYW1lfSR7WDY0fWApKSkge1xuICAgICAgICAvLyBGYWxsIGJhY2sgdG8geDg2IGJ1aWxkIGlmIHg2NCBvbmUgaXMgbm90IGF2YWlsYWJsZSBmb3IgdGhlIGdpdmVuIE9TXG4gICAgICAgIGFyY2ggPSBYODY7XG4gICAgICB9XG4gICAgICAvLyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy82NTE0Njc1MS9kZXRlY3RpbmctYXBwbGUtc2lsaWNvbi1tYWMtaW4tamF2YXNjcmlwdFxuICAgICAgaWYgKG5hbWUgPT09IE9TLm1hYyAmJiBfLmluY2x1ZGVzKF8udG9Mb3dlcihvcy5jcHVzKClbMF0ubW9kZWwpLCAnYXBwbGUnKSkge1xuICAgICAgICBmb3IgKGNvbnN0IGFybVN1ZmZpeCBvZiBBUFBMRV9BUk1fU1VGRklYRVMpIHtcbiAgICAgICAgICBpZiAoZHJpdmVyc1RvU3luYy5zb21lKChjZE5hbWUpID0+IGNkTmFtZS5pbmNsdWRlcyhhcm1TdWZmaXgpKSkge1xuICAgICAgICAgICAgLy8gcHJlZmVyIGV4ZWN1dGFibGUgZm9yIEFSTSBhcmNoIGlmIHByZXNlbnRcbiAgICAgICAgICAgIGFyY2ggPSBhcm1TdWZmaXg7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGxvZy5kZWJ1ZyhgU2VsZWN0aW5nIGNocm9tZWRyaXZlcnMgd2hvc2UgcGxhdGZvcm0gbWF0Y2hlcyB0byAke25hbWV9JHthcmNofWApO1xuICAgICAgY29uc3QgcGxhdGZvcm1SZSA9IG5ldyBSZWdFeHAoYChcXFxcYnxfKSR7bmFtZX0ke2FyY2h9XFxcXGJgKTtcbiAgICAgIGRyaXZlcnNUb1N5bmMgPSBkcml2ZXJzVG9TeW5jLmZpbHRlcigoY2ROYW1lKSA9PiBwbGF0Zm9ybVJlLnRlc3QoY2ROYW1lKSk7XG4gICAgICBsb2cuZGVidWcoYEdvdCAke3V0aWwucGx1cmFsaXplKCdpdGVtJywgZHJpdmVyc1RvU3luYy5sZW5ndGgsIHRydWUpfWApO1xuICAgIH1cblxuICAgIHJldHVybiBkcml2ZXJzVG9TeW5jO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHJpZXZlcyB0aGUgZ2l2ZW4gY2hyb21lZHJpdmVyIGZyb20gdGhlIHN0b3JhZ2VcbiAgICogYW5kIHVucGFja3MgaXQgaW50byBgdGhpcy5jaHJvbWVkcml2ZXJEaXJgIGZvbGRlclxuICAgKlxuICAgKiBAcGFyYW0ge251bWJlcn0gaW5kZXggLSBUaGUgdW5pcXVlIGRyaXZlciBpbmRleFxuICAgKiBAcGFyYW0ge3N0cmluZ30gZHJpdmVyS2V5IC0gVGhlIGRyaXZlciBrZXkgaW4gYHRoaXMubWFwcGluZ2BcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFyY2hpdmVzUm9vdCAtIFRoZSB0ZW1wb3JhcnkgZm9sZGVyIHBhdGggdG8gZXh0cmFjdFxuICAgKiBkb3dubG9hZGVkIGFyY2hpdmVzIHRvXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gaXNTdHJpY3QgW3RydWVdIC0gV2hldGhlciB0byB0aHJvdyBhbiBlcnJvciAoYHRydWVgKVxuICAgKiBvciByZXR1cm4gYSBib29sZWFuIHJlc3VsdCBpZiB0aGUgZHJpdmVyIHJldHJpZXZhbCBwcm9jZXNzIGZhaWxzXG4gICAqIEB0aHJvd3Mge0Vycm9yfSBpZiB0aGVyZSB3YXMgYSBmYWlsdXJlIHdoaWxlIHJldHJpZXZpbmcgdGhlIGRyaXZlclxuICAgKiBhbmQgYGlzU3RyaWN0YCBpcyBzZXQgdG8gYHRydWVgXG4gICAqIEByZXR1cm5zIHtib29sZWFufSBpZiBgdHJ1ZWAgdGhlbiB0aGUgY2hyb21lZHJpdmVyIGlzIHN1Y2Nlc3NmdWxseVxuICAgKiBkb3dubG9hZGVkIGFuZCBleHRyYWN0ZWQuXG4gICAqL1xuICBhc3luYyByZXRyaWV2ZURyaXZlciAoaW5kZXgsIGRyaXZlcktleSwgYXJjaGl2ZXNSb290LCBpc1N0cmljdCA9IGZhbHNlKSB7XG4gICAgY29uc3QgeyB1cmwsIGV0YWcsIHZlcnNpb24gfSA9IHRoaXMubWFwcGluZ1tkcml2ZXJLZXldO1xuICAgIGNvbnN0IGFyY2hpdmVQYXRoID0gcGF0aC5yZXNvbHZlKGFyY2hpdmVzUm9vdCwgYCR7aW5kZXh9LnppcGApO1xuICAgIGxvZy5kZWJ1ZyhgUmV0cmlldmluZyAnJHt1cmx9JyB0byAnJHthcmNoaXZlUGF0aH0nYCk7XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IG5ldC5kb3dubG9hZEZpbGUodXJsLCBhcmNoaXZlUGF0aCwge1xuICAgICAgICBpc01ldGVyZWQ6IGZhbHNlLFxuICAgICAgICB0aW1lb3V0OiBUSU1FT1VUX01TXG4gICAgICB9KTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBjb25zdCBtc2cgPSBgQ2Fubm90IGRvd25sb2FkIGNocm9tZWRyaXZlciBhcmNoaXZlLiBPcmlnaW5hbCBlcnJvcjogJHtlLm1lc3NhZ2V9YDtcbiAgICAgIGlmIChpc1N0cmljdCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IobXNnKTtcbiAgICAgIH1cbiAgICAgIGxvZy5lcnJvcihtc2cpO1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBpZiAoIWF3YWl0IGlzQ3JjT2soYXJjaGl2ZVBhdGgsIGV0YWcpKSB7XG4gICAgICBjb25zdCBtc2cgPSBgVGhlIGNoZWNrc3VtIGZvciB0aGUgZG93bmxvYWRlZCBjaHJvbWVkcml2ZXIgJyR7ZHJpdmVyS2V5fScgZGlkIG5vdCBtYXRjaGA7XG4gICAgICBpZiAoaXNTdHJpY3QpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKG1zZyk7XG4gICAgICB9XG4gICAgICBsb2cuZXJyb3IobXNnKTtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gICAgY29uc3QgZmlsZU5hbWUgPSBgJHtwYXRoLnBhcnNlKHVybCkubmFtZX1fdiR7dmVyc2lvbn1gICtcbiAgICAgIChzeXN0ZW0uaXNXaW5kb3dzKCkgPyAnLmV4ZScgOiAnJyk7XG4gICAgY29uc3QgdGFyZ2V0UGF0aCA9IHBhdGgucmVzb2x2ZSh0aGlzLmNocm9tZWRyaXZlckRpciwgZmlsZU5hbWUpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLnVuemlwRHJpdmVyKGFyY2hpdmVQYXRoLCB0YXJnZXRQYXRoKTtcbiAgICAgIGF3YWl0IGZzLmNobW9kKHRhcmdldFBhdGgsIDBvNzU1KTtcbiAgICAgIGxvZy5kZWJ1ZyhgUGVybWlzc2lvbnMgb2YgdGhlIGZpbGUgJyR7dGFyZ2V0UGF0aH0nIGhhdmUgYmVlbiBjaGFuZ2VkIHRvIDc1NWApO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGlmIChpc1N0cmljdCkge1xuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgICAgbG9nLmVycm9yKGUubWVzc2FnZSk7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IFN5bmNPcHRpb25zXG4gICAqIEBwcm9wZXJ0eSB7QXJyYXk8U3RyaW5nPn0gdmVyc2lvbnMgLSBUaGUgbGlzdCBvZiBjaHJvbWVkcml2ZXJcbiAgICogdmVyc2lvbnMgdG8gc3luYy4gSWYgZW1wdHkgKHRoZSBkZWZhdWx0IHZhbHVlKSB0aGVuIGFsbCBhdmFpbGFibGVcbiAgICogY2hyb21lZHJpdmVycyBhcmUgZ29pbmcgdG8gYmUgZG93bmxvYWRlZCBhbmQgZXh0cmFjdGVkXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfG51bWJlcn0gbWluQnJvd3NlclZlcnNpb24gLSBUaGUgbWludW11bSBzdXBwb3J0ZWRcbiAgICogQ2hyb21lIHZlcnNpb24gdGhhdCBkb3dubG9hZGVkIGNocm9tZWRyaXZlcnMgc2hvdWxkIHN1cHBvcnQuIENhbiBtYXRjaFxuICAgKiBtdWx0aXBsZSBkcml2ZXJzLlxuICAgKiBAcHJvcGVydHkgez9PU0luZm99IG9zSW5mbyAtIFN5c3RlbSBpbmZvcm1hdGlvbiB1c2VkIHRvIGZpbHRlciBvdXRcbiAgICogdGhlIGxpc3Qgb2YgdGhlIHJldHJpZXZlZCBkcml2ZXJzLiBJZiBub3QgcHJvdmlkZWQgdGhlbiB0aGUgc2NyaXB0XG4gICAqIHdpbGwgdHJ5IHRvIHJldHJpZXZlIGl0LlxuICAgKi9cblxuICAvKipcbiAgICogUmV0cmlldmVzIGNocm9tZWRyaXZlcnMgZnJvbSB0aGUgcmVtb3RlIHN0b3JhZ2VcbiAgICogdG8gdGhlIGxvY2FsIGZpbGUgc3lzdGVtXG4gICAqXG4gICAqIEBwYXJhbSB7P1N5bmNPcHRpb25zfSBvcHRzXG4gICAqIEB0aHJvd3Mge0Vycm9yfSBpZiB0aGVyZSB3YXMgYSBwcm9ibGVtIHdoaWxlIHJldHJpZXZpbmdcbiAgICogdGhlIGRyaXZlcnNcbiAgICogQHJldHVybnMge0FycmF5PFN0cmluZ30gVGhlIGxpc3Qgb2Ygc3VjY2Vzc2Z1bGx5IHN5bmNocm9uaXplZCBkcml2ZXIga2V5c1xuICAgKi9cbiAgYXN5bmMgc3luY0RyaXZlcnMgKG9wdHMgPSB7fSkge1xuICAgIGlmIChfLmlzRW1wdHkodGhpcy5tYXBwaW5nKSkge1xuICAgICAgYXdhaXQgdGhpcy5yZXRyaWV2ZU1hcHBpbmcoISFvcHRzLm1pbkJyb3dzZXJWZXJzaW9uKTtcbiAgICB9XG4gICAgaWYgKF8uaXNFbXB0eSh0aGlzLm1hcHBpbmcpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0Nhbm5vdCByZXRyaWV2ZSBjaHJvbWVkcml2ZXJzIG1hcHBpbmcgZnJvbSBHb29nbGUgc3RvcmFnZScpO1xuICAgIH1cblxuICAgIGNvbnN0IGRyaXZlcnNUb1N5bmMgPSB0aGlzLnNlbGVjdE1hdGNoaW5nRHJpdmVycyhvcHRzLm9zSW5mbyA/PyBhd2FpdCBnZXRPc0luZm8oKSwgb3B0cyk7XG4gICAgaWYgKF8uaXNFbXB0eShkcml2ZXJzVG9TeW5jKSkge1xuICAgICAgbG9nLmRlYnVnKGBUaGVyZSBhcmUgbm8gZHJpdmVycyB0byBzeW5jLiBFeGl0aW5nYCk7XG4gICAgICByZXR1cm4gW107XG4gICAgfVxuICAgIGxvZy5kZWJ1ZyhgR290ICR7dXRpbC5wbHVyYWxpemUoJ2RyaXZlcicsIGRyaXZlcnNUb1N5bmMubGVuZ3RoLCB0cnVlKX0gdG8gc3luYzogYCArXG4gICAgICBKU09OLnN0cmluZ2lmeShkcml2ZXJzVG9TeW5jLCBudWxsLCAyKSk7XG5cbiAgICBjb25zdCBzeW5jaHJvbml6ZWREcml2ZXJzID0gW107XG4gICAgY29uc3QgcHJvbWlzZXMgPSBbXTtcbiAgICBjb25zdCBhcmNoaXZlc1Jvb3QgPSBhd2FpdCB0ZW1wRGlyLm9wZW5EaXIoKTtcbiAgICB0cnkge1xuICAgICAgZm9yIChjb25zdCBbaWR4LCBkcml2ZXJLZXldIG9mIGRyaXZlcnNUb1N5bmMuZW50cmllcygpKSB7XG4gICAgICAgIHByb21pc2VzLnB1c2goKGFzeW5jICgpID0+IHtcbiAgICAgICAgICBpZiAoYXdhaXQgdGhpcy5yZXRyaWV2ZURyaXZlcihpZHgsIGRyaXZlcktleSwgYXJjaGl2ZXNSb290LCAhXy5pc0VtcHR5KG9wdHMpKSkge1xuICAgICAgICAgICAgc3luY2hyb25pemVkRHJpdmVycy5wdXNoKGRyaXZlcktleSk7XG4gICAgICAgICAgfVxuICAgICAgICB9KSgpKTtcblxuICAgICAgICBpZiAocHJvbWlzZXMubGVuZ3RoICUgTUFYX1BBUkFMTEVMX0RPV05MT0FEUyA9PT0gMCkge1xuICAgICAgICAgIGF3YWl0IEIuYWxsKHByb21pc2VzKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgYXdhaXQgQi5hbGwocHJvbWlzZXMpO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBhd2FpdCBmcy5yaW1yYWYoYXJjaGl2ZXNSb290KTtcbiAgICB9XG4gICAgaWYgKCFfLmlzRW1wdHkoc3luY2hyb25pemVkRHJpdmVycykpIHtcbiAgICAgIGxvZy5pbmZvKGBTdWNjZXNzZnVsbHkgc3luY2hyb25pemVkIGAgK1xuICAgICAgICBgJHt1dGlsLnBsdXJhbGl6ZSgnY2hyb21lZHJpdmVyJywgc3luY2hyb25pemVkRHJpdmVycy5sZW5ndGgsIHRydWUpfWApO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2cuaW5mbyhgTm8gY2hyb21lZHJpdmVycyB3ZXJlIHN5bmNocm9uaXplZGApO1xuICAgIH1cbiAgICByZXR1cm4gc3luY2hyb25pemVkRHJpdmVycztcbiAgfVxufVxuXG5cbmV4cG9ydCBkZWZhdWx0IENocm9tZWRyaXZlclN0b3JhZ2VDbGllbnQ7XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBQUE7QUFJQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUdBLE1BQU1BLFVBQVUsR0FBRyxLQUFLO0FBQ3hCLE1BQU1DLHNCQUFzQixHQUFHLENBQUM7QUFFaEMsTUFBTUMsR0FBRyxHQUFHQyxlQUFNLENBQUNDLFNBQVMsQ0FBQywyQkFBMkIsQ0FBQztBQUd6RCxlQUFlQyxPQUFPLENBQUVDLEdBQUcsRUFBRUMsUUFBUSxFQUFFO0VBQ3JDLE1BQU1DLEdBQUcsR0FBRyxNQUFNQyxXQUFFLENBQUNDLElBQUksQ0FBQ0osR0FBRyxFQUFFLEtBQUssQ0FBQztFQUNyQyxPQUFPSyxlQUFDLENBQUNDLE9BQU8sQ0FBQ0osR0FBRyxDQUFDLEtBQUtHLGVBQUMsQ0FBQ0MsT0FBTyxDQUFDTCxRQUFRLENBQUM7QUFDL0M7QUFFQSxTQUFTTSxhQUFhLENBQUVDLE1BQU0sRUFBRUMsU0FBUyxHQUFHLElBQUksRUFBRUMsSUFBSSxHQUFHLElBQUksRUFBRTtFQUM3RCxJQUFJLENBQUNELFNBQVMsSUFBSSxDQUFDQyxJQUFJLEVBQUU7SUFDdkIsT0FBTyxJQUFJO0VBQ2I7RUFDQSxJQUFJLENBQUNGLE1BQU0sQ0FBQ0csYUFBYSxFQUFFLEVBQUU7SUFDM0IsT0FBTyxJQUFJO0VBQ2I7RUFFQSxLQUFLLElBQUlDLFlBQVksR0FBRyxDQUFDLEVBQUVBLFlBQVksR0FBR0osTUFBTSxDQUFDSyxVQUFVLENBQUNDLE1BQU0sRUFBRUYsWUFBWSxFQUFFLEVBQUU7SUFDbEYsTUFBTUcsU0FBUyxHQUFHUCxNQUFNLENBQUNLLFVBQVUsQ0FBQ0QsWUFBWSxDQUFDO0lBQ2pELElBQUlILFNBQVMsSUFBSSxDQUFDQyxJQUFJLElBQUlELFNB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
412
+ exports.default = ChromedriverStorageClient;
413
+ /**
414
+ * @typedef {import('./types').SyncOptions} SyncOptions
415
+ * @typedef {import('./types').OSInfo} OSInfo
416
+ * @typedef {import('./types').ChromedriverDetails} ChromedriverDetails
417
+ * @typedef {import('./types').ChromedriverDetailsMapping} ChromedriverDetailsMapping
418
+ */
419
+ //# sourceMappingURL=storage-client.js.map