appium 2.0.0-beta.5 → 2.0.0-beta.52
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 +156 -63
- package/build/lib/appium.d.ts +226 -0
- package/build/lib/appium.d.ts.map +1 -0
- package/build/lib/appium.js +694 -439
- package/build/lib/appium.js.map +1 -0
- package/build/lib/cli/args.d.ts +17 -0
- package/build/lib/cli/args.d.ts.map +1 -0
- package/build/lib/cli/args.js +253 -319
- package/build/lib/cli/args.js.map +1 -0
- package/build/lib/cli/driver-command.d.ts +102 -0
- package/build/lib/cli/driver-command.d.ts.map +1 -0
- package/build/lib/cli/driver-command.js +126 -81
- package/build/lib/cli/driver-command.js.map +1 -0
- package/build/lib/cli/extension-command.d.ts +385 -0
- package/build/lib/cli/extension-command.d.ts.map +1 -0
- package/build/lib/cli/extension-command.js +731 -383
- package/build/lib/cli/extension-command.js.map +1 -0
- package/build/lib/cli/extension.d.ts +23 -0
- package/build/lib/cli/extension.d.ts.map +1 -0
- package/build/lib/cli/extension.js +71 -54
- package/build/lib/cli/extension.js.map +1 -0
- package/build/lib/cli/parser.d.ts +84 -0
- package/build/lib/cli/parser.d.ts.map +1 -0
- package/build/lib/cli/parser.js +240 -128
- package/build/lib/cli/parser.js.map +1 -0
- package/build/lib/cli/plugin-command.d.ts +99 -0
- package/build/lib/cli/plugin-command.d.ts.map +1 -0
- package/build/lib/cli/plugin-command.js +120 -81
- package/build/lib/cli/plugin-command.js.map +1 -0
- package/build/lib/cli/utils.d.ts +29 -0
- package/build/lib/cli/utils.d.ts.map +1 -0
- package/build/lib/cli/utils.js +72 -51
- package/build/lib/cli/utils.js.map +1 -0
- package/build/lib/config-file.d.ts +100 -0
- package/build/lib/config-file.d.ts.map +1 -0
- package/build/lib/config-file.js +207 -0
- package/build/lib/config-file.js.map +1 -0
- package/build/lib/config.d.ts +49 -0
- package/build/lib/config.d.ts.map +1 -0
- package/build/lib/config.js +265 -201
- package/build/lib/config.js.map +1 -0
- package/build/lib/constants.d.ts +49 -0
- package/build/lib/constants.d.ts.map +1 -0
- package/build/lib/constants.js +66 -0
- package/build/lib/constants.js.map +1 -0
- package/build/lib/extension/driver-config.d.ts +82 -0
- package/build/lib/extension/driver-config.d.ts.map +1 -0
- package/build/lib/extension/driver-config.js +212 -0
- package/build/lib/extension/driver-config.js.map +1 -0
- package/build/lib/extension/extension-config.d.ts +249 -0
- package/build/lib/extension/extension-config.d.ts.map +1 -0
- package/build/lib/extension/extension-config.js +581 -0
- package/build/lib/extension/extension-config.js.map +1 -0
- package/build/lib/extension/index.d.ts +48 -0
- package/build/lib/extension/index.d.ts.map +1 -0
- package/build/lib/extension/index.js +105 -0
- package/build/lib/extension/index.js.map +1 -0
- 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 +145 -0
- package/build/lib/extension/manifest.d.ts.map +1 -0
- package/build/lib/extension/manifest.js +521 -0
- package/build/lib/extension/manifest.js.map +1 -0
- package/build/lib/extension/package-changed.d.ts +11 -0
- package/build/lib/extension/package-changed.d.ts.map +1 -0
- package/build/lib/extension/package-changed.js +62 -0
- package/build/lib/extension/package-changed.js.map +1 -0
- package/build/lib/extension/plugin-config.d.ts +56 -0
- package/build/lib/extension/plugin-config.d.ts.map +1 -0
- package/build/lib/extension/plugin-config.js +102 -0
- package/build/lib/extension/plugin-config.js.map +1 -0
- package/build/lib/grid-register.d.ts +10 -0
- package/build/lib/grid-register.d.ts.map +1 -0
- package/build/lib/grid-register.js +122 -144
- package/build/lib/grid-register.js.map +1 -0
- package/build/lib/logger.d.ts +3 -0
- package/build/lib/logger.d.ts.map +1 -0
- package/build/lib/logger.js +5 -17
- package/build/lib/logger.js.map +1 -0
- package/build/lib/logsink.d.ts +4 -0
- package/build/lib/logsink.d.ts.map +1 -0
- package/build/lib/logsink.js +190 -187
- package/build/lib/logsink.js.map +1 -0
- package/build/lib/main.d.ts +62 -0
- package/build/lib/main.d.ts.map +1 -0
- package/build/lib/main.js +348 -228
- package/build/lib/main.js.map +1 -0
- package/build/lib/schema/arg-spec.d.ts +143 -0
- package/build/lib/schema/arg-spec.d.ts.map +1 -0
- package/build/lib/schema/arg-spec.js +164 -0
- package/build/lib/schema/arg-spec.js.map +1 -0
- package/build/lib/schema/cli-args.d.ts +19 -0
- package/build/lib/schema/cli-args.d.ts.map +1 -0
- package/build/lib/schema/cli-args.js +217 -0
- package/build/lib/schema/cli-args.js.map +1 -0
- package/build/lib/schema/cli-transformers.d.ts +5 -0
- package/build/lib/schema/cli-transformers.d.ts.map +1 -0
- package/build/lib/schema/cli-transformers.js +124 -0
- package/build/lib/schema/cli-transformers.js.map +1 -0
- package/build/lib/schema/index.d.ts +3 -0
- package/build/lib/schema/index.d.ts.map +1 -0
- package/build/lib/schema/index.js +19 -0
- package/build/lib/schema/index.js.map +1 -0
- package/build/lib/schema/keywords.d.ts +24 -0
- package/build/lib/schema/keywords.d.ts.map +1 -0
- package/build/lib/schema/keywords.js +128 -0
- package/build/lib/schema/keywords.js.map +1 -0
- package/build/lib/schema/schema.d.ts +259 -0
- package/build/lib/schema/schema.d.ts.map +1 -0
- package/build/lib/schema/schema.js +615 -0
- package/build/lib/schema/schema.js.map +1 -0
- package/build/lib/utils.d.ts +121 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +351 -273
- package/build/lib/utils.js.map +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/build/types/cli.d.ts +134 -0
- package/build/types/cli.d.ts.map +1 -0
- package/build/types/cli.js +3 -0
- package/build/types/cli.js.map +1 -0
- package/build/types/index.d.ts +15 -0
- package/build/types/index.d.ts.map +1 -0
- 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/driver.d.ts +1 -0
- package/driver.js +14 -0
- package/index.js +11 -0
- package/lib/appium.js +535 -186
- package/lib/cli/args.js +267 -422
- package/lib/cli/driver-command.js +126 -23
- package/lib/cli/extension-command.js +679 -271
- package/lib/cli/extension.js +48 -17
- package/lib/cli/parser.js +263 -83
- package/lib/cli/plugin-command.js +115 -20
- package/lib/cli/utils.js +24 -10
- package/lib/config-file.js +220 -0
- package/lib/config.js +244 -110
- package/lib/constants.js +71 -0
- package/lib/extension/driver-config.js +250 -0
- package/lib/extension/extension-config.js +682 -0
- package/lib/extension/index.js +116 -0
- package/lib/extension/manifest-migrations.js +120 -0
- package/lib/extension/manifest.js +573 -0
- package/lib/extension/package-changed.js +64 -0
- package/lib/extension/plugin-config.js +112 -0
- package/lib/grid-register.js +49 -35
- package/lib/logger.js +1 -2
- package/lib/logsink.js +64 -38
- package/lib/main.js +321 -101
- package/lib/schema/arg-spec.js +229 -0
- package/lib/schema/cli-args.js +238 -0
- package/lib/schema/cli-transformers.js +119 -0
- package/lib/schema/index.js +2 -0
- package/lib/schema/keywords.js +136 -0
- package/lib/schema/schema.js +722 -0
- package/lib/utils.js +291 -167
- package/package.json +83 -84
- package/plugin.d.ts +1 -0
- package/plugin.js +13 -0
- package/scripts/autoinstall-extensions.js +237 -0
- package/support.d.ts +1 -0
- package/support.js +13 -0
- package/tsconfig.json +25 -0
- package/types/cli.ts +189 -0
- package/types/index.ts +20 -0
- 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/CHANGELOG.md +0 -3515
- package/bin/ios-webkit-debug-proxy-launcher.js +0 -71
- package/build/lib/cli/npm.js +0 -208
- package/build/lib/cli/parser-helpers.js +0 -82
- package/build/lib/driver-config.js +0 -77
- package/build/lib/drivers.js +0 -96
- package/build/lib/extension-config.js +0 -253
- package/build/lib/plugin-config.js +0 -59
- package/build/lib/plugins.js +0 -14
- package/lib/cli/npm.js +0 -184
- package/lib/cli/parser-helpers.js +0 -79
- package/lib/driver-config.js +0 -46
- package/lib/drivers.js +0 -81
- package/lib/extension-config.js +0 -209
- package/lib/plugin-config.js +0 -34
- package/lib/plugins.js +0 -10
|
@@ -1,404 +1,752 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Object.defineProperty(exports, "__esModule", {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
require("
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
var _path = _interopRequireDefault(require("path"));
|
|
17
|
-
|
|
18
|
-
var _appiumSupport = require("appium-support");
|
|
19
|
-
|
|
20
|
-
var _utils = require("./utils");
|
|
21
|
-
|
|
22
|
-
var _extensionConfig = require("../extension-config");
|
|
23
|
-
|
|
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");
|
|
24
16
|
const UPDATE_ALL = 'installed';
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
*/
|
|
30
33
|
class ExtensionCommand {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
this.npm = new _npm.default(this.config.appiumHome);
|
|
40
|
-
this.knownExtensions = {};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async execute(args) {
|
|
44
|
-
const cmd = args[`${this.type}Command`];
|
|
45
|
-
|
|
46
|
-
if (!_lodash.default.isFunction(ExtensionCommand.prototype[cmd])) {
|
|
47
|
-
throw new Error(`Cannot handle ${this.type} command ${cmd}`);
|
|
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);
|
|
48
42
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
async list({
|
|
55
|
-
showInstalled,
|
|
56
|
-
showUpdates
|
|
57
|
-
}) {
|
|
58
|
-
const lsMsg = `Listing ${showInstalled ? 'installed' : 'available'} ${this.type}s`;
|
|
59
|
-
const installedNames = Object.keys(this.config.installedExtensions);
|
|
60
|
-
const knownNames = Object.keys(this.knownExtensions);
|
|
61
|
-
const exts = [...installedNames, ...knownNames].reduce((acc, name) => {
|
|
62
|
-
if (!acc[name]) {
|
|
63
|
-
if (installedNames.includes(name)) {
|
|
64
|
-
acc[name] = { ...this.config.installedExtensions[name],
|
|
65
|
-
installed: true
|
|
66
|
-
};
|
|
67
|
-
} else if (!showInstalled) {
|
|
68
|
-
acc[name] = {
|
|
69
|
-
pkgName: this.knownExtensions[name],
|
|
70
|
-
installed: false
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return acc;
|
|
76
|
-
}, {});
|
|
77
|
-
await (0, _utils.spinWith)(this.isJsonOutput, lsMsg, async () => {
|
|
78
|
-
if (!showUpdates) {
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
for (const [ext, data] of _lodash.default.toPairs(exts)) {
|
|
83
|
-
const {
|
|
84
|
-
installed,
|
|
85
|
-
installType
|
|
86
|
-
} = data;
|
|
87
|
-
|
|
88
|
-
if (!installed || installType !== _extensionConfig.INSTALL_TYPE_NPM) {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const updates = await this.checkForExtensionUpdate(ext);
|
|
93
|
-
data.updateVersion = updates.safeUpdate;
|
|
94
|
-
data.unsafeUpdateVersion = updates.unsafeUpdate;
|
|
95
|
-
data.upToDate = updates.safeUpdate === null && updates.unsafeUpdate === null;
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (this.isJsonOutput) {
|
|
100
|
-
return exts;
|
|
43
|
+
/**
|
|
44
|
+
* `driver` or `plugin`, depending on the `ExtensionConfig`.
|
|
45
|
+
*/
|
|
46
|
+
get type() {
|
|
47
|
+
return this.config.extensionType;
|
|
101
48
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
switch (installType) {
|
|
115
|
-
case _extensionConfig.INSTALL_TYPE_GIT:
|
|
116
|
-
case _extensionConfig.INSTALL_TYPE_GITHUB:
|
|
117
|
-
typeTxt = `(cloned from ${installSpec})`.yellow;
|
|
118
|
-
break;
|
|
119
|
-
|
|
120
|
-
case _extensionConfig.INSTALL_TYPE_LOCAL:
|
|
121
|
-
typeTxt = `(linked from ${installSpec})`.magenta;
|
|
122
|
-
break;
|
|
123
|
-
|
|
124
|
-
default:
|
|
125
|
-
typeTxt = '(NPM)';
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const installTxt = installed ? `@${version.yellow} ${('[installed ' + typeTxt + ']').green}` : ' [not installed]'.grey;
|
|
129
|
-
const updateTxt = showUpdates && updateVersion ? ` [${updateVersion} available]`.magenta : '';
|
|
130
|
-
const upToDateTxt = showUpdates && upToDate ? ` [Up to date]`.green : '';
|
|
131
|
-
const unsafeUpdateTxt = showUpdates && unsafeUpdateVersion ? ` [${unsafeUpdateVersion} available (potentially unsafe)]`.cyan : '';
|
|
132
|
-
console.log(`- ${name.yellow}${installTxt}${updateTxt}${upToDateTxt}${unsafeUpdateTxt}`);
|
|
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
|
+
* @throws {Error}
|
|
58
|
+
*/
|
|
59
|
+
_createFatalError(message) {
|
|
60
|
+
return new Error(this.log.decorate(message, 'error'));
|
|
133
61
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (packageName && [_extensionConfig.INSTALL_TYPE_LOCAL, _extensionConfig.INSTALL_TYPE_NPM].includes(installType)) {
|
|
148
|
-
throw new Error(`When using --source=${installType}, cannot also use --package`);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (!packageName && [_extensionConfig.INSTALL_TYPE_GIT, _extensionConfig.INSTALL_TYPE_GITHUB].includes(installType)) {
|
|
152
|
-
throw new Error(`When using --source=${installType}, must also use --package`);
|
|
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);
|
|
153
75
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
extData = await this.installViaNpm({
|
|
166
|
-
ext: installSpec,
|
|
167
|
-
pkgName: packageName
|
|
168
|
-
});
|
|
169
|
-
} else if (installType === _extensionConfig.INSTALL_TYPE_GIT) {
|
|
170
|
-
installSpec = installSpec.replace(/\.git$/, '');
|
|
171
|
-
extData = await this.installViaNpm({
|
|
172
|
-
ext: installSpec,
|
|
173
|
-
pkgName: packageName
|
|
174
|
-
});
|
|
175
|
-
} else {
|
|
176
|
-
let name, pkgVer;
|
|
177
|
-
const splits = installSpec.split('@');
|
|
178
|
-
|
|
179
|
-
if (installSpec[0] === '@') {
|
|
180
|
-
[name, pkgVer] = [`@${splits[1]}`, splits[2]];
|
|
181
|
-
} else {
|
|
182
|
-
[name, pkgVer] = splits;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
let pkgName;
|
|
186
|
-
|
|
187
|
-
if (installType === _extensionConfig.INSTALL_TYPE_NPM) {
|
|
188
|
-
pkgName = name;
|
|
189
|
-
} else {
|
|
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);
|
|
190
85
|
const knownNames = Object.keys(this.knownExtensions);
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
+
}
|
|
126
|
+
});
|
|
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;
|
|
213
173
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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;
|
|
245
312
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
+
}
|
|
255
342
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
validateExtensionFields() {
|
|
270
|
-
throw new Error('Must be implemented in final class');
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
async uninstall({
|
|
274
|
-
ext
|
|
275
|
-
}) {
|
|
276
|
-
if (!this.config.isInstalled(ext)) {
|
|
277
|
-
throw new Error(`Can't uninstall ${this.type} '${ext}'; it is not installed`);
|
|
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');
|
|
278
353
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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,
|
|
376
|
+
};
|
|
284
377
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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;
|
|
298
409
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
});
|
|
311
|
-
const update = await (0, _utils.spinWith)(this.isJsonOutput, `Checking if ${this.type} '${e}' needs an update`, async () => {
|
|
312
|
-
const update = await this.checkForExtensionUpdate(e);
|
|
313
|
-
|
|
314
|
-
if (!(update.safeUpdate || update.unsafeUpdate)) {
|
|
315
|
-
throw new NoUpdatesAvailableError();
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
return update;
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
if (!unsafe && !update.safeUpdate) {
|
|
322
|
-
throw new Error(`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`);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const updateVer = unsafe && update.unsafeUpdate ? update.unsafeUpdate : update.safeUpdate;
|
|
326
|
-
await (0, _utils.spinWith)(this.isJsonOutput, `Updating driver '${e}' from ${update.current} to ${updateVer}`, async () => await this.updateExtension(e, updateVer));
|
|
327
|
-
updates[e] = {
|
|
328
|
-
from: update.current,
|
|
329
|
-
to: updateVer
|
|
330
|
-
};
|
|
331
|
-
} catch (err) {
|
|
332
|
-
errors[e] = err;
|
|
333
|
-
}
|
|
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');
|
|
334
421
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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;
|
|
340
441
|
}
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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 };
|
|
350
508
|
}
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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 };
|
|
369
538
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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);
|
|
373
557
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
+
}
|
|
377
604
|
}
|
|
378
|
-
|
|
379
|
-
return {
|
|
380
|
-
current: version,
|
|
381
|
-
safeUpdate,
|
|
382
|
-
unsafeUpdate
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
async updateExtension(ext, version) {
|
|
387
|
-
const {
|
|
388
|
-
pkgName
|
|
389
|
-
} = this.config.installedExtensions[ext];
|
|
390
|
-
await this.installViaNpm({
|
|
391
|
-
ext,
|
|
392
|
-
pkgName,
|
|
393
|
-
pkgVer: version
|
|
394
|
-
});
|
|
395
|
-
this.config.installedExtensions[ext].version = version;
|
|
396
|
-
await this.config.write();
|
|
397
|
-
}
|
|
398
|
-
|
|
399
605
|
}
|
|
400
|
-
|
|
401
|
-
exports.default = ExtensionCommand;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi9jbGkvZXh0ZW5zaW9uLWNvbW1hbmQuanMiXSwibmFtZXMiOlsiVVBEQVRFX0FMTCIsIk5vdFVwZGF0YWJsZUVycm9yIiwiRXJyb3IiLCJOb1VwZGF0ZXNBdmFpbGFibGVFcnJvciIsIkV4dGVuc2lvbkNvbW1hbmQiLCJjb25zdHJ1Y3RvciIsImNvbmZpZyIsImpzb24iLCJ0eXBlIiwiaXNKc29uT3V0cHV0IiwibnBtIiwiTlBNIiwiYXBwaXVtSG9tZSIsImtub3duRXh0ZW5zaW9ucyIsImV4ZWN1dGUiLCJhcmdzIiwiY21kIiwiXyIsImlzRnVuY3Rpb24iLCJwcm90b3R5cGUiLCJleGVjdXRlQ21kIiwiYmluZCIsImxpc3QiLCJzaG93SW5zdGFsbGVkIiwic2hvd1VwZGF0ZXMiLCJsc01zZyIsImluc3RhbGxlZE5hbWVzIiwiT2JqZWN0Iiwia2V5cyIsImluc3RhbGxlZEV4dGVuc2lvbnMiLCJrbm93bk5hbWVzIiwiZXh0cyIsInJlZHVjZSIsImFjYyIsIm5hbWUiLCJpbmNsdWRlcyIsImluc3RhbGxlZCIsInBrZ05hbWUiLCJleHQiLCJkYXRhIiwidG9QYWlycyIsImluc3RhbGxUeXBlIiwiSU5TVEFMTF9UWVBFX05QTSIsInVwZGF0ZXMiLCJjaGVja0ZvckV4dGVuc2lvblVwZGF0ZSIsInVwZGF0ZVZlcnNpb24iLCJzYWZlVXBkYXRlIiwidW5zYWZlVXBkYXRlVmVyc2lvbiIsInVuc2FmZVVwZGF0ZSIsInVwVG9EYXRlIiwiaW5zdGFsbFNwZWMiLCJ2ZXJzaW9uIiwidHlwZVR4dCIsIklOU1RBTExfVFlQRV9HSVQiLCJJTlNUQUxMX1RZUEVfR0lUSFVCIiwieWVsbG93IiwiSU5TVEFMTF9UWVBFX0xPQ0FMIiwibWFnZW50YSIsImluc3RhbGxUeHQiLCJncmVlbiIsImdyZXkiLCJ1cGRhdGVUeHQiLCJ1cFRvRGF0ZVR4dCIsInVuc2FmZVVwZGF0ZVR4dCIsImN5YW4iLCJjb25zb2xlIiwibG9nIiwiaW5zdGFsbCIsInBhY2thZ2VOYW1lIiwiZXh0RGF0YSIsIm1zZyIsInBrZ0pzb25EYXRhIiwibGlua1BhY2thZ2UiLCJnZXRFeHRlbnNpb25GaWVsZHMiLCJpbnN0YWxsUGF0aCIsInNwbGl0IiwibGVuZ3RoIiwiaW5zdGFsbFZpYU5wbSIsInJlcGxhY2UiLCJwa2dWZXIiLCJzcGxpdHMiLCJKU09OIiwic3RyaW5naWZ5IiwiZXh0TmFtZSIsImlzSW5zdGFsbGVkIiwiYWRkRXh0ZW5zaW9uIiwiZ2V0UG9zdEluc3RhbGxUZXh0IiwibnBtU3BlYyIsInNwZWNNc2ciLCJpbnN0YWxsUGFja2FnZSIsInBrZ0RpciIsInBhdGgiLCJyZXNvbHZlIiwiZXJyIiwibWVzc2FnZSIsImFwcGl1bSIsInZhbGlkYXRlRXh0ZW5zaW9uRmllbGRzIiwidW5pbnN0YWxsIiwiZnMiLCJyaW1yYWYiLCJnZXRJbnN0YWxsUGF0aCIsInJlbW92ZUV4dGVuc2lvbiIsInVwZGF0ZSIsInVuc2FmZSIsInNob3VsZFVwZGF0ZUFsbCIsImV4dHNUb1VwZGF0ZSIsImVycm9ycyIsImUiLCJjdXJyZW50IiwidXBkYXRlVmVyIiwidXBkYXRlRXh0ZW5zaW9uIiwiZnJvbSIsInRvIiwicmVkIiwiZ2V0TGF0ZXN0VmVyc2lvbiIsImdldExhdGVzdFNhZmVVcGdyYWRlVmVyc2lvbiIsInV0aWwiLCJjb21wYXJlVmVyc2lvbnMiLCJ3cml0ZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7QUFFQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFHQSxNQUFNQSxVQUFVLEdBQUcsV0FBbkI7O0FBRUEsTUFBTUMsaUJBQU4sU0FBZ0NDLEtBQWhDLENBQXNDOztBQUN0QyxNQUFNQyx1QkFBTixTQUFzQ0QsS0FBdEMsQ0FBNEM7O0FBRTdCLE1BQU1FLGdCQUFOLENBQXVCO0FBZXBDQyxFQUFBQSxXQUFXLENBQUU7QUFBQ0MsSUFBQUEsTUFBRDtBQUFTQyxJQUFBQSxJQUFUO0FBQWVDLElBQUFBO0FBQWYsR0FBRixFQUF3QjtBQUNqQyxTQUFLRixNQUFMLEdBQWNBLE1BQWQ7QUFDQSxT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TUFBQUEsT0FBTjtBQUFlMEMsTUFBQUEsTUFBTSxFQUFFNUI7QUFBdkIsS0FBbkIsQ0FBTjtBQUNBLFNBQUs3QyxNQUFMLENBQVl1QixtQkFBWixDQUFnQ1MsR0FBaEMsRUFBcUNhLE9BQXJDLEdBQStDQSxPQUEvQztBQUNBLFVBQU0sS0FBSzdDLE1BQUwsQ0FBWWdILEtBQVosRUFBTjtBQUNEOztBQXhkbUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuby1jb25zb2xlICovXG5cbmltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgTlBNIGZyb20gJy4vbnBtJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgZnMsIHV0aWwgfSBmcm9tICdhcHBpdW0tc3VwcG9ydCc7XG5pbXBvcnQgeyBsb2csIHNwaW5XaXRoIH0gZnJvbSAnLi91dGlscyc7XG5pbXBvcnQgeyBJTlNUQUxMX1RZUEVfTlBNLCBJTlNUQUxMX1RZUEVfR0lULCBJTlNUQUxMX1RZUEVfR0lUSFVCLFxuICAgICAgICAgSU5TVEFMTF9UWVBFX0xPQ0FMIH0gZnJvbSAnLi4vZXh0ZW5zaW9uLWNvbmZpZyc7XG5cbmNvbnN0IFVQREFURV9BTEwgPSAnaW5zdGFsbGVkJztcblxuY2xhc3MgTm90VXBkYXRhYmxlRXJyb3IgZXh0ZW5kcyBFcnJvciB7fVxuY2xhc3MgTm9VcGRhdGVzQXZhaWxhYmxlRXJyb3IgZXh0ZW5kcyBFcnJvciB7fVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBFeHRlbnNpb25Db21tYW5kIHtcblxuICAvKipcbiAgICogQHR5cGVkZWYge09iamVjdH0gRXh0ZW5zaW9uQ29tbWFuZENvbnN0cnVjdG9yXG4gICAqIEBwcm9wZXJ0eSB7T2JqZWN0fSBjb25maWcgLSB0aGUgRHJpdmVyQ29uZmlnIG9yIFBsdWdpbkNvbmZpZyBvYmplY3QgdXNlZCBmb3IgdGhpcyBjb21tYW5kXG4gICAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0ganNvbiAtIHdoZXRoZXIgdGhlIG91dHB1dCBvZiB0aGlzIGNvbW1hbmQgc2hvdWxkIGJlIEpTT04gb3IgdGV4dFxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gdHlwZSAtIERSSVZFUl9UWVBFIG9yIFBMVUdJTl9UWVBFXG4gICAqL1xuXG4gIC8qKlxuICAgKiBCdWlsZCBhbiBFeHRlbnNpb25Db21tYW5kXG4gICAqXG4gICAqIEBwYXJhbSB7RXh0ZW5zaW9uQ29tbWFuZENvbnN0cnVjdG9yfSBvcHRzXG4gICAqIEByZXR1cm4ge0V4dGVuc2lvbkNvbW1hbmR9XG4gICAqL1xuICBjb25zdHJ1Y3RvciAoe2NvbmZpZywganNvbiwgdHlwZX0pIHtcbiAgICB0aGlzLmNvbmZpZyA9IGNvbmZpZztcbiAgICB0aGlzLnR5cGUgPSB0eXBlO1xuICAgIHRoaXMuaXNKc29uT3V0cHV0ID0ganNvbjtcbiAgICB0aGlzLm5wbSA9IG5ldyBOUE0odGhpcy5jb25maWcuYXBwaXVtSG9tZSk7XG4gICAgdGhpcy5rbm93bkV4dGVuc2lvbnMgPSB7fTsgLy8gdGhpcyBuZWVkcyB0byBiZSBvdmVycmlkZGVuIGluIGZpbmFsIGNsYXNzXG4gIH1cblxuICAvKipcbiAgICogVGFrZSBhIENMSSBwYXJzZSBhbmQgcnVuIGFuIGV4dGVuc2lvbiBjb21tYW5kIGJhc2VkIG9uIGl0cyB0eXBlXG4gICAqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzIC0gYSBrZXkvdmFsdWUgb2JqZWN0IHdpdGggQ0xJIGZsYWdzIGFuZCB2YWx1ZXNcbiAgICogQHJldHVybiB7b2JqZWN0fSB0aGUgcmVzdWx0IG9mIHRoZSBzcGVjaWZpYyBjb21tYW5kIHdoaWNoIGlzIGV4ZWN1dGVkXG4gICAqL1xuICBhc3luYyBleGVjdXRlIChhcmdzKSB7XG4gICAgY29uc3QgY21kID0gYXJnc1tgJHt0aGlzLnR5cGV9Q29tbWFuZGBdO1xuICAgIGlmICghXy5pc0Z1bmN0aW9uKEV4dGVuc2lvbkNvbW1hbmQucHJvdG90eXBlW2NtZF0pKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYENhbm5vdCBoYW5kbGUgJHt0aGlzLnR5cGV9IGNvbW1hbmQgJHtjbWR9YCk7XG4gICAgfVxuICAgIGNvbnN0IGV4ZWN1dGVDbWQgPSB0aGlzW2NtZF0uYmluZCh0aGlzKTtcbiAgICByZXR1cm4gYXdhaXQgZXhlY3V0ZUNtZChhcmdzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBMaXN0QXJnc1xuICAgKiBAcHJvcGVydHkge2Jvb2xlYW59IHNob3dJbnN0YWxsZWQgLSB3aGV0aGVyIHNob3VsZCBzaG93IG9ubHkgaW5zdGFsbGVkIGV4dGVuc2lvbnNcbiAgICogQHByb3BlcnR5IHtib29sZWFufSBzaG93VXBkYXRlcyAtIHdoZXRoZXIgc2hvdWxkIHNob3cgYXZhaWxhYmxlIHVwZGF0ZXNcbiAgICovXG5cbiAgLyoqXG4gICAqIExpc3QgZXh0ZW5zaW9uc1xuICAgKlxuICAgKiBAcGFyYW0ge0xpc3RBcmdzfSBhcmdzXG4gICAqIEByZXR1cm4ge29iamVjdH0gbWFwIG9mIGV4dGVuc2lvbiBuYW1lcyB0byBleHRlbnNpb24gZGF0YVxuICAgKi9cbiAgYXN5bmMgbGlzdCAoe3Nob3dJbnN0YWxsZWQsIHNob3dVcGRhdGVzfSkge1xuICAgIGNvbnN0IGxzTXNnID0gYExpc3RpbmcgJHtzaG93SW5zdGFsbGVkID8gJ2luc3RhbGxlZCcgOiAnYXZhaWxhYmxlJ30gJHt0aGlzLnR5cGV9c2A7XG4gICAgY29uc3QgaW5zdGFsbGVkTmFtZXMgPSBPYmplY3Qua2V5cyh0aGlzLmNvbmZpZy5pbnN0YWxsZWRFeHRlbnNpb25zKTtcbiAgICBjb25zdCBrbm93bk5hbWVzID0gT2JqZWN0LmtleXModGhpcy5rbm93bkV4dGVuc2lvbnMpO1xuICAgIGNvbnN0IGV4dHMgPSBbLi4uaW5zdGFsbGVkTmFtZXMsIC4uLmtub3duTmFtZXNdLnJlZHVjZSgoYWNjLCBuYW1lKSA9PiB7XG4gICAgICBpZiAoIWFjY1tuYW1lXSkge1xuICAgICAgICBpZiAoaW5zdGFsbGVkTmFtZXMuaW5jbHVkZXMobmFtZSkpIHtcbiAgICAgICAgICBhY2NbbmFtZV0gPSB7Li4udGhpcy5jb25maWcuaW5zdGFsbGVkRXh0ZW5zaW9uc1tuYW1lXSwgaW5zdGFsbGVkOiB0cnVlfTtcbiAgICAgICAgfSBlbHNlIGlmICghc2hvd0luc3RhbGxlZCkge1xuICAgICAgICAgIGFjY1tuYW1lXSA9IHtwa2dOYW1lOiB0aGlzLmtub3duRXh0ZW5zaW9uc1tuYW1lXSwgaW5zdGFsbGVkOiBmYWxzZX07XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIHJldHVybiBhY2M7XG4gICAgfSwge30pO1xuXG4gICAgLy8gaWYgd2Ugd2FudCB0byBzaG93IHdoZXRoZXIgdXBkYXRlcyBhcmUgYXZhaWxhYmxlLCBwdXQgdGhhdCBiZWhpbmQgYSBzcGlubmVyXG4gICAgYXdhaXQgc3BpbldpdGgodGhpcy5pc0pzb25PdXRwdXQsIGxzTXNnLCBhc3luYyAoKSA9PiB7XG4gICAgICBpZiAoIXNob3dVcGRhdGVzKSB7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGZvciAoY29uc3QgW2V4dCwgZGF0YV0gb2YgXy50b1BhaXJzKGV4dHMpKSB7XG4gICAgICAgIGNvbnN0IHtpbnN0YWxsZWQsIGluc3RhbGxUeXBlfSA9IGRhdGE7XG4gICAgICAgIGlmICghaW5zdGFsbGVkIHx8IGluc3RhbGxUeXBlICE9PSBJTlNUQUxMX1RZUEVfTlBNKSB7XG4gICAgICAgICAgLy8gZG9uJ3QgbmVlZCB0byBjaGVjayBmb3IgdXBkYXRlcyBvbiBleHRzIHRoYXQgYXJlbid0IGluc3RhbGxlZFxuICAgICAgICAgIC8vIGFsc28gZG9uJ3QgbmVlZCB0byBjaGVjayBmb3IgdXBkYXRlcyBvbiBub24tbnBtIGV4dHNcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCB1cGRhdGVzID0gYXdhaXQgdGhpcy5jaGVja0ZvckV4dGVuc2lvblVwZGF0ZShleHQpO1xuICAgICAgICBkYXRhLnVwZGF0ZVZlcnNpb24gPSB1cGRhdGVzLnNhZmVVcGRhdGU7XG4gICAgICAgIGRhdGEudW5zYWZlVXBkYXRlVmVyc2lvbiA9IHVwZGF0ZXMudW5zYWZlVXBkYXRlO1xuICAgICAgICBkYXRhLnVwVG9EYXRlID0gdXBkYXRlcy5zYWZlVXBkYXRlID09PSBudWxsICYmIHVwZGF0ZXMudW5zYWZlVXBkYXRlID09PSBudWxsO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgLy8gaWYgd2UncmUganVzdCBnZXR0aW5nIHRoZSBkYXRhLCBzaG9ydCBjaXJjdWl0IHJldHVybiBoZXJlIHNpbmNlIHdlIGRvbid0IG5lZWQgdG8gZG8gYW55XG4gICAgLy8gZm9ybWF0dGluZyBsb2dpY1xuICAgIGlmICh0aGlzLmlzSnNvbk91dHB1dCkge1xuICAgICAgcmV0dXJuIGV4dHM7XG4gICAgfVxuXG4gICAgZm9yIChjb25zdCBbXG4gICAgICBuYW1lLFxuICAgICAge2luc3RhbGxUeXBlLCBpbnN0YWxsU3BlYywgaW5zdGFsbGVkLCB1cGRhdGVWZXJzaW9uLCB1bnNhZmVVcGRhdGVWZXJzaW9uLCB2ZXJzaW9uLCB1cFRvRGF0ZX1cbiAgICBdIG9mIF8udG9QYWlycyhleHRzKSkge1xuICAgICAgbGV0IHR5cGVUeHQ7XG4gICAgICBzd2l0Y2ggKGluc3RhbGxUeXBlKSB7XG4gICAgICAgIGNhc2UgSU5TVEFMTF9UWVBFX0dJVDpcbiAgICAgICAgY2FzZSBJTlNUQUxMX1RZUEVfR0lUSFVCOlxuICAgICAgICAgIHR5cGVUeHQgPSBgKGNsb25lZCBmcm9tICR7aW5zdGFsbFNwZWN9KWAueWVsbG93O1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlIElOU1RBTExfVFlQRV9MT0NBTDpcbiAgICAgICAgICB0eXBlVHh0ID0gYChsaW5rZWQgZnJvbSAke2luc3RhbGxTcGVjfSlgLm1hZ2VudGE7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgdHlwZVR4dCA9ICcoTlBNKSc7XG4gICAgICB9XG4gICAgICBjb25zdCBpbnN0YWxsVHh0ID0gaW5zdGFsbGVkID9cbiAgICAgICAgYEAke3ZlcnNpb24ueWVsbG93fSAkeygnW2luc3RhbGxlZCAnICsgdHlwZVR4dCArICddJykuZ3JlZW59YCA6XG4gICAgICAgICcgW25vdCBpbnN0YWxsZWRdJy5ncmV5O1xuICAgICAgY29uc3QgdXBkYXRlVHh0ID0gc2hvd1VwZGF0ZXMgJiYgdXBkYXRlVmVyc2lvbiA/XG4gICAgICAgIGAgWyR7dXBkYXRlVmVyc2lvbn0gYXZhaWxhYmxlXWAubWFnZW50YSA6XG4gICAgICAgICcnO1xuICAgICAgY29uc3QgdXBUb0RhdGVUeHQgPSBzaG93VXBkYXRlcyAmJiB1cFRvRGF0ZSA/XG4gICAgICAgIGAgW1VwIHRvIGRhdGVdYC5ncmVlbiA6XG4gICAgICAgICcnO1xuICAgICAgY29uc3QgdW5zYWZlVXBkYXRlVHh0ID0gc2hvd1VwZGF0ZXMgJiYgdW5zYWZlVXBkYXRlVmVyc2lvbiA/XG4gICAgICAgIGAgWyR7dW5zYWZlVXBkYXRlVmVyc2lvbn0gYXZhaWxhYmxlIChwb3RlbnRpYWxseSB1bnNhZmUpXWAuY3lhbiA6XG4gICAgICAgICcnO1xuXG4gICAgICBjb25zb2xlLmxvZyhgLSAke25hbWUueWVsbG93fSR7aW5zdGFsbFR4dH0ke3VwZGF0ZVR4dH0ke3VwVG9EYXRlVHh0fSR7dW5zYWZlVXBkYXRlVHh0fWApO1xuICAgIH1cblxuICAgIHJldHVybiBleHRzO1xuICB9XG5cbiAgLyoqXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IEluc3RhbGxBcmdzXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBleHQgLSB0aGUgbmFtZSBvciBzcGVjIG9mIGFuIGV4dGVuc2lvbiB0byBpbnN0YWxsXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBpbnN0YWxsVHlwZSAtIGhvdyB0byBpbnN0YWxsIHRoaXMgZXh0ZW5zaW9uLiBPbmUgb2YgdGhlIElOU1RBTExfVFlQRVNcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IFtwYWNrYWdlTmFtZV0gLSBmb3IgZ2l0L2dpdGh1YiBpbnN0YWxscywgdGhlIGV4dGVuc2lvbiBub2RlIHBhY2thZ2UgbmFtZVxuICAgKi9cblxuICAvKipcbiAgICogSW5zdGFsbCBhbiBleHRlbnNpb25cbiAgICpcbiAgICogQHBhcmFtIHtJbnN0YWxsQXJnc30gYXJnc1xuICAgKiBAcmV0dXJuIHtvYmplY3R9IG1hcCBvZiBhbGwgaW5zdGFsbGVkIGV4dGVuc2lvbiBuYW1lcyB0byBleHRlbnNpb24gZGF0YVxuICAgKi9cbiAgYXN5bmMgaW5zdGFsbCAoe2V4dCwgaW5zdGFsbFR5cGUsIHBhY2thZ2VOYW1lfSkge1xuICAgIGxvZyh0aGlzLmlzSnNvbk91dHB1dCwgYEF0dGVtcHRpbmcgdG8gZmluZCBhbmQgaW5zdGFsbCAke3RoaXMudHlwZX0gJyR7ZXh0fSdgKTtcblxuICAgIGxldCBleHREYXRhO1xuICAgIGxldCBpbnN0YWxsU3BlYyA9IGV4dDtcblxuICAgIGlmIChwYWNrYWdlTmFtZSAmJiBbSU5TVEFMTF9UWVBFX0xPQ0FMLCBJTlNUQUxMX1RZUEVfTlBNXS5pbmNsdWRlcyhpbnN0YWxsVHlwZSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgV2hlbiB1c2luZyAtLXNvdXJjZT0ke2luc3RhbGxUeXBlfSwgY2Fubm90IGFsc28gdXNlIC0tcGFja2FnZWApO1xuICAgIH1cblxuICAgIGlmICghcGFja2FnZU5hbWUgJiYgW0lOU1RBTExfVFlQRV9HSVQsIElOU1RBTExfVFlQRV9HSVRIVUJdLmluY2x1ZGVzKGluc3RhbGxUeXBlKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBXaGVuIHVzaW5nIC0tc291cmNlPSR7aW5zdGFsbFR5cGV9LCBtdXN0IGFsc28gdXNlIC0tcGFja2FnZWApO1xuICAgIH1cblxuICAgIGlmIChpbnN0YWxsVHlwZSA9PT0gSU5TVEFMTF9UWVBFX0xPQ0FMKSB7XG4gICAgICBjb25zdCBtc2cgPSBgTGlua2luZyAke3RoaXMudHlwZX0gZnJvbSBsb2NhbCBwYXRoYDtcbiAgICAgIGNvbnN0IHBrZ0pzb25EYXRhID0gYXdhaXQgc3BpbldpdGgodGhpcy5pc0pzb25PdXRwdXQsIG1zZywgYXN5bmMgKCkgPT4gKFxuICAgICAgICBhd2FpdCB0aGlzLm5wbS5saW5rUGFja2FnZShpbnN0YWxsU3BlYykpXG4gICAgICApO1xuICAgICAgZXh0RGF0YSA9IHRoaXMuZ2V0RXh0ZW5zaW9uRmllbGRzKHBrZ0pzb25EYXRhKTtcbiAgICAgIGV4dERhdGEuaW5zdGFsbFBhdGggPSBleHREYXRhLnBrZ05hbWU7XG4gICAgfSBlbHNlIGlmIChpbnN0YWxsVHlwZSA9PT0gSU5TVEFMTF9UWVBFX0dJVEhVQikge1xuICAgICAgaWYgKGluc3RhbGxTcGVjLnNwbGl0KCcvJykubGVuZ3RoICE9PSAyKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgR2l0aHViICR7dGhpcy50eXBlfSBzcGVjICR7aW5zdGFsbFNwZWN9IGFwcGVhcmVkIHRvIGJlIGludmFsaWQ7IGAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgJ2l0IHNob3VsZCBiZSBvZiB0aGUgZm9ybSA8b3JnPi88cmVwbz4nKTtcbiAgICAgIH1cbiAgICAgIGV4dERhdGEgPSBhd2FpdCB0aGlzLmluc3RhbGxWaWFOcG0oe2V4dDogaW5zdGFsbFNwZWMsIHBrZ05hbWU6IHBhY2thZ2VOYW1lfSk7XG4gICAgfSBlbHNlIGlmIChpbnN0YWxsVHlwZSA9PT0gSU5TVEFMTF9UWVBFX0dJVCkge1xuICAgICAgLy8gZ2l0IHVybHMgY2FuIGhhdmUgJy5naXQnIGF0IHRoZSBlbmQsIGJ1dCB0aGlzIGlzIG5vdCBuZWNlc3NhcnkgYW5kIHdvdWxkIGNvbXBsaWNhdGUgdGhlXG4gICAgICAvLyB3YXkgd2UgZG93bmxvYWQgYW5kIG5hbWUgZGlyZWN0b3JpZXMsIHNvIHdlIGNhbiBqdXN0IHJlbW92ZSBpdFxuICAgICAgaW5zdGFsbFNwZWMgPSBpbnN0YWxsU3BlYy5yZXBsYWNlKC9cXC5naXQkLywgJycpO1xuICAgICAgZXh0RGF0YSA9IGF3YWl0IHRoaXMuaW5zdGFsbFZpYU5wbSh7ZXh0OiBpbnN0YWxsU3BlYywgcGtnTmFtZTogcGFja2FnZU5hbWV9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gYXQgdGhpcyBwb2ludCB3ZSBoYXZlIGVpdGhlciBhbiBucG0gcGFja2FnZSBvciBhbiBhcHBpdW0gdmVyaWZpZWQgZXh0ZW5zaW9uXG4gICAgICAvLyBuYW1lLiBib3RoIG9mIHdoaWNoIHdpbGwgYmUgaW5zdGFsbGVkIHZpYSBucG0uXG4gICAgICAvLyBleHRlbnNpb25zIGluc3RhbGxlZCB2aWEgbnBtIGNhbiBpbmNsdWRlIHZlcnNpb25zIG9yIHRhZ3MgYWZ0ZXIgdGhlICdAJ1xuICAgICAgLy8gc2lnbiwgc28gY2hlY2sgZm9yIHRoYXQuIFdlIGFsc28gbmVlZCB0byBiZSBjYXJlZnVsIHRoYXQgcGFja2FnZSBuYW1lcyB0aGVtc2VsdmVzIGNhblxuICAgICAgLy8gY29udGFpbiB0aGUgJ0AnIHN5bWJvbCwgYXMgaW4gYG5wbSBpbnN0YWxsIEBhcHBpdW0vZmFrZS1kcml2ZXJAMS4yLjBgXG4gICAgICBsZXQgbmFtZSwgcGtnVmVyO1xuICAgICAgY29uc3Qgc3BsaXRzID0gaW5zdGFsbFNwZWMuc3BsaXQoJ0AnKTtcbiAgICAgIGlmIChpbnN0YWxsU3BlY1swXSA9PT0gJ0AnKSB7XG4gICAgICAgIC8vIHRoaXMgaXMgdGhlIGNhc2Ugd2hlcmUgd2UgaGF2ZSBhbiBucG0gb3JnIGluY2x1ZGVkIGluIHRoZSBwYWNrYWdlIG5hbWVcbiAgICAgICAgW25hbWUsIHBrZ1Zlcl0gPSBbYEAke3NwbGl0c1sxXX1gLCBzcGxpdHNbMl1dO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gdGhpcyBpcyB0aGUgY2FzZSB3aXRob3V0IGFuIG5wbSBvcmdcbiAgICAgICAgW25hbWUsIHBrZ1Zlcl0gPSBzcGxpdHM7XG4gICAgICB9XG4gICAgICBsZXQgcGtnTmFtZTtcblxuICAgICAgaWYgKGluc3RhbGxUeXBlID09PSBJTlNUQUxMX1RZUEVfTlBNKSB7XG4gICAgICAgIC8vIGlmIHdlJ3JlIGluc3RhbGxpbmcgYSBuYW1lZCBwYWNrYWdlIGZyb20gbnBtLCB3ZSBkb24ndCBuZWVkIHRvIGNoZWNrXG4gICAgICAgIC8vIGFnYWluc3QgdGhlIGFwcGl1bSBleHRlbnNpb24gbGlzdDsganVzdCB1c2UgdGhlIGluc3RhbGxTcGVjIGFzIGlzXG4gICAgICAgIHBrZ05hbWUgPSBuYW1lO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgLy8gaWYgd2UncmUgaW5zdGFsbGluZyBhIG5hbWVkIGFwcGl1bSBkcml2ZXIgKGxpa2UgJ3hjdWl0ZXN0Jykgd2UgbmVlZCB0b1xuICAgICAgICAvLyBkZXJlZmVyZW5jZSB0aGUgYWN0dWFsIG5wbSBwYWNrYWdlICgnYXBwaXVwbS14Y3VpdGVzdC1kcml2ZXInKSwgc29cbiAgICAgICAgLy8gY2hlY2sgaXQgZXhpc3RzIGFuZCBnZXQgdGhlIGNvcnJlY3QgcGFja2FnZVxuICAgICAgICBjb25zdCBrbm93bk5hbWVzID0gT2JqZWN0LmtleXModGhpcy5rbm93bkV4dGVuc2lvbnMpO1xuICAgICAgICBpZiAoIV8uaW5jbHVkZXMoa25vd25OYW1lcywgbmFtZSkpIHtcbiAgICAgICAgICBjb25zdCBtc2cgPSBgQ291bGQgbm90IHJlc29sdmUgJHt0aGlzLnR5cGV9OyBhcmUgeW91IHN1cmUgaXQncyBpbiB0aGUgbGlzdCBgICtcbiAgICAgICAgICAgICAgICAgICAgICBgb2Ygc3VwcG9ydGVkICR7dGhpcy50eXBlfXM/ICR7SlNPTi5zdHJpbmdpZnkoa25vd25OYW1lcyl9YDtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IobXNnKTtcbiAgICAgICAgfVxuICAgICAgICBwa2dOYW1lID0gdGhpcy5rbm93bkV4dGVuc2lvbnNbbmFtZV07XG4gICAgICAgIC8vIGdpdmVuIHRoYXQgd2UnbGwgdXNlIHRoZSBpbnN0YWxsIHR5cGUgaW4gdGhlIGRyaXZlciBqc29uLCBzdG9yZSBpdCBhc1xuICAgICAgICAvLyAnbnBtJyBub3dcbiAgICAgICAgaW5zdGFsbFR5cGUgPSBJTlNUQUxMX1RZUEVfTlBNO1xuICAgICAgfVxuXG4gICAgICBleHREYXRhID0gYXdhaXQgdGhpcy5pbnN0YWxsVmlhTnBtKHtleHQsIHBrZ05hbWUsIHBrZ1Zlcn0pO1xuICAgIH1cblxuICAgIGNvbnN0IGV4dE5hbWUgPSBleHREYXRhW2Ake3RoaXMudHlwZX1OYW1lYF07XG4gICAgZGVsZXRlIGV4dERhdGFbYCR7dGhpcy50eXBlfU5hbWVgXTtcblxuICAgIGlmICh0aGlzLmNvbmZpZy5pc0luc3RhbGxlZChleHROYW1lKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBBICR7dGhpcy50eXBlfSBuYW1lZCAnJHtleHROYW1lfScgaXMgYWxyZWFkeSBpbnN0YWxsZWQuIGAgK1xuICAgICAgICAgICAgICAgICAgICAgIGBEaWQgeW91IG1lYW4gdG8gdXBkYXRlPyAnYXBwaXVtICR7dGhpcy50eXBlfSB1cGRhdGUnLiBTZWUgYCArXG4gICAgICAgICAgICAgICAgICAgICAgYGluc3RhbGxlZCAke3RoaXMudHlwZX1zIHdpdGggJ2FwcGl1bSAke3RoaXMudHlwZX0gbGlzdCAtLWluc3RhbGxlZCcuYCk7XG4gICAgfVxuXG4gICAgZXh0RGF0YS5pbnN0YWxsVHlwZSA9IGluc3RhbGxUeXBlO1xuICAgIGV4dERhdGEuaW5zdGFsbFNwZWMgPSBpbnN0YWxsU3BlYztcbiAgICBhd2FpdCB0aGlzLmNvbmZpZy5hZGRFeHRlbnNpb24oZXh0TmFtZSwgZXh0RGF0YSk7XG5cbiAgICAvLyBsb2cgaW5mbyBmb3IgdGhlIHVzZXJcbiAgICBsb2codGhpcy5pc0pzb25PdXRwdXQsIHRoaXMuZ2V0UG9zdEluc3RhbGxUZXh0KHtleHROYW1lLCBleHREYXRhfSkpO1xuXG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLmluc3RhbGxlZEV4dGVuc2lvbnM7XG4gIH1cblxuICAvKipcbiAgICogQHR5cGVkZWYge09iamVjdH0gSW5zdGFsbFZpYU5wbUFyZ3NcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IGV4dCAtIHRoZSBuYW1lIG9yIHNwZWMgb2YgYW4gZXh0ZW5zaW9uIHRvIGluc3RhbGxcbiAgICogQHByb3BlcnR5IHtzdHJpbmd9IHBrZ05hbWUgLSB0aGUgTlBNIHBhY2thZ2UgbmFtZSBvZiB0aGUgZXh0ZW5zaW9uXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBbcGtnVmVyXSAtIHRoZSBzcGVjaWZpYyB2ZXJzaW9uIG9mIHRoZSBOUE0gcGFja2FnZVxuICAgKi9cblxuICAvKipcbiAgICogSW5zdGFsbCBhbiBleHRlbnNpb24gdmlhIE5QTVxuICAgKlxuICAgKiBAcGFyYW0ge0luc3RhbGxWaWFOcG1BcmdzfSBhcmdzXG4gICAqL1xuICBhc3luYyBpbnN0YWxsVmlhTnBtICh7ZXh0LCBwa2dOYW1lLCBwa2dWZXJ9KSB7XG4gICAgY29uc3QgbnBtU3BlYyA9IGAke3BrZ05hbWV9JHtwa2dWZXIgPyAnQCcgKyBwa2dWZXIgOiAnJ31gO1xuICAgIGNvbnN0IHNwZWNNc2cgPSBucG1TcGVjID09PSBleHQgPyAnJyA6IGAgdXNpbmcgTlBNIGluc3RhbGwgc3BlYyAnJHtucG1TcGVjfSdgO1xuICAgIGNvbnN0IG1zZyA9IGBJbnN0YWxsaW5nICcke2V4dH0nJHtzcGVjTXNnfWA7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHBrZ0pzb25EYXRhID0gYXdhaXQgc3BpbldpdGgodGhpcy5pc0pzb25PdXRwdXQsIG1zZywgYXN5bmMgKCkgPT4gKFxuICAgICAgICBhd2FpdCB0aGlzLm5wbS5pbnN0YWxsUGFja2FnZSh7XG4gICAgICAgICAgcGtnRGlyOiBwYXRoLnJlc29sdmUodGhpcy5jb25maWcuYXBwaXVtSG9tZSwgcGtnTmFtZSksXG4gICAgICAgICAgcGtnTmFtZSxcbiAgICAgICAgICBwa2dWZXJcbiAgICAgICAgfSlcbiAgICAgICkpO1xuICAgICAgY29uc3QgZXh0RGF0YSA9IHRoaXMuZ2V0RXh0ZW5zaW9uRmllbGRzKHBrZ0pzb25EYXRhKTtcbiAgICAgIGV4dERhdGEuaW5zdGFsbFBhdGggPSBwa2dOYW1lO1xuICAgICAgcmV0dXJuIGV4dERhdGE7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEVuY291bnRlcmVkIGFuIGVycm9yIHdoZW4gaW5zdGFsbGluZyBwYWNrYWdlOiAke2Vyci5tZXNzYWdlfWApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBFeHRlbnNpb25BcmdzXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBleHROYW1lIC0gdGhlIG5hbWUgb2YgYW4gZXh0ZW5zaW9uXG4gICAqIEBwcm9wZXJ0eSB7b2JqZWN0fSBleHREYXRhIC0gdGhlIGRhdGEgZm9yIGFuIGluc3RhbGxlZCBleHRlbnNpb25cbiAgICovXG5cbiAgLyoqXG4gICAqIEdldCB0aGUgdGV4dCB3aGljaCBzaG91bGQgYmUgZGlzcGxheWVkIHRvIHRoZSB1c2VyIGFmdGVyIGFuIGV4dGVuc2lvbiBoYXMgYmVlbiBpbnN0YWxsZWQuIFRoaXNcbiAgICogaXMgZGVzaWduZWQgdG8gYmUgb3ZlcnJpZGRlbiBieSBkcml2ZXJzL3BsdWdpbnMgd2l0aCB0aGVpciBvd24gcGFydGljdWxhciB0ZXh0LlxuICAgKlxuICAgKiBAcGFyYW0ge0V4dGVuc2lvbkFyZ3N9IGFyZ3NcbiAgICovXG4gIGdldFBvc3RJbnN0YWxsVGV4dCAoLyp7ZXh0TmFtZSwgZXh0RGF0YX0qLykge1xuICAgIHRocm93IG5ldyBFcnJvcignTXVzdCBiZSBpbXBsZW1lbnRlZCBpbiBmaW5hbCBjbGFzcycpO1xuICB9XG5cbiAgLyoqXG4gICAqIFRha2UgYW4gTlBNIG1vZHVsZSdzIHBhY2thZ2UuanNvbiBhbmQgZXh0cmFjdCBBcHBpdW0gZHJpdmVyIGluZm9ybWF0aW9uIGZyb20gYSBzcGVjaWFsXG4gICAqICdhcHBpdW0nIGZpZWxkIGluIHRoZSBKU09OIGRhdGEuIFdlIG5lZWQgdGhpcyBpbmZvcm1hdGlvbiB0byBlLmcuIGRldGVybWluZSB3aGljaCBjbGFzcyB0b1xuICAgKiBsb2FkIGFzIHRoZSBtYWluIGRyaXZlciBjbGFzcywgb3IgdG8gYmUgYWJsZSB0byBkZXRlY3QgaW5jb21wYXRpYmlsaXRpZXMgYmV0d2VlbiBkcml2ZXIgYW5kXG4gICAqIGFwcGl1bSB2ZXJzaW9ucy5cbiAgICpcbiAgICogQHBhcmFtIHtvYmplY3R9IHBrZ0pzb25EYXRhIC0gdGhlIHBhY2thZ2UuanNvbiBkYXRhIGZvciBhIGRyaXZlciBtb2R1bGUsIGFzIGlmIGl0IGhhZCBiZWVuXG4gICAqIHN0cmFpZ2h0Zm9yd2FyZGx5ICdyZXF1aXJlJ2RcbiAgICovXG4gIGdldEV4dGVuc2lvbkZpZWxkcyAocGtnSnNvbkRhdGEpIHtcbiAgICBpZiAoIXBrZ0pzb25EYXRhLmFwcGl1bSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnN0YWxsZWQgZHJpdmVyIGRpZCBub3QgaGF2ZSBhbiAnYXBwaXVtJyBzZWN0aW9uIGluIGl0cyBgICtcbiAgICAgICAgICAgICAgICAgICAgICBgcGFja2FnZS5qc29uIGZpbGUgYXMgZXhwZWN0ZWRgKTtcbiAgICB9XG4gICAgY29uc3Qge2FwcGl1bSwgbmFtZSwgdmVyc2lvbn0gPSBwa2dKc29uRGF0YTtcbiAgICB0aGlzLnZhbGlkYXRlRXh0ZW5zaW9uRmllbGRzKGFwcGl1bSk7XG5cbiAgICByZXR1cm4gey4uLmFwcGl1bSwgcGtnTmFtZTogbmFtZSwgdmVyc2lvbn07XG4gIH1cblxuICAvKipcbiAgICogRm9yIGFueSBwYWNrYWdlLmpzb24gZmllbGRzIHdoaWNoIGEgcGFydGljdWxhciB0eXBlIG9mIGV4dGVuc2lvbiByZXF1aXJlcywgdmFsaWRhdGUgdGhlXG4gICAqIHByZXNlbmNlIGFuZCBmb3JtIG9mIHRob3NlIGZpZWxkcyBvbiB0aGUgcGFja2FnZS5qc29uIGRhdGEsIHRocm93aW5nIGFuIGVycm9yIGlmIGFueXRoaW5nIGlzXG4gICAqIGFtaXNzLlxuICAgKlxuICAgKiBAcGFyYW0ge29iamVjdH0gYXBwaXVtUGtnRGF0YSAtIHRoZSBkYXRhIGluIHRoZSBcImFwcGl1bVwiIGZpZWxkIG9mIHBhY2thZ2UuanNvbiBmb3IgYW5cbiAgICogZXh0ZW5zaW9uXG4gICAqL1xuICB2YWxpZGF0ZUV4dGVuc2lvbkZpZWxkcyAoLyphcHBpdW1Qa2dEYXRhKi8pIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ011c3QgYmUgaW1wbGVtZW50ZWQgaW4gZmluYWwgY2xhc3MnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBVbmluc3RhbGxBcmdzXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBleHQgLSB0aGUgbmFtZSBvciBzcGVjIG9mIGFuIGV4dGVuc2lvbiB0byB1bmluc3RhbGxcbiAgICovXG5cbiAgLyoqXG4gICAqIFVuaW5zdGFsbCBhbiBleHRlbnNpb25cbiAgICpcbiAgICogQHBhcmFtIHtVbmluc3RhbGxBcmdzfSBhcmdzXG4gICAqIEByZXR1cm4ge29iamVjdH0gbWFwIG9mIGFsbCBpbnN0YWxsZWQgZXh0ZW5zaW9uIG5hbWVzIHRvIGV4dGVuc2lvbiBkYXRhXG4gICAqL1xuICBhc3luYyB1bmluc3RhbGwgKHtleHR9KSB7XG4gICAgaWYgKCF0aGlzLmNvbmZpZy5pc0luc3RhbGxlZChleHQpKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYENhbid0IHVuaW5zdGFsbCAke3RoaXMudHlwZX0gJyR7ZXh0fSc7IGl0IGlzIG5vdCBpbnN0YWxsZWRgKTtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGF3YWl0IGZzLnJpbXJhZih0aGlzLmNvbmZpZy5nZXRJbnN0YWxsUGF0aChleHQpKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgYXdhaXQgdGhpcy5jb25maWcucmVtb3ZlRXh0ZW5zaW9uKGV4dCk7XG4gICAgfVxuICAgIGxvZyh0aGlzLmlzSnNvbk91dHB1dCwgYFN1Y2Nlc3NmdWxseSB1bmluc3RhbGxlZCAke3RoaXMudHlwZX0gJyR7ZXh0fSdgLmdyZWVuKTtcbiAgICByZXR1cm4gdGhpcy5jb25maWcuaW5zdGFsbGVkRXh0ZW5zaW9ucztcbiAgfVxuXG4gIC8qKlxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBFeHRlbnNpb25VcGRhdGVPcHRzXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBleHQgLSB0aGUgbmFtZSBvZiB0aGUgZXh0ZW5zaW9uIHRvIHVwZGF0ZVxuICAgKiBAcHJvcGVydHkge2Jvb2xlYW59IHVuc2FmZSAtIGlmIHRydWUsIHdpbGwgcGVyZm9ybSB1bnNhZmUgdXBkYXRlcyBwYXN0IG1ham9yIHJldmlzaW9uXG4gICAqIGJvdW5kYXJpZXNcbiAgICovXG5cbiAgLyoqXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IFVwZGF0ZVJlcG9ydFxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gZnJvbSAtIHZlcnNpb24gdXBkYXRlZCBmcm9tXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSB0byAtIHZlcnNpb24gdXBkYXRlZCB0b1xuICAgKi9cblxuICAvKipcbiAgICogQHR5cGVkZWYge09iamVjdH0gRXh0ZW5zaW9uVXBkYXRlUmVzdWx0XG4gICAqIEBwcm9wZXJ0eSB7T2JqZWN0fSBlcnJvcnMgLSBtYXAgb2YgZXh0IG5hbWVzIHRvIGVycm9yIG9iamVjdHNcbiAgICogQHByb3BlcnR5IHtPYmplY3R9IHVwZGF0ZXMgLSBtYXAgb2YgZXh0IG5hbWVzIHRvIHtAbGluayBVcGRhdGVSZXBvcnR9c1xuICAgKi9cblxuICAvKipcbiAgICogQXR0ZW1wdCB0byB1cGRhdGUgb25lIG9yIG1vcmUgZHJpdmVycyB1c2luZyBOUE1cbiAgICpcbiAgICogQHBhcmFtIHtFeHRlbnNpb25VcGRhdGVPcHRzfSB1cGRhdGVTcGVjXG4gICAqIEByZXR1cm4ge0V4dGVuc2lvblVwZGF0ZVJlc3VsdH1cbiAgICovXG4gIGFzeW5jIHVwZGF0ZSAoe2V4dCwgdW5zYWZlfSkge1xuICAgIGNvbnN0IHNob3VsZFVwZGF0ZUFsbCA9IGV4dCA9PT0gVVBEQVRFX0FMTDtcbiAgICAvLyBpZiB3ZSdyZSBzcGVjaWZpY2FsbHkgcmVxdWVzdGluZyBhbiB1cGRhdGUgZm9yIGFuIGV4dGVuc2lvbiwgbWFrZSBzdXJlIGl0J3MgaW5zdGFsbGVkXG4gICAgaWYgKCFzaG91bGRVcGRhdGVBbGwgJiYgIXRoaXMuY29uZmlnLmlzSW5zdGFsbGVkKGV4dCkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVGhlICR7dGhpcy50eXBlfSAnJHtleHR9JyB3YXMgbm90IGluc3RhbGxlZCwgc28gY2FuJ3QgYmUgdXBkYXRlZGApO1xuICAgIH1cbiAgICBjb25zdCBleHRzVG9VcGRhdGUgPSBzaG91bGRVcGRhdGVBbGwgPyBPYmplY3Qua2V5cyh0aGlzLmNvbmZpZy5pbnN0YWxsZWRFeHRlbnNpb25zKSA6IFtleHRdO1xuXG4gICAgLy8gJ2Vycm9ycycgd2lsbCBoYXZlIGV4dCBuYW1lcyBhcyBrZXlzIGFuZCBlcnJvciBvYmplY3RzIGFzIHZhbHVlc1xuICAgIGNvbnN0IGVycm9ycyA9IHt9O1xuXG4gICAgLy8gJ3VwZGF0ZXMnIHdpbGwgaGF2ZSBleHQgbmFtZXMgYXMga2V5cyBhbmQgdXBkYXRlIG9iamVjdHMgYXMgdmFsdWVzLCB3aGVyZSBhbiB1cGRhdGVcbiAgICAvLyBvYmplY3QgaXMgb2YgdGhlIGZvcm0ge2Zyb206IHZlcnNpb25TdHJpbmcsIHRvOiB2ZXJzaW9uU3RyaW5nfVxuICAgIGNvbnN0IHVwZGF0ZXMgPSB7fTtcblxuICAgIGZvciAoY29uc3QgZSBvZiBleHRzVG9VcGRhdGUpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IHNwaW5XaXRoKHRoaXMuaXNKc29uT3V0cHV0LCBgQ2hlY2tpbmcgaWYgJHt0aGlzLnR5cGV9ICcke2V9JyBpcyB1cGRhdGFibGVgLCAoKSA9PiB7XG4gICAgICAgICAgaWYgKHRoaXMuY29uZmlnLmluc3RhbGxlZEV4dGVuc2lvbnNbZV0uaW5zdGFsbFR5cGUgIT09IElOU1RBTExfVFlQRV9OUE0pIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBOb3RVcGRhdGFibGVFcnJvcigpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICAgIGNvbnN0IHVwZGF0ZSA9IGF3YWl0IHNwaW5XaXRoKHRoaXMuaXNKc29uT3V0cHV0LCBgQ2hlY2tpbmcgaWYgJHt0aGlzLnR5cGV9ICcke2V9JyBuZWVkcyBhbiB1cGRhdGVgLCBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgY29uc3QgdXBkYXRlID0gYXdhaXQgdGhpcy5jaGVja0ZvckV4dGVuc2lvblVwZGF0ZShlKTtcbiAgICAgICAgICBpZiAoISh1cGRhdGUuc2FmZVVwZGF0ZSB8fCB1cGRhdGUudW5zYWZlVXBkYXRlKSkge1xuICAgICAgICAgICAgdGhyb3cgbmV3IE5vVXBkYXRlc0F2YWlsYWJsZUVycm9yKCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJldHVybiB1cGRhdGU7XG4gICAgICAgIH0pO1xuICAgICAgICBpZiAoIXVuc2FmZSAmJiAhdXBkYXRlLnNhZmVVcGRhdGUpIHtcbiAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFRoZSAke3RoaXMudHlwZX0gJyR7ZX0nIGhhcyBhIG1ham9yIHJldmlzaW9uIHVwZGF0ZSBgICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgYCgke3VwZGF0ZS5jdXJyZW50fSA9PiAke3VwZGF0ZS51bnNhZmVVcGRhdGV9KSwgd2hpY2ggY291bGQgaW5jbHVkZSBgICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgYGJyZWFraW5nIGNoYW5nZXMuIElmIHlvdSB3YW50IHRvIGFwcGx5IHRoaXMgdXBkYXRlLCByZS1ydW4gd2l0aCAtLXVuc2FmZWApO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHVwZGF0ZVZlciA9IHVuc2FmZSAmJiB1cGRhdGUudW5zYWZlVXBkYXRlID8gdXBkYXRlLnVuc2FmZVVwZGF0ZSA6IHVwZGF0ZS5zYWZlVXBkYXRlO1xuICAgICAgICBhd2FpdCBzcGluV2l0aChcbiAgICAgICAgICB0aGlzLmlzSnNvbk91dHB1dCxcbiAgICAgICAgICBgVXBkYXRpbmcgZHJpdmVyICcke2V9JyBmcm9tICR7dXBkYXRlLmN1cnJlbnR9IHRvICR7dXBkYXRlVmVyfWAsXG4gICAgICAgICAgYXN5bmMgKCkgPT4gYXdhaXQgdGhpcy51cGRhdGVFeHRlbnNpb24oZSwgdXBkYXRlVmVyKVxuICAgICAgICApO1xuICAgICAgICB1cGRhdGVzW2VdID0ge2Zyb206IHVwZGF0ZS5jdXJyZW50LCB0bzogdXBkYXRlVmVyfTtcbiAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICBlcnJvcnNbZV0gPSBlcnI7XG4gICAgICB9XG4gICAgfVxuXG4gICAgbG9nKHRoaXMuaXNKc29uT3V0cHV0LCAnVXBkYXRlIHJlcG9ydDonKTtcbiAgICBmb3IgKGNvbnN0IFtlLCB1cGRhdGVdIG9mIF8udG9QYWlycyh1cGRhdGVzKSkge1xuICAgICAgbG9nKHRoaXMuaXNKc29uT3V0cHV0LCBgLSAke3RoaXMudHlwZX0gJHtlfSB1cGRhdGVkOiAke3VwZGF0ZS5mcm9tfSA9PiAke3VwZGF0ZS50b31gLmdyZWVuKTtcbiAgICB9XG4gICAgZm9yIChjb25zdCBbZSwgZXJyXSBvZiBfLnRvUGFpcnMoZXJyb3JzKSkge1xuICAgICAgaWYgKGVyciBpbnN0YW5jZW9mIE5vdFVwZGF0YWJsZUVycm9yKSB7XG4gICAgICAgIGxvZyh0aGlzLmlzSnNvbk91dHB1dCwgYC0gJyR7ZX0nIHdhcyBub3QgaW5zdGFsbGVkIHZpYSBucG0sIHNvIHdlIGNvdWxkIG5vdCBjaGVjayBgICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgZm9yIHVwZGF0ZXNgLnllbGxvdyk7XG4gICAgICB9IGVsc2UgaWYgKGVyciBpbnN0YW5jZW9mIE5vVXBkYXRlc0F2YWlsYWJsZUVycm9yKSB7XG4gICAgICAgIGxvZyh0aGlzLmlzSnNvbk91dHB1dCwgYC0gJyR7ZX0nIGhhZCBubyB1cGRhdGVzIGF2YWlsYWJsZWAueWVsbG93KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIG90aGVyd2lzZSwgbWFrZSBpdCBwb3Agd2l0aCByZWQhXG4gICAgICAgIGxvZyh0aGlzLmlzSnNvbk91dHB1dCwgYC0gJyR7ZX0nIGZhaWxlZCB0byB1cGRhdGU6ICR7ZXJyfWAucmVkKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4ge3VwZGF0ZXMsIGVycm9yc307XG4gIH1cblxuICAvKipcbiAgICogQHR5cGVkZWYgUG9zc2libGVVcGRhdGVzXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBjdXJyZW50IC0gY3VycmVudCB2ZXJzaW9uXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfG51bGx9IHNhZmVVcGRhdGUgLSB2ZXJzaW9uIHdlIGNhbiBzYWZlbHkgdXBkYXRlIHRvIGlmIGl0IGV4aXN0cywgb3IgbnVsbFxuICAgKiBAcHJvcGVydHkge3N0cmluZ3xudWxsfSB1bnNhZmVVcGRhdGUgLSB2ZXJzaW9uIHdlIGNhbiB1bnNhZmVseSB1cGRhdGUgdG8gaWYgaXQgZXhpc3RzLCBvciBudWxsXG4gICAqL1xuXG4gIC8qKlxuICAgKiBHaXZlbiBhbiBleHRlbnNpb24gbmFtZSwgZmlndXJlIG91dCB3aGF0IGl0cyBoaWdoZXN0IHBvc3NpYmxlIHZlcnNpb24gdXBncmFkZSBpcywgYW5kIGFsc28gdGhlXG4gICAqIGhpZ2hlc3QgcG9zc2libGUgc2FmZSB1cGdyYWRlLlxuICAgKlxuICAgKiBAcGFyYW0ge3N0cmluZ30gZXh0IC0gbmFtZSBvZiBleHRlbnNpb25cbiAgICogQHJldHVybiB7UG9zc2libGVVcGRhdGVzfVxuICAgKi9cbiAgYXN5bmMgY2hlY2tGb3JFeHRlbnNpb25VcGRhdGUgKGV4dCkge1xuICAgIC8vIFRPRE8gZGVjaWRlIGhvdyB3ZSB3YW50IHRvIGhhbmRsZSBiZXRhIHZlcnNpb25zP1xuICAgIC8vIHRoaXMgaXMgYSBoZWxwZXIgbWV0aG9kLCAnZXh0JyBpcyBhc3N1bWVkIHRvIGFscmVhZHkgYmUgaW5zdGFsbGVkIGhlcmUsIGFuZCBvZiB0aGUgbnBtXG4gICAgLy8gaW5zdGFsbCB0eXBlXG4gICAgY29uc3Qge3ZlcnNpb24sIHBrZ05hbWV9ID0gdGhpcy5jb25maWcuaW5zdGFsbGVkRXh0ZW5zaW9uc1tleHRdO1xuICAgIGxldCB1bnNhZmVVcGRhdGUgPSBhd2FpdCB0aGlzLm5wbS5nZXRMYXRlc3RWZXJzaW9uKHBrZ05hbWUpO1xuICAgIGxldCBzYWZlVXBkYXRlID0gYXdhaXQgdGhpcy5ucG0uZ2V0TGF0ZXN0U2FmZVVwZ3JhZGVWZXJzaW9uKHBrZ05hbWUsIHZlcnNpb24pO1xuICAgIGlmICghdXRpbC5jb21wYXJlVmVyc2lvbnModW5zYWZlVXBkYXRlLCAnPicsIHZlcnNpb24pKSB7XG4gICAgICAvLyB0aGUgbGF0ZXN0IHZlcnNpb24gaXMgbm90IGdyZWF0ZXIgdGhhbiB0aGUgY3VycmVudCB2ZXJzaW9uLCBzbyB0aGVyZSdzIG5vIHBvc3NpYmxlIHVwZGF0ZVxuICAgICAgdW5zYWZlVXBkYXRlID0gbnVsbDtcbiAgICAgIHNhZmVVcGRhdGUgPSBudWxsO1xuICAgIH1cbiAgICBpZiAodW5zYWZlVXBkYXRlICYmIHVuc2FmZVVwZGF0ZSA9PT0gc2FmZVVwZGF0ZSkge1xuICAgICAgLy8gdGhlIGxhdGVzdCB1cGRhdGUgaXMgdGhlIHNhbWUgYXMgdGhlIHNhZmUgdXBkYXRlLCB3aGljaCBtZWFucyBpdCdzIG5vdCBhY3R1YWxseSB1bnNhZmVcbiAgICAgIHVuc2FmZVVwZGF0ZSA9IG51bGw7XG4gICAgfVxuICAgIGlmIChzYWZlVXBkYXRlICYmICF1dGlsLmNvbXBhcmVWZXJzaW9ucyhzYWZlVXBkYXRlLCAnPicsIHZlcnNpb24pKSB7XG4gICAgICAvLyBldmVuIHRoZSBzYWZlIHVwZGF0ZSBpcyBub3QgbGF0ZXIgdGhhbiB0aGUgY3VycmVudCwgc28gaXQgaXMgbm90IGFjdHVhbGx5IGFuIHVwZGF0ZVxuICAgICAgc2FmZVVwZGF0ZSA9IG51bGw7XG4gICAgfVxuICAgIHJldHVybiB7Y3VycmVudDogdmVyc2lvbiwgc2FmZVVwZGF0ZSwgdW5zYWZlVXBkYXRlfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBY3R1YWxseSB1cGRhdGUgYW4gZXh0ZW5zaW9uIGluc3RhbGxlZCBieSBOUE0sIHVzaW5nIHRoZSBOUE0gY2xpLiBBbmQgdXBkYXRlIHRoZSBpbnN0YWxsYXRpb25cbiAgICogbWFuaWZlc3QuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBleHQgLSBuYW1lIG9mIGV4dGVuc2lvbiB0byB1cGRhdGVcbiAgICogQHBhcmFtIHtzdHJpbmd9IHZlcnNpb24gLSB2ZXJzaW9uIHN0cmluZyBpZGVudGlmaWVyIHRvIHVwZGF0ZSBleHRlbnNpb24gdG9cbiAgICovXG4gIGFzeW5jIHVwZGF0ZUV4dGVuc2lvbiAoZXh0LCB2ZXJzaW9uKSB7XG4gICAgY29uc3Qge3BrZ05hbWV9ID0gdGhpcy5jb25maWcuaW5zdGFsbGVkRXh0ZW5zaW9uc1tleHRdO1xuICAgIGF3YWl0IHRoaXMuaW5zdGFsbFZpYU5wbSh7ZXh0LCBwa2dOYW1lLCBwa2dWZXI6IHZlcnNpb259KTtcbiAgICB0aGlzLmNvbmZpZy5pbnN0YWxsZWRFeHRlbnNpb25zW2V4dF0udmVyc2lvbiA9IHZlcnNpb247XG4gICAgYXdhaXQgdGhpcy5jb25maWcud3JpdGUoKTtcbiAgfVxufVxuIl0sImZpbGUiOiJsaWIvY2xpL2V4dGVuc2lvbi1jb21tYW5kLmpzIiwic291cmNlUm9vdCI6Ii4uLy4uLy4uIn0=
|
|
606
|
+
exports.ExtensionCommand = ExtensionCommand;
|
|
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
|