appium-android-driver 5.13.1 → 5.13.2
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/CHANGELOG.md +7 -0
- package/build/index.js +43 -40
- package/build/lib/android-helpers.d.ts +136 -0
- package/build/lib/android-helpers.d.ts.map +1 -0
- package/build/lib/android-helpers.js +760 -679
- package/build/lib/android-helpers.js.map +1 -1
- package/build/lib/bootstrap.d.ts +29 -0
- package/build/lib/bootstrap.d.ts.map +1 -0
- package/build/lib/bootstrap.js +192 -179
- package/build/lib/bootstrap.js.map +1 -1
- package/build/lib/commands/actions.d.ts +209 -0
- package/build/lib/commands/actions.d.ts.map +1 -0
- package/build/lib/commands/actions.js +327 -265
- package/build/lib/commands/actions.js.map +1 -1
- package/build/lib/commands/alert.d.ts +10 -0
- package/build/lib/commands/alert.d.ts.map +1 -0
- package/build/lib/commands/alert.js +12 -18
- package/build/lib/commands/alert.js.map +1 -1
- package/build/lib/commands/app-management.d.ts +314 -0
- package/build/lib/commands/app-management.d.ts.map +1 -0
- package/build/lib/commands/app-management.js +278 -110
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/context.d.ts +94 -0
- package/build/lib/commands/context.d.ts.map +1 -0
- package/build/lib/commands/context.js +412 -260
- package/build/lib/commands/context.js.map +1 -1
- package/build/lib/commands/coverage.d.ts +5 -0
- package/build/lib/commands/coverage.d.ts.map +1 -0
- package/build/lib/commands/coverage.js +14 -17
- package/build/lib/commands/coverage.js.map +1 -1
- package/build/lib/commands/element.d.ts +36 -0
- package/build/lib/commands/element.d.ts.map +1 -0
- package/build/lib/commands/element.js +97 -127
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/emu-console.d.ts +49 -0
- package/build/lib/commands/emu-console.d.ts.map +1 -0
- package/build/lib/commands/emu-console.js +36 -25
- package/build/lib/commands/emu-console.js.map +1 -1
- package/build/lib/commands/execute.d.ts +6 -0
- package/build/lib/commands/execute.d.ts.map +1 -0
- package/build/lib/commands/execute.js +68 -69
- package/build/lib/commands/execute.js.map +1 -1
- package/build/lib/commands/file-actions.d.ts +129 -0
- package/build/lib/commands/file-actions.d.ts.map +1 -0
- package/build/lib/commands/file-actions.js +321 -178
- package/build/lib/commands/file-actions.js.map +1 -1
- package/build/lib/commands/find.d.ts +13 -0
- package/build/lib/commands/find.d.ts.map +1 -0
- package/build/lib/commands/find.js +69 -51
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/general.d.ts +133 -0
- package/build/lib/commands/general.d.ts.map +1 -0
- package/build/lib/commands/general.js +275 -216
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/commands/ime.d.ts +11 -0
- package/build/lib/commands/ime.d.ts.map +1 -0
- package/build/lib/commands/ime.js +27 -33
- package/build/lib/commands/ime.js.map +1 -1
- package/build/lib/commands/index.d.ts +3 -0
- package/build/lib/commands/index.d.ts.map +1 -0
- package/build/lib/commands/index.js +32 -35
- package/build/lib/commands/index.js.map +1 -1
- package/build/lib/commands/intent.d.ts +418 -0
- package/build/lib/commands/intent.d.ts.map +1 -0
- package/build/lib/commands/intent.js +281 -151
- package/build/lib/commands/intent.js.map +1 -1
- package/build/lib/commands/keyboard.d.ts +6 -0
- package/build/lib/commands/keyboard.d.ts.map +1 -0
- package/build/lib/commands/keyboard.js +6 -14
- package/build/lib/commands/keyboard.js.map +1 -1
- package/build/lib/commands/log.d.ts +45 -0
- package/build/lib/commands/log.d.ts.map +1 -0
- package/build/lib/commands/log.js +117 -103
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/media-projection.d.ts +144 -0
- package/build/lib/commands/media-projection.d.ts.map +1 -0
- package/build/lib/commands/media-projection.js +228 -171
- package/build/lib/commands/media-projection.js.map +1 -1
- package/build/lib/commands/network.d.ts +139 -0
- package/build/lib/commands/network.d.ts.map +1 -0
- package/build/lib/commands/network.js +249 -181
- package/build/lib/commands/network.js.map +1 -1
- package/build/lib/commands/performance.d.ts +101 -0
- package/build/lib/commands/performance.d.ts.map +1 -0
- package/build/lib/commands/performance.js +390 -236
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.d.ts +93 -0
- package/build/lib/commands/permissions.d.ts.map +1 -0
- package/build/lib/commands/permissions.js +133 -93
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/recordscreen.d.ts +194 -0
- package/build/lib/commands/recordscreen.d.ts.map +1 -0
- package/build/lib/commands/recordscreen.js +293 -224
- package/build/lib/commands/recordscreen.js.map +1 -1
- package/build/lib/commands/shell.d.ts +8 -0
- package/build/lib/commands/shell.d.ts.map +1 -0
- package/build/lib/commands/shell.js +38 -43
- package/build/lib/commands/shell.js.map +1 -1
- package/build/lib/commands/streamscreen.d.ts +104 -0
- package/build/lib/commands/streamscreen.d.ts.map +1 -0
- package/build/lib/commands/streamscreen.js +364 -305
- package/build/lib/commands/streamscreen.js.map +1 -1
- package/build/lib/commands/system-bars.d.ts +100 -0
- package/build/lib/commands/system-bars.d.ts.map +1 -0
- package/build/lib/commands/system-bars.js +148 -90
- package/build/lib/commands/system-bars.js.map +1 -1
- package/build/lib/commands/touch.d.ts +30 -0
- package/build/lib/commands/touch.d.ts.map +1 -0
- package/build/lib/commands/touch.js +311 -287
- package/build/lib/commands/touch.js.map +1 -1
- package/build/lib/desired-caps.d.ts +353 -0
- package/build/lib/desired-caps.d.ts.map +1 -0
- package/build/lib/desired-caps.js +291 -292
- package/build/lib/desired-caps.js.map +1 -1
- package/build/lib/driver.d.ts +430 -0
- package/build/lib/driver.d.ts.map +1 -0
- package/build/lib/driver.js +449 -384
- package/build/lib/driver.js.map +1 -1
- package/build/lib/logger.d.ts +3 -0
- package/build/lib/logger.d.ts.map +1 -0
- package/build/lib/logger.js +5 -11
- package/build/lib/logger.js.map +1 -1
- package/build/lib/method-map.d.ts +389 -0
- package/build/lib/method-map.d.ts.map +1 -0
- package/build/lib/method-map.js +220 -394
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/stubs.d.ts +8 -0
- package/build/lib/stubs.d.ts.map +1 -0
- package/build/lib/stubs.js +5 -0
- package/build/lib/stubs.js.map +1 -0
- package/build/lib/uiautomator.d.ts +24 -0
- package/build/lib/uiautomator.d.ts.map +1 -0
- package/build/lib/uiautomator.js +86 -82
- package/build/lib/uiautomator.js.map +1 -1
- package/build/lib/unlock-helpers.d.ts +38 -0
- package/build/lib/unlock-helpers.d.ts.map +1 -0
- package/build/lib/unlock-helpers.js +228 -204
- package/build/lib/unlock-helpers.js.map +1 -1
- package/build/lib/utils.d.ts +11 -0
- package/build/lib/utils.d.ts.map +1 -0
- package/build/lib/utils.js +23 -18
- package/build/lib/utils.js.map +1 -1
- package/build/lib/webview-helpers.d.ts +223 -0
- package/build/lib/webview-helpers.d.ts.map +1 -0
- package/build/lib/webview-helpers.js +476 -298
- package/build/lib/webview-helpers.js.map +1 -1
- package/index.js +3 -1
- package/lib/android-helpers.js +2 -1
- package/lib/stubs.ts +8 -0
- package/lib/unlock-helpers.js +2 -2
- package/package.json +23 -14
|
@@ -1,32 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
require("
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
var _asyncbox = require("asyncbox");
|
|
17
|
-
var _child_process = require("child_process");
|
|
18
|
-
var _url = _interopRequireDefault(require("url"));
|
|
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
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
7
|
+
const support_1 = require("@appium/support");
|
|
8
|
+
const teen_process_1 = require("teen_process");
|
|
9
|
+
const portscanner_1 = require("portscanner");
|
|
10
|
+
const http_1 = __importDefault(require("http"));
|
|
11
|
+
const net_1 = __importDefault(require("net"));
|
|
12
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
13
|
+
const asyncbox_1 = require("asyncbox");
|
|
14
|
+
const child_process_1 = require("child_process");
|
|
15
|
+
const url_1 = __importDefault(require("url"));
|
|
19
16
|
const commands = {};
|
|
20
17
|
const RECORDING_INTERVAL_SEC = 5;
|
|
21
18
|
const STREAMING_STARTUP_TIMEOUT_MS = 5000;
|
|
22
|
-
const GSTREAMER_BINARY = `gst-launch-1.0${
|
|
23
|
-
const GST_INSPECT_BINARY = `gst-inspect-1.0${
|
|
19
|
+
const GSTREAMER_BINARY = `gst-launch-1.0${support_1.system.isWindows() ? '.exe' : ''}`;
|
|
20
|
+
const GST_INSPECT_BINARY = `gst-inspect-1.0${support_1.system.isWindows() ? '.exe' : ''}`;
|
|
24
21
|
const REQUIRED_GST_PLUGINS = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
avdec_h264: 'gst-libav',
|
|
23
|
+
h264parse: 'gst-plugins-bad',
|
|
24
|
+
jpegenc: 'gst-plugins-good',
|
|
25
|
+
tcpserversink: 'gst-plugins-base',
|
|
26
|
+
multipartmux: 'gst-plugins-good',
|
|
30
27
|
};
|
|
31
28
|
const SCREENRECORD_BINARY = 'screenrecord';
|
|
32
29
|
const GST_TUTORIAL_URL = 'https://gstreamer.freedesktop.org/documentation/installing/index.html';
|
|
@@ -34,316 +31,378 @@ const DEFAULT_HOST = '127.0.0.1';
|
|
|
34
31
|
const TCP_HOST = '127.0.0.1';
|
|
35
32
|
const DEFAULT_PORT = 8093;
|
|
36
33
|
const DEFAULT_QUALITY = 70;
|
|
37
|
-
const DEFAULT_BITRATE = 4000000;
|
|
34
|
+
const DEFAULT_BITRATE = 4000000; // 4 Mbps
|
|
38
35
|
const BOUNDARY_STRING = '--2ae9746887f170b8cf7c271047ce314c';
|
|
39
36
|
const ADB_SCREEN_STREAMING_FEATURE = 'adb_screen_streaming';
|
|
40
37
|
function createStreamingLogger(streamName, udid) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
return support_1.logger.getLogger(`${streamName}@` + lodash_1.default.truncate(udid, {
|
|
39
|
+
length: 8,
|
|
40
|
+
omission: '',
|
|
41
|
+
}));
|
|
45
42
|
}
|
|
46
43
|
async function verifyStreamingRequirements(adb) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
moduleCheckPromises
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
44
|
+
if (!lodash_1.default.trim(await adb.shell(['which', SCREENRECORD_BINARY]))) {
|
|
45
|
+
throw new Error(`The required '${SCREENRECORD_BINARY}' binary is not available on the device under test`);
|
|
46
|
+
}
|
|
47
|
+
const gstreamerCheckPromises = [];
|
|
48
|
+
for (const binaryName of [GSTREAMER_BINARY, GST_INSPECT_BINARY]) {
|
|
49
|
+
gstreamerCheckPromises.push((async () => {
|
|
50
|
+
try {
|
|
51
|
+
await support_1.fs.which(binaryName);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
throw new Error(`The '${binaryName}' binary is not available in the PATH on the host system. ` +
|
|
55
|
+
`See ${GST_TUTORIAL_URL} for more details on how to install it.`);
|
|
56
|
+
}
|
|
57
|
+
})());
|
|
58
|
+
}
|
|
59
|
+
await bluebird_1.default.all(gstreamerCheckPromises);
|
|
60
|
+
const moduleCheckPromises = [];
|
|
61
|
+
for (const [name, modName] of lodash_1.default.toPairs(REQUIRED_GST_PLUGINS)) {
|
|
62
|
+
moduleCheckPromises.push((async () => {
|
|
63
|
+
const { stdout } = await (0, teen_process_1.exec)(GST_INSPECT_BINARY, [name]);
|
|
64
|
+
if (!lodash_1.default.includes(stdout, modName)) {
|
|
65
|
+
throw new Error(`The required GStreamer plugin '${name}' from '${modName}' module is not installed. ` +
|
|
66
|
+
`See ${GST_TUTORIAL_URL} for more details on how to install it.`);
|
|
67
|
+
}
|
|
68
|
+
})());
|
|
69
|
+
}
|
|
70
|
+
await bluebird_1.default.all(moduleCheckPromises);
|
|
73
71
|
}
|
|
74
72
|
async function getDeviceInfo(adb, log = null) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
const output = await adb.shell(['dumpsys', 'display']);
|
|
74
|
+
const result = {};
|
|
75
|
+
for (const [key, pattern] of [
|
|
76
|
+
['width', /\bdeviceWidth=(\d+)/],
|
|
77
|
+
['height', /\bdeviceHeight=(\d+)/],
|
|
78
|
+
['fps', /\bfps=(\d+)/],
|
|
79
|
+
]) {
|
|
80
|
+
const match = pattern.exec(output);
|
|
81
|
+
if (!match) {
|
|
82
|
+
log?.debug(output);
|
|
83
|
+
throw new Error(`Cannot parse the device ${key} from the adb command output. ` +
|
|
84
|
+
`Check the server log for more details.`);
|
|
85
|
+
}
|
|
86
|
+
result[key] = parseInt(match[1], 10);
|
|
82
87
|
}
|
|
83
|
-
result
|
|
84
|
-
|
|
85
|
-
result.udid = adb.curDeviceId;
|
|
86
|
-
return result;
|
|
88
|
+
result.udid = adb.curDeviceId;
|
|
89
|
+
return result;
|
|
87
90
|
}
|
|
88
91
|
async function initDeviceStreamingProc(adb, log, deviceInfo, opts = {}) {
|
|
89
|
-
|
|
90
|
-
width,
|
|
91
|
-
height,
|
|
92
|
-
bitRate
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
screenRecordCmd += ` --size=${adjustedWidth}x${adjustedHeight}`;
|
|
100
|
-
}
|
|
101
|
-
if (bitRate) {
|
|
102
|
-
screenRecordCmd += ` --bit-rate=${adjustedBitrate}`;
|
|
103
|
-
}
|
|
104
|
-
const adbArgs = [...adb.executable.defaultArgs, 'exec-out', `while true; do ${screenRecordCmd} -; done`];
|
|
105
|
-
const deviceStreaming = (0, _child_process.spawn)(adb.executable.path, adbArgs);
|
|
106
|
-
deviceStreaming.on('exit', (code, signal) => {
|
|
107
|
-
log.debug(`Device streaming process exited with code ${code}, signal ${signal}`);
|
|
108
|
-
});
|
|
109
|
-
let isStarted = false;
|
|
110
|
-
const deviceStreamingLogger = createStreamingLogger(SCREENRECORD_BINARY, deviceInfo.udid);
|
|
111
|
-
const errorsListener = chunk => {
|
|
112
|
-
const stderr = chunk.toString();
|
|
113
|
-
if (_lodash.default.trim(stderr)) {
|
|
114
|
-
deviceStreamingLogger.debug(stderr);
|
|
92
|
+
const { width, height, bitRate, } = opts;
|
|
93
|
+
const adjustedWidth = parseInt(width, 10) || deviceInfo.width;
|
|
94
|
+
const adjustedHeight = parseInt(height, 10) || deviceInfo.height;
|
|
95
|
+
const adjustedBitrate = parseInt(bitRate, 10) || DEFAULT_BITRATE;
|
|
96
|
+
let screenRecordCmd = SCREENRECORD_BINARY +
|
|
97
|
+
` --output-format=h264` +
|
|
98
|
+
// 5 seconds is fine to detect rotation changes
|
|
99
|
+
` --time-limit=${RECORDING_INTERVAL_SEC}`;
|
|
100
|
+
if (width || height) {
|
|
101
|
+
screenRecordCmd += ` --size=${adjustedWidth}x${adjustedHeight}`;
|
|
115
102
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const startupListener = chunk => {
|
|
119
|
-
if (!isStarted) {
|
|
120
|
-
isStarted = !_lodash.default.isEmpty(chunk);
|
|
103
|
+
if (bitRate) {
|
|
104
|
+
screenRecordCmd += ` --bit-rate=${adjustedBitrate}`;
|
|
121
105
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
106
|
+
const adbArgs = [
|
|
107
|
+
...adb.executable.defaultArgs,
|
|
108
|
+
'exec-out',
|
|
109
|
+
// The loop is required, because by default the maximum record duration
|
|
110
|
+
// for screenrecord is always limited
|
|
111
|
+
`while true; do ${screenRecordCmd} -; done`,
|
|
112
|
+
];
|
|
113
|
+
const deviceStreaming = (0, child_process_1.spawn)(adb.executable.path, adbArgs);
|
|
114
|
+
deviceStreaming.on('exit', (code, signal) => {
|
|
115
|
+
log.debug(`Device streaming process exited with code ${code}, signal ${signal}`);
|
|
129
116
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
117
|
+
let isStarted = false;
|
|
118
|
+
const deviceStreamingLogger = createStreamingLogger(SCREENRECORD_BINARY, deviceInfo.udid);
|
|
119
|
+
const errorsListener = (chunk) => {
|
|
120
|
+
const stderr = chunk.toString();
|
|
121
|
+
if (lodash_1.default.trim(stderr)) {
|
|
122
|
+
deviceStreamingLogger.debug(stderr);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
deviceStreaming.stderr.on('data', errorsListener);
|
|
126
|
+
const startupListener = (chunk) => {
|
|
127
|
+
if (!isStarted) {
|
|
128
|
+
isStarted = !lodash_1.default.isEmpty(chunk);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
deviceStreaming.stdout.on('data', startupListener);
|
|
132
|
+
try {
|
|
133
|
+
log.info(`Starting device streaming: ${support_1.util.quote([adb.executable.path, ...adbArgs])}`);
|
|
134
|
+
await (0, asyncbox_1.waitForCondition)(() => isStarted, {
|
|
135
|
+
waitMs: STREAMING_STARTUP_TIMEOUT_MS,
|
|
136
|
+
intervalMs: 300,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
log.errorAndThrow(`Cannot start the screen streaming process. Original error: ${e.message}`);
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
deviceStreaming.stderr.removeListener('data', errorsListener);
|
|
144
|
+
deviceStreaming.stdout.removeListener('data', startupListener);
|
|
145
|
+
}
|
|
146
|
+
return deviceStreaming;
|
|
137
147
|
}
|
|
138
148
|
async function initGstreamerPipeline(deviceStreamingProc, deviceInfo, log, opts = {}) {
|
|
139
|
-
|
|
140
|
-
width,
|
|
141
|
-
height,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
let didFail = false;
|
|
163
|
-
try {
|
|
164
|
-
log.info(`Starting GStreamer pipeline: ${gstreamerPipeline.rep}`);
|
|
165
|
-
await gstreamerPipeline.start(0);
|
|
166
|
-
await (0, _asyncbox.waitForCondition)(async () => {
|
|
167
|
-
try {
|
|
168
|
-
return (await (0, _portscanner.checkPortStatus)(tcpPort, TCP_HOST)) === 'open';
|
|
169
|
-
} catch (ign) {
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
}, {
|
|
173
|
-
waitMs: STREAMING_STARTUP_TIMEOUT_MS,
|
|
174
|
-
intervalMs: 300
|
|
149
|
+
const { width, height, quality, tcpPort, considerRotation, logPipelineDetails, } = opts;
|
|
150
|
+
const adjustedWidth = parseInt(width, 10) || deviceInfo.width;
|
|
151
|
+
const adjustedHeight = parseInt(height, 10) || deviceInfo.height;
|
|
152
|
+
const gstreamerPipeline = new teen_process_1.SubProcess(GSTREAMER_BINARY, [
|
|
153
|
+
'-v',
|
|
154
|
+
'fdsrc', 'fd=0',
|
|
155
|
+
'!', 'video/x-h264,' +
|
|
156
|
+
`width=${considerRotation ? Math.max(adjustedWidth, adjustedHeight) : adjustedWidth},` +
|
|
157
|
+
`height=${considerRotation ? Math.max(adjustedWidth, adjustedHeight) : adjustedHeight},` +
|
|
158
|
+
`framerate=${deviceInfo.fps}/1,` +
|
|
159
|
+
'byte-stream=true',
|
|
160
|
+
'!', 'h264parse',
|
|
161
|
+
'!', 'queue', 'leaky=downstream',
|
|
162
|
+
'!', 'avdec_h264',
|
|
163
|
+
'!', 'queue', 'leaky=downstream',
|
|
164
|
+
'!', 'jpegenc', `quality=${quality}`,
|
|
165
|
+
'!', 'multipartmux', `boundary=${BOUNDARY_STRING}`,
|
|
166
|
+
'!', 'tcpserversink', `host=${TCP_HOST}`, `port=${tcpPort}`,
|
|
167
|
+
], {
|
|
168
|
+
stdio: [deviceStreamingProc.stdout, 'pipe', 'pipe']
|
|
169
|
+
});
|
|
170
|
+
gstreamerPipeline.on('exit', (code, signal) => {
|
|
171
|
+
log.debug(`Pipeline streaming process exited with code ${code}, signal ${signal}`);
|
|
175
172
|
});
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
173
|
+
const gstreamerLogger = createStreamingLogger('gst', deviceInfo.udid);
|
|
174
|
+
const gstOutputListener = (stdout, stderr) => {
|
|
175
|
+
if (lodash_1.default.trim(stderr || stdout)) {
|
|
176
|
+
gstreamerLogger.debug(stderr || stdout);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
gstreamerPipeline.on('output', gstOutputListener);
|
|
180
|
+
let didFail = false;
|
|
181
|
+
try {
|
|
182
|
+
log.info(`Starting GStreamer pipeline: ${gstreamerPipeline.rep}`);
|
|
183
|
+
await gstreamerPipeline.start(0);
|
|
184
|
+
await (0, asyncbox_1.waitForCondition)(async () => {
|
|
185
|
+
try {
|
|
186
|
+
return (await (0, portscanner_1.checkPortStatus)(tcpPort, TCP_HOST)) === 'open';
|
|
187
|
+
}
|
|
188
|
+
catch (ign) {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}, {
|
|
192
|
+
waitMs: STREAMING_STARTUP_TIMEOUT_MS,
|
|
193
|
+
intervalMs: 300,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
didFail = true;
|
|
198
|
+
log.errorAndThrow(`Cannot start the screen streaming pipeline. Original error: ${e.message}`);
|
|
182
199
|
}
|
|
183
|
-
|
|
184
|
-
|
|
200
|
+
finally {
|
|
201
|
+
if (!logPipelineDetails || didFail) {
|
|
202
|
+
gstreamerPipeline.removeListener('output', gstOutputListener);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return gstreamerPipeline;
|
|
185
206
|
}
|
|
186
207
|
function extractRemoteAddress(req) {
|
|
187
|
-
|
|
208
|
+
return req.headers['x-forwarded-for']
|
|
209
|
+
|| req.socket.remoteAddress
|
|
210
|
+
|| req.connection.remoteAddress
|
|
211
|
+
|| req.connection.socket.remoteAddress;
|
|
188
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* @typedef {Object} StartScreenStreamingOptions
|
|
215
|
+
*
|
|
216
|
+
* @property {?number} width - The scaled width of the device's screen. If unset then the script will assign it
|
|
217
|
+
* to the actual screen width measured in pixels.
|
|
218
|
+
* @property {?number} height - The scaled height of the device's screen. If unset then the script will assign it
|
|
219
|
+
* to the actual screen height measured in pixels.
|
|
220
|
+
* @property {?number} bitRate - The video bit rate for the video, in bits per second.
|
|
221
|
+
* The default value is 4000000 (4 Mb/s). You can increase the bit rate to improve video quality,
|
|
222
|
+
* but doing so results in larger movie files.
|
|
223
|
+
* @property {?string} host [127.0.0.1] - The IP address/host name to start the MJPEG server on.
|
|
224
|
+
* You can set it to `0.0.0.0` to trigger the broadcast on all available network interfaces.
|
|
225
|
+
* @property {?string} pathname - The HTTP request path the MJPEG server should be available on.
|
|
226
|
+
* If unset then any pathname on the given `host`/`port` combination will work. Note that the value
|
|
227
|
+
* should always start with a single slash: `/`
|
|
228
|
+
* @property {?number} tcpPort [8094] - The port number to start the internal TCP MJPEG broadcast on.
|
|
229
|
+
* This type of broadcast always starts on the loopback interface (`127.0.0.1`).
|
|
230
|
+
* @property {?number} port [8093] - The port number to start the MJPEG server on.
|
|
231
|
+
* @property {?number} quality [70] - The quality value for the streamed JPEG images.
|
|
232
|
+
* This number should be in range [1, 100], where 100 is the best quality.
|
|
233
|
+
* @property {?boolean} considerRotation [false] - If set to `true` then GStreamer pipeline will
|
|
234
|
+
* increase the dimensions of the resulting images to properly fit images in both landscape and
|
|
235
|
+
* portrait orientations. Set it to `true` if the device rotation is not going to be the same during the
|
|
236
|
+
* broadcasting session.
|
|
237
|
+
* @property {?boolean} logPipelineDetails [false] - Whether to log GStreamer pipeline events into
|
|
238
|
+
* the standard log output. Might be useful for debugging purposes.
|
|
239
|
+
*/
|
|
240
|
+
/**
|
|
241
|
+
* Starts device screen broadcast by creating MJPEG server.
|
|
242
|
+
* Multiple calls to this method have no effect unless the previous streaming
|
|
243
|
+
* session is stopped.
|
|
244
|
+
* This method only works if the `adb_screen_streaming` feature is
|
|
245
|
+
* enabled on the server side.
|
|
246
|
+
*
|
|
247
|
+
* @param {?StartScreenStreamingOptions} options - The available options.
|
|
248
|
+
* @throws {Error} If screen streaming has failed to start or
|
|
249
|
+
* is not supported on the host system or
|
|
250
|
+
* the corresponding server feature is not enabled.
|
|
251
|
+
*/
|
|
189
252
|
commands.mobileStartScreenStreaming = async function mobileStartScreenStreaming(options = {}) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
bitRate,
|
|
195
|
-
host = DEFAULT_HOST,
|
|
196
|
-
port = DEFAULT_PORT,
|
|
197
|
-
pathname,
|
|
198
|
-
tcpPort = DEFAULT_PORT + 1,
|
|
199
|
-
quality = DEFAULT_QUALITY,
|
|
200
|
-
considerRotation = false,
|
|
201
|
-
logPipelineDetails = false
|
|
202
|
-
} = options;
|
|
203
|
-
if (_lodash.default.isUndefined(this._screenStreamingProps)) {
|
|
204
|
-
await verifyStreamingRequirements(this.adb);
|
|
205
|
-
}
|
|
206
|
-
if (!_lodash.default.isEmpty(this._screenStreamingProps)) {
|
|
207
|
-
this.log.info(`The screen streaming session is already running. ` + `Stop it first in order to start a new one.`);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if ((await (0, _portscanner.checkPortStatus)(port, host)) === 'open') {
|
|
211
|
-
this.log.info(`The port #${port} at ${host} is busy. ` + `Assuming the screen streaming is already running`);
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
if ((await (0, _portscanner.checkPortStatus)(tcpPort, TCP_HOST)) === 'open') {
|
|
215
|
-
this.log.errorAndThrow(`The port #${tcpPort} at ${TCP_HOST} is busy. ` + `Make sure there are no leftovers from previous sessions.`);
|
|
216
|
-
}
|
|
217
|
-
this._screenStreamingProps = null;
|
|
218
|
-
const deviceInfo = await getDeviceInfo(this.adb, this.log);
|
|
219
|
-
const deviceStreamingProc = await initDeviceStreamingProc(this.adb, this.log, deviceInfo, {
|
|
220
|
-
width,
|
|
221
|
-
height,
|
|
222
|
-
bitRate
|
|
223
|
-
});
|
|
224
|
-
let gstreamerPipeline;
|
|
225
|
-
try {
|
|
226
|
-
gstreamerPipeline = await initGstreamerPipeline(deviceStreamingProc, deviceInfo, this.log, {
|
|
227
|
-
width,
|
|
228
|
-
height,
|
|
229
|
-
quality,
|
|
230
|
-
tcpPort,
|
|
231
|
-
considerRotation,
|
|
232
|
-
logPipelineDetails
|
|
233
|
-
});
|
|
234
|
-
} catch (e) {
|
|
235
|
-
if (deviceStreamingProc.kill(0)) {
|
|
236
|
-
deviceStreamingProc.kill();
|
|
253
|
+
this.ensureFeatureEnabled(ADB_SCREEN_STREAMING_FEATURE);
|
|
254
|
+
const { width, height, bitRate, host = DEFAULT_HOST, port = DEFAULT_PORT, pathname, tcpPort = DEFAULT_PORT + 1, quality = DEFAULT_QUALITY, considerRotation = false, logPipelineDetails = false, } = options;
|
|
255
|
+
if (lodash_1.default.isUndefined(this._screenStreamingProps)) {
|
|
256
|
+
await verifyStreamingRequirements(this.adb);
|
|
237
257
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
try {
|
|
243
|
-
await new _bluebird.default((resolve, reject) => {
|
|
244
|
-
mjpegSocket = _net.default.createConnection(tcpPort, TCP_HOST, () => {
|
|
245
|
-
this.log.info(`Successfully connected to MJPEG stream at tcp://${TCP_HOST}:${tcpPort}`);
|
|
246
|
-
mjpegServer = _http.default.createServer((req, res) => {
|
|
247
|
-
const remoteAddress = extractRemoteAddress(req);
|
|
248
|
-
const currentPathname = _url.default.parse(req.url).pathname;
|
|
249
|
-
this.log.info(`Got an incoming screen bradcasting request from ${remoteAddress} ` + `(${req.headers['user-agent'] || 'User Agent unknown'}) at ${currentPathname}`);
|
|
250
|
-
if (pathname && currentPathname !== pathname) {
|
|
251
|
-
this.log.info('Rejecting the broadcast request since it does not match the given pathname');
|
|
252
|
-
res.writeHead(404, {
|
|
253
|
-
Connection: 'close',
|
|
254
|
-
'Content-Type': 'text/plain; charset=utf-8'
|
|
255
|
-
});
|
|
256
|
-
res.write(`'${currentPathname}' did not match any known endpoints`);
|
|
257
|
-
res.end();
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
this.log.info('Starting MJPEG broadcast');
|
|
261
|
-
res.writeHead(200, {
|
|
262
|
-
'Cache-Control': 'no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0',
|
|
263
|
-
Pragma: 'no-cache',
|
|
264
|
-
Connection: 'close',
|
|
265
|
-
'Content-Type': `multipart/x-mixed-replace; boundary=${BOUNDARY_STRING}`
|
|
266
|
-
});
|
|
267
|
-
mjpegSocket.pipe(res);
|
|
268
|
-
});
|
|
269
|
-
mjpegServer.on('error', e => {
|
|
270
|
-
this.log.warn(e);
|
|
271
|
-
reject(e);
|
|
272
|
-
});
|
|
273
|
-
mjpegServer.on('close', () => {
|
|
274
|
-
this.log.debug(`MJPEG server at http://${host}:${port} has been closed`);
|
|
275
|
-
});
|
|
276
|
-
mjpegServer.on('listening', () => {
|
|
277
|
-
this.log.info(`Successfully started MJPEG server at http://${host}:${port}`);
|
|
278
|
-
resolve();
|
|
279
|
-
});
|
|
280
|
-
mjpegServer.listen(port, host);
|
|
281
|
-
});
|
|
282
|
-
mjpegSocket.on('error', e => {
|
|
283
|
-
this.log.error(e);
|
|
284
|
-
reject(e);
|
|
285
|
-
});
|
|
286
|
-
}).timeout(STREAMING_STARTUP_TIMEOUT_MS, `Cannot connect to the streaming server within ${STREAMING_STARTUP_TIMEOUT_MS}ms`);
|
|
287
|
-
} catch (e) {
|
|
288
|
-
if (deviceStreamingProc.kill(0)) {
|
|
289
|
-
deviceStreamingProc.kill();
|
|
258
|
+
if (!lodash_1.default.isEmpty(this._screenStreamingProps)) {
|
|
259
|
+
this.log.info(`The screen streaming session is already running. ` +
|
|
260
|
+
`Stop it first in order to start a new one.`);
|
|
261
|
+
return;
|
|
290
262
|
}
|
|
291
|
-
if (
|
|
292
|
-
|
|
263
|
+
if ((await (0, portscanner_1.checkPortStatus)(port, host)) === 'open') {
|
|
264
|
+
this.log.info(`The port #${port} at ${host} is busy. ` +
|
|
265
|
+
`Assuming the screen streaming is already running`);
|
|
266
|
+
return;
|
|
293
267
|
}
|
|
294
|
-
if (
|
|
295
|
-
|
|
268
|
+
if ((await (0, portscanner_1.checkPortStatus)(tcpPort, TCP_HOST)) === 'open') {
|
|
269
|
+
this.log.errorAndThrow(`The port #${tcpPort} at ${TCP_HOST} is busy. ` +
|
|
270
|
+
`Make sure there are no leftovers from previous sessions.`);
|
|
296
271
|
}
|
|
297
|
-
|
|
298
|
-
|
|
272
|
+
this._screenStreamingProps = null;
|
|
273
|
+
const deviceInfo = await getDeviceInfo(this.adb, this.log);
|
|
274
|
+
const deviceStreamingProc = await initDeviceStreamingProc(this.adb, this.log, deviceInfo, {
|
|
275
|
+
width,
|
|
276
|
+
height,
|
|
277
|
+
bitRate,
|
|
278
|
+
});
|
|
279
|
+
let gstreamerPipeline;
|
|
280
|
+
try {
|
|
281
|
+
gstreamerPipeline = await initGstreamerPipeline(deviceStreamingProc, deviceInfo, this.log, {
|
|
282
|
+
width,
|
|
283
|
+
height,
|
|
284
|
+
quality,
|
|
285
|
+
tcpPort,
|
|
286
|
+
considerRotation,
|
|
287
|
+
logPipelineDetails,
|
|
288
|
+
});
|
|
299
289
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
mjpegSocket,
|
|
306
|
-
mjpegServer
|
|
307
|
-
};
|
|
308
|
-
};
|
|
309
|
-
commands.mobileStopScreenStreaming = async function mobileStopScreenStreaming() {
|
|
310
|
-
if (_lodash.default.isEmpty(this._screenStreamingProps)) {
|
|
311
|
-
if (!_lodash.default.isUndefined(this._screenStreamingProps)) {
|
|
312
|
-
this.log.debug(`Screen streaming is not running. There is nothing to stop`);
|
|
290
|
+
catch (e) {
|
|
291
|
+
if (deviceStreamingProc.kill(0)) {
|
|
292
|
+
deviceStreamingProc.kill();
|
|
293
|
+
}
|
|
294
|
+
throw e;
|
|
313
295
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
296
|
+
let mjpegSocket;
|
|
297
|
+
let mjpegServer;
|
|
298
|
+
try {
|
|
299
|
+
await new bluebird_1.default((resolve, reject) => {
|
|
300
|
+
mjpegSocket = net_1.default.createConnection(tcpPort, TCP_HOST, () => {
|
|
301
|
+
this.log.info(`Successfully connected to MJPEG stream at tcp://${TCP_HOST}:${tcpPort}`);
|
|
302
|
+
mjpegServer = http_1.default.createServer((req, res) => {
|
|
303
|
+
const remoteAddress = extractRemoteAddress(req);
|
|
304
|
+
const currentPathname = url_1.default.parse(req.url).pathname;
|
|
305
|
+
this.log.info(`Got an incoming screen bradcasting request from ${remoteAddress} ` +
|
|
306
|
+
`(${req.headers['user-agent'] || 'User Agent unknown'}) at ${currentPathname}`);
|
|
307
|
+
if (pathname && currentPathname !== pathname) {
|
|
308
|
+
this.log.info('Rejecting the broadcast request since it does not match the given pathname');
|
|
309
|
+
res.writeHead(404, {
|
|
310
|
+
Connection: 'close',
|
|
311
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
312
|
+
});
|
|
313
|
+
res.write(`'${currentPathname}' did not match any known endpoints`);
|
|
314
|
+
res.end();
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
this.log.info('Starting MJPEG broadcast');
|
|
318
|
+
res.writeHead(200, {
|
|
319
|
+
'Cache-Control': 'no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0',
|
|
320
|
+
Pragma: 'no-cache',
|
|
321
|
+
Connection: 'close',
|
|
322
|
+
'Content-Type': `multipart/x-mixed-replace; boundary=${BOUNDARY_STRING}`
|
|
323
|
+
});
|
|
324
|
+
mjpegSocket.pipe(res);
|
|
325
|
+
});
|
|
326
|
+
mjpegServer.on('error', (e) => {
|
|
327
|
+
this.log.warn(e);
|
|
328
|
+
reject(e);
|
|
329
|
+
});
|
|
330
|
+
mjpegServer.on('close', () => {
|
|
331
|
+
this.log.debug(`MJPEG server at http://${host}:${port} has been closed`);
|
|
332
|
+
});
|
|
333
|
+
mjpegServer.on('listening', () => {
|
|
334
|
+
this.log.info(`Successfully started MJPEG server at http://${host}:${port}`);
|
|
335
|
+
resolve();
|
|
336
|
+
});
|
|
337
|
+
mjpegServer.listen(port, host);
|
|
338
|
+
});
|
|
339
|
+
mjpegSocket.on('error', (e) => {
|
|
340
|
+
this.log.error(e);
|
|
341
|
+
reject(e);
|
|
342
|
+
});
|
|
343
|
+
}).timeout(STREAMING_STARTUP_TIMEOUT_MS, `Cannot connect to the streaming server within ${STREAMING_STARTUP_TIMEOUT_MS}ms`);
|
|
326
344
|
}
|
|
327
|
-
|
|
328
|
-
|
|
345
|
+
catch (e) {
|
|
346
|
+
if (deviceStreamingProc.kill(0)) {
|
|
347
|
+
deviceStreamingProc.kill();
|
|
348
|
+
}
|
|
349
|
+
if (gstreamerPipeline.isRunning) {
|
|
350
|
+
await gstreamerPipeline.stop();
|
|
351
|
+
}
|
|
352
|
+
if (mjpegSocket) {
|
|
353
|
+
mjpegSocket.destroy();
|
|
354
|
+
}
|
|
355
|
+
if (mjpegServer && mjpegServer.listening) {
|
|
356
|
+
mjpegServer.close();
|
|
357
|
+
}
|
|
358
|
+
throw e;
|
|
329
359
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
360
|
+
this._screenStreamingProps = {
|
|
361
|
+
deviceStreamingProc,
|
|
362
|
+
gstreamerPipeline,
|
|
363
|
+
mjpegSocket,
|
|
364
|
+
mjpegServer,
|
|
365
|
+
};
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* Stop screen streaming.
|
|
369
|
+
* If no screen streaming server has been started then nothing is done.
|
|
370
|
+
*/
|
|
371
|
+
commands.mobileStopScreenStreaming = async function mobileStopScreenStreaming( /* options = {} */) {
|
|
372
|
+
if (lodash_1.default.isEmpty(this._screenStreamingProps)) {
|
|
373
|
+
if (!lodash_1.default.isUndefined(this._screenStreamingProps)) {
|
|
374
|
+
this.log.debug(`Screen streaming is not running. There is nothing to stop`);
|
|
339
375
|
}
|
|
340
|
-
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const { deviceStreamingProc, gstreamerPipeline, mjpegSocket, mjpegServer, } = this._screenStreamingProps;
|
|
379
|
+
try {
|
|
380
|
+
mjpegSocket.end();
|
|
381
|
+
if (mjpegServer.listening) {
|
|
382
|
+
mjpegServer.close();
|
|
383
|
+
}
|
|
384
|
+
if (deviceStreamingProc.kill(0)) {
|
|
385
|
+
deviceStreamingProc.kill('SIGINT');
|
|
386
|
+
}
|
|
387
|
+
if (gstreamerPipeline.isRunning) {
|
|
388
|
+
try {
|
|
389
|
+
await gstreamerPipeline.stop('SIGINT');
|
|
390
|
+
}
|
|
391
|
+
catch (e) {
|
|
392
|
+
this.log.warn(e);
|
|
393
|
+
try {
|
|
394
|
+
await gstreamerPipeline.stop('SIGKILL');
|
|
395
|
+
}
|
|
396
|
+
catch (e1) {
|
|
397
|
+
this.log.error(e1);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
this.log.info(`Successfully terminated the screen streaming MJPEG server`);
|
|
402
|
+
}
|
|
403
|
+
finally {
|
|
404
|
+
this._screenStreamingProps = null;
|
|
341
405
|
}
|
|
342
|
-
this.log.info(`Successfully terminated the screen streaming MJPEG server`);
|
|
343
|
-
} finally {
|
|
344
|
-
this._screenStreamingProps = null;
|
|
345
|
-
}
|
|
346
406
|
};
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbG9kYXNoIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfc3VwcG9ydCIsIl90ZWVuX3Byb2Nlc3MiLCJfcG9ydHNjYW5uZXIiLCJfaHR0cCIsIl9uZXQiLCJfYmx1ZWJpcmQiLCJfYXN5bmNib3giLCJfY2hpbGRfcHJvY2VzcyIsIl91cmwiLCJjb21tYW5kcyIsIlJFQ09SRElOR19JTlRFUlZBTF9TRUMiLCJTVFJFQU1JTkdfU1RBUlRVUF9USU1FT1VUX01TIiwiR1NUUkVBTUVSX0JJTkFSWSIsInN5c3RlbSIsImlzV2luZG93cyIsIkdTVF9JTlNQRUNUX0JJTkFSWSIsIlJFUVVJUkVEX0dTVF9QTFVHSU5TIiwiYXZkZWNfaDI2NCIsImgyNjRwYXJzZSIsImpwZWdlbmMiLCJ0Y3BzZXJ2ZXJzaW5rIiwibXVsdGlwYXJ0bXV4IiwiU0NSRUVOUkVDT1JEX0JJTkFSWSIsIkdTVF9UVVRPUklBTF9VUkwiLCJERUZBVUxUX0hPU1QiLCJUQ1BfSE9TVCIsIkRFRkFVTFRfUE9SVCIsIkRFRkFVTFRfUVVBTElUWSIsIkRFRkFVTFRfQklUUkFURSIsIkJPVU5EQVJZX1NUUklORyIsIkFEQl9TQ1JFRU5fU1RSRUFNSU5HX0ZFQVRVUkUiLCJjcmVhdGVTdHJlYW1pbmdMb2dnZXIiLCJzdHJlYW1OYW1lIiwidWRpZCIsImxvZ2dlciIsImdldExvZ2dlciIsIl8iLCJ0cnVuY2F0ZSIsImxlbmd0aCIsIm9taXNzaW9uIiwidmVyaWZ5U3RyZWFtaW5nUmVxdWlyZW1lbnRzIiwiYWRiIiwidHJpbSIsInNoZWxsIiwiRXJyb3IiLCJnc3RyZWFtZXJDaGVja1Byb21pc2VzIiwiYmluYXJ5TmFtZSIsInB1c2giLCJmcyIsIndoaWNoIiwiZSIsIkIiLCJhbGwiLCJtb2R1bGVDaGVja1Byb21pc2VzIiwibmFtZSIsIm1vZE5hbWUiLCJ0b1BhaXJzIiwic3Rkb3V0IiwiZXhlYyIsImluY2x1ZGVzIiwiZ2V0RGV2aWNlSW5mbyIsImxvZyIsIm91dHB1dCIsInJlc3VsdCIsImtleSIsInBhdHRlcm4iLCJtYXRjaCIsImRlYnVnIiwicGFyc2VJbnQiLCJjdXJEZXZpY2VJZCIsImluaXREZXZpY2VTdHJlYW1pbmdQcm9jIiwiZGV2aWNlSW5mbyIsIm9wdHMiLCJ3aWR0aCIsImhlaWdodCIsImJpdFJhdGUiLCJhZGp1c3RlZFdpZHRoIiwiYWRqdXN0ZWRIZWlnaHQiLCJhZGp1c3RlZEJpdHJhdGUiLCJzY3JlZW5SZWNvcmRDbWQiLCJhZGJBcmdzIiwiZXhlY3V0YWJsZSIsImRlZmF1bHRBcmdzIiwiZGV2aWNlU3RyZWFtaW5nIiwic3Bhd24iLCJwYXRoIiwib24iLCJjb2RlIiwic2lnbmFsIiwiaXNTdGFydGVkIiwiZGV2aWNlU3RyZWFtaW5nTG9nZ2VyIiwiZXJyb3JzTGlzdGVuZXIiLCJjaHVuayIsInN0ZGVyciIsInRvU3RyaW5nIiwic3RhcnR1cExpc3RlbmVyIiwiaXNFbXB0eSIsImluZm8iLCJ1dGlsIiwicXVvdGUiLCJ3YWl0Rm9yQ29uZGl0aW9uIiwid2FpdE1zIiwiaW50ZXJ2YWxNcyIsImVycm9yQW5kVGhyb3ciLCJtZXNzYWdlIiwicmVtb3ZlTGlzdGVuZXIiLCJpbml0R3N0cmVhbWVyUGlwZWxpbmUiLCJkZXZpY2VTdHJlYW1pbmdQcm9jIiwicXVhbGl0eSIsInRjcFBvcnQiLCJjb25zaWRlclJvdGF0aW9uIiwibG9nUGlwZWxpbmVEZXRhaWxzIiwiZ3N0cmVhbWVyUGlwZWxpbmUiLCJTdWJQcm9jZXNzIiwiTWF0aCIsIm1heCIsImZwcyIsInN0ZGlvIiwiZ3N0cmVhbWVyTG9nZ2VyIiwiZ3N0T3V0cHV0TGlzdGVuZXIiLCJkaWRGYWlsIiwicmVwIiwic3RhcnQiLCJjaGVja1BvcnRTdGF0dXMiLCJpZ24iLCJleHRyYWN0UmVtb3RlQWRkcmVzcyIsInJlcSIsImhlYWRlcnMiLCJzb2NrZXQiLCJyZW1vdGVBZGRyZXNzIiwiY29ubmVjdGlvbiIsIm1vYmlsZVN0YXJ0U2NyZWVuU3RyZWFtaW5nIiwib3B0aW9ucyIsImVuc3VyZUZlYXR1cmVFbmFibGVkIiwiaG9zdCIsInBvcnQiLCJwYXRobmFtZSIsImlzVW5kZWZpbmVkIiwiX3NjcmVlblN0cmVhbWluZ1Byb3BzIiwia2lsbCIsIm1qcGVnU29ja2V0IiwibWpwZWdTZXJ2ZXIiLCJyZXNvbHZlIiwicmVqZWN0IiwibmV0IiwiY3JlYXRlQ29ubmVjdGlvbiIsImh0dHAiLCJjcmVhdGVTZXJ2ZXIiLCJyZXMiLCJjdXJyZW50UGF0aG5hbWUiLCJ1cmwiLCJwYXJzZSIsIndyaXRlSGVhZCIsIkNvbm5lY3Rpb24iLCJ3cml0ZSIsImVuZCIsIlByYWdtYSIsInBpcGUiLCJ3YXJuIiwibGlzdGVuIiwiZXJyb3IiLCJ0aW1lb3V0IiwiaXNSdW5uaW5nIiwic3RvcCIsImRlc3Ryb3kiLCJsaXN0ZW5pbmciLCJjbG9zZSIsIm1vYmlsZVN0b3BTY3JlZW5TdHJlYW1pbmciLCJlMSIsIl9kZWZhdWx0IiwiZXhwb3J0cyIsImRlZmF1bHQiXSwic291cmNlcyI6WyIuLi8uLi8uLi9saWIvY29tbWFuZHMvc3RyZWFtc2NyZWVuLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgeyBmcywgc3lzdGVtLCBsb2dnZXIsIHV0aWwgfSBmcm9tICdAYXBwaXVtL3N1cHBvcnQnO1xuaW1wb3J0IHsgZXhlYywgU3ViUHJvY2VzcyB9IGZyb20gJ3RlZW5fcHJvY2Vzcyc7XG5pbXBvcnQgeyBjaGVja1BvcnRTdGF0dXMgfSBmcm9tICdwb3J0c2Nhbm5lcic7XG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcbmltcG9ydCBuZXQgZnJvbSAnbmV0JztcbmltcG9ydCBCIGZyb20gJ2JsdWViaXJkJztcbmltcG9ydCB7IHdhaXRGb3JDb25kaXRpb24gfSBmcm9tICdhc3luY2JveCc7XG5pbXBvcnQgeyBzcGF3biB9IGZyb20gJ2NoaWxkX3Byb2Nlc3MnO1xuaW1wb3J0IHVybCBmcm9tICd1cmwnO1xuXG5jb25zdCBjb21tYW5kcyA9IHt9O1xuXG5jb25zdCBSRUNPUkRJTkdfSU5URVJWQUxfU0VDID0gNTtcbmNvbnN0IFNUUkVBTUlOR19TVEFSVFVQX1RJTUVPVVRfTVMgPSA1MDAwO1xuY29uc3QgR1NUUkVBTUVSX0JJTkFSWSA9IGBnc3QtbGF1bmNoLTEuMCR7c3lzdGVtLmlzV2luZG93cygpID8gJy5leGUnIDogJyd9YDtcbmNvbnN0IEdTVF9JTlNQRUNUX0JJTkFSWSA9IGBnc3QtaW5zcGVjdC0xLjAke3N5c3RlbS5pc1dpbmRvd3MoKSA/ICcuZXhlJyA6ICcnfWA7XG5jb25zdCBSRVFVSVJFRF9HU1RfUExVR0lOUyA9IHtcbiAgYXZkZWNfaDI2NDogJ2dzdC1saWJhdicsXG4gIGgyNjRwYXJzZTogJ2dzdC1wbHVnaW5zLWJhZCcsXG4gIGpwZWdlbmM6ICdnc3QtcGx1Z2lucy1nb29kJyxcbiAgdGNwc2VydmVyc2luazogJ2dzdC1wbHVnaW5zLWJhc2UnLFxuICBtdWx0aXBhcnRtdXg6ICdnc3QtcGx1Z2lucy1nb29kJyxcbn07XG5jb25zdCBTQ1JFRU5SRUNPUkRfQklOQVJZID0gJ3NjcmVlbnJlY29yZCc7XG5jb25zdCBHU1RfVFVUT1JJQUxfVVJMID0gJ2h0dHBzOi8vZ3N0cmVhbWVyLmZyZWVkZXNrdG9wLm9yZy9kb2N1bWVudGF0aW9uL2luc3RhbGxpbmcvaW5kZXguaHRtbCc7XG5jb25zdCBERUZBVUxUX0hPU1QgPSAnMTI3LjAuMC4xJztcbmNvbnN0IFRDUF9IT1NUID0gJzEyNy4wLjAuMSc7XG5jb25zdCBERUZBVUxUX1BPUlQgPSA4MDkzO1xuY29uc3QgREVGQVVMVF9RVUFMSVRZID0gNzA7XG5jb25zdCBERUZBVUxUX0JJVFJBVEUgPSA0MDAwMDAwOyAvLyA0IE1icHNcbmNvbnN0IEJPVU5EQVJZX1NUUklORyA9ICctLTJhZTk3NDY4ODdmMTcwYjhjZjdjMjcxMDQ3Y2UzMTRjJztcblxuY29uc3QgQURCX1NDUkVFTl9TVFJFQU1JTkdfRkVBVFVSRSA9ICdhZGJfc2NyZWVuX3N0cmVhbWluZyc7XG5cbmZ1bmN0aW9uIGNyZWF0ZVN0cmVhbWluZ0xvZ2dlciAoc3RyZWFtTmFtZSwgdWRpZCkge1xuICByZXR1cm4gbG9nZ2VyLmdldExvZ2dlcihgJHtzdHJlYW1OYW1lfUBgICsgXy50cnVuY2F0ZSh1ZGlkLCB7XG4gICAgbGVuZ3RoOiA4LFxuICAgIG9taXNzaW9uOiAnJyxcbiAgfSkpO1xufVxuXG5hc3luYyBmdW5jdGlvbiB2ZXJpZnlTdHJlYW1pbmdSZXF1aXJlbWVudHMgKGFkYikge1xuICBpZiAoIV8udHJpbShhd2FpdCBhZGIuc2hlbGwoWyd3aGljaCcsIFNDUkVFTlJFQ09SRF9CSU5BUlldKSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgVGhlIHJlcXVpcmVkICcke1NDUkVFTlJFQ09SRF9CSU5BUll9JyBiaW5hcnkgaXMgbm90IGF2YWlsYWJsZSBvbiB0aGUgZGV2aWNlIHVuZGVyIHRlc3RgKTtcbiAgfVxuXG4gIGNvbnN0IGdzdHJlYW1lckNoZWNrUHJvbWlzZXMgPSBbXTtcbiAgZm9yIChjb25zdCBiaW5hcnlOYW1lIG9mIFtHU1RSRUFNRVJfQklOQVJZLCBHU1RfSU5TUEVDVF9CSU5BUlldKSB7XG4gICAgZ3N0cmVhbWVyQ2hlY2tQcm9taXNlcy5wdXNoKChhc3luYyAoKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBmcy53aGljaChiaW5hcnlOYW1lKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBUaGUgJyR7YmluYXJ5TmFtZX0nIGJpbmFyeSBpcyBub3QgYXZhaWxhYmxlIGluIHRoZSBQQVRIIG9uIHRoZSBob3N0IHN5c3RlbS4gYCArXG4gICAgICAgICAgYFNlZSAke0dTVF9UVVRPUklBTF9VUkx9IGZvciBtb3JlIGRldGFpbHMgb24gaG93IHRvIGluc3RhbGwgaXQuYCk7XG4gICAgICB9XG4gICAgfSkoKSk7XG4gIH1cbiAgYXdhaXQgQi5hbGwoZ3N0cmVhbWVyQ2hlY2tQcm9taXNlcyk7XG5cbiAgY29uc3QgbW9kdWxlQ2hlY2tQcm9taXNlcyA9IFtdO1xuICBmb3IgKGNvbnN0IFtuYW1lLCBtb2ROYW1lXSBvZiBfLnRvUGFpcnMoUkVRVUlSRURfR1NUX1BMVUdJTlMpKSB7XG4gICAgbW9kdWxlQ2hlY2tQcm9taXNlcy5wdXNoKChhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCB7c3Rkb3V0fSA9IGF3YWl0IGV4ZWMoR1NUX0lOU1BFQ1RfQklOQVJZLCBbbmFtZV0pO1xuICAgICAgaWYgKCFfLmluY2x1ZGVzKHN0ZG91dCwgbW9kTmFtZSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBUaGUgcmVxdWlyZWQgR1N0cmVhbWVyIHBsdWdpbiAnJHtuYW1lfScgZnJvbSAnJHttb2ROYW1lfScgbW9kdWxlIGlzIG5vdCBpbnN0YWxsZWQuIGAgK1xuICAgICAgICAgIGBTZWUgJHtHU1RfVFVUT1JJQUxfVVJMfSBmb3IgbW9yZSBkZXRhaWxzIG9uIGhvdyB0byBpbnN0YWxsIGl0LmApO1xuICAgICAgfVxuICAgIH0pKCkpO1xuICB9XG4gIGF3YWl0IEIuYWxsKG1vZHVsZUNoZWNrUHJvbWlzZXMpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBnZXREZXZpY2VJbmZvIChhZGIsIGxvZyA9IG51bGwpIHtcbiAgY29uc3Qgb3V0cHV0ID0gYXdhaXQgYWRiLnNoZWxsKFsnZHVtcHN5cycsICdkaXNwbGF5J10pO1xuICBjb25zdCByZXN1bHQgPSB7fTtcbiAgZm9yIChjb25zdCBba2V5LCBwYXR0ZXJuXSBvZiBbXG4gICAgWyd3aWR0aCcsIC9cXGJkZXZpY2VXaWR0aD0oXFxkKykvXSxcbiAgICBbJ2hlaWdodCcsIC9cXGJkZXZpY2VIZWlnaHQ9KFxcZCspL10sXG4gICAgWydmcHMnLCAvXFxiZnBzPShcXGQrKS9dLFxuICBdKSB7XG4gICAgY29uc3QgbWF0Y2ggPSBwYXR0ZXJuLmV4ZWMob3V0cHV0KTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICBsb2c/LmRlYnVnKG91dHB1dCk7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYENhbm5vdCBwYXJzZSB0aGUgZGV2aWNlICR7a2V5fSBmcm9tIHRoZSBhZGIgY29tbWFuZCBvdXRwdXQuIGAgK1xuICAgICAgICBgQ2hlY2sgdGhlIHNlcnZlciBsb2cgZm9yIG1vcmUgZGV0YWlscy5gKTtcbiAgICB9XG4gICAgcmVzdWx0W2tleV0gPSBwYXJzZUludChtYXRjaFsxXSwgMTApO1xuICB9XG4gIHJlc3VsdC51ZGlkID0gYWRiLmN1ckRldmljZUlkO1xuICByZXR1cm4gcmVzdWx0O1xufVxuXG5hc3luYyBmdW5jdGlvbiBpbml0RGV2aWNlU3RyZWFtaW5nUHJvYyAoYWRiLCBsb2csIGRldmljZUluZm8sIG9wdHMgPSB7fSkge1xuICBjb25zdCB7XG4gICAgd2lkdGgsXG4gICAgaGVpZ2h0LFxuICAgIGJpdFJhdGUsXG4gIH0gPSBvcHRzO1xuICBjb25zdCBhZGp1c3RlZFdpZHRoID0gcGFyc2VJbnQod2lkdGgsIDEwKSB8fCBkZXZpY2VJbmZvLndpZHRoO1xuICBjb25zdCBhZGp1c3RlZEhlaWdodCA9IHBhcnNlSW50KGhlaWdodCwgMTApIHx8IGRldmljZUluZm8uaGVpZ2h0O1xuICBjb25zdCBhZGp1c3RlZEJpdHJhdGUgPSBwYXJzZUludChiaXRSYXRlLCAxMCkgfHwgREVGQVVMVF9CSVRSQVRFO1xuICBsZXQgc2NyZWVuUmVjb3JkQ21kID0gU0NSRUVOUkVDT1JEX0JJTkFSWSArXG4gICAgYCAtLW91dHB1dC1mb3JtYXQ9aDI2NGAgK1xuICAgIC8vIDUgc2Vjb25kcyBpcyBmaW5lIHRvIGRldGVjdCByb3RhdGlvbiBjaGFuZ2VzXG4gICAgYCAtLXRpbWUtbGltaXQ9JHtSRUNPUkRJTkdfSU5URVJWQUxfU0VDfWA7XG4gIGlmICh3aWR0aCB8fCBoZWlnaHQpIHtcbiAgICBzY3JlZW5SZWNvcmRDbWQgKz0gYCAtLXNpemU9JHthZGp1c3RlZFdpZHRofXgke2FkanVzdGVkSGVpZ2h0fWA7XG4gIH1cbiAgaWYgKGJpdFJhdGUpIHtcbiAgICBzY3JlZW5SZWNvcmRDbWQgKz0gYCAtLWJpdC1yYXRlPSR7YWRqdXN0ZWRCaXRyYXRlfWA7XG4gIH1cbiAgY29uc3QgYWRiQXJncyA9IFtcbiAgICAuLi5hZGIuZXhlY3V0YWJsZS5kZWZhdWx0QXJncyxcbiAgICAnZXhlYy1vdXQnLFxuICAgIC8vIFRoZSBsb29wIGlzIHJlcXVpcmVkLCBiZWNhdXNlIGJ5IGRlZmF1bHQgdGhlIG1heGltdW0gcmVjb3JkIGR1cmF0aW9uXG4gICAgLy8gZm9yIHNjcmVlbnJlY29yZCBpcyBhbHdheXMgbGltaXRlZFxuICAgIGB3aGlsZSB0cnVlOyBkbyAke3NjcmVlblJlY29yZENtZH0gLTsgZG9uZWAsXG4gIF07XG4gIGNvbnN0IGRldmljZVN0cmVhbWluZyA9IHNwYXduKGFkYi5leGVjdXRhYmxlLnBhdGgsIGFkYkFyZ3MpO1xuICBkZXZpY2VTdHJlYW1pbmcub24oJ2V4aXQnLCAoY29kZSwgc2lnbmFsKSA9PiB7XG4gICAgbG9nLmRlYnVnKGBEZXZpY2Ugc3RyZWFtaW5nIHByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LCBzaWduYWwgJHtzaWduYWx9YCk7XG4gIH0pO1xuXG4gIGxldCBpc1N0YXJ0ZWQgPSBmYWxzZTtcbiAgY29uc3QgZGV2aWNlU3RyZWFtaW5nTG9nZ2VyID0gY3JlYXRlU3RyZWFtaW5nTG9nZ2VyKFNDUkVFTlJFQ09SRF9CSU5BUlksIGRldmljZUluZm8udWRpZCk7XG4gIGNvbnN0IGVycm9yc0xpc3RlbmVyID0gKGNodW5rKSA9PiB7XG4gICAgY29uc3Qgc3RkZXJyID0gY2h1bmsudG9TdHJpbmcoKTtcbiAgICBpZiAoXy50cmltKHN0ZGVycikpIHtcbiAgICAgIGRldmljZVN0cmVhbWluZ0xvZ2dlci5kZWJ1ZyhzdGRlcnIpO1xuICAgIH1cbiAgfTtcbiAgZGV2aWNlU3RyZWFtaW5nLnN0ZGVyci5vbignZGF0YScsIGVycm9yc0xpc3RlbmVyKTtcblxuICBjb25zdCBzdGFydHVwTGlzdGVuZXIgPSAoY2h1bmspID0+IHtcbiAgICBpZiAoIWlzU3RhcnRlZCkge1xuICAgICAgaXNTdGFydGVkID0gIV8uaXNFbXB0eShjaHVuayk7XG4gICAgfVxuICB9O1xuICBkZXZpY2VTdHJlYW1pbmcuc3Rkb3V0Lm9uKCdkYXRhJywgc3RhcnR1cExpc3RlbmVyKTtcblxuICB0cnkge1xuICAgIGxvZy5pbmZvKGBTdGFydGluZyBkZXZpY2Ugc3RyZWFtaW5nOiAke3V0aWwucXVvdGUoW2FkYi5leGVjdXRhYmxlLnBhdGgsIC4uLmFkYkFyZ3NdKX1gKTtcbiAgICBhd2FpdCB3YWl0Rm9yQ29uZGl0aW9uKCgpID0+IGlzU3RhcnRlZCwge1xuICAgICAgd2FpdE1zOiBTVFJFQU1JTkdfU1RBUlRVUF9USU1FT1VUX01TLFxuICAgICAgaW50ZXJ2YWxNczogMzAwLFxuICAgIH0pO1xuICB9IGNhdGNoIChlKSB7XG4gICAgbG9nLmVycm9yQW5kVGhyb3coXG4gICAgICBgQ2Fubm90IHN0YXJ0IHRoZSBzY3JlZW4gc3RyZWFtaW5nIHByb2Nlc3MuIE9yaWdpbmFsIGVycm9yOiAke2UubWVzc2FnZX1gKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBkZXZpY2VTdHJlYW1pbmcuc3RkZXJyLnJlbW92ZUxpc3RlbmVyKCdkYXRhJywgZXJyb3JzTGlzdGVuZXIpO1xuICAgIGRldmljZVN0cmVhbWluZy5zdGRvdXQucmVtb3ZlTGlzdGVuZXIoJ2RhdGEnLCBzdGFydHVwTGlzdGVuZXIpO1xuICB9XG4gIHJldHVybiBkZXZpY2VTdHJlYW1pbmc7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGluaXRHc3RyZWFtZXJQaXBlbGluZSAoZGV2aWNlU3RyZWFtaW5nUHJvYywgZGV2aWNlSW5mbywgbG9nLCBvcHRzID0ge30pIHtcbiAgY29uc3Qge1xuICAgIHdpZHRoLFxuICAgIGhlaWdodCxcbiAgICBxdWFsaXR5LFxuICAgIHRjcFBvcnQsXG4gICAgY29uc2lkZXJSb3RhdGlvbixcbiAgICBsb2dQaXBlbGluZURldGFpbHMsXG4gIH0gPSBvcHRzO1xuICBjb25zdCBhZGp1c3RlZFdpZHRoID0gcGFyc2VJbnQod2lkdGgsIDEwKSB8fCBkZXZpY2VJbmZvLndpZHRoO1xuICBjb25zdCBhZGp1c3RlZEhlaWdodCA9IHBhcnNlSW50KGhlaWdodCwgMTApIHx8IGRldmljZUluZm8uaGVpZ2h0O1xuICBjb25zdCBnc3RyZWFtZXJQaXBlbGluZSA9IG5ldyBTdWJQcm9jZXNzKEdTVFJFQU1FUl9CSU5BUlksIFtcbiAgICAnLXYnLFxuICAgICdmZHNyYycsICdmZD0wJyxcbiAgICAnIScsICd2aWRlby94LWgyNjQsJyArXG4gICAgICBgd2lkdGg9JHtjb25zaWRlclJvdGF0aW9uID8gTWF0aC5tYXgoYWRqdXN0ZWRXaWR0aCwgYWRqdXN0ZWRIZWlnaHQpIDogYWRqdXN0ZWRXaWR0aH0sYCArXG4gICAgICBgaGVpZ2h0PSR7Y29uc2lkZXJSb3RhdGlvbiA/IE1hdGgubWF4KGFkanVzdGVkV2lkdGgsIGFkanVzdGVkSGVpZ2h0KSA6IGFkanVzdGVkSGVpZ2h0fSxgICtcbiAgICAgIGBmcmFtZXJhdGU9JHtkZXZpY2VJbmZvLmZwc30vMSxgICtcbiAgICAgICdieXRlLXN0cmVhbT10cnVlJyxcbiAgICAnIScsICdoMjY0cGFyc2UnLFxuICAgICchJywgJ3F1ZXVlJywgJ2xlYWt5PWRvd25zdHJlYW0nLFxuICAgICchJywgJ2F2ZGVjX2gyNjQnLFxuICAgICchJywgJ3F1ZXVlJywgJ2xlYWt5PWRvd25zdHJlYW0nLFxuICAgICchJywgJ2pwZWdlbmMnLCBgcXVhbGl0eT0ke3F1YWxpdHl9YCxcbiAgICAnIScsICdtdWx0aXBhcnRtdXgnLCBgYm91bmRhcnk9JHtCT1VOREFSWV9TVFJJTkd9YCxcbiAgICAnIScsICd0Y3BzZXJ2ZXJzaW5rJywgYGhvc3Q9JHtUQ1BfSE9TVH1gLCBgcG9ydD0ke3RjcFBvcnR9YCxcbiAgXSwge1xuICAgIHN0ZGlvOiBbZGV2aWNlU3RyZWFtaW5nUHJvYy5zdGRvdXQsICdwaXBlJywgJ3BpcGUnXVxuICB9KTtcbiAgZ3N0cmVhbWVyUGlwZWxpbmUub24oJ2V4aXQnLCAoY29kZSwgc2lnbmFsKSA9PiB7XG4gICAgbG9nLmRlYnVnKGBQaXBlbGluZSBzdHJlYW1pbmcgcHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0sIHNpZ25hbCAke3NpZ25hbH1gKTtcbiAgfSk7XG4gIGNvbnN0IGdzdHJlYW1lckxvZ2dlciA9IGNyZWF0ZVN0cmVhbWluZ0xvZ2dlcignZ3N0JywgZGV2aWNlSW5mby51ZGlkKTtcbiAgY29uc3QgZ3N0T3V0cHV0TGlzdGVuZXIgPSAoc3Rkb3V0LCBzdGRlcnIpID0+IHtcbiAgICBpZiAoXy50cmltKHN0ZGVyciB8fCBzdGRvdXQpKSB7XG4gICAgICBnc3RyZWFtZXJMb2dnZXIuZGVidWcoc3RkZXJyIHx8IHN0ZG91dCk7XG4gICAgfVxuICB9O1xuICBnc3RyZWFtZXJQaXBlbGluZS5vbignb3V0cHV0JywgZ3N0T3V0cHV0TGlzdGVuZXIpO1xuICBsZXQgZGlkRmFpbCA9IGZhbHNlO1xuICB0cnkge1xuICAgIGxvZy5pbmZvKGBTdGFydGluZyBHU3RyZWFtZXIgcGlwZWxpbmU6ICR7Z3N0cmVhbWVyUGlwZWxpbmUucmVwfWApO1xuICAgIGF3YWl0IGdzdHJlYW1lclBpcGVsaW5lLnN0YXJ0KDApO1xuICAgIGF3YWl0IHdhaXRGb3JDb25kaXRpb24oYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIChhd2FpdCBjaGVja1BvcnRTdGF0dXModGNwUG9ydCwgVENQX0hPU1QpKSA9PT0gJ29wZW4nO1xuICAgICAgfSBjYXRjaCAoaWduKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICB9LCB7XG4gICAgICB3YWl0TXM6IFNUUkVBTUlOR19TVEFSVFVQX1RJTUVPVVRfTVMsXG4gICAgICBpbnRlcnZhbE1zOiAzMDAsXG4gICAgfSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBkaWRGYWlsID0gdHJ1ZTtcbiAgICBsb2cuZXJyb3JBbmRUaHJvdyhcbiAgICAgIGBDYW5ub3Qgc3RhcnQgdGhlIHNjcmVlbiBzdHJlYW1pbmcgcGlwZWxpbmUuIE9yaWdpbmFsIGVycm9yOiAke2UubWVzc2FnZX1gKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBpZiAoIWxvZ1BpcGVsaW5lRGV0YWlscyB8fCBkaWRGYWlsKSB7XG4gICAgICBnc3RyZWFtZXJQaXBlbGluZS5yZW1vdmVMaXN0ZW5lcignb3V0cHV0JywgZ3N0T3V0cHV0TGlzdGVuZXIpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZ3N0cmVhbWVyUGlwZWxpbmU7XG59XG5cbmZ1bmN0aW9uIGV4dHJhY3RSZW1vdGVBZGRyZXNzIChyZXEpIHtcbiAgcmV0dXJuIHJlcS5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXVxuICAgIHx8IHJlcS5zb2NrZXQucmVtb3RlQWRkcmVzc1xuICAgIHx8IHJlcS5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcbiAgICB8fCByZXEuY29ubmVjdGlvbi5zb2NrZXQucmVtb3RlQWRkcmVzcztcbn1cblxuXG4vKipcbiAqIEB0eXBlZGVmIHtPYmplY3R9IFN0YXJ0U2NyZWVuU3RyZWFtaW5nT3B0aW9uc1xuICpcbiAqIEBwcm9wZXJ0eSB7P251bWJlcn0gd2lkdGggLSBUaGUgc2NhbGVkIHdpZHRoIG9mIHRoZSBkZXZpY2UncyBzY3JlZW4uIElmIHVuc2V0IHRoZW4gdGhlIHNjcmlwdCB3aWxsIGFzc2lnbiBpdFxuICogdG8gdGhlIGFjdHVhbCBzY3JlZW4gd2lkdGggbWVhc3VyZWQgaW4gcGl4ZWxzLlxuICogQHByb3BlcnR5IHs/bnVtYmVyfSBoZWlnaHQgLSBUaGUgc2NhbGVkIGhlaWdodCBvZiB0aGUgZGV2aWNlJ3Mgc2NyZWVuLiBJZiB1bnNldCB0aGVuIHRoZSBzY3JpcHQgd2lsbCBhc3NpZ24gaXRcbiAqIHRvIHRoZSBhY3R1YWwgc2NyZWVuIGhlaWdodCBtZWFzdXJlZCBpbiBwaXhlbHMuXG4gKiBAcHJvcGVydHkgez9udW1iZXJ9IGJpdFJhdGUgLSBUaGUgdmlkZW8gYml0IHJhdGUgZm9yIHRoZSB2aWRlbywgaW4gYml0cyBwZXIgc2Vjb25kLlxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgNDAwMDAwMCAoNCBNYi9zKS4gWW91IGNhbiBpbmNyZWFzZSB0aGUgYml0IHJhdGUgdG8gaW1wcm92ZSB2aWRlbyBxdWFsaXR5LFxuICogYnV0IGRvaW5nIHNvIHJlc3VsdHMgaW4gbGFyZ2VyIG1vdmllIGZpbGVzLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSBob3N0IFsxMjcuMC4wLjFdIC0gVGhlIElQIGFkZHJlc3MvaG9zdCBuYW1lIHRvIHN0YXJ0IHRoZSBNSlBFRyBzZXJ2ZXIgb24uXG4gKiBZb3UgY2FuIHNldCBpdCB0byBgMC4wLjAuMGAgdG8gdHJpZ2dlciB0aGUgYnJvYWRjYXN0IG9uIGFsbCBhdmFpbGFibGUgbmV0d29yayBpbnRlcmZhY2VzLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSBwYXRobmFtZSAtIFRoZSBIVFRQIHJlcXVlc3QgcGF0aCB0aGUgTUpQRUcgc2VydmVyIHNob3VsZCBiZSBhdmFpbGFibGUgb24uXG4gKiBJZiB1bnNldCB0aGVuIGFueSBwYXRobmFtZSBvbiB0aGUgZ2l2ZW4gYGhvc3RgL2Bwb3J0YCBjb21iaW5hdGlvbiB3aWxsIHdvcmsuIE5vdGUgdGhhdCB0aGUgdmFsdWVcbiAqIHNob3VsZCBhbHdheXMgc3RhcnQgd2l0aCBhIHNpbmdsZSBzbGFzaDogYC9gXG4gKiBAcHJvcGVydHkgez9udW1iZXJ9IHRjcFBvcnQgWzgwOTRdIC0gVGhlIHBvcnQgbnVtYmVyIHRvIHN0YXJ0IHRoZSBpbnRlcm5hbCBUQ1AgTUpQRUcgYnJvYWRjYXN0IG9uLlxuICogVGhpcyB0eXBlIG9mIGJyb2FkY2FzdCBhbHdheXMgc3RhcnRzIG9uIHRoZSBsb29wYmFjayBpbnRlcmZhY2UgKGAxMjcuMC4wLjFgKS5cbiAqIEBwcm9wZXJ0eSB7P251bWJlcn0gcG9ydCBbODA5M10gLSBUaGUgcG9ydCBudW1iZXIgdG8gc3RhcnQgdGhlIE1KUEVHIHNlcnZlciBvbi5cbiAqIEBwcm9wZXJ0eSB7P251bWJlcn0gcXVhbGl0eSBbNzBdIC0gVGhlIHF1YWxpdHkgdmFsdWUgZm9yIHRoZSBzdHJlYW1lZCBKUEVHIGltYWdlcy5cbiAqIFRoaXMgbnVtYmVyIHNob3VsZCBiZSBpbiByYW5nZSBbMSwgMTAwXSwgd2hlcmUgMTAwIGlzIHRoZSBiZXN0IHF1YWxpdHkuXG4gKiBAcHJvcGVydHkgez9ib29sZWFufSBjb25zaWRlclJvdGF0aW9uIFtmYWxzZV0gLSBJZiBzZXQgdG8gYHRydWVgIHRoZW4gR1N0cmVhbWVyIHBpcGVsaW5lIHdpbGxcbiAqIGluY3JlYXNlIHRoZSBkaW1lbnNpb25zIG9mIHRoZSByZXN1bHRpbmcgaW1hZ2VzIHRvIHByb3Blcmx5IGZpdCBpbWFnZXMgaW4gYm90aCBsYW5kc2NhcGUgYW5kXG4gKiBwb3J0cmFpdCBvcmllbnRhdGlvbnMuIFNldCBpdCB0byBgdHJ1ZWAgaWYgdGhlIGRldmljZSByb3RhdGlvbiBpcyBub3QgZ29pbmcgdG8gYmUgdGhlIHNhbWUgZHVyaW5nIHRoZVxuICogYnJvYWRjYXN0aW5nIHNlc3Npb24uXG4gKiBAcHJvcGVydHkgez9ib29sZWFufSBsb2dQaXBlbGluZURldGFpbHMgW2ZhbHNlXSAtIFdoZXRoZXIgdG8gbG9nIEdTdHJlYW1lciBwaXBlbGluZSBldmVudHMgaW50b1xuICogdGhlIHN0YW5kYXJkIGxvZyBvdXRwdXQuIE1pZ2h0IGJlIHVzZWZ1bCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLlxuICovXG5cbi8qKlxuICogU3RhcnRzIGRldmljZSBzY3JlZW4gYnJvYWRjYXN0IGJ5IGNyZWF0aW5nIE1KUEVHIHNlcnZlci5cbiAqIE11bHRpcGxlIGNhbGxzIHRvIHRoaXMgbWV0aG9kIGhhdmUgbm8gZWZmZWN0IHVubGVzcyB0aGUgcHJldmlvdXMgc3RyZWFtaW5nXG4gKiBzZXNzaW9uIGlzIHN0b3BwZWQuXG4gKiBUaGlzIG1ldGhvZCBvbmx5IHdvcmtzIGlmIHRoZSBgYWRiX3NjcmVlbl9zdHJlYW1pbmdgIGZlYXR1cmUgaXNcbiAqIGVuYWJsZWQgb24gdGhlIHNlcnZlciBzaWRlLlxuICpcbiAqIEBwYXJhbSB7P1N0YXJ0U2NyZWVuU3RyZWFtaW5nT3B0aW9uc30gb3B0aW9ucyAtIFRoZSBhdmFpbGFibGUgb3B0aW9ucy5cbiAqIEB0aHJvd3Mge0Vycm9yfSBJZiBzY3JlZW4gc3RyZWFtaW5nIGhhcyBmYWlsZWQgdG8gc3RhcnQgb3JcbiAqIGlzIG5vdCBzdXBwb3J0ZWQgb24gdGhlIGhvc3Qgc3lzdGVtIG9yXG4gKiB0aGUgY29ycmVzcG9uZGluZyBzZXJ2ZXIgZmVhdHVyZSBpcyBub3QgZW5hYmxlZC5cbiAqL1xuY29tbWFuZHMubW9iaWxlU3RhcnRTY3JlZW5TdHJlYW1pbmcgPSBhc3luYyBmdW5jdGlvbiBtb2JpbGVTdGFydFNjcmVlblN0cmVhbWluZyAob3B0aW9ucyA9IHt9KSB7XG4gIHRoaXMuZW5zdXJlRmVhdHVyZUVuYWJsZWQoQURCX1NDUkVFTl9TVFJFQU1JTkdfRkVBVFVSRSk7XG5cbiAgY29uc3Qge1xuICAgIHdpZHRoLFxuICAgIGhlaWdodCxcbiAgICBiaXRSYXRlLFxuICAgIGhvc3QgPSBERUZBVUxUX0hPU1QsXG4gICAgcG9ydCA9IERFRkFVTFRfUE9SVCxcbiAgICBwYXRobmFtZSxcbiAgICB0Y3BQb3J0ID0gREVGQVVMVF9QT1JUICsgMSxcbiAgICBxdWFsaXR5ID0gREVGQVVMVF9RVUFMSVRZLFxuICAgIGNvbnNpZGVyUm90YXRpb24gPSBmYWxzZSxcbiAgICBsb2dQaXBlbGluZURldGFpbHMgPSBmYWxzZSxcbiAgfSA9IG9wdGlvbnM7XG5cbiAgaWYgKF8uaXNVbmRlZmluZWQodGhpcy5fc2NyZWVuU3RyZWFtaW5nUHJvcHMpKSB7XG4gICAgYXdhaXQgdmVyaWZ5U3RyZWFtaW5nUmVxdWlyZW1lbnRzKHRoaXMuYWRiKTtcbiAgfVxuICBpZiAoIV8uaXNFbXB0eSh0aGlzLl9zY3JlZW5TdHJlYW1pbmdQcm9wcykpIHtcbiAgICB0aGlzLmxvZy5pbmZvKGBUaGUgc2NyZWVuIHN0cmVhbWluZyBzZXNzaW9uIGlzIGFscmVhZHkgcnVubmluZy4gYCArXG4gICAgICBgU3RvcCBpdCBmaXJzdCBpbiBvcmRlciB0byBzdGFydCBhIG5ldyBvbmUuYCk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGlmICgoYXdhaXQgY2hlY2tQb3J0U3RhdHVzKHBvcnQsIGhvc3QpKSA9PT0gJ29wZW4nKSB7XG4gICAgdGhpcy5sb2cuaW5mbyhgVGhlIHBvcnQgIyR7cG9ydH0gYXQgJHtob3N0fSBpcyBidXN5LiBgICtcbiAgICAgIGBBc3N1bWluZyB0aGUgc2NyZWVuIHN0cmVhbWluZyBpcyBhbHJlYWR5IHJ1bm5pbmdgKTtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKChhd2FpdCBjaGVja1BvcnRTdGF0dXModGNwUG9ydCwgVENQX0hPU1QpKSA9PT0gJ29wZW4nKSB7XG4gICAgdGhpcy5sb2cuZXJyb3JBbmRUaHJvdyhgVGhlIHBvcnQgIyR7dGNwUG9ydH0gYXQgJHtUQ1BfSE9TVH0gaXMgYnVzeS4gYCArXG4gICAgICBgTWFrZSBzdXJlIHRoZXJlIGFyZSBubyBsZWZ0b3ZlcnMgZnJvbSBwcmV2aW91cyBzZXNzaW9ucy5gKTtcbiAgfVxuICB0aGlzLl9zY3JlZW5TdHJlYW1pbmdQcm9wcyA9IG51bGw7XG5cbiAgY29uc3QgZGV2aWNlSW5mbyA9IGF3YWl0IGdldERldmljZUluZm8odGhpcy5hZGIsIHRoaXMubG9nKTtcbiAgY29uc3QgZGV2aWNlU3RyZWFtaW5nUHJvYyA9IGF3YWl0IGluaXREZXZpY2VTdHJlYW1pbmdQcm9jKHRoaXMuYWRiLCB0aGlzLmxvZywgZGV2aWNlSW5mbywge1xuICAgIHdpZHRoLFxuICAgIGhlaWdodCxcbiAgICBiaXRSYXRlLFxuICB9KTtcbiAgbGV0IGdzdHJlYW1lclBpcGVsaW5lO1xuICB0cnkge1xuICAgIGdzdHJlYW1lclBpcGVsaW5lID0gYXdhaXQgaW5pdEdzdHJlYW1lclBpcGVsaW5lKGRldmljZVN0cmVhbWluZ1Byb2MsIGRldmljZUluZm8sIHRoaXMubG9nLCB7XG4gICAgICB3aWR0aCxcbiAgICAgIGhlaWdodCxcbiAgICAgIHF1YWxpdHksXG4gICAgICB0Y3BQb3J0LFxuICAgICAgY29uc2lkZXJSb3RhdGlvbixcbiAgICAgIGxvZ1BpcGVsaW5lRGV0YWlscyxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGlmIChkZXZpY2VTdHJlYW1pbmdQcm9jLmtpbGwoMCkpIHtcbiAgICAgIGRldmljZVN0cmVhbWluZ1Byb2Mua2lsbCgpO1xuICAgIH1cbiAgICB0aHJvdyBlO1xuICB9XG5cbiAgbGV0IG1qcGVnU29ja2V0O1xuICBsZXQgbWpwZWdTZXJ2ZXI7XG4gIHRyeSB7XG4gICAgYXdhaXQgbmV3IEIoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgbWpwZWdTb2NrZXQgPSBuZXQuY3JlYXRlQ29ubmVjdGlvbih0Y3BQb3J0LCBUQ1BfSE9TVCwgKCkgPT4ge1xuICAgICAgICB0aGlzLmxvZy5pbmZvKGBTdWNjZXNzZnVsbHkgY29ubmVjdGVkIHRvIE1KUEVHIHN0cmVhbSBhdCB0Y3A6Ly8ke1RDUF9IT1NUfToke3RjcFBvcnR9YCk7XG4gICAgICAgIG1qcGVnU2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoKHJlcSwgcmVzKSA9PiB7XG4gICAgICAgICAgY29uc3QgcmVtb3RlQWRkcmVzcyA9IGV4dHJhY3RSZW1vdGVBZGRyZXNzKHJlcSk7XG4gICAgICAgICAgY29uc3QgY3VycmVudFBhdGhuYW1lID0gdXJsLnBhcnNlKHJlcS51cmwpLnBhdGhuYW1lO1xuICAgICAgICAgIHRoaXMubG9nLmluZm8oYEdvdCBhbiBpbmNvbWluZyBzY3JlZW4gYnJhZGNhc3RpbmcgcmVxdWVzdCBmcm9tICR7cmVtb3RlQWRkcmVzc30gYCArXG4gICAgICAgICAgICBgKCR7cmVxLmhlYWRlcnNbJ3VzZXItYWdlbnQnXSB8fCAnVXNlciBBZ2VudCB1bmtub3duJ30pIGF0ICR7Y3VycmVudFBhdGhuYW1lfWApO1xuXG4gICAgICAgICAgaWYgKHBhdGhuYW1lICYmIGN1cnJlbnRQYXRobmFtZSAhPT0gcGF0aG5hbWUpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLmluZm8oJ1JlamVjdGluZyB0aGUgYnJvYWRjYXN0IHJlcXVlc3Qgc2luY2UgaXQgZG9lcyBub3QgbWF0Y2ggdGhlIGdpdmVuIHBhdGhuYW1lJyk7XG4gICAgICAgICAgICByZXMud3JpdGVIZWFkKDQwNCwge1xuICAgICAgICAgICAgICBDb25uZWN0aW9uOiAnY2xvc2UnLFxuICAgICAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ3RleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgnLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXMud3JpdGUoYCcke2N1cnJlbnRQYXRobmFtZX0nIGRpZCBub3QgbWF0Y2ggYW55IGtub3duIGVuZHBvaW50c2ApO1xuICAgICAgICAgICAgcmVzLmVuZCgpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRoaXMubG9nLmluZm8oJ1N0YXJ0aW5nIE1KUEVHIGJyb2FkY2FzdCcpO1xuICAgICAgICAgIHJlcy53cml0ZUhlYWQoMjAwLCB7XG4gICAgICAgICAgICAnQ2FjaGUtQ29udHJvbCc6ICduby1zdG9yZSwgbm8tY2FjaGUsIG11c3QtcmV2YWxpZGF0ZSwgcHJlLWNoZWNrPTAsIHBvc3QtY2hlY2s9MCwgbWF4LWFnZT0wJyxcbiAgICAgICAgICAgIFByYWdtYTogJ25vLWNhY2hlJyxcbiAgICAgICAgICAgIENvbm5lY3Rpb246ICdjbG9zZScsXG4gICAgICAgICAgICAnQ29udGVudC1UeXBlJzogYG11bHRpcGFydC94LW1peGVkLXJlcGxhY2U7IGJvdW5kYXJ5PSR7Qk9VTkRBUllfU1RSSU5HfWBcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIG1qcGVnU29ja2V0LnBpcGUocmVzKTtcbiAgICAgICAgfSk7XG4gICAgICAgIG1qcGVnU2VydmVyLm9uKCdlcnJvcicsIChlKSA9PiB7XG4gICAgICAgICAgdGhpcy5sb2cud2FybihlKTtcbiAgICAgICAgICByZWplY3QoZSk7XG4gICAgICAgIH0pO1xuICAgICAgICBtanBlZ1NlcnZlci5vbignY2xvc2UnLCAoKSA9PiB7XG4gICAgICAgICAgdGhpcy5sb2cuZGVidWcoYE1KUEVHIHNlcnZlciBhdCBodHRwOi8vJHtob3N0fToke3BvcnR9IGhhcyBiZWVuIGNsb3NlZGApO1xuICAgICAgICB9KTtcbiAgICAgICAgbWpwZWdTZXJ2ZXIub24oJ2xpc3RlbmluZycsICgpID0+IHtcbiAgICAgICAgICB0aGlzLmxvZy5pbmZvKGBTdWNjZXNzZnVsbHkgc3RhcnRlZCBNSlBFRyBzZXJ2ZXIgYXQgaHR0cDovLyR7aG9zdH06JHtwb3J0fWApO1xuICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIG1qcGVnU2VydmVyLmxpc3Rlbihwb3J0LCBob3N0KTtcbiAgICAgIH0pO1xuICAgICAgbWpwZWdTb2NrZXQub24oJ2Vycm9yJywgKGUpID0+IHtcbiAgICAgICAgdGhpcy5sb2cuZXJyb3IoZSk7XG4gICAgICAgIHJlamVjdChlKTtcbiAgICAgIH0pO1xuICAgIH0pLnRpbWVvdXQoU1RSRUFNSU5HX1NUQVJUVVBfVElNRU9VVF9NUyxcbiAgICAgIGBDYW5ub3QgY29ubmVjdCB0byB0aGUgc3RyZWFtaW5nIHNlcnZlciB3aXRoaW4gJHtTVFJFQU1JTkdfU1RBUlRVUF9USU1FT1VUX01TfW1zYCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBpZiAoZGV2aWNlU3RyZWFtaW5nUHJvYy5raWxsKDApKSB7XG4gICAgICBkZXZpY2VTdHJlYW1pbmdQcm9jLmtpbGwoKTtcbiAgICB9XG4gICAgaWYgKGdzdHJlYW1lclBpcGVsaW5lLmlzUnVubmluZykge1xuICAgICAgYXdhaXQgZ3N0cmVhbWVyUGlwZWxpbmUuc3RvcCgpO1xuICAgIH1cbiAgICBpZiAobWpwZWdTb2NrZXQpIHtcbiAgICAgIG1qcGVnU29ja2V0LmRlc3Ryb3koKTtcbiAgICB9XG4gICAgaWYgKG1qcGVnU2VydmVyICYmIG1qcGVnU2VydmVyLmxpc3RlbmluZykge1xuICAgICAgbWpwZWdTZXJ2ZXIuY2xvc2UoKTtcbiAgICB9XG4gICAgdGhyb3cgZTtcbiAgfVxuXG4gIHRoaXMuX3NjcmVlblN0cmVhbWluZ1Byb3BzID0ge1xuICAgIGRldmljZVN0cmVhbWluZ1Byb2MsXG4gICAgZ3N0cmVhbWVyUGlwZWxpbmUsXG4gICAgbWpwZWdTb2NrZXQsXG4gICAgbWpwZWdTZXJ2ZXIsXG4gIH07XG59O1xuXG4vKipcbiAqIFN0b3Agc2NyZWVuIHN0cmVhbWluZy5cbiAqIElmIG5vIHNjcmVlbiBzdHJlYW1pbmcgc2VydmVyIGhhcyBiZWVuIHN0YXJ0ZWQgdGhlbiBub3RoaW5nIGlzIGRvbmUuXG4gKi9cbmNvbW1hbmRzLm1vYmlsZVN0b3BTY3JlZW5TdHJlYW1pbmcgPSBhc3luYyBmdW5jdGlvbiBtb2JpbGVTdG9wU2NyZWVuU3RyZWFtaW5nICgvKiBvcHRpb25zID0ge30gKi8pIHtcbiAgaWYgKF8uaXNFbXB0eSh0aGlzLl9zY3JlZW5TdHJlYW1pbmdQcm9wcykpIHtcbiAgICBpZiAoIV8uaXNVbmRlZmluZWQodGhpcy5fc2NyZWVuU3RyZWFtaW5nUHJvcHMpKSB7XG4gICAgICB0aGlzLmxvZy5kZWJ1ZyhgU2NyZWVuIHN0cmVhbWluZyBpcyBub3QgcnVubmluZy4gVGhlcmUgaXMgbm90aGluZyB0byBzdG9wYCk7XG4gICAgfVxuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHtcbiAgICBkZXZpY2VTdHJlYW1pbmdQcm9jLFxuICAgIGdzdHJlYW1lclBpcGVsaW5lLFxuICAgIG1qcGVnU29ja2V0LFxuICAgIG1qcGVnU2VydmVyLFxuICB9ID0gdGhpcy5fc2NyZWVuU3RyZWFtaW5nUHJvcHM7XG5cbiAgdHJ5IHtcbiAgICBtanBlZ1NvY2tldC5lbmQoKTtcbiAgICBpZiAobWpwZWdTZXJ2ZXIubGlzdGVuaW5nKSB7XG4gICAgICBtanBlZ1NlcnZlci5jbG9zZSgpO1xuICAgIH1cbiAgICBpZiAoZGV2aWNlU3RyZWFtaW5nUHJvYy5raWxsKDApKSB7XG4gICAgICBkZXZpY2VTdHJlYW1pbmdQcm9jLmtpbGwoJ1NJR0lOVCcpO1xuICAgIH1cbiAgICBpZiAoZ3N0cmVhbWVyUGlwZWxpbmUuaXNSdW5uaW5nKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBnc3RyZWFtZXJQaXBlbGluZS5zdG9wKCdTSUdJTlQnKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdGhpcy5sb2cud2FybihlKTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBnc3RyZWFtZXJQaXBlbGluZS5zdG9wKCdTSUdLSUxMJyk7XG4gICAgICAgIH0gY2F0Y2ggKGUxKSB7XG4gICAgICAgICAgdGhpcy5sb2cuZXJyb3IoZTEpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMubG9nLmluZm8oYFN1Y2Nlc3NmdWxseSB0ZXJtaW5hdGVkIHRoZSBzY3JlZW4gc3RyZWFtaW5nIE1KUEVHIHNlcnZlcmApO1xuICB9IGZpbmFsbHkge1xuICAgIHRoaXMuX3NjcmVlblN0cmVhbWluZ1Byb3BzID0gbnVsbDtcbiAgfVxufTtcblxuXG5leHBvcnQgZGVmYXVsdCBjb21tYW5kcztcbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFBQSxJQUFBQSxPQUFBLEdBQUFDLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBQyxRQUFBLEdBQUFELE9BQUE7QUFDQSxJQUFBRSxhQUFBLEdBQUFGLE9BQUE7QUFDQSxJQUFBRyxZQUFBLEdBQUFILE9BQUE7QUFDQSxJQUFBSSxLQUFBLEdBQUFMLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBSyxJQUFBLEdBQUFOLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBTSxTQUFBLEdBQUFQLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBTyxTQUFBLEdBQUFQLE9BQUE7QUFDQSxJQUFBUSxjQUFBLEdBQUFSLE9BQUE7QUFDQSxJQUFBUyxJQUFBLEdBQUFWLHNCQUFBLENBQUFDLE9BQUE7QUFFQSxNQUFNVSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0FBRW5CLE1BQU1DLHNCQUFzQixHQUFHLENBQUM7QUFDaEMsTUFBTUMsNEJBQTRCLEdBQUcsSUFBSTtBQUN6QyxNQUFNQyxnQkFBZ0IsR0FBSSxpQkFBZ0JDLGVBQU0sQ0FBQ0MsU0FBUyxDQUFDLENBQUMsR0FBRyxNQUFNLEdBQUcsRUFBRyxFQUFDO0FBQzVFLE1BQU1DLGtCQUFrQixHQUFJLGtCQUFpQkYsZUFBTSxDQUFDQyxTQUFTLENBQUMsQ0FBQyxHQUFHLE1BQU0sR0FBRyxFQUFHLEVBQUM7QUFDL0UsTUFBTUUsb0JBQW9CLEdBQUc7RUFDM0JDLFVBQVUsRUFBRSxXQUFXO0VBQ3ZCQyxTQUFTLEVBQUUsaUJBQWlCO0VBQzVCQyxPQUFPLEVBQUUsa0JBQWtCO0VBQzNCQyxhQUFhLEVBQUUsa0JBQWtCO0VBQ2pDQyxZQUFZLEVBQUU7QUFDaEIsQ0FBQztBQUNELE1BQU1DLG1CQUFtQixHQUFHLGNBQWM7QUFDMUMsTUFBTUMsZ0JBQWdCLEdBQUcsdUVBQXVFO0FBQ2hHLE1BQU1DLFlBQVksR0FBRyxXQUFXO0FBQ2hDLE1BQU1DLFFBQVEsR0FBRyxXQUFXO0FBQzVCLE1BQU1DLFlBQVksR0FBRyxJQUFJO0FBQ3pCLE1BQU1DLGVBQWUsR0FBRyxFQUFFO0FBQzFCLE1BQU1DLGVBQWUsR0FBRyxPQUFPO0FBQy9CLE1BQU1DLGVBQWUsR0FBRyxvQ0FBb0M7QUFFNUQsTUFBTUMsNEJBQTRCLEdBQUcsc0JBQXNCO0FBRTNELFNBQVNDLHFCQUFxQkEsQ0FBRUMsVUFBVSxFQUFFQyxJQUFJLEVBQUU7RUFDaEQsT0FBT0MsZUFBTSxDQUFDQyxTQUFTLENBQUUsR0FBRUgsVUFBVyxHQUFFLEdBQUdJLGVBQUMsQ0FBQ0MsUUFBUSxDQUFDSixJQUFJLEVBQUU7SUFDMURLLE1BQU0sRUFBRSxDQUFDO0lBQ1RDLFFBQVEsRUFBRTtFQUNaLENBQUMsQ0FBQyxDQUFDO0FBQ0w7QUFFQSxlQUFlQywyQkFBMkJBLENBQUVDLEdBQUcsRUFBRTtFQUMvQyxJQUFJLENBQUNMLGVBQUMsQ0FBQ00sSUFBSSxDQUFDLE1BQU1ELEdBQUcsQ0FBQ0UsS0FBSyxDQUFDLENBQUMsT0FBTyxFQUFFckIsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLEVBQUU7SUFDNUQsTUFBTSxJQUFJc0IsS0FBSyxDQUNaLGlCQUFnQnRCLG1CQUFvQixvREFBbUQsQ0FBQztFQUM3RjtFQUVBLE1BQU11QixzQkFBc0IsR0FBRyxFQUFFO0VBQ2pDLEtBQUssTUFBTUMsVUFBVSxJQUFJLENBQUNsQyxnQkFBZ0IsRUFBRUcsa0JBQWtCLENBQUMsRUFBRTtJQUMvRDhCLHNCQUFzQixDQUFDRSxJQUFJLENBQUMsQ0FBQyxZQUFZO01BQ3ZDLElBQUk7UUFDRixNQUFNQyxXQUFFLENBQUNDLEtBQUssQ0FBQ0gsVUFBVSxDQUFDO01BQzVCLENBQUMsQ0FBQyxPQUFPSSxDQUFDLEVBQUU7UUFDVixNQUFNLElBQUlOLEtBQUssQ0FBRSxRQUFPRSxVQUFXLDREQUEyRCxHQUMzRixPQUFNdkIsZ0JBQWlCLHlDQUF3QyxDQUFDO01BQ3JFO0lBQ0YsQ0FBQyxFQUFFLENBQUMsQ0FBQztFQUNQO0VBQ0EsTUFBTTRCLGlCQUFDLENBQUNDLEdBQUcsQ0FBQ1Asc0JBQXNCLENBQUM7RUFFbkMsTUFBTVEsbUJBQW1CLEdBQUcsRUFBRTtFQUM5QixLQUFLLE1BQU0sQ0FBQ0MsSUFBSSxFQUFFQyxPQUFPLENBQUMsSUFBSW5CLGVBQUMsQ0FBQ29CLE9BQU8sQ0FBQ3hDLG9CQUFvQixDQUFDLEVBQUU7SUFDN0RxQyxtQkFBbUIsQ0FBQ04sSUFBSSxDQUFDLENBQUMsWUFBWTtNQUNwQyxNQUFNO1FBQUNVO01BQU0sQ0FBQyxHQUFHLE1BQU0sSUFBQUMsa0JBQUksRUFBQzNDLGtCQUFrQixFQUFFLENBQUN1QyxJQUFJLENBQUMsQ0FBQztNQUN2RCxJQUFJLENBQUNsQixlQUFDLENBQUN1QixRQUFRLENBQUNGLE1BQU0sRUFBRUYsT0FBTyxDQUFDLEVBQUU7UUFDaEMsTUFBTSxJQUFJWCxLQUFLLENBQ1osa0NBQWlDVSxJQUFLLFdBQVVDLE9BQVEsNkJBQTRCLEdBQ3BGLE9BQU1oQyxnQkFBaUIseUNBQXdDLENBQUM7TUFDckU7SUFDRixDQUFDLEVBQUUsQ0FBQyxDQUFDO0VBQ1A7RUFDQSxNQUFNNEIsaUJBQUMsQ0FBQ0MsR0FBRyxDQUFDQyxtQkFBbUIsQ0FBQztBQUNsQztBQUVBLGVBQWVPLGFBQWFBLENBQUVuQixHQUFHLEVBQUVvQixHQUFHLEdBQUcsSUFBSSxFQUFFO0VBQzdDLE1BQU1DLE1BQU0sR0FBRyxNQUFNckIsR0FBRyxDQUFDRSxLQUFLLENBQUMsQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLENBQUM7RUFDdEQsTUFBTW9CLE1BQU0sR0FBRyxDQUFDLENBQUM7RUFDakIsS0FBSyxNQUFNLENBQUNDLEdBQUcsRUFBRUMsT0FBTyxDQUFDLElBQUksQ0FDM0IsQ0FBQyxPQUFPLEVBQUUscUJBQXFCLENBQUMsRUFDaEMsQ0FBQyxRQUFRLEVBQUUsc0JBQXNCLENBQUMsRUFDbEMsQ0FBQyxLQUFLLEVBQUUsYUFBYSxDQUFDLENBQ3ZCLEVBQUU7SUFDRCxNQUFNQyxLQUFLLEdBQUdELE9BQU8sQ0FBQ1AsSUFBSSxDQUFDSSxNQUFNLENBQUM7SUFDbEMsSUFBSSxDQUFDSSxLQUFLLEVBQUU7TUFDVkwsR0FBRyxhQUFIQSxHQUFHLHVCQUFIQSxHQUFHLENBQUVNLEtBQUssQ0FBQ0wsTUFBTSxDQUFDO01BQ2xCLE1BQU0sSUFBSWxCLEtBQUssQ0FBRSwyQkFBMEJvQixHQUFJLGdDQUErQixHQUMzRSx3Q0FBdUMsQ0FBQztJQUM3QztJQUNBRCxNQUFNLENBQUNDLEdBQUcsQ0FBQyxHQUFHSSxRQUFRLENBQUNGLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUM7RUFDdEM7RUFDQUgsTUFBTSxDQUFDOUIsSUFBSSxHQUFHUSxHQUFHLENBQUM0QixXQUFXO0VBQzdCLE9BQU9OLE1BQU07QUFDZjtBQUVBLGVBQWVPLHVCQUF1QkEsQ0FBRTdCLEdBQUcsRUFBRW9CLEdBQUcsRUFBRVUsVUFBVSxFQUFFQyxJQUFJLEdBQUcsQ0FBQyxDQUFDLEVBQUU7RUFDdkUsTUFBTTtJQUNKQyxLQUFLO0lBQ0xDLE1BQU07SUFDTkM7RUFDRixDQUFDLEdBQUdILElBQUk7RUFDUixNQUFNSSxhQUFhLEdBQUdSLFFBQVEsQ0FBQ0ssS0FBSyxFQUFFLEVBQUUsQ0FBQyxJQUFJRixVQUFVLENBQUNFLEtBQUs7RUFDN0QsTUFBTUksY0FBYyxHQUFHVCxRQUFRLENBQUNNLE1BQU0sRUFBRSxFQUFFLENBQUMsSUFBSUgsVUFBVSxDQUFDRyxNQUFNO0VBQ2hFLE1BQU1JLGVBQWUsR0FBR1YsUUFBUSxDQUFDTyxPQUFPLEVBQUUsRUFBRSxDQUFDLElBQUkvQyxlQUFlO0VBQ2hFLElBQUltRCxlQUFlLEdBQUd6RCxtQkFBbUIsR0FDdEMsdUJBQXNCLEdBRXRCLGlCQUFnQlosc0JBQXVCLEVBQUM7RUFDM0MsSUFBSStELEtBQUssSUFBSUMsTUFBTSxFQUFFO0lBQ25CSyxlQUFlLElBQUssV0FBVUgsYUFBYyxJQUFHQyxjQUFlLEVBQUM7RUFDakU7RUFDQSxJQUFJRixPQUFPLEVBQUU7SUFDWEksZUFBZSxJQUFLLGVBQWNELGVBQWdCLEVBQUM7RUFDckQ7RUFDQSxNQUFNRSxPQUFPLEdBQUcsQ0FDZCxHQUFHdkMsR0FBRyxDQUFDd0MsVUFBVSxDQUFDQyxXQUFXLEVBQzdCLFVBQVUsRUFHVCxrQkFBaUJILGVBQWdCLFVBQVMsQ0FDNUM7RUFDRCxNQUFNSSxlQUFlLEdBQUcsSUFBQUMsb0JBQUssRUFBQzNDLEdBQUcsQ0FBQ3dDLFVBQVUsQ0FBQ0ksSUFBSSxFQUFFTCxPQUFPLENBQUM7RUFDM0RHLGVBQWUsQ0FBQ0csRUFBRSxDQUFDLE1BQU0sRUFBRSxDQUFDQyxJQUFJLEVBQUVDLE1BQU0sS0FBSztJQUMzQzNCLEdBQUcsQ0FBQ00sS0FBSyxDQUFFLDZDQUE0Q29CLElBQUssWUFBV0MsTUFBTyxFQUFDLENBQUM7RUFDbEYsQ0FBQyxDQUFDO0VBRUYsSUFBSUMsU0FBUyxHQUFHLEtBQUs7RUFDckIsTUFBTUMscUJBQXFCLEdBQUczRCxxQkFBcUIsQ0FBQ1QsbUJBQW1CLEVBQUVpRCxVQUFVLENBQUN0QyxJQUFJLENBQUM7RUFDekYsTUFBTTBELGNBQWMsR0FBSUMsS0FBSyxJQUFLO0lBQ2hDLE1BQU1DLE1BQU0sR0FBR0QsS0FBSyxDQUFDRSxRQUFRLENBQUMsQ0FBQztJQUMvQixJQUFJMUQsZUFBQyxDQUFDTSxJQUFJLENBQUNtRCxNQUFNLENBQUMsRUFBRTtNQUNsQkgscUJBQXFCLENBQUN2QixLQUFLLENBQUMwQixNQUFNLENBQUM7SUFDckM7RUFDRixDQUFDO0VBQ0RWLGVBQWUsQ0FBQ1UsTUFBTSxDQUFDUCxFQUFFLENBQUMsTUFBTSxFQUFFSyxjQUFjLENBQUM7RUFFakQsTUFBTUksZUFBZSxHQUFJSCxLQUFLLElBQUs7SUFDakMsSUFBSSxDQUFDSCxTQUFTLEVBQUU7TUFDZEEsU0FBUyxHQUFHLENBQUNyRCxlQUFDLENBQUM0RCxPQUFPLENBQUNKLEtBQUssQ0FBQztJQUMvQjtFQUNGLENBQUM7RUFDRFQsZUFBZSxDQUFDMUIsTUFBTSxDQUFDNkIsRUFBRSxDQUFDLE1BQU0sRUFBRVMsZUFBZSxDQUFDO0VBRWxELElBQUk7SUFDRmxDLEdBQUcsQ0FBQ29DLElBQUksQ0FBRSw4QkFBNkJDLGFBQUksQ0FBQ0MsS0FBSyxDQUFDLENBQUMxRCxHQUFHLENBQUN3QyxVQUFVLENBQUNJLElBQUksRUFBRSxHQUFHTCxPQUFPLENBQUMsQ0FBRSxFQUFDLENBQUM7SUFDdkYsTUFBTSxJQUFBb0IsMEJBQWdCLEVBQUMsTUFBTVgsU0FBUyxFQUFFO01BQ3RDWSxNQUFNLEVBQUUxRiw0QkFBNEI7TUFDcEMyRixVQUFVLEVBQUU7SUFDZCxDQUFDLENBQUM7RUFDSixDQUFDLENBQUMsT0FBT3BELENBQUMsRUFBRTtJQUNWVyxHQUFHLENBQUMwQyxhQUFhLENBQ2QsOERBQTZEckQsQ0FBQyxDQUFDc0QsT0FBUSxFQUFDLENBQUM7RUFDOUUsQ0FBQyxTQUFTO0lBQ1JyQixlQUFlLENBQUNVLE1BQU0sQ0FBQ1ksY0FBYyxDQUFDLE1BQU0sRUFBRWQsY0FBYyxDQUFDO0lBQzdEUixlQUFlLENBQUMxQixNQUFNLENBQUNnRCxjQUFjLENBQUMsTUFBTSxFQUFFVixlQUFlLENBQUM7RUFDaEU7RUFDQSxPQUFPWixlQUFlO0FBQ3hCO0FBRUEsZUFBZXVCLHFCQUFxQkEsQ0FBRUMsbUJBQW1CLEVBQUVwQyxVQUFVLEVBQUVWLEdBQUcsRUFBRVcsSUFBSSxHQUFHLENBQUMsQ0FBQyxFQUFFO0VBQ3JGLE1BQU07SUFDSkMsS0FBSztJQUNMQyxNQUFNO0lBQ05rQyxPQUFPO0lBQ1BDLE9BQU87SUFDUEMsZ0JBQWdCO0lBQ2hCQztFQUNGLENBQUMsR0FBR3ZDLElBQUk7RUFDUixNQUFNSSxhQUFhLEdBQUdSLFFBQVEsQ0FBQ0ssS0FBSyxFQUFFLEVBQUUsQ0FBQyxJQUFJRixVQUFVLENBQUNFLEtBQUs7RUFDN0QsTUFBTUksY0FBYyxHQUFHVCxRQUFRLENBQUNNLE1BQU0sRUFBRSxFQUFFLENBQUMsSUFBSUgsVUFBVSxDQUFDRyxNQUFNO0VBQ2hFLE1BQU1zQyxpQkFBaUIsR0FBRyxJQUFJQyx3QkFBVSxDQUFDckcsZ0JBQWdCLEVBQUUsQ0FDekQsSUFBSSxFQUNKLE9BQU8sRUFBRSxNQUFNLEVBQ2YsR0FBRyxFQUFFLGVBQWUsR0FDakIsU0FBUWtHLGdCQUFnQixHQUFHSSxJQUFJLENBQUNDLEdBQUcsQ0FBQ3ZDLGFBQWEsRUFBRUMsY0FBYyxDQUFDLEdBQUdELGFBQWMsR0FBRSxHQUNyRixVQUFTa0MsZ0JBQWdCLEdBQUdJLElBQUksQ0FBQ0MsR0FBRyxDQUFDdkMsYUFBYSxFQUFFQyxjQUFjLENBQUMsR0FBR0EsY0FBZSxHQUFFLEdBQ3ZGLGFBQVlOLFVBQVUsQ0FBQzZDLEdBQUksS0FBSSxHQUNoQyxrQkFBa0IsRUFDcEIsR0FBRyxFQUFFLFdBQVcsRUFDaEIsR0FBRyxFQUFFLE9BQU8sRUFBRSxrQkFBa0IsRUFDaEMsR0FBRyxFQUFFLFlBQVksRUFDakIsR0FBRyxFQUFFLE9BQU8sRUFBRSxrQkFBa0IsRUFDaEMsR0FBRyxFQUFFLFNBQVMsRUFBRyxXQUFVUixPQUFRLEVBQUMsRUFDcEMsR0FBRyxFQUFFLGNBQWMsRUFBRyxZQUFXL0UsZUFBZ0IsRUFBQyxFQUNsRCxHQUFHLEVBQUUsZUFBZSxFQUFHLFFBQU9KLFFBQVMsRUFBQyxFQUFHLFFBQU9vRixPQUFRLEVBQUMsQ0FDNUQsRUFBRTtJQUNEUSxLQUFLLEVBQUUsQ0FBQ1YsbUJBQW1CLENBQUNsRCxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU07RUFDcEQsQ0FBQyxDQUFDO0VBQ0Z1RCxpQkFBaUIsQ0FBQzFCLEVBQUUsQ0FBQyxNQUFNLEVBQUUsQ0FBQ0MsSUFBSSxFQUFFQyxNQUFNLEtBQUs7SUFDN0MzQixHQUFHLENBQUNNLEtBQUssQ0FBRSwrQ0FBOENvQixJQUFLLFlBQVdDLE1BQU8sRUFBQyxDQUFDO0VBQ3BGLENBQUMsQ0FBQztFQUNGLE1BQU04QixlQUFlLEdBQUd2RixxQkFBcUIsQ0FBQyxLQUFLLEVBQUV3QyxVQUFVLENBQUN0QyxJQUFJLENBQUM7RUFDckUsTUFBTXNGLGlCQUFpQixHQUFHQSxDQUFDOUQsTUFBTSxFQUFFb0MsTUFBTSxLQUFLO0lBQzVDLElBQUl6RCxlQUFDLENBQUNNLElBQUksQ0FBQ21ELE1BQU0sSUFBSXBDLE1BQU0sQ0FBQyxFQUFFO01BQzVCNkQsZUFBZSxDQUFDbkQsS0FBSyxDQUFDMEIsTUFBTSxJQUFJcEMsTUFBTSxDQUFDO0lBQ3pDO0VBQ0YsQ0FBQztFQUNEdUQsaUJBQWlCLENBQUMxQixFQUFFLENBQUMsUUFBUSxFQUFFaUMsaUJBQWlCLENBQUM7RUFDakQsSUFBSUMsT0FBTyxHQUFHLEtBQUs7RUFDbkIsSUFBSTtJQUNGM0QsR0FBRyxDQUFDb0MsSUFBSSxDQUFFLGdDQUErQmUsaUJBQWlCLENBQUNTLEdBQUksRUFBQyxDQUFDO0lBQ2pFLE1BQU1ULGlCQUFpQixDQUFDVSxLQUFLLENBQUMsQ0FBQyxDQUFDO0lBQ2hDLE1BQU0sSUFBQXRCLDBCQUFnQixFQUFDLFlBQVk7TUFDakMsSUFBSTtRQUNGLE9BQU8sQ0FBQyxNQUFNLElBQUF1Qiw0QkFBZSxFQUFDZCxPQUFPLEVBQUVwRixRQUFRLENBQUMsTUFBTSxNQUFNO01BQzlELENBQUMsQ0FBQyxPQUFPbUcsR0FBRyxFQUFFO1FBQ1osT0FBTyxLQUFLO01BQ2Q7SUFDRixDQUFDLEVBQUU7TUFDRHZCLE1BQU0sRUFBRTFGLDRCQUE0QjtNQUNwQzJGLFVBQVUsRUFBRTtJQUNkLENBQUMsQ0FBQztFQUNKLENBQUMsQ0FBQyxPQUFPcEQsQ0FBQyxFQUFFO0lBQ1ZzRSxPQUFPLEdBQUcsSUFBSTtJQUNkM0QsR0FBRyxDQUFDMEMsYUFBYSxDQUNkLCtEQUE4RHJELENBQUMsQ0FBQ3NELE9BQVEsRUFBQyxDQUFDO0VBQy9FLENBQUMsU0FBUztJQUNSLElBQUksQ0FBQ08sa0JBQWtCLElBQUlTLE9BQU8sRUFBRTtNQUNsQ1IsaUJBQWlCLENBQUNQLGNBQWMsQ0FBQyxRQUFRLEVBQUVjLGlCQUFpQixDQUFDO0lBQy9EO0VBQ0Y7RUFDQSxPQUFPUCxpQkFBaUI7QUFDMUI7QUFFQSxTQUFTYSxvQkFBb0JBLENBQUVDLEdBQUcsRUFBRTtFQUNsQyxPQUFPQSxHQUFHLENBQUNDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxJQUNoQ0QsR0FBRyxDQUFDRSxNQUFNLENBQUNDLGFBQWEsSUFDeEJILEdBQUcsQ0FBQ0ksVUFBVSxDQUFDRCxhQUFhLElBQzVCSCxHQUFHLENBQUNJLFVBQVUsQ0FBQ0YsTUFBTSxDQUFDQyxhQUFhO0FBQzFDO0FBMkNBeEgsUUFBUSxDQUFDMEgsMEJBQTBCLEdBQUcsZUFBZUEsMEJBQTBCQSxDQUFFQyxPQUFPLEdBQUcsQ0FBQyxDQUFDLEVBQUU7RUFDN0YsSUFBSSxDQUFDQyxvQkFBb0IsQ0FBQ3ZHLDRCQUE0QixDQUFDO0VBRXZELE1BQU07SUFDSjJDLEtBQUs7SUFDTEMsTUFBTTtJQUNOQyxPQUFPO0lBQ1AyRCxJQUFJLEdBQUc5RyxZQUFZO0lBQ25CK0csSUFBSSxHQUFHN0csWUFBWTtJQUNuQjhHLFFBQVE7SUFDUjNCLE9BQU8sR0FBR25GLFlBQVksR0FBRyxDQUFDO0lBQzFCa0YsT0FBTyxHQUFHakYsZUFBZTtJQUN6Qm1GLGdCQUFnQixHQUFHLEtBQUs7SUFDeEJDLGtCQUFrQixHQUFHO0VBQ3ZCLENBQUMsR0FBR3FCLE9BQU87RUFFWCxJQUFJaEcsZUFBQyxDQUFDcUcsV0FBVyxDQUFDLElBQUksQ0FBQ0MscUJBQXFCLENBQUMsRUFBRTtJQUM3QyxNQUFNbEcsMkJBQTJCLENBQUMsSUFBSSxDQUFDQyxHQUFHLENBQUM7RUFDN0M7RUFDQSxJQUFJLENBQUNMLGVBQUMsQ0FBQzRELE9BQU8sQ0FBQyxJQUFJLENBQUMwQyxxQkFBcUIsQ0FBQyxFQUFFO0lBQzFDLElBQUksQ0FBQzdFLEdBQUcsQ0FBQ29DLElBQUksQ0FBRSxtREFBa0QsR0FDOUQsNENBQTJDLENBQUM7SUFDL0M7RUFDRjtFQUNBLElBQUksQ0FBQyxNQUFNLElBQUEwQiw0QkFBZSxFQUFDWSxJQUFJLEVBQUVELElBQUksQ0FBQyxNQUFNLE1BQU0sRUFBRTtJQUNsRCxJQUFJLENBQUN6RSxHQUFHLENBQUNvQyxJQUFJLENBQUUsYUFBWXNDLElBQUssT0FBTUQsSUFBSyxZQUFXLEdBQ25ELGtEQUFpRCxDQUFDO0lBQ3JEO0VBQ0Y7RUFDQSxJQUFJLENBQUMsTUFBTSxJQUFBWCw0QkFBZSxFQUFDZCxPQUFPLEVBQUVwRixRQUFRLENBQUMsTUFBTSxNQUFNLEVBQUU7SUFDekQsSUFBSSxDQUFDb0MsR0FBRyxDQUFDMEMsYUFBYSxDQUFFLGFBQVlNLE9BQVEsT0FBTXBGLFFBQVMsWUFBVyxHQUNuRSwwREFBeUQsQ0FBQztFQUMvRDtFQUNBLElBQUksQ0FBQ2lILHFCQUFxQixHQUFHLElBQUk7RUFFakMsTUFBTW5FLFVBQVUsR0FBRyxNQUFNWCxhQUFhLENBQUMsSUFBSSxDQUFDbkIsR0FBRyxFQUFFLElBQUksQ0FBQ29CLEdBQUcsQ0FBQztFQUMxRCxNQUFNOEMsbUJBQW1CLEdBQUcsTUFBTXJDLHVCQUF1QixDQUFDLElBQUksQ0FBQzdCLEdBQUcsRUFBRSxJQUFJLENBQUNvQixHQUFHLEVBQUVVLFVBQVUsRUFBRTtJQUN4RkUsS0FBSztJQUNMQyxNQUFNO0lBQ05DO0VBQ0YsQ0FBQyxDQUFDO0VBQ0YsSUFBSXFDLGlCQUFpQjtFQUNyQixJQUFJO0lBQ0ZBLGlCQUFpQixHQUFHLE1BQU1OLHFCQUFxQixDQUFDQyxtQkFBbUIsRUFBRXBDLFVBQVUsRUFBRSxJQUFJLENBQUNWLEdBQUcsRUFBRTtNQUN6RlksS0FBSztNQUNMQyxNQUFNO01BQ05rQyxPQUFPO01BQ1BDLE9BQU87TUFDUEMsZ0JBQWdCO01BQ2hCQztJQUNGLENBQUMsQ0FBQztFQUNKLENBQUMsQ0FBQyxPQUFPN0QsQ0FBQyxFQUFFO0lBQ1YsSUFBSXlELG1CQUFtQixDQUFDZ0MsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFO01BQy9CaEMsbUJBQW1CLENBQUNnQyxJQUFJLENBQUMsQ0FBQztJQUM1QjtJQUNBLE1BQU16RixDQUFDO0VBQ1Q7RUFFQSxJQUFJMEYsV0FBVztFQUNmLElBQUlDLFdBQVc7RUFDZixJQUFJO0lBQ0YsTUFBTSxJQUFJMUYsaUJBQUMsQ0FBQyxDQUFDMkYsT0FBTyxFQUFFQyxNQUFNLEtBQUs7TUFDL0JILFdBQVcsR0FBR0ksWUFBRyxDQUFDQyxnQkFBZ0IsQ0FBQ3BDLE9BQU8sRUFBRXBGLFFBQVEsRUFBRSxNQUFNO1FBQzFELElBQUksQ0FBQ29DLEdBQUcsQ0FBQ29DLElBQUksQ0FBRSxtREFBa0R4RSxRQUFTLElBQUdvRixPQUFRLEVBQUMsQ0FBQztRQUN2RmdDLFdBQVcsR0FBR0ssYUFBSSxDQUFDQyxZQUFZLENBQUMsQ0FBQ3JCLEdBQUcsRUFBRXNCLEdBQUcsS0FBSztVQUM1QyxNQUFNbkIsYUFBYSxHQUFHSixvQkFBb0IsQ0FBQ0MsR0FBRyxDQUFDO1VBQy9DLE1BQU11QixlQUFlLEdBQUdDLFlBQUcsQ0FBQ0MsS0FBSyxDQUFDekIsR0FBRyxDQUFDd0IsR0FBRyxDQUFDLENBQUNkLFFBQVE7VUFDbkQsSUFBSSxDQUFDM0UsR0FBRyxDQUFDb0MsSUFBSSxDQUFFLG1EQUFrRGdDLGFBQWMsR0FBRSxHQUM5RSxJQUFHSCxHQUFHLENBQUNDLE9BQU8sQ0FBQyxZQUFZLENBQUMsSUFBSSxvQkFBcUIsUUFBT3NCLGVBQWdCLEVBQUMsQ0FBQztVQUVqRixJQUFJYixRQUFRLElBQUlhLGVBQWUsS0FBS2IsUUFBUSxFQUFFO1lBQzVDLElBQUksQ0FBQzNFLEdBQUcsQ0FBQ29DLElBQUksQ0FBQyw0RUFBNEUsQ0FBQztZQUMzRm1ELEdBQUcsQ0FBQ0ksU0FBUyxDQUFDLEdBQUcsRUFBRTtjQUNqQkMsVUFBVSxFQUFFLE9BQU87Y0FDbkIsY0FBYyxFQUFFO1lBQ2xCLENBQUMsQ0FBQztZQUNGTCxHQUFHLENBQUNNLEtBQUssQ0FBRSxJQUFHTCxlQUFnQixxQ0FBb0MsQ0FBQztZQUNuRUQsR0FBRyxDQUFDTyxHQUFHLENBQUMsQ0FBQztZQUNUO1VBQ0Y7VUFFQSxJQUFJLENBQUM5RixHQUFHLENBQUNvQyxJQUFJLENBQUMsMEJBQTBCLENBQUM7VUFDekNtRCxHQUFHLENBQUNJLFNBQVMsQ0FBQyxHQUFHLEVBQUU7WUFDakIsZUFBZSxFQUFFLDJFQUEyRTtZQUM1RkksTUFBTSxFQUFFLFVBQVU7WUFDbEJILFVBQVUsRUFBRSxPQUFPO1lBQ25CLGNBQWMsRUFBRyx1Q0FBc0M1SCxlQUFnQjtVQUN6RSxDQUFDLENBQUM7VUFFRitHLFdBQVcsQ0FBQ2lCLElBQUksQ0FBQ1QsR0FBRyxDQUFDO1FBQ3ZCLENBQUMsQ0FBQztRQUNGUCxXQUFXLENBQUN2RCxFQUFFLENBQUMsT0FBTyxFQUFHcEMsQ0FBQyxJQUFLO1VBQzdCLElBQUksQ0FBQ1csR0FBRyxDQUFDaUcsSUFBSSxDQUFDNUcsQ0FBQyxDQUFDO1VBQ2hCNkYsTUFBTSxDQUFDN0YsQ0FBQyxDQUFDO1FBQ1gsQ0FBQyxDQUFDO1FBQ0YyRixXQUFXLENBQUN2RCxFQUFFLENBQUMsT0FBTyxFQUFFLE1BQU07VUFDNUIsSUFBSSxDQUFDekIsR0FBRyxDQUFDTSxLQUFLLENBQUUsMEJBQXlCbUUsSUFBSyxJQUFHQyxJQUFLLGtCQUFpQixDQUFDO1FBQzFFLENBQUMsQ0FBQztRQUNGTSxXQUFXLENBQUN2RCxFQUFFLENBQUMsV0FBVyxFQUFFLE1BQU07VUFDaEMsSUFBSSxDQUFDekIsR0FBRyxDQUFDb0MsSUFBSSxDQUFFLCtDQUE4Q3FDLElBQUssSUFBR0MsSUFBSyxFQUFDLENBQUM7VUFDNUVPLE9BQU8sQ0FBQyxDQUFDO1FBQ1gsQ0FBQyxDQUFDO1FBQ0ZELFdBQVcsQ0FBQ2tCLE1BQU0sQ0FBQ3hCLElBQUksRUFBRUQsSUFBSSxDQUFDO01BQ2hDLENBQUMsQ0FBQztNQUNGTSxXQUFXLENBQUN0RCxFQUFFLENBQUMsT0FBTyxFQUFHcEMsQ0FBQyxJQUFLO1FBQzdCLElBQUksQ0FBQ1csR0FBRyxDQUFDbUcsS0FBSyxDQUFDOUcsQ0FBQyxDQUFDO1FBQ2pCNkYsTUFBTSxDQUFDN0YsQ0FBQyxDQUFDO01BQ1gsQ0FBQyxDQUFDO0lBQ0osQ0FBQyxDQUFDLENBQUMrRyxPQUFPLENBQUN0Siw0QkFBNEIsRUFDcEMsaURBQWdEQSw0QkFBNkIsSUFBRyxDQUFDO0VBQ3RGLENBQUMsQ0FBQyxPQUFPdUMsQ0FBQyxFQUFFO0lBQ1YsSUFBSXlELG1CQUFtQixDQUFDZ0MsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFO01BQy9CaEMsbUJBQW1CLENBQUNnQyxJQUFJLENBQUMsQ0FBQztJQUM1QjtJQUNBLElBQUkzQixpQkFBaUIsQ0FBQ2tELFNBQVMsRUFBRTtNQUMvQixNQUFNbEQsaUJBQWlCLENBQUNtRCxJQUFJLENBQUMsQ0FBQztJQUNoQztJQUNBLElBQUl2QixXQUFXLEVBQUU7TUFDZkEsV0FBVyxDQUFDd0IsT0FBTyxDQUFDLENBQUM7SUFDdkI7SUFDQSxJQUFJdkIsV0FBVyxJQUFJQSxXQUFXLENBQUN3QixTQUFTLEVBQUU7TUFDeEN4QixXQUFXLENBQUN5QixLQUFLLENBQUMsQ0FBQztJQUNyQjtJQUNBLE1BQU1wSCxDQUFDO0VBQ1Q7RUFFQSxJQUFJLENBQUN3RixxQkFBcUIsR0FBRztJQUMzQi9CLG1CQUFtQjtJQUNuQkssaUJBQWlCO0lBQ2pCNEIsV0FBVztJQUNYQztFQUNGLENBQUM7QUFDSCxDQUFDO0FBTURwSSxRQUFRLENBQUM4Six5QkFBeUIsR0FBRyxlQUFlQSx5QkFBeUJBLENBQUEsRUFBc0I7RUFDakcsSUFBSW5JLGVBQUMsQ0FBQzRELE9BQU8sQ0FBQyxJQUFJLENBQUMwQyxxQkFBcUIsQ0FBQyxFQUFFO0lBQ3pDLElBQUksQ0FBQ3RHLGVBQUMsQ0FBQ3FHLFdBQVcsQ0FBQyxJQUFJLENBQUNDLHFCQUFxQixDQUFDLEVBQUU7TUFDOUMsSUFBSSxDQUFDN0UsR0FBRyxDQUFDTSxLQUFLLENBQUUsMkRBQTBELENBQUM7SUFDN0U7SUFDQTtFQUNGO0VBRUEsTUFBTTtJQUNKd0MsbUJBQW1CO0lBQ25CSyxpQkFBaUI7SUFDakI0QixXQUFXO0lBQ1hDO0VBQ0YsQ0FBQyxHQUFHLElBQUksQ0FBQ0gscUJBQXFCO0VBRTlCLElBQUk7SUFDRkUsV0FBVyxDQUFDZSxHQUFHLENBQUMsQ0FBQztJQUNqQixJQUFJZCxXQUFXLENBQUN3QixTQUFTLEVBQUU7TUFDekJ4QixXQUFXLENBQUN5QixLQUFLLENBQUMsQ0FBQztJQUNyQjtJQUNBLElBQUkzRCxtQkFBbUIsQ0FBQ2dDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRTtNQUMvQmhDLG1CQUFtQixDQUFDZ0MsSUFBSSxDQUFDLFFBQVEsQ0FBQztJQUNwQztJQUNBLElBQUkzQixpQkFBaUIsQ0FBQ2tELFNBQVMsRUFBRTtNQUMvQixJQUFJO1FBQ0YsTUFBTWxELGlCQUFpQixDQUFDbUQsSUFBSSxDQUFDLFFBQVEsQ0FBQztNQUN4QyxDQUFDLENBQUMsT0FBT2pILENBQUMsRUFBRTtRQUNWLElBQUksQ0FBQ1csR0FBRyxDQUFDaUcsSUFBSSxDQUFDNUcsQ0FBQyxDQUFDO1FBQ2hCLElBQUk7VUFDRixNQUFNOEQsaUJBQWlCLENBQUNtRCxJQUFJLENBQUMsU0FBUyxDQUFDO1FBQ3pDLENBQUMsQ0FBQyxPQUFPSyxFQUFFLEVBQUU7VUFDWCxJQUFJLENBQUMzRyxHQUFHLENBQUNtRyxLQUFLLENBQUNRLEVBQUUsQ0FBQztRQUNwQjtNQUNGO0lBQ0Y7SUFDQSxJQUFJLENBQUMzRyxHQUFHLENBQUNvQyxJQUFJLENBQUUsMkRBQTBELENBQUM7RUFDNUUsQ0FBQyxTQUFTO0lBQ1IsSUFBSSxDQUFDeUMscUJBQXFCLEdBQUcsSUFBSTtFQUNuQztBQUNGLENBQUM7QUFBQyxJQUFBK0IsUUFBQSxHQUdhaEssUUFBUTtBQUFBaUssT0FBQSxDQUFBQyxPQUFBLEdBQUFGLFFBQUEifQ==
|
|
407
|
+
exports.default = commands;
|
|
408
|
+
//# sourceMappingURL=streamscreen.js.map
|