appium-chromedriver 5.2.16 → 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/CHANGELOG.md +14 -0
- package/build/index.d.ts +11 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +18 -18
- package/build/index.js.map +1 -0
- package/build/lib/chromedriver.d.ts +108 -0
- package/build/lib/chromedriver.d.ts.map +1 -0
- package/build/lib/chromedriver.js +685 -559
- package/build/lib/chromedriver.js.map +1 -1
- package/build/lib/install.d.ts +3 -0
- package/build/lib/install.d.ts.map +1 -0
- package/build/lib/install.js +39 -32
- package/build/lib/install.js.map +1 -1
- package/build/lib/protocol-helpers.d.ts +15 -0
- package/build/lib/protocol-helpers.d.ts.map +1 -0
- package/build/lib/protocol-helpers.js +38 -21
- package/build/lib/protocol-helpers.js.map +1 -1
- package/build/lib/storage-client.d.ts +119 -0
- package/build/lib/storage-client.d.ts.map +1 -0
- package/build/lib/storage-client.js +393 -274
- package/build/lib/storage-client.js.map +1 -1
- package/build/lib/types.d.ts +101 -0
- package/build/lib/types.d.ts.map +1 -0
- package/build/lib/types.js +3 -0
- package/build/lib/types.js.map +1 -0
- package/build/lib/utils.d.ts +52 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +117 -78
- package/build/lib/utils.js.map +1 -1
- package/config/mapping.json +1 -0
- package/index.js +9 -0
- package/lib/chromedriver.js +300 -162
- package/lib/install.js +8 -1
- package/lib/protocol-helpers.js +17 -1
- package/lib/storage-client.js +165 -129
- package/lib/types.ts +101 -0
- package/lib/utils.js +86 -42
- package/package.json +34 -30
- package/tsconfig.json +11 -0
|
@@ -1,300 +1,419 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
require("
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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 =
|
|
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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 (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
264
|
-
|
|
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
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
299
|
-
|
|
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
|