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.
Files changed (151) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/build/index.js +43 -40
  3. package/build/lib/android-helpers.d.ts +136 -0
  4. package/build/lib/android-helpers.d.ts.map +1 -0
  5. package/build/lib/android-helpers.js +760 -679
  6. package/build/lib/android-helpers.js.map +1 -1
  7. package/build/lib/bootstrap.d.ts +29 -0
  8. package/build/lib/bootstrap.d.ts.map +1 -0
  9. package/build/lib/bootstrap.js +192 -179
  10. package/build/lib/bootstrap.js.map +1 -1
  11. package/build/lib/commands/actions.d.ts +209 -0
  12. package/build/lib/commands/actions.d.ts.map +1 -0
  13. package/build/lib/commands/actions.js +327 -265
  14. package/build/lib/commands/actions.js.map +1 -1
  15. package/build/lib/commands/alert.d.ts +10 -0
  16. package/build/lib/commands/alert.d.ts.map +1 -0
  17. package/build/lib/commands/alert.js +12 -18
  18. package/build/lib/commands/alert.js.map +1 -1
  19. package/build/lib/commands/app-management.d.ts +314 -0
  20. package/build/lib/commands/app-management.d.ts.map +1 -0
  21. package/build/lib/commands/app-management.js +278 -110
  22. package/build/lib/commands/app-management.js.map +1 -1
  23. package/build/lib/commands/context.d.ts +94 -0
  24. package/build/lib/commands/context.d.ts.map +1 -0
  25. package/build/lib/commands/context.js +412 -260
  26. package/build/lib/commands/context.js.map +1 -1
  27. package/build/lib/commands/coverage.d.ts +5 -0
  28. package/build/lib/commands/coverage.d.ts.map +1 -0
  29. package/build/lib/commands/coverage.js +14 -17
  30. package/build/lib/commands/coverage.js.map +1 -1
  31. package/build/lib/commands/element.d.ts +36 -0
  32. package/build/lib/commands/element.d.ts.map +1 -0
  33. package/build/lib/commands/element.js +97 -127
  34. package/build/lib/commands/element.js.map +1 -1
  35. package/build/lib/commands/emu-console.d.ts +49 -0
  36. package/build/lib/commands/emu-console.d.ts.map +1 -0
  37. package/build/lib/commands/emu-console.js +36 -25
  38. package/build/lib/commands/emu-console.js.map +1 -1
  39. package/build/lib/commands/execute.d.ts +6 -0
  40. package/build/lib/commands/execute.d.ts.map +1 -0
  41. package/build/lib/commands/execute.js +68 -69
  42. package/build/lib/commands/execute.js.map +1 -1
  43. package/build/lib/commands/file-actions.d.ts +129 -0
  44. package/build/lib/commands/file-actions.d.ts.map +1 -0
  45. package/build/lib/commands/file-actions.js +321 -178
  46. package/build/lib/commands/file-actions.js.map +1 -1
  47. package/build/lib/commands/find.d.ts +13 -0
  48. package/build/lib/commands/find.d.ts.map +1 -0
  49. package/build/lib/commands/find.js +69 -51
  50. package/build/lib/commands/find.js.map +1 -1
  51. package/build/lib/commands/general.d.ts +133 -0
  52. package/build/lib/commands/general.d.ts.map +1 -0
  53. package/build/lib/commands/general.js +275 -216
  54. package/build/lib/commands/general.js.map +1 -1
  55. package/build/lib/commands/ime.d.ts +11 -0
  56. package/build/lib/commands/ime.d.ts.map +1 -0
  57. package/build/lib/commands/ime.js +27 -33
  58. package/build/lib/commands/ime.js.map +1 -1
  59. package/build/lib/commands/index.d.ts +3 -0
  60. package/build/lib/commands/index.d.ts.map +1 -0
  61. package/build/lib/commands/index.js +32 -35
  62. package/build/lib/commands/index.js.map +1 -1
  63. package/build/lib/commands/intent.d.ts +418 -0
  64. package/build/lib/commands/intent.d.ts.map +1 -0
  65. package/build/lib/commands/intent.js +281 -151
  66. package/build/lib/commands/intent.js.map +1 -1
  67. package/build/lib/commands/keyboard.d.ts +6 -0
  68. package/build/lib/commands/keyboard.d.ts.map +1 -0
  69. package/build/lib/commands/keyboard.js +6 -14
  70. package/build/lib/commands/keyboard.js.map +1 -1
  71. package/build/lib/commands/log.d.ts +45 -0
  72. package/build/lib/commands/log.d.ts.map +1 -0
  73. package/build/lib/commands/log.js +117 -103
  74. package/build/lib/commands/log.js.map +1 -1
  75. package/build/lib/commands/media-projection.d.ts +144 -0
  76. package/build/lib/commands/media-projection.d.ts.map +1 -0
  77. package/build/lib/commands/media-projection.js +228 -171
  78. package/build/lib/commands/media-projection.js.map +1 -1
  79. package/build/lib/commands/network.d.ts +139 -0
  80. package/build/lib/commands/network.d.ts.map +1 -0
  81. package/build/lib/commands/network.js +249 -181
  82. package/build/lib/commands/network.js.map +1 -1
  83. package/build/lib/commands/performance.d.ts +101 -0
  84. package/build/lib/commands/performance.d.ts.map +1 -0
  85. package/build/lib/commands/performance.js +390 -236
  86. package/build/lib/commands/performance.js.map +1 -1
  87. package/build/lib/commands/permissions.d.ts +93 -0
  88. package/build/lib/commands/permissions.d.ts.map +1 -0
  89. package/build/lib/commands/permissions.js +133 -93
  90. package/build/lib/commands/permissions.js.map +1 -1
  91. package/build/lib/commands/recordscreen.d.ts +194 -0
  92. package/build/lib/commands/recordscreen.d.ts.map +1 -0
  93. package/build/lib/commands/recordscreen.js +293 -224
  94. package/build/lib/commands/recordscreen.js.map +1 -1
  95. package/build/lib/commands/shell.d.ts +8 -0
  96. package/build/lib/commands/shell.d.ts.map +1 -0
  97. package/build/lib/commands/shell.js +38 -43
  98. package/build/lib/commands/shell.js.map +1 -1
  99. package/build/lib/commands/streamscreen.d.ts +104 -0
  100. package/build/lib/commands/streamscreen.d.ts.map +1 -0
  101. package/build/lib/commands/streamscreen.js +364 -305
  102. package/build/lib/commands/streamscreen.js.map +1 -1
  103. package/build/lib/commands/system-bars.d.ts +100 -0
  104. package/build/lib/commands/system-bars.d.ts.map +1 -0
  105. package/build/lib/commands/system-bars.js +148 -90
  106. package/build/lib/commands/system-bars.js.map +1 -1
  107. package/build/lib/commands/touch.d.ts +30 -0
  108. package/build/lib/commands/touch.d.ts.map +1 -0
  109. package/build/lib/commands/touch.js +311 -287
  110. package/build/lib/commands/touch.js.map +1 -1
  111. package/build/lib/desired-caps.d.ts +353 -0
  112. package/build/lib/desired-caps.d.ts.map +1 -0
  113. package/build/lib/desired-caps.js +291 -292
  114. package/build/lib/desired-caps.js.map +1 -1
  115. package/build/lib/driver.d.ts +430 -0
  116. package/build/lib/driver.d.ts.map +1 -0
  117. package/build/lib/driver.js +449 -384
  118. package/build/lib/driver.js.map +1 -1
  119. package/build/lib/logger.d.ts +3 -0
  120. package/build/lib/logger.d.ts.map +1 -0
  121. package/build/lib/logger.js +5 -11
  122. package/build/lib/logger.js.map +1 -1
  123. package/build/lib/method-map.d.ts +389 -0
  124. package/build/lib/method-map.d.ts.map +1 -0
  125. package/build/lib/method-map.js +220 -394
  126. package/build/lib/method-map.js.map +1 -1
  127. package/build/lib/stubs.d.ts +8 -0
  128. package/build/lib/stubs.d.ts.map +1 -0
  129. package/build/lib/stubs.js +5 -0
  130. package/build/lib/stubs.js.map +1 -0
  131. package/build/lib/uiautomator.d.ts +24 -0
  132. package/build/lib/uiautomator.d.ts.map +1 -0
  133. package/build/lib/uiautomator.js +86 -82
  134. package/build/lib/uiautomator.js.map +1 -1
  135. package/build/lib/unlock-helpers.d.ts +38 -0
  136. package/build/lib/unlock-helpers.d.ts.map +1 -0
  137. package/build/lib/unlock-helpers.js +228 -204
  138. package/build/lib/unlock-helpers.js.map +1 -1
  139. package/build/lib/utils.d.ts +11 -0
  140. package/build/lib/utils.d.ts.map +1 -0
  141. package/build/lib/utils.js +23 -18
  142. package/build/lib/utils.js.map +1 -1
  143. package/build/lib/webview-helpers.d.ts +223 -0
  144. package/build/lib/webview-helpers.d.ts.map +1 -0
  145. package/build/lib/webview-helpers.js +476 -298
  146. package/build/lib/webview-helpers.js.map +1 -1
  147. package/index.js +3 -1
  148. package/lib/android-helpers.js +2 -1
  149. package/lib/stubs.ts +8 -0
  150. package/lib/unlock-helpers.js +2 -2
  151. package/package.json +23 -14
