appium 2.0.0-beta.7 → 2.0.0-beta.70
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/LICENSE +1 -1
- package/README.md +149 -58
- package/build/lib/appium.d.ts +229 -0
- package/build/lib/appium.d.ts.map +1 -0
- package/build/lib/appium.js +678 -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 +263 -300
- 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 +131 -81
- package/build/lib/cli/driver-command.js.map +1 -0
- package/build/lib/cli/extension-command.d.ts +402 -0
- package/build/lib/cli/extension-command.d.ts.map +1 -0
- package/build/lib/cli/extension-command.js +799 -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 -60
- 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 +252 -148
- 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 +125 -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 +262 -223
- package/build/lib/config.js.map +1 -0
- package/build/lib/constants.d.ts +56 -0
- package/build/lib/constants.d.ts.map +1 -0
- package/build/lib/constants.js +73 -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 +210 -0
- package/build/lib/extension/driver-config.js.map +1 -0
- package/build/lib/extension/extension-config.d.ts +270 -0
- package/build/lib/extension/extension-config.d.ts.map +1 -0
- package/build/lib/extension/extension-config.js +601 -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 +134 -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 +528 -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 +189 -184
- 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 +388 -234
- 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 +220 -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 +260 -0
- package/build/lib/schema/schema.d.ts.map +1 -0
- package/build/lib/schema/schema.js +640 -0
- package/build/lib/schema/schema.js.map +1 -0
- package/build/lib/utils.d.ts +266 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +349 -273
- package/build/lib/utils.js.map +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 +21 -0
- package/build/types/manifest/index.d.ts.map +1 -0
- package/build/types/manifest/index.js +42 -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/build/types/manifest/v4.d.ts +139 -0
- package/build/types/manifest/v4.d.ts.map +1 -0
- package/build/types/manifest/v4.js +3 -0
- package/build/types/manifest/v4.js.map +1 -0
- package/driver.d.ts +1 -0
- package/driver.js +14 -0
- package/index.js +11 -0
- package/lib/appium.js +558 -186
- package/lib/cli/args.js +275 -407
- package/lib/cli/driver-command.js +132 -24
- package/lib/cli/extension-command.js +751 -272
- package/lib/cli/extension.js +47 -20
- package/lib/cli/parser.js +267 -95
- package/lib/cli/plugin-command.js +122 -22
- package/lib/cli/utils.js +24 -10
- package/lib/config-file.js +220 -0
- package/lib/config.js +243 -132
- package/lib/constants.js +79 -0
- package/lib/extension/driver-config.js +247 -0
- package/lib/extension/extension-config.js +709 -0
- package/lib/extension/index.js +116 -0
- package/lib/extension/manifest-migrations.js +136 -0
- package/lib/extension/manifest.js +580 -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 +59 -36
- package/lib/main.js +369 -104
- package/lib/schema/arg-spec.js +229 -0
- package/lib/schema/cli-args.js +241 -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 +725 -0
- package/lib/utils.js +289 -167
- package/package.json +84 -83
- package/plugin.d.ts +1 -0
- package/plugin.js +13 -0
- package/scripts/autoinstall-extensions.js +243 -0
- package/support.d.ts +1 -0
- package/support.js +13 -0
- package/tsconfig.json +25 -0
- package/types/cli.ts +193 -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 +28 -0
- package/types/manifest/v3.ts +161 -0
- package/types/manifest/v4.ts +161 -0
- package/CHANGELOG.md +0 -3594
- package/bin/ios-webkit-debug-proxy-launcher.js +0 -71
- package/build/lib/cli/argparse-actions.js +0 -104
- package/build/lib/cli/npm.js +0 -200
- package/build/lib/cli/parser-helpers.js +0 -93
- package/build/lib/driver-config.js +0 -77
- package/build/lib/drivers.js +0 -99
- 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/argparse-actions.js +0 -77
- package/lib/cli/npm.js +0 -175
- package/lib/cli/parser-helpers.js +0 -91
- package/lib/driver-config.js +0 -46
- package/lib/drivers.js +0 -84
- package/lib/extension-config.js +0 -209
- package/lib/plugin-config.js +0 -34
- package/lib/plugins.js +0 -10
package/build/lib/appium.js
CHANGED
|
@@ -1,459 +1,698 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Object.defineProperty(exports, "__esModule", {
|
|
6
|
-
value: true
|
|
7
|
-
});
|
|
8
|
-
exports.AppiumDriver = void 0;
|
|
9
|
-
|
|
10
|
-
require("source-map-support/register");
|
|
11
|
-
|
|
12
|
-
var _lodash = _interopRequireDefault(require("lodash"));
|
|
13
|
-
|
|
14
|
-
var _logger = _interopRequireDefault(require("./logger"));
|
|
15
|
-
|
|
16
|
-
var _config = require("./config");
|
|
17
|
-
|
|
18
|
-
var _drivers = require("./drivers");
|
|
19
|
-
|
|
20
|
-
var _appiumBaseDriver = require("appium-base-driver");
|
|
21
|
-
|
|
22
|
-
var _bluebird = _interopRequireDefault(require("bluebird"));
|
|
23
|
-
|
|
24
|
-
var _asyncLock = _interopRequireDefault(require("async-lock"));
|
|
25
|
-
|
|
26
|
-
var _utils = require("./utils");
|
|
27
|
-
|
|
28
|
-
var _appiumSupport = require("appium-support");
|
|
29
|
-
|
|
30
|
-
const desiredCapabilityConstraints = {
|
|
31
|
-
automationName: {
|
|
32
|
-
presence: true,
|
|
33
|
-
isString: true
|
|
34
|
-
},
|
|
35
|
-
platformName: {
|
|
36
|
-
presence: true,
|
|
37
|
-
isString: true
|
|
38
|
-
}
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
39
4
|
};
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AppiumDriver = exports.NoDriverProxyCommandError = void 0;
|
|
7
|
+
/* eslint-disable no-unused-vars */
|
|
8
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
9
|
+
const config_1 = require("./config");
|
|
10
|
+
const base_driver_1 = require("@appium/base-driver");
|
|
11
|
+
const async_lock_1 = __importDefault(require("async-lock"));
|
|
12
|
+
const utils_1 = require("./utils");
|
|
13
|
+
const support_1 = require("@appium/support");
|
|
14
|
+
const schema_1 = require("./schema");
|
|
15
|
+
const constants_1 = require("./constants");
|
|
16
|
+
const desiredCapabilityConstraints = /** @type {const} */ ({
|
|
17
|
+
automationName: {
|
|
18
|
+
presence: true,
|
|
19
|
+
isString: true,
|
|
20
|
+
},
|
|
21
|
+
platformName: {
|
|
22
|
+
presence: true,
|
|
23
|
+
isString: true,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
const sessionsListGuard = new async_lock_1.default();
|
|
27
|
+
const pendingDriversGuard = new async_lock_1.default();
|
|
28
|
+
/**
|
|
29
|
+
* @extends {DriverCore<AppiumDriverConstraints>}
|
|
30
|
+
*/
|
|
31
|
+
class AppiumDriver extends base_driver_1.DriverCore {
|
|
32
|
+
/**
|
|
33
|
+
* @param {import('@appium/types').DriverOpts<AppiumDriverConstraints>} opts
|
|
34
|
+
*/
|
|
35
|
+
constructor(opts) {
|
|
36
|
+
// It is necessary to set `--tmp` here since it should be set to
|
|
37
|
+
// process.env.APPIUM_TMP_DIR once at an initial point in the Appium lifecycle.
|
|
38
|
+
// The process argument will be referenced by BaseDriver.
|
|
39
|
+
// Please call @appium/support.tempDir module to apply this benefit.
|
|
40
|
+
if (opts.tmpDir) {
|
|
41
|
+
process.env.APPIUM_TMP_DIR = opts.tmpDir;
|
|
42
|
+
}
|
|
43
|
+
super(opts);
|
|
44
|
+
this.args = { ...opts };
|
|
45
|
+
this.sessions = {};
|
|
46
|
+
this.pendingDrivers = {};
|
|
47
|
+
this.newCommandTimeoutMs = 0;
|
|
48
|
+
this.pluginClasses = new Map();
|
|
49
|
+
this.sessionPlugins = {};
|
|
50
|
+
this.sessionlessPlugins = [];
|
|
51
|
+
this.desiredCapConstraints = desiredCapabilityConstraints;
|
|
52
|
+
// allow this to happen in the background, so no `await`
|
|
53
|
+
(async () => {
|
|
54
|
+
try {
|
|
55
|
+
await (0, config_1.updateBuildInfo)();
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
// make sure we catch any possible errors to avoid unhandled rejections
|
|
59
|
+
this.log.debug(`Cannot fetch Appium build info: ${e.message}`);
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
47
62
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
get isCommandsQueueEnabled() {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
sessionExists(sessionId) {
|
|
64
|
-
const dstSession = this.sessions[sessionId];
|
|
65
|
-
return dstSession && dstSession.sessionId !== null;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
driverForSession(sessionId) {
|
|
69
|
-
return this.sessions[sessionId];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
async getStatus() {
|
|
73
|
-
return {
|
|
74
|
-
build: _lodash.default.clone((0, _config.getBuildInfo)())
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async getSessions() {
|
|
79
|
-
const sessions = await sessionsListGuard.acquire(AppiumDriver.name, () => this.sessions);
|
|
80
|
-
return _lodash.default.toPairs(sessions).map(([id, driver]) => ({
|
|
81
|
-
id,
|
|
82
|
-
capabilities: driver.caps
|
|
83
|
-
}));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
printNewSessionAnnouncement(driverName, driverVersion) {
|
|
87
|
-
const introString = driverVersion ? `Appium v${_config.APPIUM_VER} creating new ${driverName} (v${driverVersion}) session` : `Appium v${_config.APPIUM_VER} creating new ${driverName} session`;
|
|
88
|
-
|
|
89
|
-
_logger.default.info(introString);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
_findMatchingDriver(...args) {
|
|
93
|
-
return (0, _drivers.findMatchingDriver)(...args);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async createSession(jsonwpCaps, reqCaps, w3cCapabilities) {
|
|
97
|
-
const defaultCapabilities = _lodash.default.cloneDeep(this.args.defaultCapabilities);
|
|
98
|
-
|
|
99
|
-
const defaultSettings = (0, _utils.pullSettings)(defaultCapabilities);
|
|
100
|
-
jsonwpCaps = _lodash.default.cloneDeep(jsonwpCaps);
|
|
101
|
-
const jwpSettings = Object.assign({}, defaultSettings, (0, _utils.pullSettings)(jsonwpCaps));
|
|
102
|
-
w3cCapabilities = _lodash.default.cloneDeep(w3cCapabilities);
|
|
103
|
-
const w3cSettings = Object.assign({}, jwpSettings);
|
|
104
|
-
Object.assign(w3cSettings, (0, _utils.pullSettings)((w3cCapabilities || {}).alwaysMatch || {}));
|
|
105
|
-
|
|
106
|
-
for (const firstMatchEntry of (w3cCapabilities || {}).firstMatch || []) {
|
|
107
|
-
Object.assign(w3cSettings, (0, _utils.pullSettings)(firstMatchEntry));
|
|
63
|
+
/**
|
|
64
|
+
* Retrieves logger instance for the current umbrella driver instance
|
|
65
|
+
*/
|
|
66
|
+
get log() {
|
|
67
|
+
if (!this._log) {
|
|
68
|
+
const instanceName = `${this.constructor.name}@${support_1.node.getObjectId(this).substring(0, 4)}`;
|
|
69
|
+
this._log = support_1.logger.getLogger(instanceName);
|
|
70
|
+
}
|
|
71
|
+
return this._log;
|
|
108
72
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Cancel commands queueing for the umbrella Appium driver
|
|
75
|
+
*/
|
|
76
|
+
get isCommandsQueueEnabled() {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
sessionExists(sessionId) {
|
|
80
|
+
const dstSession = this.sessions[sessionId];
|
|
81
|
+
return dstSession && dstSession.sessionId !== null;
|
|
82
|
+
}
|
|
83
|
+
driverForSession(sessionId) {
|
|
84
|
+
return this.sessions[sessionId];
|
|
85
|
+
}
|
|
86
|
+
// eslint-disable-next-line require-await
|
|
87
|
+
async getStatus() {
|
|
88
|
+
return {
|
|
89
|
+
build: lodash_1.default.clone((0, config_1.getBuildInfo)()),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async getSessions() {
|
|
93
|
+
return lodash_1.default.toPairs(this.sessions).map(([id, driver]) => ({
|
|
94
|
+
id,
|
|
95
|
+
capabilities: /** @type {import('@appium/types').DriverCaps} */ (driver.caps),
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
printNewSessionAnnouncement(driverName, driverVersion, driverBaseVersion) {
|
|
99
|
+
this.log.info(driverVersion
|
|
100
|
+
? `Appium v${config_1.APPIUM_VER} creating new ${driverName} (v${driverVersion}) session`
|
|
101
|
+
: `Appium v${config_1.APPIUM_VER} creating new ${driverName} session`);
|
|
102
|
+
this.log.info(`Checking BaseDriver versions for Appium and ${driverName}`);
|
|
103
|
+
this.log.info(AppiumDriver.baseVersion
|
|
104
|
+
? `Appium's BaseDriver version is ${AppiumDriver.baseVersion}`
|
|
105
|
+
: `Could not determine Appium's BaseDriver version`);
|
|
106
|
+
this.log.info(driverBaseVersion
|
|
107
|
+
? `${driverName}'s BaseDriver version is ${driverBaseVersion}`
|
|
108
|
+
: `Could not determine ${driverName}'s BaseDriver version`);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Retrieves all CLI arguments for a specific plugin.
|
|
112
|
+
* @param {string} extName - Plugin name
|
|
113
|
+
* @returns {StringRecord} Arguments object. If none, an empty object.
|
|
114
|
+
*/
|
|
115
|
+
getCliArgsForPlugin(extName) {
|
|
116
|
+
return /** @type {StringRecord} */ (this.args.plugin?.[extName] ?? {});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Retrieves CLI args for a specific driver.
|
|
120
|
+
*
|
|
121
|
+
* _Any arg which is equal to its default value will not be present in the returned object._
|
|
122
|
+
*
|
|
123
|
+
* _Note that this behavior currently (May 18 2022) differs from how plugins are handled_ (see {@linkcode AppiumDriver.getCliArgsForPlugin}).
|
|
124
|
+
* @param {string} extName - Driver name
|
|
125
|
+
* @returns {StringRecord|undefined} Arguments object. If none, `undefined`
|
|
126
|
+
*/
|
|
127
|
+
getCliArgsForDriver(extName) {
|
|
128
|
+
const allCliArgsForExt = /** @type {StringRecord|undefined} */ (this.args.driver?.[extName]);
|
|
129
|
+
if (!lodash_1.default.isEmpty(allCliArgsForExt)) {
|
|
130
|
+
const defaults = (0, schema_1.getDefaultsForExtension)(constants_1.DRIVER_TYPE, extName);
|
|
131
|
+
const cliArgs = lodash_1.default.isEmpty(defaults)
|
|
132
|
+
? allCliArgsForExt
|
|
133
|
+
: lodash_1.default.omitBy(allCliArgsForExt, (value, key) => lodash_1.default.isEqual(defaults[key], value));
|
|
134
|
+
if (!lodash_1.default.isEmpty(cliArgs)) {
|
|
135
|
+
return cliArgs;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Create a new session
|
|
141
|
+
* @param {W3CAppiumDriverCaps} jsonwpCaps JSONWP formatted desired capabilities
|
|
142
|
+
* @param {W3CAppiumDriverCaps} [reqCaps] Required capabilities (JSONWP standard)
|
|
143
|
+
* @param {W3CAppiumDriverCaps} [w3cCapabilities] W3C capabilities
|
|
144
|
+
* @returns {Promise<SessionHandlerCreateResult>}
|
|
145
|
+
*/
|
|
146
|
+
async createSession(jsonwpCaps, reqCaps, w3cCapabilities) {
|
|
147
|
+
const defaultCapabilities = lodash_1.default.cloneDeep(this.args.defaultCapabilities);
|
|
148
|
+
const defaultSettings = (0, utils_1.pullSettings)(defaultCapabilities);
|
|
149
|
+
jsonwpCaps = lodash_1.default.cloneDeep(jsonwpCaps);
|
|
150
|
+
const jwpSettings = { ...defaultSettings, ...(0, utils_1.pullSettings)(jsonwpCaps) };
|
|
151
|
+
w3cCapabilities = lodash_1.default.cloneDeep(w3cCapabilities);
|
|
152
|
+
// It is possible that the client only provides caps using JSONWP standard,
|
|
153
|
+
// although firstMatch/alwaysMatch properties are still present.
|
|
154
|
+
// In such case we assume the client understands W3C protocol and merge the given
|
|
155
|
+
// JSONWP caps to W3C caps
|
|
156
|
+
const w3cSettings = {
|
|
157
|
+
...jwpSettings,
|
|
158
|
+
...(0, utils_1.pullSettings)((w3cCapabilities ?? {}).alwaysMatch ?? {}),
|
|
159
|
+
};
|
|
160
|
+
for (const firstMatchEntry of (w3cCapabilities ?? {}).firstMatch ?? []) {
|
|
161
|
+
Object.assign(w3cSettings, (0, utils_1.pullSettings)(firstMatchEntry));
|
|
162
|
+
}
|
|
163
|
+
/** @type {string|undefined} */
|
|
164
|
+
let protocol;
|
|
165
|
+
let innerSessionId, dCaps;
|
|
166
|
+
try {
|
|
167
|
+
// Parse the caps into a format that the InnerDriver will accept
|
|
168
|
+
const parsedCaps = (0, utils_1.parseCapsForInnerDriver)(jsonwpCaps, (0, base_driver_1.promoteAppiumOptions)(/** @type {W3CAppiumDriverCaps} */ (w3cCapabilities)), this.desiredCapConstraints, defaultCapabilities ? (0, base_driver_1.promoteAppiumOptionsForObject)(defaultCapabilities) : undefined);
|
|
169
|
+
const { desiredCaps, processedJsonwpCapabilities, processedW3CCapabilities } =
|
|
170
|
+
/** @type {import('./utils').ParsedDriverCaps<AppiumDriverConstraints>} */ (parsedCaps);
|
|
171
|
+
protocol = parsedCaps.protocol;
|
|
172
|
+
const error = /** @type {import('./utils').InvalidCaps<AppiumDriverConstraints>} */ (parsedCaps).error;
|
|
173
|
+
// If the parsing of the caps produced an error, throw it in here
|
|
174
|
+
if (error) {
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
const { driver: InnerDriver, version: driverVersion, driverName, } = this.driverConfig.findMatchingDriver(desiredCaps);
|
|
178
|
+
this.printNewSessionAnnouncement(InnerDriver.name, driverVersion, InnerDriver.baseVersion);
|
|
179
|
+
if (this.args.sessionOverride) {
|
|
180
|
+
await this.deleteAllSessions();
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* @type {DriverData[]}
|
|
184
|
+
*/
|
|
185
|
+
let runningDriversData = [];
|
|
186
|
+
/**
|
|
187
|
+
* @type {DriverData[]}
|
|
188
|
+
*/
|
|
189
|
+
let otherPendingDriversData = [];
|
|
190
|
+
const driverInstance = /** @type {ExternalDriver} */ (new InnerDriver(this.args, true));
|
|
191
|
+
// We want to assign security values directly on the driver. The driver
|
|
192
|
+
// should not read security values from `this.opts` because those values
|
|
193
|
+
// could have been set by a malicious user via capabilities, whereas we
|
|
194
|
+
// want a guarantee the values were set by the appium server admin
|
|
195
|
+
if (this.args.relaxedSecurityEnabled) {
|
|
196
|
+
this.log.info(`Applying relaxed security to '${InnerDriver.name}' as per ` +
|
|
197
|
+
`server command line argument. All insecure features will be ` +
|
|
198
|
+
`enabled unless explicitly disabled by --deny-insecure`);
|
|
199
|
+
driverInstance.relaxedSecurityEnabled = true;
|
|
200
|
+
}
|
|
201
|
+
if (!lodash_1.default.isEmpty(this.args.denyInsecure)) {
|
|
202
|
+
this.log.info('Explicitly preventing use of insecure features:');
|
|
203
|
+
this.args.denyInsecure.map((a) => this.log.info(` ${a}`));
|
|
204
|
+
driverInstance.denyInsecure = this.args.denyInsecure;
|
|
205
|
+
}
|
|
206
|
+
if (!lodash_1.default.isEmpty(this.args.allowInsecure)) {
|
|
207
|
+
this.log.info('Explicitly enabling use of insecure features:');
|
|
208
|
+
this.args.allowInsecure.map((a) => this.log.info(` ${a}`));
|
|
209
|
+
driverInstance.allowInsecure = this.args.allowInsecure;
|
|
210
|
+
}
|
|
211
|
+
// Likewise, any driver-specific CLI args that were passed in should be assigned directly to
|
|
212
|
+
// the driver so that they cannot be mimicked by a malicious user sending in capabilities
|
|
213
|
+
const cliArgs = this.getCliArgsForDriver(driverName);
|
|
214
|
+
if (!lodash_1.default.isUndefined(cliArgs)) {
|
|
215
|
+
driverInstance.cliArgs = cliArgs;
|
|
216
|
+
}
|
|
217
|
+
// This assignment is required for correct web sockets functionality inside the driver
|
|
218
|
+
// Drivers/plugins might also want to know where they are hosted
|
|
219
|
+
// XXX: temporary hack to work around #16747
|
|
220
|
+
driverInstance.server = this.server;
|
|
221
|
+
driverInstance.serverHost = this.args.address;
|
|
222
|
+
driverInstance.serverPort = this.args.port;
|
|
223
|
+
driverInstance.serverPath = this.args.basePath;
|
|
224
|
+
try {
|
|
225
|
+
runningDriversData = (await this.curSessionDataForDriver(InnerDriver)) ?? [];
|
|
226
|
+
}
|
|
227
|
+
catch (e) {
|
|
228
|
+
throw new base_driver_1.errors.SessionNotCreatedError(e.message);
|
|
229
|
+
}
|
|
230
|
+
await pendingDriversGuard.acquire(AppiumDriver.name, () => {
|
|
231
|
+
this.pendingDrivers[InnerDriver.name] = this.pendingDrivers[InnerDriver.name] || [];
|
|
232
|
+
otherPendingDriversData = lodash_1.default.compact(this.pendingDrivers[InnerDriver.name].map((drv) => drv.driverData));
|
|
233
|
+
this.pendingDrivers[InnerDriver.name].push(driverInstance);
|
|
234
|
+
});
|
|
235
|
+
try {
|
|
236
|
+
[innerSessionId, dCaps] = await driverInstance.createSession(processedJsonwpCapabilities, reqCaps, processedW3CCapabilities, [...runningDriversData, ...otherPendingDriversData]);
|
|
237
|
+
protocol = driverInstance.protocol;
|
|
238
|
+
this.sessions[innerSessionId] = driverInstance;
|
|
239
|
+
}
|
|
240
|
+
finally {
|
|
241
|
+
await pendingDriversGuard.acquire(AppiumDriver.name, () => {
|
|
242
|
+
lodash_1.default.pull(this.pendingDrivers[InnerDriver.name], driverInstance);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
this.attachUnexpectedShutdownHandler(driverInstance, innerSessionId);
|
|
246
|
+
this.log.info(`New ${InnerDriver.name} session created successfully, session ` +
|
|
247
|
+
`${innerSessionId} added to master session list`);
|
|
248
|
+
// set the New Command Timeout for the inner driver
|
|
249
|
+
driverInstance.startNewCommandTimeout();
|
|
250
|
+
// apply initial values to Appium settings (if provided)
|
|
251
|
+
if (driverInstance.isW3CProtocol() && !lodash_1.default.isEmpty(w3cSettings)) {
|
|
252
|
+
this.log.info(`Applying the initial values to Appium settings parsed from W3C caps: ` +
|
|
253
|
+
JSON.stringify(w3cSettings));
|
|
254
|
+
await driverInstance.updateSettings(w3cSettings);
|
|
255
|
+
}
|
|
256
|
+
else if (driverInstance.isMjsonwpProtocol() && !lodash_1.default.isEmpty(jwpSettings)) {
|
|
257
|
+
this.log.info(`Applying the initial values to Appium settings parsed from MJSONWP caps: ` +
|
|
258
|
+
JSON.stringify(jwpSettings));
|
|
259
|
+
await driverInstance.updateSettings(jwpSettings);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
return {
|
|
264
|
+
protocol,
|
|
265
|
+
error,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
protocol,
|
|
270
|
+
value: [innerSessionId, dCaps, protocol],
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
*
|
|
275
|
+
* @param {ExternalDriver} driver
|
|
276
|
+
* @param {string} innerSessionId
|
|
277
|
+
*/
|
|
278
|
+
attachUnexpectedShutdownHandler(driver, innerSessionId) {
|
|
279
|
+
const onShutdown = (cause = new Error('Unknown error')) => {
|
|
280
|
+
this.log.warn(`Ending session, cause was '${cause.message}'`);
|
|
281
|
+
if (this.sessionPlugins[innerSessionId]) {
|
|
282
|
+
for (const plugin of this.sessionPlugins[innerSessionId]) {
|
|
283
|
+
if (lodash_1.default.isFunction(plugin.onUnexpectedShutdown)) {
|
|
284
|
+
this.log.debug(`Plugin ${plugin.name} defines an unexpected shutdown handler; calling it now`);
|
|
285
|
+
try {
|
|
286
|
+
plugin.onUnexpectedShutdown(driver, cause);
|
|
287
|
+
}
|
|
288
|
+
catch (e) {
|
|
289
|
+
this.log.warn(`Got an error when running plugin ${plugin.name} shutdown handler: ${e}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
this.log.debug(`Plugin ${plugin.name} does not define an unexpected shutdown handler`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
this.log.info(`Removing session '${innerSessionId}' from our master session list`);
|
|
298
|
+
delete this.sessions[innerSessionId];
|
|
299
|
+
delete this.sessionPlugins[innerSessionId];
|
|
300
|
+
};
|
|
301
|
+
if (lodash_1.default.isFunction(driver.onUnexpectedShutdown)) {
|
|
302
|
+
driver.onUnexpectedShutdown(onShutdown);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
this.log.warn(`Failed to attach the unexpected shutdown listener. ` +
|
|
306
|
+
`Is 'onUnexpectedShutdown' method available for '${driver.constructor.name}'?`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
*
|
|
311
|
+
* @param {((...args: any[]) => any)|(new(...args: any[]) => any)} InnerDriver
|
|
312
|
+
* @returns {Promise<DriverData[]>}}
|
|
313
|
+
* @privateRemarks The _intent_ is that `InnerDriver` is the class of a driver, but it only really
|
|
314
|
+
* needs to be a function or constructor.
|
|
315
|
+
*/
|
|
316
|
+
async curSessionDataForDriver(InnerDriver) {
|
|
317
|
+
const data = lodash_1.default.compact(lodash_1.default.values(this.sessions)
|
|
318
|
+
.filter((s) => s.constructor.name === InnerDriver.name)
|
|
319
|
+
.map((s) => s.driverData));
|
|
320
|
+
for (const datum of data) {
|
|
321
|
+
if (!datum) {
|
|
322
|
+
throw new Error(`Problem getting session data for driver type ` +
|
|
323
|
+
`${InnerDriver.name}; does it implement 'get driverData'?`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return data;
|
|
207
327
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
328
|
+
/**
|
|
329
|
+
* @param {string} sessionId
|
|
330
|
+
*/
|
|
331
|
+
async deleteSession(sessionId) {
|
|
332
|
+
let protocol;
|
|
333
|
+
try {
|
|
334
|
+
let otherSessionsData;
|
|
335
|
+
const dstSession = await sessionsListGuard.acquire(AppiumDriver.name, () => {
|
|
336
|
+
if (!this.sessions[sessionId]) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
const curConstructorName = this.sessions[sessionId].constructor.name;
|
|
340
|
+
otherSessionsData = lodash_1.default.toPairs(this.sessions)
|
|
341
|
+
.filter(([key, value]) => value.constructor.name === curConstructorName && key !== sessionId)
|
|
342
|
+
.map(([, value]) => value.driverData);
|
|
343
|
+
const dstSession = this.sessions[sessionId];
|
|
344
|
+
protocol = dstSession.protocol;
|
|
345
|
+
this.log.info(`Removing session ${sessionId} from our master session list`);
|
|
346
|
+
// regardless of whether the deleteSession completes successfully or not
|
|
347
|
+
// make the session unavailable, because who knows what state it might
|
|
348
|
+
// be in otherwise
|
|
349
|
+
delete this.sessions[sessionId];
|
|
350
|
+
delete this.sessionPlugins[sessionId];
|
|
351
|
+
return dstSession;
|
|
352
|
+
});
|
|
353
|
+
// this may not be correct, but if `dstSession` was falsy, the call to `deleteSession()` would
|
|
354
|
+
// throw anyway.
|
|
355
|
+
if (!dstSession) {
|
|
356
|
+
throw new Error('Session not found');
|
|
357
|
+
}
|
|
358
|
+
return {
|
|
359
|
+
protocol,
|
|
360
|
+
value: await dstSession.deleteSession(sessionId, otherSessionsData),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
catch (e) {
|
|
364
|
+
this.log.error(`Had trouble ending session ${sessionId}: ${e.message}`);
|
|
365
|
+
return {
|
|
366
|
+
protocol,
|
|
367
|
+
error: e,
|
|
368
|
+
};
|
|
230
369
|
}
|
|
231
|
-
});
|
|
232
|
-
} else if (_lodash.default.isFunction(driver.onUnexpectedShutdown)) {
|
|
233
|
-
driver.onUnexpectedShutdown(removeSessionFromMasterList);
|
|
234
|
-
} else {
|
|
235
|
-
_logger.default.warn(`Failed to attach the unexpected shutdown listener. ` + `Is 'onUnexpectedShutdown' method available for '${driver.constructor.name}'?`);
|
|
236
370
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
371
|
+
async deleteAllSessions(opts = {}) {
|
|
372
|
+
const sessionsCount = lodash_1.default.size(this.sessions);
|
|
373
|
+
if (0 === sessionsCount) {
|
|
374
|
+
this.log.debug('There are no active sessions for cleanup');
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const { force = false, reason } = opts;
|
|
378
|
+
this.log.debug(`Cleaning up ${support_1.util.pluralize('active session', sessionsCount, true)}`);
|
|
379
|
+
const cleanupPromises = force
|
|
380
|
+
? lodash_1.default.values(this.sessions).map((drv) => drv.startUnexpectedShutdown(reason && new Error(reason)))
|
|
381
|
+
: lodash_1.default.keys(this.sessions).map((id) => this.deleteSession(id));
|
|
382
|
+
for (const cleanupPromise of cleanupPromises) {
|
|
383
|
+
try {
|
|
384
|
+
await cleanupPromise;
|
|
385
|
+
}
|
|
386
|
+
catch (e) {
|
|
387
|
+
this.log.debug(e);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
248
390
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
391
|
+
/**
|
|
392
|
+
* Get the appropriate plugins for a session (or sessionless plugins)
|
|
393
|
+
*
|
|
394
|
+
* @param {?string} sessionId - the sessionId (or null) to use to find plugins
|
|
395
|
+
* @returns {Array} - array of plugin instances
|
|
396
|
+
*/
|
|
397
|
+
pluginsForSession(sessionId = null) {
|
|
398
|
+
if (sessionId) {
|
|
399
|
+
if (!this.sessionPlugins[sessionId]) {
|
|
400
|
+
this.sessionPlugins[sessionId] = this.createPluginInstances();
|
|
401
|
+
}
|
|
402
|
+
return this.sessionPlugins[sessionId];
|
|
403
|
+
}
|
|
404
|
+
if (lodash_1.default.isEmpty(this.sessionlessPlugins)) {
|
|
405
|
+
this.sessionlessPlugins = this.createPluginInstances();
|
|
262
406
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return {
|
|
281
|
-
protocol,
|
|
282
|
-
error: e
|
|
283
|
-
};
|
|
407
|
+
return this.sessionlessPlugins;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* To get plugins for a command, we either get the plugin instances associated with the
|
|
411
|
+
* particular command's session, or in the case of sessionless plugins, pull from the set of
|
|
412
|
+
* plugin instances reserved for sessionless commands (and we lazily create plugin instances on
|
|
413
|
+
* first use)
|
|
414
|
+
*
|
|
415
|
+
* @param {string} cmd - the name of the command to find a plugin to handle
|
|
416
|
+
* @param {?string} sessionId - the particular session for which to find a plugin, or null if
|
|
417
|
+
* sessionless
|
|
418
|
+
*/
|
|
419
|
+
pluginsToHandleCmd(cmd, sessionId = null) {
|
|
420
|
+
// to handle a given command, a plugin should either implement that command as a plugin
|
|
421
|
+
// instance method or it should implement a generic 'handle' method
|
|
422
|
+
return this.pluginsForSession(sessionId).filter((p) => lodash_1.default.isFunction(p[cmd]) || lodash_1.default.isFunction(p.handle));
|
|
284
423
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
424
|
+
/**
|
|
425
|
+
* Creates instances of all of the enabled Plugin classes
|
|
426
|
+
* @returns {Plugin[]}
|
|
427
|
+
*/
|
|
428
|
+
createPluginInstances() {
|
|
429
|
+
/** @type {Plugin[]} */
|
|
430
|
+
const pluginInstances = [];
|
|
431
|
+
for (const [PluginClass, name] of this.pluginClasses.entries()) {
|
|
432
|
+
const cliArgs = this.getCliArgsForPlugin(name);
|
|
433
|
+
const plugin = new PluginClass(name, cliArgs);
|
|
434
|
+
pluginInstances.push(plugin);
|
|
435
|
+
}
|
|
436
|
+
return pluginInstances;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
*
|
|
440
|
+
* @param {string} cmd
|
|
441
|
+
* @param {...any} args
|
|
442
|
+
* @returns {Promise<{value: any, error?: Error, protocol: string} | import('type-fest').AsyncReturnType<ExternalDriver['executeCommand']>>}
|
|
443
|
+
*/
|
|
444
|
+
async executeCommand(cmd, ...args) {
|
|
445
|
+
// We have basically three cases for how to handle commands:
|
|
446
|
+
// 1. handle getStatus (we do this as a special out of band case so it doesn't get added to an
|
|
447
|
+
// execution queue, and can be called while e.g. createSession is in progress)
|
|
448
|
+
// 2. handle commands that this umbrella driver should handle, rather than the actual session
|
|
449
|
+
// driver (for example, deleteSession, or other non-session commands)
|
|
450
|
+
// 3. handle session driver commands.
|
|
451
|
+
// The tricky part is that because we support command plugins, we need to wrap any of these
|
|
452
|
+
// cases with plugin handling.
|
|
453
|
+
const isGetStatus = cmd === base_driver_1.GET_STATUS_COMMAND;
|
|
454
|
+
const isUmbrellaCmd = isAppiumDriverCommand(cmd);
|
|
455
|
+
const isSessionCmd = (0, base_driver_1.isSessionCommand)(cmd);
|
|
456
|
+
// if a plugin override proxying for this command and that is why we are here instead of just
|
|
457
|
+
// letting the protocol proxy the command entirely, determine that, get the request object for
|
|
458
|
+
// use later on, then clean up the args
|
|
459
|
+
const reqForProxy = lodash_1.default.last(args)?.reqForProxy;
|
|
460
|
+
if (reqForProxy) {
|
|
461
|
+
args.pop();
|
|
462
|
+
}
|
|
463
|
+
// first do some error checking. If we're requesting a session command execution, then make
|
|
464
|
+
// sure that session actually exists on the session driver, and set the session driver itself
|
|
465
|
+
let sessionId = null;
|
|
466
|
+
let dstSession = null;
|
|
467
|
+
let protocol = null;
|
|
468
|
+
/** @type {this | ExternalDriver} */
|
|
469
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
470
|
+
let driver = this;
|
|
471
|
+
if (isSessionCmd) {
|
|
472
|
+
sessionId = lodash_1.default.last(args);
|
|
473
|
+
dstSession = this.sessions[sessionId];
|
|
474
|
+
if (!dstSession) {
|
|
475
|
+
throw new Error(`The session with id '${sessionId}' does not exist`);
|
|
476
|
+
}
|
|
477
|
+
// now save the response protocol given that the session driver's protocol might differ
|
|
478
|
+
protocol = dstSession.protocol;
|
|
479
|
+
if (!isUmbrellaCmd) {
|
|
480
|
+
driver = dstSession;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
// get any plugins which are registered as handling this command
|
|
484
|
+
const plugins = this.pluginsToHandleCmd(cmd, sessionId);
|
|
485
|
+
// now we define a 'cmdHandledBy' object which will keep track of which plugins have handled this
|
|
486
|
+
// command. we care about this because (a) multiple plugins can handle the same command, and
|
|
487
|
+
// (b) there's no guarantee that a plugin will actually call the next() method which runs the
|
|
488
|
+
// original command execution. This results in a situation where the command might be handled
|
|
489
|
+
// by some but not all plugins, or by plugin(s) but not by the default behavior. So start out
|
|
490
|
+
// this object declaring that the default handler has not been executed.
|
|
491
|
+
const cmdHandledBy = { default: false };
|
|
492
|
+
// now we define an async function which will be passed to plugins, and successively wrapped
|
|
493
|
+
// if there is more than one plugin that can handle the command. To start off with, the async
|
|
494
|
+
// function is defined as calling the default behavior, i.e., whichever of the 3 cases above is
|
|
495
|
+
// the appropriate one
|
|
496
|
+
const defaultBehavior = async () => {
|
|
497
|
+
// if we're running with plugins, make sure we log that the default behavior is actually
|
|
498
|
+
// happening so we can tell when the plugin call chain is unwrapping to the default behavior
|
|
499
|
+
// if that's what happens
|
|
500
|
+
plugins.length && this.log.info(`Executing default handling behavior for command '${cmd}'`);
|
|
501
|
+
// if we make it here, we know that the default behavior is handled
|
|
502
|
+
cmdHandledBy.default = true;
|
|
503
|
+
if (reqForProxy) {
|
|
504
|
+
// we would have proxied this command had a plugin not handled it, so the default behavior
|
|
505
|
+
// is to do the proxy and retrieve the result internally so it can be passed to the plugin
|
|
506
|
+
// in case it calls 'await next()'. This requires that the driver have defined
|
|
507
|
+
// 'proxyCommand' and not just 'proxyReqRes'.
|
|
508
|
+
if (!dstSession.proxyCommand) {
|
|
509
|
+
throw new NoDriverProxyCommandError();
|
|
510
|
+
}
|
|
511
|
+
return await dstSession.proxyCommand(reqForProxy.originalUrl, reqForProxy.method, reqForProxy.body);
|
|
512
|
+
}
|
|
513
|
+
if (isGetStatus) {
|
|
514
|
+
return await this.getStatus();
|
|
515
|
+
}
|
|
516
|
+
if (isUmbrellaCmd) {
|
|
517
|
+
// some commands, like deleteSession, we want to make sure to handle on *this* driver,
|
|
518
|
+
// not the platform driver
|
|
519
|
+
return await base_driver_1.BaseDriver.prototype.executeCommand.call(this, cmd, ...args);
|
|
520
|
+
}
|
|
521
|
+
// here we know that we are executing a session command, and have a valid session driver
|
|
522
|
+
return await dstSession.executeCommand(cmd, ...args);
|
|
523
|
+
};
|
|
524
|
+
// now take our default behavior, wrap it with any number of plugin behaviors, and run it
|
|
525
|
+
const wrappedCmd = this.wrapCommandWithPlugins({
|
|
526
|
+
driver,
|
|
527
|
+
cmd,
|
|
528
|
+
args,
|
|
529
|
+
plugins,
|
|
530
|
+
cmdHandledBy,
|
|
531
|
+
next: defaultBehavior,
|
|
532
|
+
});
|
|
533
|
+
const res = await this.executeWrappedCommand({ wrappedCmd, protocol });
|
|
534
|
+
// if we had plugins, make sure to log out the helpful report about which plugins ended up
|
|
535
|
+
// handling the command and which didn't
|
|
536
|
+
this.logPluginHandlerReport(plugins, { cmd, cmdHandledBy });
|
|
537
|
+
// And finally, if the command was createSession, we want to migrate any plugins which were
|
|
538
|
+
// previously sessionless to use the new sessionId, so that plugins can share state between
|
|
539
|
+
// their createSession method and other instance methods
|
|
540
|
+
if (cmd === base_driver_1.CREATE_SESSION_COMMAND && this.sessionlessPlugins.length && !res.error) {
|
|
541
|
+
const sessionId = lodash_1.default.first(res.value);
|
|
542
|
+
this.log.info(`Promoting ${this.sessionlessPlugins.length} sessionless plugins to be attached ` +
|
|
543
|
+
`to session ID ${sessionId}`);
|
|
544
|
+
this.sessionPlugins[sessionId] = this.sessionlessPlugins;
|
|
545
|
+
this.sessionlessPlugins = [];
|
|
546
|
+
}
|
|
547
|
+
return res;
|
|
294
548
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
549
|
+
wrapCommandWithPlugins({ driver, cmd, args, next, cmdHandledBy, plugins }) {
|
|
550
|
+
plugins.length &&
|
|
551
|
+
this.log.info(`Plugins which can handle cmd '${cmd}': ${plugins.map((p) => p.name)}`);
|
|
552
|
+
// now we can go through each plugin and wrap `next` around its own handler, passing the *old*
|
|
553
|
+
// next in so that it can call it if it wants to
|
|
554
|
+
for (const plugin of plugins) {
|
|
555
|
+
// need an IIFE here because we want the value of next that's passed to plugin.handle to be
|
|
556
|
+
// exactly the value of next here before reassignment; we don't want it to be lazily
|
|
557
|
+
// evaluated, otherwise we end up with infinite recursion of the last `next` to be defined.
|
|
558
|
+
cmdHandledBy[plugin.name] = false; // we see a new plugin, so add it to the 'cmdHandledBy' object
|
|
559
|
+
next = ((_next) => async () => {
|
|
560
|
+
this.log.info(`Plugin ${plugin.name} is now handling cmd '${cmd}'`);
|
|
561
|
+
cmdHandledBy[plugin.name] = true; // if we make it here, this plugin has attempted to handle cmd
|
|
562
|
+
// first attempt to handle the command via a command-specific handler on the plugin
|
|
563
|
+
if (plugin[cmd]) {
|
|
564
|
+
return await plugin[cmd](_next, driver, ...args);
|
|
565
|
+
}
|
|
566
|
+
// otherwise, call the generic 'handle' method
|
|
567
|
+
return await plugin.handle(_next, driver, cmd, ...args);
|
|
568
|
+
})(next);
|
|
569
|
+
}
|
|
570
|
+
return next;
|
|
311
571
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
dstSession = await sessionsListGuard.acquire(AppiumDriver.name, () => this.sessions[sessionId]);
|
|
330
|
-
|
|
331
|
-
if (!dstSession) {
|
|
332
|
-
throw new Error(`The session with id '${sessionId}' does not exist`);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
protocol = dstSession.protocol;
|
|
572
|
+
logPluginHandlerReport(plugins, { cmd, cmdHandledBy }) {
|
|
573
|
+
if (!plugins.length) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
// at the end of the day, we have an object representing which plugins ended up getting
|
|
577
|
+
// their code run as part of handling this command. Because plugins can choose *not* to
|
|
578
|
+
// pass control to other plugins or to the default driver behavior, this is information
|
|
579
|
+
// which is probably useful to the user (especially in situations where plugins might not
|
|
580
|
+
// interact well together, and it would be hard to debug otherwise without this kind of
|
|
581
|
+
// message).
|
|
582
|
+
const didHandle = Object.keys(cmdHandledBy).filter((k) => cmdHandledBy[k]);
|
|
583
|
+
const didntHandle = Object.keys(cmdHandledBy).filter((k) => !cmdHandledBy[k]);
|
|
584
|
+
if (didntHandle.length > 0) {
|
|
585
|
+
this.log.info(`Command '${cmd}' was *not* handled by the following behaviours or plugins, even ` +
|
|
586
|
+
`though they were registered to handle it: ${JSON.stringify(didntHandle)}. The ` +
|
|
587
|
+
`command *was* handled by these: ${JSON.stringify(didHandle)}.`);
|
|
588
|
+
}
|
|
336
589
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
plugins,
|
|
361
|
-
cmdHandledBy,
|
|
362
|
-
next: defaultBehavior
|
|
363
|
-
});
|
|
364
|
-
const res = await this.executeWrappedCommand({
|
|
365
|
-
wrappedCmd,
|
|
366
|
-
protocol
|
|
367
|
-
});
|
|
368
|
-
plugins.length && this.logPluginHandlerReport({
|
|
369
|
-
cmd,
|
|
370
|
-
cmdHandledBy
|
|
371
|
-
});
|
|
372
|
-
return res;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
wrapCommandWithPlugins({
|
|
376
|
-
cmd,
|
|
377
|
-
args,
|
|
378
|
-
next,
|
|
379
|
-
cmdHandledBy,
|
|
380
|
-
plugins
|
|
381
|
-
}) {
|
|
382
|
-
plugins.length && _logger.default.info(`Plugins which can handle cmd '${cmd}': ${plugins.map(p => p.name)}`);
|
|
383
|
-
|
|
384
|
-
for (const plugin of plugins) {
|
|
385
|
-
cmdHandledBy[plugin.name] = false;
|
|
386
|
-
|
|
387
|
-
next = (_next => async () => {
|
|
388
|
-
_logger.default.info(`Plugin ${plugin.name} is now handling cmd '${cmd}'`);
|
|
389
|
-
|
|
390
|
-
cmdHandledBy[plugin.name] = true;
|
|
391
|
-
return await plugin.handle(_next, this, cmd, ...args);
|
|
392
|
-
})(next);
|
|
590
|
+
async executeWrappedCommand({ wrappedCmd, protocol }) {
|
|
591
|
+
let cmdRes, cmdErr, res = {};
|
|
592
|
+
try {
|
|
593
|
+
// At this point, `wrappedCmd` defines a whole sequence of plugin handlers, culminating in
|
|
594
|
+
// our default handler. Whatever it returns is what we're going to want to send back to the
|
|
595
|
+
// user.
|
|
596
|
+
cmdRes = await wrappedCmd();
|
|
597
|
+
}
|
|
598
|
+
catch (e) {
|
|
599
|
+
cmdErr = e;
|
|
600
|
+
}
|
|
601
|
+
// Sadly, we don't know exactly what kind of object will be returned. It will either be a bare
|
|
602
|
+
// object, or a protocol-aware object with protocol and error/value keys. So we need to sniff
|
|
603
|
+
// it and make sure we don't double-wrap it if it's the latter kind.
|
|
604
|
+
if (lodash_1.default.isPlainObject(cmdRes) && lodash_1.default.has(cmdRes, 'protocol')) {
|
|
605
|
+
res = cmdRes;
|
|
606
|
+
}
|
|
607
|
+
else {
|
|
608
|
+
res.value = cmdRes;
|
|
609
|
+
res.error = cmdErr;
|
|
610
|
+
res.protocol = protocol;
|
|
611
|
+
}
|
|
612
|
+
return res;
|
|
393
613
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
logPluginHandlerReport({
|
|
399
|
-
cmd,
|
|
400
|
-
cmdHandledBy
|
|
401
|
-
}) {
|
|
402
|
-
const didHandle = Object.keys(cmdHandledBy).filter(k => cmdHandledBy[k]);
|
|
403
|
-
const didntHandle = Object.keys(cmdHandledBy).filter(k => !cmdHandledBy[k]);
|
|
404
|
-
|
|
405
|
-
if (didntHandle.length > 0) {
|
|
406
|
-
_logger.default.info(`Command '${cmd}' was not handled by the following beahviors or plugins, even ` + `though they were registered to handle it: ${JSON.stringify(didntHandle)}. The ` + `command *was* handled by these: ${JSON.stringify(didHandle)}.`);
|
|
614
|
+
proxyActive(sessionId) {
|
|
615
|
+
const dstSession = this.sessions[sessionId];
|
|
616
|
+
return dstSession && lodash_1.default.isFunction(dstSession.proxyActive) && dstSession.proxyActive(sessionId);
|
|
407
617
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
res = {};
|
|
417
|
-
|
|
418
|
-
try {
|
|
419
|
-
cmdRes = await wrappedCmd();
|
|
420
|
-
} catch (e) {
|
|
421
|
-
cmdErr = e;
|
|
618
|
+
/**
|
|
619
|
+
*
|
|
620
|
+
* @param {string} sessionId
|
|
621
|
+
* @returns {import('@appium/types').RouteMatcher[]}
|
|
622
|
+
*/
|
|
623
|
+
getProxyAvoidList(sessionId) {
|
|
624
|
+
const dstSession = this.sessions[sessionId];
|
|
625
|
+
return dstSession ? dstSession.getProxyAvoidList() : [];
|
|
422
626
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
} else {
|
|
427
|
-
res.value = cmdRes;
|
|
428
|
-
res.error = cmdErr;
|
|
429
|
-
res.protocol = protocol;
|
|
627
|
+
canProxy(sessionId) {
|
|
628
|
+
const dstSession = this.sessions[sessionId];
|
|
629
|
+
return dstSession && dstSession.canProxy(sessionId);
|
|
430
630
|
}
|
|
431
|
-
|
|
432
|
-
return res;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
proxyActive(sessionId) {
|
|
436
|
-
const dstSession = this.sessions[sessionId];
|
|
437
|
-
return dstSession && _lodash.default.isFunction(dstSession.proxyActive) && dstSession.proxyActive(sessionId);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
getProxyAvoidList(sessionId) {
|
|
441
|
-
const dstSession = this.sessions[sessionId];
|
|
442
|
-
return dstSession ? dstSession.getProxyAvoidList() : [];
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
canProxy(sessionId) {
|
|
446
|
-
const dstSession = this.sessions[sessionId];
|
|
447
|
-
return dstSession && dstSession.canProxy(sessionId);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
631
|
}
|
|
451
|
-
|
|
452
632
|
exports.AppiumDriver = AppiumDriver;
|
|
453
|
-
|
|
633
|
+
// help decide which commands should be proxied to sub-drivers and which
|
|
634
|
+
// should be handled by this, our umbrella driver
|
|
454
635
|
function isAppiumDriverCommand(cmd) {
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi9hcHBpdW0uanMiXSwibmFtZXMiOlsiZGVzaXJlZENhcGFiaWxpdHlDb25zdHJhaW50cyIsImF1dG9tYXRpb25OYW1lIiwicHJlc2VuY2UiLCJpc1N0cmluZyIsInBsYXRmb3JtTmFtZSIsInNlc3Npb25zTGlzdEd1YXJkIiwiQXN5bmNMb2NrIiwicGVuZGluZ0RyaXZlcnNHdWFyZCIsIkFwcGl1bURyaXZlciIsIkJhc2VEcml2ZXIiLCJjb25zdHJ1Y3RvciIsImFyZ3MiLCJ0bXBEaXIiLCJwcm9jZXNzIiwiZW52IiwiQVBQSVVNX1RNUF9ESVIiLCJkZXNpcmVkQ2FwQ29uc3RyYWludHMiLCJuZXdDb21tYW5kVGltZW91dE1zIiwiT2JqZWN0IiwiYXNzaWduIiwic2Vzc2lvbnMiLCJwZW5kaW5nRHJpdmVycyIsInBsdWdpbnMiLCJpc0NvbW1hbmRzUXVldWVFbmFibGVkIiwic2Vzc2lvbkV4aXN0cyIsInNlc3Npb25JZCIsImRzdFNlc3Npb24iLCJkcml2ZXJGb3JTZXNzaW9uIiwiZ2V0U3RhdHVzIiwiYnVpbGQiLCJfIiwiY2xvbmUiLCJnZXRTZXNzaW9ucyIsImFjcXVpcmUiLCJuYW1lIiwidG9QYWlycyIsIm1hcCIsImlkIiwiZHJpdmVyIiwiY2FwYWJpbGl0aWVzIiwiY2FwcyIsInByaW50TmV3U2Vzc2lvbkFubm91bmNlbWVudCIsImRyaXZlck5hbWUiLCJkcml2ZXJWZXJzaW9uIiwiaW50cm9TdHJpbmciLCJBUFBJVU1fVkVSIiwibG9nIiwiaW5mbyIsIl9maW5kTWF0Y2hpbmdEcml2ZXIiLCJjcmVhdGVTZXNzaW9uIiwianNvbndwQ2FwcyIsInJlcUNhcHMiLCJ3M2NDYXBhYmlsaXRpZXMiLCJkZWZhdWx0Q2FwYWJpbGl0aWVzIiwiY2xvbmVEZWVwIiwiZGVmYXVsdFNldHRpbmdzIiwiandwU2V0dGluZ3MiLCJ3M2NTZXR0aW5ncyIsImFsd2F5c01hdGNoIiwiZmlyc3RNYXRjaEVudHJ5IiwiZmlyc3RNYXRjaCIsInByb3RvY29sIiwiaW5uZXJTZXNzaW9uSWQiLCJkQ2FwcyIsInBhcnNlZENhcHMiLCJkZXNpcmVkQ2FwcyIsInByb2Nlc3NlZEpzb253cENhcGFiaWxpdGllcyIsInByb2Nlc3NlZFczQ0NhcGFiaWxpdGllcyIsImVycm9yIiwiSW5uZXJEcml2ZXIiLCJ2ZXJzaW9uIiwiZHJpdmVyQ29uZmlnIiwic2Vzc2lvbk92ZXJyaWRlIiwiZGVsZXRlQWxsU2Vzc2lvbnMiLCJydW5uaW5nRHJpdmVyc0RhdGEiLCJvdGhlclBlbmRpbmdEcml2ZXJzRGF0YSIsImQiLCJyZWxheGVkU2VjdXJpdHlFbmFibGVkIiwiaXNFbXB0eSIsImRlbnlJbnNlY3VyZSIsImEiLCJhbGxvd0luc2VjdXJlIiwic2VydmVyIiwiY3VyU2Vzc2lvbkRhdGFGb3JEcml2ZXIiLCJlIiwiZXJyb3JzIiwiU2Vzc2lvbk5vdENyZWF0ZWRFcnJvciIsIm1lc3NhZ2UiLCJkcnYiLCJkcml2ZXJEYXRhIiwicHVzaCIsInB1bGwiLCJhdHRhY2hVbmV4cGVjdGVkU2h1dGRvd25IYW5kbGVyIiwic3RhcnROZXdDb21tYW5kVGltZW91dCIsImlzVzNDUHJvdG9jb2wiLCJKU09OIiwic3RyaW5naWZ5IiwidXBkYXRlU2V0dGluZ3MiLCJpc01qc29ud3BQcm90b2NvbCIsInZhbHVlIiwicmVtb3ZlU2Vzc2lvbkZyb21NYXN0ZXJMaXN0IiwiY2F1c2UiLCJFcnJvciIsIndhcm4iLCJpc0Z1bmN0aW9uIiwib25VbmV4cGVjdGVkU2h1dGRvd24iLCJ0aGVuIiwiY2F0Y2giLCJCIiwiQ2FuY2VsbGF0aW9uRXJyb3IiLCJkYXRhIiwidmFsdWVzIiwiZmlsdGVyIiwicyIsImRhdHVtIiwiZGVsZXRlU2Vzc2lvbiIsIm90aGVyU2Vzc2lvbnNEYXRhIiwiY3VyQ29uc3RydWN0b3JOYW1lIiwia2V5Iiwib3B0cyIsInNlc3Npb25zQ291bnQiLCJzaXplIiwiZGVidWciLCJmb3JjZSIsInJlYXNvbiIsInV0aWwiLCJwbHVyYWxpemUiLCJjbGVhbnVwUHJvbWlzZXMiLCJzdGFydFVuZXhwZWN0ZWRTaHV0ZG93biIsImtleXMiLCJjbGVhbnVwUHJvbWlzZSIsInBsdWdpbnNUb0hhbmRsZUNtZCIsImNtZCIsInAiLCJjb21tYW5kcyIsImlzQXJyYXkiLCJpbmNsdWRlcyIsImV4ZWN1dGVDb21tYW5kIiwiaXNHZXRTdGF0dXMiLCJpc1VtYnJlbGxhQ21kIiwiaXNBcHBpdW1Ecml2ZXJDb21tYW5kIiwiaXNTZXNzaW9uQ21kIiwibGFzdCIsImNtZEhhbmRsZWRCeSIsImRlZmF1bHQiLCJkZWZhdWx0QmVoYXZpb3IiLCJsZW5ndGgiLCJ3cmFwcGVkQ21kIiwid3JhcENvbW1hbmRXaXRoUGx1Z2lucyIsIm5leHQiLCJyZXMiLCJleGVjdXRlV3JhcHBlZENvbW1hbmQiLCJsb2dQbHVnaW5IYW5kbGVyUmVwb3J0IiwicGx1Z2luIiwiX25leHQiLCJoYW5kbGUiLCJkaWRIYW5kbGUiLCJrIiwiZGlkbnRIYW5kbGUiLCJjbWRSZXMiLCJjbWRFcnIiLCJpc1BsYWluT2JqZWN0IiwiaGFzIiwicHJveHlBY3RpdmUiLCJnZXRQcm94eUF2b2lkTGlzdCIsImNhblByb3h5Il0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQUFBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUVBLE1BQU1BLDRCQUE0QixHQUFHO0FBQ25DQyxFQUFBQSxjQUFjLEVBQUU7QUFDZEMsSUFBQUEsUUFBUSxFQUFFLElBREk7QUFFZEMsSUFBQUEsUUFBUSxFQUFFO0FBRkksR0FEbUI7QUFLbkNDLEVBQUFBLFlBQVksRUFBRTtBQUNaRixJQUFBQSxRQUFRLEVBQUUsSUFERTtBQUVaQyxJQUFBQSxRQUFRLEVBQUU7QUFGRTtBQUxxQixDQUFyQztBQVdBLE1BQU1FLGlCQUFpQixHQUFHLElBQUlDLGtCQUFKLEVBQTFCO0FBQ0EsTUFBTUMsbUJBQW1CLEdBQUcsSUFBSUQsa0JBQUosRUFBNUI7O0FBRUEsTUFBTUUsWUFBTixTQUEyQkMsNEJBQTNCLENBQXNDO0FBQ3BDQyxFQUFBQSxXQUFXLENBQUVD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LE1BRU87QUFDTFQsTUFBQUEsR0FBRyxDQUFDbkQsS0FBSixHQUFZNEQsTUFBWjtBQUNBVCxNQUFBQSxHQUFHLENBQUNsRixLQUFKLEdBQVk0RixNQUFaO0FBQ0FWLE1BQUFBLEdBQUcsQ0FBQ3pGLFFBQUosR0FBZUEsUUFBZjtBQUNEOztBQUNELFdBQU95RixHQUFQO0FBQ0Q7O0FBR0RhLEVBQUFBLFdBQVcsQ0FBRTFJLFNBQUYsRUFBYTtBQUN0QixVQUFNQyxVQUFVLEdBQUcsS0FBS04sUUFBTCxDQUFjSyxTQUFkLENBQW5CO0FBQ0EsV0FBT0MsVUFBVSxJQUFJSSxnQkFBRTBFLFVBQUYsQ0FBYTlFLFVBQVUsQ0FBQ3lJLFdBQXhCLENBQWQsSUFBc0R6SSxVQUFVLENBQUN5SSxXQUFYLENBQXVCMUksU0FBdkIsQ0FBN0Q7QUFDRDs7QUFFRDJJLEVBQUFBLGlCQUFpQixDQUFFM0ksU0FBRixFQUFhO0FBQzVCLFVBQU1DLFVBQVUsR0FBRyxLQUFLTixRQUFMLENBQWNLLFNBQWQsQ0FBbkI7QUFDQSxXQUFPQyxVQUFVLEdBQUdBLFVBQVUsQ0FBQzBJLGlCQUFYLEVBQUgsR0FBb0MsRUFBckQ7QUFDRDs7QUFFREMsRUFBQUEsUUFBUSxDQUFFNUksU0FBRixFQUFhO0FBQ25CLFVBQU1DLFVBQVUsR0FBRyxLQUFLTixRQUFMLENBQWNLLFNBQWQsQ0FBbkI7QUFDQSxXQUFPQyxVQUFVLElBQUlBLFVBQVUsQ0FBQzJJLFFBQVgsQ0FBb0I1SSxTQUFwQixDQUFyQjtBQUNEOztBQW5lbUM7Ozs7QUF3ZXRDLFNBQVNtSCxxQkFBVCxDQUFnQ1IsR0FBaEMsRUFBcUM7QUFDbkMsU0FBTyxDQUFDLHdDQUFpQkEsR0FBakIsQ0FBRCxJQUEwQkEsR0FBRyxLQUFLLGVBQXpDO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IGxvZyBmcm9tICcuL2xvZ2dlcic7XG5pbXBvcnQgeyBnZXRCdWlsZEluZm8sIHVwZGF0ZUJ1aWxkSW5mbywgQVBQSVVNX1ZFUiB9IGZyb20gJy4vY29uZmlnJztcbmltcG9ydCB7IGZpbmRNYXRjaGluZ0RyaXZlciB9IGZyb20gJy4vZHJpdmVycyc7XG5pbXBvcnQgeyBCYXNlRHJpdmVyLCBlcnJvcnMsIGlzU2Vzc2lvbkNvbW1hbmQgfSBmcm9tICdhcHBpdW0tYmFzZS1kcml2ZXInO1xuaW1wb3J0IEIgZnJvbSAnYmx1ZWJpcmQnO1xuaW1wb3J0IEFzeW5jTG9jayBmcm9tICdhc3luYy1sb2NrJztcbmltcG9ydCB7IHBhcnNlQ2Fwc0ZvcklubmVyRHJpdmVyLCBwdWxsU2V0dGluZ3MgfSBmcm9tICcuL3V0aWxzJztcbmltcG9ydCB7IHV0aWwgfSBmcm9tICdhcHBpdW0tc3VwcG9ydCc7XG5cbmNvbnN0IGRlc2lyZWRDYXBhYmlsaXR5Q29uc3RyYWludHMgPSB7XG4gIGF1dG9tYXRpb25OYW1lOiB7XG4gICAgcHJlc2VuY2U6IHRydWUsXG4gICAgaXNTdHJpbmc6IHRydWUsXG4gIH0sXG4gIHBsYXRmb3JtTmFtZToge1xuICAgIHByZXNlbmNlOiB0cnVlLFxuICAgIGlzU3RyaW5nOiB0cnVlLFxuICB9LFxufTtcblxuY29uc3Qgc2Vzc2lvbnNMaXN0R3VhcmQgPSBuZXcgQXN5bmNMb2NrKCk7XG5jb25zdCBwZW5kaW5nRHJpdmVyc0d1YXJkID0gbmV3IEFzeW5jTG9jaygpO1xuXG5jbGFzcyBBcHBpdW1Ecml2ZXIgZXh0ZW5kcyBCYXNlRHJpdmVyIHtcbiAgY29uc3RydWN0b3IgKGFyZ3MpIHtcbiAgICAvLyBJdCBpcyBuZWNlc3NhcnkgdG8gc2V0IGAtLXRtcGAgaGVyZSBzaW5jZSBpdCBzaG91bGQgYmUgc2V0IHRvXG4gICAgLy8gcHJvY2Vzcy5lbnYuQVBQSVVNX1RNUF9ESVIgb25jZSBhdCBhbiBpbml0aWFsIHBvaW50IGluIHRoZSBBcHBpdW0gbGlmZWN5Y2xlLlxuICAgIC8vIFRoZSBwcm9jZXNzIGFyZ3VtZW50IHdpbGwgYmUgcmVmZXJlbmNlZCBieSBCYXNlRHJpdmVyLlxuICAgIC8vIFBsZWFzZSBjYWxsIGFwcGl1bS1zdXBwb3J0LnRlbXBEaXIgbW9kdWxlIHRvIGFwcGx5IHRoaXMgYmVuZWZpdC5cbiAgICBpZiAoYXJncy50bXBEaXIpIHtcbiAgICAgIHByb2Nlc3MuZW52LkFQUElVTV9UTVBfRElSID0gYXJncy50bXBEaXI7XG4gICAgfVxuXG4gICAgc3VwZXIoYXJncyk7XG5cbiAgICB0aGlzLmRlc2lyZWRDYXBDb25zdHJhaW50cyA9IGRlc2lyZWRDYXBhYmlsaXR5Q29uc3RyYWludHM7XG5cbiAgICAvLyB0aGUgbWFpbiBBcHBpdW0gRHJpdmVyIGhhcyBubyBuZXcgY29tbWFuZCB0aW1lb3V0XG4gICAgdGhpcy5uZXdDb21tYW5kVGltZW91dE1zID0gMDtcblxuICAgIHRoaXMuYXJncyA9IE9iamVjdC5hc3NpZ24oe30sIGFyZ3MpO1xuXG4gICAgLy8gQWNjZXNzIHRvIHNlc3Npb25zIGxpc3QgbXVzdCBiZSBndWFyZGVkIHdpdGggYSBTZW1hcGhvcmUsIGJlY2F1c2VcbiAgICAvLyBpdCBtaWdodCBiZSBjaGFuZ2VkIGJ5IG90aGVyIGFzeW5jIGNhbGxzIGF0IGFueSB0aW1lXG4gICAgLy8gSXQgaXMgbm90IHJlY29tbWVuZGVkIHRvIGFjY2VzcyB0aGlzIHByb3BlcnR5IGRpcmVjdGx5IGZyb20gdGhlIG91dHNpZGVcbiAgICB0aGlzLnNlc3Npb25zID0ge307XG5cbiAgICAvLyBBY2Nlc3MgdG8gcGVuZGluZyBkcml2ZXJzIGxpc3QgbXVzdCBiZSBndWFyZGVkIHdpdGggYSBTZW1hcGhvcmUsIGJlY2F1c2VcbiAgICAvLyBpdCBtaWdodCBiZSBjaGFuZ2VkIGJ5IG90aGVyIGFzeW5jIGNhbGxzIGF0IGFueSB0aW1lXG4gICAgLy8gSXQgaXMgbm90IHJlY29tbWVuZGVkIHRvIGFjY2VzcyB0aGlzIHByb3BlcnR5IGRpcmVjdGx5IGZyb20gdGhlIG91dHNpZGVcbiAgICB0aGlzLnBlbmRpbmdEcml2ZXJzID0ge307XG5cbiAgICB0aGlzLnBsdWdpbnMgPSBbXTtcblxuICAgIC8vIGFsbG93IHRoaXMgdG8gaGFwcGVuIGluIHRoZSBiYWNrZ3JvdW5kLCBzbyBubyBgYXdhaXRgXG4gICAgdXBkYXRlQnVpbGRJbmZvKCk7XG4gIH1cblxuICAvKipcbiAgICogQ2FuY2VsIGNvbW1hbmRzIHF1ZXVlaW5nIGZvciB0aGUgdW1icmVsbGEgQXBwaXVtIGRyaXZlclxuICAgKi9cbiAgZ2V0IGlzQ29tbWFuZHNRdWV1ZUVuYWJsZWQgKCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIHNlc3Npb25FeGlzdHMgKHNlc3Npb25JZCkge1xuICAgIGNvbnN0IGRzdFNlc3Npb24gPSB0aGlzLnNlc3Npb25zW3Nlc3Npb25JZF07XG4gICAgcmV0dXJuIGRzdFNlc3Npb24gJiYgZHN0U2Vzc2lvbi5zZXNzaW9uSWQgIT09IG51bGw7XG4gIH1cblxuICBkcml2ZXJGb3JTZXNzaW9uIChzZXNzaW9uSWQpIHtcbiAgICByZXR1cm4gdGhpcy5zZXNzaW9uc1tzZXNzaW9uSWRdO1xuICB9XG5cbiAgYXN5bmMgZ2V0U3RhdHVzICgpIHsgLy8gZXNsaW50LWRpc2FibGUtbGluZSByZXF1aXJlLWF3YWl0XG4gICAgcmV0dXJuIHtcbiAgICAgIGJ1aWxkOiBfLmNsb25lKGdldEJ1aWxkSW5mbygpKSxcbiAgICB9O1xuICB9XG5cbiAgYXN5bmMgZ2V0U2Vzc2lvbnMgKCkge1xuICAgIGNvbnN0IHNlc3Npb25zID0gYXdhaXQgc2Vzc2lvbnNMaXN0R3VhcmQuYWNxdWlyZShBcHBpdW1Ecml2ZXIubmFtZSwgKCkgPT4gdGhpcy5zZXNzaW9ucyk7XG4gICAgcmV0dXJuIF8udG9QYWlycyhzZXNzaW9ucylcbiAgICAgIC5tYXAoKFtpZCwgZHJpdmVyXSkgPT4gKHtpZCwgY2FwYWJpbGl0aWVzOiBkcml2ZXIuY2Fwc30pKTtcbiAgfVxuXG4gIHByaW50TmV3U2Vzc2lvbkFubm91bmNlbWVudCAoZHJpdmVyTmFtZSwgZHJpdmVyVmVyc2lvbikge1xuICAgIGNvbnN0IGludHJvU3RyaW5nID0gZHJpdmVyVmVyc2lvblxuICAgICAgPyBgQXBwaXVtIHYke0FQUElVTV9WRVJ9IGNyZWF0aW5nIG5ldyAke2RyaXZlck5hbWV9ICh2JHtkcml2ZXJWZXJzaW9ufSkgc2Vzc2lvbmBcbiAgICAgIDogYEFwcGl1bSB2JHtBUFBJVU1fVkVSfSBjcmVhdGluZyBuZXcgJHtkcml2ZXJOYW1lfSBzZXNzaW9uYDtcbiAgICBsb2cuaW5mbyhpbnRyb1N0cmluZyk7XG4gIH1cblxuICAvKipcbiAgICogVGhpcyBpcyBqdXN0IGFuIGFsaWFzIGZvciBkcml2ZXIuanMncyBtZXRob2QsIHdoaWNoIGlzIG5lY2Vzc2FyeSBmb3JcbiAgICogbW9ja2luZyBpbiB0aGUgdGVzdCBzdWl0ZVxuICAgKi9cbiAgX2ZpbmRNYXRjaGluZ0RyaXZlciAoLi4uYXJncykge1xuICAgIHJldHVybiBmaW5kTWF0Y2hpbmdEcml2ZXIoLi4uYXJncyk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlIGEgbmV3IHNlc3Npb25cbiAgICogQHBhcmFtIHtPYmplY3R9IGpzb253cENhcHMgSlNPTldQIGZvcm1hdHRlZCBkZXNpcmVkIGNhcGFiaWxpdGllc1xuICAgKiBAcGFyYW0ge09iamVjdH0gcmVxQ2FwcyBSZXF1aXJlZCBjYXBhYmlsaXRpZXMgKEpTT05XUCBzdGFuZGFyZClcbiAgICogQHBhcmFtIHtPYmplY3R9IHczY0NhcGFiaWxpdGllcyBXM0MgY2FwYWJpbGl0aWVzXG4gICAqIEByZXR1cm4ge0FycmF5fSBVbmlxdWUgc2Vzc2lvbiBJRCBhbmQgY2FwYWJpbGl0aWVzXG4gICAqL1xuICBhc3luYyBjcmVhdGVTZXNzaW9uIChqc29ud3BDYXBzLCByZXFDYXBzLCB3M2NDYXBhYmlsaXRpZXMpIHtcbiAgICBjb25zdCBkZWZhdWx0Q2FwYWJpbGl0aWVzID0gXy5jbG9uZURlZXAodGhpcy5hcmdzLmRlZmF1bHRDYXBhYmlsaXRpZXMpO1xuICAgIGNvbnN0IGRlZmF1bHRTZXR0aW5ncyA9IHB1bGxTZXR0aW5ncyhkZWZhdWx0Q2FwYWJpbGl0aWVzKTtcbiAgICBqc29ud3BDYXBzID0gXy5jbG9uZURlZXAoanNvbndwQ2Fwcyk7XG4gICAgY29uc3QgandwU2V0dGluZ3MgPSBPYmplY3QuYXNzaWduKHt9LCBkZWZhdWx0U2V0dGluZ3MsIHB1bGxTZXR0aW5ncyhqc29ud3BDYXBzKSk7XG4gICAgdzNjQ2FwYWJpbGl0aWVzID0gXy5jbG9uZURlZXAodzNjQ2FwYWJpbGl0aWVzKTtcbiAgICAvLyBJdCBpcyBwb3NzaWJsZSB0aGF0IHRoZSBjbGllbnQgb25seSBwcm92aWRlcyBjYXBzIHVzaW5nIEpTT05XUCBzdGFuZGFyZCxcbiAgICAvLyBhbHRob3VnaCBmaXJzdE1hdGNoL2Fsd2F5c01hdGNoIHByb3BlcnRpZXMgYXJlIHN0aWxsIHByZXNlbnQuXG4gICAgLy8gSW4gc3VjaCBjYXNlIHdlIGFzc3VtZSB0aGUgY2xpZW50IHVuZGVyc3RhbmRzIFczQyBwcm90b2NvbCBhbmQgbWVyZ2UgdGhlIGdpdmVuXG4gICAgLy8gSlNPTldQIGNhcHMgdG8gVzNDIGNhcHNcbiAgICBjb25zdCB3M2NTZXR0aW5ncyA9IE9iamVjdC5hc3NpZ24oe30sIGp3cFNldHRpbmdzKTtcbiAgICBPYmplY3QuYXNzaWduKHczY1NldHRpbmdzLCBwdWxsU2V0dGluZ3MoKHczY0NhcGFiaWxpdGllcyB8fCB7fSkuYWx3YXlzTWF0Y2ggfHwge30pKTtcbiAgICBmb3IgKGNvbnN0IGZpcnN0TWF0Y2hFbnRyeSBvZiAoKHczY0NhcGFiaWxpdGllcyB8fCB7fSkuZmlyc3RNYXRjaCB8fCBbXSkpIHtcbiAgICAgIE9iamVjdC5hc3NpZ24odzNjU2V0dGluZ3MsIHB1bGxTZXR0aW5ncyhmaXJzdE1hdGNoRW50cnkpKTtcbiAgICB9XG5cbiAgICBsZXQgcHJvdG9jb2w7XG4gICAgbGV0IGlubmVyU2Vzc2lvbklkLCBkQ2FwcztcbiAgICB0cnkge1xuICAgICAgLy8gUGFyc2UgdGhlIGNhcHMgaW50byBhIGZvcm1hdCB0aGF0IHRoZSBJbm5lckRyaXZlciB3aWxsIGFjY2VwdFxuICAgICAgY29uc3QgcGFyc2VkQ2FwcyA9IHBhcnNlQ2Fwc0ZvcklubmVyRHJpdmVyKFxuICAgICAgICBqc29ud3BDYXBzLFxuICAgICAgICB3M2NDYXBhYmlsaXRpZXMsXG4gICAgICAgIHRoaXMuZGVzaXJlZENhcENvbnN0cmFpbnRzLFxuICAgICAgICBkZWZhdWx0Q2FwYWJpbGl0aWVzXG4gICAgICApO1xuXG4gICAgICBjb25zdCB7ZGVzaXJlZENhcHMsIHByb2Nlc3NlZEpzb253cENhcGFiaWxpdGllcywgcHJvY2Vzc2VkVzNDQ2FwYWJpbGl0aWVzLCBlcnJvcn0gPSBwYXJzZWRDYXBzO1xuICAgICAgcHJvdG9jb2wgPSBwYXJzZWRDYXBzLnByb3RvY29sO1xuXG4gICAgICAvLyBJZiB0aGUgcGFyc2luZyBvZiB0aGUgY2FwcyBwcm9kdWNlZCBhbiBlcnJvciwgdGhyb3cgaXQgaW4gaGVyZVxuICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgIHRocm93IGVycm9yO1xuICAgICAgfVxuXG4gICAgICBjb25zdCB7XG4gICAgICAgIGRyaXZlcjogSW5uZXJEcml2ZXIsXG4gICAgICAgIHZlcnNpb246IGRyaXZlclZlcnNpb25cbiAgICAgIH0gPSB0aGlzLl9maW5kTWF0Y2hpbmdEcml2ZXIodGhpcy5kcml2ZXJDb25maWcsIGRlc2lyZWRDYXBzKTtcbiAgICAgIHRoaXMucHJpbnROZXdTZXNzaW9uQW5ub3VuY2VtZW50KElubmVyRHJpdmVyLm5hbWUsIGRyaXZlclZlcnNpb24pO1xuXG4gICAgICBpZiAodGhpcy5hcmdzLnNlc3Npb25PdmVycmlkZSkge1xuICAgICAgICBhd2FpdCB0aGlzLmRlbGV0ZUFsbFNlc3Npb25zKCk7XG4gICAgICB9XG5cbiAgICAgIGxldCBydW5uaW5nRHJpdmVyc0RhdGEsIG90aGVyUGVuZGluZ0RyaXZlcnNEYXRhO1xuICAgICAgY29uc3QgZCA9IG5ldyBJbm5lckRyaXZlcih0aGlzLmFyZ3MpO1xuXG4gICAgICAvLyBXZSB3YW50IHRvIGFzc2lnbiBzZWN1cml0eSB2YWx1ZXMgZGlyZWN0bHkgb24gdGhlIGRyaXZlci4gVGhlIGRyaXZlclxuICAgICAgLy8gc2hvdWxkIG5vdCByZWFkIHNlY3VyaXR5IHZhbHVlcyBmcm9tIGB0aGlzLm9wdHNgIGJlY2F1c2UgdGhvc2UgdmFsdWVzXG4gICAgICAvLyBjb3VsZCBoYXZlIGJlZW4gc2V0IGJ5IGEgbWFsaWNpb3VzIHVzZXIgdmlhIGNhcGFiaWxpdGllcywgd2hlcmVhcyB3ZVxuICAgICAgLy8gd2FudCBhIGd1YXJhbnRlZSB0aGUgdmFsdWVzIHdlcmUgc2V0IGJ5IHRoZSBhcHBpdW0gc2VydmVyIGFkbWluXG4gICAgICBpZiAodGhpcy5hcmdzLnJlbGF4ZWRTZWN1cml0eUVuYWJsZWQpIHtcbiAgICAgICAgbG9nLmluZm8oYEFwcGx5aW5nIHJlbGF4ZWQgc2VjdXJpdHkgdG8gJyR7SW5uZXJEcml2ZXIubmFtZX0nIGFzIHBlciBgICtcbiAgICAgICAgICAgICAgICAgYHNlcnZlciBjb21tYW5kIGxpbmUgYXJndW1lbnQuIEFsbCBpbnNlY3VyZSBmZWF0dXJlcyB3aWxsIGJlIGAgK1xuICAgICAgICAgICAgICAgICBgZW5hYmxlZCB1bmxlc3MgZXhwbGljaXRseSBkaXNhYmxlZCBieSAtLWRlbnktaW5zZWN1cmVgKTtcbiAgICAgICAgZC5yZWxheGVkU2VjdXJpdHlFbmFibGVkID0gdHJ1ZTtcbiAgICAgIH1cblxuICAgICAgaWYgKCFfLmlzRW1wdHkodGhpcy5hcmdzLmRlbnlJbnNlY3VyZSkpIHtcbiAgICAgICAgbG9nLmluZm8oJ0V4cGxpY2l0bHkgcHJldmVudGluZyB1c2Ugb2YgaW5zZWN1cmUgZmVhdHVyZXM6Jyk7XG4gICAgICAgIHRoaXMuYXJncy5kZW55SW5zZWN1cmUubWFwKChhKSA9PiBsb2cuaW5mbyhgICAgICR7YX1gKSk7XG4gICAgICAgIGQuZGVueUluc2VjdXJlID0gdGhpcy5hcmdzLmRlbnlJbnNlY3VyZTtcbiAgICAgIH1cblxuICAgICAgaWYgKCFfLmlzRW1wdHkodGhpcy5hcmdzLmFsbG93SW5zZWN1cmUpKSB7XG4gICAgICAgIGxvZy5pbmZvKCdFeHBsaWNpdGx5IGVuYWJsaW5nIHVzZSBvZiBpbnNlY3VyZSBmZWF0dXJlczonKTtcbiAgICAgICAgdGhpcy5hcmdzLmFsbG93SW5zZWN1cmUubWFwKChhKSA9PiBsb2cuaW5mbyhgICAgICR7YX1gKSk7XG4gICAgICAgIGQuYWxsb3dJbnNlY3VyZSA9IHRoaXMuYXJncy5hbGxvd0luc2VjdXJlO1xuICAgICAgfVxuXG4gICAgICAvLyBUaGlzIGFzc2lnbm1lbnQgaXMgcmVxdWlyZWQgZm9yIGNvcnJlY3Qgd2ViIHNvY2tldHMgZnVuY3Rpb25hbGl0eSBpbnNpZGUgdGhlIGRyaXZlclxuICAgICAgZC5zZXJ2ZXIgPSB0aGlzLnNlcnZlcjtcbiAgICAgIHRyeSB7XG4gICAgICAgIHJ1bm5pbmdEcml2ZXJzRGF0YSA9IGF3YWl0IHRoaXMuY3VyU2Vzc2lvbkRhdGFGb3JEcml2ZXIoSW5uZXJEcml2ZXIpO1xuICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICB0aHJvdyBuZXcgZXJyb3JzLlNlc3Npb25Ob3RDcmVhdGVkRXJyb3IoZS5tZXNzYWdlKTtcbiAgICAgIH1cbiAgICAgIGF3YWl0IHBlbmRpbmdEcml2ZXJzR3VhcmQuYWNxdWlyZShBcHBpdW1Ecml2ZXIubmFtZSwgKCkgPT4ge1xuICAgICAgICB0aGlzLnBlbmRpbmdEcml2ZXJzW0lubmVyRHJpdmVyLm5hbWVdID0gdGhpcy5wZW5kaW5nRHJpdmVyc1tJbm5lckRyaXZlci5uYW1lXSB8fCBbXTtcbiAgICAgICAgb3RoZXJQZW5kaW5nRHJpdmVyc0RhdGEgPSB0aGlzLnBlbmRpbmdEcml2ZXJzW0lubmVyRHJpdmVyLm5hbWVdLm1hcCgoZHJ2KSA9PiBkcnYuZHJpdmVyRGF0YSk7XG4gICAgICAgIHRoaXMucGVuZGluZ0RyaXZlcnNbSW5uZXJEcml2ZXIubmFtZV0ucHVzaChkKTtcbiAgICAgIH0pO1xuXG4gICAgICB0cnkge1xuICAgICAgICBbaW5uZXJTZXNzaW9uSWQsIGRDYXBzXSA9IGF3YWl0IGQuY3JlYXRlU2Vzc2lvbihcbiAgICAgICAgICBwcm9jZXNzZWRKc29ud3BDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgcmVxQ2FwcyxcbiAgICAgICAgICBwcm9jZXNzZWRXM0NDYXBhYmlsaXRpZXMsXG4gICAgICAgICAgWy4uLnJ1bm5pbmdEcml2ZXJzRGF0YSwgLi4ub3RoZXJQZW5kaW5nRHJpdmVyc0RhdGFdXG4gICAgICAgICk7XG4gICAgICAgIHByb3RvY29sID0gZC5wcm90b2NvbDtcbiAgICAgICAgYXdhaXQgc2Vzc2lvbnNMaXN0R3VhcmQuYWNxdWlyZShBcHBpdW1Ecml2ZXIubmFtZSwgKCkgPT4ge1xuICAgICAgICAgIHRoaXMuc2Vzc2lvbnNbaW5uZXJTZXNzaW9uSWRdID0gZDtcbiAgICAgICAgfSk7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBhd2FpdCBwZW5kaW5nRHJpdmVyc0d1YXJkLmFjcXVpcmUoQXBwaXVtRHJpdmVyLm5hbWUsICgpID0+IHtcbiAgICAgICAgICBfLnB1bGwodGhpcy5wZW5kaW5nRHJpdmVyc1tJbm5lckRyaXZlci5uYW1lXSwgZCk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuXG4gICAgICB0aGlzLmF0dGFjaFVuZXhwZWN0ZWRTaHV0ZG93bkhhbmRsZXIoZCwgaW5uZXJTZXNzaW9uSWQpO1xuXG4gICAgICBsb2cuaW5mbyhgTmV3ICR7SW5uZXJEcml2ZXIubmFtZX0gc2Vzc2lvbiBjcmVhdGVkIHN1Y2Nlc3NmdWxseSwgc2Vzc2lvbiBgICtcbiAgICAgICAgICAgICAgYCR7aW5uZXJTZXNzaW9uSWR9IGFkZGVkIHRvIG1hc3RlciBzZXNzaW9uIGxpc3RgKTtcblxuICAgICAgLy8gc2V0IHRoZSBOZXcgQ29tbWFuZCBUaW1lb3V0IGZvciB0aGUgaW5uZXIgZHJpdmVyXG4gICAgICBkLnN0YXJ0TmV3Q29tbWFuZFRpbWVvdXQoKTtcblxuICAgICAgLy8gYXBwbHkgaW5pdGlhbCB2YWx1ZXMgdG8gQXBwaXVtIHNldHRpbmdzIChpZiBwcm92aWRlZClcbiAgICAgIGlmIChkLmlzVzNDUHJvdG9jb2woKSAmJiAhXy5pc0VtcHR5KHczY1NldHRpbmdzKSkge1xuICAgICAgICBsb2cuaW5mbyhgQXBwbHlpbmcgdGhlIGluaXRpYWwgdmFsdWVzIHRvIEFwcGl1bSBzZXR0aW5ncyBwYXJzZWQgZnJvbSBXM0MgY2FwczogYCArXG4gICAgICAgICAgSlNPTi5zdHJpbmdpZnkodzNjU2V0dGluZ3MpKTtcbiAgICAgICAgYXdhaXQgZC51cGRhdGVTZXR0aW5ncyh3M2NTZXR0aW5ncyk7XG4gICAgICB9IGVsc2UgaWYgKGQuaXNNanNvbndwUHJvdG9jb2woKSAmJiAhXy5pc0VtcHR5KGp3cFNldHRpbmdzKSkge1xuICAgICAgICBsb2cuaW5mbyhgQXBwbHlpbmcgdGhlIGluaXRpYWwgdmFsdWVzIHRvIEFwcGl1bSBzZXR0aW5ncyBwYXJzZWQgZnJvbSBNSlNPTldQIGNhcHM6IGAgK1xuICAgICAgICAgIEpTT04uc3RyaW5naWZ5KGp3cFNldHRpbmdzKSk7XG4gICAgICAgIGF3YWl0IGQudXBkYXRlU2V0dGluZ3MoandwU2V0dGluZ3MpO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBwcm90b2NvbCxcbiAgICAgICAgZXJyb3IsXG4gICAgICB9O1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICBwcm90b2NvbCxcbiAgICAgIHZhbHVlOiBbaW5uZXJTZXNzaW9uSWQsIGRDYXBzLCBwcm90b2NvbF1cbiAgICB9O1xuICB9XG5cbiAgYXR0YWNoVW5leHBlY3RlZFNodXRkb3duSGFuZGxlciAoZHJpdmVyLCBpbm5lclNlc3Npb25JZCkge1xuICAgIGNvbnN0IHJlbW92ZVNlc3Npb25Gcm9tTWFzdGVyTGlzdCA9IChjYXVzZSA9IG5ldyBFcnJvcignVW5rbm93biBlcnJvcicpKSA9PiB7XG4gICAgICBsb2cud2FybihgQ2xvc2luZyBzZXNzaW9uLCBjYXVzZSB3YXMgJyR7Y2F1c2UubWVzc2FnZX0nYCk7XG4gICAgICBsb2cuaW5mbyhgUmVtb3Zpbmcgc2Vzc2lvbiAnJHtpbm5lclNlc3Npb25JZH0nIGZyb20gb3VyIG1hc3RlciBzZXNzaW9uIGxpc3RgKTtcbiAgICAgIGRlbGV0ZSB0aGlzLnNlc3Npb25zW2lubmVyU2Vzc2lvbklkXTtcbiAgICB9O1xuXG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHByb21pc2UvcHJlZmVyLWF3YWl0LXRvLXRoZW5cbiAgICBpZiAoXy5pc0Z1bmN0aW9uKChkcml2ZXIub25VbmV4cGVjdGVkU2h1dGRvd24gfHwge30pLnRoZW4pKSB7XG4gICAgICAvLyBUT0RPOiBSZW1vdmUgdGhpcyBibG9jayBhZnRlciBhbGwgdGhlIGRyaXZlcnMgdXNlIGJhc2UgZHJpdmVyIGFib3ZlIHYgNS4wLjBcbiAgICAgIC8vIFJlbW92ZSB0aGUgc2Vzc2lvbiBvbiB1bmV4cGVjdGVkIHNodXRkb3duLCBzbyB0aGF0IHdlIGFyZSBpbiBhIHBvc2l0aW9uXG4gICAgICAvLyB0byBvcGVuIGFub3RoZXIgc2Vzc2lvbiBsYXRlciBvbi5cbiAgICAgIGRyaXZlci5vblVuZXhwZWN0ZWRTaHV0ZG93blxuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcHJvbWlzZS9wcmVmZXItYXdhaXQtdG8tdGhlblxuICAgICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgICAgLy8gaWYgd2UgZ2V0IGhlcmUsIHdlJ3ZlIGhhZCBhbiB1bmV4cGVjdGVkIHNodXRkb3duLCBzbyBlcnJvclxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcignVW5leHBlY3RlZCBzaHV0ZG93bicpO1xuICAgICAgICB9KVxuICAgICAgICAuY2F0Y2goKGUpID0+IHtcbiAgICAgICAgICAvLyBpZiB3ZSBjYW5jZWxsZWQgdGhlIHVuZXhwZWN0ZWQgc2h1dGRvd24gcHJvbWlzZSwgdGhhdCBtZWFucyB3ZVxuICAgICAgICAgIC8vIG5vIGxvbmdlciBjYXJlIGFib3V0IGl0LCBhbmQgY2FuIHNhZmVseSBpZ25vcmUgaXRcbiAgICAgICAgICBpZiAoIShlIGluc3RhbmNlb2YgQi5DYW5jZWxsYXRpb25FcnJvcikpIHtcbiAgICAgICAgICAgIHJlbW92ZVNlc3Npb25Gcm9tTWFzdGVyTGlzdChlKTtcbiAgICAgICAgICB9XG4gICAgICAgIH0pOyAvLyB0aGlzIGlzIGEgY2FuY2VsbGFibGUgcHJvbWlzZVxuICAgIH0gZWxzZSBpZiAoXy5pc0Z1bmN0aW9uKGRyaXZlci5vblVuZXhwZWN0ZWRTaHV0ZG93bikpIHtcbiAgICAgIC8vIHNpbmNlIGJhc2UgZHJpdmVyIHYgNS4wLjBcbiAgICAgIGRyaXZlci5vblVuZXhwZWN0ZWRTaHV0ZG93bihyZW1vdmVTZXNzaW9uRnJvbU1hc3Rlckxpc3QpO1xuICAgIH0gZWxzZSB7XG4gICAgICBsb2cud2FybihgRmFpbGVkIHRvIGF0dGFjaCB0aGUgdW5leHBlY3RlZCBzaHV0ZG93biBsaXN0ZW5lci4gYCArXG4gICAgICAgIGBJcyAnb25VbmV4cGVjdGVkU2h1dGRvd24nIG1ldGhvZCBhdmFpbGFibGUgZm9yICcke2RyaXZlci5jb25zdHJ1Y3Rvci5uYW1lfSc/YCk7XG4gICAgfVxuICB9XG5cbiAgYXN5bmMgY3VyU2Vzc2lvbkRhdGFGb3JEcml2ZXIgKElubmVyRHJpdmVyKSB7XG4gICAgY29uc3Qgc2Vzc2lvbnMgPSBhd2FpdCBzZXNzaW9uc0xpc3RHdWFyZC5hY3F1aXJlKEFwcGl1bURyaXZlci5uYW1lLCAoKSA9PiB0aGlzLnNlc3Npb25zKTtcbiAgICBjb25zdCBkYXRhID0gXy52YWx1ZXMoc2Vzc2lvbnMpXG4gICAgICAgICAgICAgICAgICAgLmZpbHRlcigocykgPT4gcy5jb25zdHJ1Y3Rvci5uYW1lID09PSBJbm5lckRyaXZlci5uYW1lKVxuICAgICAgICAgICAgICAgICAgIC5tYXAoKHMpID0+IHMuZHJpdmVyRGF0YSk7XG4gICAgZm9yIChsZXQgZGF0dW0gb2YgZGF0YSkge1xuICAgICAgaWYgKCFkYXR1bSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFByb2JsZW0gZ2V0dGluZyBzZXNzaW9uIGRhdGEgZm9yIGRyaXZlciB0eXBlIGAgK1xuICAgICAgICAgICAgICAgICAgICAgICAgYCR7SW5uZXJEcml2ZXIubmFtZX07IGRvZXMgaXQgaW1wbGVtZW50ICdnZXQgYCArXG4gICAgICAgICAgICAgICAgICAgICAgICBgZHJpdmVyRGF0YSc/YCk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBkYXRhO1xuICB9XG5cbiAgYXN5bmMgZGVsZXRlU2Vzc2lvbiAoc2Vzc2lvbklkKSB7XG4gICAgbGV0IHByb3RvY29sO1xuICAgIHRyeSB7XG4gICAgICBsZXQgb3RoZXJTZXNzaW9uc0RhdGEgPSBudWxsO1xuICAgICAgbGV0IGRzdFNlc3Npb24gPSBudWxsO1xuICAgICAgYXdhaXQgc2Vzc2lvbnNMaXN0R3VhcmQuYWNxdWlyZShBcHBpdW1Ecml2ZXIubmFtZSwgKCkgPT4ge1xuICAgICAgICBpZiAoIXRoaXMuc2Vzc2lvbnNbc2Vzc2lvbklkXSkge1xuICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBjdXJDb25zdHJ1Y3Rvck5hbWUgPSB0aGlzLnNlc3Npb25zW3Nlc3Npb25JZF0uY29uc3RydWN0b3IubmFtZTtcbiAgICAgICAgb3RoZXJTZXNzaW9uc0RhdGEgPSBfLnRvUGFpcnModGhpcy5zZXNzaW9ucylcbiAgICAgICAgICAgICAgLmZpbHRlcigoW2tleSwgdmFsdWVdKSA9PiB2YWx1ZS5jb25zdHJ1Y3Rvci5uYW1lID09PSBjdXJDb25zdHJ1Y3Rvck5hbWUgJiYga2V5ICE9PSBzZXNzaW9uSWQpXG4gICAgICAgICAgICAgIC5tYXAoKFssIHZhbHVlXSkgPT4gdmFsdWUuZHJpdmVyRGF0YSk7XG4gICAgICAgIGRzdFNlc3Npb24gPSB0aGlzLnNlc3Npb25zW3Nlc3Npb25JZF07XG4gICAgICAgIHByb3RvY29sID0gZHN0U2Vzc2lvbi5wcm90b2NvbDtcbiAgICAgICAgbG9nLmluZm8oYFJlbW92aW5nIHNlc3Npb24gJHtzZXNzaW9uSWR9IGZyb20gb3VyIG1hc3RlciBzZXNzaW9uIGxpc3RgKTtcbiAgICAgICAgLy8gcmVnYXJkbGVzcyBvZiB3aGV0aGVyIHRoZSBkZWxldGVTZXNzaW9uIGNvbXBsZXRlcyBzdWNjZXNzZnVsbHkgb3Igbm90XG4gICAgICAgIC8vIG1ha2UgdGhlIHNlc3Npb24gdW5hdmFpbGFibGUsIGJlY2F1c2Ugd2hvIGtub3dzIHdoYXQgc3RhdGUgaXQgbWlnaHRcbiAgICAgICAgLy8gYmUgaW4gb3RoZXJ3aXNlXG4gICAgICAgIGRlbGV0ZSB0aGlzLnNlc3Npb25zW3Nlc3Npb25JZF07XG4gICAgICB9KTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHByb3RvY29sLFxuICAgICAgICB2YWx1ZTogYXdhaXQgZHN0U2Vzc2lvbi5kZWxldGVTZXNzaW9uKHNlc3Npb25JZCwgb3RoZXJTZXNzaW9uc0RhdGEpLFxuICAgICAgfTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICBsb2cuZXJyb3IoYEhhZCB0cm91YmxlIGVuZGluZyBzZXNzaW9uICR7c2Vzc2lvbklkfTogJHtlLm1lc3NhZ2V9YCk7XG4gICAgICByZXR1cm4ge1xuICAgICAgICBwcm90b2NvbCxcbiAgICAgICAgZXJyb3I6IGUsXG4gICAgICB9O1xuICAgIH1cbiAgfVxuXG4gIGFzeW5jIGRlbGV0ZUFsbFNlc3Npb25zIChvcHRzID0ge30pIHtcbiAgICBjb25zdCBzZXNzaW9uc0NvdW50ID0gXy5zaXplKHRoaXMuc2Vzc2lvbnMpO1xuICAgIGlmICgwID09PSBzZXNzaW9uc0NvdW50KSB7XG4gICAgICBsb2cuZGVidWcoJ1RoZXJlIGFyZSBubyBhY3RpdmUgc2Vzc2lvbnMgZm9yIGNsZWFudXAnKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCB7XG4gICAgICBmb3JjZSA9IGZhbHNlLFxuICAgICAgcmVhc29uLFxuICAgIH0gPSBvcHRzO1xuICAgIGxvZy5kZWJ1ZyhgQ2xlYW5pbmcgdXAgJHt1dGlsLnBsdXJhbGl6ZSgnYWN0aXZlIHNlc3Npb24nLCBzZXNzaW9uc0NvdW50LCB0cnVlKX1gKTtcbiAgICBjb25zdCBjbGVhbnVwUHJvbWlzZXMgPSBmb3JjZVxuICAgICAgPyBfLnZhbHVlcyh0aGlzLnNlc3Npb25zKS5tYXAoKGRydikgPT4gZHJ2LnN0YXJ0VW5leHBlY3RlZFNodXRkb3duKHJlYXNvbiAmJiBuZXcgRXJyb3IocmVhc29uKSkpXG4gICAgICA6IF8ua2V5cyh0aGlzLnNlc3Npb25zKS5tYXAoKGlkKSA9PiB0aGlzLmRlbGV0ZVNlc3Npb24oaWQpKTtcbiAgICBmb3IgKGNvbnN0IGNsZWFudXBQcm9taXNlIG9mIGNsZWFudXBQcm9taXNlcykge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgY2xlYW51cFByb21pc2U7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGxvZy5kZWJ1ZyhlKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBwbHVnaW5zVG9IYW5kbGVDbWQgKGNtZCkge1xuICAgIHJldHVybiB0aGlzLnBsdWdpbnMuZmlsdGVyKChwKSA9PlxuICAgICAgcC5jb21tYW5kcyA9PT0gdHJ1ZSB8fFxuICAgICAgKF8uaXNBcnJheShwLmNvbW1hbmRzKSAmJiBfLmluY2x1ZGVzKHAuY29tbWFuZHMsIGNtZCkpXG4gICAgKTtcbiAgfVxuXG4gIGFzeW5jIGV4ZWN1dGVDb21tYW5kIChjbWQsIC4uLmFyZ3MpIHtcbiAgICAvLyBXZSBoYXZlIGJhc2ljYWxseSB0aHJlZSBjYXNlcyBmb3IgaG93IHRvIGhhbmRsZSBjb21tYW5kczpcbiAgICAvLyAxLiBoYW5kbGUgZ2V0U3RhdHVzICh3ZSBkbyB0aGlzIGFzIGEgc3BlY2lhbCBvdXQgb2YgYmFuZCBjYXNlIHNvIGl0IGRvZXNuJ3QgZ2V0IGFkZGVkIHRvIGFuXG4gICAgLy8gICAgZXhlY3V0aW9uIHF1ZXVlLCBhbmQgY2FuIGJlIGNhbGxlZCB3aGlsZSBlLmcuIGNyZWF0ZVNlc3Npb24gaXMgaW4gcHJvZ3Jlc3MpXG4gICAgLy8gMi4gaGFuZGxlIGNvbW1hbmRzIHRoYXQgdGhpcyB1bWJyZWxsYSBkcml2ZXIgc2hvdWxkIGhhbmRsZSwgcmF0aGVyIHRoYW4gdGhlIGFjdHVhbCBzZXNzaW9uXG4gICAgLy8gICAgZHJpdmVyIChmb3IgZXhhbXBsZSwgZGVsZXRlU2Vzc2lvbiwgb3Igb3RoZXIgbm9uLXNlc3Npb24gY29tbWFuZHMpXG4gICAgLy8gMy4gaGFuZGxlIHNlc3Npb24gZHJpdmVyIGNvbW1hbmRzLlxuICAgIC8vIFRoZSB0cmlja3kgcGFydCBpcyB0aGF0IGJlY2F1c2Ugd2Ugc3VwcG9ydCBjb21tYW5kIHBsdWdpbnMsIHdlIG5lZWQgdG8gd3JhcCBhbnkgb2YgdGhlc2VcbiAgICAvLyBjYXNlcyB3aXRoIHBsdWdpbiBoYW5kbGluZy5cblxuICAgIGNvbnN0IGlzR2V0U3RhdHVzID0gY21kID09PSAnZ2V0U3RhdHVzJztcbiAgICBjb25zdCBpc1VtYnJlbGxhQ21kID0gIWlzR2V0U3RhdHVzICYmIGlzQXBwaXVtRHJpdmVyQ29tbWFuZChjbWQpO1xuICAgIGNvbnN0IGlzU2Vzc2lvbkNtZCA9ICFpc0dldFN0YXR1cyAmJiAhaXNVbWJyZWxsYUNtZDtcblxuICAgIC8vIGdldCBhbnkgcGx1Z2lucyB3aGljaCBhcmUgcmVnaXN0ZXJlZCBhcyBoYW5kbGluZyB0aGlzIGNvbW1hbmRcbiAgICBjb25zdCBwbHVnaW5zID0gdGhpcy5wbHVnaW5zVG9IYW5kbGVDbWQoY21kKTtcblxuICAgIC8vIGZpcnN0IGRvIHNvbWUgZXJyb3IgY2hlY2tpbmcuIElmIHdlJ3JlIHJlcXVlc3RpbmcgYSBzZXNzaW9uIGNvbW1hbmQgZXhlY3V0aW9uLCB0aGVuIG1ha2VcbiAgICAvLyBzdXJlIHRoYXQgc2Vzc2lvbiBhY3R1YWxseSBleGlzdHMgb24gdGhlIHNlc3Npb24gZHJpdmVyLCBhbmQgc2V0IHRoZSBzZXNzaW9uIGRyaXZlciBpdHNlbGZcbiAgICBsZXQgc2Vzc2lvbklkID0gbnVsbDtcbiAgICBsZXQgZHN0U2Vzc2lvbiA9IG51bGw7XG4gICAgbGV0IHByb3RvY29sID0gbnVsbDtcbiAgICBpZiAoaXNTZXNzaW9uQ21kKSB7XG4gICAgICBzZXNzaW9uSWQgPSBfLmxhc3QoYXJncyk7XG4gICAgICBkc3RTZXNzaW9uID0gYXdhaXQgc2Vzc2lvbnNMaXN0R3VhcmQuYWNxdWlyZShBcHBpdW1Ecml2ZXIubmFtZSwgKCkgPT4gdGhpcy5zZXNzaW9uc1tzZXNzaW9uSWRdKTtcbiAgICAgIGlmICghZHN0U2Vzc2lvbikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYFRoZSBzZXNzaW9uIHdpdGggaWQgJyR7c2Vzc2lvbklkfScgZG9lcyBub3QgZXhpc3RgKTtcbiAgICAgIH1cbiAgICAgIC8vIG5vdyBzYXZlIHRoZSByZXNwb25zZSBwcm90b2NvbCBnaXZlbiB0aGF0IHRoZSBzZXNzaW9uIGRyaXZlcidzIHByb3RvY29sIG1pZ2h0IGRpZmZlclxuICAgICAgcHJvdG9jb2wgPSBkc3RTZXNzaW9uLnByb3RvY29sO1xuICAgIH1cblxuICAgIC8vIG5vdyB3ZSBkZWZpbmUgYSAnY21kSGFuZGxlZEJ5JyBvYmplY3Qgd2hpY2ggd2lsbCBrZWVwIHRyYWNrIG9mIHdoaWNoIHBsdWdpbnMgaGF2ZSBoYW5kbGVkIHRoaXNcbiAgICAvLyBjb21tYW5kLiB3ZSBjYXJlIGFib3V0IHRoaXMgYmVjYXVzZSAoYSkgbXVsdGlwbGUgcGx1Z2lucyBjYW4gaGFuZGxlIHRoZSBzYW1lIGNvbW1hbmQsIGFuZFxuICAgIC8vIChiKSB0aGVyZSdzIG5vIGd1YXJhbnRlZSB0aGF0IGEgcGx1Z2luIHdpbGwgYWN0dWFsbHkgY2FsbCB0aGUgbmV4dCgpIG1ldGhvZCB3aGljaCBydW5zIHRoZVxuICAgIC8vIG9yaWdpbmFsIGNvbW1hbmQgZXhlY3V0aW9uLiBUaGlzIHJlc3VsdHMgaW4gYSBzaXR1YXRpb24gd2hlcmUgdGhlIGNvbW1hbmQgbWlnaHQgYmUgaGFuZGxlZFxuICAgIC8vIGJ5IHNvbWUgYnV0IG5vdCBhbGwgcGx1Z2lucywgb3IgYnkgcGx1Z2luKHMpIGJ1dCBub3QgYnkgdGhlIGRlZmF1bHQgYmVoYXZpb3IuIFNvIHN0YXJ0IG91dFxuICAgIC8vIHRoaXMgb2JqZWN0IGRlY2xhcmluZyB0aGF0IHRoZSBkZWZhdWx0IGhhbmRsZXIgaGFzIG5vdCBiZWVuIGV4ZWN1dGVkLlxuICAgIGNvbnN0IGNtZEhhbmRsZWRCeSA9IHtkZWZhdWx0OiBmYWxzZX07XG5cbiAgICAvLyBub3cgd2UgZGVmaW5lIGFuIGFzeW5jIGZ1bmN0aW9uIHdoaWNoIHdpbGwgYmUgcGFzc2VkIHRvIHBsdWdpbnMsIGFuZCBzdWNjZXNzaXZlbHkgd3JhcHBlZFxuICAgIC8vIGlmIHRoZXJlIGlzIG1vcmUgdGhhbiBvbmUgcGx1Z2luIHRoYXQgY2FuIGhhbmRsZSB0aGUgY29tbWFuZC4gVG8gc3RhcnQgb2ZmIHdpdGgsIHRoZSBhc3luY1xuICAgIC8vIGZ1bmN0aW9uIGlzIGRlZmluZWQgYXMgY2FsbGluZyB0aGUgZGVmYXVsdCBiZWhhdmlvciwgaS5lLiwgd2hpY2hldmVyIG9mIHRoZSAzIGNhc2VzIGFib3ZlIGlzXG4gICAgLy8gdGhlIGFwcHJvcHJpYXRlIG9uZVxuICAgIGNvbnN0IGRlZmF1bHRCZWhhdmlvciA9IGFzeW5jICgpID0+IHtcbiAgICAgIC8vIGlmIHdlJ3JlIHJ1bm5pbmcgd2l0aCBwbHVnaW5zLCBtYWtlIHN1cmUgd2UgbG9nIHRoYXQgdGhlIGRlZmF1bHQgYmVoYXZpb3IgaXMgYWN0dWFsbHlcbiAgICAgIC8vIGhhcHBlbmluZyBzbyB3ZSBjYW4gdGVsbCB3aGVuIHRoZSBwbHVnaW4gY2FsbCBjaGFpbiBpcyB1bndyYXBwaW5nIHRvIHRoZSBkZWZhdWx0IGJlaGF2aW9yXG4gICAgICAvLyBpZiB0aGF0J3Mgd2hhdCBoYXBwZW5zXG4gICAgICBwbHVnaW5zLmxlbmd0aCAmJiBsb2cuaW5mbyhgRXhlY3V0aW5nIGRlZmF1bHQgaGFuZGxpbmcgYmVoYXZpb3IgZm9yIGNvbW1hbmQgJyR7Y21kfSdgKTtcblxuICAgICAgLy8gaWYgd2UgbWFrZSBpdCBoZXJlLCB3ZSBrbm93IHRoYXQgdGhlIGRlZmF1bHQgYmVoYXZpb3IgaXMgaGFuZGxlZFxuICAgICAgY21kSGFuZGxlZEJ5LmRlZmF1bHQgPSB0cnVlO1xuXG4gICAgICBpZiAoaXNHZXRTdGF0dXMpIHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IHRoaXMuZ2V0U3RhdHVzKCk7XG4gICAgICB9XG5cbiAgICAgIGlmIChpc1VtYnJlbGxhQ21kKSB7XG4gICAgICAgIC8vIHNvbWUgY29tbWFuZHMsIGxpa2UgZGVsZXRlU2Vzc2lvbiwgd2Ugd2FudCB0byBtYWtlIHN1cmUgdG8gaGFuZGxlIG9uICp0aGlzKiBkcml2ZXIsXG4gICAgICAgIC8vIG5vdCB0aGUgcGxhdGZvcm0gZHJpdmVyXG4gICAgICAgIHJldHVybiBhd2FpdCBzdXBlci5leGVjdXRlQ29tbWFuZChjbWQsIC4uLmFyZ3MpO1xuICAgICAgfVxuXG4gICAgICAvLyBoZXJlIHdlIGtub3cgdGhhdCB3ZSBhcmUgZXhlY3V0aW5nIGEgc2Vzc2lvbiBjb21tYW5kLCBhbmQgaGF2ZSBhIHZhbGlkIHNlc3Npb24gZHJpdmVyXG4gICAgICByZXR1cm4gYXdhaXQgZHN0U2Vzc2lvbi5leGVjdXRlQ29tbWFuZChjbWQsIC4uLmFyZ3MpO1xuICAgIH07XG5cbiAgICAvLyBub3cgdGFrZSBvdXIgZGVmYXVsdCBiZWhhdmlvciwgd3JhcCBpdCB3aXRoIGFueSBudW1iZXIgb2YgcGx1Z2luIGJlaGF2aW9ycywgYW5kIHJ1biBpdFxuICAgIGNvbnN0IHdyYXBwZWRDbWQgPSB0aGlzLndyYXBDb21tYW5kV2l0aFBsdWdpbnMoe2NtZCwgYXJncywgcGx1Z2lucywgY21kSGFuZGxlZEJ5LCBuZXh0OiBkZWZhdWx0QmVoYXZpb3J9KTtcbiAgICBjb25zdCByZXMgPSBhd2FpdCB0aGlzLmV4ZWN1dGVXcmFwcGVkQ29tbWFuZCh7d3JhcHBlZENtZCwgcHJvdG9jb2x9KTtcblxuICAgIC8vIGlmIHdlIGhhZCBwbHVnaW5zLCBtYWtlIHN1cmUgdG8gbG9nIG91dCB0aGUgaGVscGZ1bCByZXBvcnQgYWJvdXQgd2hpY2ggcGx1Z2lucyBlbmRlZCB1cFxuICAgIC8vIGhhbmRsaW5nIHRoZSBjb21tYW5kIGFuZCB3aGljaCBkaWRuJ3RcbiAgICBwbHVnaW5zLmxlbmd0aCAmJiB0aGlzLmxvZ1BsdWdpbkhhbmRsZXJSZXBvcnQoe2NtZCwgY21kSGFuZGxlZEJ5fSk7XG5cbiAgICByZXR1cm4gcmVzO1xuICB9XG5cbiAgd3JhcENvbW1hbmRXaXRoUGx1Z2lucyAoe2NtZCwgYXJncywgbmV4dCwgY21kSGFuZGxlZEJ5LCBwbHVnaW5zfSkge1xuICAgIHBsdWdpbnMubGVuZ3RoICYmIGxvZy5pbmZvKGBQbHVnaW5zIHdoaWNoIGNhbiBoYW5kbGUgY21kICcke2NtZH0nOiAke3BsdWdpbnMubWFwKChwKSA9PiBwLm5hbWUpfWApO1xuXG4gICAgLy8gbm93IHdlIGNhbiBnbyB0aHJvdWdoIGVhY2ggcGx1Z2luIGFuZCB3cmFwIGBuZXh0YCBhcm91bmQgaXRzIG93biBoYW5kbGVyLCBwYXNzaW5nIHRoZSAqb2xkKlxuICAgIC8vIG5leHQgaW4gc28gdGhhdCBpdCBjYW4gY2FsbCBpdCBpZiBpdCB3YW50cyB0b1xuICAgIGZvciAoY29uc3QgcGx1Z2luIG9mIHBsdWdpbnMpIHtcbiAgICAgIC8vIG5lZWQgYW4gSUlGRSBoZXJlIGJlY2F1c2Ugd2Ugd2FudCB0aGUgdmFsdWUgb2YgbmV4dCB0aGF0J3MgcGFzc2VkIHRvIHBsdWdpbi5oYW5kbGUgdG8gYmVcbiAgICAgIC8vIGV4YWN0bHkgdGhlIHZhbHVlIG9mIG5leHQgaGVyZSBiZWZvcmUgcmVhc3NpZ25tZW50OyB3ZSBkb24ndCB3YW50IGl0IHRvIGJlIGxhemlseVxuICAgICAgLy8gZXZhbHVhdGVkLCBvdGhlcndpc2Ugd2UgZW5kIHVwIHdpdGggaW5maW5pdGUgcmVjdXJzaW9uIG9mIHRoZSBsYXN0IGBuZXh0YCB0byBiZSBkZWZpbmVkLlxuICAgICAgY21kSGFuZGxlZEJ5W3BsdWdpbi5uYW1lXSA9IGZhbHNlOyAvLyB3ZSBzZWUgYSBuZXcgcGx1Z2luLCBzbyBhZGQgaXQgdG8gdGhlICdjbWRIYW5kbGVkQnknIG9iamVjdFxuICAgICAgbmV4dCA9ICgoX25leHQpID0+IGFzeW5jICgpID0+IHtcbiAgICAgICAgbG9nLmluZm8oYFBsdWdpbiAke3BsdWdpbi5uYW1lfSBpcyBub3cgaGFuZGxpbmcgY21kICcke2NtZH0nYCk7XG4gICAgICAgIGNtZEhhbmRsZWRCeVtwbHVnaW4ubmFtZV0gPSB0cnVlOyAvLyBpZiB3ZSBtYWtlIGl0IGhlcmUsIHRoaXMgcGx1Z2luIGhhcyBhdHRlbXB0ZWQgdG8gaGFuZGxlIGNtZFxuICAgICAgICByZXR1cm4gYXdhaXQgcGx1Z2luLmhhbmRsZShfbmV4dCwgdGhpcywgY21kLCAuLi5hcmdzKTtcbiAgICAgIH0pKG5leHQpO1xuICAgIH1cblxuICAgIHJldHVybiBuZXh0O1xuICB9XG5cbiAgbG9nUGx1Z2luSGFuZGxlclJlcG9ydCAoe2NtZCwgY21kSGFuZGxlZEJ5fSkge1xuICAgIC8vIGF0IHRoZSBlbmQgb2YgdGhlIGRheSwgd2UgaGF2ZSBhbiBvYmplY3QgcmVwcmVzZW50aW5nIHdoaWNoIHBsdWdpbnMgZW5kZWQgdXAgZ2V0dGluZ1xuICAgIC8vIHRoZWlyIGNvZGUgcnVuIGFzIHBhcnQgb2YgaGFuZGxpbmcgdGhpcyBjb21tYW5kLiBCZWNhdXNlIHBsdWdpbnMgY2FuIGNob29zZSAqbm90KiB0b1xuICAgIC8vIHBhc3MgY29udHJvbCB0byBvdGhlciBwbHVnaW5zIG9yIHRvIHRoZSBkZWZhdWx0IGRyaXZlciBiZWhhdmlvciwgdGhpcyBpcyBpbmZvcm1hdGlvblxuICAgIC8vIHdoaWNoIGlzIHByb2JhYmx5IHVzZWZ1bCB0byB0aGUgdXNlciAoZXNwZWNpYWxseSBpbiBzaXR1YXRpb25zIHdoZXJlIHBsdWdpbnMgbWlnaHQgbm90XG4gICAgLy8gaW50ZXJhY3Qgd2VsbCB0b2dldGhlciwgYW5kIGl0IHdvdWxkIGJlIGhhcmQgdG8gZGVidWcgb3RoZXJ3aXNlIHdpdGhvdXQgdGhpcyBraW5kIG9mXG4gICAgLy8gbWVzc2FnZSkuXG4gICAgY29uc3QgZGlkSGFuZGxlID0gT2JqZWN0LmtleXMoY21kSGFuZGxlZEJ5KS5maWx0ZXIoKGspID0+IGNtZEhhbmRsZWRCeVtrXSk7XG4gICAgY29uc3QgZGlkbnRIYW5kbGUgPSBPYmplY3Qua2V5cyhjbWRIYW5kbGVkQnkpLmZpbHRlcigoaykgPT4gIWNtZEhhbmRsZWRCeVtrXSk7XG4gICAgaWYgKGRpZG50SGFuZGxlLmxlbmd0aCA+IDApIHtcbiAgICAgIGxvZy5pbmZvKGBDb21tYW5kICcke2NtZH0nIHdhcyBub3QgaGFuZGxlZCBieSB0aGUgZm9sbG93aW5nIGJlYWh2aW9ycyBvciBwbHVnaW5zLCBldmVuIGAgK1xuICAgICAgICAgICAgICAgYHRob3VnaCB0aGV5IHdlcmUgcmVnaXN0ZXJlZCB0byBoYW5kbGUgaXQ6ICR7SlNPTi5zdHJpbmdpZnkoZGlkbnRIYW5kbGUpfS4gVGhlIGAgK1xuICAgICAgICAgICAgICAgYGNvbW1hbmQgKndhcyogaGFuZGxlZCBieSB0aGVzZTogJHtKU09OLnN0cmluZ2lmeShkaWRIYW5kbGUpfS5gKTtcbiAgICB9XG4gIH1cblxuICBhc3luYyBleGVjdXRlV3JhcHBlZENvbW1hbmQgKHt3cmFwcGVkQ21kLCBwcm90b2NvbH0pIHtcbiAgICBsZXQgY21kUmVzLCBjbWRFcnIsIHJlcyA9IHt9O1xuICAgIHRyeSB7XG4gICAgICAvLyBBdCB0aGlzIHBvaW50LCBgd3JhcHBlZENtZGAgZGVmaW5lcyBhIHdob2xlIHNlcXVlbmNlIG9mIHBsdWdpbiBoYW5kbGVycywgY3VsbWluYXRpbmcgaW5cbiAgICAgIC8vIG91ciBkZWZhdWx0IGhhbmRsZXIuIFdoYXRldmVyIGl0IHJldHVybnMgaXMgd2hhdCB3ZSdyZSBnb2luZyB0byB3YW50IHRvIHNlbmQgYmFjayB0byB0aGVcbiAgICAgIC8vIHVzZXIuXG4gICAgICBjbWRSZXMgPSBhd2FpdCB3cmFwcGVkQ21kKCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgY21kRXJyID0gZTtcbiAgICB9XG5cbiAgICAvLyBTYWRseSwgd2UgZG9uJ3Qga25vdyBleGFjdGx5IHdoYXQga2luZCBvZiBvYmplY3Qgd2lsbCBiZSByZXR1cm5lZC4gSXQgd2lsbCBlaXRoZXIgYmUgYSBiYXJlXG4gICAgLy8gb2JqZWN0LCBvciBhIHByb3RvY29sLWF3YXJlIG9iamVjdCB3aXRoIHByb3RvY29sIGFuZCBlcnJvci92YWx1ZSBrZXlzLiBTbyB3ZSBuZWVkIHRvIHNuaWZmXG4gICAgLy8gaXQgYW5kIG1ha2Ugc3VyZSB3ZSBkb24ndCBkb3VibGUtd3JhcCBpdCBpZiBpdCdzIHRoZSBsYXR0ZXIga2luZC5cbiAgICBpZiAoXy5pc1BsYWluT2JqZWN0KGNtZFJlcykgJiYgXy5oYXMoY21kUmVzLCAncHJvdG9jb2wnKSkge1xuICAgICAgcmVzID0gY21kUmVzO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXMudmFsdWUgPSBjbWRSZXM7XG4gICAgICByZXMuZXJyb3IgPSBjbWRFcnI7XG4gICAgICByZXMucHJvdG9jb2wgPSBwcm90b2NvbDtcbiAgICB9XG4gICAgcmV0dXJuIHJlcztcbiAgfVxuXG5cbiAgcHJveHlBY3RpdmUgKHNlc3Npb25JZCkge1xuICAgIGNvbnN0IGRzdFNlc3Npb24gPSB0aGlzLnNlc3Npb25zW3Nlc3Npb25JZF07XG4gICAgcmV0dXJuIGRzdFNlc3Npb24gJiYgXy5pc0Z1bmN0aW9uKGRzdFNlc3Npb24ucHJveHlBY3RpdmUpICYmIGRzdFNlc3Npb24ucHJveHlBY3RpdmUoc2Vzc2lvbklkKTtcbiAgfVxuXG4gIGdldFByb3h5QXZvaWRMaXN0IChzZXNzaW9uSWQpIHtcbiAgICBjb25zdCBkc3RTZXNzaW9uID0gdGhpcy5zZXNzaW9uc1tzZXNzaW9uSWRdO1xuICAgIHJldHVybiBkc3RTZXNzaW9uID8gZHN0U2Vzc2lvbi5nZXRQcm94eUF2b2lkTGlzdCgpIDogW107XG4gIH1cblxuICBjYW5Qcm94eSAoc2Vzc2lvbklkKSB7XG4gICAgY29uc3QgZHN0U2Vzc2lvbiA9IHRoaXMuc2Vzc2lvbnNbc2Vzc2lvbklkXTtcbiAgICByZXR1cm4gZHN0U2Vzc2lvbiAmJiBkc3RTZXNzaW9uLmNhblByb3h5KHNlc3Npb25JZCk7XG4gIH1cbn1cblxuLy8gaGVscCBkZWNpZGUgd2hpY2ggY29tbWFuZHMgc2hvdWxkIGJlIHByb3hpZWQgdG8gc3ViLWRyaXZlcnMgYW5kIHdoaWNoXG4vLyBzaG91bGQgYmUgaGFuZGxlZCBieSB0aGlzLCBvdXIgdW1icmVsbGEgZHJpdmVyXG5mdW5jdGlvbiBpc0FwcGl1bURyaXZlckNvbW1hbmQgKGNtZCkge1xuICByZXR1cm4gIWlzU2Vzc2lvbkNvbW1hbmQoY21kKSB8fCBjbWQgPT09ICdkZWxldGVTZXNzaW9uJztcbn1cblxuZXhwb3J0IHsgQXBwaXVtRHJpdmVyIH07XG4iXSwiZmlsZSI6ImxpYi9hcHBpdW0uanMiLCJzb3VyY2VSb290IjoiLi4vLi4ifQ==
|
|
636
|
+
return !(0, base_driver_1.isSessionCommand)(cmd) || cmd === base_driver_1.DELETE_SESSION_COMMAND;
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Thrown when Appium tried to proxy a command using a driver's `proxyCommand` method but the
|
|
640
|
+
* method did not exist
|
|
641
|
+
*/
|
|
642
|
+
class NoDriverProxyCommandError extends Error {
|
|
643
|
+
constructor() {
|
|
644
|
+
super(`The default behavior for this command was to proxy, but the driver ` +
|
|
645
|
+
`did not have the 'proxyCommand' method defined. To fully support ` +
|
|
646
|
+
`plugins, drivers should have 'proxyCommand' set to a jwpProxy object's ` +
|
|
647
|
+
`'command()' method, in addition to the normal 'proxyReqRes'`);
|
|
648
|
+
/**
|
|
649
|
+
* @type {Readonly<string>}
|
|
650
|
+
*/
|
|
651
|
+
this.code = 'APPIUMERR_NO_DRIVER_PROXYCOMMAND';
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
exports.NoDriverProxyCommandError = NoDriverProxyCommandError;
|
|
655
|
+
/**
|
|
656
|
+
* @typedef {import('@appium/types').DriverData} DriverData
|
|
657
|
+
* @typedef {import('@appium/types').ServerArgs} DriverOpts
|
|
658
|
+
* @typedef {import('@appium/types').Constraints} Constraints
|
|
659
|
+
* @typedef {import('@appium/types').AppiumServer} AppiumServer
|
|
660
|
+
* @typedef {import('@appium/types').ExtensionType} ExtensionType
|
|
661
|
+
* @typedef {import('./extension/driver-config').DriverConfig} DriverConfig
|
|
662
|
+
* @typedef {import('@appium/types').PluginType} PluginType
|
|
663
|
+
* @typedef {import('@appium/types').DriverType} DriverType
|
|
664
|
+
* @typedef {import('@appium/types').StringRecord} StringRecord
|
|
665
|
+
* @typedef {import('@appium/types').ExternalDriver} ExternalDriver
|
|
666
|
+
* @typedef {import('@appium/types').PluginClass} PluginClass
|
|
667
|
+
* @typedef {import('@appium/types').Plugin} Plugin
|
|
668
|
+
* @typedef {import('@appium/types').DriverClass<import('@appium/types').Driver>} DriverClass
|
|
669
|
+
*/
|
|
670
|
+
/**
|
|
671
|
+
* @typedef {import('@appium/types').ISessionHandler<AppiumDriverConstraints,
|
|
672
|
+
* SessionHandlerCreateResult, SessionHandlerDeleteResult>} AppiumSessionHandler
|
|
673
|
+
*/
|
|
674
|
+
/**
|
|
675
|
+
* @typedef {SessionHandlerResult<[innerSessionId: string, caps:
|
|
676
|
+
* import('@appium/types').DriverCaps<Constraints>, protocol: string|undefined]>} SessionHandlerCreateResult
|
|
677
|
+
*/
|
|
678
|
+
/**
|
|
679
|
+
* @template {Constraints} C
|
|
680
|
+
* @typedef {import('@appium/types').Core<C>} Core
|
|
681
|
+
*/
|
|
682
|
+
/**
|
|
683
|
+
* @typedef {SessionHandlerResult<void>} SessionHandlerDeleteResult
|
|
684
|
+
*/
|
|
685
|
+
/**
|
|
686
|
+
* Used by {@linkcode AppiumDriver.createSession} and {@linkcode AppiumDriver.deleteSession} to describe
|
|
687
|
+
* result.
|
|
688
|
+
* @template V
|
|
689
|
+
* @typedef SessionHandlerResult
|
|
690
|
+
* @property {V} [value]
|
|
691
|
+
* @property {Error} [error]
|
|
692
|
+
* @property {string} [protocol]
|
|
693
|
+
*/
|
|
694
|
+
/**
|
|
695
|
+
* @typedef {typeof desiredCapabilityConstraints} AppiumDriverConstraints
|
|
696
|
+
* @typedef {import('@appium/types').W3CDriverCaps<AppiumDriverConstraints>} W3CAppiumDriverCaps
|
|
697
|
+
*/
|
|
698
|
+
//# sourceMappingURL=appium.js.map
|