@appium/base-driver 8.7.2 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/build/lib/basedriver/capabilities.d.ts +11 -163
  2. package/build/lib/basedriver/capabilities.d.ts.map +1 -1
  3. package/build/lib/basedriver/capabilities.js +354 -236
  4. package/build/lib/basedriver/capabilities.js.map +1 -1
  5. package/build/lib/basedriver/commands/event.d.ts +7 -6
  6. package/build/lib/basedriver/commands/event.d.ts.map +1 -1
  7. package/build/lib/basedriver/commands/event.js +55 -35
  8. package/build/lib/basedriver/commands/event.js.map +1 -1
  9. package/build/lib/basedriver/commands/execute.d.ts +7 -6
  10. package/build/lib/basedriver/commands/execute.d.ts.map +1 -1
  11. package/build/lib/basedriver/commands/execute.js +66 -58
  12. package/build/lib/basedriver/commands/execute.js.map +1 -1
  13. package/build/lib/basedriver/commands/find.d.ts +9 -7
  14. package/build/lib/basedriver/commands/find.d.ts.map +1 -1
  15. package/build/lib/basedriver/commands/find.js +102 -54
  16. package/build/lib/basedriver/commands/find.js.map +1 -1
  17. package/build/lib/basedriver/commands/index.d.ts +3 -7
  18. package/build/lib/basedriver/commands/index.d.ts.map +1 -1
  19. package/build/lib/basedriver/commands/index.js +30 -33
  20. package/build/lib/basedriver/commands/index.js.map +1 -1
  21. package/build/lib/basedriver/commands/log.d.ts +8 -9
  22. package/build/lib/basedriver/commands/log.d.ts.map +1 -1
  23. package/build/lib/basedriver/commands/log.js +54 -38
  24. package/build/lib/basedriver/commands/log.js.map +1 -1
  25. package/build/lib/basedriver/commands/session.d.ts +7 -6
  26. package/build/lib/basedriver/commands/session.d.ts.map +1 -1
  27. package/build/lib/basedriver/commands/session.js +46 -39
  28. package/build/lib/basedriver/commands/session.js.map +1 -1
  29. package/build/lib/basedriver/commands/settings.d.ts +7 -7
  30. package/build/lib/basedriver/commands/settings.d.ts.map +1 -1
  31. package/build/lib/basedriver/commands/settings.js +35 -28
  32. package/build/lib/basedriver/commands/settings.js.map +1 -1
  33. package/build/lib/basedriver/commands/timeout.d.ts +7 -5
  34. package/build/lib/basedriver/commands/timeout.d.ts.map +1 -1
  35. package/build/lib/basedriver/commands/timeout.js +144 -162
  36. package/build/lib/basedriver/commands/timeout.js.map +1 -1
  37. package/build/lib/basedriver/core.d.ts +6 -157
  38. package/build/lib/basedriver/core.d.ts.map +1 -1
  39. package/build/lib/basedriver/core.js +361 -230
  40. package/build/lib/basedriver/core.js.map +1 -1
  41. package/build/lib/basedriver/desired-caps.js +80 -110
  42. package/build/lib/basedriver/desired-caps.js.map +1 -1
  43. package/build/lib/basedriver/device-settings.js +57 -62
  44. package/build/lib/basedriver/device-settings.js.map +1 -1
  45. package/build/lib/basedriver/driver.d.ts +21 -267
  46. package/build/lib/basedriver/driver.d.ts.map +1 -1
  47. package/build/lib/basedriver/driver.js +362 -258
  48. package/build/lib/basedriver/driver.js.map +1 -1
  49. package/build/lib/basedriver/helpers.js +500 -495
  50. package/build/lib/basedriver/helpers.js.map +1 -1
  51. package/build/lib/basedriver/logger.d.ts +1 -1
  52. package/build/lib/basedriver/logger.d.ts.map +1 -1
  53. package/build/lib/basedriver/logger.js +5 -15
  54. package/build/lib/basedriver/logger.js.map +1 -1
  55. package/build/lib/constants.js +14 -14
  56. package/build/lib/constants.js.map +1 -1
  57. package/build/lib/express/crash.js +8 -15
  58. package/build/lib/express/crash.js.map +1 -1
  59. package/build/lib/express/express-logging.js +49 -59
  60. package/build/lib/express/express-logging.js.map +1 -1
  61. package/build/lib/express/idempotency.js +125 -177
  62. package/build/lib/express/idempotency.js.map +1 -1
  63. package/build/lib/express/logger.d.ts +1 -1
  64. package/build/lib/express/logger.d.ts.map +1 -1
  65. package/build/lib/express/logger.js +5 -15
  66. package/build/lib/express/logger.js.map +1 -1
  67. package/build/lib/express/middleware.js +82 -107
  68. package/build/lib/express/middleware.js.map +1 -1
  69. package/build/lib/express/server.d.ts +17 -5
  70. package/build/lib/express/server.d.ts.map +1 -1
  71. package/build/lib/express/server.js +259 -224
  72. package/build/lib/express/server.js.map +1 -1
  73. package/build/lib/express/static.js +64 -81
  74. package/build/lib/express/static.js.map +1 -1
  75. package/build/lib/express/websocket.js +115 -87
  76. package/build/lib/express/websocket.js.map +1 -1
  77. package/build/lib/helpers/capabilities.d.ts +1 -59
  78. package/build/lib/helpers/capabilities.d.ts.map +1 -1
  79. package/build/lib/helpers/capabilities.js +72 -69
  80. package/build/lib/helpers/capabilities.js.map +1 -1
  81. package/build/lib/index.js +64 -180
  82. package/build/lib/index.js.map +1 -1
  83. package/build/lib/jsonwp-proxy/protocol-converter.js +215 -227
  84. package/build/lib/jsonwp-proxy/protocol-converter.js.map +1 -1
  85. package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
  86. package/build/lib/jsonwp-proxy/proxy.js +355 -393
  87. package/build/lib/jsonwp-proxy/proxy.js.map +1 -1
  88. package/build/lib/jsonwp-status/status.js +119 -130
  89. package/build/lib/jsonwp-status/status.js.map +1 -1
  90. package/build/lib/protocol/errors.d.ts +135 -32
  91. package/build/lib/protocol/errors.d.ts.map +1 -1
  92. package/build/lib/protocol/errors.js +871 -919
  93. package/build/lib/protocol/errors.js.map +1 -1
  94. package/build/lib/protocol/helpers.js +37 -37
  95. package/build/lib/protocol/helpers.js.map +1 -1
  96. package/build/lib/protocol/index.js +22 -109
  97. package/build/lib/protocol/index.js.map +1 -1
  98. package/build/lib/protocol/protocol.js +394 -350
  99. package/build/lib/protocol/protocol.js.map +1 -1
  100. package/build/lib/protocol/routes.d.ts +1238 -4
  101. package/build/lib/protocol/routes.d.ts.map +1 -1
  102. package/build/lib/protocol/routes.js +964 -1327
  103. package/build/lib/protocol/routes.js.map +1 -1
  104. package/build/lib/protocol/validators.js +32 -39
  105. package/build/lib/protocol/validators.js.map +1 -1
  106. package/build/tsconfig.tsbuildinfo +1 -1
  107. package/lib/basedriver/capabilities.js +80 -39
  108. package/lib/basedriver/commands/event.js +10 -5
  109. package/lib/basedriver/commands/execute.js +14 -9
  110. package/lib/basedriver/commands/find.js +18 -12
  111. package/lib/basedriver/commands/index.js +21 -16
  112. package/lib/basedriver/commands/log.js +24 -18
  113. package/lib/basedriver/commands/session.js +10 -5
  114. package/lib/basedriver/commands/settings.js +9 -6
  115. package/lib/basedriver/commands/timeout.js +10 -4
  116. package/lib/basedriver/core.js +2 -3
  117. package/lib/basedriver/driver.js +29 -18
  118. package/lib/express/server.js +6 -3
  119. package/lib/protocol/errors.js +155 -44
  120. package/lib/protocol/routes.js +11 -7
  121. package/package.json +14 -16
@@ -1,546 +1,551 @@
1
1
  "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.configureApp = configureApp;
7
- exports.default = void 0;
8
- exports.duplicateKeys = duplicateKeys;
9
- exports.generateDriverLogPrefix = generateDriverLogPrefix;
10
- exports.isPackageOrBundle = isPackageOrBundle;
11
- exports.parseCapsArray = parseCapsArray;
12
-
13
- require("source-map-support/register");
14
-
15
- var _lodash = _interopRequireDefault(require("lodash"));
16
-
17
- var _path = _interopRequireDefault(require("path"));
18
-
19
- var _url = _interopRequireDefault(require("url"));
20
-
21
- var _logger = _interopRequireDefault(require("./logger"));
22
-
23
- var _support = require("@appium/support");
24
-
25
- var _lruCache = _interopRequireDefault(require("lru-cache"));
26
-
27
- var _asyncLock = _interopRequireDefault(require("async-lock"));
28
-
29
- var _axios = _interopRequireDefault(require("axios"));
30
-
31
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
32
-
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateDriverLogPrefix = exports.parseCapsArray = exports.duplicateKeys = exports.isPackageOrBundle = exports.configureApp = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const url_1 = __importDefault(require("url"));
10
+ const logger_1 = __importDefault(require("./logger"));
11
+ const support_1 = require("@appium/support");
12
+ const lru_cache_1 = __importDefault(require("lru-cache"));
13
+ const async_lock_1 = __importDefault(require("async-lock"));
14
+ const axios_1 = __importDefault(require("axios"));
33
15
  const IPA_EXT = '.ipa';
34
16
  const ZIP_EXTS = ['.zip', IPA_EXT];
35
17
  const ZIP_MIME_TYPES = ['application/zip', 'application/x-zip-compressed', 'multipart/x-zip'];
36
- const CACHED_APPS_MAX_AGE = 1000 * 60 * 60 * 24;
18
+ const CACHED_APPS_MAX_AGE = 1000 * 60 * 60 * 24; // ms
37
19
  const MAX_CACHED_APPS = 1024;