@@ -1,32 +1,29 @@
1
1
  "use strict";
2
-
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
- Object.defineProperty(exports, "__esModule", {
5
- value: true
6
- });
7
- exports.default = void 0;
8
- require("source-map-support/register");
9
- var _lodash = _interopRequireDefault(require("lodash"));
10
- var _support = require("@appium/support");
11
- var _teen_process = require("teen_process");
12
- var _portscanner = require("portscanner");
13
- var _http = _interopRequireDefault(require("http"));
14
- var _net = _interopRequireDefault(require("net"));
15
- var _bluebird = _interopRequireDefault(require("bluebird"));
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${_support.system.isWindows() ? '.exe' : ''}`;
23
- const GST_INSPECT_BINARY = `gst-inspect-1.0${_support.system.isWindows() ? '.exe' : ''}`;
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
- avdec_h264: 'gst-libav',
26
- h264parse: 'gst-plugins-bad',
27
- jpegenc: 'gst-plugins-good',
28
- tcpserversink: 'gst-plugins-base',
29
- multipartmux: 'gst-plugins-good'
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
- return _support.logger.getLogger(`${streamName}@` + _lodash.default.truncate(udid, {
42
- length: 8,
43
- omission: ''
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
- if (!_lodash.default.trim(await adb.shell(['which', SCREENRECORD_BINARY]))) {
48
- throw new Error(`The required '${SCREENRECORD_BINARY}' binary is not available on the device under test`);
49
- }
50
- const gstreamerCheckPromises = [];
51
- for (const binaryName of [GSTREAMER_BINARY, GST_INSPECT_BINARY]) {
52
- gstreamerCheckPromises.push((async () => {
53
- try {
54
- await _support.fs.which(binaryName);
55
- } catch (e) {
56
- throw new Error(`The '${binaryName}' binary is not available in the PATH on the host system. ` + `See ${GST_TUTORIAL_URL} for more details on how to install it.`);
57
- }
58
- })());
59
- }
60
- await _bluebird.default.all(gstreamerCheckPromises);
61
- const moduleCheckPromises = [];
62
- for (const [name, modName] of _lodash.default.toPairs(REQUIRED_GST_PLUGINS)) {
63
- moduleCheckPromises.push((async () => {
64
- const {
65
- stdout
66
- } = await (0, _teen_process.exec)(GST_INSPECT_BINARY, [name]);
67
- if (!_lodash.default.includes(stdout, modName)) {
68
- throw new Error(`The required GStreamer plugin '${name}' from '${modName}' module is not installed. ` + `See ${GST_TUTORIAL_URL} for more details on how to install it.`);
69
- }
70
- })());
71
- }
72
- await _bluebird.default.all(moduleCheckPromises);
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
- const output = await adb.shell(['dumpsys', 'display']);
76
- const result = {};
77
- for (const [key, pattern] of [['width', /\bdeviceWidth=(\d+)/], ['height', /\bdeviceHeight=(\d+)/], ['fps', /\bfps=(\d+)/]]) {
78
- const match = pattern.exec(output);
79
- if (!match) {
80
- log === null || log === void 0 ? void 0 : log.debug(output);
81
- throw new Error(`Cannot parse the device ${key} from the adb command output. ` + `Check the server log for more details.`);
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[key] = parseInt(match[1], 10);
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
- const {
90
- width,
91
- height,
92
- bitRate
93
- } = opts;
94
- const adjustedWidth = parseInt(width, 10) || deviceInfo.width;
95
- const adjustedHeight = parseInt(height, 10) || deviceInfo.height;
96
- const adjustedBitrate = parseInt(bitRate, 10) || DEFAULT_BITRATE;
97
- let screenRecordCmd = SCREENRECORD_BINARY + ` --output-format=h264` + ` --time-limit=${RECORDING_INTERVAL_SEC}`;
98
- if (width || height) {
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
- deviceStreaming.stderr.on('data', errorsListener);
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
- deviceStreaming.stdout.on('data', startupListener);
124
- try {
125
- log.info(`Starting device streaming: ${_support.util.quote([adb.executable.path, ...adbArgs])}`);
126
- await (0, _asyncbox.waitForCondition)(() => isStarted, {
127
- waitMs: STREAMING_STARTUP_TIMEOUT_MS,
128
- intervalMs: 300
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
- } catch (e) {
131
- log.errorAndThrow(`Cannot start the screen streaming process. Original error: ${e.message}`);
132
- } finally {
133
- deviceStreaming.stderr.removeListener('data', errorsListener);
134
- deviceStreaming.stdout.removeListener('data', startupListener);
135
- }
136
- return deviceStreaming;
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
- const {
140
- width,
141
- height,
142
- quality,
143
- tcpPort,
144
- considerRotation,
145
- logPipelineDetails
146
- } = opts;
147
- const adjustedWidth = parseInt(width, 10) || deviceInfo.width;
148
- const adjustedHeight = parseInt(height, 10) || deviceInfo.height;
149
- const gstreamerPipeline = new _teen_process.SubProcess(GSTREAMER_BINARY, ['-v', 'fdsrc', 'fd=0', '!', 'video/x-h264,' + `width=${considerRotation ? Math.max(adjustedWidth, adjustedHeight) : adjustedWidth},` + `height=${considerRotation ? Math.max(adjustedWidth, adjustedHeight) : adjustedHeight},` + `framerate=${deviceInfo.fps}/1,` + 'byte-stream=true', '!', 'h264parse', '!', 'queue', 'leaky=downstream', '!', 'avdec_h264', '!', 'queue', 'leaky=downstream', '!', 'jpegenc', `quality=${quality}`, '!', 'multipartmux', `boundary=${BOUNDARY_STRING}`, '!', 'tcpserversink', `host=${TCP_HOST}`, `port=${tcpPort}`], {
150
- stdio: [deviceStreamingProc.stdout, 'pipe', 'pipe']
151
- });
152
- gstreamerPipeline.on('exit', (code, signal) => {
153
- log.debug(`Pipeline streaming process exited with code ${code}, signal ${signal}`);
154
- });
155
- const gstreamerLogger = createStreamingLogger('gst', deviceInfo.udid);
156
- const gstOutputListener = (stdout, stderr) => {
157
- if (_lodash.default.trim(stderr || stdout)) {
158
- gstreamerLogger.debug(stderr || stdout);
159
- }
160
- };
161
- gstreamerPipeline.on('output', gstOutputListener);
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
- } catch (e) {
177
- didFail = true;
178
- log.errorAndThrow(`Cannot start the screen streaming pipeline. Original error: ${e.message}`);
179
- } finally {
180
- if (!logPipelineDetails || didFail) {
181
- gstreamerPipeline.removeListener('output', gstOutputListener);
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
- return gstreamerPipeline;
200
+ finally {
201
+ if (!logPipelineDetails || didFail) {
202
+ gstreamerPipeline.removeListener('output', gstOutputListener);
203
+ }
204
+ }
205
+ return gstreamerPipeline;
185
206
  }
186
207
  function extractRemoteAddress(req) {
187
- return req.headers['x-forwarded-for'] || req.socket.remoteAddress || req.connection.remoteAddress || req.connection.socket.remoteAddress;
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
- this.ensureFeatureEnabled(ADB_SCREEN_STREAMING_FEATURE);
191
- const {
192
- width,
193
- height,
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
- throw e;
239
- }
240
- let mjpegSocket;
241
- let mjpegServer;
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 (gstreamerPipeline.isRunning) {
292
- await gstreamerPipeline.stop();
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 (mjpegSocket) {
295
- mjpegSocket.destroy();
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
- if (mjpegServer && mjpegServer.listening) {
298
- mjpegServer.close();
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
- throw e;
301
- }
302
- this._screenStreamingProps = {
303
- deviceStreamingProc,
304
- gstreamerPipeline,
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
- return;
315
- }
316
- const {
317
- deviceStreamingProc,
318
- gstreamerPipeline,
319
- mjpegSocket,
320
- mjpegServer
321
- } = this._screenStreamingProps;
322
- try {
323
- mjpegSocket.end();
324
- if (mjpegServer.listening) {
325
- mjpegServer.close();
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
- if (deviceStreamingProc.kill(0)) {
328
- deviceStreamingProc.kill('SIGINT');
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
- if (gstreamerPipeline.isRunning) {
331
- try {
332
- await gstreamerPipeline.stop('SIGINT');
333
- } catch (e) {
334
- this.log.warn(e);
335
- try {
336
- await gstreamerPipeline.stop('SIGKILL');
337
- } catch (e1) {
338
- this.log.error(e1);
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
- var _default = commands;
348
- exports.default = _default;
349
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbG9kYXNoIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfc3VwcG9ydCIsIl90ZWVuX3Byb2Nlc3MiLCJfcG9ydHNjYW5uZXIiLCJfaHR0cCIsIl9uZXQiLCJfYmx1ZWJpcmQiLCJfYXN5bmNib3giLCJfY2hpbGRfcHJvY2VzcyIsIl91cmwiLCJjb21tYW5kcyIsIlJFQ09SRElOR19JTlRFUlZBTF9TRUMiLCJTVFJFQU1JTkdfU1RBUlRVUF9USU1FT1VUX01TIiwiR1NUUkVBTUVSX0JJTkFSWSIsInN5c3RlbSIsImlzV2luZG93cyIsIkdTVF9JTlNQRUNUX0JJTkFSWSIsIlJFUVVJUkVEX0dTVF9QTFVHSU5TIiwiYXZkZWNfaDI2NCIsImgyNjRwYXJzZSIsImpwZWdlbmMiLCJ0Y3BzZXJ2ZXJzaW5rIiwibXVsdGlwYXJ0bXV4IiwiU0NSRUVOUkVDT1JEX0JJTkFSWSIsIkdTVF9UVVRPUklBTF9VUkwiLCJERUZBVUxUX0hPU1QiLCJUQ1BfSE9TVCIsIkRFRkFVTFRfUE9SVCIsIkRFRkFVTFRfUVVBTElUWSIsIkRFRkFVTFRfQklUUkFURSIsIkJPVU5EQVJZX1NUUklORyIsIkFEQl9TQ1JFRU5fU1RSRUFNSU5HX0ZFQVRVUkUiLCJjcmVhdGVTdHJlYW1pbmdMb2dnZXIiLCJzdHJlYW1OYW1lIiwidWRpZCIsImxvZ2dlciIsImdldExvZ2dlciIsIl8iLCJ0cnVuY2F0ZSIsImxlbmd0aCIsIm9taXNzaW9uIiwidmVyaWZ5U3RyZWFtaW5nUmVxdWlyZW1lbnRzIiwiYWRiIiwidHJpbSIsInNoZWxsIiwiRXJyb3IiLCJnc3RyZWFtZXJDaGVja1Byb21pc2VzIiwiYmluYXJ5TmFtZSIsInB1c2giLCJmcyIsIndoaWNoIiwiZSIsIkIiLCJhbGwiLCJtb2R1bGVDaGVja1Byb21pc2VzIiwibmFtZSIsIm1vZE5hbWUiLCJ0b1BhaXJzIiwic3Rkb3V0IiwiZXhlYyIsImluY2x1ZGVzIiwiZ2V0RGV2aWNlSW5mbyIsImxvZyIsIm91dHB1dCIsInJlc3VsdCIsImtleSIsInBhdHRlcm4iLCJtYXRjaCIsImRlYnVnIiwicGFyc2VJbnQiLCJjdXJEZXZpY2VJZCIsImluaXREZXZpY2VTdHJlYW1pbmdQcm9jIiwiZGV2aWNlSW5mbyIsIm9wdHMiLCJ3aWR0aCIsImhlaWdodCIsImJpdFJhdGUiLCJhZGp1c3RlZFdpZHRoIiwiYWRqdXN0ZWRIZWlnaHQiLCJhZGp1c3RlZEJpdHJhdGUiLCJzY3JlZW5SZWNvcmRDbWQiLCJhZGJBcmdzIiwiZXhlY3V0YWJsZSIsImRlZmF1bHRBcmdzIiwiZGV2aWNlU3RyZWFtaW5nIiwic3Bhd24iLCJwYXRoIiwib24iLCJjb2RlIiwic2lnbmFsIiwiaXNTdGFydGVkIiwiZGV2aWNlU3RyZWFtaW5nTG9nZ2VyIiwiZXJyb3JzTGlzdGVuZXIiLCJjaHVuayIsInN0ZGVyciIsInRvU3RyaW5nIiwic3RhcnR1cExpc3RlbmVyIiwiaXNFbXB0eSIsImluZm8iLCJ1dGlsIiwicXVvdGUiLCJ3YWl0Rm9yQ29uZGl0aW9uIiwid2FpdE1zIiwiaW50ZXJ2YWxNcyIsImVycm9yQW5kVGhyb3ciLCJtZXNzYWdlIiwicmVtb3ZlTGlzdGVuZXIiLCJpbml0R3N0cmVhbWVyUGlwZWxpbmUiLCJkZXZpY2VTdHJlYW1pbmdQcm9jIiwicXVhbGl0eSIsInRjcFBvcnQiLCJjb25zaWRlclJvdGF0aW9uIiwibG9nUGlwZWxpbmVEZXRhaWxzIiwiZ3N0cmVhbWVyUGlwZWxpbmUiLCJTdWJQcm9jZXNzIiwiTWF0aCIsIm1heCIsImZwcyIsInN0ZGlvIiwiZ3N0cmVhbWVyTG9nZ2VyIiwiZ3N0T3V0cHV0TGlzdGVuZXIiLCJkaWRGYWlsIiwicmVwIiwic3RhcnQiLCJjaGVja1BvcnRTdGF0dXMiLCJpZ24iLCJleHRyYWN0UmVtb3RlQWRkcmVzcyIsInJlcSIsImhlYWRlcnMiLCJzb2NrZXQiLCJyZW1vdGVBZGRyZXNzIiwiY29ubmVjdGlvbiIsIm1vYmlsZVN0YXJ0U2NyZWVuU3RyZWFtaW5nIiwib3B0aW9ucyIsImVuc3VyZUZlYXR1cmVFbmFibGVkIiwiaG9zdCIsInBvcnQiLCJwYXRobmFtZSIsImlzVW5kZWZpbmVkIiwiX3NjcmVlblN0cmVhbWluZ1Byb3BzIiwia2lsbCIsIm1qcGVnU29ja2V0IiwibWpwZWdTZXJ2ZXIiLCJyZXNvbHZlIiwicmVqZWN0IiwibmV0IiwiY3JlYXRlQ29ubmVjdGlvbiIsImh0dHAiLCJjcmVhdGVTZXJ2ZXIiLCJyZXMiLCJjdXJyZW50UGF0aG5hbWUiLCJ1cmwiLCJwYXJzZSIsIndyaXRlSGVhZCIsIkNvbm5lY3Rpb24iLCJ3cml0ZSIsImVuZCIsIlByYWdtYSIsInBpcGUiLCJ3YXJuIiwibGlzdGVuIiwiZXJyb3IiLCJ0aW1lb3V0IiwiaXNSdW5uaW5nIiwic3RvcCIsImRlc3Ryb3kiLCJsaXN0ZW5pbmciLCJjbG9zZSIsIm1vYmlsZVN0b3BTY3JlZW5TdHJlYW1pbmciLCJlMSIsIl9kZWZhdWx0IiwiZXhwb3J0cyIsImRlZmF1bHQiXSwic291cmNlcyI6WyIuLi8uLi8uLi9saWIvY29tbWFuZHMvc3RyZWFtc2NyZWVuLmpzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgeyBmcywgc3lzdGVtLCBsb2dnZXIsIHV0aWwgfSBmcm9tICdAYXBwaXVtL3N1cHBvcnQnO1xuaW1wb3J0IHsgZXhlYywgU3ViUHJvY2VzcyB9IGZyb20gJ3RlZW5fcHJvY2Vzcyc7XG5pbXBvcnQgeyBjaGVja1BvcnRTdGF0dXMgfSBmcm9tICdwb3J0c2Nhbm5lcic7XG5pbXBvcnQgaHR0cCBmcm9tICdodHRwJztcbmltcG9ydCBuZXQgZnJvbSAnbmV0JztcbmltcG9ydCBCIGZyb20gJ2JsdWViaXJkJztcbmltcG9ydCB7IHdhaXRGb3JDb25kaXRpb24gfSBmcm9tICdhc3luY2JveCc7XG5pbXBvcnQgeyBzcGF3biB9IGZyb20gJ2NoaWxkX3Byb2Nlc3MnO1xuaW1wb3J0IHVybCBmcm9tICd1cmwnO1xuXG5jb25zdCBjb21tYW5kcyA9IHt9O1xuXG5jb25zdCBSRUNPUkRJTkdfSU5URVJWQUxfU0VDID0gNTtcbmNvbnN0IFNUUkVBTUlOR19TVEFSVFVQX1RJTUVPVVRfTVMgPSA1MDAwO1xuY29uc3QgR1NUUkVBTUVSX0JJTkFSWSA9IGBnc3QtbGF1bmNoLTEuMCR7c3lzdGVtLmlzV2luZG93cygpID8gJy5leGUnIDogJyd9YDtcbmNvbnN0IEdTVF9JTlNQRUNUX0JJTkFSWSA9IGBnc3QtaW5zcGVjdC0xLjAke3N5c3RlbS5pc1dpbmRvd3MoKSA/ICcuZXhlJyA6ICcnfWA7XG5jb25zdCBSRVFVSVJFRF9HU1RfUExVR0lOUyA9IHtcbiAgYXZkZWNfaDI2NDogJ2dzdC1saWJhdicsXG4gIGgyNjRwYXJzZTogJ2dzdC1wbHVnaW5zLWJhZCcsXG4gIGpwZWdlbmM6ICdnc3QtcGx1Z2lucy1nb29kJyxcbiAgdGNwc2VydmVyc2luazogJ2dzdC1wbHVnaW5zLWJhc2UnLFxuICBtdWx0aXBhcnRtdXg6ICdnc3QtcGx1Z2lucy1nb29kJyxcbn07XG5jb25zdCBTQ1JFRU5SRUNPUkRfQklOQVJZID0gJ3NjcmVlbnJlY29yZCc7XG5jb25zdCBHU1RfVFVUT1JJQUxfVVJMID0gJ2h0dHBzOi8vZ3N0cmVhbWVyLmZyZWVkZXNrdG9wLm9yZy9kb2N1bWVudGF0aW9uL2luc3RhbGxpbmcvaW5kZXguaHRtbCc7XG5jb25zdCBERUZBVUxUX0hPU1QgPSAnMTI3LjAuMC4xJztcbmNvbnN0IFRDUF9IT1NUID0gJzEyNy4wLjAuMSc7XG5jb25zdCBERUZBVUxUX1BPUlQgPSA4MDkzO1xuY29uc3QgREVGQVVMVF9RVUFMSVRZID0gNzA7XG5jb25zdCBERUZBVUxUX0JJVFJBVEUgPSA0MDAwMDAwOyAvLyA0IE1icHNcbmNvbnN0IEJPVU5EQVJZX1NUUklORyA9ICctLTJhZTk3NDY4ODdmMTcwYjhjZjdjMjcxMDQ3Y2UzMTRjJztcblxuY29uc3QgQURCX1NDUkVFTl9TVFJFQU1JTkdfRkVBVFVSRSA9ICdhZGJfc2NyZWVuX3N0cmVhbWluZyc7XG5cbmZ1bmN0aW9uIGNyZWF0ZVN0cmVhbWluZ0xvZ2dlciAoc3RyZWFtTmFtZSwgdWRpZCkge1xuICByZXR1cm4gbG9nZ2VyLmdldExvZ2dlcihgJHtzdHJlYW1OYW1lfUBgICsgXy50cnVuY2F0ZSh1ZGlkLCB7XG4gICAgbGVuZ3RoOiA4LFxuICAgIG9taXNzaW9uOiAnJyxcbiAgfSkpO1xufVxuXG5hc3luYyBmdW5jdGlvbiB2ZXJpZnlTdHJlYW1pbmdSZXF1aXJlbWVudHMgKGFkYikge1xuICBpZiAoIV8udHJpbShhd2FpdCBhZGIuc2hlbGwoWyd3aGljaCcsIFNDUkVFTlJFQ09SRF9CSU5BUlldKSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgVGhlIHJlcXVpcmVkICcke1NDUkVFTlJFQ09SRF9CSU5BUll9JyBiaW5hcnkgaXMgbm90IGF2YWlsYWJsZSBvbiB0aGUgZGV2aWNlIHVuZGVyIHRlc3RgKTtcbiAgfVxuXG4gIGNvbnN0IGdzdHJlYW1lckNoZWNrUHJvbWlzZXMgPSBbXTtcbiAgZm9yIChjb25zdCBiaW5hcnlOYW1lIG9mIFtHU1RSRUFNRVJfQklOQVJZLCBHU1RfSU5TUEVDVF9CSU5BUlldKSB7XG4gICAgZ3N0cmVhbWVyQ2hlY2tQcm9taXNlcy5wdXNoKChhc3luYyAoKSA9PiB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBmcy53aGljaChiaW5hcnlOYW1lKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBUaGUgJyR7YmluYXJ5TmFtZX0nIGJpbmFyeSBpcyBub3QgYXZhaWxhYmxlIGluIHRoZSBQQVRIIG9uIHRoZSBob3N0IHN5c3RlbS4gYCArXG4gICAgICAgICAgYFNlZSAke0dTVF9UVVRPUklBTF9VUkx9IGZvciBtb3JlIGRldGFpbHMgb24gaG93IHRvIGluc3RhbGwgaXQuYCk7XG4gICAgICB9XG4gICAgfSkoKSk7XG4gIH1cbiAgYXdhaXQgQi5hbGwoZ3N0cmVhbWVyQ2hlY2tQcm9taXNlcyk7XG5cbiAgY29uc3QgbW9kdWxlQ2hlY2tQcm9taXNlcyA9IFtdO1xuICBmb3IgKGNvbnN0IFtuYW1lLCBtb2ROYW1lXSBvZiBfLnRvUGFpcnMoUkVRVUlSRURfR1NUX1BMVUdJTlMpKSB7XG4gICAgbW9kdWxlQ2hlY2tQcm9taXNlcy5wdXNoKChhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCB7c3Rkb3V0fSA9IGF3YWl0IGV4ZWMoR1NUX0lOU1BFQ1RfQklOQVJZLCBbbmFtZV0pO1xuICAgICAgaWYgKCFfLmluY2x1ZGVzKHN0ZG91dCwgbW9kTmFtZSkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAgIGBUaGUgcmVxdWlyZWQgR1N0cmVhbWVyIHBsdWdpbiAnJHtuYW1lfScgZnJvbSAnJHttb2ROYW1lfScgbW9kdWxlIGlzIG5vdCBpbnN0YWxsZWQuIGAgK1xuICAgICAgICAgIGBTZWUgJHtHU1RfVFVUT1JJQUxfVVJMfSBmb3IgbW9yZSBkZXRhaWxzIG9uIGhvdyB0byBpbnN0YWxsIGl0LmApO1xuICAgICAgfVxuICAgIH0pKCkpO1xuICB9XG4gIGF3YWl0IEIuYWxsKG1vZHVsZUNoZWNrUHJvbWlzZXMpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBnZXREZXZpY2VJbmZvIChhZGIsIGxvZyA9IG51bGwpIHtcbiAgY29uc3Qgb3V0cHV0ID0gYXdhaXQgYWRiLnNoZWxsKFsnZHVtcHN5cycsICdkaXNwbGF5J10pO1xuICBjb25zdCByZXN1bHQgPSB7fTtcbiAgZm9yIChjb25zdCBba2V5LCBwYXR0ZXJuXSBvZiBbXG4gICAgWyd3aWR0aCcsIC9cXGJkZXZpY2VXaWR0aD0oXFxkKykvXSxcbiAgICBbJ2hlaWdodCcsIC9cXGJkZXZpY2VIZWlnaHQ9KFxcZCspL10sXG4gICAgWydmcHMnLCAvXFxiZnBzPShcXGQrKS9dLFxuICBdKSB7XG4gICAgY29uc3QgbWF0Y2ggPSBwYXR0ZXJuLmV4ZWMob3V0cHV0KTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICBsb2c/LmRlYnVnKG91dHB1dCk7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYENhbm5vdCBwYXJzZSB0aGUgZGV2aWNlICR7a2V5fSBmcm9tIHRoZSBhZGIgY29tbWFuZCBvdXRwdXQuIGAgK1xuICAgICAgICBgQ2hlY2sgdGhlIHNlcnZlciBsb2cgZm9yIG1vcmUgZGV0YWlscy5gKTtcbiAgICB9XG4gICAgcmVzdWx0W2tleV0gPSBwYXJzZUludChtYXRjaFsxXSwgMTApO1xuICB9XG4gIHJlc3VsdC51ZGlkID0gYWRiLmN1ckRldmljZUlkO1xuICByZXR1cm4gcmVzdWx0O1xufVxuXG5hc3luYyBmdW5jdGlvbiBpbml0RGV2aWNlU3RyZWFtaW5nUHJvYyAoYWRiLCBsb2csIGRldmljZUluZm8sIG9wdHMgPSB7fSkge1xuICBjb25zdCB7XG4gICAgd2lkdGgsXG4gICAgaGVpZ2h0LFxuICAgIGJpdFJhdGUsXG4gIH0gPSBvcHRzO1xuICBjb25zdCBhZGp1c3RlZFdpZHRoID0gcGFyc2VJbnQod2lkdGgsIDEwKSB8fCBkZXZpY2VJbmZvLndpZHRoO1xuICBjb25zdCBhZGp1c3RlZEhlaWdodCA9IHBhcnNlSW50KGhlaWdodCwgMTApIHx8IGRldmljZUluZm8uaGVpZ2h0O1xuICBjb25zdCBhZGp1c3RlZEJpdHJhdGUgPSBwYXJzZUludChiaXRSYXRlLCAxMCkgfHwgREVGQVVMVF9CSVRSQVRFO1xuICBsZXQgc2NyZWVuUmVjb3JkQ21kID0gU0NSRUVOUkVDT1JEX0JJTkFSWSArXG4gICAgYCAtLW91dHB1dC1mb3JtYXQ9aDI2NGAgK1xuICAgIC8vIDUgc2Vjb25kcyBpcyBmaW5lIHRvIGRldGVjdCByb3RhdGlvbiBjaGFuZ2VzXG4gICAgYCAtLXRpbWUtbGltaXQ9JHtSRUNPUkRJTkdfSU5URVJWQUxfU0VDfWA7XG4gIGlmICh3aWR0aCB8fCBoZWlnaHQpIHtcbiAgICBzY3JlZW5SZWNvcmRDbWQgKz0gYCAtLXNpemU9JHthZGp1c3RlZFdpZHRofXgke2FkanVzdGVkSGVpZ2h0fWA7XG4gIH1cbiAgaWYgKGJpdFJhdGUpIHtcbiAgICBzY3JlZW5SZWNvcmRDbWQgKz0gYCAtLWJpdC1yYXRlPSR7YWRqdXN0ZWRCaXRyYXRlfWA7XG4gIH1cbiAgY29uc3QgYWRiQXJncyA9IFtcbiAgICAuLi5hZGIuZXhlY3V0YWJsZS5kZWZhdWx0QXJncyxcbiAgICAnZXhlYy1vdXQnLFxuICAgIC8vIFRoZSBsb29wIGlzIHJlcXVpcmVkLCBiZWNhdXNlIGJ5IGRlZmF1bHQgdGhlIG1heGltdW0gcmVjb3JkIGR1cmF0aW9uXG4gICAgLy8gZm9yIHNjcmVlbnJlY29yZCBpcyBhbHdheXMgbGltaXRlZFxuICAgIGB3aGlsZSB0cnVlOyBkbyAke3NjcmVlblJlY29yZENtZH0gLTsgZG9uZWAsXG4gIF07XG4gIGNvbnN0IGRldmljZVN0cmVhbWluZyA9IHNwYXduKGFkYi5leGVjdXRhYmxlLnBhdGgsIGFkYkFyZ3MpO1xuICBkZXZpY2VTdHJlYW1pbmcub24oJ2V4aXQnLCAoY29kZSwgc2lnbmFsKSA9PiB7XG4gICAgbG9nLmRlYnVnKGBEZXZpY2Ugc3RyZWFtaW5nIHByb2Nlc3MgZXhpdGVkIHdpdGggY29kZSAke2NvZGV9LCBzaWduYWwgJHtzaWduYWx9YCk7XG4gIH0pO1xuXG4gIGxldCBpc1N0YXJ0ZWQgPSBmYWxzZTtcbiAgY29uc3QgZGV2aWNlU3RyZWFtaW5nTG9nZ2VyID0gY3JlYXRlU3RyZWFtaW5nTG9nZ2VyKFNDUkVFTlJFQ09SRF9CSU5BUlksIGRldmljZUluZm8udWRpZCk7XG4gIGNvbnN0IGVycm9yc0xpc3RlbmVyID0gKGNodW5rKSA9PiB7XG4gICAgY29uc3Qgc3RkZXJyID0gY2h1bmsudG9TdHJpbmcoKTtcbiAgICBpZiAoXy50cmltKHN0ZGVycikpIHtcbiAgICAgIGRldmljZVN0cmVhbWluZ0xvZ2dlci5kZWJ1ZyhzdGRlcnIpO1xuICAgIH1cbiAgfTtcbiAgZGV2aWNlU3RyZWFtaW5nLnN0ZGVyci5vbignZGF0YScsIGVycm9yc0xpc3RlbmVyKTtcblxuICBjb25zdCBzdGFydHVwTGlzdGVuZXIgPSAoY2h1bmspID0+IHtcbiAgICBpZiAoIWlzU3RhcnRlZCkge1xuICAgICAgaXNTdGFydGVkID0gIV8uaXNFbXB0eShjaHVuayk7XG4gICAgfVxuICB9O1xuICBkZXZpY2VTdHJlYW1pbmcuc3Rkb3V0Lm9uKCdkYXRhJywgc3RhcnR1cExpc3RlbmVyKTtcblxuICB0cnkge1xuICAgIGxvZy5pbmZvKGBTdGFydGluZyBkZXZpY2Ugc3RyZWFtaW5nOiAke3V0aWwucXVvdGUoW2FkYi5leGVjdXRhYmxlLnBhdGgsIC4uLmFkYkFyZ3NdKX1gKTtcbiAgICBhd2FpdCB3YWl0Rm9yQ29uZGl0aW9uKCgpID0+IGlzU3RhcnRlZCwge1xuICAgICAgd2FpdE1zOiBTVFJFQU1JTkdfU1RBUlRVUF9USU1FT1VUX01TLFxuICAgICAgaW50ZXJ2YWxNczogMzAwLFxuICAgIH0pO1xuICB9IGNhdGNoIChlKSB7XG4gICAgbG9nLmVycm9yQW5kVGhyb3coXG4gICAgICBgQ2Fubm90IHN0YXJ0IHRoZSBzY3JlZW4gc3RyZWFtaW5nIHByb2Nlc3MuIE9yaWdpbmFsIGVycm9yOiAke2UubWVzc2FnZX1gKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBkZXZpY2VTdHJlYW1pbmcuc3RkZXJyLnJlbW92ZUxpc3RlbmVyKCdkYXRhJywgZXJyb3JzTGlzdGVuZXIpO1xuICAgIGRldmljZVN0cmVhbWluZy5zdGRvdXQucmVtb3ZlTGlzdGVuZXIoJ2RhdGEnLCBzdGFydHVwTGlzdGVuZXIpO1xuICB9XG4gIHJldHVybiBkZXZpY2VTdHJlYW1pbmc7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGluaXRHc3RyZWFtZXJQaXBlbGluZSAoZGV2aWNlU3RyZWFtaW5nUHJvYywgZGV2aWNlSW5mbywgbG9nLCBvcHRzID0ge30pIHtcbiAgY29uc3Qge1xuICAgIHdpZHRoLFxuICAgIGhlaWdodCxcbiAgICBxdWFsaXR5LFxuICAgIHRjcFBvcnQsXG4gICAgY29uc2lkZXJSb3RhdGlvbixcbiAgICBsb2dQaXBlbGluZURldGFpbHMsXG4gIH0gPSBvcHRzO1xuICBjb25zdCBhZGp1c3RlZFdpZHRoID0gcGFyc2VJbnQod2lkdGgsIDEwKSB8fCBkZXZpY2VJbmZvLndpZHRoO1xuICBjb25zdCBhZGp1c3RlZEhlaWdodCA9IHBhcnNlSW50KGhlaWdodCwgMTApIHx8IGRldmljZUluZm8uaGVpZ2h0O1xuICBjb25zdCBnc3RyZWFtZXJQaXBlbGluZSA9IG5ldyBTdWJQcm9jZXNzKEdTVFJFQU1FUl9CSU5BUlksIFtcbiAgICAnLXYnLFxuICAgICdmZHNyYycsICdmZD0wJyxcbiAgICAnIScsICd2aWRlby94LWgyNjQsJyArXG4gICAgICBgd2lkdGg9JHtjb25zaWRlclJvdGF0aW9uID8gTWF0aC5tYXgoYWRqdXN0ZWRXaWR0aCwgYWRqdXN0ZWRIZWlnaHQpIDogYWRqdXN0ZWRXaWR0aH0sYCArXG4gICAgICBgaGVpZ2h0PSR7Y29uc2lkZXJSb3RhdGlvbiA/IE1hdGgubWF4KGFkanVzdGVkV2lkdGgsIGFkanVzdGVkSGVpZ2h0KSA6IGFkanVzdGVkSGVpZ2h0fSxgICtcbiAgICAgIGBmcmFtZXJhdGU9JHtkZXZpY2VJbmZvLmZwc30vMSxgICtcbiAgICAgICdieXRlLXN0cmVhbT10cnVlJyxcbiAgICAnIScsICdoMjY0cGFyc2UnLFxuICAgICchJywgJ3F1ZXVlJywgJ2xlYWt5PWRvd25zdHJlYW0nLFxuICAgICchJywgJ2F2ZGVjX2gyNjQnLFxuICAgICchJywgJ3F1ZXVlJywgJ2xlYWt5PWRvd25zdHJlYW0nLFxuICAgICchJywgJ2pwZWdlbmMnLCBgcXVhbGl0eT0ke3F1YWxpdHl9YCxcbiAgICAnIScsICdtdWx0aXBhcnRtdXgnLCBgYm91bmRhcnk9JHtCT1VOREFSWV9TVFJJTkd9YCxcbiAgICAnIScsICd0Y3BzZXJ2ZXJzaW5rJywgYGhvc3Q9JHtUQ1BfSE9TVH1gLCBgcG9ydD0ke3RjcFBvcnR9YCxcbiAgXSwge1xuICAgIHN0ZGlvOiBbZGV2aWNlU3RyZWFtaW5nUHJvYy5zdGRvdXQsICdwaXBlJywgJ3BpcGUnXVxuICB9KTtcbiAgZ3N0cmVhbWVyUGlwZWxpbmUub24oJ2V4aXQnLCAoY29kZSwgc2lnbmFsKSA9PiB7XG4gICAgbG9nLmRlYnVnKGBQaXBlbGluZSBzdHJlYW1pbmcgcHJvY2VzcyBleGl0ZWQgd2l0aCBjb2RlICR7Y29kZX0sIHNpZ25hbCAke3NpZ25hbH1gKTtcbiAgfSk7XG4gIGNvbnN0IGdzdHJlYW1lckxvZ2dlciA9IGNyZWF0ZVN0cmVhbWluZ0xvZ2dlcignZ3N0JywgZGV2aWNlSW5mby51ZGlkKTtcbiAgY29uc3QgZ3N0T3V0cHV0TGlzdGVuZXIgPSAoc3Rkb3V0LCBzdGRlcnIpID0+IHtcbiAgICBpZiAoXy50cmltKHN0ZGVyciB8fCBzdGRvdXQpKSB7XG4gICAgICBnc3RyZWFtZXJMb2dnZXIuZGVidWcoc3RkZXJyIHx8IHN0ZG91dCk7XG4gICAgfVxuICB9O1xuICBnc3RyZWFtZXJQaXBlbGluZS5vbignb3V0cHV0JywgZ3N0T3V0cHV0TGlzdGVuZXIpO1xuICBsZXQgZGlkRmFpbCA9IGZhbHNlO1xuICB0cnkge1xuICAgIGxvZy5pbmZvKGBTdGFydGluZyBHU3RyZWFtZXIgcGlwZWxpbmU6ICR7Z3N0cmVhbWVyUGlwZWxpbmUucmVwfWApO1xuICAgIGF3YWl0IGdzdHJlYW1lclBpcGVsaW5lLnN0YXJ0KDApO1xuICAgIGF3YWl0IHdhaXRGb3JDb25kaXRpb24oYXN5bmMgKCkgPT4ge1xuICAgICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIChhd2FpdCBjaGVja1BvcnRTdGF0dXModGNwUG9ydCwgVENQX0hPU1QpKSA9PT0gJ29wZW4nO1xuICAgICAgfSBjYXRjaCAoaWduKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICB9LCB7XG4gICAgICB3YWl0TXM6IFNUUkVBTUlOR19TVEFSVFVQX1RJTUVPVVRfTVMsXG4gICAgICBpbnRlcnZhbE1zOiAzMDAsXG4gICAgfSk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBkaWRGYWlsID0gdHJ1ZTtcbiAgICBsb2cuZXJyb3JBbmRUaHJvdyhcbiAgICAgIGBDYW5ub3Qgc3RhcnQgdGhlIHNjcmVlbiBzdHJlYW1pbmcgcGlwZWxpbmUuIE9yaWdpbmFsIGVycm9yOiAke2UubWVzc2FnZX1gKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBpZiAoIWxvZ1BpcGVsaW5lRGV0YWlscyB8fCBkaWRGYWlsKSB7XG4gICAgICBnc3RyZWFtZXJQaXBlbGluZS5yZW1vdmVMaXN0ZW5lcignb3V0cHV0JywgZ3N0T3V0cHV0TGlzdGVuZXIpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZ3N0cmVhbWVyUGlwZWxpbmU7XG59XG5cbmZ1bmN0aW9uIGV4dHJhY3RSZW1vdGVBZGRyZXNzIChyZXEpIHtcbiAgcmV0dXJuIHJlcS5oZWFkZXJzWyd4LWZvcndhcmRlZC1mb3InXVxuICAgIHx8IHJlcS5zb2NrZXQucmVtb3RlQWRkcmVzc1xuICAgIHx8IHJlcS5jb25uZWN0aW9uLnJlbW90ZUFkZHJlc3NcbiAgICB8fCByZXEuY29ubmVjdGlvbi5zb2NrZXQucmVtb3RlQWRkcmVzcztcbn1cblxuXG4vKipcbiAqIEB0eXBlZGVmIHtPYmplY3R9IFN0YXJ0U2NyZWVuU3RyZWFtaW5nT3B0aW9uc1xuICpcbiAqIEBwcm9wZXJ0eSB7P251bWJlcn0gd2lkdGggLSBUaGUgc2NhbGVkIHdpZHRoIG9mIHRoZSBkZXZpY2UncyBzY3JlZW4uIElmIHVuc2V0IHRoZW4gdGhlIHNjcmlwdCB3aWxsIGFzc2lnbiBpdFxuICogdG8gdGhlIGFjdHVhbCBzY3JlZW4gd2lkdGggbWVhc3VyZWQgaW4gcGl4ZWxzLlxuICogQHByb3BlcnR5IHs/bnVtYmVyfSBoZWlnaHQgLSBUaGUgc2NhbGVkIGhlaWdodCBvZiB0aGUgZGV2aWNlJ3Mgc2NyZWVuLiBJZiB1bnNldCB0aGVuIHRoZSBzY3JpcHQgd2lsbCBhc3NpZ24gaXRcbiAqIHRvIHRoZSBhY3R1YWwgc2NyZWVuIGhlaWdodCBtZWFzdXJlZCBpbiBwaXhlbHMuXG4gKiBAcHJvcGVydHkgez9udW1iZXJ9IGJpdFJhdGUgLSBUaGUgdmlkZW8gYml0IHJhdGUgZm9yIHRoZSB2aWRlbywgaW4gYml0cyBwZXIgc2Vjb25kLlxuICogVGhlIGRlZmF1bHQgdmFsdWUgaXMgNDAwMDAwMCAoNCBNYi9zKS4gWW91IGNhbiBpbmNyZWFzZSB0aGUgYml0IHJhdGUgdG8gaW1wcm92ZSB2aWRlbyBxdWFsaXR5LFxuICogYnV0IGRvaW5nIHNvIHJlc3VsdHMgaW4gbGFyZ2VyIG1vdmllIGZpbGVzLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSBob3N0IFsxMjcuMC4wLjFdIC0gVGhlIElQIGFkZHJlc3MvaG9zdCBuYW1lIHRvIHN0YXJ0IHRoZSBNSlBFRyBzZXJ2ZXIgb24uXG4gKiBZb3UgY2FuIHNldCBpdCB0byBgMC4wLjAuMGAgdG8gdHJpZ2dlciB0aGUgYnJvYWRjYXN0IG9uIGFsbCBhdmFpbGFibGUgbmV0d29yayBpbnRlcmZhY2VzLlxuICogQHByb3BlcnR5IHs/c3RyaW5nfSBwYXRobmFtZSAtIFRoZSBIVFRQIHJlcXVlc3QgcGF0aCB0aGUgTUpQRUcgc2VydmVyIHNob3VsZCBiZSBhdmFpbGFibGUgb24uXG4gKiBJZiB1bnNldCB0aGVuIGFueSBwYXRobmFtZSBvbiB0aGUgZ2l2ZW4gYGhvc3RgL2Bwb3J0YCBjb21iaW5hdGlvbiB3aWxsIHdvcmsuIE5vdGUgdGhhdCB0aGUgdmFsdWVcbiAqIHNob3VsZCBhbHdheXMgc3RhcnQgd2l0aCBhIHNpbmdsZSBzbGFzaDogYC9gXG4gKiBAcHJvcGVydHkgez9udW1iZXJ9IHRjcFBvcnQgWzgwOTRdIC0gVGhlIHBvcnQgbnVtYmVyIHRvIHN0YXJ0IHRoZSBpbnRlcm5hbCBUQ1AgTUpQRUcgYnJvYWRjYXN0IG9uLlxuICogVGhpcyB0eXBlIG9mIGJyb2FkY2FzdCBhbHdheXMgc3RhcnRzIG9uIHRoZSBsb29wYmFjayBpbnRlcmZhY2UgKGAxMjcuMC4wLjFgKS5cbiAqIEBwcm9wZXJ0eSB7P251bWJlcn0gcG9ydCBbODA5M10gLSBUaGUgcG9ydCBudW1iZXIgdG8gc3RhcnQgdGhlIE1KUEVHIHNlcnZlciBvbi5cbiAqIEBwcm9wZXJ0eSB7P251bWJlcn0gcXVhbGl0eSBbNzBdIC0gVGhlIHF1YWxpdHkgdmFsdWUgZm9yIHRoZSBzdHJlYW1lZCBKUEVHIGltYWdlcy5cbiAqIFRoaXMgbnVtYmVyIHNob3VsZCBiZSBpbiByYW5nZSBbMSwgMTAwXSwgd2hlcmUgMTAwIGlzIHRoZSBiZXN0IHF1YWxpdHkuXG4gKiBAcHJvcGVydHkgez9ib29sZWFufSBjb25zaWRlclJvdGF0aW9uIFtmYWxzZV0gLSBJZiBzZXQgdG8gYHRydWVgIHRoZW4gR1N0cmVhbWVyIHBpcGVsaW5lIHdpbGxcbiAqIGluY3JlYXNlIHRoZSBkaW1lbnNpb25zIG9mIHRoZSByZXN1bHRpbmcgaW1hZ2VzIHRvIHByb3Blcmx5IGZpdCBpbWFnZXMgaW4gYm90aCBsYW5kc2NhcGUgYW5kXG4gKiBwb3J0cmFpdCBvcmllbnRhdGlvbnMuIFNldCBpdCB0byBgdHJ1ZWAgaWYgdGhlIGRldmljZSByb3RhdGlvbiBpcyBub3QgZ29pbmcgdG8gYmUgdGhlIHNhbWUgZHVyaW5nIHRoZVxuICogYnJvYWRjYXN0aW5nIHNlc3Npb24uXG4gKiBAcHJvcGVydHkgez9ib29sZWFufSBsb2dQaXBlbGluZURldGFpbHMgW2ZhbHNlXSAtIFdoZXRoZXIgdG8gbG9nIEdTdHJlYW1lciBwaXBlbGluZSBldmVudHMgaW50b1xuICogdGhlIHN0YW5kYXJkIGxvZyBvdXRwdXQuIE1pZ2h0IGJlIHVzZWZ1bCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzLlxuICovXG5cbi8qKlxuICogU3RhcnRzIGRldmljZSBzY3JlZW4gYnJvYWRjYXN0IGJ5IGNyZWF0aW5nIE1KUEVHIHNlcnZlci5cbiAqIE11bHRpcGxlIGNhbGxzIHRvIHRoaXMgbWV0aG9kIGhhdmUgbm8gZWZmZWN0IHVubGVzcyB0aGUgcHJldmlvdXMgc3RyZWFtaW5nXG4gKiBzZXNzaW9uIGlzIHN0b3BwZWQuXG4gKiBUaGlzIG1ldGhvZCBvbmx5IHdvcmtzIGlmIHRoZSBgYWRiX3NjcmVlbl9zdHJlYW1pbmdgIGZlYXR1cmUgaXNcbiAqIGVuYWJsZWQgb24gdGhlIHNlcnZlciBzaWRlLlxuICpcbiAqIEBwYXJhbSB7P1N0YXJ0U2NyZWVuU3RyZWFtaW5nT3B0aW9uc30gb3B0aW9ucyAtIFRoZSBhdmFpbGFibGUgb3B0aW9ucy5cbiAqIEB0aHJvd3Mge0Vycm9yfSBJZiBzY3JlZW4gc3RyZWFtaW5nIGhhcyBmYWlsZWQgdG8gc3RhcnQgb3JcbiAqIGlzIG5vdCBzdXBwb3J0ZWQgb24gdGhlIGhvc3Qgc3lzdGVtIG9yXG4gKiB0aGUgY29ycmVzcG9uZGluZyBzZXJ2ZXIgZmVhdHVyZSBpcyBub3QgZW5hYmxlZC5cbiAqL1xuY29tbWFuZHMubW9iaWxlU3RhcnRTY3JlZW5TdHJlYW1pbmcgPSBhc3luYyBmdW5jdGlvbiBtb2JpbGVTdGFydFNjcmVlblN0cmVhbWluZyAob3B0aW9ucyA9IHt9KSB7XG4gIHRoaXMuZW5zdXJlRmVhdHVyZUVuYWJsZWQoQURCX1NDUkVFTl9TVFJFQU1JTkdfRkVBVFVSRSk7XG5cbiAgY29uc3Qge1xuICAgIHdpZHRoLFxuICAgIGhlaWdodCxcbiAgICBiaXRSYXRlLFxuICAgIGhvc3QgPSBERUZBVUxUX0hPU1QsXG4gICAgcG9ydCA9IERFRkFVTFRfUE9SVCxcbiAgICBwYXRobmFtZSxcbiAgICB0Y3BQb3J0ID0gREVGQVVMVF9QT1JUICsgMSxcbiAgICBxdWFsaXR5ID0gREVGQVVMVF9RVUFMSVRZLFxuICAgIGNvbnNpZGVyUm90YXRpb24gPSBmYWxzZSxcbiAgICBsb2dQaXBlbGluZURldGFpbHMgPSBmYWxzZSxcbiAgfSA9IG9wdGlvbnM7XG5cbiAgaWYgKF8uaXNVbmRlZmluZWQodGhpcy5fc2NyZWVuU3RyZWFtaW5nUHJvcHMpKSB7XG4gICAgYXdhaXQgdmVyaWZ5U3RyZWFtaW5nUmVxdWlyZW1lbnRzKHRoaXMuYWRiKTtcbiAgfVxuICBpZiAoIV8uaXNFbXB0eSh0aGlzLl9zY3JlZW5TdHJlYW1pbmdQcm9wcykpIHtcbiAgICB0aGlzLmxvZy5pbmZvKGBUaGUgc2NyZWVuIHN0cmVhbWluZyBzZXNzaW9uIGlzIGFscmVhZHkgcnVubmluZy4gYCArXG4gICAgICBgU3RvcCBpdCBmaXJzdCBpbiBvcmRlciB0byBzdGFydCBhIG5ldyBvbmUuYCk7XG4gICAgcmV0dXJuO1xuICB9XG4gIGlmICgoYXdhaXQgY2hlY2tQb3J0U3RhdHVzKHBvcnQsIGhvc3QpKSA9PT0gJ29wZW4nKSB7XG4gICAgdGhpcy5sb2cuaW5mbyhgVGhlIHBvcnQgIyR7cG9ydH0gYXQgJHtob3N0fSBpcyBidXN5LiBgICtcbiAgICAgIGBBc3N1bWluZyB0aGUgc2NyZWVuIHN0cmVhbWluZyBpcyBhbHJlYWR5IHJ1bm5pbmdgKTtcbiAgICByZXR1cm47XG4gIH1cbiAgaWYgKChhd2FpdCBjaGVja1BvcnRTdGF0dXModGNwUG9ydCwgVENQX0hPU1QpKSA9PT0gJ29wZW4nKSB7XG4gICAgdGhpcy5sb2cuZXJyb3JBbmRUaHJvdyhgVGhlIHBvcnQgIyR7dGNwUG9ydH0gYXQgJHtUQ1BfSE9TVH0gaXMgYnVzeS4gYCArXG4gICAgICBgTWFrZSBzdXJlIHRoZXJlIGFyZSBubyBsZWZ0b3ZlcnMgZnJvbSBwcmV2aW91cyBzZXNzaW9ucy5gKTtcbiAgfVxuICB0aGlzLl9zY3JlZW5TdHJlYW1pbmdQcm9wcyA9IG51bGw7XG5cbiAgY29uc3QgZGV2aWNlSW5mbyA9IGF3YWl0IGdldERldmljZUluZm8odGhpcy5hZGIsIHRoaXMubG9nKTtcbiAgY29uc3QgZGV2aWNlU3RyZWFtaW5nUHJvYyA9IGF3YWl0IGluaXREZXZpY2VTdHJlYW1pbmdQcm9jKHRoaXMuYWRiLCB0aGlzLmxvZywgZGV2aWNlSW5mbywge1xuICAgIHdpZHRoLFxuICAgIGhlaWdodCxcbiAgICBiaXRSYXRlLFxuICB9KTtcbiAgbGV0IGdzdHJlYW1lclBpcGVsaW5lO1xuICB0cnkge1xuICAgIGdzdHJlYW1lclBpcGVsaW5lID0gYXdhaXQgaW5pdEdzdHJlYW1lclBpcGVsaW5lKGRldmljZVN0cmVhbWluZ1Byb2MsIGRldmljZUluZm8sIHRoaXMubG9nLCB7XG4gICAgICB3aWR0aCxcbiAgICAgIGhlaWdodCxcbiAgICAgIHF1YWxpdHksXG4gICAgICB0Y3BQb3J0LFxuICAgICAgY29uc2lkZXJSb3RhdGlvbixcbiAgICAgIGxvZ1BpcGVsaW5lRGV0YWlscyxcbiAgICB9KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIGlmIChkZXZpY2VTdHJlYW1pbmdQcm9jLmtpbGwoMCkpIHtcbiAgICAgIGRldmljZVN0cmVhbWluZ1Byb2Mua2lsbCgpO1xuICAgIH1cbiAgICB0aHJvdyBlO1xuICB9XG5cbiAgbGV0IG1qcGVnU29ja2V0O1xuICBsZXQgbWpwZWdTZXJ2ZXI7XG4gIHRyeSB7XG4gICAgYXdhaXQgbmV3IEIoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgbWpwZWdTb2NrZXQgPSBuZXQuY3JlYXRlQ29ubmVjdGlvbih0Y3BQb3J0LCBUQ1BfSE9TVCwgKCkgPT4ge1xuICAgICAgICB0aGlzLmxvZy5pbmZvKGBTdWNjZXNzZnVsbHkgY29ubmVjdGVkIHRvIE1KUEVHIHN0cmVhbSBhdCB0Y3A6Ly8ke1RDUF9IT1NUfToke3RjcFBvcnR9YCk7XG4gICAgICAgIG1qcGVnU2VydmVyID0gaHR0cC5jcmVhdGVTZXJ2ZXIoKHJlcSwgcmVzKSA9PiB7XG4gICAgICAgICAgY29uc3QgcmVtb3RlQWRkcmVzcyA9IGV4dHJhY3RSZW1vdGVBZGRyZXNzKHJlcSk7XG4gICAgICAgICAgY29uc3QgY3VycmVudFBhdGhuYW1lID0gdXJsLnBhcnNlKHJlcS51cmwpLnBhdGhuYW1lO1xuICAgICAgICAgIHRoaXMubG9nLmluZm8oYEdvdCBhbiBpbmNvbWluZyBzY3JlZW4gYnJhZGNhc3RpbmcgcmVxdWVzdCBmcm9tICR7cmVtb3RlQWRkcmVzc30gYCArXG4gICAgICAgICAgICBgKCR7cmVxLmhlYWRlcnNbJ3VzZXItYWdlbnQnXSB8fCAnVXNlciBBZ2VudCB1bmtub3duJ30pIGF0ICR7Y3VycmVudFBhdGhuYW1lfWApO1xuXG4gICAgICAgICAgaWYgKHBhdGhuYW1lICYmIGN1cnJlbnRQYXRobmFtZSAhPT0gcGF0aG5hbWUpIHtcbiAgICAgICAgICAgIHRoaXMubG9nLmluZm8oJ1JlamVjdGluZyB0aGUgYnJvYWRjYXN0IHJlcXVlc3Qgc2luY2UgaXQgZG9lcyBub3QgbWF0Y2ggdGhlIGdpdmVuIHBhdGhuYW1lJyk7XG4gICAgICAgICAgICByZXMud3JpdGVIZWFkKDQwNCwge1xuICAgICAgICAgICAgICBDb25uZWN0aW9uOiAnY2xvc2UnLFxuICAgICAgICAgICAgICAnQ29udGVudC1UeXBlJzogJ3RleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgnLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICByZXMud3JpdGUoYCcke2N1cnJlbnRQYXRobmFtZX0nIGRpZCBub3QgbWF0Y2ggYW55IGtub3duIGVuZHBvaW50c2ApO1xuICAgICAgICAgICAgcmVzLmVuZCgpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHRoaXMubG9nLmluZm8oJ1N0YXJ0aW5nIE1KUEVHIGJyb2FkY2FzdCcpO1xuICAgICAgICAgIHJlcy53cml0ZUhlYWQoMjAwLCB7XG4gICAgICAgICAgICAnQ2FjaGUtQ29udHJvbCc6ICduby1zdG9yZSwgbm8tY2FjaGUsIG11c3QtcmV2YWxpZGF0ZSwgcHJlLWNoZWNrPTAsIHBvc3QtY2hlY2s9MCwgbWF4LWFnZT0wJyxcbiAgICAgICAgICAgIFByYWdtYTogJ25vLWNhY2hlJyxcbiAgICAgICAgICAgIENvbm5lY3Rpb246ICdjbG9zZScsXG4gICAgICAgICAgICAnQ29udGVudC1UeXBlJzogYG11bHRpcGFydC94LW1peGVkLXJlcGxhY2U7IGJvdW5kYXJ5PSR7Qk9VTkRBUllfU1RSSU5HfWBcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIG1qcGVnU29ja2V0LnBpcGUocmVzKTtcbiAgICAgICAgfSk7XG4gICAgICAgIG1qcGVnU2VydmVyLm9uKCdlcnJvcicsIChlKSA9PiB7XG4gICAgICAgICAgdGhpcy5sb2cud2FybihlKTtcbiAgICAgICAgICByZWplY3QoZSk7XG4gICAgICAgIH0pO1xuICAgICAgICBtanBlZ1NlcnZlci5vbignY2xvc2UnLCAoKSA9PiB7XG4gICAgICAgICAgdGhpcy5sb2cuZGVidWcoYE1KUEVHIHNlcnZlciBhdCBodHRwOi8vJHtob3N0fToke3BvcnR9IGhhcyBiZWVuIGNsb3NlZGApO1xuICAgICAgICB9KTtcbiAgICAgICAgbWpwZWdTZXJ2ZXIub24oJ2xpc3RlbmluZycsICgpID0+IHtcbiAgICAgICAgICB0aGlzLmxvZy5pbmZvKGBTdWNjZXNzZnVsbHkgc3RhcnRlZCBNSlBFRyBzZXJ2ZXIgYXQgaHR0cDovLyR7aG9zdH06JHtwb3J0fWApO1xuICAgICAgICAgIHJlc29sdmUoKTtcbiAgICAgICAgfSk7XG4gICAgICAgIG1qcGVnU2VydmVyLmxpc3Rlbihwb3J0LCBob3N0KTtcbiAgICAgIH0pO1xuICAgICAgbWpwZWdTb2NrZXQub24oJ2Vycm9yJywgKGUpID0+IHtcbiAgICAgICAgdGhpcy5sb2cuZXJyb3IoZSk7XG4gICAgICAgIHJlamVjdChlKTtcbiAgICAgIH0pO1xuICAgIH0pLnRpbWVvdXQoU1RSRUFNSU5HX1NUQVJUVVBfVElNRU9VVF9NUyxcbiAgICAgIGBDYW5ub3QgY29ubmVjdCB0byB0aGUgc3RyZWFtaW5nIHNlcnZlciB3aXRoaW4gJHtTVFJFQU1JTkdfU1RBUlRVUF9USU1FT1VUX01TfW1zYCk7XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICBpZiAoZGV2aWNlU3RyZWFtaW5nUHJvYy5raWxsKDApKSB7XG4gICAgICBkZXZpY2VTdHJlYW1pbmdQcm9jLmtpbGwoKTtcbiAgICB9XG4gICAgaWYgKGdzdHJlYW1lclBpcGVsaW5lLmlzUnVubmluZykge1xuICAgICAgYXdhaXQgZ3N0cmVhbWVyUGlwZWxpbmUuc3RvcCgpO1xuICAgIH1cbiAgICBpZiAobWpwZWdTb2NrZXQpIHtcbiAgICAgIG1qcGVnU29ja2V0LmRlc3Ryb3koKTtcbiAgICB9XG4gICAgaWYgKG1qcGVnU2VydmVyICYmIG1qcGVnU2VydmVyLmxpc3RlbmluZykge1xuICAgICAgbWpwZWdTZXJ2ZXIuY2xvc2UoKTtcbiAgICB9XG4gICAgdGhyb3cgZTtcbiAgfVxuXG4gIHRoaXMuX3NjcmVlblN0cmVhbWluZ1Byb3BzID0ge1xuICAgIGRldmljZVN0cmVhbWluZ1Byb2MsXG4gICAgZ3N0cmVhbWVyUGlwZWxpbmUsXG4gICAgbWpwZWdTb2NrZXQsXG4gICAgbWpwZWdTZXJ2ZXIsXG4gIH07XG59O1xuXG4vKipcbiAqIFN0b3Agc2NyZWVuIHN0cmVhbWluZy5cbiAqIElmIG5vIHNjcmVlbiBzdHJlYW1pbmcgc2VydmVyIGhhcyBiZWVuIHN0YXJ0ZWQgdGhlbiBub3RoaW5nIGlzIGRvbmUuXG4gKi9cbmNvbW1hbmRzLm1vYmlsZVN0b3BTY3JlZW5TdHJlYW1pbmcgPSBhc3luYyBmdW5jdGlvbiBtb2JpbGVTdG9wU2NyZWVuU3RyZWFtaW5nICgvKiBvcHRpb25zID0ge30gKi8pIHtcbiAgaWYgKF8uaXNFbXB0eSh0aGlzLl9zY3JlZW5TdHJlYW1pbmdQcm9wcykpIHtcbiAgICBpZiAoIV8uaXNVbmRlZmluZWQodGhpcy5fc2NyZWVuU3RyZWFtaW5nUHJvcHMpKSB7XG4gICAgICB0aGlzLmxvZy5kZWJ1ZyhgU2NyZWVuIHN0cmVhbWluZyBpcyBub3QgcnVubmluZy4gVGhlcmUgaXMgbm90aGluZyB0byBzdG9wYCk7XG4gICAgfVxuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IHtcbiAgICBkZXZpY2VTdHJlYW1pbmdQcm9jLFxuICAgIGdzdHJlYW1lclBpcGVsaW5lLFxuICAgIG1qcGVnU29ja2V0LFxuICAgIG1qcGVnU2VydmVyLFxuICB9ID0gdGhpcy5fc2NyZWVuU3RyZWFtaW5nUHJvcHM7XG5cbiAgdHJ5IHtcbiAgICBtanBlZ1NvY2tldC5lbmQoKTtcbiAgICBpZiAobWpwZWdTZXJ2ZXIubGlzdGVuaW5nKSB7XG4gICAgICBtanBlZ1NlcnZlci5jbG9zZSgpO1xuICAgIH1cbiAgICBpZiAoZGV2aWNlU3RyZWFtaW5nUHJvYy5raWxsKDApKSB7XG4gICAgICBkZXZpY2VTdHJlYW1pbmdQcm9jLmtpbGwoJ1NJR0lOVCcpO1xuICAgIH1cbiAgICBpZiAoZ3N0cmVhbWVyUGlwZWxpbmUuaXNSdW5uaW5nKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCBnc3RyZWFtZXJQaXBlbGluZS5zdG9wKCdTSUdJTlQnKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdGhpcy5sb2cud2FybihlKTtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBhd2FpdCBnc3RyZWFtZXJQaXBlbGluZS5zdG9wKCdTSUdLSUxMJyk7XG4gICAgICAgIH0gY2F0Y2ggKGUxKSB7XG4gICAgICAgICAgdGhpcy5sb2cuZXJyb3IoZTEpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHRoaXMubG9nLmluZm8oYFN1Y2Nlc3NmdWxseSB0ZXJtaW5hdGVkIHRoZSBzY3JlZW4gc3RyZWFtaW5nIE1KUEVHIHNlcnZlcmApO1xuICB9IGZpbmFsbHkge1xuICAgIHRoaXMuX3NjcmVlblN0cmVhbWluZ1Byb3BzID0gbnVsbDtcbiAgfVxufTtcblxuXG5leHBvcnQgZGVmYXVsdCBjb21tYW5kcztcbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFBQSxJQUFBQSxPQUFBLEdBQUFDLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBQyxRQUFBLEdBQUFELE9BQUE7QUFDQSxJQUFBRSxhQUFBLEdBQUFGLE9BQUE7QUFDQSxJQUFBRyxZQUFBLEdBQUFILE9BQUE7QUFDQSxJQUFBSSxLQUFBLEdBQUFMLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBSyxJQUFBLEdBQUFOLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBTSxTQUFBLEdBQUFQLHNCQUFBLENBQUFDLE9BQUE7QUFDQSxJQUFBTyxTQUFBLEdBQUFQLE9BQUE7QUFDQSxJQUFBUSxjQUFBLEdBQUFSLE9BQUE7QUFDQSxJQUFBUyxJQUFBLEdBQUFWLHNCQUFBLENBQUFDLE9BQUE7QUFFQSxNQUFNVSxRQUFRLEdBQUcsQ0FBQyxDQUFDO0FBRW5CLE1BQU1DLHNCQUFzQixHQUFHLENBQUM7QUFDaEMsTUFBTUMsNEJBQTRCLEdBQUcsSUFBSTtBQUN6QyxNQUFNQyxn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
407
+ exports.default = commands;
408
+ //# sourceMappingURL=streamscreen.js.map