appium 2.0.0-beta.45 → 2.0.0-beta.47

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