38
- const APPLICATIONS_CACHE = new _lruCache.default({
39
- max: MAX_CACHED_APPS,
40
- ttl: CACHED_APPS_MAX_AGE,
41
- updateAgeOnGet: true,
42
- dispose: (app, {
43
- fullPath
44
- }) => {
45
- _logger.default.info(`The application '${app}' cached at '${fullPath}' has ` + `expired after ${CACHED_APPS_MAX_AGE}ms`);
46
-
47
- if (fullPath) {
48
- _support.fs.rimraf(fullPath);
49
- }
50
- },
51
- noDisposeOnSet: true
20
+ const APPLICATIONS_CACHE = new lru_cache_1.default({
21
+ max: MAX_CACHED_APPS,
22
+ ttl: CACHED_APPS_MAX_AGE,
23
+ updateAgeOnGet: true,
24
+ dispose: (app, { fullPath }) => {
25
+ logger_1.default.info(`The application '${app}' cached at '${fullPath}' has ` +
26
+ `expired after ${CACHED_APPS_MAX_AGE}ms`);
27
+ if (fullPath) {
28
+ support_1.fs.rimraf(fullPath);
29
+ }
30
+ },
31
+ noDisposeOnSet: true,
52
32
  });
53
- const APPLICATIONS_CACHE_GUARD = new _asyncLock.default();
33
+ const APPLICATIONS_CACHE_GUARD = new async_lock_1.default();
54
34
  const SANITIZE_REPLACEMENT = '-';
55
35
  const DEFAULT_BASENAME = 'appium-app';
56
36
  const APP_DOWNLOAD_TIMEOUT_MS = 120 * 1000;
57
37
  process.on('exit', () => {
58
- if (APPLICATIONS_CACHE.size === 0) {
59
- return;
60
- }
61
-
62
- const appPaths = [...APPLICATIONS_CACHE.values()].map(({
63
- fullPath
64
- }) => fullPath);
65
-
66
- _logger.default.debug(`Performing cleanup of ${appPaths.length} cached ` + _support.util.pluralize('application', appPaths.length));
67
-
68
- for (const appPath of appPaths) {
69
- try {
70
- _support.fs.rimrafSync(appPath);
71
- } catch (e) {
72
- _logger.default.warn(e.message);
38
+ if (APPLICATIONS_CACHE.size === 0) {
39
+ return;
40
+ }
41
+ const appPaths = [...APPLICATIONS_CACHE.values()].map(({ fullPath }) => fullPath);
42
+ logger_1.default.debug(`Performing cleanup of ${appPaths.length} cached ` +
43
+ support_1.util.pluralize('application', appPaths.length));
44
+ for (const appPath of appPaths) {
45
+ try {
46
+ // Asynchronous calls are not supported in onExit handler
47
+ support_1.fs.rimrafSync(appPath);
48
+ }
49
+ catch (e) {
50
+ logger_1.default.warn(e.message);
51
+ }
73
52
  }
74
- }
75
53
  });
76
-
77
54
  async function retrieveHeaders(link) {
78
- try {
79
- return (await (0, _axios.default)({
80
- url: link,
81
- method: 'HEAD',
82
- timeout: 5000
83
- })).headers;
84
- } catch (e) {
85
- _logger.default.info(`Cannot send HEAD request to '${link}'. Original error: ${e.message}`);
86
- }
87
-
88
- return {};
55
+ try {
56
+ return (await (0, axios_1.default)({
57
+ url: link,
58
+ method: 'HEAD',
59
+ timeout: 5000,
60
+ })).headers;
61
+ }
62
+ catch (e) {
63
+ logger_1.default.info(`Cannot send HEAD request to '${link}'. Original error: ${e.message}`);
64
+ }
65
+ return {};
89
66
  }
90
-
91
67
  function getCachedApplicationPath(link, currentAppProps = {}, cachedAppInfo = {}) {
92
- const refresh = () => {
93
- _logger.default.debug(`A fresh copy of the application is going to be downloaded from ${link}`);
94
-
95
- return null;
96
- };
97
-
98
- if (!_lodash.default.isPlainObject(cachedAppInfo) || !_lodash.default.isPlainObject(currentAppProps)) {
99
- return refresh();
100
- }
101
-
102
- const {
103
- lastModified: currentModified,
104
- immutable: currentImmutable,
105
- maxAge: currentMaxAge
106
- } = currentAppProps;
107
- const {
108
- lastModified,
109
- immutable,
110
- timestamp,
111
- fullPath
112
- } = cachedAppInfo;
113
-
114
- if (lastModified && currentModified) {
115
- if (currentModified.getTime() <= lastModified.getTime()) {
116
- _logger.default.debug(`The application at ${link} has not been modified since ${lastModified}`);
117
-
118
- return fullPath;
68
+ const refresh = () => {
69
+ logger_1.default.debug(`A fresh copy of the application is going to be downloaded from ${link}`);
70
+ return null;
71
+ };
72
+ if (!lodash_1.default.isPlainObject(cachedAppInfo) || !lodash_1.default.isPlainObject(currentAppProps)) {
73
+ // if an invalid arg is passed then assume cache miss
74
+ return refresh();
119
75
  }
120
-
121
- _logger.default.debug(`The application at ${link} has been modified since ${lastModified}`);
122
-
123
- return refresh();
124
- }
125
-
126
- if (immutable && currentImmutable) {
127
- _logger.default.debug(`The application at ${link} is immutable`);
128
-
129
- return fullPath;
130
- }
131
-
132
- if (currentMaxAge && timestamp) {
133
- const msLeft = timestamp + currentMaxAge * 1000 - Date.now();
134
-
135
- if (msLeft > 0) {
136
- _logger.default.debug(`The cached application '${_path.default.basename(fullPath)}' will expire in ${msLeft / 1000}s`);
137
-
138
- return fullPath;
76
+ const { lastModified: currentModified, immutable: currentImmutable,
77
+ // maxAge is in seconds
78
+ maxAge: currentMaxAge, } = currentAppProps;
79
+ const {
80
+ // Date instance
81
+ lastModified,
82
+ // boolean
83
+ immutable,
84
+ // Unix time in milliseconds
85
+ timestamp, fullPath, } = cachedAppInfo;
86
+ if (lastModified && currentModified) {
87
+ if (currentModified.getTime() <= lastModified.getTime()) {
88
+ logger_1.default.debug(`The application at ${link} has not been modified since ${lastModified}`);
89
+ return fullPath;
90
+ }
91
+ logger_1.default.debug(`The application at ${link} has been modified since ${lastModified}`);
92
+ return refresh();
139
93
  }
140
-
141
- _logger.default.debug(`The cached application '${_path.default.basename(fullPath)}' has expired`);
142
- }
143
-
144
- return refresh();
94
+ if (immutable && currentImmutable) {
95
+ logger_1.default.debug(`The application at ${link} is immutable`);
96
+ return fullPath;
97
+ }
98
+ if (currentMaxAge && timestamp) {
99
+ const msLeft = timestamp + currentMaxAge * 1000 - Date.now();
100
+ if (msLeft > 0) {
101
+ logger_1.default.debug(`The cached application '${path_1.default.basename(fullPath)}' will expire in ${msLeft / 1000}s`);
102
+ return fullPath;
103
+ }
104
+ logger_1.default.debug(`The cached application '${path_1.default.basename(fullPath)}' has expired`);
105
+ }
106
+ return refresh();
145
107
  }
146
-
147
108
  function verifyAppExtension(app, supportedAppExtensions) {
148
- if (supportedAppExtensions.map(_lodash.default.toLower).includes(_lodash.default.toLower(_path.default.extname(app)))) {
149
- return app;
150
- }
151
-
152
- throw new Error(`New app path '${app}' did not have ` + `${_support.util.pluralize('extension', supportedAppExtensions.length, false)}: ` + supportedAppExtensions);
109
+ if (supportedAppExtensions.map(lodash_1.default.toLower).includes(lodash_1.default.toLower(path_1.default.extname(app)))) {
110
+ return app;
111
+ }
112
+ throw new Error(`New app path '${app}' did not have ` +
113
+ `${support_1.util.pluralize('extension', supportedAppExtensions.length, false)}: ` +
114
+ supportedAppExtensions);
153
115
  }
154
-
155
116
  async function calculateFolderIntegrity(folderPath) {
156
- return (await _support.fs.glob('**/*', {
157
- cwd: folderPath,
158
- strict: false,
159
- nosort: true
160
- })).length;
117
+ return (await support_1.fs.glob('**/*', { cwd: folderPath, strict: false, nosort: true })).length;
161
118
  }
162
-
163
119
  async function calculateFileIntegrity(filePath) {
164
- return await _support.fs.hash(filePath);
120
+ return await support_1.fs.hash(filePath);
165
121
  }
166
-
167
122
  async function isAppIntegrityOk(currentPath, expectedIntegrity = {}) {
168
- if (!(await _support.fs.exists(currentPath))) {
169
- return false;
170
- }
171
-
172
- return (await _support.fs.stat(currentPath)).isDirectory() ? (await calculateFolderIntegrity(currentPath)) >= (expectedIntegrity === null || expectedIntegrity === void 0 ? void 0 : expectedIntegrity.folder) : (await calculateFileIntegrity(currentPath)) === (expectedIntegrity === null || expectedIntegrity === void 0 ? void 0 : expectedIntegrity.file);
123
+ if (!(await support_1.fs.exists(currentPath))) {
124
+ return false;
125
+ }
126
+ // Folder integrity check is simple:
127
+ // Verify the previous amount of files is not greater than the current one.
128
+ // We don't want to use equality comparison because of an assumption that the OS might
129
+ // create some unwanted service files/cached inside of that folder or its subfolders.
130
+ // Ofc, validating the hash sum of each file (or at least of file path) would be much
131
+ // more precise, but we don't need to be very precise here and also don't want to
132
+ // overuse RAM and have a performance drop.
133
+ return (await support_1.fs.stat(currentPath)).isDirectory()
134
+ ? (await calculateFolderIntegrity(currentPath)) >= expectedIntegrity?.folder
135
+ : (await calculateFileIntegrity(currentPath)) === expectedIntegrity?.file;
173
136
  }
174
-
175
- async function configureApp(app, options = {}) {
176
- if (!_lodash.default.isString(app)) {
177
- return;
178
- }
179
-
180
- let supportedAppExtensions;
181
- const onPostProcess = !_lodash.default.isString(options) && !_lodash.default.isArray(options) ? options.onPostProcess : undefined;
182
-
183
- if (_lodash.default.isString(options)) {
184
- supportedAppExtensions = [options];
185
- } else if (_lodash.default.isArray(options)) {
186
- supportedAppExtensions = options;
187
- } else if (_lodash.default.isPlainObject(options)) {
188
- supportedAppExtensions = options.supportedExtensions;
189
- }
190
-
191
- if (_lodash.default.isEmpty(supportedAppExtensions)) {
192
- throw new Error(`One or more supported app extensions must be provided`);
193
- }
194
-
195
- let newApp = app;
196
- let shouldUnzipApp = false;
197
- let packageHash = null;
198
- let headers = null;
199
- const remoteAppProps = {
200
- lastModified: null,
201
- immutable: false,
202
- maxAge: null
203
- };
204
-
205
- const {
206
- protocol,
207
- pathname
208
- } = _url.default.parse(newApp);
209
-
210
- const isUrl = protocol === null ? false : ['http:', 'https:'].includes(protocol);
211
- const cachedAppInfo = APPLICATIONS_CACHE.get(app);
212
- return await APPLICATIONS_CACHE_GUARD.acquire(app, async () => {
213
- if (isUrl) {
214
- _logger.default.info(`Using downloadable app '${newApp}'`);
215
-
216
- headers = await retrieveHeaders(newApp);
217
-
218
- if (!_lodash.default.isEmpty(headers)) {
219
- if (headers['last-modified']) {
220
- remoteAppProps.lastModified = new Date(headers['last-modified']);
137
+ /**
138
+ * @typedef PostProcessOptions
139
+ * @property {?Object} cachedAppInfo The information about the previously cached app instance (if exists):
140
+ * - packageHash: SHA1 hash of the package if it is a file and not a folder
141
+ * - lastModified: Optional Date instance, the value of file's `Last-Modified` header
142
+ * - immutable: Optional boolean value. Contains true if the file has an `immutable` mark
143
+ * in `Cache-control` header
144
+ * - maxAge: Optional integer representation of `maxAge` parameter in `Cache-control` header
145
+ * - timestamp: The timestamp this item has been added to the cache (measured in Unix epoch
146
+ * milliseconds)
147
+ * - integrity: An object containing either `file` property with SHA1 hash of the file
148
+ * or `folder` property with total amount of cached files and subfolders
149
+ * - fullPath: the full path to the cached app
150
+ * @property {boolean} isUrl Whether the app has been downloaded from a remote URL
151
+ * @property {?Object} headers Optional headers object. Only present if `isUrl` is true and if the server
152
+ * responds to HEAD requests. All header names are normalized to lowercase.
153
+ * @property {string} appPath A string containing full path to the preprocessed application package (either
154
+ * downloaded or a local one)
155
+ */
156
+ /**
157
+ * @typedef PostProcessResult
158
+ * @property {string} appPath The full past to the post-processed application package on the
159
+ * local file system (might be a file or a folder path)
160
+ */
161
+ /**
162
+ * @typedef ConfigureAppOptions
163
+ * @property {(obj: PostProcessOptions) => (Promise<PostProcessResult|undefined>|PostProcessResult|undefined)} [onPostProcess]
164
+ * Optional function, which should be applied
165
+ * to the application after it is downloaded/preprocessed. This function may be async
166
+ * and is expected to accept single object parameter.
167
+ * The function is expected to either return a falsy value, which means the app must not be
168
+ * cached and a fresh copy of it is downloaded each time. If this function returns an object
169
+ * containing `appPath` property then the integrity of it will be verified and stored into
170
+ * the cache.
171
+ * @property {string[]} supportedExtensions List of supported application extensions (
172
+ * including starting dots). This property is mandatory and must not be empty.
173
+ */
174
+ /**
175
+ * Prepares an app to be used in an automated test. The app gets cached automatically
176
+ * if it is an archive or if it is downloaded from an URL.
177
+ * If the downloaded app has `.zip` extension, this method will unzip it.
178
+ * The unzip does not work when `onPostProcess` is provided.
179
+ *
180
+ * @param {string} app Either a full path to the app or a remote URL
181
+ * @param {string|string[]|ConfigureAppOptions} options
182
+ * @returns The full path to the resulting application bundle
183
+ */
184
+ async function configureApp(app, options = /** @type {ConfigureAppOptions} */ ({})) {
185
+ if (!lodash_1.default.isString(app)) {
186
+ // immediately shortcircuit if not given an app
187
+ return;
188
+ }
189
+ let supportedAppExtensions;
190
+ const onPostProcess = !lodash_1.default.isString(options) && !lodash_1.default.isArray(options) ? options.onPostProcess : undefined;
191
+ if (lodash_1.default.isString(options)) {
192
+ supportedAppExtensions = [options];
193
+ }
194
+ else if (lodash_1.default.isArray(options)) {
195
+ supportedAppExtensions = options;
196
+ }
197
+ else if (lodash_1.default.isPlainObject(options)) {
198
+ supportedAppExtensions = options.supportedExtensions;
199
+ }
200
+ if (lodash_1.default.isEmpty(supportedAppExtensions)) {
201
+ throw new Error(`One or more supported app extensions must be provided`);
202
+ }
203
+ let newApp = app;
204
+ let shouldUnzipApp = false;
205
+ let packageHash = null;
206
+ let headers = null;
207
+ /** @type {RemoteAppProps} */
208
+ const remoteAppProps = {
209
+ lastModified: null,
210
+ immutable: false,
211
+ maxAge: null,
212
+ };
213
+ const { protocol, pathname } = url_1.default.parse(newApp);
214
+ const isUrl = protocol === null ? false : ['http:', 'https:'].includes(protocol);
215
+ const cachedAppInfo = APPLICATIONS_CACHE.get(app);
216
+ return await APPLICATIONS_CACHE_GUARD.acquire(app, async () => {
217
+ if (isUrl) {
218
+ // Use the app from remote URL
219
+ logger_1.default.info(`Using downloadable app '${newApp}'`);
220
+ headers = await retrieveHeaders(newApp);
221
+ if (!lodash_1.default.isEmpty(headers)) {
222
+ if (headers['last-modified']) {
223
+ remoteAppProps.lastModified = new Date(headers['last-modified']);
224
+ }
225
+ logger_1.default.debug(`Last-Modified: ${headers['last-modified']}`);
226
+ if (headers['cache-control']) {
227
+ remoteAppProps.immutable = /\bimmutable\b/i.test(headers['cache-control']);
228
+ const maxAgeMatch = /\bmax-age=(\d+)\b/i.exec(headers['cache-control']);
229
+ if (maxAgeMatch) {
230
+ remoteAppProps.maxAge = parseInt(maxAgeMatch[1], 10);
231
+ }
232
+ }
233
+ logger_1.default.debug(`Cache-Control: ${headers['cache-control']}`);
234
+ }
235
+ const cachedPath = getCachedApplicationPath(app, remoteAppProps, cachedAppInfo);
236
+ if (cachedPath) {
237
+ if (await isAppIntegrityOk(cachedPath, cachedAppInfo?.integrity)) {
238
+ logger_1.default.info(`Reusing previously downloaded application at '${cachedPath}'`);
239
+ return verifyAppExtension(cachedPath, supportedAppExtensions);
240
+ }
241
+ logger_1.default.info(`The application at '${cachedPath}' does not exist anymore ` +
242
+ `or its integrity has been damaged. Deleting it from the internal cache`);
243
+ APPLICATIONS_CACHE.delete(app);
244
+ }
245
+ let fileName = null;
246
+ const basename = support_1.fs.sanitizeName(path_1.default.basename(decodeURIComponent(pathname ?? '')), {
247
+ replacement: SANITIZE_REPLACEMENT,
248
+ });
249
+ const extname = path_1.default.extname(basename);
250
+ // to determine if we need to unzip the app, we have a number of places
251
+ // to look: content type, content disposition, or the file extension
252
+ if (ZIP_EXTS.includes(extname)) {
253
+ fileName = basename;
254
+ shouldUnzipApp = true;
255
+ }
256
+ if (headers['content-type']) {
257
+ const ct = headers['content-type'];
258
+ logger_1.default.debug(`Content-Type: ${ct}`);
259
+ // the filetype may not be obvious for certain urls, so check the mime type too
260
+ if (ZIP_MIME_TYPES.some((mimeType) => new RegExp(`\\b${lodash_1.default.escapeRegExp(mimeType)}\\b`).test(ct))) {
261
+ if (!fileName) {
262
+ fileName = `${DEFAULT_BASENAME}.zip`;
263
+ }
264
+ shouldUnzipApp = true;
265
+ }
266
+ }
267
+ if (headers['content-disposition'] && /^attachment/i.test(headers['content-disposition'])) {
268
+ logger_1.default.debug(`Content-Disposition: ${headers['content-disposition']}`);
269
+ const match = /filename="([^"]+)/i.exec(headers['content-disposition']);
270
+ if (match) {
271
+ fileName = support_1.fs.sanitizeName(match[1], {
272
+ replacement: SANITIZE_REPLACEMENT,
273
+ });
274
+ shouldUnzipApp = shouldUnzipApp || ZIP_EXTS.includes(path_1.default.extname(fileName));
275
+ }
276
+ }
277
+ if (!fileName) {
278
+ // assign the default file name and the extension if none has been detected
279
+ const resultingName = basename
280
+ ? basename.substring(0, basename.length - extname.length)
281
+ : DEFAULT_BASENAME;
282
+ let resultingExt = extname;
283
+ if (!supportedAppExtensions.includes(resultingExt)) {
284
+ logger_1.default.info(`The current file extension '${resultingExt}' is not supported. ` +
285
+ `Defaulting to '${lodash_1.default.first(supportedAppExtensions)}'`);
286
+ resultingExt = /** @type {string} */ (lodash_1.default.first(supportedAppExtensions));
287
+ }
288
+ fileName = `${resultingName}${resultingExt}`;
289
+ }
290
+ const targetPath = await support_1.tempDir.path({
291
+ prefix: fileName,
292
+ suffix: '',
293
+ });
294
+ newApp = await downloadApp(newApp, targetPath);
221
295
  }
222
-
223
- _logger.default.debug(`Last-Modified: ${headers['last-modified']}`);
224
-
225
- if (headers['cache-control']) {
226
- remoteAppProps.immutable = /\bimmutable\b/i.test(headers['cache-control']);
227
- const maxAgeMatch = /\bmax-age=(\d+)\b/i.exec(headers['cache-control']);
228
-
229
- if (maxAgeMatch) {
230
- remoteAppProps.maxAge = parseInt(maxAgeMatch[1], 10);
231
- }
296
+ else if (await support_1.fs.exists(newApp)) {
297
+ // Use the local app
298
+ logger_1.default.info(`Using local app '${newApp}'`);
299
+ shouldUnzipApp = ZIP_EXTS.includes(path_1.default.extname(newApp));
232
300
  }
233
-
234
- _logger.default.debug(`Cache-Control: ${headers['cache-control']}`);
235
- }
236
-
237
- const cachedPath = getCachedApplicationPath(app, remoteAppProps, cachedAppInfo);
238
-
239
- if (cachedPath) {
240
- if (await isAppIntegrityOk(cachedPath, cachedAppInfo === null || cachedAppInfo === void 0 ? void 0 : cachedAppInfo.integrity)) {
241
- _logger.default.info(`Reusing previously downloaded application at '${cachedPath}'`);
242
-
243
- return verifyAppExtension(cachedPath, supportedAppExtensions);
301
+ else {
302
+ let errorMessage = `The application at '${newApp}' does not exist or is not accessible`;
303
+ // protocol value for 'C:\\temp' is 'c:', so we check the length as well
304
+ if (lodash_1.default.isString(protocol) && protocol.length > 2) {
305
+ errorMessage =
306
+ `The protocol '${protocol}' used in '${newApp}' is not supported. ` +
307
+ `Only http: and https: protocols are supported`;
308
+ }
309
+ throw new Error(errorMessage);
244
310
  }
245
-
246
- _logger.default.info(`The application at '${cachedPath}' does not exist anymore ` + `or its integrity has been damaged. Deleting it from the internal cache`);
247
-
248
- APPLICATIONS_CACHE.delete(app);
249
- }
250
-
251
- let fileName = null;
252
-
253
- const basename = _support.fs.sanitizeName(_path.default.basename(decodeURIComponent(pathname ?? '')), {
254
- replacement: SANITIZE_REPLACEMENT
255
- });
256
-
257
- const extname = _path.default.extname(basename);
258
-
259
- if (ZIP_EXTS.includes(extname)) {
260
- fileName = basename;
261
- shouldUnzipApp = true;
262
- }
263
-
264
- if (headers['content-type']) {
265
- const ct = headers['content-type'];
266
-
267
- _logger.default.debug(`Content-Type: ${ct}`);
268
-
269
- if (ZIP_MIME_TYPES.some(mimeType => new RegExp(`\\b${_lodash.default.escapeRegExp(mimeType)}\\b`).test(ct))) {
270
- if (!fileName) {
271
- fileName = `${DEFAULT_BASENAME}.zip`;
272
- }
273
-
274
- shouldUnzipApp = true;
275
- }
276
- }
277
-
278
- if (headers['content-disposition'] && /^attachment/i.test(headers['content-disposition'])) {
279
- _logger.default.debug(`Content-Disposition: ${headers['content-disposition']}`);
280
-
281
- const match = /filename="([^"]+)/i.exec(headers['content-disposition']);
282
-
283
- if (match) {
284
- fileName = _support.fs.sanitizeName(match[1], {
285
- replacement: SANITIZE_REPLACEMENT
286
- });
287
- shouldUnzipApp = shouldUnzipApp || ZIP_EXTS.includes(_path.default.extname(fileName));
311
+ const isPackageAFile = (await support_1.fs.stat(newApp)).isFile();
312
+ if (isPackageAFile) {
313
+ packageHash = await calculateFileIntegrity(newApp);
288
314
  }
289
- }
290
-
291
- if (!fileName) {
292
- const resultingName = basename ? basename.substring(0, basename.length - extname.length) : DEFAULT_BASENAME;
293
- let resultingExt = extname;
294
-
295
- if (!supportedAppExtensions.includes(resultingExt)) {
296
- _logger.default.info(`The current file extension '${resultingExt}' is not supported. ` + `Defaulting to '${_lodash.default.first(supportedAppExtensions)}'`);
297
-
298
- resultingExt = _lodash.default.first(supportedAppExtensions);
315
+ if (isPackageAFile && shouldUnzipApp && !lodash_1.default.isFunction(onPostProcess)) {
316
+ const archivePath = newApp;
317
+ if (packageHash === cachedAppInfo?.packageHash) {
318
+ const { fullPath } = cachedAppInfo;
319
+ if (await isAppIntegrityOk(fullPath, cachedAppInfo?.integrity)) {
320
+ if (archivePath !== app) {
321
+ await support_1.fs.rimraf(archivePath);
322
+ }
323
+ logger_1.default.info(`Will reuse previously cached application at '${fullPath}'`);
324
+ return verifyAppExtension(fullPath, supportedAppExtensions);
325
+ }
326
+ logger_1.default.info(`The application at '${fullPath}' does not exist anymore ` +
327
+ `or its integrity has been damaged. Deleting it from the cache`);
328
+ APPLICATIONS_CACHE.delete(app);
329
+ }
330
+ const tmpRoot = await support_1.tempDir.openDir();
331
+ try {
332
+ newApp = await unzipApp(archivePath, tmpRoot, supportedAppExtensions);
333
+ }
334
+ finally {
335
+ if (newApp !== archivePath && archivePath !== app) {
336
+ await support_1.fs.rimraf(archivePath);
337
+ }
338
+ }
339
+ logger_1.default.info(`Unzipped local app to '${newApp}'`);
299
340
  }
300
-
301
- fileName = `${resultingName}${resultingExt}`;
302
- }
303
-
304
- const targetPath = await _support.tempDir.path({
305
- prefix: fileName,
306
- suffix: ''
307
- });
308
- newApp = await downloadApp(newApp, targetPath);
309
- } else if (await _support.fs.exists(newApp)) {
310
- _logger.default.info(`Using local app '${newApp}'`);
311
-
312
- shouldUnzipApp = ZIP_EXTS.includes(_path.default.extname(newApp));
313
- } else {
314
- let errorMessage = `The application at '${newApp}' does not exist or is not accessible`;
315
-
316
- if (_lodash.default.isString(protocol) && protocol.length > 2) {
317
- errorMessage = `The protocol '${protocol}' used in '${newApp}' is not supported. ` + `Only http: and https: protocols are supported`;
318
- }
319
-
320
- throw new Error(errorMessage);
321
- }
322
-
323
- const isPackageAFile = (await _support.fs.stat(newApp)).isFile();
324
-
325
- if (isPackageAFile) {
326
- packageHash = await calculateFileIntegrity(newApp);
327
- }
328
-
329
- if (isPackageAFile && shouldUnzipApp && !_lodash.default.isFunction(onPostProcess)) {
330
- const archivePath = newApp;
331
-
332
- if (packageHash === (cachedAppInfo === null || cachedAppInfo === void 0 ? void 0 : cachedAppInfo.packageHash)) {
333
- const {
334
- fullPath
335
- } = cachedAppInfo;
336
-
337
- if (await isAppIntegrityOk(fullPath, cachedAppInfo === null || cachedAppInfo === void 0 ? void 0 : cachedAppInfo.integrity)) {
338
- if (archivePath !== app) {
339
- await _support.fs.rimraf(archivePath);
340
- }
341
-
342
- _logger.default.info(`Will reuse previously cached application at '${fullPath}'`);
343
-
344
- return verifyAppExtension(fullPath, supportedAppExtensions);
341
+ else if (!path_1.default.isAbsolute(newApp)) {
342
+ newApp = path_1.default.resolve(process.cwd(), newApp);
343
+ logger_1.default.warn(`The current application path '${app}' is not absolute ` +
344
+ `and has been rewritten to '${newApp}'. Consider using absolute paths rather than relative`);
345
+ app = newApp;
345
346
  }
346
-
347
- _logger.default.info(`The application at '${fullPath}' does not exist anymore ` + `or its integrity has been damaged. Deleting it from the cache`);
348
-
349
- APPLICATIONS_CACHE.delete(app);
350
- }
351
-
352
- const tmpRoot = await _support.tempDir.openDir();
353
-
354
- try {
355
- newApp = await unzipApp(archivePath, tmpRoot, supportedAppExtensions);
356
- } finally {
357
- if (newApp !== archivePath && archivePath !== app) {
358
- await _support.fs.rimraf(archivePath);
347
+ const storeAppInCache = async (appPathToCache) => {
348
+ const cachedFullPath = cachedAppInfo?.fullPath;
349
+ if (cachedFullPath && cachedFullPath !== appPathToCache) {
350
+ await support_1.fs.rimraf(cachedFullPath);
351
+ }
352
+ const integrity = {};
353
+ if ((await support_1.fs.stat(appPathToCache)).isDirectory()) {
354
+ integrity.folder = await calculateFolderIntegrity(appPathToCache);
355
+ }
356
+ else {
357
+ integrity.file = await calculateFileIntegrity(appPathToCache);
358
+ }
359
+ APPLICATIONS_CACHE.set(app, {
360
+ ...remoteAppProps,
361
+ timestamp: Date.now(),
362
+ packageHash,
363
+ integrity,
364
+ fullPath: appPathToCache,
365
+ });
366
+ return appPathToCache;
367
+ };
368
+ if (lodash_1.default.isFunction(onPostProcess)) {
369
+ const result = await onPostProcess({
370
+ cachedAppInfo: lodash_1.default.clone(cachedAppInfo),
371
+ isUrl,
372
+ headers: lodash_1.default.clone(headers),
373
+ appPath: newApp,
374
+ });
375
+ return !result?.appPath || app === result?.appPath || !(await support_1.fs.exists(result?.appPath))
376
+ ? newApp
377
+ : await storeAppInCache(result.appPath);
359
378
  }
360
- }
361
-
362
- _logger.default.info(`Unzipped local app to '${newApp}'`);
363
- } else if (!_path.default.isAbsolute(newApp)) {
364
- newApp = _path.default.resolve(process.cwd(), newApp);
365
-
366
- _logger.default.warn(`The current application path '${app}' is not absolute ` + `and has been rewritten to '${newApp}'. Consider using absolute paths rather than relative`);
367
-
368
- app = newApp;
369
- }
370
-
371
- const storeAppInCache = async appPathToCache => {
372
- const cachedFullPath = cachedAppInfo === null || cachedAppInfo === void 0 ? void 0 : cachedAppInfo.fullPath;
373
-
374
- if (cachedFullPath && cachedFullPath !== appPathToCache) {
375
- await _support.fs.rimraf(cachedFullPath);
376
- }
377
-
378
- const integrity = {};
379
-
380
- if ((await _support.fs.stat(appPathToCache)).isDirectory()) {
381
- integrity.folder = await calculateFolderIntegrity(appPathToCache);
382
- } else {
383
- integrity.file = await calculateFileIntegrity(appPathToCache);
384
- }
385
-
386
- APPLICATIONS_CACHE.set(app, { ...remoteAppProps,
387
- timestamp: Date.now(),
388
- packageHash,
389
- integrity,
390
- fullPath: appPathToCache
391
- });
392
- return appPathToCache;
393
- };
394
-
395
- if (_lodash.default.isFunction(onPostProcess)) {
396
- const result = await onPostProcess({
397
- cachedAppInfo: _lodash.default.clone(cachedAppInfo),
398
- isUrl,
399
- headers: _lodash.default.clone(headers),
400
- appPath: newApp
401
- });
402
- return !(result !== null && result !== void 0 && result.appPath) || app === (result === null || result === void 0 ? void 0 : result.appPath) || !(await _support.fs.exists(result === null || result === void 0 ? void 0 : result.appPath)) ? newApp : await storeAppInCache(result.appPath);
403
- }
404
-
405
- verifyAppExtension(newApp, supportedAppExtensions);
406
- return app !== newApp && (packageHash || _lodash.default.values(remoteAppProps).some(Boolean)) ? await storeAppInCache(newApp) : newApp;
407
- });
379
+ verifyAppExtension(newApp, supportedAppExtensions);
380
+ return app !== newApp && (packageHash || lodash_1.default.values(remoteAppProps).some(Boolean))
381
+ ? await storeAppInCache(newApp)
382
+ : newApp;
383
+ });
408
384
  }
409
-
385
+ exports.configureApp = configureApp;
410
386
  async function downloadApp(app, targetPath) {
411
- const {
412
- href
413
- } = _url.default.parse(app);
414
-
415
- try {
416
- await _support.net.downloadFile(href, targetPath, {
417
- timeout: APP_DOWNLOAD_TIMEOUT_MS
418
- });
419
- } catch (err) {
420
- throw new Error(`Unable to download the app: ${err.message}`);
421
- }
422
-
423
- return targetPath;
387
+ const { href } = url_1.default.parse(app);
388
+ try {
389
+ await support_1.net.downloadFile(href, targetPath, {
390
+ timeout: APP_DOWNLOAD_TIMEOUT_MS,
391
+ });
392
+ }
393
+ catch (err) {
394
+ throw new Error(`Unable to download the app: ${err.message}`);
395
+ }
396
+ return targetPath;
424
397
  }
425
-
398
+ /**
399
+ * Extracts the bundle from an archive into the given folder
400
+ *
401
+ * @param {string} zipPath Full path to the archive containing the bundle
402
+ * @param {string} dstRoot Full path to the folder where the extracted bundle
403
+ * should be placed
404
+ * @param {Array<string>|string} supportedAppExtensions The list of extensions
405
+ * the target application bundle supports, for example ['.apk', '.apks'] for
406
+ * Android packages
407
+ * @returns {Promise<string>} Full path to the bundle in the destination folder
408
+ * @throws {Error} If the given archive is invalid or no application bundles
409
+ * have been found inside
410
+ */
426
411
  async function unzipApp(zipPath, dstRoot, supportedAppExtensions) {
427
- await _support.zip.assertValidZip(zipPath);
428
-
429
- if (!_lodash.default.isArray(supportedAppExtensions)) {
430
- supportedAppExtensions = [supportedAppExtensions];
431
- }
432
-
433
- const tmpRoot = await _support.tempDir.openDir();
434
-
435
- try {
436
- _logger.default.debug(`Unzipping '${zipPath}'`);
437
-
438
- const timer = new _support.timing.Timer().start();
439
- const useSystemUnzipEnv = process.env.APPIUM_PREFER_SYSTEM_UNZIP;
440
- const useSystemUnzip = _lodash.default.isEmpty(useSystemUnzipEnv) || !['0', 'false'].includes(_lodash.default.toLower(useSystemUnzipEnv));
441
- const extractionOpts = {
442
- useSystemUnzip
443
- };
444
-
445
- if (_path.default.extname(zipPath) === IPA_EXT) {
446
- _logger.default.debug(`Enforcing UTF-8 encoding on the extracted file names for '${_path.default.basename(zipPath)}'`);
447
-
448
- extractionOpts.fileNamesEncoding = 'utf8';
412
+ await support_1.zip.assertValidZip(zipPath);
413
+ if (!lodash_1.default.isArray(supportedAppExtensions)) {
414
+ supportedAppExtensions = [supportedAppExtensions];
449
415
  }
450
-
451
- await _support.zip.extractAllTo(zipPath, tmpRoot, extractionOpts);
452
- const globPattern = `**/*.+(${supportedAppExtensions.map(ext => ext.replace(/^\./, '')).join('|')})`;
453
- const sortedBundleItems = (await _support.fs.glob(globPattern, {
454
- cwd: tmpRoot,
455
- strict: false
456
- })).sort((a, b) => a.split(_path.default.sep).length - b.split(_path.default.sep).length);
457
-
458
- if (_lodash.default.isEmpty(sortedBundleItems)) {
459
- _logger.default.errorAndThrow(`App unzipped OK, but we could not find any '${supportedAppExtensions}' ` + _support.util.pluralize('bundle', supportedAppExtensions.length, false) + ` in it. Make sure your archive contains at least one package having ` + `'${supportedAppExtensions}' ${_support.util.pluralize('extension', supportedAppExtensions.length, false)}`);
416
+ const tmpRoot = await support_1.tempDir.openDir();
417
+ try {
418
+ logger_1.default.debug(`Unzipping '${zipPath}'`);
419
+ const timer = new support_1.timing.Timer().start();
420
+ const useSystemUnzipEnv = process.env.APPIUM_PREFER_SYSTEM_UNZIP;
421
+ const useSystemUnzip = lodash_1.default.isEmpty(useSystemUnzipEnv) || !['0', 'false'].includes(lodash_1.default.toLower(useSystemUnzipEnv));
422
+ /**
423
+ * Attempt to use use the system `unzip` (e.g., `/usr/bin/unzip`) due
424
+ * to the significant performance improvement it provides over the native
425
+ * JS "unzip" implementation.
426
+ * @type {import('@appium/support/lib/zip').ExtractAllOptions}
427
+ */
428
+ const extractionOpts = { useSystemUnzip };
429
+ // https://github.com/appium/appium/issues/14100
430
+ if (path_1.default.extname(zipPath) === IPA_EXT) {
431
+ logger_1.default.debug(`Enforcing UTF-8 encoding on the extracted file names for '${path_1.default.basename(zipPath)}'`);
432
+ extractionOpts.fileNamesEncoding = 'utf8';
433
+ }
434
+ await support_1.zip.extractAllTo(zipPath, tmpRoot, extractionOpts);
435
+ const globPattern = `**/*.+(${supportedAppExtensions
436
+ .map((ext) => ext.replace(/^\./, ''))
437
+ .join('|')})`;
438
+ const sortedBundleItems = (await support_1.fs.glob(globPattern, {
439
+ cwd: tmpRoot,
440
+ strict: false,
441
+ // Get the top level match
442
+ })).sort((a, b) => a.split(path_1.default.sep).length - b.split(path_1.default.sep).length);
443
+ if (lodash_1.default.isEmpty(sortedBundleItems)) {
444
+ logger_1.default.errorAndThrow(`App unzipped OK, but we could not find any '${supportedAppExtensions}' ` +
445
+ support_1.util.pluralize('bundle', supportedAppExtensions.length, false) +
446
+ ` in it. Make sure your archive contains at least one package having ` +
447
+ `'${supportedAppExtensions}' ${support_1.util.pluralize('extension', supportedAppExtensions.length, false)}`);
448
+ }
449
+ logger_1.default.debug(`Extracted ${support_1.util.pluralize('bundle item', sortedBundleItems.length, true)} ` +
450
+ `from '${zipPath}' in ${Math.round(timer.getDuration().asMilliSeconds)}ms: ${sortedBundleItems}`);
451
+ const matchedBundle = /** @type {string} */ (lodash_1.default.first(sortedBundleItems));
452
+ logger_1.default.info(`Assuming '${matchedBundle}' is the correct bundle`);
453
+ const dstPath = path_1.default.resolve(dstRoot, path_1.default.basename(matchedBundle));
454
+ await support_1.fs.mv(path_1.default.resolve(tmpRoot, matchedBundle), dstPath, { mkdirp: true });
455
+ return dstPath;
456
+ }
457
+ finally {
458
+ await support_1.fs.rimraf(tmpRoot);
460
459
  }
461
-
462
- _logger.default.debug(`Extracted ${_support.util.pluralize('bundle item', sortedBundleItems.length, true)} ` + `from '${zipPath}' in ${Math.round(timer.getDuration().asMilliSeconds)}ms: ${sortedBundleItems}`);
463
-
464
- const matchedBundle = _lodash.default.first(sortedBundleItems);
465
-
466
- _logger.default.info(`Assuming '${matchedBundle}' is the correct bundle`);
467
-
468
- const dstPath = _path.default.resolve(dstRoot, _path.default.basename(matchedBundle));
469
-
470
- await _support.fs.mv(_path.default.resolve(tmpRoot, matchedBundle), dstPath, {
471
- mkdirp: true
472
- });
473
- return dstPath;
474
- } finally {
475
- await _support.fs.rimraf(tmpRoot);
476
- }
477
460
  }
478
-
479
461
  function isPackageOrBundle(app) {
480
- return /^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/.test(app);
462
+ return /^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/.test(app);
481
463
  }
482
-
464
+ exports.isPackageOrBundle = isPackageOrBundle;
465
+ /**
466
+ * Finds all instances 'firstKey' and create a duplicate with the key 'secondKey',
467
+ * Do the same thing in reverse. If we find 'secondKey', create a duplicate with the key 'firstKey'.
468
+ *
469
+ * This will cause keys to be overwritten if the object contains 'firstKey' and 'secondKey'.
470
+
471
+ * @param {*} input Any type of input
472
+ * @param {String} firstKey The first key to duplicate
473
+ * @param {String} secondKey The second key to duplicate
474
+ */
483
475
  function duplicateKeys(input, firstKey, secondKey) {
484
- if (_lodash.default.isArray(input)) {
485
- return input.map(item => duplicateKeys(item, firstKey, secondKey));
486
- }
487
-
488
- if (_lodash.default.isPlainObject(input)) {
489
- const resultObj = {};
490
-
491
- for (let [key, value] of _lodash.default.toPairs(input)) {
492
- const recursivelyCalledValue = duplicateKeys(value, firstKey, secondKey);
493
-
494
- if (key === firstKey) {
495
- resultObj[secondKey] = recursivelyCalledValue;
496
- } else if (key === secondKey) {
497
- resultObj[firstKey] = recursivelyCalledValue;
498
- }
499
-
500
- resultObj[key] = recursivelyCalledValue;
476
+ // If array provided, recursively call on all elements
477
+ if (lodash_1.default.isArray(input)) {
478
+ return input.map((item) => duplicateKeys(item, firstKey, secondKey));
501
479
  }
502
-
503
- return resultObj;
504
- }
505
-
506
- return input;
480
+ // If object, create duplicates for keys and then recursively call on values
481
+ if (lodash_1.default.isPlainObject(input)) {
482
+ const resultObj = {};
483
+ for (let [key, value] of lodash_1.default.toPairs(input)) {
484
+ const recursivelyCalledValue = duplicateKeys(value, firstKey, secondKey);
485
+ if (key === firstKey) {
486
+ resultObj[secondKey] = recursivelyCalledValue;
487
+ }
488
+ else if (key === secondKey) {
489
+ resultObj[firstKey] = recursivelyCalledValue;
490
+ }
491
+ resultObj[key] = recursivelyCalledValue;
492
+ }
493
+ return resultObj;
494
+ }
495
+ // Base case. Return primitives without doing anything.
496
+ return input;
507
497
  }
508
-
498
+ exports.duplicateKeys = duplicateKeys;
499
+ /**
500
+ * Takes a desired capability and tries to JSON.parse it as an array,
501
+ * and either returns the parsed array or a singleton array.
502
+ *
503
+ * @param {string|Array<String>} cap A desired capability
504
+ */
509
505
  function parseCapsArray(cap) {
510
- if (_lodash.default.isArray(cap)) {
511
- return cap;
512
- }
513
-
514
- let parsedCaps;
515
-
516
- try {
517
- parsedCaps = JSON.parse(cap);
518
-
519
- if (_lodash.default.isArray(parsedCaps)) {
520
- return parsedCaps;
506
+ if (lodash_1.default.isArray(cap)) {
507
+ return cap;
521
508
  }
522
- } catch (ign) {
523
- _logger.default.warn(`Failed to parse capability as JSON array`);
524
- }
525
-
526
- if (_lodash.default.isString(cap)) {
527
- return [cap];
528
- }
529
-
530
- throw new Error(`must provide a string or JSON Array; received ${cap}`);
509
+ let parsedCaps;
510
+ try {
511
+ parsedCaps = JSON.parse(cap);
512
+ if (lodash_1.default.isArray(parsedCaps)) {
513
+ return parsedCaps;
514
+ }
515
+ }
516
+ catch (ign) {
517
+ logger_1.default.warn(`Failed to parse capability as JSON array`);
518
+ }
519
+ if (lodash_1.default.isString(cap)) {
520
+ return [cap];
521
+ }
522
+ throw new Error(`must provide a string or JSON Array; received ${cap}`);
531
523
  }
532
-
524
+ exports.parseCapsArray = parseCapsArray;
525
+ /**
526
+ * Generate a string that uniquely describes driver instance
527
+ *
528
+ * @param {import('@appium/types').Core} obj driver instance
529
+ * @param {string?} sessionId session identifier (if exists)
530
+ * @returns {string}
531
+ */
533
532
  function generateDriverLogPrefix(obj, sessionId = null) {
534
- const instanceName = `${obj.constructor.name}@${_support.node.getObjectId(obj).substring(0, 4)}`;
535
- return sessionId ? `${instanceName} (${sessionId.substring(0, 8)})` : instanceName;
533
+ const instanceName = `${obj.constructor.name}@${support_1.node.getObjectId(obj).substring(0, 4)}`;
534
+ return sessionId ? `${instanceName} (${sessionId.substring(0, 8)})` : instanceName;
536
535
  }
537
-
538
- var _default = {
539
- configureApp,
540
- isPackageOrBundle,
541
- duplicateKeys,
542
- parseCapsArray,
543
- generateDriverLogPrefix
536
+ exports.generateDriverLogPrefix = generateDriverLogPrefix;
537
+ /** @type {import('@appium/types').DriverHelpers} */
538
+ exports.default = {
539
+ configureApp,
540
+ isPackageOrBundle,
541
+ duplicateKeys,
542
+ parseCapsArray,
543
+ generateDriverLogPrefix,
544
544
  };
545
- exports.default = _default;
546
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJJUEFfRVhUIiwiWklQX0VYVFMiLCJaSVBfTUlNRV9UWVBFUyIsIkNBQ0hFRF9BUFBTX01BWF9BR0UiLCJNQVhfQ0FDSEVEX0FQUFMiLCJBUFBMSUNBVElPTlNfQ0FDSEUiLCJMUlUiLCJtYXgiLCJ0dGwiLCJ1cGRhdGVBZ2VPbkdldCIsImRpc3Bvc2UiLCJhcHAiLCJmdWxsUGF0aCIsImxvZ2dlciIsImluZm8iLCJmcyIsInJpbXJhZiIsIm5vRGlzcG9zZU9uU2V0IiwiQVBQTElDQVRJT05TX0NBQ0hFX0dVQVJEIiwiQXN5bmNMb2NrIiwiU0FOSVRJWkVfUkVQTEFDRU1FTlQiLCJERUZBVUxUX0JBU0VOQU1FIiwiQVBQX0RPV05MT0FEX1RJTUVPVVRfTVMiLCJwcm9jZXNzIiwib24iLCJzaXplIiwiYXBwUGF0aHMiLCJ2YWx1ZXMiLCJtYXAiLCJkZWJ1ZyIsImxlbmd0aCIsInV0aWwiLCJwbHVyYWxpemUiLCJhcHBQYXRoIiwicmltcmFmU3luYyIsImUiLCJ3YXJuIiwibWVzc2FnZSIsInJldHJpZXZlSGVhZGVycyIsImxpbmsiLCJheGlvcyIsInVybCIsIm1ldGhvZCIsInRpbWVvdXQiLCJoZWFkZXJzIiwiZ2V0Q2FjaGVkQXBwbGljYXRpb25QYXRoIiwiY3VycmVudEFwcFByb3BzIiwiY2FjaGVkQXBwSW5mbyIsInJlZnJlc2giLCJfIiwiaXNQbGFpbk9iamVjdCIsImxhc3RNb2RpZmllZCIsImN1cnJlbnRNb2RpZmllZCIsImltbXV0YWJsZSIsImN1cnJlbnRJbW11dGFibGUiLCJtYXhBZ2UiLCJjdXJyZW50TWF4QWdlIiwidGltZXN0YW1wIiwiZ2V0VGltZSIsIm1zTGVmdCIsIkRhdGUiLCJub3ciLCJwYXRoIiwiYmFzZW5hbWUiLCJ2ZXJpZnlBcHBFeHRlbnNpb24iLCJzdXBwb3J0ZWRBcHBFeHRlbnNpb25zIiwidG9Mb3dlciIsImluY2x1ZGVzIiwiZXh0bmFtZSIsIkVycm9yIiwiY2FsY3VsYXRlRm9sZGVySW50ZWdyaXR5IiwiZm9sZGVyUGF0aCIsImdsb2IiLCJjd2QiLCJzdHJpY3QiLCJub3NvcnQiLCJjYWxjdWxhdGVGaWxlSW50ZWdyaXR5IiwiZmlsZVBhdGgiLCJoYXNoIiwiaXNBcHBJbnRlZ3JpdHlPayIsImN1cnJlbnRQYXRoIiwiZXhwZWN0ZWRJbnRlZ3JpdHkiLCJleGlzdHMiLCJzdGF0IiwiaXNEaXJlY3RvcnkiLCJmb2xkZXIiLCJmaWxlIiwiY29uZmlndXJlQXBwIiwib3B0aW9ucyIsImlzU3RyaW5nIiwib25Qb3N0UHJvY2VzcyIsImlzQXJyYXkiLCJ1bmRlZmluZWQiLCJzdXBwb3J0ZWRFeHRlbnNpb25zIiwiaXNFbXB0eSIsIm5ld0FwcCIsInNob3VsZFVuemlwQXBwIiwicGFja2FnZUhhc2giLCJyZW1vdGVBcHBQcm9wcyIsInByb3RvY29sIiwicGF0aG5hbWUiLCJwYXJzZSIsImlzVXJsIiwiZ2V0IiwiYWNxdWlyZSIsInRlc3QiLCJtYXhBZ2VNYXRjaCIsImV4ZWMiLCJwYXJzZUludCIsImNhY2hlZFBhdGgiLCJpbnRlZ3JpdHkiLCJkZWxldGUiLCJmaWxlTmFtZSIsInNhbml0aXplTmFtZSIsImRlY29kZVVSSUNvbXBvbmVudCIsInJlcGxhY2VtZW50IiwiY3QiLCJzb21lIiwibWltZVR5cGUiLCJSZWdFeHAiLCJlc2NhcGVSZWdFeHAiLCJtYXRjaCIsInJlc3VsdGluZ05hbWUiLCJzdWJzdHJpbmciLCJyZXN1bHRpbmdFeHQiLCJmaXJzdCIsInRhcmdldFBhdGgiLCJ0ZW1wRGlyIiwicHJlZml4Iiwic3VmZml4IiwiZG93bmxvYWRBcHAiLCJlcnJvck1lc3NhZ2UiLCJpc1BhY2thZ2VBRmlsZSIsImlzRmlsZSIsImlzRnVuY3Rpb24iLCJhcmNoaXZlUGF0aCIsInRtcFJvb3QiLCJvcGVuRGlyIiwidW56aXBBcHAiLCJpc0Fic29sdXRlIiwicmVzb2x2ZSIsInN0b3JlQXBwSW5DYWNoZSIsImFwcFBhdGhUb0NhY2hlIiwiY2FjaGVkRnVsbFBhdGgiLCJzZXQiLCJyZXN1bHQiLCJjbG9uZSIsIkJvb2xlYW4iLCJocmVmIiwibmV0IiwiZG93bmxvYWRGaWxlIiwiZXJyIiwiemlwUGF0aCIsImRzdFJvb3QiLCJ6aXAiLCJhc3NlcnRWYWxpZFppcCIsInRpbWVyIiwidGltaW5nIiwiVGltZXIiLCJzdGFydCIsInVzZVN5c3RlbVVuemlwRW52IiwiZW52IiwiQVBQSVVNX1BSRUZFUl9TWVNURU1fVU5aSVAiLCJ1c2VTeXN0ZW1VbnppcCIsImV4dHJhY3Rpb25PcHRzIiwiZmlsZU5hbWVzRW5jb2RpbmciLCJleHRyYWN0QWxsVG8iLCJnbG9iUGF0dGVybiIsImV4dCIsInJlcGxhY2UiLCJqb2luIiwic29ydGVkQnVuZGxlSXRlbXMiLCJzb3J0IiwiYSIsImIiLCJzcGxpdCIsInNlcCIsImVycm9yQW5kVGhyb3ciLCJNYXRoIiwicm91bmQiLCJnZXREdXJhdGlvbiIsImFzTWlsbGlTZWNvbmRzIiwibWF0Y2hlZEJ1bmRsZSIsImRzdFBhdGgiLCJtdiIsIm1rZGlycCIsImlzUGFja2FnZU9yQnVuZGxlIiwiZHVwbGljYXRlS2V5cyIsImlucHV0IiwiZmlyc3RLZXkiLCJzZWNvbmRLZXkiLCJpdGVtIiwicmVzdWx0T2JqIiwia2V5IiwidmFsdWUiLCJ0b1BhaXJzIiwicmVjdXJzaXZlbHlDYWxsZWRWYWx1ZSIsInBhcnNlQ2Fwc0FycmF5IiwiY2FwIiwicGFyc2VkQ2FwcyIsIkpTT04iLCJpZ24iLCJnZW5lcmF0ZURyaXZlckxvZ1ByZWZpeCIsIm9iaiIsInNlc3Npb25JZCIsImluc3RhbmNlTmFtZSIsImNvbnN0cnVjdG9yIiwibmFtZSIsIm5vZGUiLCJnZXRPYmplY3RJZCJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL2xpYi9iYXNlZHJpdmVyL2hlbHBlcnMuanMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHVybCBmcm9tICd1cmwnO1xuaW1wb3J0IGxvZ2dlciBmcm9tICcuL2xvZ2dlcic7XG5pbXBvcnQge3RlbXBEaXIsIGZzLCB1dGlsLCB6aXAsIG5ldCwgdGltaW5nLCBub2RlfSBmcm9tICdAYXBwaXVtL3N1cHBvcnQnO1xuaW1wb3J0IExSVSBmcm9tICdscnUtY2FjaGUnO1xuaW1wb3J0IEFzeW5jTG9jayBmcm9tICdhc3luYy1sb2NrJztcbmltcG9ydCBheGlvcyBmcm9tICdheGlvcyc7XG5cbmNvbnN0IElQQV9FWFQgPSAnLmlwYSc7XG5jb25zdCBaSVBfRVhUUyA9IFsnLnppcCcsIElQQV9FWFRdO1xuY29uc3QgWklQX01JTUVfVFlQRVMgPSBbJ2FwcGxpY2F0aW9uL3ppcCcsICdhcHBsaWNhdGlvbi94LXppcC1jb21wcmVzc2VkJywgJ211bHRpcGFydC94LXppcCddO1xuY29uc3QgQ0FDSEVEX0FQUFNfTUFYX0FHRSA9IDEwMDAgKiA2MCAqIDYwICogMjQ7IC8vIG1zXG5jb25zdCBNQVhfQ0FDSEVEX0FQUFMgPSAxMDI0O1xuY29uc3QgQVBQTElDQVRJT05TX0NBQ0hFID0gbmV3IExSVSh7XG4gIG1heDogTUFYX0NBQ0hFRF9BUFBTLFxuICB0dGw6IENBQ0hFRF9BUFBTX01BWF9BR0UsIC8vIGV4cGlyZSBhZnRlciAyNCBob3Vyc1xuICB1cGRhdGVBZ2VPbkdldDogdHJ1ZSxcbiAgZGlzcG9zZTogKGFwcCwge2Z1bGxQYXRofSkgPT4ge1xuICAgIGxvZ2dlci5pbmZvKFxuICAgICAgYFRoZSBhcHBsaWNhdGlvbiAnJHthcHB9JyBjYWNoZWQgYXQgJyR7ZnVsbFBhdGh9JyBoYXMgYCArXG4gICAgICAgIGBleHBpcmVkIGFmdGVyICR7Q0FDSEVEX0FQUFNfTUFYX0FHRX1tc2BcbiAgICApO1xuICAgIGlmIChmdWxsUGF0aCkge1xuICAgICAgZnMucmltcmFmKGZ1bGxQYXRoKTtcbiAgICB9XG4gIH0sXG4gIG5vRGlzcG9zZU9uU2V0OiB0cnVlLFxufSk7XG5jb25zdCBBUFBMSUNBVElPTlNfQ0FDSEVfR1VBUkQgPSBuZXcgQXN5bmNMb2NrKCk7XG5jb25zdCBTQU5JVElaRV9SRVBMQUNFTUVOVCA9ICctJztcbmNvbnN0IERFRkFVTFRfQkFTRU5BTUUgPSAnYXBwaXVtLWFwcCc7XG5jb25zdCBBUFBfRE9XTkxPQURfVElNRU9VVF9NUyA9IDEyMCAqIDEwMDA7XG5cbnByb2Nlc3Mub24oJ2V4aXQnLCAoKSA9PiB7XG4gIGlmIChBUFBMSUNBVElPTlNfQ0FDSEUuc2l6ZSA9PT0gMCkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IGFwcFBhdGhzID0gWy4uLkFQUExJQ0FUSU9OU19DQUNIRS52YWx1ZXMoKV0ubWFwKCh7ZnVsbFBhdGh9KSA9PiBmdWxsUGF0aCk7XG4gIGxvZ2dlci5kZWJ1ZyhcbiAgICBgUGVyZm9ybWluZyBjbGVhbnVwIG9mICR7YXBwUGF0aHMubGVuZ3RofSBjYWNoZWQgYCArXG4gICAgICB1dGlsLnBsdXJhbGl6ZSgnYXBwbGljYXRpb24nLCBhcHBQYXRocy5sZW5ndGgpXG4gICk7XG4gIGZvciAoY29uc3QgYXBwUGF0aCBvZiBhcHBQYXRocykge1xuICAgIHRyeSB7XG4gICAgICAvLyBBc3luY2hyb25vdXMgY2FsbHMgYXJlIG5vdCBzdXBwb3J0ZWQgaW4gb25FeGl0IGhhbmRsZXJcbiAgICAgIGZzLnJpbXJhZlN5bmMoYXBwUGF0aCk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgbG9nZ2VyLndhcm4oZS5tZXNzYWdlKTtcbiAgICB9XG4gIH1cbn0pO1xuXG5hc3luYyBmdW5jdGlvbiByZXRyaWV2ZUhlYWRlcnMobGluaykge1xuICB0cnkge1xuICAgIHJldHVybiAoXG4gICAgICBhd2FpdCBheGlvcyh7XG4gICAgICAgIHVybDogbGluayxcbiAgICAgICAgbWV0aG9kOiAnSEVBRCcsXG4gICAgICAgIHRpbWVvdXQ6IDUwMDAsXG4gICAgICB9KVxuICAgICkuaGVhZGVycztcbiAgfSBjYXRjaCAoZSkge1xuICAgIGxvZ2dlci5pbmZvKGBDYW5ub3Qgc2VuZCBIRUFEIHJlcXVlc3QgdG8gJyR7bGlua30nLiBPcmlnaW5hbCBlcnJvcjogJHtlLm1lc3NhZ2V9YCk7XG4gIH1cbiAgcmV0dXJuIHt9O1xufVxuXG5mdW5jdGlvbiBnZXRDYWNoZWRBcHBsaWNhdGlvblBhdGgobGluaywgY3VycmVudEFwcFByb3BzID0ge30sIGNhY2hlZEFwcEluZm8gPSB7fSkge1xuICBjb25zdCByZWZyZXNoID0gKCkgPT4ge1xuICAgIGxvZ2dlci5kZWJ1ZyhgQSBmcmVzaCBjb3B5IG9mIHRoZSBhcHBsaWNhdGlvbiBpcyBnb2luZyB0byBiZSBkb3dubG9hZGVkIGZyb20gJHtsaW5rfWApO1xuICAgIHJldHVybiBudWxsO1xuICB9O1xuXG4gIGlmICghXy5pc1BsYWluT2JqZWN0KGNhY2hlZEFwcEluZm8pIHx8ICFfLmlzUGxhaW5PYmplY3QoY3VycmVudEFwcFByb3BzKSkge1xuICAgIC8vIGlmIGFuIGludmFsaWQgYXJnIGlzIHBhc3NlZCB0aGVuIGFzc3VtZSBjYWNoZSBtaXNzXG4gICAgcmV0dXJuIHJlZnJlc2goKTtcbiAgfVxuXG4gIGNvbnN0IHtcbiAgICBsYXN0TW9kaWZpZWQ6IGN1cnJlbnRNb2RpZmllZCxcbiAgICBpbW11dGFibGU6IGN1cnJlbnRJbW11dGFibGUsXG4gICAgLy8gbWF4QWdlIGlzIGluIHNlY29uZHNcbiAgICBtYXhBZ2U6IGN1cnJlbnRNYXhBZ2UsXG4gIH0gPSBjdXJyZW50QXBwUHJvcHM7XG4gIGNvbnN0IHtcbiAgICAvLyBEYXRlIGluc3RhbmNlXG4gICAgbGFzdE1vZGlmaWVkLFxuICAgIC8vIGJvb2xlYW5cbiAgICBpbW11dGFibGUsXG4gICAgLy8gVW5peCB0aW1lIGluIG1pbGxpc2Vjb25kc1xuICAgIHRpbWVzdGFtcCxcbiAgICBmdWxsUGF0aCxcbiAgfSA9IGNhY2hlZEFwcEluZm87XG4gIGlmIChsYXN0TW9kaWZpZWQgJiYgY3VycmVudE1vZGlmaWVkKSB7XG4gICAgaWYgKGN1cnJlbnRNb2RpZmllZC5nZXRUaW1lKCkgPD0gbGFzdE1vZGlmaWVkLmdldFRpbWUoKSkge1xuICAgICAgbG9nZ2VyLmRlYnVnKGBUaGUgYXBwbGljYXRpb24gYXQgJHtsaW5rfSBoYXMgbm90IGJlZW4gbW9kaWZpZWQgc2luY2UgJHtsYXN0TW9kaWZpZWR9YCk7XG4gICAgICByZXR1cm4gZnVsbFBhdGg7XG4gICAgfVxuICAgIGxvZ2dlci5kZWJ1ZyhgVGhlIGFwcGxpY2F0aW9uIGF0ICR7bGlua30gaGFzIGJlZW4gbW9kaWZpZWQgc2luY2UgJHtsYXN0TW9kaWZpZWR9YCk7XG4gICAgcmV0dXJuIHJlZnJlc2goKTtcbiAgfVxuICBpZiAoaW1tdXRhYmxlICYmIGN1cnJlbnRJbW11dGFibGUpIHtcbiAgICBsb2dnZXIuZGVidWcoYFRoZSBhcHBsaWNhdGlvbiBhdCAke2xpbmt9IGlzIGltbXV0YWJsZWApO1xuICAgIHJldHVybiBmdWxsUGF0aDtcbiAgfVxuICBpZiAoY3VycmVudE1heEFnZSAmJiB0aW1lc3RhbXApIHtcbiAgICBjb25zdCBtc0xlZnQgPSB0aW1lc3RhbXAgKyBjdXJyZW50TWF4QWdlICogMTAwMCAtIERhdGUubm93KCk7XG4gICAgaWYgKG1zTGVmdCA+IDApIHtcbiAgICAgIGxvZ2dlci5kZWJ1ZyhcbiAgICAgICAgYFRoZSBjYWNoZWQgYXBwbGljYXRpb24gJyR7cGF0aC5iYXNlbmFtZShmdWxsUGF0aCl9JyB3aWxsIGV4cGlyZSBpbiAke21zTGVmdCAvIDEwMDB9c2BcbiAgICAgICk7XG4gICAgICByZXR1cm4gZnVsbFBhdGg7XG4gICAgfVxuICAgIGxvZ2dlci5kZWJ1ZyhgVGhlIGNhY2hlZCBhcHBsaWNhdGlvbiAnJHtwYXRoLmJhc2VuYW1lKGZ1bGxQYXRoKX0nIGhhcyBleHBpcmVkYCk7XG4gIH1cbiAgcmV0dXJuIHJlZnJlc2goKTtcbn1cblxuZnVuY3Rpb24gdmVyaWZ5QXBwRXh0ZW5zaW9uKGFwcCwgc3VwcG9ydGVkQXBwRXh0ZW5zaW9ucykge1xuICBpZiAoc3VwcG9ydGVkQXBwRXh0ZW5zaW9ucy5tYXAoXy50b0xvd2VyKS5pbmNsdWRlcyhfLnRvTG93ZXIocGF0aC5leHRuYW1lKGFwcCkpKSkge1xuICAgIHJldHVybiBhcHA7XG4gIH1cbiAgdGhyb3cgbmV3IEVycm9yKFxuICAgIGBOZXcgYXBwIHBhdGggJyR7YXBwfScgZGlkIG5vdCBoYXZlIGAgK1xuICAgICAgYCR7dXRpbC5wbHVyYWxpemUoJ2V4dGVuc2lvbicsIHN1cHBvcnRlZEFwcEV4dGVuc2lvbnMubGVuZ3RoLCBmYWxzZSl9OiBgICtcbiAgICAgIHN1cHBvcnRlZEFwcEV4dGVuc2lvbnNcbiAgKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gY2FsY3VsYXRlRm9sZGVySW50ZWdyaXR5KGZvbGRlclBhdGgpIHtcbiAgcmV0dXJuIChhd2FpdCBmcy5nbG9iKCcqKi8qJywge2N3ZDogZm9sZGVyUGF0aCwgc3RyaWN0OiBmYWxzZSwgbm9zb3J0OiB0cnVlfSkpLmxlbmd0aDtcbn1cblxuYXN5bmMgZnVuY3Rpb24gY2FsY3VsYXRlRmlsZUludGVncml0eShmaWxlUGF0aCkge1xuICByZXR1cm4gYXdhaXQgZnMuaGFzaChmaWxlUGF0aCk7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGlzQXBwSW50ZWdyaXR5T2soY3VycmVudFBhdGgsIGV4cGVjdGVkSW50ZWdyaXR5ID0ge30pIHtcbiAgaWYgKCEoYXdhaXQgZnMuZXhpc3RzKGN1cnJlbnRQYXRoKSkpIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICAvLyBGb2xkZXIgaW50ZWdyaXR5IGNoZWNrIGlzIHNpbXBsZTpcbiAgLy8gVmVyaWZ5IHRoZSBwcmV2aW91cyBhbW91bnQgb2YgZmlsZXMgaXMgbm90IGdyZWF0ZXIgdGhhbiB0aGUgY3VycmVudCBvbmUuXG4gIC8vIFdlIGRvbid0IHdhbnQgdG8gdXNlIGVxdWFsaXR5IGNvbXBhcmlzb24gYmVjYXVzZSBvZiBhbiBhc3N1bXB0aW9uIHRoYXQgdGhlIE9TIG1pZ2h0XG4gIC8vIGNyZWF0ZSBzb21lIHVud2FudGVkIHNlcnZpY2UgZmlsZXMvY2FjaGVkIGluc2lkZSBvZiB0aGF0IGZvbGRlciBvciBpdHMgc3ViZm9sZGVycy5cbiAgLy8gT2ZjLCB2YWxpZGF0aW5nIHRoZSBoYXNoIHN1bSBvZiBlYWNoIGZpbGUgKG9yIGF0IGxlYXN0IG9mIGZpbGUgcGF0aCkgd291bGQgYmUgbXVjaFxuICAvLyBtb3JlIHByZWNpc2UsIGJ1dCB3ZSBkb24ndCBuZWVkIHRvIGJlIHZlcnkgcHJlY2lzZSBoZXJlIGFuZCBhbHNvIGRvbid0IHdhbnQgdG9cbiAgLy8gb3ZlcnVzZSBSQU0gYW5kIGhhdmUgYSBwZXJmb3JtYW5jZSBkcm9wLlxuICByZXR1cm4gKGF3YWl0IGZzLnN0YXQoY3VycmVudFBhdGgpKS5pc0RpcmVjdG9yeSgpXG4gICAgPyAoYXdhaXQgY2FsY3VsYXRlRm9sZGVySW50ZWdyaXR5KGN1cnJlbnRQYXRoKSkgPj0gZXhwZWN0ZWRJbnRlZ3JpdHk/LmZvbGRlclxuICAgIDogKGF3YWl0IGNhbGN1bGF0ZUZpbGVJbnRlZ3JpdHkoY3VycmVudFBhdGgpKSA9PT0gZXhwZWN0ZWRJbnRlZ3JpdHk/LmZpbGU7XG59XG5cbi8qKlxuICogQHR5cGVkZWYgUG9zdFByb2Nlc3NPcHRpb25zXG4gKiBAcHJvcGVydHkgez9PYmplY3R9IGNhY2hlZEFwcEluZm8gVGhlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBwcmV2aW91c2x5IGNhY2hlZCBhcHAgaW5zdGFuY2UgKGlmIGV4aXN0cyk6XG4gKiAgICAtIHBhY2thZ2VIYXNoOiBTSEExIGhhc2ggb2YgdGhlIHBhY2thZ2UgaWYgaXQgaXMgYSBmaWxlIGFuZCBub3QgYSBmb2xkZXJcbiAqICAgIC0gbGFzdE1vZGlmaWVkOiBPcHRpb25hbCBEYXRlIGluc3RhbmNlLCB0aGUgdmFsdWUgb2YgZmlsZSdzIGBMYXN0LU1vZGlmaWVkYCBoZWFkZXJcbiAqICAgIC0gaW1tdXRhYmxlOiBPcHRpb25hbCBib29sZWFuIHZhbHVlLiBDb250YWlucyB0cnVlIGlmIHRoZSBmaWxlIGhhcyBhbiBgaW1tdXRhYmxlYCBtYXJrXG4gKiAgICAgICAgICAgICAgICAgaW4gYENhY2hlLWNvbnRyb2xgIGhlYWRlclxuICogICAgLSBtYXhBZ2U6IE9wdGlvbmFsIGludGVnZXIgcmVwcmVzZW50YXRpb24gb2YgYG1heEFnZWAgcGFyYW1ldGVyIGluIGBDYWNoZS1jb250cm9sYCBoZWFkZXJcbiAqICAgIC0gdGltZXN0YW1wOiBUaGUgdGltZXN0YW1wIHRoaXMgaXRlbSBoYXMgYmVlbiBhZGRlZCB0byB0aGUgY2FjaGUgKG1lYXN1cmVkIGluIFVuaXggZXBvY2hcbiAqICAgICAgICAgICAgICAgICBtaWxsaXNlY29uZHMpXG4gKiAgICAtIGludGVncml0eTogQW4gb2JqZWN0IGNvbnRhaW5pbmcgZWl0aGVyIGBmaWxlYCBwcm9wZXJ0eSB3aXRoIFNIQTEgaGFzaCBvZiB0aGUgZmlsZVxuICogICAgICAgICAgICAgICAgIG9yIGBmb2xkZXJgIHByb3BlcnR5IHdpdGggdG90YWwgYW1vdW50IG9mIGNhY2hlZCBmaWxlcyBhbmQgc3ViZm9sZGVyc1xuICogICAgLSBmdWxsUGF0aDogdGhlIGZ1bGwgcGF0aCB0byB0aGUgY2FjaGVkIGFwcFxuICogQHByb3BlcnR5IHtib29sZWFufSBpc1VybCBXaGV0aGVyIHRoZSBhcHAgaGFzIGJlZW4gZG93bmxvYWRlZCBmcm9tIGEgcmVtb3RlIFVSTFxuICogQHByb3BlcnR5IHs/T2JqZWN0fSBoZWFkZXJzIE9wdGlvbmFsIGhlYWRlcnMgb2JqZWN0LiBPbmx5IHByZXNlbnQgaWYgYGlzVXJsYCBpcyB0cnVlIGFuZCBpZiB0aGUgc2VydmVyXG4gKiByZXNwb25kcyB0byBIRUFEIHJlcXVlc3RzLiBBbGwgaGVhZGVyIG5hbWVzIGFyZSBub3JtYWxpemVkIHRvIGxvd2VyY2FzZS5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBhcHBQYXRoIEEgc3RyaW5nIGNvbnRhaW5pbmcgZnVsbCBwYXRoIHRvIHRoZSBwcmVwcm9jZXNzZWQgYXBwbGljYXRpb24gcGFja2FnZSAoZWl0aGVyXG4gKiBkb3dubG9hZGVkIG9yIGEgbG9jYWwgb25lKVxuICovXG5cbi8qKlxuICogQHR5cGVkZWYgUG9zdFByb2Nlc3NSZXN1bHRcbiAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBhcHBQYXRoIFRoZSBmdWxsIHBhc3QgdG8gdGhlIHBvc3QtcHJvY2Vzc2VkIGFwcGxpY2F0aW9uIHBhY2thZ2Ugb24gdGhlXG4gKiBsb2NhbCBmaWxlIHN5c3RlbSAobWlnaHQgYmUgYSBmaWxlIG9yIGEgZm9sZGVyIHBhdGgpXG4gKi9cblxuLyoqXG4gKiBAdHlwZWRlZiBDb25maWd1cmVBcHBPcHRpb25zXG4gKiBAcHJvcGVydHkgeyhvYmo6IFBvc3RQcm9jZXNzT3B0aW9ucykgPT4gKFByb21pc2U8UG9zdFByb2Nlc3NSZXN1bHR8dW5kZWZpbmVkPnxQb3N0UHJvY2Vzc1Jlc3VsdHx1bmRlZmluZWQpfSBbb25Qb3N0UHJvY2Vzc11cbiAqIE9wdGlvbmFsIGZ1bmN0aW9uLCB3aGljaCBzaG91bGQgYmUgYXBwbGllZFxuICogdG8gdGhlIGFwcGxpY2F0aW9uIGFmdGVyIGl0IGlzIGRvd25sb2FkZWQvcHJlcHJvY2Vzc2VkLiBUaGlzIGZ1bmN0aW9uIG1heSBiZSBhc3luY1xuICogYW5kIGlzIGV4cGVjdGVkIHRvIGFjY2VwdCBzaW5nbGUgb2JqZWN0IHBhcmFtZXRlci5cbiAqIFRoZSBmdW5jdGlvbiBpcyBleHBlY3RlZCB0byBlaXRoZXIgcmV0dXJuIGEgZmFsc3kgdmFsdWUsIHdoaWNoIG1lYW5zIHRoZSBhcHAgbXVzdCBub3QgYmVcbiAqIGNhY2hlZCBhbmQgYSBmcmVzaCBjb3B5IG9mIGl0IGlzIGRvd25sb2FkZWQgZWFjaCB0aW1lLiBJZiB0aGlzIGZ1bmN0aW9uIHJldHVybnMgYW4gb2JqZWN0XG4gKiBjb250YWluaW5nIGBhcHBQYXRoYCBwcm9wZXJ0eSB0aGVuIHRoZSBpbnRlZ3JpdHkgb2YgaXQgd2lsbCBiZSB2ZXJpZmllZCBhbmQgc3RvcmVkIGludG9cbiAqIHRoZSBjYWNoZS5cbiAqIEBwcm9wZXJ0eSB7c3RyaW5nW119IHN1cHBvcnRlZEV4dGVuc2lvbnMgTGlzdCBvZiBzdXBwb3J0ZWQgYXBwbGljYXRpb24gZXh0ZW5zaW9ucyAoXG4gKiBpbmNsdWRpbmcgc3RhcnRpbmcgZG90cykuIFRoaXMgcHJvcGVydHkgaXMgbWFuZGF0b3J5IGFuZCBtdXN0IG5vdCBiZSBlbXB0eS5cbiAqL1xuXG4vKipcbiAqIFByZXBhcmVzIGFuIGFwcCB0byBiZSB1c2VkIGluIGFuIGF1dG9tYXRlZCB0ZXN0LiBUaGUgYXBwIGdldHMgY2FjaGVkIGF1dG9tYXRpY2FsbHlcbiAqIGlmIGl0IGlzIGFuIGFyY2hpdmUgb3IgaWYgaXQgaXMgZG93bmxvYWRlZCBmcm9tIGFuIFVSTC5cbiAqIElmIHRoZSBkb3dubG9hZGVkIGFwcCBoYXMgYC56aXBgIGV4dGVuc2lvbiwgdGhpcyBtZXRob2Qgd2lsbCB1bnppcCBpdC5cbiAqIFRoZSB1bnppcCBkb2VzIG5vdCB3b3JrIHdoZW4gYG9uUG9zdFByb2Nlc3NgIGlzIHByb3ZpZGVkLlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBhcHAgRWl0aGVyIGEgZnVsbCBwYXRoIHRvIHRoZSBhcHAgb3IgYSByZW1vdGUgVVJMXG4gKiBAcGFyYW0ge3N0cmluZ3xzdHJpbmdbXXxDb25maWd1cmVBcHBPcHRpb25zfSBvcHRpb25zXG4gKiBAcmV0dXJucyBUaGUgZnVsbCBwYXRoIHRvIHRoZSByZXN1bHRpbmcgYXBwbGljYXRpb24gYnVuZGxlXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGNvbmZpZ3VyZUFwcChhcHAsIG9wdGlvbnMgPSAvKiogQHR5cGUge0NvbmZpZ3VyZUFwcE9wdGlvbnN9ICovICh7fSkpIHtcbiAgaWYgKCFfLmlzU3RyaW5nKGFwcCkpIHtcbiAgICAvLyBpbW1lZGlhdGVseSBzaG9ydGNpcmN1aXQgaWYgbm90IGdpdmVuIGFuIGFwcFxuICAgIHJldHVybjtcbiAgfVxuXG4gIGxldCBzdXBwb3J0ZWRBcHBFeHRlbnNpb25zO1xuICBjb25zdCBvblBvc3RQcm9jZXNzID1cbiAgICAhXy5pc1N0cmluZyhvcHRpb25zKSAmJiAhXy5pc0FycmF5KG9wdGlvbnMpID8gb3B0aW9ucy5vblBvc3RQcm9jZXNzIDogdW5kZWZpbmVkO1xuXG4gIGlmIChfLmlzU3RyaW5nKG9wdGlvbnMpKSB7XG4gICAgc3VwcG9ydGVkQXBwRXh0ZW5zaW9ucyA9IFtvcHRpb25zXTtcbiAgfSBlbHNlIGlmIChfLmlzQXJyYXkob3B0aW9ucykpIHtcbiAgICBzdXBwb3J0ZWRBcHBFeHRlbnNpb25zID0gb3B0aW9ucztcbiAgfSBlbHNlIGlmIChfLmlzUGxhaW5PYmplY3Qob3B0aW9ucykpIHtcbiAgICBzdXBwb3J0ZWRBcHBFeHRlbnNpb25zID0gb3B0aW9ucy5zdXBwb3J0ZWRFeHRlbnNpb25zO1xuICB9XG4gIGlmIChfLmlzRW1wdHkoc3VwcG9ydGVkQXBwRXh0ZW5zaW9ucykpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYE9uZSBvciBtb3JlIHN1cHBvcnRlZCBhcHAgZXh0ZW5zaW9ucyBtdXN0IGJlIHByb3ZpZGVkYCk7XG4gIH1cblxuICBsZXQgbmV3QXBwID0gYXBwO1xuICBsZXQgc2hvdWxkVW56aXBBcHAgPSBmYWxzZTtcbiAgbGV0IHBhY2thZ2VIYXNoID0gbnVsbDtcbiAgbGV0IGhlYWRlcnMgPSBudWxsO1xuICAvKiogQHR5cGUge1JlbW90ZUFwcFByb3BzfSAqL1xuICBjb25zdCByZW1vdGVBcHBQcm9wcyA9IHtcbiAgICBsYXN0TW9kaWZpZWQ6IG51bGwsXG4gICAgaW1tdXRhYmxlOiBmYWxzZSxcbiAgICBtYXhBZ2U6IG51bGwsXG4gIH07XG4gIGNvbnN0IHtwcm90b2NvbCwgcGF0aG5hbWV9ID0gdXJsLnBhcnNlKG5ld0FwcCk7XG4gIGNvbnN0IGlzVXJsID0gcHJvdG9jb2wgPT09IG51bGwgPyBmYWxzZSA6IFsnaHR0cDonLCAnaHR0cHM6J10uaW5jbHVkZXMocHJvdG9jb2wpO1xuXG4gIGNvbnN0IGNhY2hlZEFwcEluZm8gPSBBUFBMSUNBVElPTlNfQ0FDSEUuZ2V0KGFwcCk7XG5cbiAgcmV0dXJuIGF3YWl0IEFQUExJQ0FUSU9OU19DQUNIRV9HVUFSRC5hY3F1aXJlKGFwcCwgYXN5bmMgKCkgPT4ge1xuICAgIGlmIChpc1VybCkge1xuICAgICAgLy8gVXNlIHRoZSBhcHAgZnJvbSByZW1vdGUgVVJMXG4gICAgICBsb2dnZXIuaW5mbyhgVXNpbmcgZG93bmxvYWRhYmxlIGFwcCAnJHtuZXdBcHB9J2ApO1xuICAgICAgaGVhZGVycyA9IGF3YWl0IHJldHJpZXZlSGVhZGVycyhuZXdBcHApO1xuICAgICAgaWYgKCFfLmlzRW1wdHkoaGVhZGVycykpIHtcbiAgICAgICAgaWYgKGhlYWRlcnNbJ2xhc3QtbW9kaWZpZWQnXSkge1xuICAgICAgICAgIHJlbW90ZUFwcFByb3BzLmxhc3RNb2RpZmllZCA9IG5ldyBEYXRlKGhlYWRlcnNbJ2xhc3QtbW9kaWZpZWQnXSk7XG4gICAgICAgIH1cbiAgICAgICAgbG9nZ2VyLmRlYnVnKGBMYXN0LU1vZGlmaWVkOiAke2hlYWRlcnNbJ2xhc3QtbW9kaWZpZWQnXX1gKTtcbiAgICAgICAgaWYgKGhlYWRlcnNbJ2NhY2hlLWNvbnRyb2wnXSkge1xuICAgICAgICAgIHJlbW90ZUFwcFByb3BzLmltbXV0YWJsZSA9IC9cXGJpbW11dGFibGVcXGIvaS50ZXN0KGhlYWRlcnNbJ2NhY2hlLWNvbnRyb2wnXSk7XG4gICAgICAgICAgY29uc3QgbWF4QWdlTWF0Y2ggPSAvXFxibWF4LWFnZT0oXFxkKylcXGIvaS5leGVjKGhlYWRlcnNbJ2NhY2hlLWNvbnRyb2wnXSk7XG4gICAgICAgICAgaWYgKG1heEFnZU1hdGNoKSB7XG4gICAgICAgICAgICByZW1vdGVBcHBQcm9wcy5tYXhBZ2UgPSBwYXJzZUludChtYXhBZ2VNYXRjaFsxXSwgMTApO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBsb2dnZXIuZGVidWcoYENhY2hlLUNvbnRyb2w6ICR7aGVhZGVyc1snY2FjaGUtY29udHJvbCddfWApO1xuICAgICAgfVxuICAgICAgY29uc3QgY2FjaGVkUGF0aCA9IGdldENhY2hlZEFwcGxpY2F0aW9uUGF0aChhcHAsIHJlbW90ZUFwcFByb3BzLCBjYWNoZWRBcHBJbmZvKTtcbiAgICAgIGlmIChjYWNoZWRQYXRoKSB7XG4gICAgICAgIGlmIChhd2FpdCBpc0FwcEludGVncml0eU9rKGNhY2hlZFBhdGgsIGNhY2hlZEFwcEluZm8/LmludGVncml0eSkpIHtcbiAgICAgICAgICBsb2dnZXIuaW5mbyhgUmV1c2luZyBwcmV2aW91c2x5IGRvd25sb2FkZWQgYXBwbGljYXRpb24gYXQgJyR7Y2FjaGVkUGF0aH0nYCk7XG4gICAgICAgICAgcmV0dXJuIHZlcmlmeUFwcEV4dGVuc2lvbihjYWNoZWRQYXRoLCBzdXBwb3J0ZWRBcHBFeHRlbnNpb25zKTtcbiAgICAgICAgfVxuICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICBgVGhlIGFwcGxpY2F0aW9uIGF0ICcke2NhY2hlZFBhdGh9JyBkb2VzIG5vdCBleGlzdCBhbnltb3JlIGAgK1xuICAgICAgICAgICAgYG9yIGl0cyBpbnRlZ3JpdHkgaGFzIGJlZW4gZGFtYWdlZC4gRGVsZXRpbmcgaXQgZnJvbSB0aGUgaW50ZXJuYWwgY2FjaGVgXG4gICAgICAgICk7XG4gICAgICAgIEFQUExJQ0FUSU9OU19DQUNIRS5kZWxldGUoYXBwKTtcbiAgICAgIH1cblxuICAgICAgbGV0IGZpbGVOYW1lID0gbnVsbDtcbiAgICAgIGNvbnN0IGJhc2VuYW1lID0gZnMuc2FuaXRpemVOYW1lKHBhdGguYmFzZW5hbWUoZGVjb2RlVVJJQ29tcG9uZW50KHBhdGhuYW1lID8/ICcnKSksIHtcbiAgICAgICAgcmVwbGFjZW1lbnQ6IFNBTklUSVpFX1JFUExBQ0VNRU5ULFxuICAgICAgfSk7XG4gICAgICBjb25zdCBleHRuYW1lID0gcGF0aC5leHRuYW1lKGJhc2VuYW1lKTtcbiAgICAgIC8vIHRvIGRldGVybWluZSBpZiB3ZSBuZWVkIHRvIHVuemlwIHRoZSBhcHAsIHdlIGhhdmUgYSBudW1iZXIgb2YgcGxhY2VzXG4gICAgICAvLyB0byBsb29rOiBjb250ZW50IHR5cGUsIGNvbnRlbnQgZGlzcG9zaXRpb24sIG9yIHRoZSBmaWxlIGV4dGVuc2lvblxuICAgICAgaWYgKFpJUF9FWFRTLmluY2x1ZGVzKGV4dG5hbWUpKSB7XG4gICAgICAgIGZpbGVOYW1lID0gYmFzZW5hbWU7XG4gICAgICAgIHNob3VsZFVuemlwQXBwID0gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIGlmIChoZWFkZXJzWydjb250ZW50LXR5cGUnXSkge1xuICAgICAgICBjb25zdCBjdCA9IGhlYWRlcnNbJ2NvbnRlbnQtdHlwZSddO1xuICAgICAgICBsb2dnZXIuZGVidWcoYENvbnRlbnQtVHlwZTogJHtjdH1gKTtcbiAgICAgICAgLy8gdGhlIGZpbGV0eXBlIG1heSBub3QgYmUgb2J2aW91cyBmb3IgY2VydGFpbiB1cmxzLCBzbyBjaGVjayB0aGUgbWltZSB0eXBlIHRvb1xuICAgICAgICBpZiAoXG4gICAgICAgICAgWklQX01JTUVfVFlQRVMuc29tZSgobWltZVR5cGUpID0+XG4gICAgICAgICAgICBuZXcgUmVnRXhwKGBcXFxcYiR7Xy5lc2NhcGVSZWdFeHAobWltZVR5cGUpfVxcXFxiYCkudGVzdChjdClcbiAgICAgICAgICApXG4gICAgICAgICkge1xuICAgICAgICAgIGlmICghZmlsZU5hbWUpIHtcbiAgICAgICAgICAgIGZpbGVOYW1lID0gYCR7REVGQVVMVF9CQVNFTkFNRX0uemlwYDtcbiAgICAgICAgICB9XG4gICAgICAgICAgc2hvdWxkVW56aXBBcHAgPSB0cnVlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBpZiAoaGVhZGVyc1snY29udGVudC1kaXNwb3NpdGlvbiddICYmIC9eYXR0YWNobWVudC9pLnRlc3QoaGVhZGVyc1snY29udGVudC1kaXNwb3NpdGlvbiddKSkge1xuICAgICAgICBsb2dnZXIuZGVidWcoYENvbnRlbnQtRGlzcG9zaXRpb246ICR7aGVhZGVyc1snY29udGVudC1kaXNwb3NpdGlvbiddfWApO1xuICAgICAgICBjb25zdCBtYXRjaCA9IC9maWxlbmFtZT1cIihbXlwiXSspL2kuZXhlYyhoZWFkZXJzWydjb250ZW50LWRpc3Bvc2l0aW9uJ10pO1xuICAgICAgICBpZiAobWF0Y2gpIHtcbiAgICAgICAgICBmaWxlTmFtZSA9IGZzLnNhbml0aXplTmFtZShtYXRjaFsxXSwge1xuICAgICAgICAgICAgcmVwbGFjZW1lbnQ6IFNBTklUSVpFX1JFUExBQ0VNRU5ULFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIHNob3VsZFVuemlwQXBwID0gc2hvdWxkVW56aXBBcHAgfHwgWklQX0VYVFMuaW5jbHVkZXMocGF0aC5leHRuYW1lKGZpbGVOYW1lKSk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIGlmICghZmlsZU5hbWUpIHtcbiAgICAgICAgLy8gYXNzaWduIHRoZSBkZWZhdWx0IGZpbGUgbmFtZSBhbmQgdGhlIGV4dGVuc2lvbiBpZiBub25lIGhhcyBiZWVuIGRldGVjdGVkXG4gICAgICAgIGNvbnN0IHJlc3VsdGluZ05hbWUgPSBiYXNlbmFtZVxuICAgICAgICAgID8gYmFzZW5hbWUuc3Vic3RyaW5nKDAsIGJhc2VuYW1lLmxlbmd0aCAtIGV4dG5hbWUubGVuZ3RoKVxuICAgICAgICAgIDogREVGQVVMVF9CQVNFTkFNRTtcbiAgICAgICAgbGV0IHJlc3VsdGluZ0V4dCA9IGV4dG5hbWU7XG4gICAgICAgIGlmICghc3VwcG9ydGVkQXBwRXh0ZW5zaW9ucy5pbmNsdWRlcyhyZXN1bHRpbmdFeHQpKSB7XG4gICAgICAgICAgbG9nZ2VyLmluZm8oXG4gICAgICAgICAgICBgVGhlIGN1cnJlbnQgZmlsZSBleHRlbnNpb24gJyR7cmVzdWx0aW5nRXh0fScgaXMgbm90IHN1cHBvcnRlZC4gYCArXG4gICAgICAgICAgICAgIGBEZWZhdWx0aW5nIHRvICcke18uZmlyc3Qoc3VwcG9ydGVkQXBwRXh0ZW5zaW9ucyl9J2BcbiAgICAgICAgICApO1xuICAgICAgICAgIHJlc3VsdGluZ0V4dCA9IC8qKiBAdHlwZSB7c3RyaW5nfSAqLyAoXy5maXJzdChzdXBwb3J0ZWRBcHBFeHRlbnNpb25zKSk7XG4gICAgICAgIH1cbiAgICAgICAgZmlsZU5hbWUgPSBgJHtyZXN1bHRpbmdOYW1lfSR7cmVzdWx0aW5nRXh0fWA7XG4gICAgICB9XG4gICAgICBjb25zdCB0YXJnZXRQYXRoID0gYXdhaXQgdGVtcERpci5wYXRoKHtcbiAgICAgICAgcHJlZml4OiBmaWxlTmFtZSxcbiAgICAgICAgc3VmZml4OiAnJyxcbiAgICAgIH0pO1xuICAgICAgbmV3QXBwID0gYXdhaXQgZG93bmxvYWRBcHAobmV3QXBwLCB0YXJnZXRQYXRoKTtcbiAgICB9IGVsc2UgaWYgKGF3YWl0IGZzLmV4aXN0cyhuZXdBcHApKSB7XG4gICAgICAvLyBVc2UgdGhlIGxvY2FsIGFwcFxuICAgICAgbG9nZ2VyLmluZm8oYFVzaW5nIGxvY2FsIGFwcCAnJHtuZXdBcHB9J2ApO1xuICAgICAgc2hvdWxkVW56aXBBcHAgPSBaSVBfRVhUUy5pbmNsdWRlcyhwYXRoLmV4dG5hbWUobmV3QXBwKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxldCBlcnJvck1lc3NhZ2UgPSBgVGhlIGFwcGxpY2F0aW9uIGF0ICcke25ld0FwcH0nIGRvZXMgbm90IGV4aXN0IG9yIGlzIG5vdCBhY2Nlc3NpYmxlYDtcbiAgICAgIC8vIHByb3RvY29sIHZhbHVlIGZvciAnQzpcXFxcdGVtcCcgaXMgJ2M6Jywgc28gd2UgY2hlY2sgdGhlIGxlbmd0aCBhcyB3ZWxsXG4gICAgICBpZiAoXy5pc1N0cmluZyhwcm90b2NvbCkgJiYgcHJvdG9jb2wubGVuZ3RoID4gMikge1xuICAgICAgICBlcnJvck1lc3NhZ2UgPVxuICAgICAgICAgIGBUaGUgcHJvdG9jb2wgJyR7cHJvdG9jb2x9JyB1c2VkIGluICcke25ld0FwcH0nIGlzIG5vdCBzdXBwb3J0ZWQuIGAgK1xuICAgICAgICAgIGBPbmx5IGh0dHA6IGFuZCBodHRwczogcHJvdG9jb2xzIGFyZSBzdXBwb3J0ZWRgO1xuICAgICAgfVxuICAgICAgdGhyb3cgbmV3IEVycm9yKGVycm9yTWVzc2FnZSk7XG4gICAgfVxuXG4gICAgY29uc3QgaXNQYWNrYWdlQUZpbGUgPSAoYXdhaXQgZnMuc3RhdChuZXdBcHApKS5pc0ZpbGUoKTtcbiAgICBpZiAoaXNQYWNrYWdlQUZpbGUpIHtcbiAgICAgIHBhY2thZ2VIYXNoID0gYXdhaXQgY2FsY3VsYXRlRmlsZUludGVncml0eShuZXdBcHApO1xuICAgIH1cblxuICAgIGlmIChpc1BhY2thZ2VBRmlsZSAmJiBzaG91bGRVbnppcEFwcCAmJiAhXy5pc0Z1bmN0aW9uKG9uUG9zdFByb2Nlc3MpKSB7XG4gICAgICBjb25zdCBhcmNoaXZlUGF0aCA9IG5ld0FwcDtcbiAgICAgIGlmIChwYWNrYWdlSGFzaCA9PT0gY2FjaGVkQXBwSW5mbz8ucGFja2FnZUhhc2gpIHtcbiAgICAgICAgY29uc3Qge2Z1bGxQYXRofSA9IGNhY2hlZEFwcEluZm87XG4gICAgICAgIGlmIChhd2FpdCBpc0FwcEludGVncml0eU9rKGZ1bGxQYXRoLCBjYWNoZWRBcHBJbmZvPy5pbnRlZ3JpdHkpKSB7XG4gICAgICAgICAgaWYgKGFyY2hpdmVQYXRoICE9PSBhcHApIHtcbiAgICAgICAgICAgIGF3YWl0IGZzLnJpbXJhZihhcmNoaXZlUGF0aCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIGxvZ2dlci5pbmZvKGBXaWxsIHJldXNlIHByZXZpb3VzbHkgY2FjaGVkIGFwcGxpY2F0aW9uIGF0ICcke2Z1bGxQYXRofSdgKTtcbiAgICAgICAgICByZXR1cm4gdmVyaWZ5QXBwRXh0ZW5zaW9uKGZ1bGxQYXRoLCBzdXBwb3J0ZWRBcHBFeHRlbnNpb25zKTtcbiAgICAgICAgfVxuICAgICAgICBsb2dnZXIuaW5mbyhcbiAgICAgICAgICBgVGhlIGFwcGxpY2F0aW9uIGF0ICcke2Z1bGxQYXRofScgZG9lcyBub3QgZXhpc3QgYW55bW9yZSBgICtcbiAgICAgICAgICAgIGBvciBpdHMgaW50ZWdyaXR5IGhhcyBiZWVuIGRhbWFnZWQuIERlbGV0aW5nIGl0IGZyb20gdGhlIGNhY2hlYFxuICAgICAgICApO1xuICAgICAgICBBUFBMSUNBVElPTlNfQ0FDSEUuZGVsZXRlKGFwcCk7XG4gICAgICB9XG4gICAgICBjb25zdCB0bXBSb290ID0gYXdhaXQgdGVtcERpci5vcGVuRGlyKCk7XG4gICAgICB0cnkge1xuICAgICAgICBuZXdBcHAgPSBhd2FpdCB1bnppcEFwcChhcmNoaXZlUGF0aCwgdG1wUm9vdCwgc3VwcG9ydGVkQXBwRXh0ZW5zaW9ucyk7XG4gICAgICB9IGZpbmFsbHkge1xuICAgICAgICBpZiAobmV3QXBwICE9PSBhcmNoaXZlUGF0aCAmJiBhcmNoaXZlUGF0aCAhPT0gYXBwKSB7XG4gICAgICAgICAgYXdhaXQgZnMucmltcmFmKGFyY2hpdmVQYXRoKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgbG9nZ2VyLmluZm8oYFVuemlwcGVkIGxvY2FsIGFwcCB0byAnJHtuZXdBcHB9J2ApO1xuICAgIH0gZWxzZSBpZiAoIXBhdGguaXNBYnNvbHV0ZShuZXdBcHApKSB7XG4gICAgICBuZXdBcHAgPSBwYXRoLnJlc29sdmUocHJvY2Vzcy5jd2QoKSwgbmV3QXBwKTtcbiAgICAgIGxvZ2dlci53YXJuKFxuICAgICAgICBgVGhlIGN1cnJlbnQgYXBwbGljYXRpb24gcGF0aCAnJHthcHB9JyBpcyBub3QgYWJzb2x1dGUgYCArXG4gICAgICAgICAgYGFuZCBoYXMgYmVlbiByZXdyaXR0ZW4gdG8gJyR7bmV3QXBwfScuIENvbnNpZGVyIHVzaW5nIGFic29sdXRlIHBhdGhzIHJhdGhlciB0aGFuIHJlbGF0aXZlYFxuICAgICAgKTtcbiAgICAgIGFwcCA9IG5ld0FwcDtcbiAgICB9XG5cbiAgICBjb25zdCBzdG9yZUFwcEluQ2FjaGUgPSBhc3luYyAoYXBwUGF0aFRvQ2FjaGUpID0+IHtcbiAgICAgIGNvbnN0IGNhY2hlZEZ1bGxQYXRoID0gY2FjaGVkQXBwSW5mbz8uZnVsbFBhdGg7XG4gICAgICBpZiAoY2FjaGVkRnVsbFBhdGggJiYgY2FjaGVkRnVsbFBhdGggIT09IGFwcFBhdGhUb0NhY2hlKSB7XG4gICAgICAgIGF3YWl0IGZzLnJpbXJhZihjYWNoZWRGdWxsUGF0aCk7XG4gICAgICB9XG4gICAgICBjb25zdCBpbnRlZ3JpdHkgPSB7fTtcbiAgICAgIGlmICgoYXdhaXQgZnMuc3RhdChhcHBQYXRoVG9DYWNoZSkpLmlzRGlyZWN0b3J5KCkpIHtcbiAgICAgICAgaW50ZWdyaXR5LmZvbGRlciA9IGF3YWl0IGNhbGN1bGF0ZUZvbGRlckludGVncml0eShhcHBQYXRoVG9DYWNoZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBpbnRlZ3JpdHkuZmlsZSA9IGF3YWl0IGNhbGN1bGF0ZUZpbGVJbnRlZ3JpdHkoYXBwUGF0aFRvQ2FjaGUpO1xuICAgICAgfVxuICAgICAgQVBQTElDQVRJT05TX0NBQ0hFLnNldChhcHAsIHtcbiAgICAgICAgLi4ucmVtb3RlQXBwUHJvcHMsXG4gICAgICAgIHRpbWVzdGFtcDogRGF0ZS5ub3coKSxcbiAgICAgICAgcGFja2FnZUhhc2gsXG4gICAgICAgIGludGVncml0eSxcbiAgICAgICAgZnVsbFBhdGg6IGFwcFBhdGhUb0NhY2hlLFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gYXBwUGF0aFRvQ2FjaGU7XG4gICAgfTtcblxuICAgIGlmIChfLmlzRnVuY3Rpb24ob25Qb3N0UHJvY2VzcykpIHtcbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IG9uUG9zdFByb2Nlc3Moe1xuICAgICAgICBjYWNoZWRBcHBJbmZvOiBfLmNsb25lKGNhY2hlZEFwcEluZm8pLFxuICAgICAgICBpc1VybCxcbiAgICAgICAgaGVhZGVyczogXy5jbG9uZShoZWFkZXJzKSxcbiAgICAgICAgYXBwUGF0aDogbmV3QXBwLFxuICAgICAgfSk7XG4gICAgICByZXR1cm4gIXJlc3VsdD8uYXBwUGF0aCB8fCBhcHAgPT09IHJlc3VsdD8uYXBwUGF0aCB8fCAhKGF3YWl0IGZzLmV4aXN0cyhyZXN1bHQ/LmFwcFBhdGgpKVxuICAgICAgICA/IG5ld0FwcFxuICAgICAgICA6IGF3YWl0IHN0b3JlQXBwSW5DYWNoZShyZXN1bHQuYXBwUGF0aCk7XG4gICAgfVxuXG4gICAgdmVyaWZ5QXBwRXh0ZW5zaW9uKG5ld0FwcCwgc3VwcG9ydGVkQXBwRXh0ZW5zaW9ucyk7XG4gICAgcmV0dXJuIGFwcCAhPT0gbmV3QXBwICYmIChwYWNrYWdlSGFzaCB8fCBfLnZhbHVlcyhyZW1vdGVBcHBQcm9wcykuc29tZShCb29sZWFuKSlcbiAgICAgID8gYXdhaXQgc3RvcmVBcHBJbkNhY2hlKG5ld0FwcClcbiAgICAgIDogbmV3QXBwO1xuICB9KTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gZG93bmxvYWRBcHAoYXBwLCB0YXJnZXRQYXRoKSB7XG4gIGNvbnN0IHtocmVmfSA9IHVybC5wYXJzZShhcHApO1xuICB0cnkge1xuICAgIGF3YWl0IG5ldC5kb3dubG9hZEZpbGUoaHJlZiwgdGFyZ2V0UGF0aCwge1xuICAgICAgdGltZW91dDogQVBQX0RPV05MT0FEX1RJTUVPVVRfTVMsXG4gICAgfSk7XG4gIH0gY2F0Y2ggKGVycikge1xuICAgIHRocm93IG5ldyBFcnJvcihgVW5hYmxlIHRvIGRvd25sb2FkIHRoZSBhcHA6ICR7ZXJyLm1lc3NhZ2V9YCk7XG4gIH1cbiAgcmV0dXJuIHRhcmdldFBhdGg7XG59XG5cbi8qKlxuICogRXh0cmFjdHMgdGhlIGJ1bmRsZSBmcm9tIGFuIGFyY2hpdmUgaW50byB0aGUgZ2l2ZW4gZm9sZGVyXG4gKlxuICogQHBhcmFtIHtzdHJpbmd9IHppcFBhdGggRnVsbCBwYXRoIHRvIHRoZSBhcmNoaXZlIGNvbnRhaW5pbmcgdGhlIGJ1bmRsZVxuICogQHBhcmFtIHtzdHJpbmd9IGRzdFJvb3QgRnVsbCBwYXRoIHRvIHRoZSBmb2xkZXIgd2hlcmUgdGhlIGV4dHJhY3RlZCBidW5kbGVcbiAqIHNob3VsZCBiZSBwbGFjZWRcbiAqIEBwYXJhbSB7QXJyYXk8c3RyaW5nPnxzdHJpbmd9IHN1cHBvcnRlZEFwcEV4dGVuc2lvbnMgVGhlIGxpc3Qgb2YgZXh0ZW5zaW9uc1xuICogdGhlIHRhcmdldCBhcHBsaWNhdGlvbiBidW5kbGUgc3VwcG9ydHMsIGZvciBleGFtcGxlIFsnLmFwaycsICcuYXBrcyddIGZvclxuICogQW5kcm9pZCBwYWNrYWdlc1xuICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gRnVsbCBwYXRoIHRvIHRoZSBidW5kbGUgaW4gdGhlIGRlc3RpbmF0aW9uIGZvbGRlclxuICogQHRocm93cyB7RXJyb3J9IElmIHRoZSBnaXZlbiBhcmNoaXZlIGlzIGludmFsaWQgb3Igbm8gYXBwbGljYXRpb24gYnVuZGxlc1xuICogaGF2ZSBiZWVuIGZvdW5kIGluc2lkZVxuICovXG5hc3luYyBmdW5jdGlvbiB1bnppcEFwcCh6aXBQYXRoLCBkc3RSb290LCBzdXBwb3J0ZWRBcHBFeHRlbnNpb25zKSB7XG4gIGF3YWl0IHppcC5hc3NlcnRWYWxpZFppcCh6aXBQYXRoKTtcblxuICBpZiAoIV8uaXNBcnJheShzdXBwb3J0ZWRBcHBFeHRlbnNpb25zKSkge1xuICAgIHN1cHBvcnRlZEFwcEV4dGVuc2lvbnMgPSBbc3VwcG9ydGVkQXBwRXh0ZW5zaW9uc107XG4gIH1cblxuICBjb25zdCB0bXBSb290ID0gYXdhaXQgdGVtcERpci5vcGVuRGlyKCk7XG4gIHRyeSB7XG4gICAgbG9nZ2VyLmRlYnVnKGBVbnppcHBpbmcgJyR7emlwUGF0aH0nYCk7XG4gICAgY29uc3QgdGltZXIgPSBuZXcgdGltaW5nLlRpbWVyKCkuc3RhcnQoKTtcbiAgICBjb25zdCB1c2VTeXN0ZW1VbnppcEVudiA9IHByb2Nlc3MuZW52LkFQUElVTV9QUkVGRVJfU1lTVEVNX1VOWklQO1xuICAgIGNvbnN0IHVzZVN5c3RlbVVuemlwID1cbiAgICAgIF8uaXNFbXB0eSh1c2VTeXN0ZW1VbnppcEVudikgfHwgIVsnMCcsICdmYWxzZSddLmluY2x1ZGVzKF8udG9Mb3dlcih1c2VTeXN0ZW1VbnppcEVudikpO1xuICAgIC8qKlxuICAgICAqIEF0dGVtcHQgdG8gdXNlIHVzZSB0aGUgc3lzdGVtIGB1bnppcGAgKGUuZy4sIGAvdXNyL2Jpbi91bnppcGApIGR1ZVxuICAgICAqIHRvIHRoZSBzaWduaWZpY2FudCBwZXJmb3JtYW5jZSBpbXByb3ZlbWVudCBpdCBwcm92aWRlcyBvdmVyIHRoZSBuYXRpdmVcbiAgICAgKiBKUyBcInVuemlwXCIgaW1wbGVtZW50YXRpb24uXG4gICAgICogQHR5cGUge2ltcG9ydCgnQGFwcGl1bS9zdXBwb3J0L2xpYi96aXAnKS5FeHRyYWN0QWxsT3B0aW9uc31cbiAgICAgKi9cbiAgICBjb25zdCBleHRyYWN0aW9uT3B0cyA9IHt1c2VTeXN0ZW1VbnppcH07XG4gICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL2FwcGl1bS9hcHBpdW0vaXNzdWVzLzE0MTAwXG4gICAgaWYgKHBhdGguZXh0bmFtZSh6aXBQYXRoKSA9PT0gSVBBX0VYVCkge1xuICAgICAgbG9nZ2VyLmRlYnVnKFxuICAgICAgICBgRW5mb3JjaW5nIFVURi04IGVuY29kaW5nIG9uIHRoZSBleHRyYWN0ZWQgZmlsZSBuYW1lcyBmb3IgJyR7cGF0aC5iYXNlbmFtZSh6aXBQYXRoKX0nYFxuICAgICAgKTtcbiAgICAgIGV4dHJhY3Rpb25PcHRzLmZpbGVOYW1lc0VuY29kaW5nID0gJ3V0ZjgnO1xuICAgIH1cbiAgICBhd2FpdCB6aXAuZXh0cmFjdEFsbFRvKHppcFBhdGgsIHRtcFJvb3QsIGV4dHJhY3Rpb25PcHRzKTtcbiAgICBjb25zdCBnbG9iUGF0dGVybiA9IGAqKi8qLisoJHtzdXBwb3J0ZWRBcHBFeHRlbnNpb25zXG4gICAgICAubWFwKChleHQpID0+IGV4dC5yZXBsYWNlKC9eXFwuLywgJycpKVxuICAgICAgLmpvaW4oJ3wnKX0pYDtcbiAgICBjb25zdCBzb3J0ZWRCdW5kbGVJdGVtcyA9IChcbiAgICAgIGF3YWl0IGZzLmdsb2IoZ2xvYlBhdHRlcm4sIHtcbiAgICAgICAgY3dkOiB0bXBSb290LFxuICAgICAgICBzdHJpY3Q6IGZhbHNlLFxuICAgICAgICAvLyBHZXQgdGhlIHRvcCBsZXZlbCBtYXRjaFxuICAgICAgfSlcbiAgICApLnNvcnQoKGEsIGIpID0+IGEuc3BsaXQocGF0aC5zZXApLmxlbmd0aCAtIGIuc3BsaXQocGF0aC5zZXApLmxlbmd0aCk7XG4gICAgaWYgKF8uaXNFbXB0eShzb3J0ZWRCdW5kbGVJdGVtcykpIHtcbiAgICAgIGxvZ2dlci5lcnJvckFuZFRocm93KFxuICAgICAgICBgQXBwIHVuemlwcGVkIE9LLCBidXQgd2UgY291bGQgbm90IGZpbmQgYW55ICcke3N1cHBvcnRlZEFwcEV4dGVuc2lvbnN9JyBgICtcbiAgICAgICAgICB1dGlsLnBsdXJhbGl6ZSgnYnVuZGxlJywgc3VwcG9ydGVkQXBwRXh0ZW5zaW9ucy5sZW5ndGgsIGZhbHNlKSArXG4gICAgICAgICAgYCBpbiBpdC4gTWFrZSBzdXJlIHlvdXIgYXJjaGl2ZSBjb250YWlucyBhdCBsZWFzdCBvbmUgcGFja2FnZSBoYXZpbmcgYCArXG4gICAgICAgICAgYCcke3N1cHBvcnRlZEFwcEV4dGVuc2lvbnN9JyAke3V0aWwucGx1cmFsaXplKFxuICAgICAgICAgICAgJ2V4dGVuc2lvbicsXG4gICAgICAgICAgICBzdXBwb3J0ZWRBcHBFeHRlbnNpb25zLmxlbmd0aCxcbiAgICAgICAgICAgIGZhbHNlXG4gICAgICAgICAgKX1gXG4gICAgICApO1xuICAgIH1cbiAgICBsb2dnZXIuZGVidWcoXG4gICAgICBgRXh0cmFjdGVkICR7dXRpbC5wbHVyYWxpemUoJ2J1bmRsZSBpdGVtJywgc29ydGVkQnVuZGxlSXRlbXMubGVuZ3RoLCB0cnVlKX0gYCArXG4gICAgICAgIGBmcm9tICcke3ppcFBhdGh9JyBpbiAke01hdGgucm91bmQoXG4gICAgICAgICAgdGltZXIuZ2V0RHVyYXRpb24oKS5hc01pbGxpU2Vjb25kc1xuICAgICAgICApfW1zOiAke3NvcnRlZEJ1bmRsZUl0ZW1zfWBcbiAgICApO1xuICAgIGNvbnN0IG1hdGNoZWRCdW5kbGUgPSAvKiogQHR5cGUge3N0cmluZ30gKi8gKF8uZmlyc3Qoc29ydGVkQnVuZGxlSXRlbXMpKTtcbiAgICBsb2dnZXIuaW5mbyhgQXNzdW1pbmcgJyR7bWF0Y2hlZEJ1bmRsZX0nIGlzIHRoZSBjb3JyZWN0IGJ1bmRsZWApO1xuICAgIGNvbnN0IGRzdFBhdGggPSBwYXRoLnJlc29sdmUoZHN0Um9vdCwgcGF0aC5iYXNlbmFtZShtYXRjaGVkQnVuZGxlKSk7XG4gICAgYXdhaXQgZnMubXYocGF0aC5yZXNvbHZlKHRtcFJvb3QsIG1hdGNoZWRCdW5kbGUpLCBkc3RQYXRoLCB7bWtkaXJwOiB0cnVlfSk7XG4gICAgcmV0dXJuIGRzdFBhdGg7XG4gIH0gZmluYWxseSB7XG4gICAgYXdhaXQgZnMucmltcmFmKHRtcFJvb3QpO1xuICB9XG59XG5cbmZ1bmN0aW9uIGlzUGFja2FnZU9yQnVuZGxlKGFwcCkge1xuICByZXR1cm4gL14oW2EtekEtWjAtOVxcLV9dK1xcLlthLXpBLVowLTlcXC1fXSspKyQvLnRlc3QoYXBwKTtcbn1cblxuLyoqXG4gKiBGaW5kcyBhbGwgaW5zdGFuY2VzICdmaXJzdEtleScgYW5kIGNyZWF0ZSBhIGR1cGxpY2F0ZSB3aXRoIHRoZSBrZXkgJ3NlY29uZEtleScsXG4gKiBEbyB0aGUgc2FtZSB0aGluZyBpbiByZXZlcnNlLiBJZiB3ZSBmaW5kICdzZWNvbmRLZXknLCBjcmVhdGUgYSBkdXBsaWNhdGUgd2l0aCB0aGUga2V5ICdmaXJzdEtleScuXG4gKlxuICogVGhpcyB3aWxsIGNhdXNlIGtleXMgdG8gYmUgb3ZlcndyaXR0ZW4gaWYgdGhlIG9iamVjdCBjb250YWlucyAnZmlyc3RLZXknIGFuZCAnc2Vjb25kS2V5Jy5cblxuICogQHBhcmFtIHsqfSBpbnB1dCBBbnkgdHlwZSBvZiBpbnB1dFxuICogQHBhcmFtIHtTdHJpbmd9IGZpcnN0S2V5IFRoZSBmaXJzdCBrZXkgdG8gZHVwbGljYXRlXG4gKiBAcGFyYW0ge1N0cmluZ30gc2Vjb25kS2V5IFRoZSBzZWNvbmQga2V5IHRvIGR1cGxpY2F0ZVxuICovXG5mdW5jdGlvbiBkdXBsaWNhdGVLZXlzKGlucHV0LCBmaXJzdEtleSwgc2Vjb25kS2V5KSB7XG4gIC8vIElmIGFycmF5IHByb3ZpZGVkLCByZWN1cnNpdmVseSBjYWxsIG9uIGFsbCBlbGVtZW50c1xuICBpZiAoXy5pc0FycmF5KGlucHV0KSkge1xuICAgIHJldHVybiBpbnB1dC5tYXAoKGl0ZW0pID0+IGR1cGxpY2F0ZUtleXMoaXRlbSwgZmlyc3RLZXksIHNlY29uZEtleSkpO1xuICB9XG5cbiAgLy8gSWYgb2JqZWN0LCBjcmVhdGUgZHVwbGljYXRlcyBmb3Iga2V5cyBhbmQgdGhlbiByZWN1cnNpdmVseSBjYWxsIG9uIHZhbHVlc1xuICBpZiAoXy5pc1BsYWluT2JqZWN0KGlucHV0KSkge1xuICAgIGNvbnN0IHJlc3VsdE9iaiA9IHt9O1xuICAgIGZvciAobGV0IFtrZXksIHZhbHVlXSBvZiBfLnRvUGFpcnMoaW5wdXQpKSB7XG4gICAgICBjb25zdCByZWN1cnNpdmVseUNhbGxlZFZhbHVlID0gZHVwbGljYXRlS2V5cyh2YWx1ZSwgZmlyc3RLZXksIHNlY29uZEtleSk7XG4gICAgICBpZiAoa2V5ID09PSBmaXJzdEtleSkge1xuICAgICAgICByZXN1bHRPYmpbc2Vjb25kS2V5XSA9IHJlY3Vyc2l2ZWx5Q2FsbGVkVmFsdWU7XG4gICAgICB9IGVsc2UgaWYgKGtleSA9PT0gc2Vjb25kS2V5KSB7XG4gICAgICAgIHJlc3VsdE9ialtmaXJzdEtleV0gPSByZWN1cnNpdmVseUNhbGxlZFZhbHVlO1xuICAgICAgfVxuICAgICAgcmVzdWx0T2JqW2tleV0gPSByZWN1cnNpdmVseUNhbGxlZFZhbHVlO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0T2JqO1xuICB9XG5cbiAgLy8gQmFzZSBjYXNlLiBSZXR1cm4gcHJpbWl0aXZlcyB3aXRob3V0IGRvaW5nIGFueXRoaW5nLlxuICByZXR1cm4gaW5wdXQ7XG59XG5cbi8qKlxuICogVGFrZXMgYSBkZXNpcmVkIGNhcGFiaWxpdHkgYW5kIHRyaWVzIHRvIEpTT04ucGFyc2UgaXQgYXMgYW4gYXJyYXksXG4gKiBhbmQgZWl0aGVyIHJldHVybnMgdGhlIHBhcnNlZCBhcnJheSBvciBhIHNpbmdsZXRvbiBhcnJheS5cbiAqXG4gKiBAcGFyYW0ge3N0cmluZ3xBcnJheTxTdHJpbmc+fSBjYXAgQSBkZXNpcmVkIGNhcGFiaWxpdHlcbiAqL1xuZnVuY3Rpb24gcGFyc2VDYXBzQXJyYXkoY2FwKSB7XG4gIGlmIChfLmlzQXJyYXkoY2FwKSkge1xuICAgIHJldHVybiBjYXA7XG4gIH1cblxuICBsZXQgcGFyc2VkQ2FwcztcbiAgdHJ5IHtcbiAgICBwYXJzZWRDYXBzID0gSlNPTi5wYXJzZShjYXApO1xuICAgIGlmIChfLmlzQXJyYXkocGFyc2VkQ2FwcykpIHtcbiAgICAgIHJldHVybiBwYXJzZWRDYXBzO1xuICAgIH1cbiAgfSBjYXRjaCAoaWduKSB7XG4gICAgbG9nZ2VyLndhcm4oYEZhaWxlZCB0byBwYXJzZSBjYXBhYmlsaXR5IGFzIEpTT04gYXJyYXlgKTtcbiAgfVxuICBpZiAoXy5pc1N0cmluZyhjYXApKSB7XG4gICAgcmV0dXJuIFtjYXBdO1xuICB9XG4gIHRocm93IG5ldyBFcnJvcihgbXVzdCBwcm92aWRlIGEgc3RyaW5nIG9yIEpTT04gQXJyYXk7IHJlY2VpdmVkICR7Y2FwfWApO1xufVxuXG4vKipcbiAqIEdlbmVyYXRlIGEgc3RyaW5nIHRoYXQgdW5pcXVlbHkgZGVzY3JpYmVzIGRyaXZlciBpbnN0YW5jZVxuICpcbiAqIEBwYXJhbSB7aW1wb3J0KCdAYXBwaXVtL3R5cGVzJykuQ29yZX0gb2JqIGRyaXZlciBpbnN0YW5jZVxuICogQHBhcmFtIHtzdHJpbmc/fSBzZXNzaW9uSWQgc2Vzc2lvbiBpZGVudGlmaWVyIChpZiBleGlzdHMpXG4gKiBAcmV0dXJucyB7c3RyaW5nfVxuICovXG5mdW5jdGlvbiBnZW5lcmF0ZURyaXZlckxvZ1ByZWZpeChvYmosIHNlc3Npb25JZCA9IG51bGwpIHtcbiAgY29uc3QgaW5zdGFuY2VOYW1lID0gYCR7b2JqLmNvbnN0cnVjdG9yLm5hbWV9QCR7bm9kZS5nZXRPYmplY3RJZChvYmopLnN1YnN0cmluZygwLCA0KX1gO1xuICByZXR1cm4gc2Vzc2lvbklkID8gYCR7aW5zdGFuY2VOYW1lfSAoJHtzZXNzaW9uSWQuc3Vic3RyaW5nKDAsIDgpfSlgIDogaW5zdGFuY2VOYW1lO1xufVxuXG4vKiogQHR5cGUge2ltcG9ydCgnQGFwcGl1bS90eXBlcycpLkRyaXZlckhlbHBlcnN9ICovXG5leHBvcnQgZGVmYXVsdCB7XG4gIGNvbmZpZ3VyZUFwcCxcbiAgaXNQYWNrYWdlT3JCdW5kbGUsXG4gIGR1cGxpY2F0ZUtleXMsXG4gIHBhcnNlQ2Fwc0FycmF5LFxuICBnZW5lcmF0ZURyaXZlckxvZ1ByZWZpeCxcbn07XG5leHBvcnQge2NvbmZpZ3VyZUFwcCwgaXNQYWNrYWdlT3JCdW5kbGUsIGR1cGxpY2F0ZUtleXMsIHBhcnNlQ2Fwc0FycmF5LCBnZW5lcmF0ZURyaXZlckxvZ1ByZWZpeH07XG5cbi8qKlxuICogQHR5cGVkZWYgUmVtb3RlQXBwUHJvcHNcbiAqIEBwcm9wZXJ0eSB7RGF0ZT99IGxhc3RNb2RpZmllZFxuICogQHByb3BlcnR5IHtib29sZWFufSBpbW11dGFibGVcbiAqIEBwcm9wZXJ0eSB7bnVtYmVyP30gbWF4QWdlXG4gKi9cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7QUFBQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7OztBQUVBLE1BQU1BLE9BQU8sR0FBRyxNQUFoQjtBQUNBLE1BQU1DLFFBQVEsR0FBRyxDQUFDLE1BQUQsRUFBU0QsT0FBVCxDQUFqQjtBQUNBLE1BQU1FLGNBQWMsR0FBRyxDQUFDLGlCQUFELEVBQW9CLDhCQUFwQixFQUFvRCxpQkFBcEQsQ0FBdkI7QUFDQSxNQUFNQyxtQkFBbUIsR0FBRyxPQUFPLEVBQVAsR0FBWSxFQUFaLEdBQWlCLEVBQTdDO0FBQ0EsTUFBTUMsZUFBZSxHQUFHLElBQXhCO0FBQ0EsTUFBTUMsa0JBQWtCLEdBQUcsSUFBSUMsaUJBQUosQ0FBUTtFQUNqQ0MsR0FBRyxFQUFFSCxlQUQ0QjtFQUVqQ0ksR0FBRyxFQUFFTCxtQkFGNEI7RUFHakNNLGNBQWMsRUFBRSxJQUhpQjtFQUlqQ0MsT0FBTyxFQUFFLENBQUNDLEdBQUQsRUFBTTtJQUFDQztFQUFELENBQU4sS0FBcUI7SUFDNUJDLGVBQUEsQ0FBT0MsSUFBUCxDQUNHLG9CQUFtQkgsR0FBSSxnQkFBZUMsUUFBUyxRQUFoRCxH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
545
+ /**
546
+ * @typedef RemoteAppProps
547
+ * @property {Date?} lastModified
548
+ * @property {boolean} immutable
549
+ * @property {number?} maxAge
550
+ */
551
+ //# sourceMappingURL=helpers.js.map