@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.
- package/build/lib/basedriver/capabilities.d.ts +11 -163
- package/build/lib/basedriver/capabilities.d.ts.map +1 -1
- package/build/lib/basedriver/capabilities.js +354 -236
- package/build/lib/basedriver/capabilities.js.map +1 -1
- package/build/lib/basedriver/commands/event.d.ts +7 -6
- package/build/lib/basedriver/commands/event.d.ts.map +1 -1
- package/build/lib/basedriver/commands/event.js +55 -35
- package/build/lib/basedriver/commands/event.js.map +1 -1
- package/build/lib/basedriver/commands/execute.d.ts +7 -6
- package/build/lib/basedriver/commands/execute.d.ts.map +1 -1
- package/build/lib/basedriver/commands/execute.js +66 -58
- package/build/lib/basedriver/commands/execute.js.map +1 -1
- package/build/lib/basedriver/commands/find.d.ts +9 -7
- package/build/lib/basedriver/commands/find.d.ts.map +1 -1
- package/build/lib/basedriver/commands/find.js +102 -54
- package/build/lib/basedriver/commands/find.js.map +1 -1
- package/build/lib/basedriver/commands/index.d.ts +3 -7
- package/build/lib/basedriver/commands/index.d.ts.map +1 -1
- package/build/lib/basedriver/commands/index.js +30 -33
- package/build/lib/basedriver/commands/index.js.map +1 -1
- package/build/lib/basedriver/commands/log.d.ts +8 -9
- package/build/lib/basedriver/commands/log.d.ts.map +1 -1
- package/build/lib/basedriver/commands/log.js +54 -38
- package/build/lib/basedriver/commands/log.js.map +1 -1
- package/build/lib/basedriver/commands/session.d.ts +7 -6
- package/build/lib/basedriver/commands/session.d.ts.map +1 -1
- package/build/lib/basedriver/commands/session.js +46 -39
- package/build/lib/basedriver/commands/session.js.map +1 -1
- package/build/lib/basedriver/commands/settings.d.ts +7 -7
- package/build/lib/basedriver/commands/settings.d.ts.map +1 -1
- package/build/lib/basedriver/commands/settings.js +35 -28
- package/build/lib/basedriver/commands/settings.js.map +1 -1
- package/build/lib/basedriver/commands/timeout.d.ts +7 -5
- package/build/lib/basedriver/commands/timeout.d.ts.map +1 -1
- package/build/lib/basedriver/commands/timeout.js +144 -162
- package/build/lib/basedriver/commands/timeout.js.map +1 -1
- package/build/lib/basedriver/core.d.ts +6 -157
- package/build/lib/basedriver/core.d.ts.map +1 -1
- package/build/lib/basedriver/core.js +361 -230
- package/build/lib/basedriver/core.js.map +1 -1
- package/build/lib/basedriver/desired-caps.js +80 -110
- package/build/lib/basedriver/desired-caps.js.map +1 -1
- package/build/lib/basedriver/device-settings.js +57 -62
- package/build/lib/basedriver/device-settings.js.map +1 -1
- package/build/lib/basedriver/driver.d.ts +21 -267
- package/build/lib/basedriver/driver.d.ts.map +1 -1
- package/build/lib/basedriver/driver.js +362 -258
- package/build/lib/basedriver/driver.js.map +1 -1
- package/build/lib/basedriver/helpers.js +500 -495
- package/build/lib/basedriver/helpers.js.map +1 -1
- package/build/lib/basedriver/logger.d.ts +1 -1
- package/build/lib/basedriver/logger.d.ts.map +1 -1
- package/build/lib/basedriver/logger.js +5 -15
- package/build/lib/basedriver/logger.js.map +1 -1
- package/build/lib/constants.js +14 -14
- package/build/lib/constants.js.map +1 -1
- package/build/lib/express/crash.js +8 -15
- package/build/lib/express/crash.js.map +1 -1
- package/build/lib/express/express-logging.js +49 -59
- package/build/lib/express/express-logging.js.map +1 -1
- package/build/lib/express/idempotency.js +125 -177
- package/build/lib/express/idempotency.js.map +1 -1
- package/build/lib/express/logger.d.ts +1 -1
- package/build/lib/express/logger.d.ts.map +1 -1
- package/build/lib/express/logger.js +5 -15
- package/build/lib/express/logger.js.map +1 -1
- package/build/lib/express/middleware.js +82 -107
- package/build/lib/express/middleware.js.map +1 -1
- package/build/lib/express/server.d.ts +17 -5
- package/build/lib/express/server.d.ts.map +1 -1
- package/build/lib/express/server.js +259 -224
- package/build/lib/express/server.js.map +1 -1
- package/build/lib/express/static.js +64 -81
- package/build/lib/express/static.js.map +1 -1
- package/build/lib/express/websocket.js +115 -87
- package/build/lib/express/websocket.js.map +1 -1
- package/build/lib/helpers/capabilities.d.ts +1 -59
- package/build/lib/helpers/capabilities.d.ts.map +1 -1
- package/build/lib/helpers/capabilities.js +72 -69
- package/build/lib/helpers/capabilities.js.map +1 -1
- package/build/lib/index.js +64 -180
- package/build/lib/index.js.map +1 -1
- package/build/lib/jsonwp-proxy/protocol-converter.js +215 -227
- package/build/lib/jsonwp-proxy/protocol-converter.js.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
- package/build/lib/jsonwp-proxy/proxy.js +355 -393
- package/build/lib/jsonwp-proxy/proxy.js.map +1 -1
- package/build/lib/jsonwp-status/status.js +119 -130
- package/build/lib/jsonwp-status/status.js.map +1 -1
- package/build/lib/protocol/errors.d.ts +135 -32
- package/build/lib/protocol/errors.d.ts.map +1 -1
- package/build/lib/protocol/errors.js +871 -919
- package/build/lib/protocol/errors.js.map +1 -1
- package/build/lib/protocol/helpers.js +37 -37
- package/build/lib/protocol/helpers.js.map +1 -1
- package/build/lib/protocol/index.js +22 -109
- package/build/lib/protocol/index.js.map +1 -1
- package/build/lib/protocol/protocol.js +394 -350
- package/build/lib/protocol/protocol.js.map +1 -1
- package/build/lib/protocol/routes.d.ts +1238 -4
- package/build/lib/protocol/routes.d.ts.map +1 -1
- package/build/lib/protocol/routes.js +964 -1327
- package/build/lib/protocol/routes.js.map +1 -1
- package/build/lib/protocol/validators.js +32 -39
- package/build/lib/protocol/validators.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/basedriver/capabilities.js +80 -39
- package/lib/basedriver/commands/event.js +10 -5
- package/lib/basedriver/commands/execute.js +14 -9
- package/lib/basedriver/commands/find.js +18 -12
- package/lib/basedriver/commands/index.js +21 -16
- package/lib/basedriver/commands/log.js +24 -18
- package/lib/basedriver/commands/session.js +10 -5
- package/lib/basedriver/commands/settings.js +9 -6
- package/lib/basedriver/commands/timeout.js +10 -4
- package/lib/basedriver/core.js +2 -3
- package/lib/basedriver/driver.js +29 -18
- package/lib/express/server.js +6 -3
- package/lib/protocol/errors.js +155 -44
- package/lib/protocol/routes.js +11 -7
- package/package.json +14 -16
|
@@ -1,546 +1,551 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
});
|
|
6
|
-
exports.configureApp =
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
require("
|
|
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
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
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
|
-
|
|
120
|
+
return await support_1.fs.hash(filePath);
|
|
165
121
|
}
|
|
166
|
-
|
|
167
122
|
async function isAppIntegrityOk(currentPath, expectedIntegrity = {}) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
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
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
|
|
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
|
-
|
|
485
|
-
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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
|
-
|
|
511
|
-
|
|
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
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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
|
-
|
|
535
|
-
|
|
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
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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
|
-
|
|
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
|