appium 2.0.0-beta.46 → 2.0.0-beta.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -44
- package/build/lib/appium.d.ts +3 -103
- package/build/lib/appium.d.ts.map +1 -1
- package/build/lib/appium.js +679 -549
- package/build/lib/appium.js.map +1 -1
- package/build/lib/cli/args.js +247 -127
- package/build/lib/cli/args.js.map +1 -1
- package/build/lib/cli/driver-command.d.ts +24 -5
- package/build/lib/cli/driver-command.d.ts.map +1 -1
- package/build/lib/cli/driver-command.js +78 -88
- package/build/lib/cli/driver-command.js.map +1 -1
- package/build/lib/cli/extension-command.d.ts +33 -24
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +729 -512
- package/build/lib/cli/extension-command.js.map +1 -1
- package/build/lib/cli/extension.d.ts +7 -6
- package/build/lib/cli/extension.d.ts.map +1 -1
- package/build/lib/cli/extension.js +68 -65
- package/build/lib/cli/extension.js.map +1 -1
- package/build/lib/cli/parser.d.ts +3 -3
- package/build/lib/cli/parser.d.ts.map +1 -1
- package/build/lib/cli/parser.js +234 -192
- package/build/lib/cli/parser.js.map +1 -1
- package/build/lib/cli/plugin-command.js +58 -87
- package/build/lib/cli/plugin-command.js.map +1 -1
- package/build/lib/cli/utils.js +66 -69
- package/build/lib/cli/utils.js.map +1 -1
- package/build/lib/config-file.d.ts.map +1 -1
- package/build/lib/config-file.js +189 -120
- package/build/lib/config-file.js.map +1 -1
- package/build/lib/config.d.ts.map +1 -1
- package/build/lib/config.js +254 -213
- package/build/lib/config.js.map +1 -1
- package/build/lib/constants.d.ts +6 -5
- package/build/lib/constants.d.ts.map +1 -1
- package/build/lib/constants.js +65 -59
- package/build/lib/constants.js.map +1 -1
- package/build/lib/extension/driver-config.js +199 -164
- package/build/lib/extension/driver-config.js.map +1 -1
- package/build/lib/extension/extension-config.d.ts +33 -26
- package/build/lib/extension/extension-config.d.ts.map +1 -1
- package/build/lib/extension/extension-config.js +541 -396
- package/build/lib/extension/extension-config.js.map +1 -1
- package/build/lib/extension/index.js +98 -68
- package/build/lib/extension/index.js.map +1 -1
- package/build/lib/extension/manifest-migrations.d.ts +27 -0
- package/build/lib/extension/manifest-migrations.d.ts.map +1 -0
- package/build/lib/extension/manifest-migrations.js +118 -0
- package/build/lib/extension/manifest-migrations.js.map +1 -0
- package/build/lib/extension/manifest.d.ts +35 -63
- package/build/lib/extension/manifest.d.ts.map +1 -1
- package/build/lib/extension/manifest.js +500 -240
- package/build/lib/extension/manifest.js.map +1 -1
- package/build/lib/extension/package-changed.js +57 -61
- package/build/lib/extension/package-changed.js.map +1 -1
- package/build/lib/extension/plugin-config.d.ts +2 -3
- package/build/lib/extension/plugin-config.d.ts.map +1 -1
- package/build/lib/extension/plugin-config.js +94 -70
- package/build/lib/extension/plugin-config.js.map +1 -1
- package/build/lib/grid-register.js +119 -137
- package/build/lib/grid-register.js.map +1 -1
- package/build/lib/logger.d.ts +1 -1
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +5 -15
- package/build/lib/logger.js.map +1 -1
- package/build/lib/logsink.d.ts.map +1 -1
- package/build/lib/logsink.js +189 -183
- package/build/lib/logsink.js.map +1 -1
- package/build/lib/main.d.ts +19 -12
- package/build/lib/main.d.ts.map +1 -1
- package/build/lib/main.js +330 -304
- package/build/lib/main.js.map +1 -1
- package/build/lib/schema/arg-spec.js +153 -108
- package/build/lib/schema/arg-spec.js.map +1 -1
- package/build/lib/schema/cli-args.js +203 -164
- package/build/lib/schema/cli-args.js.map +1 -1
- package/build/lib/schema/cli-transformers.js +117 -72
- package/build/lib/schema/cli-transformers.js.map +1 -1
- package/build/lib/schema/index.js +17 -32
- package/build/lib/schema/index.js.map +1 -1
- package/build/lib/schema/keywords.js +125 -67
- package/build/lib/schema/keywords.js.map +1 -1
- package/build/lib/schema/schema.d.ts.map +1 -1
- package/build/lib/schema/schema.js +582 -417
- package/build/lib/schema/schema.js.map +1 -1
- package/build/lib/utils.d.ts +41 -255
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +342 -193
- package/build/lib/utils.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/cli.d.ts +45 -34
- package/build/types/cli.d.ts.map +1 -1
- package/build/types/cli.js +3 -0
- package/build/types/cli.js.map +1 -0
- package/build/types/index.d.ts +1 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/index.js +19 -0
- package/build/types/index.js.map +1 -0
- package/build/types/manifest/base.d.ts +135 -0
- package/build/types/manifest/base.d.ts.map +1 -0
- package/build/types/manifest/base.js +3 -0
- package/build/types/manifest/base.js.map +1 -0
- package/build/types/manifest/index.d.ts +19 -0
- package/build/types/manifest/index.d.ts.map +1 -0
- package/build/types/manifest/index.js +40 -0
- package/build/types/manifest/index.js.map +1 -0
- package/build/types/manifest/v3.d.ts +139 -0
- package/build/types/manifest/v3.d.ts.map +1 -0
- package/build/types/manifest/v3.js +3 -0
- package/build/types/manifest/v3.js.map +1 -0
- package/lib/appium.js +1 -1
- package/lib/cli/args.js +1 -1
- package/lib/cli/driver-command.js +17 -0
- package/lib/cli/extension-command.js +119 -65
- package/lib/cli/extension.js +9 -8
- package/lib/cli/parser.js +2 -2
- package/lib/config-file.js +2 -3
- package/lib/config.js +3 -2
- package/lib/constants.js +7 -5
- package/lib/extension/extension-config.js +52 -47
- package/lib/extension/manifest-migrations.js +120 -0
- package/lib/extension/manifest.js +184 -103
- package/lib/extension/plugin-config.js +1 -2
- package/lib/logsink.js +26 -5
- package/lib/main.js +58 -50
- package/lib/schema/schema.js +6 -1
- package/lib/utils.js +62 -0
- package/package.json +24 -25
- package/scripts/autoinstall-extensions.js +78 -26
- package/types/cli.ts +81 -42
- package/types/index.ts +1 -2
- package/types/manifest/README.md +30 -0
- package/types/manifest/base.ts +158 -0
- package/types/manifest/index.ts +27 -0
- package/types/manifest/v3.ts +161 -0
- package/build/types/appium-manifest.d.ts +0 -59
- package/build/types/appium-manifest.d.ts.map +0 -1
- package/build/types/extension-manifest.d.ts +0 -55
- package/build/types/extension-manifest.d.ts.map +0 -1
- package/types/appium-manifest.ts +0 -73
- package/types/extension-manifest.ts +0 -64
|
@@ -1,535 +1,752 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
});
|
|
6
|
-
exports.
|
|
7
|
-
|
|
8
|
-
require("
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
var _support = require("@appium/support");
|
|
17
|
-
|
|
18
|
-
var _utils = require("./utils");
|
|
19
|
-
|
|
20
|
-
var _teen_process = require("teen_process");
|
|
21
|
-
|
|
22
|
-
var _extensionConfig = require("../extension/extension-config");
|
|
23
|
-
|
|
24
|
-
var _packageChanged = require("../extension/package-changed");
|
|
25
|
-
|
|
26
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
27
|
-
|
|
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
|
+
exports.ExtensionCommand = void 0;
|
|
7
|
+
/* eslint-disable no-console */
|
|
8
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
9
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const support_1 = require("@appium/support");
|
|
12
|
+
const utils_1 = require("./utils");
|
|
13
|
+
const teen_process_1 = require("teen_process");
|
|
14
|
+
const extension_config_1 = require("../extension/extension-config");
|
|
15
|
+
const package_changed_1 = require("../extension/package-changed");
|
|
28
16
|
const UPDATE_ALL = 'installed';
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
class NotUpdatableError extends Error {
|
|
18
|
+
}
|
|
19
|
+
class NoUpdatesAvailableError extends Error {
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Omits `driverName`/`pluginName` props from the receipt to make a {@linkcode ExtManifest}
|
|
23
|
+
* @template {ExtensionType} ExtType
|
|
24
|
+
* @param {ExtInstallReceipt<ExtType>} receipt
|
|
25
|
+
* @returns {ExtManifest<ExtType>}
|
|
26
|
+
*/
|
|
27
|
+
function receiptToManifest(receipt) {
|
|
28
|
+
return /** @type {ExtManifest<ExtType>} */ (lodash_1.default.omit(receipt, 'driverName', 'pluginName'));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* @template {ExtensionType} ExtType
|
|
32
|
+
*/
|
|
34
33
|
class ExtensionCommand {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
this.config = config;
|
|
44
|
-
this.log = new _support.console.CliConsole({
|
|
45
|
-
jsonMode: json
|
|
46
|
-
});
|
|
47
|
-
this.isJsonOutput = Boolean(json);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get type() {
|
|
51
|
-
return this.config.extensionType;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
_createFatalError(message) {
|
|
55
|
-
return new Error(this.log.decorate(message, 'error'));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async execute(args) {
|
|
59
|
-
const cmd = args[`${this.type}Command`];
|
|
60
|
-
|
|
61
|
-
if (!_lodash.default.isFunction(this[cmd])) {
|
|
62
|
-
throw this._createFatalError(`Cannot handle ${this.type} command ${cmd}`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const executeCmd = this[cmd].bind(this);
|
|
66
|
-
return await executeCmd(args);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async list({
|
|
70
|
-
showInstalled,
|
|
71
|
-
showUpdates
|
|
72
|
-
}) {
|
|
73
|
-
const lsMsg = `Listing ${showInstalled ? 'installed' : 'available'} ${this.type}s`;
|
|
74
|
-
const installedNames = Object.keys(this.config.installedExtensions);
|
|
75
|
-
const knownNames = Object.keys(this.knownExtensions);
|
|
76
|
-
const exts = [...installedNames, ...knownNames].reduce((acc, name) => {
|
|
77
|
-
if (!acc[name]) {
|
|
78
|
-
if (installedNames.includes(name)) {
|
|
79
|
-
acc[name] = { ...this.config.installedExtensions[name],
|
|
80
|
-
installed: true
|
|
81
|
-
};
|
|
82
|
-
} else if (!showInstalled) {
|
|
83
|
-
acc[name] = {
|
|
84
|
-
pkgName: this.knownExtensions[name],
|
|
85
|
-
installed: false
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return acc;
|
|
91
|
-
}, {});
|
|
92
|
-
await (0, _utils.spinWith)(this.isJsonOutput, lsMsg, async () => {
|
|
93
|
-
if (!showUpdates) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
for (const [ext, data] of _lodash.default.toPairs(exts)) {
|
|
98
|
-
if (!data.installed || data.installType !== _extensionConfig.INSTALL_TYPE_NPM) {
|
|
99
|
-
continue;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const updates = await this.checkForExtensionUpdate(ext);
|
|
103
|
-
data.updateVersion = updates.safeUpdate;
|
|
104
|
-
data.unsafeUpdateVersion = updates.unsafeUpdate;
|
|
105
|
-
data.upToDate = updates.safeUpdate === null && updates.unsafeUpdate === null;
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
const listData = exts;
|
|
109
|
-
|
|
110
|
-
if (this.isJsonOutput) {
|
|
111
|
-
return listData;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
for (const [name, data] of _lodash.default.toPairs(listData)) {
|
|
115
|
-
let installTxt = ' [not installed]'.grey;
|
|
116
|
-
let updateTxt = '';
|
|
117
|
-
let upToDateTxt = '';
|
|
118
|
-
let unsafeUpdateTxt = '';
|
|
119
|
-
|
|
120
|
-
if (data.installed) {
|
|
121
|
-
const {
|
|
122
|
-
installType,
|
|
123
|
-
installSpec,
|
|
124
|
-
updateVersion,
|
|
125
|
-
unsafeUpdateVersion,
|
|
126
|
-
version,
|
|
127
|
-
upToDate
|
|
128
|
-
} = data;
|
|
129
|
-
let typeTxt;
|
|
130
|
-
|
|
131
|
-
switch (installType) {
|
|
132
|
-
case _extensionConfig.INSTALL_TYPE_GIT:
|
|
133
|
-
case _extensionConfig.INSTALL_TYPE_GITHUB:
|
|
134
|
-
typeTxt = `(cloned from ${installSpec})`.yellow;
|
|
135
|
-
break;
|
|
136
|
-
|
|
137
|
-
case _extensionConfig.INSTALL_TYPE_LOCAL:
|
|
138
|
-
typeTxt = `(linked from ${installSpec})`.magenta;
|
|
139
|
-
break;
|
|
140
|
-
|
|
141
|
-
default:
|
|
142
|
-
typeTxt = '(NPM)';
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
installTxt = `@${version.yellow} ${('[installed ' + typeTxt + ']').green}`;
|
|
146
|
-
|
|
147
|
-
if (showUpdates) {
|
|
148
|
-
if (updateVersion) {
|
|
149
|
-
updateTxt = ` [${updateVersion} available]`.magenta;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (upToDate) {
|
|
153
|
-
upToDateTxt = ` [Up to date]`.green;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
if (unsafeUpdateVersion) {
|
|
157
|
-
unsafeUpdateTxt = ` [${unsafeUpdateVersion} available (potentially unsafe)]`.cyan;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
this.log.log(`- ${name.yellow}${installTxt}${updateTxt}${upToDateTxt}${unsafeUpdateTxt}`);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return listData;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
async _install({
|
|
169
|
-
installSpec,
|
|
170
|
-
installType,
|
|
171
|
-
packageName
|
|
172
|
-
}) {
|
|
173
|
-
let extData;
|
|
174
|
-
|
|
175
|
-
if (packageName && [_extensionConfig.INSTALL_TYPE_LOCAL, _extensionConfig.INSTALL_TYPE_NPM].includes(installType)) {
|
|
176
|
-
throw this._createFatalError(`When using --source=${installType}, cannot also use --package`);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (!packageName && [_extensionConfig.INSTALL_TYPE_GIT, _extensionConfig.INSTALL_TYPE_GITHUB].includes(installType)) {
|
|
180
|
-
throw this._createFatalError(`When using --source=${installType}, must also use --package`);
|
|
34
|
+
/**
|
|
35
|
+
* Build an ExtensionCommand
|
|
36
|
+
* @param {ExtensionCommandOptions<ExtType>} opts
|
|
37
|
+
*/
|
|
38
|
+
constructor({ config, json }) {
|
|
39
|
+
this.config = config;
|
|
40
|
+
this.log = new support_1.console.CliConsole({ jsonMode: json });
|
|
41
|
+
this.isJsonOutput = Boolean(json);
|
|
181
42
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (installSpec.split('/').length !== 2) {
|
|
188
|
-
throw this._createFatalError(`Github ${this.type} spec ${installSpec} appeared to be invalid; ` + 'it should be of the form <org>/<repo>');
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
installOpts = {
|
|
192
|
-
installSpec,
|
|
193
|
-
pkgName: packageName
|
|
194
|
-
};
|
|
195
|
-
probableExtName = installSpec;
|
|
196
|
-
} else if (installType === _extensionConfig.INSTALL_TYPE_GIT) {
|
|
197
|
-
installSpec = installSpec.replace(/\.git$/, '');
|
|
198
|
-
installOpts = {
|
|
199
|
-
installSpec,
|
|
200
|
-
pkgName: packageName
|
|
201
|
-
};
|
|
202
|
-
probableExtName = installSpec;
|
|
203
|
-
} else {
|
|
204
|
-
let pkgName, pkgVer;
|
|
205
|
-
|
|
206
|
-
if (installType === _extensionConfig.INSTALL_TYPE_LOCAL) {
|
|
207
|
-
pkgName = _path.default.isAbsolute(installSpec) ? installSpec : _path.default.resolve(installSpec);
|
|
208
|
-
} else {
|
|
209
|
-
let name;
|
|
210
|
-
const splits = installSpec.split('@');
|
|
211
|
-
|
|
212
|
-
if (installSpec[0] === '@') {
|
|
213
|
-
[name, pkgVer] = [`@${splits[1]}`, splits[2]];
|
|
214
|
-
} else {
|
|
215
|
-
[name, pkgVer] = splits;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (installType === _extensionConfig.INSTALL_TYPE_NPM) {
|
|
219
|
-
pkgName = name;
|
|
220
|
-
} else {
|
|
221
|
-
const knownNames = Object.keys(this.knownExtensions);
|
|
222
|
-
|
|
223
|
-
if (!_lodash.default.includes(knownNames, name)) {
|
|
224
|
-
const msg = `Could not resolve ${this.type}; are you sure it's in the list ` + `of supported ${this.type}s? ${JSON.stringify(knownNames)}`;
|
|
225
|
-
throw this._createFatalError(msg);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
probableExtName = name;
|
|
229
|
-
pkgName = this.knownExtensions[name];
|
|
230
|
-
installType = _extensionConfig.INSTALL_TYPE_NPM;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
installOpts = {
|
|
235
|
-
installSpec,
|
|
236
|
-
pkgName,
|
|
237
|
-
pkgVer
|
|
238
|
-
};
|
|
43
|
+
/**
|
|
44
|
+
* `driver` or `plugin`, depending on the `ExtensionConfig`.
|
|
45
|
+
*/
|
|
46
|
+
get type() {
|
|
47
|
+
return this.config.extensionType;
|
|
239
48
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Logs a message and returns an {@linkcode Error} to throw.
|
|
51
|
+
*
|
|
52
|
+
* For TS to understand that a function throws an exception, it must actually throw an exception--
|
|
53
|
+
* in other words, _calling_ a function which is guaranteed to throw an exception is not enough--
|
|
54
|
+
* nor is something like `@returns {never}` which does not imply a thrown exception.
|
|
55
|
+
* @param {string} message
|
|
56
|
+
* @protected
|
|
57
|
+
* @returns {Error}
|
|
58
|
+
*/
|
|
59
|
+
_createFatalError(message) {
|
|
60
|
+
return new Error(this.log.decorate(message, 'error'));
|
|
243
61
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const [errors, warnings] = await _bluebird.default.all([this.config.getProblems(extName, extManifest), this.config.getWarnings(extName, extManifest)]);
|
|
258
|
-
const errorMap = new Map([[extName, errors]]);
|
|
259
|
-
const warningMap = new Map([[extName, warnings]]);
|
|
260
|
-
const {
|
|
261
|
-
errorSummaries,
|
|
262
|
-
warningSummaries
|
|
263
|
-
} = this.config.getValidationResultSummaries(errorMap, warningMap);
|
|
264
|
-
|
|
265
|
-
if (!_lodash.default.isEmpty(errorSummaries)) {
|
|
266
|
-
throw this._createFatalError(errorSummaries.join('\n'));
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
if (!_lodash.default.isEmpty(warningSummaries)) {
|
|
270
|
-
this.log.warn(warningSummaries.join('\n'));
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
await this.config.addExtension(extName, extManifest);
|
|
274
|
-
|
|
275
|
-
if (await _support.env.hasAppiumDependency(this.config.appiumHome)) {
|
|
276
|
-
await (0, _packageChanged.packageDidChange)(this.config.appiumHome);
|
|
62
|
+
/**
|
|
63
|
+
* Take a CLI parse and run an extension command based on its type
|
|
64
|
+
*
|
|
65
|
+
* @param {object} args - a key/value object with CLI flags and values
|
|
66
|
+
* @return {Promise<object>} the result of the specific command which is executed
|
|
67
|
+
*/
|
|
68
|
+
async execute(args) {
|
|
69
|
+
const cmd = args[`${this.type}Command`];
|
|
70
|
+
if (!lodash_1.default.isFunction(this[cmd])) {
|
|
71
|
+
throw this._createFatalError(`Cannot handle ${this.type} command ${cmd}`);
|
|
72
|
+
}
|
|
73
|
+
const executeCmd = this[cmd].bind(this);
|
|
74
|
+
return await executeCmd(args);
|
|
277
75
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
76
|
+
/**
|
|
77
|
+
* List extensions
|
|
78
|
+
*
|
|
79
|
+
* @param {ListOptions} opts
|
|
80
|
+
* @return {Promise<ExtensionListData>} map of extension names to extension data
|
|
81
|
+
*/
|
|
82
|
+
async list({ showInstalled, showUpdates }) {
|
|
83
|
+
const lsMsg = `Listing ${showInstalled ? 'installed' : 'available'} ${this.type}s`;
|
|
84
|
+
const installedNames = Object.keys(this.config.installedExtensions);
|
|
85
|
+
const knownNames = Object.keys(this.knownExtensions);
|
|
86
|
+
const exts = [...installedNames, ...knownNames].reduce((acc, name) => {
|
|
87
|
+
if (!acc[name]) {
|
|
88
|
+
if (installedNames.includes(name)) {
|
|
89
|
+
acc[name] = {
|
|
90
|
+
...this.config.installedExtensions[name],
|
|
91
|
+
installed: true,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
else if (!showInstalled) {
|
|
95
|
+
acc[name] = { pkgName: this.knownExtensions[name], installed: false };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return acc;
|
|
99
|
+
},
|
|
100
|
+
/**
|
|
101
|
+
* This accumulator contains either {@linkcode UninstalledExtensionLIstData} _or_
|
|
102
|
+
* {@linkcode InstalledExtensionListData} without upgrade information (which is added by the below code block)
|
|
103
|
+
* @type {Record<string,Partial<InstalledExtensionListData>|UninstalledExtensionListData>}
|
|
104
|
+
*/ ({}));
|
|
105
|
+
// if we want to show whether updates are available, put that behind a spinner
|
|
106
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, lsMsg, async () => {
|
|
107
|
+
if (!showUpdates) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
for (const [ext, data] of lodash_1.default.toPairs(exts)) {
|
|
111
|
+
if (!data.installed || data.installType !== extension_config_1.INSTALL_TYPE_NPM) {
|
|
112
|
+
// don't need to check for updates on exts that aren't installed
|
|
113
|
+
// also don't need to check for updates on non-npm exts
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const updates = await this.checkForExtensionUpdate(ext);
|
|
118
|
+
data.updateVersion = updates.safeUpdate;
|
|
119
|
+
data.unsafeUpdateVersion = updates.unsafeUpdate;
|
|
120
|
+
data.upToDate = updates.safeUpdate === null && updates.unsafeUpdate === null;
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
data.updateError = e.message;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
299
126
|
});
|
|
300
|
-
|
|
301
|
-
return
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
127
|
+
const listData = /** @type {ExtensionListData} */ (exts);
|
|
128
|
+
// if we're just getting the data, short circuit return here since we don't need to do any
|
|
129
|
+
// formatting logic
|
|
130
|
+
if (this.isJsonOutput) {
|
|
131
|
+
return listData;
|
|
132
|
+
}
|
|
133
|
+
for (const [name, data] of lodash_1.default.toPairs(listData)) {
|
|
134
|
+
let installTxt = ' [not installed]'.grey;
|
|
135
|
+
let updateTxt = '';
|
|
136
|
+
let upToDateTxt = '';
|
|
137
|
+
let unsafeUpdateTxt = '';
|
|
138
|
+
if (data.installed) {
|
|
139
|
+
const { installType, installSpec, updateVersion, unsafeUpdateVersion, version, upToDate, updateError, } = data;
|
|
140
|
+
let typeTxt;
|
|
141
|
+
switch (installType) {
|
|
142
|
+
case extension_config_1.INSTALL_TYPE_GIT:
|
|
143
|
+
case extension_config_1.INSTALL_TYPE_GITHUB:
|
|
144
|
+
typeTxt = `(cloned from ${installSpec})`.yellow;
|
|
145
|
+
break;
|
|
146
|
+
case extension_config_1.INSTALL_TYPE_LOCAL:
|
|
147
|
+
typeTxt = `(linked from ${installSpec})`.magenta;
|
|
148
|
+
break;
|
|
149
|
+
default:
|
|
150
|
+
typeTxt = '(NPM)';
|
|
151
|
+
}
|
|
152
|
+
installTxt = `@${version.yellow} ${('[installed ' + typeTxt + ']').green}`;
|
|
153
|
+
if (showUpdates) {
|
|
154
|
+
if (updateError) {
|
|
155
|
+
updateTxt = ` [Cannot check for updates: ${updateError}]`.red;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
if (updateVersion) {
|
|
159
|
+
updateTxt = ` [${updateVersion} available]`.magenta;
|
|
160
|
+
}
|
|
161
|
+
if (upToDate) {
|
|
162
|
+
upToDateTxt = ` [Up to date]`.green;
|
|
163
|
+
}
|
|
164
|
+
if (unsafeUpdateVersion) {
|
|
165
|
+
unsafeUpdateTxt = ` [${unsafeUpdateVersion} available (potentially unsafe)]`.cyan;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
this.log.log(`- ${name.yellow}${installTxt}${updateTxt}${upToDateTxt}${unsafeUpdateTxt}`);
|
|
171
|
+
}
|
|
172
|
+
return listData;
|
|
343
173
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Install an extension
|
|
176
|
+
*
|
|
177
|
+
* @param {InstallOpts} opts
|
|
178
|
+
* @return {Promise<ExtRecord<ExtType>>} map of all installed extension names to extension data
|
|
179
|
+
*/
|
|
180
|
+
async _install({ installSpec, installType, packageName }) {
|
|
181
|
+
/** @type {ExtInstallReceipt<ExtType>} */
|
|
182
|
+
let receipt;
|
|
183
|
+
if (packageName && [extension_config_1.INSTALL_TYPE_LOCAL, extension_config_1.INSTALL_TYPE_NPM].includes(installType)) {
|
|
184
|
+
throw this._createFatalError(`When using --source=${installType}, cannot also use --package`);
|
|
185
|
+
}
|
|
186
|
+
if (!packageName && [extension_config_1.INSTALL_TYPE_GIT, extension_config_1.INSTALL_TYPE_GITHUB].includes(installType)) {
|
|
187
|
+
throw this._createFatalError(`When using --source=${installType}, must also use --package`);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* @type {InstallViaNpmArgs}
|
|
191
|
+
*/
|
|
192
|
+
let installViaNpmOpts;
|
|
193
|
+
/**
|
|
194
|
+
* The probable (?) name of the extension derived from the install spec.
|
|
195
|
+
*
|
|
196
|
+
* If using a local install type, this will remain empty.
|
|
197
|
+
* @type {string}
|
|
198
|
+
*/
|
|
199
|
+
let probableExtName = '';
|
|
200
|
+
// depending on `installType`, build the options to pass into `installViaNpm`
|
|
201
|
+
if (installType === extension_config_1.INSTALL_TYPE_GITHUB) {
|
|
202
|
+
if (installSpec.split('/').length !== 2) {
|
|
203
|
+
throw this._createFatalError(`Github ${this.type} spec ${installSpec} appeared to be invalid; ` +
|
|
204
|
+
'it should be of the form <org>/<repo>');
|
|
205
|
+
}
|
|
206
|
+
installViaNpmOpts = {
|
|
207
|
+
installSpec,
|
|
208
|
+
installType,
|
|
209
|
+
pkgName: /** @type {string} */ (packageName),
|
|
210
|
+
};
|
|
211
|
+
probableExtName = installSpec;
|
|
212
|
+
}
|
|
213
|
+
else if (installType === extension_config_1.INSTALL_TYPE_GIT) {
|
|
214
|
+
// git urls can have '.git' at the end, but this is not necessary and would complicate the
|
|
215
|
+
// way we download and name directories, so we can just remove it
|
|
216
|
+
installSpec = installSpec.replace(/\.git$/, '');
|
|
217
|
+
installViaNpmOpts = {
|
|
218
|
+
installSpec,
|
|
219
|
+
installType,
|
|
220
|
+
pkgName: /** @type {string} */ (packageName),
|
|
221
|
+
};
|
|
222
|
+
probableExtName = installSpec;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
let pkgName, pkgVer;
|
|
226
|
+
if (installType === extension_config_1.INSTALL_TYPE_LOCAL) {
|
|
227
|
+
pkgName = path_1.default.isAbsolute(installSpec) ? installSpec : path_1.default.resolve(installSpec);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// at this point we have either an npm package or an appium verified extension
|
|
231
|
+
// name or a local path. both of which will be installed via npm.
|
|
232
|
+
// extensions installed via npm can include versions or tags after the '@'
|
|
233
|
+
// sign, so check for that. We also need to be careful that package names themselves can
|
|
234
|
+
// contain the '@' symbol, as in `npm install @appium/fake-driver@1.2.0`
|
|
235
|
+
let name;
|
|
236
|
+
const splits = installSpec.split('@');
|
|
237
|
+
if (installSpec[0] === '@') {
|
|
238
|
+
// this is the case where we have an npm org included in the package name
|
|
239
|
+
[name, pkgVer] = [`@${splits[1]}`, splits[2]];
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// this is the case without an npm org
|
|
243
|
+
[name, pkgVer] = splits;
|
|
244
|
+
}
|
|
245
|
+
if (installType === extension_config_1.INSTALL_TYPE_NPM) {
|
|
246
|
+
// if we're installing a named package from npm, we don't need to check
|
|
247
|
+
// against the appium extension list; just use the installSpec as is
|
|
248
|
+
pkgName = name;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
// if we're installing a named appium driver (like 'xcuitest') we need to
|
|
252
|
+
// dereference the actual npm package ('appiupm-xcuitest-driver'), so
|
|
253
|
+
// check it exists and get the correct package
|
|
254
|
+
const knownNames = Object.keys(this.knownExtensions);
|
|
255
|
+
if (!lodash_1.default.includes(knownNames, name)) {
|
|
256
|
+
const msg = `Could not resolve ${this.type}; are you sure it's in the list ` +
|
|
257
|
+
`of supported ${this.type}s? ${JSON.stringify(knownNames)}`;
|
|
258
|
+
throw this._createFatalError(msg);
|
|
259
|
+
}
|
|
260
|
+
probableExtName = name;
|
|
261
|
+
pkgName = this.knownExtensions[name];
|
|
262
|
+
// given that we'll use the install type in the driver json, store it as
|
|
263
|
+
// 'npm' now
|
|
264
|
+
installType = extension_config_1.INSTALL_TYPE_NPM;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
installViaNpmOpts = { installSpec, pkgName, pkgVer, installType };
|
|
268
|
+
}
|
|
269
|
+
// fail fast here if we can
|
|
270
|
+
if (probableExtName && this.config.isInstalled(probableExtName)) {
|
|
271
|
+
throw this._createFatalError(`A ${this.type} named "${probableExtName}" is already installed. ` +
|
|
272
|
+
`Did you mean to update? Run "appium ${this.type} update". See ` +
|
|
273
|
+
`installed ${this.type}s with "appium ${this.type} list --installed".`);
|
|
274
|
+
}
|
|
275
|
+
receipt = await this.installViaNpm(installViaNpmOpts);
|
|
276
|
+
// this _should_ be the same as `probablyExtName` as the one derived above unless
|
|
277
|
+
// install type is local.
|
|
278
|
+
/** @type {string} */
|
|
279
|
+
const extName = receipt[ /** @type {string} */(`${this.type}Name`)];
|
|
280
|
+
// check _a second time_ with the more-accurate extName
|
|
281
|
+
if (this.config.isInstalled(extName)) {
|
|
282
|
+
throw this._createFatalError(`A ${this.type} named "${extName}" is already installed. ` +
|
|
283
|
+
`Did you mean to update? Run "appium ${this.type} update". See ` +
|
|
284
|
+
`installed ${this.type}s with "appium ${this.type} list --installed".`);
|
|
285
|
+
}
|
|
286
|
+
// this field does not exist as such in the manifest (it's used as a property name instead)
|
|
287
|
+
// so that's why it's being removed here.
|
|
288
|
+
/** @type {ExtManifest<ExtType>} */
|
|
289
|
+
const extManifest = receiptToManifest(receipt);
|
|
290
|
+
const [errors, warnings] = await bluebird_1.default.all([
|
|
291
|
+
this.config.getProblems(extName, extManifest),
|
|
292
|
+
this.config.getWarnings(extName, extManifest),
|
|
293
|
+
]);
|
|
294
|
+
const errorMap = new Map([[extName, errors]]);
|
|
295
|
+
const warningMap = new Map([[extName, warnings]]);
|
|
296
|
+
const { errorSummaries, warningSummaries } = this.config.getValidationResultSummaries(errorMap, warningMap);
|
|
297
|
+
if (!lodash_1.default.isEmpty(errorSummaries)) {
|
|
298
|
+
throw this._createFatalError(errorSummaries.join('\n'));
|
|
299
|
+
}
|
|
300
|
+
// note that we won't show any warnings if there were errors.
|
|
301
|
+
if (!lodash_1.default.isEmpty(warningSummaries)) {
|
|
302
|
+
this.log.warn(warningSummaries.join('\n'));
|
|
303
|
+
}
|
|
304
|
+
await this.config.addExtension(extName, extManifest);
|
|
305
|
+
// update the hash if we've changed the local `package.json`
|
|
306
|
+
if (await support_1.env.hasAppiumDependency(this.config.appiumHome)) {
|
|
307
|
+
await (0, package_changed_1.packageDidChange)(this.config.appiumHome);
|
|
308
|
+
}
|
|
309
|
+
// log info for the user
|
|
310
|
+
this.log.info(this.getPostInstallText({ extName, extData: receipt }));
|
|
311
|
+
return this.config.installedExtensions;
|
|
347
312
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
313
|
+
/**
|
|
314
|
+
* Install an extension via NPM
|
|
315
|
+
*
|
|
316
|
+
* @param {InstallViaNpmArgs} args
|
|
317
|
+
* @returns {Promise<ExtInstallReceipt<ExtType>>}
|
|
318
|
+
*/
|
|
319
|
+
async installViaNpm({ installSpec, pkgName, pkgVer, installType }) {
|
|
320
|
+
const npmSpec = `${pkgName}${pkgVer ? '@' + pkgVer : ''}`;
|
|
321
|
+
const specMsg = npmSpec === installSpec ? '' : ` using NPM install spec '${npmSpec}'`;
|
|
322
|
+
const msg = `Installing '${installSpec}'${specMsg}`;
|
|
323
|
+
try {
|
|
324
|
+
const { pkg, path } = await (0, utils_1.spinWith)(this.isJsonOutput, msg, async () => {
|
|
325
|
+
const { pkg, installPath: path } = await support_1.npm.installPackage(this.config.appiumHome, pkgName, {
|
|
326
|
+
pkgVer,
|
|
327
|
+
installType,
|
|
328
|
+
});
|
|
329
|
+
this.validatePackageJson(pkg, installSpec);
|
|
330
|
+
return { pkg, path };
|
|
331
|
+
});
|
|
332
|
+
return this.getInstallationReceipt({
|
|
333
|
+
pkg,
|
|
334
|
+
installPath: path,
|
|
335
|
+
installType,
|
|
336
|
+
installSpec,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
throw this._createFatalError(`Encountered an error when installing package: ${err.message}`);
|
|
341
|
+
}
|
|
362
342
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
unsafe
|
|
374
|
-
}) {
|
|
375
|
-
const shouldUpdateAll = installSpec === UPDATE_ALL;
|
|
376
|
-
|
|
377
|
-
if (!shouldUpdateAll && !this.config.isInstalled(installSpec)) {
|
|
378
|
-
throw this._createFatalError(`The ${this.type} "${installSpec}" was not installed, so can't be updated`);
|
|
343
|
+
/**
|
|
344
|
+
* Get the text which should be displayed to the user after an extension has been installed. This
|
|
345
|
+
* is designed to be overridden by drivers/plugins with their own particular text.
|
|
346
|
+
*
|
|
347
|
+
* @param {ExtensionArgs} args
|
|
348
|
+
* @returns {string}
|
|
349
|
+
*/
|
|
350
|
+
// eslint-disable-next-line no-unused-vars
|
|
351
|
+
getPostInstallText(args) {
|
|
352
|
+
throw this._createFatalError('Must be implemented in final class');
|
|
379
353
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if (!unsafe && !update.safeUpdate) {
|
|
403
|
-
throw this._createFatalError(`The ${this.type} '${e}' has a major revision update ` + `(${update.current} => ${update.unsafeUpdate}), which could include ` + `breaking changes. If you want to apply this update, re-run with --unsafe`);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
const updateVer = unsafe && update.unsafeUpdate ? update.unsafeUpdate : update.safeUpdate;
|
|
407
|
-
await (0, _utils.spinWith)(this.isJsonOutput, `Updating driver '${e}' from ${update.current} to ${updateVer}`, async () => await this.updateExtension(e, updateVer));
|
|
408
|
-
updates[e] = {
|
|
409
|
-
from: update.current,
|
|
410
|
-
to: updateVer
|
|
354
|
+
/**
|
|
355
|
+
* Once a package is installed on-disk, this gathers some necessary metadata for validation.
|
|
356
|
+
*
|
|
357
|
+
* @param {GetInstallationReceiptOpts<ExtType>} opts
|
|
358
|
+
* @returns {ExtInstallReceipt<ExtType>}
|
|
359
|
+
*/
|
|
360
|
+
getInstallationReceipt({ pkg, installPath, installType, installSpec }) {
|
|
361
|
+
const { appium, name, version, peerDependencies } = pkg;
|
|
362
|
+
/** @type {import('appium/types').InternalMetadata} */
|
|
363
|
+
const internal = {
|
|
364
|
+
pkgName: name,
|
|
365
|
+
version,
|
|
366
|
+
installType,
|
|
367
|
+
installSpec,
|
|
368
|
+
installPath,
|
|
369
|
+
appiumVersion: peerDependencies?.appium,
|
|
370
|
+
};
|
|
371
|
+
/** @type {ExtMetadata<ExtType>} */
|
|
372
|
+
const extMetadata = appium;
|
|
373
|
+
return {
|
|
374
|
+
...internal,
|
|
375
|
+
...extMetadata,
|
|
411
376
|
};
|
|
412
|
-
} catch (err) {
|
|
413
|
-
errors[e] = err;
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
this.log.info('Update report:');
|
|
418
|
-
|
|
419
|
-
for (const [e, update] of _lodash.default.toPairs(updates)) {
|
|
420
|
-
this.log.ok(` - ${this.type} ${e} updated: ${update.from} => ${update.to}`.green);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
for (const [e, err] of _lodash.default.toPairs(errors)) {
|
|
424
|
-
if (err instanceof NotUpdatableError) {
|
|
425
|
-
this.log.warn(` - '${e}' was not installed via npm, so we could not check ` + `for updates`.yellow);
|
|
426
|
-
} else if (err instanceof NoUpdatesAvailableError) {
|
|
427
|
-
this.log.info(` - '${e}' had no updates available`.yellow);
|
|
428
|
-
} else {
|
|
429
|
-
this.log.error(` - '${e}' failed to update: ${err}`.red);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return {
|
|
434
|
-
updates,
|
|
435
|
-
errors
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
async checkForExtensionUpdate(ext) {
|
|
440
|
-
const {
|
|
441
|
-
version,
|
|
442
|
-
pkgName
|
|
443
|
-
} = this.config.installedExtensions[ext];
|
|
444
|
-
let unsafeUpdate = await _support.npm.getLatestVersion(this.config.appiumHome, pkgName);
|
|
445
|
-
let safeUpdate = await _support.npm.getLatestSafeUpgradeVersion(this.config.appiumHome, pkgName, version);
|
|
446
|
-
|
|
447
|
-
if (unsafeUpdate !== null && !_support.util.compareVersions(unsafeUpdate, '>', version)) {
|
|
448
|
-
unsafeUpdate = null;
|
|
449
|
-
safeUpdate = null;
|
|
450
377
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
378
|
+
/**
|
|
379
|
+
* Validates the _required_ root fields of an extension's `package.json` file.
|
|
380
|
+
*
|
|
381
|
+
* These required fields are:
|
|
382
|
+
* - `name`
|
|
383
|
+
* - `version`
|
|
384
|
+
* - `appium`
|
|
385
|
+
* @param {import('type-fest').PackageJson} pkg - `package.json` of extension
|
|
386
|
+
* @param {string} installSpec - Extension name/spec
|
|
387
|
+
* @throws {ReferenceError} If `package.json` has a missing or invalid field
|
|
388
|
+
* @returns {pkg is ExtPackageJson<ExtType>}
|
|
389
|
+
*/
|
|
390
|
+
validatePackageJson(pkg, installSpec) {
|
|
391
|
+
const { appium, name, version } = /** @type {ExtPackageJson<ExtType>} */ (pkg);
|
|
392
|
+
/**
|
|
393
|
+
*
|
|
394
|
+
* @param {string} field
|
|
395
|
+
* @returns {ReferenceError}
|
|
396
|
+
*/
|
|
397
|
+
const createMissingFieldError = (field) => new ReferenceError(`${this.type} "${installSpec}" invalid; missing a \`${field}\` field of its \`package.json\``);
|
|
398
|
+
if (!name) {
|
|
399
|
+
throw createMissingFieldError('name');
|
|
400
|
+
}
|
|
401
|
+
if (!version) {
|
|
402
|
+
throw createMissingFieldError('version');
|
|
403
|
+
}
|
|
404
|
+
if (!appium) {
|
|
405
|
+
throw createMissingFieldError('appium');
|
|
406
|
+
}
|
|
407
|
+
this.validateExtensionFields(appium, installSpec);
|
|
408
|
+
return true;
|
|
454
409
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
410
|
+
/**
|
|
411
|
+
* For any `package.json` fields which a particular type of extension requires, validate the
|
|
412
|
+
* presence and form of those fields on the `package.json` data, throwing an error if anything is
|
|
413
|
+
* amiss.
|
|
414
|
+
*
|
|
415
|
+
* @param {ExtMetadata<ExtType>} extMetadata - the data in the "appium" field of `package.json` for an extension
|
|
416
|
+
* @param {string} installSpec - Extension name/spec
|
|
417
|
+
*/
|
|
418
|
+
// eslint-disable-next-line no-unused-vars
|
|
419
|
+
validateExtensionFields(extMetadata, installSpec) {
|
|
420
|
+
throw this._createFatalError('Must be implemented in final class');
|
|
458
421
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
async _run({
|
|
481
|
-
installSpec,
|
|
482
|
-
scriptName,
|
|
483
|
-
extraArgs = []
|
|
484
|
-
}) {
|
|
485
|
-
if (!this.config.isInstalled(installSpec)) {
|
|
486
|
-
throw this._createFatalError(`The ${this.type} "${installSpec}" is not installed`);
|
|
422
|
+
/**
|
|
423
|
+
* Uninstall an extension.
|
|
424
|
+
*
|
|
425
|
+
* First tries to do this via `npm uninstall`, but if that fails, just `rm -rf`'s the extension dir.
|
|
426
|
+
*
|
|
427
|
+
* Will only remove the extension from the manifest if it has been successfully removed.
|
|
428
|
+
*
|
|
429
|
+
* @param {UninstallOpts} opts
|
|
430
|
+
* @return {Promise<ExtRecord<ExtType>>} map of all installed extension names to extension data (without the extension just uninstalled)
|
|
431
|
+
*/
|
|
432
|
+
async _uninstall({ installSpec }) {
|
|
433
|
+
if (!this.config.isInstalled(installSpec)) {
|
|
434
|
+
throw this._createFatalError(`Can't uninstall ${this.type} '${installSpec}'; it is not installed`);
|
|
435
|
+
}
|
|
436
|
+
const pkgName = this.config.installedExtensions[installSpec].pkgName;
|
|
437
|
+
await support_1.npm.uninstallPackage(this.config.appiumHome, pkgName);
|
|
438
|
+
await this.config.removeExtension(installSpec);
|
|
439
|
+
this.log.ok(`Successfully uninstalled ${this.type} '${installSpec}'`.green);
|
|
440
|
+
return this.config.installedExtensions;
|
|
487
441
|
}
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
442
|
+
/**
|
|
443
|
+
* Attempt to update one or more drivers using NPM
|
|
444
|
+
*
|
|
445
|
+
* @param {ExtensionUpdateOpts} updateSpec
|
|
446
|
+
* @return {Promise<ExtensionUpdateResult>}
|
|
447
|
+
*/
|
|
448
|
+
async _update({ installSpec, unsafe }) {
|
|
449
|
+
const shouldUpdateAll = installSpec === UPDATE_ALL;
|
|
450
|
+
// if we're specifically requesting an update for an extension, make sure it's installed
|
|
451
|
+
if (!shouldUpdateAll && !this.config.isInstalled(installSpec)) {
|
|
452
|
+
throw this._createFatalError(`The ${this.type} "${installSpec}" was not installed, so can't be updated`);
|
|
453
|
+
}
|
|
454
|
+
const extsToUpdate = shouldUpdateAll
|
|
455
|
+
? Object.keys(this.config.installedExtensions)
|
|
456
|
+
: [installSpec];
|
|
457
|
+
// 'errors' will have ext names as keys and error objects as values
|
|
458
|
+
/** @type {Record<string,Error>} */
|
|
459
|
+
const errors = {};
|
|
460
|
+
// 'updates' will have ext names as keys and update objects as values, where an update
|
|
461
|
+
// object is of the form {from: versionString, to: versionString}
|
|
462
|
+
/** @type {Record<string,UpdateReport>} */
|
|
463
|
+
const updates = {};
|
|
464
|
+
for (const e of extsToUpdate) {
|
|
465
|
+
try {
|
|
466
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, `Checking if ${this.type} '${e}' is updatable`, () => {
|
|
467
|
+
if (this.config.installedExtensions[e].installType !== extension_config_1.INSTALL_TYPE_NPM) {
|
|
468
|
+
throw new NotUpdatableError();
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
const update = await (0, utils_1.spinWith)(this.isJsonOutput, `Checking if ${this.type} '${e}' needs an update`, async () => {
|
|
472
|
+
const update = await this.checkForExtensionUpdate(e);
|
|
473
|
+
if (!(update.safeUpdate || update.unsafeUpdate)) {
|
|
474
|
+
throw new NoUpdatesAvailableError();
|
|
475
|
+
}
|
|
476
|
+
return update;
|
|
477
|
+
});
|
|
478
|
+
if (!unsafe && !update.safeUpdate) {
|
|
479
|
+
throw this._createFatalError(`The ${this.type} '${e}' has a major revision update ` +
|
|
480
|
+
`(${update.current} => ${update.unsafeUpdate}), which could include ` +
|
|
481
|
+
`breaking changes. If you want to apply this update, re-run with --unsafe`);
|
|
482
|
+
}
|
|
483
|
+
const updateVer = unsafe && update.unsafeUpdate ? update.unsafeUpdate : update.safeUpdate;
|
|
484
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, `Updating driver '${e}' from ${update.current} to ${updateVer}`, async () => await this.updateExtension(e, updateVer));
|
|
485
|
+
updates[e] = { from: update.current, to: updateVer };
|
|
486
|
+
}
|
|
487
|
+
catch (err) {
|
|
488
|
+
errors[e] = err;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
this.log.info('Update report:');
|
|
492
|
+
for (const [e, update] of lodash_1.default.toPairs(updates)) {
|
|
493
|
+
this.log.ok(` - ${this.type} ${e} updated: ${update.from} => ${update.to}`.green);
|
|
494
|
+
}
|
|
495
|
+
for (const [e, err] of lodash_1.default.toPairs(errors)) {
|
|
496
|
+
if (err instanceof NotUpdatableError) {
|
|
497
|
+
this.log.warn(` - '${e}' was not installed via npm, so we could not check ` + `for updates`.yellow);
|
|
498
|
+
}
|
|
499
|
+
else if (err instanceof NoUpdatesAvailableError) {
|
|
500
|
+
this.log.info(` - '${e}' had no updates available`.yellow);
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
// otherwise, make it pop with red!
|
|
504
|
+
this.log.error(` - '${e}' failed to update: ${err}`.red);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return { updates, errors };
|
|
493
508
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
509
|
+
/**
|
|
510
|
+
* Given an extension name, figure out what its highest possible version upgrade is, and also the
|
|
511
|
+
* highest possible safe upgrade.
|
|
512
|
+
*
|
|
513
|
+
* @param {string} ext - name of extension
|
|
514
|
+
* @return {Promise<PossibleUpdates>}
|
|
515
|
+
*/
|
|
516
|
+
async checkForExtensionUpdate(ext) {
|
|
517
|
+
// TODO decide how we want to handle beta versions?
|
|
518
|
+
// this is a helper method, 'ext' is assumed to already be installed here, and of the npm
|
|
519
|
+
// install type
|
|
520
|
+
const { version, pkgName } = this.config.installedExtensions[ext];
|
|
521
|
+
/** @type {string?} */
|
|
522
|
+
let unsafeUpdate = await support_1.npm.getLatestVersion(this.config.appiumHome, pkgName);
|
|
523
|
+
let safeUpdate = await support_1.npm.getLatestSafeUpgradeVersion(this.config.appiumHome, pkgName, version);
|
|
524
|
+
if (unsafeUpdate !== null && !support_1.util.compareVersions(unsafeUpdate, '>', version)) {
|
|
525
|
+
// the latest version is not greater than the current version, so there's no possible update
|
|
526
|
+
unsafeUpdate = null;
|
|
527
|
+
safeUpdate = null;
|
|
528
|
+
}
|
|
529
|
+
if (unsafeUpdate && unsafeUpdate === safeUpdate) {
|
|
530
|
+
// the latest update is the same as the safe update, which means it's not actually unsafe
|
|
531
|
+
unsafeUpdate = null;
|
|
532
|
+
}
|
|
533
|
+
if (safeUpdate && !support_1.util.compareVersions(safeUpdate, '>', version)) {
|
|
534
|
+
// even the safe update is not later than the current, so it is not actually an update
|
|
535
|
+
safeUpdate = null;
|
|
536
|
+
}
|
|
537
|
+
return { current: version, safeUpdate, unsafeUpdate };
|
|
499
538
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
539
|
+
/**
|
|
540
|
+
* Actually update an extension installed by NPM, using the NPM cli. And update the installation
|
|
541
|
+
* manifest.
|
|
542
|
+
*
|
|
543
|
+
* @param {string} installSpec - name of extension to update
|
|
544
|
+
* @param {string} version - version string identifier to update extension to
|
|
545
|
+
* @returns {Promise<void>}
|
|
546
|
+
*/
|
|
547
|
+
async updateExtension(installSpec, version) {
|
|
548
|
+
const { pkgName, installType } = this.config.installedExtensions[installSpec];
|
|
549
|
+
const extData = await this.installViaNpm({
|
|
550
|
+
installSpec,
|
|
551
|
+
installType,
|
|
552
|
+
pkgName,
|
|
553
|
+
pkgVer: version,
|
|
554
|
+
});
|
|
555
|
+
delete extData[ /** @type {string} */(`${this.type}Name`)];
|
|
556
|
+
await this.config.updateExtension(installSpec, extData);
|
|
503
557
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
558
|
+
/**
|
|
559
|
+
* Runs a script cached inside the "scripts" field under "appium"
|
|
560
|
+
* inside of the driver/plugins "package.json" file. Will throw
|
|
561
|
+
* an error if the driver/plugin does not contain a "scripts" field
|
|
562
|
+
* underneath the "appium" field in its package.json, if the
|
|
563
|
+
* "scripts" field is not a plain object, or if the scriptName is
|
|
564
|
+
* not found within "scripts" object.
|
|
565
|
+
*
|
|
566
|
+
* @param {RunOptions} opts
|
|
567
|
+
* @return {Promise<RunOutput>}
|
|
568
|
+
*/
|
|
569
|
+
async _run({ installSpec, scriptName, extraArgs = [] }) {
|
|
570
|
+
if (!this.config.isInstalled(installSpec)) {
|
|
571
|
+
throw this._createFatalError(`The ${this.type} "${installSpec}" is not installed`);
|
|
572
|
+
}
|
|
573
|
+
const extConfig = this.config.installedExtensions[installSpec];
|
|
574
|
+
// note: TS cannot understand that _.has() is a type guard
|
|
575
|
+
if (!('scripts' in extConfig)) {
|
|
576
|
+
throw this._createFatalError(`The ${this.type} named '${installSpec}' does not contain the ` +
|
|
577
|
+
`"scripts" field underneath the "appium" field in its package.json`);
|
|
578
|
+
}
|
|
579
|
+
const extScripts = extConfig.scripts;
|
|
580
|
+
if (!extScripts || !lodash_1.default.isPlainObject(extScripts)) {
|
|
581
|
+
throw this._createFatalError(`The ${this.type} named '${installSpec}' "scripts" field must be a plain object`);
|
|
582
|
+
}
|
|
583
|
+
if (!(scriptName in extScripts)) {
|
|
584
|
+
throw this._createFatalError(`The ${this.type} named '${installSpec}' does not support the script: '${scriptName}'`);
|
|
585
|
+
}
|
|
586
|
+
const runner = new teen_process_1.SubProcess(process.execPath, [extScripts[scriptName], ...extraArgs], {
|
|
587
|
+
cwd: this.config.getInstallPath(installSpec),
|
|
588
|
+
});
|
|
589
|
+
const output = new utils_1.RingBuffer(50);
|
|
590
|
+
runner.on('stream-line', (line) => {
|
|
591
|
+
output.enqueue(line);
|
|
592
|
+
this.log.log(line);
|
|
593
|
+
});
|
|
594
|
+
await runner.start(0);
|
|
595
|
+
try {
|
|
596
|
+
await runner.join();
|
|
597
|
+
this.log.ok(`${scriptName} successfully ran`.green);
|
|
598
|
+
return { output: output.getBuff() };
|
|
599
|
+
}
|
|
600
|
+
catch (err) {
|
|
601
|
+
this.log.error(`Encountered an error when running '${scriptName}': ${err.message}`.red);
|
|
602
|
+
return { error: err.message, output: output.getBuff() };
|
|
603
|
+
}
|
|
527
604
|
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
605
|
}
|
|
531
|
-
|
|
532
606
|
exports.ExtensionCommand = ExtensionCommand;
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJVUERBVEVfQUxMIiwiTm90VXBkYXRhYmxlRXJyb3IiLCJFcnJvciIsIk5vVXBkYXRlc0F2YWlsYWJsZUVycm9yIiwiRXh0ZW5zaW9uQ29tbWFuZCIsImNvbmZpZyIsImtub3duRXh0ZW5zaW9ucyIsImlzSnNvbk91dHB1dCIsImNvbnN0cnVjdG9yIiwianNvbiIsImxvZyIsImNvbnNvbGUiLCJDbGlDb25zb2xlIiwianNvbk1vZGUiLCJCb29sZWFuIiwidHlwZSIsImV4dGVuc2lvblR5cGUiLCJfY3JlYXRlRmF0YWxFcnJvciIsIm1lc3NhZ2UiLCJkZWNvcmF0ZSIsImV4ZWN1dGUiLCJhcmdzIiwiY21kIiwiXyIsImlzRnVuY3Rpb24iLCJleGVjdXRlQ21kIiwiYmluZCIsImxpc3QiLCJzaG93SW5zdGFsbGVkIiwic2hvd1VwZGF0ZXMiLCJsc01zZyIsImluc3RhbGxlZE5hbWVzIiwiT2JqZWN0Iiwia2V5cyIsImluc3RhbGxlZEV4dGVuc2lvbnMiLCJrbm93bk5hbWVzIiwiZXh0cyIsInJlZHVjZSIsImFjYyIsIm5hbWUiLCJpbmNsdWRlcyIsImluc3RhbGxlZCIsInBrZ05hbWUiLCJzcGluV2l0aCIsImV4dCIsImRhdGEiLCJ0b1BhaXJzIiwiaW5zdGFsbFR5cGUiLCJJTlNUQUxMX1RZUEVfTlBNIiwidXBkYXRlcyIsImNoZWNrRm9yRXh0ZW5zaW9uVXBkYXRlIiwidXBkYXRlVmVyc2lvbiIsInNhZmVVcGRhdGUiLCJ1bnNhZmVVcGRhdGVWZXJzaW9uIiwidW5zYWZlVXBkYXRlIiwidXBUb0RhdGUiLCJsaXN0RGF0YSIsImluc3RhbGxUeHQiLCJncmV5IiwidXBkYXRlVHh0IiwidXBUb0RhdGVUeHQiLCJ1bnNhZmVVcGRhdGVUeHQiLCJpbnN0YWxsU3BlYyIsInZlcnNpb24iLCJ0eXBlVHh0IiwiSU5TVEFMTF9UWVBFX0dJVCIsIklOU1RBTExfVFlQRV9HSVRIVUIiLCJ5ZWxsb3ciLCJJTlNUQUxMX1RZUEVfTE9DQUwiLCJtYWdlbnRhIiwiZ3JlZW4iLCJjeWFuIiwiX2luc3RhbGwiLCJwYWNrYWdlTmFtZSIsImV4dERhdGEiLCJpbnN0YWxsT3B0cyIsInByb2JhYmxlRXh0TmFtZSIsInNwbGl0IiwibGVuZ3RoIiwicmVwbGFjZSIsInBrZ1ZlciIsInBhdGgiLCJpc0Fic29sdXRlIiwicmVzb2x2ZSIsInNwbGl0cyIsIm1zZyIsIkpTT04iLCJzdHJpbmdpZnkiLCJpc0luc3RhbGxlZCIsImluc3RhbGxWaWFOcG0iLCJleHROYW1lIiwiZXh0TWFuaWZlc3QiLCJlcnJvcnMiLCJ3YXJuaW5ncyIsIkIiLCJhbGwiLCJnZXRQcm9ibGVtcyIsImdldFdhcm5pbmdzIiwiZXJyb3JNYXAiLCJNYXAiLCJ3YXJuaW5nTWFwIiwiZXJyb3JTdW1tYXJpZXMiLCJ3YXJuaW5nU3VtbWFyaWVzIiwiZ2V0VmFsaWRhdGlvblJlc3VsdFN1bW1hcmllcyIsImlzRW1wdHkiLCJqb2luIiwid2FybiIsImFkZEV4dGVuc2lvbiIsImVudiIsImhhc0FwcGl1bURlcGVuZGVuY3kiLCJhcHBpdW1Ib21lIiwicGFja2FnZURpZENoYW5nZSIsImluZm8iLCJnZXRQb3N0SW5zdGFsbFRleHQiLCJucG1TcGVjIiwic3BlY01zZyIsInBrZ0pzb25EYXRhIiwibnBtIiwiaW5zdGFsbFBhY2thZ2UiLCJ2YWxpZGF0ZVBhY2thZ2VKc29uIiwiZ2V0RXh0ZW5zaW9uRmllbGRzIiwiZXJyIiwicGtnSnNvbiIsImFwcGl1bSIsInBlZXJEZXBlbmRlbmNpZXMiLCJyZXN1bHQiLCJhcHBpdW1WZXJzaW9uIiwiY3JlYXRlTWlzc2luZ0ZpZWxkRXJyb3IiLCJmaWVsZCIsIlJlZmVyZW5jZUVycm9yIiwidmFsaWRhdGVFeHRlbnNpb25GaWVsZHMiLCJleHRNZXRhZGF0YSIsIl91bmluc3RhbGwiLCJ1bmluc3RhbGxQYWNrYWdlIiwicmVtb3ZlRXh0ZW5zaW9uIiwib2siLCJfdXBkYXRlIiwidW5zYWZlIiwic2hvdWxkVXBkYXRlQWxsIiwiZXh0c1RvVXBkYXRlIiwiZSIsInVwZGF0ZSIsImN1cnJlbnQiLCJ1cGRhdGVWZXIiLCJ1cGRhdGVFeHRlbnNpb24iLCJmcm9tIiwidG8iLCJlcnJvciIsInJlZCIsImdldExhdGVzdFZlcnNpb24iLCJnZXRMYXRlc3RTYWZlVXBncmFkZVZlcnNpb24iLCJ1dGlsIiwiY29tcGFyZVZlcnNpb25zIiwiX3J1biIsInNjcmlwdE5hbWUiLCJleHRyYUFyZ3MiLCJleHRDb25maWciLCJzY3JpcHRzIiwiZXh0U2NyaXB0cyIsImlzUGxhaW5PYmplY3QiLCJoYXMiLCJydW5uZXIiLCJTdWJQcm9jZXNzIiwicHJvY2VzcyIsImV4ZWNQYXRoIiwiY3dkIiwiZ2V0SW5zdGFsbFBhdGgiLCJvdXRwdXQiLCJSaW5nQnVmZmVyIiwib24iLCJsaW5lIiwiZW5xdWV1ZSIsInN0YXJ0IiwiZ2V0QnVmZiJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9jbGkvZXh0ZW5zaW9uLWNvbW1hbmQuanMiXSwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgbm8tY29uc29sZSAqL1xuaW1wb3J0IEIgZnJvbSAnYmx1ZWJpcmQnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHtucG0sIHV0aWwsIGVudiwgY29uc29sZX0gZnJvbSAnQGFwcGl1bS9zdXBwb3J0JztcbmltcG9ydCB7c3BpbldpdGgsIFJpbmdCdWZmZXJ9IGZyb20gJy4vdXRpbHMnO1xuaW1wb3J0IHtTdWJQcm9jZXNzfSBmcm9tICd0ZWVuX3Byb2Nlc3MnO1xuaW1wb3J0IHtcbiAgSU5TVEFMTF9UWVBFX05QTSxcbiAgSU5TVEFMTF9UWVBFX0dJVCxcbiAgSU5TVEFMTF9UWVBFX0dJVEhVQixcbiAgSU5TVEFMTF9UWVBFX0xPQ0FMLFxufSBmcm9tICcuLi9leHRlbnNpb24vZXh0ZW5zaW9uLWNvbmZpZyc7XG5pbXBvcnQge3BhY2thZ2VEaWRDaGFuZ2V9IGZyb20gJy4uL2V4dGVuc2lvbi9wYWNrYWdlLWNoYW5nZWQnO1xuXG5jb25zdCBVUERBVEVfQUxMID0gJ2luc3RhbGxlZCc7XG5cbmNsYXNzIE5vdFVwZGF0YWJsZUVycm9yIGV4dGVuZHMgRXJyb3Ige31cbmNsYXNzIE5vVXBkYXRlc0F2YWlsYWJsZUVycm9yIGV4dGVuZHMgRXJyb3Ige31cblxuLyoqXG4gKiBAdGVtcGxhdGUge0V4dGVuc2lvblR5cGV9IEV4dFR5cGVcbiAqL1xuY2xhc3MgRXh0ZW5zaW9uQ29tbWFuZCB7XG4gIC8qKlxuICAgKiBUaGlzIGlzIHRoZSBgRHJpdmVyQ29uZmlnYCBvciBgUGx1Z2luQ29uZmlnYCwgZGVwZW5kaW5nIG9uIGBFeHRUeXBlYC5cbiAgICogQHR5cGUge0V4dGVuc2lvbkNvbmZpZzxFeHRUeXBlPn1cbiAgICovXG4gIGNvbmZpZztcblxuICAvKipcbiAgICoge0BsaW5rY29kZSBSZWNvcmR9IG9mIG9mZmljaWFsIHBsdWdpbnMgb3IgZHJpdmVycy5cbiAgICogQHR5cGUge0tub3duRXh0ZW5zaW9uczxFeHRUeXBlPn1cbiAgICovXG4gIGtub3duRXh0ZW5zaW9ucztcblxuICAvKipcbiAgICogSWYgYHRydWVgLCBjb21tYW5kIG91dHB1dCBoYXMgYmVlbiByZXF1ZXN0ZWQgYXMgSlNPTi5cbiAgICogQHR5cGUge2Jvb2xlYW59XG4gICAqL1xuICBpc0pzb25PdXRwdXQ7XG5cbiAgLyoqXG4gICAqIEJ1aWxkIGFuIEV4dGVuc2lvbkNvbW1hbmRcbiAgICogQHBhcmFtIHtFeHRlbnNpb25Db21tYW5kT3B0aW9uczxFeHRUeXBlPn0gb3B0c1xuICAgKi9cbiAgY29uc3RydWN0b3Ioe2NvbmZpZywganNvbn0pIHtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICB0aGlzLmxvZyA9IG5ldyBjb25zb2xlLkNsaUNvbnNvbGUoe2pzb25Nb2RlOiBqc29ufSk7XG4gICAgdGhpcy5pc0pzb25PdXRwdXQgPSBCb29sZWFuKGpzb24pO1xuICB9XG5cbiAgLyoqXG4gICAqIGBkcml2ZXJgIG9yIGBwbHVnaW5gLCBkZXBlbmRpbmcgb24gdGhlIGBFeHRlbnNpb25Db25maWdgLlxuICAgKi9cbiAgZ2V0IHR5cGUoKSB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLmV4dGVuc2lvblR5cGU7XG4gIH1cblxuICAvKipcbiAgICogTG9ncyBhIG1lc3NhZ2UgYW5kIHJldHVybnMgYW4ge0BsaW5rY29kZSBFcnJvcn0gdG8gdGhyb3cuXG4gICAqXG4gICAqIEZvciBUUyB0byB1bmRlcnN0YW5kIHRoYXQgYSBmdW5jdGlvbiB0aHJvd3MgYW4gZXhjZXB0aW9uLCBpdCBtdXN0IGFjdHVhbGx5IHRocm93IGFuIGV4Y2VwdGlvbi0tXG4gICAqIGluIG90aGVyIHdvcmRzLCBfY2FsbGluZ18gYSBmdW5jdGlvbiB3aGljaCBpcyBndWFyYW50ZWVkIHRvIHRocm93IGFuIGV4Y2VwdGlvbiBpcyBub3QgZW5vdWdoLS1cbiAgICogbm9yIGlzIHNvbWV0aGluZyBsaWtlIGBAcmV0dXJucyB7bmV2ZXJ9YCB3aGljaCBkb2VzIG5vdCBpbXBseSBhIHRocm93biBleGNlcHRpb24uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBtZXNzYWdlXG4gICAqIEBwcm90ZWN0ZWRcbiAgICogQHJldHVybnMge0Vycm9yfVxuICAgKi9cbiAgX2NyZWF0ZUZhdGFsRXJyb3IobWVzc2FnZSkge1xuICAgIHJldHVybiBuZXcgRXJyb3IodGhpcy5sb2cuZGVjb3JhdGUobWVzc2FnZSwgJ2Vycm9yJykpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRha2UgYSBDTEkgcGFyc2UgYW5kIHJ1biBhbiBleHRlbnNpb24gY29tbWFuZCBiYXNlZCBvbiBpdHMgdHlwZVxuICAgKlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXJncyAtIGEga2V5L3ZhbHVlIG9iamVjdCB3aXRoIENMSSBmbGFncyBhbmQgdmFsdWVzXG4gICAqIEByZXR1cm4ge1Byb21pc2U8b2JqZWN0Pn0gdGhlIHJlc3VsdCBvZiB0aGUgc3BlY2lmaWMgY29tbWFuZCB3aGljaCBpcyBleGVjdXRlZFxuICAgKi9cbiAgYXN5bmMgZXhlY3V0ZShhcmdzKSB7XG4gICAgY29uc3QgY21kID0gYXJnc1tgJHt0aGlzLnR5cGV9Q29tbWFuZGBdO1xuICAgIGlmICghXy5pc0Z1bmN0aW9uKHRoaXNbY21kXSkpIHtcbiAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IoYENhbm5vdCBoYW5kbGUgJHt0aGlzLnR5cGV9IGNvbW1hbmQgJHtjbWR9YCk7XG4gICAgfVxuICAgIGNvbnN0IGV4ZWN1dGVDbWQgPSB0aGlzW2NtZF0uYmluZCh0aGlzKTtcbiAgICByZXR1cm4gYXdhaXQgZXhlY3V0ZUNtZChhcmdzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBMaXN0IGV4dGVuc2lvbnNcbiAgICpcbiAgICogQHBhcmFtIHtMaXN0T3B0aW9uc30gb3B0c1xuICAgKiBAcmV0dXJuIHtQcm9taXNlPEV4dGVuc2lvbkxpc3REYXRhPn0gbWFwIG9mIGV4dGVuc2lvbiBuYW1lcyB0byBleHRlbnNpb24gZGF0YVxuICAgKi9cbiAgYXN5bmMgbGlzdCh7c2hvd0luc3RhbGxlZCwgc2hvd1VwZGF0ZXN9KSB7XG4gICAgY29uc3QgbHNNc2cgPSBgTGlzdGluZyAke3Nob3dJbnN0YWxsZWQgPyAnaW5zdGFsbGVkJyA6ICdhdmFpbGFibGUnfSAke3RoaXMudHlwZX1zYDtcbiAgICBjb25zdCBpbnN0YWxsZWROYW1lcyA9IE9iamVjdC5rZXlzKHRoaXMuY29uZmlnLmluc3RhbGxlZEV4dGVuc2lvbnMpO1xuICAgIGNvbnN0IGtub3duTmFtZXMgPSBPYmplY3Qua2V5cyh0aGlzLmtub3duRXh0ZW5zaW9ucyk7XG4gICAgY29uc3QgZXh0cyA9IFsuLi5pbnN0YWxsZWROYW1lcywgLi4ua25vd25OYW1lc10ucmVkdWNlKFxuICAgICAgKGFjYywgbmFtZSkgPT4ge1xuICAgICAgICBpZiAoIWFjY1tuYW1lXSkge1xuICAgICAgICAgIGlmIChpbnN0YWxsZWROYW1lcy5pbmNsdWRlcyhuYW1lKSkge1xuICAgICAgICAgICAgYWNjW25hbWVdID0ge1xuICAgICAgICAgICAgICAuLi50aGlzLmNvbmZpZy5pbnN0YWxsZWRFeHRlbnNpb25zW25hbWVdLFxuICAgICAgICAgICAgICBpbnN0YWxsZWQ6IHRydWUsXG4gICAgICAgICAgICB9O1xuICAgICAgICAgIH0gZWxzZSBpZiAoIXNob3dJbnN0YWxsZWQpIHtcbiAgICAgICAgICAgIGFjY1tuYW1lXSA9IHtwa2dOYW1lOiB0aGlzLmtub3duRXh0ZW5zaW9uc1tuYW1lXSwgaW5zdGFsbGVkOiBmYWxzZX07XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBhY2M7XG4gICAgICB9LFxuICAgICAgLyoqXG4gICAgICAgKiBUaGlzIGFjY3VtdWxhdG9yIGNvbnRhaW5zIGVpdGhlciB7QGxpbmtjb2RlIFVuaW5zdGFsbGVkRXh0ZW5zaW9uTElzdERhdGF9IF9vcl9cbiAgICAgICAqIHtAbGlua2NvZGUgSW5zdGFsbGVkRXh0ZW5zaW9uTGlzdERhdGF9IHdpdGhvdXQgdXBncmFkZSBpbmZvcm1hdGlvbiAod2hpY2ggaXMgYWRkZWQgYnkgdGhlIGJlbG93IGNvZGUgYmxvY2spXG4gICAgICAgKiBAdHlwZSB7UmVjb3JkPHN0cmluZyxQYXJ0aWFsPEluc3RhbGxlZEV4dGVuc2lvbkxpc3REYXRhPnxVbmluc3RhbGxlZEV4dGVuc2lvbkxpc3REYXRhPn1cbiAgICAgICAqLyAoe30pXG4gICAgKTtcblxuICAgIC8vIGlmIHdlIHdhbnQgdG8gc2hvdyB3aGV0aGVyIHVwZGF0ZXMgYXJlIGF2YWlsYWJsZSwgcHV0IHRoYXQgYmVoaW5kIGEgc3Bpbm5lclxuICAgIGF3YWl0IHNwaW5XaXRoKHRoaXMuaXNKc29uT3V0cHV0LCBsc01zZywgYXN5bmMgKCkgPT4ge1xuICAgICAgaWYgKCFzaG93VXBkYXRlcykge1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICBmb3IgKGNvbnN0IFtleHQsIGRhdGFdIG9mIF8udG9QYWlycyhleHRzKSkge1xuICAgICAgICBpZiAoIWRhdGEuaW5zdGFsbGVkIHx8IGRhdGEuaW5zdGFsbFR5cGUgIT09IElOU1RBTExfVFlQRV9OUE0pIHtcbiAgICAgICAgICAvLyBkb24ndCBuZWVkIHRvIGNoZWNrIGZvciB1cGRhdGVzIG9uIGV4dHMgdGhhdCBhcmVuJ3QgaW5zdGFsbGVkXG4gICAgICAgICAgLy8gYWxzbyBkb24ndCBuZWVkIHRvIGNoZWNrIGZvciB1cGRhdGVzIG9uIG5vbi1ucG0gZXh0c1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHVwZGF0ZXMgPSBhd2FpdCB0aGlzLmNoZWNrRm9yRXh0ZW5zaW9uVXBkYXRlKGV4dCk7XG4gICAgICAgIGRhdGEudXBkYXRlVmVyc2lvbiA9IHVwZGF0ZXMuc2FmZVVwZGF0ZTtcbiAgICAgICAgZGF0YS51bnNhZmVVcGRhdGVWZXJzaW9uID0gdXBkYXRlcy51bnNhZmVVcGRhdGU7XG4gICAgICAgIGRhdGEudXBUb0RhdGUgPSB1cGRhdGVzLnNhZmVVcGRhdGUgPT09IG51bGwgJiYgdXBkYXRlcy51bnNhZmVVcGRhdGUgPT09IG51bGw7XG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBjb25zdCBsaXN0RGF0YSA9IC8qKiBAdHlwZSB7RXh0ZW5zaW9uTGlzdERhdGF9ICovIChleHRzKTtcblxuICAgIC8vIGlmIHdlJ3JlIGp1c3QgZ2V0dGluZyB0aGUgZGF0YSwgc2hvcnQgY2lyY3VpdCByZXR1cm4gaGVyZSBzaW5jZSB3ZSBkb24ndCBuZWVkIHRvIGRvIGFueVxuICAgIC8vIGZvcm1hdHRpbmcgbG9naWNcbiAgICBpZiAodGhpcy5pc0pzb25PdXRwdXQpIHtcbiAgICAgIHJldHVybiBsaXN0RGF0YTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IFtuYW1lLCBkYXRhXSBvZiBfLnRvUGFpcnMobGlzdERhdGEpKSB7XG4gICAgICBsZXQgaW5zdGFsbFR4dCA9ICcgW25vdCBpbnN0YWxsZWRdJy5ncmV5O1xuICAgICAgbGV0IHVwZGF0ZVR4dCA9ICcnO1xuICAgICAgbGV0IHVwVG9EYXRlVHh0ID0gJyc7XG4gICAgICBsZXQgdW5zYWZlVXBkYXRlVHh0ID0gJyc7XG4gICAgICBpZiAoZGF0YS5pbnN0YWxsZWQpIHtcbiAgICAgICAgY29uc3Qge2luc3RhbGxUeXBlLCBpbnN0YWxsU3BlYywgdXBkYXRlVmVyc2lvbiwgdW5zYWZlVXBkYXRlVmVyc2lvbiwgdmVyc2lvbiwgdXBUb0RhdGV9ID1cbiAgICAgICAgICBkYXRhO1xuICAgICAgICBsZXQgdHlwZVR4dDtcbiAgICAgICAgc3dpdGNoIChpbnN0YWxsVHlwZSkge1xuICAgICAgICAgIGNhc2UgSU5TVEFMTF9UWVBFX0dJVDpcbiAgICAgICAgICBjYXNlIElOU1RBTExfVFlQRV9HSVRIVUI6XG4gICAgICAgICAgICB0eXBlVHh0ID0gYChjbG9uZWQgZnJvbSAke2luc3RhbGxTcGVjfSlgLnllbGxvdztcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgIGNhc2UgSU5TVEFMTF9UWVBFX0xPQ0FMOlxuICAgICAgICAgICAgdHlwZVR4dCA9IGAobGlua2VkIGZyb20gJHtpbnN0YWxsU3BlY30pYC5tYWdlbnRhO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgIHR5cGVUeHQgPSAnKE5QTSknO1xuICAgICAgICB9XG4gICAgICAgIGluc3RhbGxUeHQgPSBgQCR7dmVyc2lvbi55ZWxsb3d9ICR7KCdbaW5zdGFsbGVkICcgKyB0eXBlVHh0ICsgJ10nKS5ncmVlbn1gO1xuXG4gICAgICAgIGlmIChzaG93VXBkYXRlcykge1xuICAgICAgICAgIGlmICh1cGRhdGVWZXJzaW9uKSB7XG4gICAgICAgICAgICB1cGRhdGVUeHQgPSBgIFske3VwZGF0ZVZlcnNpb259IGF2YWlsYWJsZV1gLm1hZ2VudGE7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh1cFRvRGF0ZSkge1xuICAgICAgICAgICAgdXBUb0RhdGVUeHQgPSBgIFtVcCB0byBkYXRlXWAuZ3JlZW47XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmICh1bnNhZmVVcGRhdGVWZXJzaW9uKSB7XG4gICAgICAgICAgICB1bnNhZmVVcGRhdGVUeHQgPSBgIFske3Vuc2FmZVVwZGF0ZVZlcnNpb259IGF2YWlsYWJsZSAocG90ZW50aWFsbHkgdW5zYWZlKV1gLmN5YW47XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHRoaXMubG9nLmxvZyhgLSAke25hbWUueWVsbG93fSR7aW5zdGFsbFR4dH0ke3VwZGF0ZVR4dH0ke3VwVG9EYXRlVHh0fSR7dW5zYWZlVXBkYXRlVHh0fWApO1xuICAgIH1cblxuICAgIHJldHVybiBsaXN0RGF0YTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnN0YWxsIGFuIGV4dGVuc2lvblxuICAgKlxuICAgKiBAcGFyYW0ge0luc3RhbGxBcmdzfSBhcmdzXG4gICAqIEByZXR1cm4ge1Byb21pc2U8RXh0UmVjb3JkPEV4dFR5cGU+Pn0gbWFwIG9mIGFsbCBpbnN0YWxsZWQgZXh0ZW5zaW9uIG5hbWVzIHRvIGV4dGVuc2lvbiBkYXRhXG4gICAqL1xuICBhc3luYyBfaW5zdGFsbCh7aW5zdGFsbFNwZWMsIGluc3RhbGxUeXBlLCBwYWNrYWdlTmFtZX0pIHtcbiAgICAvKiogQHR5cGUge0V4dGVuc2lvbkZpZWxkczxFeHRUeXBlPn0gKi9cbiAgICBsZXQgZXh0RGF0YTtcblxuICAgIGlmIChwYWNrYWdlTmFtZSAmJiBbSU5TVEFMTF9UWVBFX0xPQ0FMLCBJTlNUQUxMX1RZUEVfTlBNXS5pbmNsdWRlcyhpbnN0YWxsVHlwZSkpIHtcbiAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IoYFdoZW4gdXNpbmcgLS1zb3VyY2U9JHtpbnN0YWxsVHlwZX0sIGNhbm5vdCBhbHNvIHVzZSAtLXBhY2thZ2VgKTtcbiAgICB9XG5cbiAgICBpZiAoIXBhY2thZ2VOYW1lICYmIFtJTlNUQUxMX1RZUEVfR0lULCBJTlNUQUxMX1RZUEVfR0lUSFVCXS5pbmNsdWRlcyhpbnN0YWxsVHlwZSkpIHtcbiAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IoYFdoZW4gdXNpbmcgLS1zb3VyY2U9JHtpbnN0YWxsVHlwZX0sIG11c3QgYWxzbyB1c2UgLS1wYWNrYWdlYCk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQHR5cGUge0luc3RhbGxWaWFOcG1BcmdzfVxuICAgICAqL1xuICAgIGxldCBpbnN0YWxsT3B0cztcblxuICAgIC8qKlxuICAgICAqIFRoZSBwcm9iYWJsZSAoPykgbmFtZSBvZiB0aGUgZXh0ZW5zaW9uIGRlcml2ZWQgZnJvbSB0aGUgaW5zdGFsbCBzcGVjLlxuICAgICAqXG4gICAgICogSWYgdXNpbmcgYSBsb2NhbCBpbnN0YWxsIHR5cGUsIHRoaXMgd2lsbCByZW1haW4gZW1wdHkuXG4gICAgICogQHR5cGUge3N0cmluZ31cbiAgICAgKi9cbiAgICBsZXQgcHJvYmFibGVFeHROYW1lID0gJyc7XG5cbiAgICAvLyBkZXBlbmRpbmcgb24gYGluc3RhbGxUeXBlYCwgYnVpbGQgdGhlIG9wdGlvbnMgdG8gcGFzcyBpbnRvIGBpbnN0YWxsVmlhTnBtYFxuICAgIGlmIChpbnN0YWxsVHlwZSA9PT0gSU5TVEFMTF9UWVBFX0dJVEhVQikge1xuICAgICAgaWYgKGluc3RhbGxTcGVjLnNwbGl0KCcvJykubGVuZ3RoICE9PSAyKSB7XG4gICAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IoXG4gICAgICAgICAgYEdpdGh1YiAke3RoaXMudHlwZX0gc3BlYyAke2luc3RhbGxTcGVjfSBhcHBlYXJlZCB0byBiZSBpbnZhbGlkOyBgICtcbiAgICAgICAgICAgICdpdCBzaG91bGQgYmUgb2YgdGhlIGZvcm0gPG9yZz4vPHJlcG8+J1xuICAgICAgICApO1xuICAgICAgfVxuICAgICAgaW5zdGFsbE9wdHMgPSB7XG4gICAgICAgIGluc3RhbGxTcGVjLFxuICAgICAgICBwa2dOYW1lOiAvKiogQHR5cGUge3N0cmluZ30gKi8gKHBhY2thZ2VOYW1lKSxcbiAgICAgIH07XG4gICAgICBwcm9iYWJsZUV4dE5hbWUgPSBpbnN0YWxsU3BlYztcbiAgICB9IGVsc2UgaWYgKGluc3RhbGxUeXBlID09PSBJTlNUQUxMX1RZUEVfR0lUKSB7XG4gICAgICAvLyBnaXQgdXJscyBjYW4gaGF2ZSAnLmdpdCcgYXQgdGhlIGVuZCwgYnV0IHRoaXMgaXMgbm90IG5lY2Vzc2FyeSBhbmQgd291bGQgY29tcGxpY2F0ZSB0aGVcbiAgICAgIC8vIHdheSB3ZSBkb3dubG9hZCBhbmQgbmFtZSBkaXJlY3Rvcmllcywgc28gd2UgY2FuIGp1c3QgcmVtb3ZlIGl0XG4gICAgICBpbnN0YWxsU3BlYyA9IGluc3RhbGxTcGVjLnJlcGxhY2UoL1xcLmdpdCQvLCAnJyk7XG4gICAgICBpbnN0YWxsT3B0cyA9IHtcbiAgICAgICAgaW5zdGFsbFNwZWMsXG4gICAgICAgIHBrZ05hbWU6IC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAocGFja2FnZU5hbWUpLFxuICAgICAgfTtcbiAgICAgIHByb2JhYmxlRXh0TmFtZSA9IGluc3RhbGxTcGVjO1xuICAgIH0gZWxzZSB7XG4gICAgICBsZXQgcGtnTmFtZSwgcGtnVmVyO1xuICAgICAgaWYgKGluc3RhbGxUeXBlID09PSBJTlNUQUxMX1RZUEVfTE9DQUwpIHtcbiAgICAgICAgcGtnTmFtZSA9IHBhdGguaXNBYnNvbHV0ZShpbnN0YWxsU3BlYykgPyBpbnN0YWxsU3BlYyA6IHBhdGgucmVzb2x2ZShpbnN0YWxsU3BlYyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBhdCB0aGlzIHBvaW50IHdlIGhhdmUgZWl0aGVyIGFuIG5wbSBwYWNrYWdlIG9yIGFuIGFwcGl1bSB2ZXJpZmllZCBleHRlbnNpb25cbiAgICAgICAgLy8gbmFtZSBvciBhIGxvY2FsIHBhdGguIGJvdGggb2Ygd2hpY2ggd2lsbCBiZSBpbnN0YWxsZWQgdmlhIG5wbS5cbiAgICAgICAgLy8gZXh0ZW5zaW9ucyBpbnN0YWxsZWQgdmlhIG5wbSBjYW4gaW5jbHVkZSB2ZXJzaW9ucyBvciB0YWdzIGFmdGVyIHRoZSAnQCdcbiAgICAgICAgLy8gc2lnbiwgc28gY2hlY2sgZm9yIHRoYXQuIFdlIGFsc28gbmVlZCB0byBiZSBjYXJlZnVsIHRoYXQgcGFja2FnZSBuYW1lcyB0aGVtc2VsdmVzIGNhblxuICAgICAgICAvLyBjb250YWluIHRoZSAnQCcgc3ltYm9sLCBhcyBpbiBgbnBtIGluc3RhbGwgQGFwcGl1bS9mYWtlLWRyaXZlckAxLjIuMGBcbiAgICAgICAgbGV0IG5hbWU7XG4gICAgICAgIGNvbnN0IHNwbGl0cyA9IGluc3RhbGxTcGVjLnNwbGl0KCdAJyk7XG4gICAgICAgIGlmIChpbnN0YWxsU3BlY1swXSA9PT0gJ0AnKSB7XG4gICAgICAgICAgLy8gdGhpcyBpcyB0aGUgY2FzZSB3aGVyZSB3ZSBoYXZlIGFuIG5wbSBvcmcgaW5jbHVkZWQgaW4gdGhlIHBhY2thZ2UgbmFtZVxuICAgICAgICAgIFtuYW1lLCBwa2dWZXJdID0gW2BAJHtzcGxpdHNbMV19YCwgc3BsaXRzWzJdXTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAvLyB0aGlzIGlzIHRoZSBjYXNlIHdpdGhvdXQgYW4gbnBtIG9yZ1xuICAgICAgICAgIFtuYW1lLCBwa2dWZXJdID0gc3BsaXRzO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGluc3RhbGxUeXBlID09PSBJTlNUQUxMX1RZUEVfTlBNKSB7XG4gICAgICAgICAgLy8gaWYgd2UncmUgaW5zdGFsbGluZyBhIG5hbWVkIHBhY2thZ2UgZnJvbSBucG0sIHdlIGRvbid0IG5lZWQgdG8gY2hlY2tcbiAgICAgICAgICAvLyBhZ2FpbnN0IHRoZSBhcHBpdW0gZXh0ZW5zaW9uIGxpc3Q7IGp1c3QgdXNlIHRoZSBpbnN0YWxsU3BlYyBhcyBpc1xuICAgICAgICAgIHBrZ05hbWUgPSBuYW1lO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIC8vIGlmIHdlJ3JlIGluc3RhbGxpbmcgYSBuYW1lZCBhcHBpdW0gZHJpdmVyIChsaWtlICd4Y3VpdGVzdCcpIHdlIG5lZWQgdG9cbiAgICAgICAgICAvLyBkZXJlZmVyZW5jZSB0aGUgYWN0dWFsIG5wbSBwYWNrYWdlICgnYXBwaXVwbS14Y3VpdGVzdC1kcml2ZXInKSwgc29cbiAgICAgICAgICAvLyBjaGVjayBpdCBleGlzdHMgYW5kIGdldCB0aGUgY29ycmVjdCBwYWNrYWdlXG4gICAgICAgICAgY29uc3Qga25vd25OYW1lcyA9IE9iamVjdC5rZXlzKHRoaXMua25vd25FeHRlbnNpb25zKTtcbiAgICAgICAgICBpZiAoIV8uaW5jbHVkZXMoa25vd25OYW1lcywgbmFtZSkpIHtcbiAgICAgICAgICAgIGNvbnN0IG1zZyA9XG4gICAgICAgICAgICAgIGBDb3VsZCBub3QgcmVzb2x2ZSAke3RoaXMudHlwZX07IGFyZSB5b3Ugc3VyZSBpdCdzIGluIHRoZSBsaXN0IGAgK1xuICAgICAgICAgICAgICBgb2Ygc3VwcG9ydGVkICR7dGhpcy50eXBlfXM/ICR7SlNPTi5zdHJpbmdpZnkoa25vd25OYW1lcyl9YDtcbiAgICAgICAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IobXNnKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcHJvYmFibGVFeHROYW1lID0gbmFtZTtcbiAgICAgICAgICBwa2dOYW1lID0gdGhpcy5rbm93bkV4dGVuc2lvbnNbbmFtZV07XG4gICAgICAgICAgLy8gZ2l2ZW4gdGhhdCB3ZSdsbCB1c2UgdGhlIGluc3RhbGwgdHlwZSBpbiB0aGUgZHJpdmVyIGpzb24sIHN0b3JlIGl0IGFzXG4gICAgICAgICAgLy8gJ25wbScgbm93XG4gICAgICAgICAgaW5zdGFsbFR5cGUgPSBJTlNUQUxMX1RZUEVfTlBNO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpbnN0YWxsT3B0cyA9IHtpbnN0YWxsU3BlYywgcGtnTmFtZSwgcGtnVmVyfTtcbiAgICB9XG5cbiAgICAvLyBmYWlsIGZhc3QgaGVyZSBpZiB3ZSBjYW5cbiAgICBpZiAocHJvYmFibGVFeHROYW1lICYmIHRoaXMuY29uZmlnLmlzSW5zdGFsbGVkKHByb2JhYmxlRXh0TmFtZSkpIHtcbiAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IoXG4gICAgICAgIGBBICR7dGhpcy50eXBlfSBuYW1lZCBcIiR7cHJvYmFibGVFeHROYW1lfVwiIGlzIGFscmVhZHkgaW5zdGFsbGVkLiBgICtcbiAgICAgICAgICBgRGlkIHlvdSBtZWFuIHRvIHVwZGF0ZT8gUnVuIFwiYXBwaXVtICR7dGhpcy50eXBlfSB1cGRhdGVcIi4gU2VlIGAgK1xuICAgICAgICAgIGBpbnN0YWxsZWQgJHt0aGlzLnR5cGV9cyB3aXRoIFwiYXBwaXVtICR7dGhpcy50eXBlfSBsaXN0IC0taW5zdGFsbGVkXCIuYFxuICAgICAgKTtcbiAgICB9XG5cbiAgICBleHREYXRhID0gYXdhaXQgdGhpcy5pbnN0YWxsVmlhTnBtKGluc3RhbGxPcHRzKTtcblxuICAgIC8vIHRoaXMgX3Nob3VsZF8gYmUgdGhlIHNhbWUgYXMgYHByb2JhYmx5RXh0TmFtZWAgYXMgdGhlIG9uZSBkZXJpdmVkIGFib3ZlIHVubGVzc1xuICAgIC8vIGluc3RhbGwgdHlwZSBpcyBsb2NhbC5cbiAgICBjb25zdCBleHROYW1lID0gZXh0RGF0YVsvKiogQHR5cGUge3N0cmluZ30gKi8gKGAke3RoaXMudHlwZX1OYW1lYCldO1xuXG4gICAgLy8gY2hlY2sgX2Egc2Vjb25kIHRpbWVfIHdpdGggdGhlIG1vcmUtYWNjdXJhdGUgZXh0TmFtZVxuICAgIGlmICh0aGlzLmNvbmZpZy5pc0luc3RhbGxlZChleHROYW1lKSkge1xuICAgICAgdGhyb3cgdGhpcy5fY3JlYXRlRmF0YWxFcnJvcihcbiAgICAgICAgYEEgJHt0aGlzLnR5cGV9IG5hbWVkIFwiJHtleHROYW1lfVwiIGlzIGFscmVhZHkgaW5zdGFsbGVkLiBgICtcbiAgICAgICAgICBgRGlkIHlvdSBtZWFuIHRvIHVwZGF0ZT8gUnVuIFwiYXBwaXVtICR7dGhpcy50eXBlfSB1cGRhdGVcIi4gU2VlIGAgK1xuICAgICAgICAgIGBpbnN0YWxsZWQgJHt0aGlzLnR5cGV9cyB3aXRoIFwiYXBwaXVtICR7dGhpcy50eXBlfSBsaXN0IC0taW5zdGFsbGVkXCIuYFxuICAgICAgKTtcbiAgICB9XG5cbiAgICAvLyB0aGlzIGZpZWxkIGRvZXMgbm90IGV4aXN0IGFzIHN1Y2ggaW4gdGhlIG1hbmlmZXN0IChpdCdzIHVzZWQgYXMgYSBwcm9wZXJ0eSBuYW1lIGluc3RlYWQpXG4gICAgLy8gc28gdGhhdCdzIHdoeSBpdCdzIGJlaW5nIHJlbW92ZWQgaGVyZS5cbiAgICBkZWxldGUgZXh0RGF0YVsvKiogQHR5cGUge3N0cmluZ30gKi8gKGAke3RoaXMudHlwZX1OYW1lYCldO1xuXG4gICAgLyoqIEB0eXBlIHtFeHRNYW5pZmVzdDxFeHRUeXBlPn0gKi9cbiAgICBjb25zdCBleHRNYW5pZmVzdCA9IHsuLi5leHREYXRhLCBpbnN0YWxsVHlwZSwgaW5zdGFsbFNwZWN9O1xuICAgIGNvbnN0IFtlcnJvcnMsIHdhcm5pbmdzXSA9IGF3YWl0IEIuYWxsKFtcbiAgICAgIHRoaXMuY29uZmlnLmdldFByb2JsZW1zKGV4dE5hbWUsIGV4dE1hbmlmZXN0KSxcbiAgICAgIHRoaXMuY29uZmlnLmdldFdhcm5pbmdzKGV4dE5hbWUsIGV4dE1hbmlmZXN0KSxcbiAgICBdKTtcbiAgICBjb25zdCBlcnJvck1hcCA9IG5ldyBNYXAoW1tleHROYW1lLCBlcnJvcnNdXSk7XG4gICAgY29uc3Qgd2FybmluZ01hcCA9IG5ldyBNYXAoW1tleHROYW1lLCB3YXJuaW5nc11dKTtcbiAgICBjb25zdCB7ZXJyb3JTdW1tYXJpZXMsIHdhcm5pbmdTdW1tYXJpZXN9ID0gdGhpcy5jb25maWcuZ2V0VmFsaWRhdGlvblJlc3VsdFN1bW1hcmllcyhcbiAgICAgIGVycm9yTWFwLFxuICAgICAgd2FybmluZ01hcFxuICAgICk7XG5cbiAgICBpZiAoIV8uaXNFbXB0eShlcnJvclN1bW1hcmllcykpIHtcbiAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IoZXJyb3JTdW1tYXJpZXMuam9pbignXFxuJykpO1xuICAgIH1cblxuICAgIC8vIG5vdGUgdGhhdCB3ZSB3b24ndCBzaG93IGFueSB3YXJuaW5ncyBpZiB0aGVyZSB3ZXJlIGVycm9ycy5cbiAgICBpZiAoIV8uaXNFbXB0eSh3YXJuaW5nU3VtbWFyaWVzKSkge1xuICAgICAgdGhpcy5sb2cud2Fybih3YXJuaW5nU3VtbWFyaWVzLmpvaW4oJ1xcbicpKTtcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLmNvbmZpZy5hZGRFeHRlbnNpb24oZXh0TmFtZSwgZXh0TWFuaWZlc3QpO1xuXG4gICAgLy8gdXBkYXRlIHRoZSBpZiB3ZSd2ZSBjaGFuZ2VkIHRoZSBsb2NhbCBgcGFja2FnZS5qc29uYFxuICAgIGlmIChhd2FpdCBlbnYuaGFzQXBwaXVtRGVwZW5kZW5jeSh0aGlzLmNvbmZpZy5hcHBpdW1Ib21lKSkge1xuICAgICAgYXdhaXQgcGFja2FnZURpZENoYW5nZSh0aGlzLmNvbmZpZy5hcHBpdW1Ib21lKTtcbiAgICB9XG5cbiAgICAvLyBsb2cgaW5mbyBmb3IgdGhlIHVzZXJcbiAgICB0aGlzLmxvZy5pbmZvKHRoaXMuZ2V0UG9zdEluc3RhbGxUZXh0KHtleHROYW1lLCBleHREYXRhfSkpO1xuXG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLmluc3RhbGxlZEV4dGVuc2lvbnM7XG4gIH1cblxuICAvKipcbiAgICogSW5zdGFsbCBhbiBleHRlbnNpb24gdmlhIE5QTVxuICAgKlxuICAgKiBAcGFyYW0ge0luc3RhbGxWaWFOcG1BcmdzfSBhcmdzXG4gICAqL1xuICBhc3luYyBpbnN0YWxsVmlhTnBtKHtpbnN0YWxsU3BlYywgcGtnTmFtZSwgcGtnVmVyfSkge1xuICAgIGNvbnN0IG5wbVNwZWMgPSBgJHtwa2dOYW1lfSR7cGtnVmVyID8gJ0AnICsgcGtnVmVyIDogJyd9YDtcbiAgICBjb25zdCBzcGVjTXNnID0gbnBtU3BlYyA9PT0gaW5zdGFsbFNwZWMgPyAnJyA6IGAgdXNpbmcgTlBNIGluc3RhbGwgc3BlYyAnJHtucG1TcGVjfSdgO1xuICAgIGNvbnN0IG1zZyA9IGBJbnN0YWxsaW5nICcke2luc3RhbGxTcGVjfScke3NwZWNNc2d9YDtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcGtnSnNvbkRhdGEgPSBhd2FpdCBzcGluV2l0aCh0aGlzLmlzSnNvbk91dHB1dCwgbXNnLCBhc3luYyAoKSA9PiB7XG4gICAgICAgIGNvbnN0IHBrZ0pzb25EYXRhID0gYXdhaXQgbnBtLmluc3RhbGxQYWNrYWdlKHRoaXMuY29uZmlnLmFwcGl1bUhvbWUsIHBrZ05hbWUsIHtcbiAgICAgICAgICBwa2dWZXIsXG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLnZhbGlkYXRlUGFja2FnZUpzb24ocGtnSnNvbkRhdGEsIGluc3RhbGxTcGVjKTtcbiAgICAgICAgcmV0dXJuIHBrZ0pzb25EYXRhO1xuICAgICAgfSk7XG5cbiAgICAgIHJldHVybiB0aGlzLmdldEV4dGVuc2lvbkZpZWxkcyhwa2dKc29uRGF0YSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICB0aHJvdyB0aGlzLl9jcmVhdGVGYXRhbEVycm9yKGBFbmNvdW50ZXJlZCBhbiBlcnJvciB3aGVuIGluc3RhbGxpbmcgcGFja2FnZTogJHtlcnIubWVzc2FnZX1gKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSB0ZXh0IHdoaWNoIHNob3VsZCBiZSBkaXNwbGF5ZWQgdG8gdGhlIHVzZXIgYWZ0ZXIgYW4gZXh0ZW5zaW9uIGhhcyBiZWVuIGluc3RhbGxlZC4gVGhpc1xuICAgKiBpcyBkZXNpZ25lZCB0byBiZSBvdmVycmlkZGVuIGJ5IGRyaXZlcnMvcGx1Z2lucyB3aXRoIHRoZWlyIG93biBwYXJ0aWN1bGFyIHRleHQuXG4gICAqXG4gICAqIEBwYXJhbSB7RXh0ZW5zaW9uQXJnc30gYXJnc1xuICAgKiBAcmV0dXJucyB7c3RyaW5nfVxuICAgKi9cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzXG4gIGdldFBvc3RJbnN0YWxsVGV4dChhcmdzKSB7XG4gICAgdGhyb3cgdGhpcy5fY3JlYXRlRmF0YWxFcnJvcignTXVzdCBiZSBpbXBsZW1lbnRlZCBpbiBmaW5hbCBjbGFzcycpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRha2UgYW4gTlBNIG1vZHVsZSdzIHBhY2thZ2UuanNvbiBhbmQgZXh0cmFjdCBBcHBpdW0gZHJpdmVyIGluZm9ybWF0aW9uIGZyb20gYSBzcGVjaWFsXG4gICAqICdhcHBpdW0nIGZpZWxkIGluIHRoZSBKU09OIGRhdGEuIFdlIG5lZWQgdGhpcyBpbmZvcm1hdGlvbiB0byBlLmcuIGRldGVybWluZSB3aGljaCBjbGFzcyB0b1xuICAgKiBsb2FkIGFzIHRoZSBtYWluIGRyaXZlciBjbGFzcywgb3IgdG8gYmUgYWJsZSB0byBkZXRlY3QgaW5jb21wYXRpYmlsaXRpZXMgYmV0d2VlbiBkcml2ZXIgYW5kXG4gICAqIGFwcGl1bSB2ZXJzaW9ucy5cbiAgICpcbiAgICogQHBhcmFtIHtFeHRQYWNrYWdlSnNvbjxFeHRUeXBlPn0gcGtnSnNvbiAtIHRoZSBwYWNrYWdlLmpzb24gZGF0YSBmb3IgYSBkcml2ZXIgbW9kdWxlLCBhcyBpZiBpdCBoYWQgYmVlbiBzdHJhaWdodGZvcndhcmRseSAncmVxdWlyZSdkXG4gICAqIEByZXR1cm5zIHtFeHRlbnNpb25GaWVsZHM8RXh0VHlwZT59XG4gICAqL1xuICBnZXRFeHRlbnNpb25GaWVsZHMocGtnSnNvbikge1xuICAgIGNvbnN0IHthcHBpdW0sIG5hbWUsIHZlcnNpb24sIHBlZXJEZXBlbmRlbmNpZXN9ID0gcGtnSnNvbjtcblxuICAgIC8qKiBAdHlwZSB7dW5rbm93bn0gKi9cbiAgICBjb25zdCByZXN1bHQgPSB7XG4gICAgICAuLi5hcHBpdW0sXG4gICAgICBwa2dOYW1lOiBuYW1lLFxuICAgICAgdmVyc2lvbixcbiAgICAgIGFwcGl1bVZlcnNpb246IHBlZXJEZXBlbmRlbmNpZXM/LmFwcGl1bSxcbiAgICB9O1xuICAgIHJldHVybiAvKiogQHR5cGUge0V4dGVuc2lvbkZpZWxkczxFeHRUeXBlPn0gKi8gKHJlc3VsdCk7XG4gIH1cblxuICAvKipcbiAgICogVmFsaWRhdGVzIHRoZSBfcmVxdWlyZWRfIHJvb3QgZmllbGRzIG9mIGFuIGV4dGVuc2lvbidzIGBwYWNrYWdlLmpzb25gIGZpbGUuXG4gICAqXG4gICAqIFRoZXNlIHJlcXVpcmVkIGZpZWxkcyBhcmU6XG4gICAqIC0gYG5hbWVgXG4gICAqIC0gYHZlcnNpb25gXG4gICAqIC0gYGFwcGl1bWBcbiAgICogQHBhcmFtIHtpbXBvcnQoJ3R5cGUtZmVzdCcpLlBhY2thZ2VKc29ufSBwa2dKc29uIC0gYHBhY2thZ2UuanNvbmAgb2YgZXh0ZW5zaW9uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YWxsU3BlYyAtIEV4dGVuc2lvbiBuYW1lL3NwZWNcbiAgICogQHRocm93cyB7UmVmZXJlbmNlRXJyb3J9IElmIGBwYWNrYWdlLmpzb25gIGhhcyBhIG1pc3Npbmcgb3IgaW52YWxpZCBmaWVsZFxuICAgKiBAcmV0dXJucyB7cGtnSnNvbiBpcyBFeHRQYWNrYWdlSnNvbjxFeHRUeXBlPn1cbiAgICovXG4gIHZhbGlkYXRlUGFja2FnZUpzb24ocGtnSnNvbiwgaW5zdGFsbFNwZWMpIHtcbiAgICBjb25zdCB7YXBwaXVtLCBuYW1lLCB2ZXJzaW9ufSA9IC8qKiBAdHlwZSB7RXh0UGFja2FnZUpzb248RXh0VHlwZT59ICovIChwa2dKc29uKTtcblxuICAgIC8qKlxuICAgICAqXG4gICAgICogQHBhcmFtIHtzdHJpbmd9IGZpZWxkXG4gICAgICogQHJldHVybnMge1JlZmVyZW5jZUVycm9yfVxuICAgICAqL1xuICAgIGNvbnN0IGNyZWF0ZU1pc3NpbmdGaWVsZEVycm9yID0gKGZpZWxkKSA9PlxuICAgICAgbmV3IFJlZmVyZW5jZUVycm9yKFxuICAgICAgICBgJHt0aGlzLnR5cGV9IFwiJHtpbnN0YWxsU3BlY31cIiBpbnZhbGlkOyBtaXNzaW5nIGEgXFxgJHtmaWVsZH1cXGAgZmllbGQgb2YgaXRzIFxcYHBhY2thZ2UuanNvblxcYGBcbiAgICAgICk7XG5cbiAgICBpZiAoIW5hbWUpIHtcbiAgICAgIHRocm93IGNyZWF0ZU1pc3NpbmdGaWVsZEVycm9yKCduYW1lJyk7XG4gICAgfVxuICAgIGlmICghdmVyc2lvbikge1xuICAgICAgdGhyb3cgY3JlYXRlTWlzc2luZ0ZpZWxkRXJyb3IoJ3ZlcnNpb24nKTtcbiAgICB9XG4gICAgaWYgKCFhcHBpdW0pIHtcbiAgICAgIHRocm93IGNyZWF0ZU1pc3NpbmdGaWVsZEVycm9yKCdhcHBpdW0nKTtcbiAgICB9XG5cbiAgICB0aGlzLnZhbGlkYXRlRXh0ZW5zaW9uRmllbGRzKGFwcGl1bSwgaW5zdGFsbFNwZWMpO1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogRm9yIGFueSBgcGFja2FnZS5qc29uYCBmaWVsZHMgd2hpY2ggYSBwYXJ0aWN1bGFyIHR5cGUgb2YgZXh0ZW5zaW9uIHJlcXVpcmVzLCB2YWxpZGF0ZSB0aGVcbiAgICogcHJlc2VuY2UgYW5kIGZvcm0gb2YgdGhvc2UgZmllbGRzIG9uIHRoZSBgcGFja2FnZS5qc29uYCBkYXRhLCB0aHJvd2luZyBhbiBlcnJvciBpZiBhbnl0aGluZyBpc1xuICAgKiBhbWlzcy5cbiAgICpcbiAgICogQHBhcmFtIHtFeHRNZXRhZGF0YTxFeHRUeXBlPn0gZXh0TWV0YWRhdGEgLSB0aGUgZGF0YSBpbiB0aGUgXCJhcHBpdW1cIiBmaWVsZCBvZiBgcGFja2FnZS5qc29uYCBmb3IgYW4gZXh0ZW5zaW9uXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YWxsU3BlYyAtIEV4dGVuc2lvbiBuYW1lL3NwZWNcbiAgICovXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFyc1xuICB2YWxpZGF0ZUV4dGVuc2lvbkZpZWxkcyhleHRNZXRhZGF0YSwgaW5zdGFsbFNwZWMpIHtcbiAgICB0aHJvdyB0aGlzLl9jcmVhdGVGYXRhbEVycm9yKCdNdXN0IGJlIGltcGxlbWVudGVkIGluIGZpbmFsIGNsYXNzJyk7XG4gIH1cblxuICAvKipcbiAgICogVW5pbnN0YWxsIGFuIGV4dGVuc2lvbi5cbiAgICpcbiAgICogRmlyc3QgdHJpZXMgdG8gZG8gdGhpcyB2aWEgYG5wbSB1bmluc3RhbGxgLCBidXQgaWYgdGhhdCBmYWlscywganVzdCBgcm0gLXJmYCdzIHRoZSBleHRlbnNpb24gZGlyLlxuICAgKlxuICAgKiBXaWxsIG9ubHkgcmVtb3ZlIHRoZSBleHRlbnNpb24gZnJvbSB0aGUgbWFuaWZlc3QgaWYgaXQgaGFzIGJlZW4gc3VjY2Vzc2Z1bGx5IHJlbW92ZWQuXG4gICAqXG4gICAqIEBwYXJhbSB7VW5pbnN0YWxsT3B0c30gb3B0c1xuICAgKiBAcmV0dXJuIHtQcm9taXNlPEV4dFJlY29yZDxFeHRUeXBlPj59IG1hcCBvZiBhbGwgaW5zdGFsbGVkIGV4dGVuc2lvbiBuYW1lcyB0byBleHRlbnNpb24gZGF0YSAod2l0aG91dCB0aGUgZXh0ZW5zaW9uIGp1c3QgdW5pbnN0YWxsZWQpXG4gICAqL1xuICBhc3luYyBfdW5pbnN0YWxsKHtpbnN0YWxsU3BlY30pIHtcbiAgICBpZiAoIXRoaXMuY29uZmlnLmlzSW5zdGFsbGVkKGluc3RhbGxTcGVjKSkge1xuICAgICAgdGhyb3cgdGhpcy5fY3JlYXRlRmF0YWxFcnJvcihcbiAgICAgICAgYENhbid0IHVuaW5zdGFsbCAke3RoaXMudHlwZX0gJyR7aW5zdGFsbFNwZWN9JzsgaXQgaXMgbm90IGluc3RhbGxlZGBcbiAgICAgICk7XG4gICAgfVxuICAgIGNvbnN0IHBrZ05hbWUgPSB0aGlzLmNvbmZpZy5pbnN0YWxsZWRFeHRlbnNpb25zW2luc3RhbGxTcGVjXS5wa2dOYW1lO1xuICAgIGF3YWl0IG5wbS51bmluc3RhbGxQYWNrYWdlKHRoaXMuY29uZmlnLmFwcGl1bUhvbWUsIHBrZ05hbWUpO1xuICAgIGF3YWl0IHRoaXMuY29uZmlnLnJlbW92ZUV4dGVuc2lvbihpbnN0YWxsU3BlYyk7XG4gICAgdGhpcy5sb2cub2soYFN1Y2Nlc3NmdWxseSB1bmluc3RhbGxlZCAke3RoaXMudHlwZX0gJyR7aW5zdGFsbFNwZWN9J2AuZ3JlZW4pO1xuICAgIHJldHVybiB0aGlzLmNvbmZpZy5pbnN0YWxsZWRFeHRlbnNpb25zO1xuICB9XG5cbiAgLyoqXG4gICAqIEF0dGVtcHQgdG8gdXBkYXRlIG9uZSBvciBtb3JlIGRyaXZlcnMgdXNpbmcgTlBNXG4gICAqXG4gICAqIEBwYXJhbSB7RXh0ZW5zaW9uVXBkYXRlT3B0c30gdXBkYXRlU3BlY1xuICAgKiBAcmV0dXJuIHtQcm9taXNlPEV4dGVuc2lvblVwZGF0ZVJlc3VsdD59XG4gICAqL1xuICBhc3luYyBfdXBkYXRlKHtpbnN0YWxsU3BlYywgdW5zYWZlfSkge1xuICAgIGNvbnN0IHNob3VsZFVwZGF0ZUFsbCA9IGluc3RhbGxTcGVjID09PSBVUERBVEVfQUxMO1xuICAgIC8vIGlmIHdlJ3JlIHNwZWNpZmljYWxseSByZXF1ZXN0aW5nIGFuIHVwZGF0ZSBmb3IgYW4gZXh0ZW5zaW9uLCBtYWtlIHN1cmUgaXQncyBpbnN0YWxsZWRcbiAgICBpZiAoIXNob3VsZFVwZGF0ZUFsbCAmJiAhdGhpcy5jb25maWcuaXNJbnN0YWxsZWQoaW5zdGFsbFNwZWMpKSB7XG4gICAgICB0aHJvdyB0aGlzLl9jcmVhdGVGYXRhbEVycm9yKFxuICAgICAgICBgVGhlICR7dGhpcy50eXBlfSBcIiR7aW5zdGFsbFNwZWN9XCIgd2FzIG5vdCBpbnN0YWxsZWQsIHNvIGNhbid0IGJlIHVwZGF0ZWRgXG4gICAgICApO1xuICAgIH1cbiAgICBjb25zdCBleHRzVG9VcGRhdGUgPSBzaG91bGRVcGRhdGVBbGxcbiAgICAgID8gT2JqZWN0LmtleXModGhpcy5jb25maWcuaW5zdGFsbGVkRXh0ZW5zaW9ucylcbiAgICAgIDogW2luc3RhbGxTcGVjXTtcblxuICAgIC8vICdlcnJvcnMnIHdpbGwgaGF2ZSBleHQgbmFtZXMgYXMga2V5cyBhbmQgZXJyb3Igb2JqZWN0cyBhcyB2YWx1ZXNcbiAgICAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsRXJyb3I+fSAqL1xuICAgIGNvbnN0IGVycm9ycyA9IHt9O1xuXG4gICAgLy8gJ3VwZGF0ZXMnIHdpbGwgaGF2ZSBleHQgbmFtZXMgYXMga2V5cyBhbmQgdXBkYXRlIG9iamVjdHMgYXMgdmFsdWVzLCB3aGVyZSBhbiB1cGRhdGVcbiAgICAvLyBvYmplY3QgaXMgb2YgdGhlIGZvcm0ge2Zyb206IHZlcnNpb25TdHJpbmcsIHRvOiB2ZXJzaW9uU3RyaW5nfVxuICAgIC8qKiBAdHlwZSB7UmVjb3JkPHN0cmluZyxVcGRhdGVSZXBvcnQ+fSAqL1xuICAgIGNvbnN0IHVwZGF0ZXMgPSB7fTtcblxuICAgIGZvciAoY29uc3QgZSBvZiBleHRzVG9VcGRhdGUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHNwaW5XaXRoKHRoaXMuaXNKc29uT3V0cHV0LCBgQ2hlY2tpbmcgaWYgJHt0aGlzLnR5cGV9ICcke2V9JyBpcyB1cGRhdGFibGVgLCAoKSA9PiB7XG4gICAgICAgICAgaWYgKHRoaXMuY29uZmlnLmluc3RhbGxlZEV4dGVuc2lvbnNbZV0uaW5zdGFsbFR5cGUgIT09IElOU1RBTExfVFlQRV9OUE0pIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBOb3RVcGRhdGFibGVFcnJvcigpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IHVwZGF0ZSA9IGF3YWl0IHNwaW5XaXRoKFxuICAgICAgICAgIHRoaXMuaXNKc29uT3V0cHV0LFxuICAgICAgICAgIGBDaGVja2luZyBpZiAke3RoaXMudHlwZX0gJyR7ZX0nIG5lZWRzIGFuIHVwZGF0ZWAsXG4gICAgICAgICAgYXN5bmMgKCkgPT4ge1xuICAgICAgICAgICAgY29uc3QgdXBkYXRlID0gYXdhaXQgdGhpcy5jaGVja0ZvckV4dGVuc2lvblVwZGF0ZShlKTtcbiAgICAgICAgICAgIGlmICghKHVwZGF0ZS5zYWZlVXBkYXRlIHx8IHVwZGF0ZS51bnNhZmVVcGRhdGUpKSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBOb1VwZGF0ZXNBdmFpbGFibGVFcnJvcigpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHVwZGF0ZTtcbiAgICAgICAgICB9XG4gICAgICAgICk7XG4gICAgICAgIGlmICghdW5zYWZlICYmICF1cGRhdGUuc2FmZVVwZGF0ZSkge1xuICAgICAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IoXG4gICAgICAgICAgICBgVGhlICR7dGhpcy50eXBlfSAnJHtlfScgaGFzIGEgbWFqb3IgcmV2aXNpb24gdXBkYXRlIGAgK1xuICAgICAgICAgICAgICBgKCR7dXBkYXRlLmN1cnJlbnR9ID0+ICR7dXBkYXRlLnVuc2FmZVVwZGF0ZX0pLCB3aGljaCBjb3VsZCBpbmNsdWRlIGAgK1xuICAgICAgICAgICAgICBgYnJlYWtpbmcgY2hhbmdlcy4gSWYgeW91IHdhbnQgdG8gYXBwbHkgdGhpcyB1cGRhdGUsIHJlLXJ1biB3aXRoIC0tdW5zYWZlYFxuICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgdXBkYXRlVmVyID0gdW5zYWZlICYmIHVwZGF0ZS51bnNhZmVVcGRhdGUgPyB1cGRhdGUudW5zYWZlVXBkYXRlIDogdXBkYXRlLnNhZmVVcGRhdGU7XG4gICAgICAgIGF3YWl0IHNwaW5XaXRoKFxuICAgICAgICAgIHRoaXMuaXNKc29uT3V0cHV0LFxuICAgICAgICAgIGBVcGRhdGluZyBkcml2ZXIgJyR7ZX0nIGZyb20gJHt1cGRhdGUuY3VycmVudH0gdG8gJHt1cGRhdGVWZXJ9YCxcbiAgICAgICAgICBhc3luYyAoKSA9PiBhd2FpdCB0aGlzLnVwZGF0ZUV4dGVuc2lvbihlLCB1cGRhdGVWZXIpXG4gICAgICAgICk7XG4gICAgICAgIHVwZGF0ZXNbZV0gPSB7ZnJvbTogdXBkYXRlLmN1cnJlbnQsIHRvOiB1cGRhdGVWZXJ9O1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGVycm9yc1tlXSA9IGVycjtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB0aGlzLmxvZy5pbmZvKCdVcGRhdGUgcmVwb3J0OicpO1xuXG4gICAgZm9yIChjb25zdCBbZSwgdXBkYXRlXSBvZiBfLnRvUGFpcnModXBkYXRlcykpIHtcbiAgICAgIHRoaXMubG9nLm9rKGAgIC0gJHt0aGlzLnR5cGV9ICR7ZX0gdXBkYXRlZDogJHt1cGRhdGUuZnJvbX0gPT4gJHt1cGRhdGUudG99YC5ncmVlbik7XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBbZSwgZXJyXSBvZiBfLnRvUGFpcnMoZXJyb3JzKSkge1xuICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIE5vdFVwZGF0YWJsZUVycm9yKSB7XG4gICAgICAgIHRoaXMubG9nLndhcm4oXG4gICAgICAgICAgYCAgLSAnJHtlfScgd2FzIG5vdCBpbnN0YWxsZWQgdmlhIG5wbSwgc28gd2UgY291bGQgbm90IGNoZWNrIGAgKyBgZm9yIHVwZGF0ZXNgLnllbGxvd1xuICAgICAgICApO1xuICAgICAgfSBlbHNlIGlmIChlcnIgaW5zdGFuY2VvZiBOb1VwZGF0ZXNBdmFpbGFibGVFcnJvcikge1xuICAgICAgICB0aGlzLmxvZy5pbmZvKGAgIC0gJyR7ZX0nIGhhZCBubyB1cGRhdGVzIGF2YWlsYWJsZWAueWVsbG93KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgbWFrZSBpdCBwb3Agd2l0aCByZWQhXG4gICAgICAgIHRoaXMubG9nLmVycm9yKGAgIC0gJyR7ZX0nIGZhaWxlZCB0byB1cGRhdGU6ICR7ZXJyfWAucmVkKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHt1cGRhdGVzLCBlcnJvcnN9O1xuICB9XG5cbiAgLyoqXG4gICAqIEdpdmVuIGFuIGV4dGVuc2lvbiBuYW1lLCBmaWd1cmUgb3V0IHdoYXQgaXRzIGhpZ2hlc3QgcG9zc2libGUgdmVyc2lvbiB1cGdyYWRlIGlzLCBhbmQgYWxzbyB0aGVcbiAgICogaGlnaGVzdCBwb3NzaWJsZSBzYWZlIHVwZ3JhZGUuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBleHQgLSBuYW1lIG9mIGV4dGVuc2lvblxuICAgKiBAcmV0dXJuIHtQcm9taXNlPFBvc3NpYmxlVXBkYXRlcz59XG4gICAqL1xuICBhc3luYyBjaGVja0ZvckV4dGVuc2lvblVwZGF0ZShleHQpIHtcbiAgICAvLyBUT0RPIGRlY2lkZSBob3cgd2Ugd2FudCB0byBoYW5kbGUgYmV0YSB2ZXJzaW9ucz9cbiAgICAvLyB0aGlzIGlzIGEgaGVscGVyIG1ldGhvZCwgJ2V4dCcgaXMgYXNzdW1lZCB0byBhbHJlYWR5IGJlIGluc3RhbGxlZCBoZXJlLCBhbmQgb2YgdGhlIG5wbVxuICAgIC8vIGluc3RhbGwgdHlwZVxuICAgIGNvbnN0IHt2ZXJzaW9uLCBwa2dOYW1lfSA9IHRoaXMuY29uZmlnLmluc3RhbGxlZEV4dGVuc2lvbnNbZXh0XTtcbiAgICAvKiogQHR5cGUge3N0cmluZz99ICovXG4gICAgbGV0IHVuc2FmZVVwZGF0ZSA9IGF3YWl0IG5wbS5nZXRMYXRlc3RWZXJzaW9uKHRoaXMuY29uZmlnLmFwcGl1bUhvbWUsIHBrZ05hbWUpO1xuICAgIGxldCBzYWZlVXBkYXRlID0gYXdhaXQgbnBtLmdldExhdGVzdFNhZmVVcGdyYWRlVmVyc2lvbihcbiAgICAgIHRoaXMuY29uZmlnLmFwcGl1bUhvbWUsXG4gICAgICBwa2dOYW1lLFxuICAgICAgdmVyc2lvblxuICAgICk7XG4gICAgaWYgKHVuc2FmZVVwZGF0ZSAhPT0gbnVsbCAmJiAhdXRpbC5jb21wYXJlVmVyc2lvbnModW5zYWZlVXBkYXRlLCAnPicsIHZlcnNpb24pKSB7XG4gICAgICAvLyB0aGUgbGF0ZXN0IHZlcnNpb24gaXMgbm90IGdyZWF0ZXIgdGhhbiB0aGUgY3VycmVudCB2ZXJzaW9uLCBzbyB0aGVyZSdzIG5vIHBvc3NpYmxlIHVwZGF0ZVxuICAgICAgdW5zYWZlVXBkYXRlID0gbnVsbDtcbiAgICAgIHNhZmVVcGRhdGUgPSBudWxsO1xuICAgIH1cbiAgICBpZiAodW5zYWZlVXBkYXRlICYmIHVuc2FmZVVwZGF0ZSA9PT0gc2FmZVVwZGF0ZSkge1xuICAgICAgLy8gdGhlIGxhdGVzdCB1cGRhdGUgaXMgdGhlIHNhbWUgYXMgdGhlIHNhZmUgdXBkYXRlLCB3aGljaCBtZWFucyBpdCdzIG5vdCBhY3R1YWxseSB1bnNhZmVcbiAgICAgIHVuc2FmZVVwZGF0ZSA9IG51bGw7XG4gICAgfVxuICAgIGlmIChzYWZlVXBkYXRlICYmICF1dGlsLmNvbXBhcmVWZXJzaW9ucyhzYWZlVXBkYXRlLCAnPicsIHZlcnNpb24pKSB7XG4gICAgICAvLyBldmVuIHRoZSBzYWZlIHVwZGF0ZSBpcyBub3QgbGF0ZXIgdGhhbiB0aGUgY3VycmVudCwgc28gaXQgaXMgbm90IGFjdHVhbGx5IGFuIHVwZGF0ZVxuICAgICAgc2FmZVVwZGF0ZSA9IG51bGw7XG4gICAgfVxuICAgIHJldHVybiB7Y3VycmVudDogdmVyc2lvbiwgc2FmZVVwZGF0ZSwgdW5zYWZlVXBkYXRlfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBY3R1YWxseSB1cGRhdGUgYW4gZXh0ZW5zaW9uIGluc3RhbGxlZCBieSBOUE0sIHVzaW5nIHRoZSBOUE0gY2xpLiBBbmQgdXBkYXRlIHRoZSBpbnN0YWxsYXRpb25cbiAgICogbWFuaWZlc3QuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YWxsU3BlYyAtIG5hbWUgb2YgZXh0ZW5zaW9uIHRvIHVwZGF0ZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gdmVyc2lvbiAtIHZlcnNpb24gc3RyaW5nIGlkZW50aWZpZXIgdG8gdXBkYXRlIGV4dGVuc2lvbiB0b1xuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn1cbiAgICovXG4gIGFzeW5jIHVwZGF0ZUV4dGVuc2lvbihpbnN0YWxsU3BlYywgdmVyc2lvbikge1xuICAgIGNvbnN0IHtwa2dOYW1lfSA9IHRoaXMuY29uZmlnLmluc3RhbGxlZEV4dGVuc2lvbnNbaW5zdGFsbFNwZWNdO1xuICAgIGNvbnN0IGV4dERhdGEgPSBhd2FpdCB0aGlzLmluc3RhbGxWaWFOcG0oe1xuICAgICAgaW5zdGFsbFNwZWMsXG4gICAgICBwa2dOYW1lLFxuICAgICAgcGtnVmVyOiB2ZXJzaW9uLFxuICAgIH0pO1xuICAgIGRlbGV0ZSBleHREYXRhWy8qKiBAdHlwZSB7c3RyaW5nfSAqLyAoYCR7dGhpcy50eXBlfU5hbWVgKV07XG4gICAgYXdhaXQgdGhpcy5jb25maWcudXBkYXRlRXh0ZW5zaW9uKGluc3RhbGxTcGVjLCBleHREYXRhKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIGEgc2NyaXB0IGNhY2hlZCBpbnNpZGUgdGhlIFwic2NyaXB0c1wiIGZpZWxkIHVuZGVyIFwiYXBwaXVtXCJcbiAgICogaW5zaWRlIG9mIHRoZSBkcml2ZXIvcGx1Z2lucyBcInBhY2thZ2UuanNvblwiIGZpbGUuIFdpbGwgdGhyb3dcbiAgICogYW4gZXJyb3IgaWYgdGhlIGRyaXZlci9wbHVnaW4gZG9lcyBub3QgY29udGFpbiBhIFwic2NyaXB0c1wiIGZpZWxkXG4gICAqIHVuZGVybmVhdGggdGhlIFwiYXBwaXVtXCIgZmllbGQgaW4gaXRzIHBhY2thZ2UuanNvbiwgaWYgdGhlXG4gICAqIFwic2NyaXB0c1wiIGZpZWxkIGlzIG5vdCBhIHBsYWluIG9iamVjdCwgb3IgaWYgdGhlIHNjcmlwdE5hbWUgaXNcbiAgICogbm90IGZvdW5kIHdpdGhpbiBcInNjcmlwdHNcIiBvYmplY3QuXG4gICAqXG4gICAqIEBwYXJhbSB7UnVuT3B0aW9uc30gb3B0c1xuICAgKiBAcmV0dXJuIHtQcm9taXNlPFJ1bk91dHB1dD59XG4gICAqL1xuICBhc3luYyBfcnVuKHtpbnN0YWxsU3BlYywgc2NyaXB0TmFtZSwgZXh0cmFBcmdzID0gW119KSB7XG4gICAgaWYgKCF0aGlzLmNvbmZpZy5pc0luc3RhbGxlZChpbnN0YWxsU3BlYykpIHtcbiAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IoYFRoZSAke3RoaXMudHlwZX0gXCIke2luc3RhbGxTcGVjfVwiIGlzIG5vdCBpbnN0YWxsZWRgKTtcbiAgICB9XG5cbiAgICBjb25zdCBleHRDb25maWcgPSB0aGlzLmNvbmZpZy5pbnN0YWxsZWRFeHRlbnNpb25zW2luc3RhbGxTcGVjXTtcblxuICAgIC8vIG5vdGU6IFRTIGNhbm5vdCB1bmRlcnN0YW5kIHRoYXQgXy5oYXMoKSBpcyBhIHR5cGUgZ3VhcmRcbiAgICBpZiAoIWV4dENvbmZpZy5zY3JpcHRzKSB7XG4gICAgICB0aHJvdyB0aGlzLl9jcmVhdGVGYXRhbEVycm9yKFxuICAgICAgICBgVGhlICR7dGhpcy50eXBlfSBuYW1lZCAnJHtpbnN0YWxsU3BlY30nIGRvZXMgbm90IGNvbnRhaW4gdGhlIGAgK1xuICAgICAgICAgIGBcInNjcmlwdHNcIiBmaWVsZCB1bmRlcm5lYXRoIHRoZSBcImFwcGl1bVwiIGZpZWxkIGluIGl0cyBwYWNrYWdlLmpzb25gXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IGV4dFNjcmlwdHMgPSBleHRDb25maWcuc2NyaXB0cztcblxuICAgIGlmICghXy5pc1BsYWluT2JqZWN0KGV4dFNjcmlwdHMpKSB7XG4gICAgICB0aHJvdyB0aGlzLl9jcmVhdGVGYXRhbEVycm9yKFxuICAgICAgICBgVGhlICR7dGhpcy50eXBlfSBuYW1lZCAnJHtpbnN0YWxsU3BlY30nIFwic2NyaXB0c1wiIGZpZWxkIG11c3QgYmUgYSBwbGFpbiBvYmplY3RgXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmICghXy5oYXMoZXh0U2NyaXB0cywgc2NyaXB0TmFtZSkpIHtcbiAgICAgIHRocm93IHRoaXMuX2NyZWF0ZUZhdGFsRXJyb3IoXG4gICAgICAgIGBUaGUgJHt0aGlzLnR5cGV9IG5hbWVkICcke2luc3RhbGxTcGVjfScgZG9lcyBub3Qgc3VwcG9ydCB0aGUgc2NyaXB0OiAnJHtzY3JpcHROYW1lfSdgXG4gICAgICApO1xuICAgIH1cblxuICAgIGNvbnN0IHJ1bm5lciA9IG5ldyBTdWJQcm9jZXNzKHByb2Nlc3MuZXhlY1BhdGgsIFtleHRTY3JpcHRzW3NjcmlwdE5hbWVdLCAuLi5leHRyYUFyZ3NdLCB7XG4gICAgICBjd2Q6IHRoaXMuY29uZmlnLmdldEluc3RhbGxQYXRoKGluc3RhbGxTcGVjKSxcbiAgICB9KTtcblxuICAgIGNvbnN0IG91dHB1dCA9IG5ldyBSaW5nQnVmZmVyKDUwKTtcblxuICAgIHJ1bm5lci5vbignc3RyZWFtLWxpbmUnLCAobGluZSkgPT4ge1xuICAgICAgb3V0cHV0LmVucXVldWUobGluZSk7XG4gICAgICB0aGlzLmxvZy5sb2cobGluZSk7XG4gICAgfSk7XG5cbiAgICBhd2FpdCBydW5uZXIuc3RhcnQoMCk7XG5cbiAgICB0cnkge1xuICAgICAgYXdhaXQgcnVubmVyLmpvaW4oKTtcbiAgICAgIHRoaXMubG9nLm9rKGAke3NjcmlwdE5hbWV9IHN1Y2Nlc3NmdWxseSByYW5gLmdyZWVuKTtcbiAgICAgIHJldHVybiB7b3V0cHV0OiBvdXRwdXQuZ2V0QnVmZigpfTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMubG9nLmVycm9yKGBFbmNvdW50ZXJlZCBhbiBlcnJvciB3aGVuIHJ1bm5pbmcgJyR7c2NyaXB0TmFtZX0nOiAke2Vyci5tZXNzYWdlfWAucmVkKTtcbiAgICAgIHJldHVybiB7ZXJyb3I6IGVyci5tZXNzYWdlLCBvdXRwdXQ6IG91dHB1dC5nZXRCdWZmKCl9O1xuICAgIH1cbiAgfVxufVxuXG5leHBvcnQgZGVmYXVsdCBFeHRlbnNpb25Db21tYW5kO1xuZXhwb3J0IHtFeHRlbnNpb25Db21tYW5kfTtcblxuLyoqXG4gKiBPcHRpb25zIGZvciB0aGUge0BsaW5rY29kZSBFeHRlbnNpb25Db21tYW5kfSBjb25zdHJ1Y3RvclxuICogQHRlbXBsYXRlIHtFeHRlbnNpb25UeXBlfSBFeHRUeXBlXG4gKiBAdHlwZWRlZiBFeHRlbnNpb25Db21tYW5kT3B0aW9uc1xuICogQHByb3BlcnR5IHtFeHRlbnNpb25Db25maWc8RXh0VHlwZT59IGNvbmZpZyAtIHRoZSBgRHJpdmVyQ29uZmlnYCBvciBgUGx1Z2luQ29uZmlnYCBpbnN0YW5jZSB1c2VkIGZvciB0aGlzIGNvbW1hbmRcbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0ganNvbiAtIHdoZXRoZXIgdGhlIG91dHB1dCBvZiB0aGlzIGNvbW1hbmQgc2hvdWxkIGJlIEpTT04gb3IgdGV4dFxuICovXG5cbi8qKlxuICogRXh0cmEgc3R1ZmYgYWJvdXQgZXh0ZW5zaW9uczsgdXNlZCBpbmRpcmVjdGx5IGJ5IHtAbGlua2NvZGUgRXh0ZW5zaW9uQ29tbWFuZC5saXN0fS5cbiAqXG4gKiBAdHlwZWRlZiBFeHRlbnNpb25NZXRhZGF0YVxuICogQHByb3BlcnR5IHtib29sZWFufSBpbnN0YWxsZWQgLSBJZiBgdHJ1ZWAsIHRoZSBleHRlbnNpb24gaXMgaW5zdGFsbGVkXG4gKiBAcHJvcGVydHkge3N0cmluZz99IHVwZGF0ZVZlcnNpb24gLSBJZiB0aGUgZXh0ZW5zaW9uIGlzIGluc3RhbGxlZCwgdGhlIHZlcnNpb24gaXQgY2FuIGJlIHVwZGF0ZWQgdG9cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nP30gdW5zYWZlVXBkYXRlVmVyc2lvbiAtIFNhbWUgYXMgYWJvdmUsIGJ1dCBhIG1ham9yIHZlcnNpb24gYnVtcFxuICogQHByb3BlcnR5IHtib29sZWFufSB1cFRvRGF0ZSAtIElmIHRoZSBleHRlbnNpb24gaXMgaW5zdGFsbGVkIGFuZCB0aGUgbGF0ZXN0XG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiB7aW1wb3J0KCdAYXBwaXVtL3R5cGVzJykuRXh0ZW5zaW9uVHlwZX0gRXh0ZW5zaW9uVHlwZVxuICogQHR5cGVkZWYge2ltcG9ydCgnQGFwcGl1bS90eXBlcycpLkRyaXZlclR5cGV9IERyaXZlclR5cGVcbiAqIEB0eXBlZGVmIHtpbXBvcnQoJ0BhcHBpdW0vdHlwZXMnKS5QbHVnaW5UeXBlfSBQbHVnaW5UeXBlXG4gKi9cblxuLyoqXG4gKiBAdGVtcGxhdGUge0V4dGVuc2lvblR5cGV9IEV4dFR5cGVcbiAqIEB0eXBlZGVmIHtpbXBvcnQoJ2FwcGl1bS90eXBlcycpLkV4dFJlY29yZDxFeHRUeXBlPn0gRXh0UmVjb3JkXG4gKi9cblxuLyoqXG4gKiBAdGVtcGxhdGUge0V4dGVuc2lvblR5cGV9IEV4dFR5cGVcbiAqIEB0eXBlZGVmIHtpbXBvcnQoJy4uL2V4dGVuc2lvbi9leHRlbnNpb24tY29uZmlnJykuRXh0ZW5zaW9uQ29uZmlnPEV4dFR5cGU+fSBFeHRlbnNpb25Db25maWdcbiAqL1xuXG4vKipcbiAqIEB0ZW1wbGF0ZSB7RXh0ZW5zaW9uVHlwZX0gRXh0VHlwZVxuICogQHR5cGVkZWYge2ltcG9ydCgnYXBwaXVtL3R5cGVzJykuRXh0TWV0YWRhdGE8RXh0VHlwZT59IEV4dE1ldGFkYXRhXG4gKi9cblxuLyoqXG4gKiBAdGVtcGxhdGUge0V4dGVuc2lvblR5cGV9IEV4dFR5cGVcbiAqIEB0eXBlZGVmIHtpbXBvcnQoJ2FwcGl1bS90eXBlcycpLkV4dE1hbmlmZXN0PEV4dFR5cGU+fSBFeHRNYW5pZmVzdFxuICovXG5cbi8qKlxuICogQHRlbXBsYXRlIHtFeHRlbnNpb25UeXBlfSBFeHRUeXBlXG4gKiBAdHlwZWRlZiB7aW1wb3J0KCdhcHBpdW0vdHlwZXMnKS5FeHRQYWNrYWdlSnNvbjxFeHRUeXBlPn0gRXh0UGFja2FnZUpzb25cbiAqL1xuXG4vKipcbiAqIFBvc3NpYmxlIHJldHVybiB2YWx1ZSBmb3Ige0BsaW5rY29kZSBFeHRlbnNpb25Db21tYW5kLmxpc3R9XG4gKiBAdHlwZWRlZiBVbmluc3RhbGxlZEV4dGVuc2lvbkxpc3REYXRhXG4gKiBAcHJvcGVydHkge3N0cmluZ30gcGtnTmFtZVxuICogQHByb3BlcnR5IHtmYWxzZX0gaW5zdGFsbGVkXG4gKi9cblxuLyoqXG4gKiBQb3NzaWJsZSByZXR1cm4gdmFsdWUgZm9yIHtAbGlua2NvZGUgRXh0ZW5zaW9uQ29tbWFuZC5saXN0fVxuICogQHR5cGVkZWYge2ltcG9ydCgnYXBwaXVtL3R5cGVzJykuSW50ZXJuYWxNZXRhZGF0YSAmIEV4dGVuc2lvbk1ldGFkYXRhfSBJbnN0YWxsZWRFeHRlbnNpb25MaXN0RGF0YVxuICovXG5cbi8qKlxuICogUmV0dXJuIHZhbHVlIG9mIHtAbGlua2NvZGUgRXh0ZW5zaW9uQ29tbWFuZC5saXN0fS5cbiAqIEB0eXBlZGVmIHtSZWNvcmQ8c3RyaW5nLEluc3RhbGxlZEV4dGVuc2lvbkxpc3REYXRhfFVuaW5zdGFsbGVkRXh0ZW5zaW9uTGlzdERhdGE+fSBFeHRlbnNpb25MaXN0RGF0YVxuICovXG5cbi8qKlxuICogT3B0aW9ucyBmb3Ige0BsaW5rY29kZSBFeHRlbnNpb25Db21tYW5kLl9ydW59LlxuICogQHR5cGVkZWYgUnVuT3B0aW9uc1xuICogQHByb3BlcnR5IHtzdHJpbmd9IGluc3RhbGxTcGVjIC0gbmFtZSBvZiB0aGUgZXh0ZW5zaW9uIHRvIHJ1biBhIHNjcmlwdCBmcm9tXG4gKiBAcHJvcGVydHkge3N0cmluZ30gc2NyaXB0TmFtZSAtIG5hbWUgb2YgdGhlIHNjcmlwdCB0byBydW5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nW119IFtleHRyYUFyZ3NdIC0gYXJndW1lbnRzIHRvIHBhc3MgdG8gdGhlIHNjcmlwdFxuICovXG5cbi8qKlxuICogUmV0dXJuIHZhbHVlIG9mIHtAbGlua2NvZGUgRXh0ZW5zaW9uQ29tbWFuZC5fcnVufVxuICpcbiAqIEB0eXBlZGVmIFJ1bk91dHB1dFxuICogQHByb3BlcnR5IHtzdHJpbmd9IFtlcnJvcl0gLSBlcnJvciBtZXNzYWdlIGlmIHNjcmlwdCByYW4gdW5zdWNjZXNzZnVsbHksIG90aGVyd2lzZSB1bmRlZmluZWRcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nW119IG91dHB1dCAtIHNjcmlwdCBvdXRwdXRcbiAqL1xuXG4vKipcbiAqIE9wdGlvbnMgZm9yIHtAbGlua2NvZGUgRXh0ZW5zaW9uQ29tbWFuZC5fdXBkYXRlfS5cbiAqIEB0eXBlZGVmIEV4dGVuc2lvblVwZGF0ZU9wdHNcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBpbnN0YWxsU3BlYyAtIHRoZSBuYW1lIG9mIHRoZSBleHRlbnNpb24gdG8gdXBkYXRlXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IHVuc2FmZSAtIGlmIHRydWUsIHdpbGwgcGVyZm9ybSB1bnNhZmUgdXBkYXRlcyBwYXN0IG1ham9yIHJldmlzaW9uIGJvdW5kYXJpZXNcbiAqL1xuXG4vKipcbiAqIFJldHVybiB2YWx1ZSBvZiB7QGxpbmtjb2RlIEV4dGVuc2lvbkNvbW1hbmQuX3VwZGF0ZX0uXG4gKiBAdHlwZWRlZiBFeHRlbnNpb25VcGRhdGVSZXN1bHRcbiAqIEBwcm9wZXJ0eSB7UmVjb3JkPHN0cmluZyxFcnJvcj59IGVycm9ycyAtIG1hcCBvZiBleHQgbmFtZXMgdG8gZXJyb3Igb2JqZWN0c1xuICogQHByb3BlcnR5IHtSZWNvcmQ8c3RyaW5nLFVwZGF0ZVJlcG9ydD59IHVwZGF0ZXMgLSBtYXAgb2YgZXh0IG5hbWVzIHRvIHtAbGlua2NvZGUgVXBkYXRlUmVwb3J0fXNcbiAqL1xuXG4vKipcbiAqIFBhcnQgb2YgcmVzdWx0IG9mIHtAbGlua2NvZGUgRXh0ZW5zaW9uQ29tbWFuZC5fdXBkYXRlfS5cbiAqIEB0eXBlZGVmIFVwZGF0ZVJlcG9ydFxuICogQHByb3BlcnR5IHtzdHJpbmd9IGZyb20gLSB2ZXJzaW9uIHRoZSBleHRlbnNpb24gd2FzIHVwZGF0ZWQgZnJvbVxuICogQHByb3BlcnR5IHtzdHJpbmd9IHRvIC0gdmVyc2lvbiB0aGUgZXh0ZW5zaW9uIHdhcyB1cGRhdGVkIHRvXG4gKi9cblxuLyoqXG4gKiBPcHRpb25zIGZvciB7QGxpbmtjb2RlIEV4dGVuc2lvbkNvbW1hbmQuX3VuaW5zdGFsbH0uXG4gKiBAdHlwZWRlZiBVbmluc3RhbGxPcHRzXG4gKiBAcHJvcGVydHkge3N0cmluZ30gaW5zdGFsbFNwZWMgLSB0aGUgbmFtZSBvciBzcGVjIG9mIGFuIGV4dGVuc2lvbiB0byB1bmluc3RhbGxcbiAqL1xuXG4vKipcbiAqIFVzZWQgYnkge0BsaW5rY29kZSBFeHRlbnNpb25Db21tYW5kLmdldFBvc3RJbnN0YWxsVGV4dH1cbiAqIEB0eXBlZGVmIEV4dGVuc2lvbkFyZ3NcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBleHROYW1lIC0gdGhlIG5hbWUgb2YgYW4gZXh0ZW5zaW9uXG4gKiBAcHJvcGVydHkge29iamVjdH0gZXh0RGF0YSAtIHRoZSBkYXRhIGZvciBhbiBpbnN0YWxsZWQgZXh0ZW5zaW9uXG4gKi9cblxuLyoqXG4gKiBPcHRpb25zIGZvciB7QGxpbmtjb2RlIEV4dGVuc2lvbkNvbW1hbmQuaW5zdGFsbFZpYU5wbX1cbiAqIEB0eXBlZGVmIEluc3RhbGxWaWFOcG1BcmdzXG4gKiBAcHJvcGVydHkge3N0cmluZ30gaW5zdGFsbFNwZWMgLSB0aGUgbmFtZSBvciBzcGVjIG9mIGFuIGV4dGVuc2lvbiB0byBpbnN0YWxsXG4gKiBAcHJvcGVydHkge3N0cmluZ30gcGtnTmFtZSAtIHRoZSBOUE0gcGFja2FnZSBuYW1lIG9mIHRoZSBleHRlbnNpb25cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbcGtnVmVyXSAtIHRoZSBzcGVjaWZpYyB2ZXJzaW9uIG9mIHRoZSBOUE0gcGFja2FnZVxuICovXG5cbi8qKlxuICogT2JqZWN0IHJldHVybmVkIGJ5IHtAbGlua2NvZGUgRXh0ZW5zaW9uQ29tbWFuZC5jaGVja0ZvckV4dGVuc2lvblVwZGF0ZX1cbiAqIEB0eXBlZGVmIFBvc3NpYmxlVXBkYXRlc1xuICogQHByb3BlcnR5IHtzdHJpbmd9IGN1cnJlbnQgLSBjdXJyZW50IHZlcnNpb25cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nP30gc2FmZVVwZGF0ZSAtIHZlcnNpb24gd2UgY2FuIHNhZmVseSB1cGRhdGUgdG8gaWYgaXQgZXhpc3RzLCBvciBudWxsXG4gKiBAcHJvcGVydHkge3N0cmluZz99IHVuc2FmZVVwZGF0ZSAtIHZlcnNpb24gd2UgY2FuIHVuc2FmZWx5IHVwZGF0ZSB0byBpZiBpdCBleGlzdHMsIG9yIG51bGxcbiAqL1xuXG4vKipcbiAqIE9wdGlvbnMgZm9yIHtAbGlua2NvZGUgRXh0ZW5zaW9uQ29tbWFuZC5faW5zdGFsbH1cbiAqIEB0eXBlZGVmIEluc3RhbGxBcmdzXG4gKiBAcHJvcGVydHkge3N0cmluZ30gaW5zdGFsbFNwZWMgLSB0aGUgbmFtZSBvciBzcGVjIG9mIGFuIGV4dGVuc2lvbiB0byBpbnN0YWxsXG4gKiBAcHJvcGVydHkge2ltcG9ydCgnYXBwaXVtL3R5cGVzJykuSW5zdGFsbFR5cGV9IGluc3RhbGxUeXBlIC0gaG93IHRvIGluc3RhbGwgdGhpcyBleHRlbnNpb24uIE9uZSBvZiB0aGUgSU5TVEFMTF9UWVBFU1xuICogQHByb3BlcnR5IHtzdHJpbmd9IFtwYWNrYWdlTmFtZV0gLSBmb3IgZ2l0L2dpdGh1YiBpbnN0YWxscywgdGhlIGV4dGVuc2lvbiBub2RlIHBhY2thZ2UgbmFtZVxuICovXG5cbi8qKlxuICogUmV0dXJuZWQgYnkge0BsaW5rY29kZSBFeHRlbnNpb25Db21tYW5kLmdldEV4dGVuc2lvbkZpZWxkc31cbiAqIEB0ZW1wbGF0ZSB7RXh0ZW5zaW9uVHlwZX0gRXh0VHlwZVxuICogQHR5cGVkZWYge0V4dE1ldGFkYXRhPEV4dFR5cGU+ICYgeyBwa2dOYW1lOiBzdHJpbmcsIHZlcnNpb246IHN0cmluZywgYXBwaXVtVmVyc2lvbjogc3RyaW5nIH0gJiBpbXBvcnQoJ2FwcGl1bS90eXBlcycpLkNvbW1vbkV4dE1ldGFkYXRhfSBFeHRlbnNpb25GaWVsZHNcbiAqL1xuXG4vKipcbiAqIEB0ZW1wbGF0ZSB7RXh0ZW5zaW9uVHlwZX0gRXh0VHlwZVxuICogQHR5cGVkZWYge0V4dFR5cGUgZXh0ZW5kcyBEcml2ZXJUeXBlID8gdHlwZW9mIGltcG9ydCgnLi4vY29uc3RhbnRzJykuS05PV05fRFJJVkVSUyA6IEV4dFR5cGUgZXh0ZW5kcyBQbHVnaW5UeXBlID8gdHlwZW9mIGltcG9ydCgnLi4vY29uc3RhbnRzJykuS05PV05fUExVR0lOUyA6IG5ldmVyfSBLbm93bkV4dGVuc2lvbnNcbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIExpc3RPcHRpb25zXG4gKiBAcHJvcGVydHkge2Jvb2xlYW59IHNob3dJbnN0YWxsZWQgLSB3aGV0aGVyIHNob3VsZCBzaG93IG9ubHkgaW5zdGFsbGVkIGV4dGVuc2lvbnNcbiAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gc2hvd1VwZGF0ZXMgLSB3aGV0aGVyIHNob3VsZCBzaG93IGF2YWlsYWJsZSB1cGRhdGVzXG4gKi9cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBTUE7Ozs7QUFFQSxNQUFN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
|
|
607
|
+
exports.default = ExtensionCommand;
|
|
608
|
+
/**
|
|
609
|
+
* Options for the {@linkcode ExtensionCommand} constructor
|
|
610
|
+
* @template {ExtensionType} ExtType
|
|
611
|
+
* @typedef ExtensionCommandOptions
|
|
612
|
+
* @property {ExtensionConfig<ExtType>} config - the `DriverConfig` or `PluginConfig` instance used for this command
|
|
613
|
+
* @property {boolean} json - whether the output of this command should be JSON or text
|
|
614
|
+
*/
|
|
615
|
+
/**
|
|
616
|
+
* Extra stuff about extensions; used indirectly by {@linkcode ExtensionCommand.list}.
|
|
617
|
+
*
|
|
618
|
+
* @typedef ExtensionMetadata
|
|
619
|
+
* @property {boolean} installed - If `true`, the extension is installed
|
|
620
|
+
* @property {string?} updateVersion - If the extension is installed, the version it can be updated to
|
|
621
|
+
* @property {string?} unsafeUpdateVersion - Same as above, but a major version bump
|
|
622
|
+
* @property {boolean} upToDate - If the extension is installed and the latest
|
|
623
|
+
* @property {string?} updateError - Update check error message (if present)
|
|
624
|
+
*/
|
|
625
|
+
/**
|
|
626
|
+
* @typedef {import('@appium/types').ExtensionType} ExtensionType
|
|
627
|
+
* @typedef {import('@appium/types').DriverType} DriverType
|
|
628
|
+
* @typedef {import('@appium/types').PluginType} PluginType
|
|
629
|
+
*/
|
|
630
|
+
/**
|
|
631
|
+
* @template {ExtensionType} ExtType
|
|
632
|
+
* @typedef {import('appium/types').ExtRecord<ExtType>} ExtRecord
|
|
633
|
+
*/
|
|
634
|
+
/**
|
|
635
|
+
* @template {ExtensionType} ExtType
|
|
636
|
+
* @typedef {import('../extension/extension-config').ExtensionConfig<ExtType>} ExtensionConfig
|
|
637
|
+
*/
|
|
638
|
+
/**
|
|
639
|
+
* @template {ExtensionType} ExtType
|
|
640
|
+
* @typedef {import('appium/types').ExtMetadata<ExtType>} ExtMetadata
|
|
641
|
+
*/
|
|
642
|
+
/**
|
|
643
|
+
* @template {ExtensionType} ExtType
|
|
644
|
+
* @typedef {import('appium/types').ExtManifest<ExtType>} ExtManifest
|
|
645
|
+
*/
|
|
646
|
+
/**
|
|
647
|
+
* @template {ExtensionType} ExtType
|
|
648
|
+
* @typedef {import('appium/types').ExtPackageJson<ExtType>} ExtPackageJson
|
|
649
|
+
*/
|
|
650
|
+
/**
|
|
651
|
+
* @template {ExtensionType} ExtType
|
|
652
|
+
* @typedef {import('appium/types').ExtInstallReceipt<ExtType>} ExtInstallReceipt
|
|
653
|
+
*/
|
|
654
|
+
/**
|
|
655
|
+
* Possible return value for {@linkcode ExtensionCommand.list}
|
|
656
|
+
* @typedef {Partial<InstalledExtensionListData> & {pkgName: string, installed: false}} UninstalledExtensionListData
|
|
657
|
+
*/
|
|
658
|
+
/**
|
|
659
|
+
* Possible return value for {@linkcode ExtensionCommand.list}
|
|
660
|
+
* @typedef {import('appium/types').InternalMetadata & ExtensionMetadata} InstalledExtensionListData
|
|
661
|
+
*/
|
|
662
|
+
/**
|
|
663
|
+
* Return value of {@linkcode ExtensionCommand.list}.
|
|
664
|
+
* @typedef {Record<string,InstalledExtensionListData|UninstalledExtensionListData>} ExtensionListData
|
|
665
|
+
*/
|
|
666
|
+
/**
|
|
667
|
+
* Options for {@linkcode ExtensionCommand._run}.
|
|
668
|
+
* @typedef RunOptions
|
|
669
|
+
* @property {string} installSpec - name of the extension to run a script from
|
|
670
|
+
* @property {string} scriptName - name of the script to run
|
|
671
|
+
* @property {string[]} [extraArgs] - arguments to pass to the script
|
|
672
|
+
*/
|
|
673
|
+
/**
|
|
674
|
+
* Return value of {@linkcode ExtensionCommand._run}
|
|
675
|
+
*
|
|
676
|
+
* @typedef RunOutput
|
|
677
|
+
* @property {string} [error] - error message if script ran unsuccessfully, otherwise undefined
|
|
678
|
+
* @property {string[]} output - script output
|
|
679
|
+
*/
|
|
680
|
+
/**
|
|
681
|
+
* Options for {@linkcode ExtensionCommand._update}.
|
|
682
|
+
* @typedef ExtensionUpdateOpts
|
|
683
|
+
* @property {string} installSpec - the name of the extension to update
|
|
684
|
+
* @property {boolean} unsafe - if true, will perform unsafe updates past major revision boundaries
|
|
685
|
+
*/
|
|
686
|
+
/**
|
|
687
|
+
* Return value of {@linkcode ExtensionCommand._update}.
|
|
688
|
+
* @typedef ExtensionUpdateResult
|
|
689
|
+
* @property {Record<string,Error>} errors - map of ext names to error objects
|
|
690
|
+
* @property {Record<string,UpdateReport>} updates - map of ext names to {@linkcode UpdateReport}s
|
|
691
|
+
*/
|
|
692
|
+
/**
|
|
693
|
+
* Part of result of {@linkcode ExtensionCommand._update}.
|
|
694
|
+
* @typedef UpdateReport
|
|
695
|
+
* @property {string} from - version the extension was updated from
|
|
696
|
+
* @property {string} to - version the extension was updated to
|
|
697
|
+
*/
|
|
698
|
+
/**
|
|
699
|
+
* Options for {@linkcode ExtensionCommand._uninstall}.
|
|
700
|
+
* @typedef UninstallOpts
|
|
701
|
+
* @property {string} installSpec - the name or spec of an extension to uninstall
|
|
702
|
+
*/
|
|
703
|
+
/**
|
|
704
|
+
* Used by {@linkcode ExtensionCommand.getPostInstallText}
|
|
705
|
+
* @typedef ExtensionArgs
|
|
706
|
+
* @property {string} extName - the name of an extension
|
|
707
|
+
* @property {object} extData - the data for an installed extension
|
|
708
|
+
*/
|
|
709
|
+
/**
|
|
710
|
+
* Options for {@linkcode ExtensionCommand.installViaNpm}
|
|
711
|
+
* @typedef InstallViaNpmArgs
|
|
712
|
+
* @property {string} installSpec - the name or spec of an extension to install
|
|
713
|
+
* @property {string} pkgName - the NPM package name of the extension
|
|
714
|
+
* @property {import('appium/types').InstallType} installType - type of install
|
|
715
|
+
* @property {string} [pkgVer] - the specific version of the NPM package
|
|
716
|
+
*/
|
|
717
|
+
/**
|
|
718
|
+
* Object returned by {@linkcode ExtensionCommand.checkForExtensionUpdate}
|
|
719
|
+
* @typedef PossibleUpdates
|
|
720
|
+
* @property {string} current - current version
|
|
721
|
+
* @property {string?} safeUpdate - version we can safely update to if it exists, or null
|
|
722
|
+
* @property {string?} unsafeUpdate - version we can unsafely update to if it exists, or null
|
|
723
|
+
*/
|
|
724
|
+
/**
|
|
725
|
+
* Options for {@linkcode ExtensionCommand._install}
|
|
726
|
+
* @typedef InstallOpts
|
|
727
|
+
* @property {string} installSpec - the name or spec of an extension to install
|
|
728
|
+
* @property {InstallType} installType - how to install this extension. One of the INSTALL_TYPES
|
|
729
|
+
* @property {string} [packageName] - for git/github installs, the extension node package name
|
|
730
|
+
*/
|
|
731
|
+
/**
|
|
732
|
+
* @template {ExtensionType} ExtType
|
|
733
|
+
* @typedef {ExtType extends DriverType ? typeof import('../constants').KNOWN_DRIVERS : ExtType extends PluginType ? typeof import('../constants').KNOWN_PLUGINS : never} KnownExtensions
|
|
734
|
+
*/
|
|
735
|
+
/**
|
|
736
|
+
* @typedef ListOptions
|
|
737
|
+
* @property {boolean} showInstalled - whether should show only installed extensions
|
|
738
|
+
* @property {boolean} showUpdates - whether should show available updates
|
|
739
|
+
*/
|
|
740
|
+
/**
|
|
741
|
+
* Opts for {@linkcode ExtensionCommand.getInstallationReceipt}
|
|
742
|
+
* @template {ExtensionType} ExtType
|
|
743
|
+
* @typedef GetInstallationReceiptOpts
|
|
744
|
+
* @property {string} installPath
|
|
745
|
+
* @property {string} installSpec
|
|
746
|
+
* @property {ExtPackageJson<ExtType>} pkg
|
|
747
|
+
* @property {InstallType} installType
|
|
748
|
+
*/
|
|
749
|
+
/**
|
|
750
|
+
* @typedef {import('appium/types').InstallType} InstallType
|
|
751
|
+
*/
|
|
752
|
+
//# sourceMappingURL=extension-command.js.map
|