obsidian-integration-testing 2.2.2 → 2.2.3

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.
@@ -51,328 +51,361 @@ const APPIUM_START_POLL_INTERVAL_IN_MILLISECONDS = 500;
51
51
  const APPIUM_START_TIMEOUT_IN_MILLISECONDS = 6e4;
52
52
  const CDP_DEFAULT_PORT = 8315;
53
53
  const COMMAND_TIMEOUT_IN_MILLISECONDS = 300;
54
+ const DEFAULT_TRANSPORT_TYPE = "obsidian-cli";
54
55
  const EMULATOR_BOOT_POLL_INTERVAL_IN_MILLISECONDS = 2e3;
55
56
  const EMULATOR_BOOT_TIMEOUT_IN_MILLISECONDS = 12e4;
56
57
  const KEYCODE_MENU = 82;
57
58
  const KEYCODE_WAKEUP = 224;
58
59
  const SERVER_LAUNCH_TIMEOUT_IN_MILLISECONDS = 3e4;
59
60
  let cachedTransport;
60
- async function createTransportFromOptions(options) {
61
- if (!options || options.type === "obsidian-cli") {
62
- (0, import_log.log)("[transport-factory] Creating DesktopCliTransport (no options or type=obsidian-cli)");
63
- return new import_transport_desktop_cli.DesktopCliTransport();
61
+ class AppiumTransportFactory {
62
+ type;
63
+ constructor(type) {
64
+ this.type = type;
64
65
  }
65
- if (options.type === "obsidian-cdp") {
66
- (0, import_log.log)(`[transport-factory] Creating DesktopCdpTransport (host=${options.host ?? "localhost"}, port=${String(options.port ?? CDP_DEFAULT_PORT)})`);
67
- return new import_transport_desktop_cdp.DesktopCdpTransport({
68
- ...options.host !== void 0 && { cdpHost: options.host },
69
- ...options.port !== void 0 && { cdpPort: options.port },
70
- ...options.commandTimeoutInMilliseconds !== void 0 && { commandTimeoutInMilliseconds: options.commandTimeoutInMilliseconds }
71
- });
66
+ /**
67
+ * Creates an Appium transport by establishing a WebDriverIO session.
68
+ *
69
+ * @param options - Android Appium transport options.
70
+ * @returns A configured Appium transport.
71
+ */
72
+ async create(options) {
73
+ this.log(`Creating AppiumTransport (url=${options.appiumUrl}, device=${options.deviceId})`);
74
+ const url = new URL(options.appiumUrl);
75
+ const port = Number(url.port);
76
+ if (isNaN(port)) {
77
+ throw new Error(`Invalid port in appiumUrl: ${url.port}`);
78
+ }
79
+ const appId = options.appId ?? APP_PACKAGE;
80
+ let appiumProcess;
81
+ let emulatorProcess;
82
+ try {
83
+ let killAutoStartedProcesses2 = function() {
84
+ if (appiumProcess) {
85
+ killProcessTree(appiumProcess);
86
+ (0, import_log.log)(`[transport-factory:${options.type}] Auto-started Appium server stopped.`);
87
+ }
88
+ if (emulatorProcess) {
89
+ killProcessTree(emulatorProcess);
90
+ (0, import_log.log)(`[transport-factory:${options.type}] Auto-started emulator stopped.`);
91
+ }
92
+ };
93
+ var killAutoStartedProcesses = killAutoStartedProcesses2;
94
+ [appiumProcess, emulatorProcess] = await this.startAppiumAndEmulator({
95
+ appiumUrl: url,
96
+ avdName: options.avdName,
97
+ deviceId: options.deviceId,
98
+ port,
99
+ shouldAutoStartAppium: options.shouldAutoStartAppium
100
+ });
101
+ this.log(
102
+ `Connecting to Appium (device=${options.deviceId}, app=${appId}, retryTimeout: ${String(APPIUM_CONNECTION_RETRY_TIMEOUT_IN_MILLISECONDS)}ms, retries: ${String(APPIUM_CONNECTION_RETRY_COUNT)})...`
103
+ );
104
+ const browser = await (0, import_webdriverio.remote)({
105
+ capabilities: {
106
+ "appium:appActivity": APP_ACTIVITY,
107
+ "appium:appPackage": appId,
108
+ "appium:autoGrantPermissions": true,
109
+ "appium:automationName": "UiAutomator2",
110
+ "appium:newCommandTimeout": COMMAND_TIMEOUT_IN_MILLISECONDS,
111
+ "appium:noReset": true,
112
+ "appium:settings": {
113
+ "appium:chromedriverAutodownload": true
114
+ },
115
+ "appium:skipServerInstallation": true,
116
+ "appium:udid": options.deviceId,
117
+ "appium:uiautomator2ServerLaunchTimeout": SERVER_LAUNCH_TIMEOUT_IN_MILLISECONDS,
118
+ "platformName": "Android"
119
+ },
120
+ connectionRetryCount: APPIUM_CONNECTION_RETRY_COUNT,
121
+ connectionRetryTimeout: APPIUM_CONNECTION_RETRY_TIMEOUT_IN_MILLISECONDS,
122
+ hostname: url.hostname,
123
+ logLevel: "warn",
124
+ path: url.pathname,
125
+ port
126
+ });
127
+ this.log("Appium session established.");
128
+ const appiumTransport = new import_transport_appium.AppiumTransport({
129
+ appId,
130
+ browser,
131
+ platform: "android",
132
+ ...options.vaultBasePath !== void 0 && { vaultBasePath: options.vaultBasePath }
133
+ });
134
+ const originalDispose = appiumTransport.dispose.bind(appiumTransport);
135
+ const transport = appiumTransport;
136
+ transport.dispose = async () => {
137
+ try {
138
+ await originalDispose();
139
+ } finally {
140
+ killAutoStartedProcesses2();
141
+ }
142
+ };
143
+ transport.disposeSync = () => {
144
+ killAutoStartedProcesses2();
145
+ };
146
+ return transport;
147
+ } catch (error) {
148
+ if (appiumProcess) {
149
+ killProcessTree(appiumProcess);
150
+ this.log("Killed auto-started Appium server after connection failure.");
151
+ }
152
+ if (emulatorProcess) {
153
+ killProcessTree(emulatorProcess);
154
+ this.log("Killed auto-started emulator after connection failure.");
155
+ }
156
+ throw error;
157
+ }
72
158
  }
73
- (0, import_log.log)(`[transport-factory] Creating AppiumTransport (url=${options.appiumUrl}, device=${options.deviceId})`);
74
- return createAppiumTransport(options);
75
- }
76
- async function getOrCreateTransport(options) {
77
- if (cachedTransport) {
78
- return cachedTransport;
159
+ checkAppiumReachable(url) {
160
+ return new Promise((resolve, reject) => {
161
+ const statusUrl = new URL("/status", url);
162
+ const req = import_node_http.default.get(statusUrl, { timeout: APPIUM_PREFLIGHT_TIMEOUT_IN_MILLISECONDS }, (res) => {
163
+ res.resume();
164
+ resolve();
165
+ });
166
+ req.on("timeout", () => {
167
+ req.destroy();
168
+ reject(
169
+ new Error(
170
+ `Appium server at ${url.origin} did not respond within ${String(APPIUM_PREFLIGHT_TIMEOUT_IN_MILLISECONDS)}ms. Is the Appium server running?`
171
+ )
172
+ );
173
+ });
174
+ req.on("error", (err) => {
175
+ reject(
176
+ new Error(
177
+ `Cannot reach Appium server at ${url.origin}: ${err.message}. Is the Appium server running?`
178
+ )
179
+ );
180
+ });
181
+ });
79
182
  }
80
- const result = await createTransportFromOptions(options);
81
- cachedTransport = result;
82
- return result;
83
- }
84
- function checkAppiumReachable(url) {
85
- return new Promise((resolve, reject) => {
86
- const statusUrl = new URL("/status", url);
87
- const req = import_node_http.default.get(statusUrl, { timeout: APPIUM_PREFLIGHT_TIMEOUT_IN_MILLISECONDS }, (res) => {
88
- res.resume();
89
- resolve();
183
+ async checkDeviceConnected(deviceId) {
184
+ const output = await new Promise((resolve, reject) => {
185
+ (0, import_node_child_process.execFile)("adb", ["devices"], { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS }, (error, stdout, stderr) => {
186
+ if (error) {
187
+ reject(new Error(`Failed to run 'adb devices': ${error.message}. Is ADB installed and in PATH?`));
188
+ return;
189
+ }
190
+ if (stderr) {
191
+ this.log(`ADB stderr: ${stderr.trim()}`);
192
+ }
193
+ resolve(stdout);
194
+ });
90
195
  });
91
- req.on("timeout", () => {
92
- req.destroy();
93
- reject(
94
- new Error(
95
- `Appium server at ${url.origin} did not respond within ${String(APPIUM_PREFLIGHT_TIMEOUT_IN_MILLISECONDS)}ms. Is the Appium server running?`
96
- )
196
+ const lines = output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
197
+ const deviceLine = lines.find((line) => line.startsWith(deviceId));
198
+ return deviceLine?.includes(" device") ?? false;
199
+ }
200
+ async ensureDeviceConnected(avdName, deviceId) {
201
+ this.log(`Checking device ${deviceId} is connected via ADB...`);
202
+ if (await this.checkDeviceConnected(deviceId)) {
203
+ this.log(`Device ${deviceId} is connected.`);
204
+ return void 0;
205
+ }
206
+ if (!avdName) {
207
+ throw new Error(
208
+ `Device "${deviceId}" is not connected. Start the emulator before running integration tests, or set avdName in transport options to auto-start it.`
97
209
  );
98
- });
99
- req.on("error", (err) => {
100
- reject(
101
- new Error(
102
- `Cannot reach Appium server at ${url.origin}: ${err.message}. Is the Appium server running?`
103
- )
210
+ }
211
+ this.log(`Device ${deviceId} not found, starting emulator AVD "${avdName}"...`);
212
+ const emulatorProcess = this.startEmulator(avdName);
213
+ await this.waitForDevice(deviceId);
214
+ this.log(`Emulator "${avdName}" started, device ${deviceId} is connected.`);
215
+ return emulatorProcess;
216
+ }
217
+ log(message) {
218
+ (0, import_log.log)(`[transport-factory:${this.type}] ${message}`);
219
+ }
220
+ resolveEmulatorBinary() {
221
+ const sdkRoot = import_node_process.default.env["ANDROID_HOME"] ?? import_node_process.default.env["ANDROID_SDK_ROOT"];
222
+ if (!sdkRoot) {
223
+ throw new Error(
224
+ "Cannot find Android emulator: neither ANDROID_HOME nor ANDROID_SDK_ROOT environment variable is set."
225
+ );
226
+ }
227
+ return (0, import_node_path.join)(sdkRoot, "emulator", "emulator");
228
+ }
229
+ async sendKeyEvent(deviceId, keyCode, description) {
230
+ await new Promise((resolve) => {
231
+ (0, import_node_child_process.execFile)(
232
+ "adb",
233
+ ["-s", deviceId, "shell", "input", "keyevent", String(keyCode)],
234
+ { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS },
235
+ (error) => {
236
+ if (error) {
237
+ this.log(
238
+ `Warning: failed to ${description} (keyevent ${String(keyCode)}): ${error instanceof Error ? error.message : "unknown error"}`
239
+ );
240
+ }
241
+ resolve();
242
+ }
104
243
  );
105
244
  });
106
- });
107
- }
108
- async function checkDeviceConnected(deviceId) {
109
- const output = await new Promise((resolve, reject) => {
110
- (0, import_node_child_process.execFile)("adb", ["devices"], { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS }, (error, stdout, stderr) => {
111
- if (error) {
112
- reject(new Error(`Failed to run 'adb devices': ${error.message}. Is ADB installed and in PATH?`));
113
- return;
114
- }
115
- if (stderr) {
116
- (0, import_log.log)(`[transport-factory] ADB stderr: ${stderr.trim()}`);
245
+ }
246
+ async startAppiumAndEmulator(params) {
247
+ const { appiumUrl, avdName, deviceId, port, shouldAutoStartAppium } = params;
248
+ let needsAppiumStart = false;
249
+ this.log(`Checking Appium server at ${appiumUrl.href}...`);
250
+ try {
251
+ await this.checkAppiumReachable(appiumUrl);
252
+ this.log("Appium server is reachable.");
253
+ } catch (error) {
254
+ if (shouldAutoStartAppium === false) {
255
+ throw error;
117
256
  }
118
- resolve(stdout);
257
+ needsAppiumStart = true;
258
+ }
259
+ let appiumProcess;
260
+ if (needsAppiumStart) {
261
+ this.log(`Appium not reachable, auto-starting on port ${String(port)}...`);
262
+ appiumProcess = this.startAppiumServer(port);
263
+ }
264
+ const [, emulatorProcess] = await Promise.all([
265
+ needsAppiumStart ? this.waitForAppiumReady(appiumUrl).then(() => {
266
+ this.log("Auto-started Appium server is ready.");
267
+ }) : Promise.resolve(),
268
+ this.ensureDeviceConnected(avdName, deviceId)
269
+ ]);
270
+ return [appiumProcess, emulatorProcess];
271
+ }
272
+ startAppiumServer(port) {
273
+ const child = (0, import_node_child_process.spawn)(`npx appium --log-timestamp --port ${String(port)}`, {
274
+ detached: true,
275
+ shell: true,
276
+ stdio: ["ignore", "inherit", "inherit"]
119
277
  });
120
- });
121
- const lines = output.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
122
- const deviceLine = lines.find((line) => line.startsWith(deviceId));
123
- return deviceLine?.includes(" device") ?? false;
124
- }
125
- async function createAppiumTransport(options) {
126
- const url = new URL(options.appiumUrl);
127
- const port = Number(url.port);
128
- if (isNaN(port)) {
129
- throw new Error(`Invalid port in appiumUrl: ${url.port}`);
278
+ child.unref();
279
+ return child;
130
280
  }
131
- const appId = options.appId ?? APP_PACKAGE;
132
- let appiumProcess;
133
- let emulatorProcess;
134
- try {
135
- let killAutoStartedProcesses2 = function() {
136
- if (appiumProcess) {
137
- appiumProcess.kill();
138
- (0, import_log.log)("[transport-factory] Auto-started Appium server stopped.");
139
- }
140
- if (emulatorProcess) {
141
- emulatorProcess.kill();
142
- (0, import_log.log)("[transport-factory] Auto-started emulator stopped.");
143
- }
144
- };
145
- var killAutoStartedProcesses = killAutoStartedProcesses2;
146
- [appiumProcess, emulatorProcess] = await startAppiumAndEmulator({
147
- appiumUrl: url,
148
- avdName: options.avdName,
149
- deviceId: options.deviceId,
150
- port,
151
- shouldAutoStartAppium: options.shouldAutoStartAppium
281
+ startEmulator(avdName) {
282
+ const emulatorBinary = this.resolveEmulatorBinary();
283
+ this.log(`Running: ${emulatorBinary} -avd ${avdName} -no-snapshot-save`);
284
+ const child = (0, import_node_child_process.spawn)(emulatorBinary, ["-avd", avdName, "-no-snapshot-save"], {
285
+ detached: true,
286
+ stdio: ["ignore", "ignore", "ignore"]
152
287
  });
153
- (0, import_log.log)(
154
- `[transport-factory] Connecting to Appium (device=${options.deviceId}, app=${appId}, retryTimeout: ${String(APPIUM_CONNECTION_RETRY_TIMEOUT_IN_MILLISECONDS)}ms, retries: ${String(APPIUM_CONNECTION_RETRY_COUNT)})...`
288
+ child.unref();
289
+ return child;
290
+ }
291
+ async waitForAppiumReady(url) {
292
+ this.log(
293
+ `Waiting for Appium at ${url.href} (timeout: ${String(APPIUM_START_TIMEOUT_IN_MILLISECONDS)}ms, poll: ${String(APPIUM_START_POLL_INTERVAL_IN_MILLISECONDS)}ms)...`
155
294
  );
156
- const browser = await (0, import_webdriverio.remote)({
157
- capabilities: {
158
- "appium:appActivity": APP_ACTIVITY,
159
- "appium:appPackage": appId,
160
- "appium:autoGrantPermissions": true,
161
- "appium:automationName": "UiAutomator2",
162
- "appium:newCommandTimeout": COMMAND_TIMEOUT_IN_MILLISECONDS,
163
- "appium:noReset": true,
164
- "appium:settings": {
165
- "appium:chromedriverAutodownload": true
166
- },
167
- "appium:skipServerInstallation": true,
168
- "appium:udid": options.deviceId,
169
- "appium:uiautomator2ServerLaunchTimeout": SERVER_LAUNCH_TIMEOUT_IN_MILLISECONDS,
170
- "platformName": "Android"
171
- },
172
- connectionRetryCount: APPIUM_CONNECTION_RETRY_COUNT,
173
- connectionRetryTimeout: APPIUM_CONNECTION_RETRY_TIMEOUT_IN_MILLISECONDS,
174
- hostname: url.hostname,
175
- logLevel: "warn",
176
- path: url.pathname,
177
- port
178
- });
179
- (0, import_log.log)("[transport-factory] Appium session established.");
180
- const appiumTransport = new import_transport_appium.AppiumTransport({
181
- appId,
182
- browser,
183
- platform: "android",
184
- ...options.vaultBasePath !== void 0 && { vaultBasePath: options.vaultBasePath }
185
- });
186
- const originalDispose = appiumTransport.dispose.bind(appiumTransport);
187
- const transport = appiumTransport;
188
- transport.dispose = async () => {
295
+ const deadline = Date.now() + APPIUM_START_TIMEOUT_IN_MILLISECONDS;
296
+ while (Date.now() < deadline) {
189
297
  try {
190
- await originalDispose();
191
- } finally {
192
- killAutoStartedProcesses2();
298
+ await this.checkAppiumReachable(url);
299
+ return;
300
+ } catch {
301
+ await new Promise((resolve) => {
302
+ setTimeout(resolve, APPIUM_START_POLL_INTERVAL_IN_MILLISECONDS);
303
+ });
193
304
  }
194
- };
195
- transport.disposeSync = () => {
196
- killAutoStartedProcesses2();
197
- };
198
- return transport;
199
- } catch (error) {
200
- if (appiumProcess) {
201
- appiumProcess.kill();
202
- (0, import_log.log)("[transport-factory] Killed auto-started Appium server after connection failure.");
203
305
  }
204
- if (emulatorProcess) {
205
- emulatorProcess.kill();
206
- (0, import_log.log)("[transport-factory] Killed auto-started emulator after connection failure.");
207
- }
208
- throw error;
209
- }
210
- }
211
- async function ensureDeviceConnected(params) {
212
- const { avdName, deviceId } = params;
213
- (0, import_log.log)(`[transport-factory] Checking device ${deviceId} is connected via ADB...`);
214
- if (await checkDeviceConnected(deviceId)) {
215
- (0, import_log.log)(`[transport-factory] Device ${deviceId} is connected.`);
216
- return void 0;
217
- }
218
- if (!avdName) {
219
306
  throw new Error(
220
- `Device "${deviceId}" is not connected. Start the emulator before running integration tests, or set avdName in transport options to auto-start it.`
307
+ `Auto-started Appium server did not become ready within ${String(APPIUM_START_TIMEOUT_IN_MILLISECONDS)}ms`
221
308
  );
222
309
  }
223
- (0, import_log.log)(`[transport-factory] Device ${deviceId} not found, starting emulator AVD "${avdName}"...`);
224
- const emulatorProcess = startEmulator(avdName);
225
- await waitForDevice(deviceId);
226
- (0, import_log.log)(`[transport-factory] Emulator "${avdName}" started, device ${deviceId} is connected.`);
227
- return emulatorProcess;
228
- }
229
- function resolveEmulatorBinary() {
230
- const sdkRoot = import_node_process.default.env["ANDROID_HOME"] ?? import_node_process.default.env["ANDROID_SDK_ROOT"];
231
- if (!sdkRoot) {
232
- throw new Error(
233
- "Cannot find Android emulator: neither ANDROID_HOME nor ANDROID_SDK_ROOT environment variable is set."
310
+ async waitForBoot(deviceId, deadline) {
311
+ const remainingMs = Math.max(0, deadline - Date.now());
312
+ this.log(
313
+ `Waiting for device ${deviceId} to finish booting (remaining: ${String(remainingMs)}ms, poll: ${String(EMULATOR_BOOT_POLL_INTERVAL_IN_MILLISECONDS)}ms)...`
234
314
  );
235
- }
236
- return (0, import_node_path.join)(sdkRoot, "emulator", "emulator");
237
- }
238
- async function sendKeyEvent(deviceId, keyCode, description) {
239
- await new Promise((resolve) => {
240
- (0, import_node_child_process.execFile)(
241
- "adb",
242
- ["-s", deviceId, "shell", "input", "keyevent", String(keyCode)],
243
- { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS },
244
- (error) => {
245
- if (error) {
246
- (0, import_log.log)(
247
- `[transport-factory] Warning: failed to ${description} (keyevent ${String(keyCode)}): ${error instanceof Error ? error.message : "unknown error"}`
248
- );
249
- }
250
- resolve();
315
+ while (Date.now() < deadline) {
316
+ const isBooted = await new Promise((resolve) => {
317
+ (0, import_node_child_process.execFile)(
318
+ "adb",
319
+ ["-s", deviceId, "shell", "getprop", "sys.boot_completed"],
320
+ { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS },
321
+ (_error, stdout) => {
322
+ resolve(stdout.trim() === "1");
323
+ }
324
+ );
325
+ });
326
+ if (isBooted) {
327
+ this.log(`Device ${deviceId} boot completed.`);
328
+ return;
251
329
  }
252
- );
253
- });
254
- }
255
- async function startAppiumAndEmulator(params) {
256
- const { appiumUrl, avdName, deviceId, port, shouldAutoStartAppium } = params;
257
- let needsAppiumStart = false;
258
- (0, import_log.log)(`[transport-factory] Checking Appium server at ${appiumUrl.href}...`);
259
- try {
260
- await checkAppiumReachable(appiumUrl);
261
- (0, import_log.log)("[transport-factory] Appium server is reachable.");
262
- } catch (error) {
263
- if (shouldAutoStartAppium === false) {
264
- throw error;
330
+ await new Promise((resolve) => {
331
+ setTimeout(resolve, EMULATOR_BOOT_POLL_INTERVAL_IN_MILLISECONDS);
332
+ });
265
333
  }
266
- needsAppiumStart = true;
267
- }
268
- let appiumProcess;
269
- if (needsAppiumStart) {
270
- (0, import_log.log)(`[transport-factory] Appium not reachable, auto-starting on port ${String(port)}...`);
271
- appiumProcess = startAppiumServer(port);
334
+ throw new Error(
335
+ `Device "${deviceId}" connected but did not finish booting within ${String(EMULATOR_BOOT_TIMEOUT_IN_MILLISECONDS)}ms.`
336
+ );
272
337
  }
273
- const [, emulatorProcess] = await Promise.all([
274
- needsAppiumStart ? waitForAppiumReady(appiumUrl).then(() => {
275
- (0, import_log.log)("[transport-factory] Auto-started Appium server is ready.");
276
- }) : Promise.resolve(),
277
- ensureDeviceConnected({ avdName, deviceId })
278
- ]);
279
- return [appiumProcess, emulatorProcess];
280
- }
281
- function startAppiumServer(port) {
282
- const child = (0, import_node_child_process.spawn)(`npx appium --log-timestamp --port ${String(port)}`, {
283
- detached: true,
284
- shell: true,
285
- stdio: ["ignore", "inherit", "inherit"]
286
- });
287
- child.unref();
288
- return child;
289
- }
290
- function startEmulator(avdName) {
291
- const emulatorBinary = resolveEmulatorBinary();
292
- (0, import_log.log)(`[transport-factory] Running: ${emulatorBinary} -avd ${avdName} -no-snapshot-save`);
293
- const child = (0, import_node_child_process.spawn)(emulatorBinary, ["-avd", avdName, "-no-snapshot-save"], {
294
- detached: true,
295
- stdio: ["ignore", "ignore", "ignore"]
296
- });
297
- child.unref();
298
- return child;
299
- }
300
- async function waitForAppiumReady(url) {
301
- (0, import_log.log)(
302
- `[transport-factory] Waiting for Appium at ${url.href} (timeout: ${String(APPIUM_START_TIMEOUT_IN_MILLISECONDS)}ms, poll: ${String(APPIUM_START_POLL_INTERVAL_IN_MILLISECONDS)}ms)...`
303
- );
304
- const deadline = Date.now() + APPIUM_START_TIMEOUT_IN_MILLISECONDS;
305
- while (Date.now() < deadline) {
306
- try {
307
- await checkAppiumReachable(url);
308
- return;
309
- } catch {
338
+ async waitForDevice(deviceId) {
339
+ this.log(
340
+ `Waiting for device ${deviceId} to appear in ADB (timeout: ${String(EMULATOR_BOOT_TIMEOUT_IN_MILLISECONDS)}ms, poll: ${String(EMULATOR_BOOT_POLL_INTERVAL_IN_MILLISECONDS)}ms)...`
341
+ );
342
+ const deadline = Date.now() + EMULATOR_BOOT_TIMEOUT_IN_MILLISECONDS;
343
+ while (Date.now() < deadline) {
344
+ if (await this.checkDeviceConnected(deviceId)) {
345
+ this.log(`Device ${deviceId} appeared in ADB, waiting for boot to complete...`);
346
+ await this.waitForBoot(deviceId, deadline);
347
+ await this.wakeScreen(deviceId);
348
+ return;
349
+ }
310
350
  await new Promise((resolve) => {
311
- setTimeout(resolve, APPIUM_START_POLL_INTERVAL_IN_MILLISECONDS);
351
+ setTimeout(resolve, EMULATOR_BOOT_POLL_INTERVAL_IN_MILLISECONDS);
312
352
  });
313
353
  }
354
+ throw new Error(
355
+ `Emulator device "${deviceId}" did not appear within ${String(EMULATOR_BOOT_TIMEOUT_IN_MILLISECONDS)}ms.`
356
+ );
357
+ }
358
+ async wakeScreen(deviceId) {
359
+ this.log(`Waking screen on device ${deviceId}...`);
360
+ await this.sendKeyEvent(deviceId, KEYCODE_WAKEUP, "wake screen");
361
+ await this.sendKeyEvent(deviceId, KEYCODE_MENU, "dismiss lock screen");
362
+ this.log(`Screen wake complete on device ${deviceId}.`);
314
363
  }
315
- throw new Error(
316
- `Auto-started Appium server did not become ready within ${String(APPIUM_START_TIMEOUT_IN_MILLISECONDS)}ms`
317
- );
318
364
  }
319
- async function waitForBoot(deviceId, deadline) {
320
- const remainingMs = Math.max(0, deadline - Date.now());
321
- (0, import_log.log)(
322
- `[transport-factory] Waiting for device ${deviceId} to finish booting (remaining: ${String(remainingMs)}ms, poll: ${String(EMULATOR_BOOT_POLL_INTERVAL_IN_MILLISECONDS)}ms)...`
323
- );
324
- while (Date.now() < deadline) {
325
- const isBooted = await new Promise((resolve) => {
326
- (0, import_node_child_process.execFile)(
327
- "adb",
328
- ["-s", deviceId, "shell", "getprop", "sys.boot_completed"],
329
- { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS },
330
- (_error, stdout) => {
331
- resolve(stdout.trim() === "1");
332
- }
333
- );
334
- });
335
- if (isBooted) {
336
- (0, import_log.log)(`[transport-factory] Device ${deviceId} boot completed.`);
337
- return;
338
- }
339
- await new Promise((resolve) => {
340
- setTimeout(resolve, EMULATOR_BOOT_POLL_INTERVAL_IN_MILLISECONDS);
365
+ async function createTransportFromOptions(options) {
366
+ const type = options?.type ?? DEFAULT_TRANSPORT_TYPE;
367
+ if (!options || options.type === "obsidian-cli") {
368
+ (0, import_log.log)(`[transport-factory:${type}] Creating DesktopCliTransport`);
369
+ return new import_transport_desktop_cli.DesktopCliTransport();
370
+ }
371
+ if (options.type === "obsidian-cdp") {
372
+ (0, import_log.log)(`[transport-factory:${type}] Creating DesktopCdpTransport (host=${options.host ?? "localhost"}, port=${String(options.port ?? CDP_DEFAULT_PORT)})`);
373
+ return new import_transport_desktop_cdp.DesktopCdpTransport({
374
+ ...options.host !== void 0 && { cdpHost: options.host },
375
+ ...options.port !== void 0 && { cdpPort: options.port },
376
+ ...options.commandTimeoutInMilliseconds !== void 0 && { commandTimeoutInMilliseconds: options.commandTimeoutInMilliseconds }
341
377
  });
342
378
  }
343
- throw new Error(
344
- `Device "${deviceId}" connected but did not finish booting within ${String(EMULATOR_BOOT_TIMEOUT_IN_MILLISECONDS)}ms.`
345
- );
379
+ const factory = new AppiumTransportFactory(type);
380
+ return factory.create(options);
346
381
  }
347
- async function waitForDevice(deviceId) {
348
- (0, import_log.log)(
349
- `[transport-factory] Waiting for device ${deviceId} to appear in ADB (timeout: ${String(EMULATOR_BOOT_TIMEOUT_IN_MILLISECONDS)}ms, poll: ${String(EMULATOR_BOOT_POLL_INTERVAL_IN_MILLISECONDS)}ms)...`
350
- );
351
- const deadline = Date.now() + EMULATOR_BOOT_TIMEOUT_IN_MILLISECONDS;
352
- while (Date.now() < deadline) {
353
- if (await checkDeviceConnected(deviceId)) {
354
- (0, import_log.log)(`[transport-factory] Device ${deviceId} appeared in ADB, waiting for boot to complete...`);
355
- await waitForBoot(deviceId, deadline);
356
- await wakeScreen(deviceId);
357
- return;
358
- }
359
- await new Promise((resolve) => {
360
- setTimeout(resolve, EMULATOR_BOOT_POLL_INTERVAL_IN_MILLISECONDS);
361
- });
382
+ async function getOrCreateTransport(options) {
383
+ if (cachedTransport) {
384
+ return cachedTransport;
362
385
  }
363
- throw new Error(
364
- `Emulator device "${deviceId}" did not appear within ${String(EMULATOR_BOOT_TIMEOUT_IN_MILLISECONDS)}ms.`
365
- );
386
+ const result = await createTransportFromOptions(options);
387
+ cachedTransport = result;
388
+ return result;
366
389
  }
367
- async function wakeScreen(deviceId) {
368
- (0, import_log.log)(`[transport-factory] Waking screen on device ${deviceId}...`);
369
- await sendKeyEvent(deviceId, KEYCODE_WAKEUP, "wake screen");
370
- await sendKeyEvent(deviceId, KEYCODE_MENU, "dismiss lock screen");
371
- (0, import_log.log)(`[transport-factory] Screen wake complete on device ${deviceId}.`);
390
+ function killProcessTree(child) {
391
+ if (child.pid === void 0) {
392
+ return;
393
+ }
394
+ if (import_node_process.default.platform === "win32") {
395
+ try {
396
+ (0, import_node_child_process.execFileSync)("taskkill", ["/F", "/T", "/PID", String(child.pid)], { stdio: "ignore" });
397
+ } catch (error) {
398
+ (0, import_log.log)(
399
+ `[transport-factory] taskkill for PID ${String(child.pid)} failed (may have already exited): ${error instanceof Error ? error.message : "unknown error"}`
400
+ );
401
+ }
402
+ } else {
403
+ child.kill("SIGKILL");
404
+ }
372
405
  }
373
406
  // Annotate the CommonJS export names for ESM import in node:
374
407
  0 && (module.exports = {
375
408
  createTransportFromOptions,
376
409
  getOrCreateTransport
377
410
  });
378
- //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL3RyYW5zcG9ydC1mYWN0b3J5LnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIEBmaWxlXG4gKlxuICogRmFjdG9yeSBmb3IgY3JlYXRpbmcgdHJhbnNwb3J0IGluc3RhbmNlcyBmcm9tIHtAbGluayBPYnNpZGlhblRyYW5zcG9ydE9wdGlvbnN9LlxuICovXG5cbi8qIHY4IGlnbm9yZSBzdGFydCAtLSBJbnRlZ3JhdGlvbi10aW1lIGZhY3RvcnkgY292ZXJlZCBieSBpbnRlZ3JhdGlvbiB0ZXN0cywgbm90IHVuaXQgdGVzdHMuICovXG5cbmltcG9ydCB0eXBlIHsgQ2hpbGRQcm9jZXNzIH0gZnJvbSAnbm9kZTpjaGlsZF9wcm9jZXNzJztcblxuaW1wb3J0IHtcbiAgZXhlY0ZpbGUsXG4gIHNwYXduXG59IGZyb20gJ25vZGU6Y2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgaHR0cCBmcm9tICdub2RlOmh0dHAnO1xuaW1wb3J0IHsgam9pbiB9IGZyb20gJ25vZGU6cGF0aCc7XG5pbXBvcnQgcHJvY2VzcyBmcm9tICdub2RlOnByb2Nlc3MnO1xuaW1wb3J0IHsgcmVtb3RlIH0gZnJvbSAnd2ViZHJpdmVyaW8nO1xuXG5pbXBvcnQgdHlwZSB7XG4gIE9ic2lkaWFuQW5kcm9pZEFwcGl1bVRyYW5zcG9ydE9wdGlvbnMsXG4gIE9ic2lkaWFuVHJhbnNwb3J0T3B0aW9uc1xufSBmcm9tICcuL3RyYW5zcG9ydC1vcHRpb25zLmNqcyc7XG5pbXBvcnQgdHlwZSB7IE9ic2lkaWFuVHJhbnNwb3J0IH0gZnJvbSAnLi90cmFuc3BvcnQuY2pzJztcblxuaW1wb3J0IHsgbG9nIH0gZnJvbSAnLi9sb2cuY2pzJztcbmltcG9ydCB7IEFwcGl1bVRyYW5zcG9ydCB9IGZyb20gJy4vdHJhbnNwb3J0LWFwcGl1bS5janMnO1xuaW1wb3J0IHsgRGVza3RvcENkcFRyYW5zcG9ydCB9IGZyb20gJy4vdHJhbnNwb3J0LWRlc2t0b3AtY2RwLmNqcyc7XG5pbXBvcnQgeyBEZXNrdG9wQ2xpVHJhbnNwb3J0IH0gZnJvbSAnLi90cmFuc3BvcnQtZGVza3RvcC1jbGkuY2pzJztcblxuY29uc3QgQVBQX1BBQ0tBR0UgPSAnbWQub2JzaWRpYW4nO1xuY29uc3QgQVBQX0FDVElWSVRZID0gYCR7QVBQX1BBQ0tBR0V9Lk1haW5BY3Rpdml0eWA7XG5jb25zdCBBREJfREVWSUNFX0NIRUNLX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gNTAwMDtcbmNvbnN0IEFQUElVTV9DT05ORUNUSU9OX1JFVFJZX0NPVU5UID0gMTtcbmNvbnN0IEFQUElVTV9DT05ORUNUSU9OX1JFVFJZX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gNjAwMDA7XG5jb25zdCBBUFBJVU1fUFJFRkxJR0hUX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gNTAwMDtcbmNvbnN0IEFQUElVTV9TVEFSVF9QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyA9IDUwMDtcbmNvbnN0IEFQUElVTV9TVEFSVF9USU1FT1VUX0lOX01JTExJU0VDT05EUyA9IDYwMDAwO1xuY29uc3QgQ0RQX0RFRkFVTFRfUE9SVCA9IDgzMTU7XG5jb25zdCBDT01NQU5EX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gMzAwO1xuY29uc3QgRU1VTEFUT1JfQk9PVF9QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyA9IDIwMDA7XG5jb25zdCBFTVVMQVRPUl9CT09UX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gMTIwMDAwO1xuY29uc3QgS0VZQ09ERV9NRU5VID0gODI7XG5jb25zdCBLRVlDT0RFX1dBS0VVUCA9IDIyNDtcbmNvbnN0IFNFUlZFUl9MQVVOQ0hfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSAzMDAwMDtcblxuLyoqXG4gKiBQYXJhbWV0ZXJzIGZvciB7QGxpbmsgZW5zdXJlRGV2aWNlQ29ubmVjdGVkfS5cbiAqL1xuaW50ZXJmYWNlIEVuc3VyZURldmljZUNvbm5lY3RlZFBhcmFtcyB7XG4gIC8qKiBBVkQgbmFtZSB0byBhdXRvLXN0YXJ0IGlmIHRoZSBkZXZpY2UgaXMgbm90IGNvbm5lY3RlZC4gKi9cbiAgYXZkTmFtZT86IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAvKiogRGV2aWNlIFVESUQgdG8gY2hlY2sgKGUuZy4gYCdlbXVsYXRvci01NTU0J2ApLiAqL1xuICBkZXZpY2VJZDogc3RyaW5nO1xufVxuXG4vKipcbiAqIFBhcmFtZXRlcnMgZm9yIHtAbGluayBzdGFydEFwcGl1bUFuZEVtdWxhdG9yfS5cbiAqL1xuaW50ZXJmYWNlIFN0YXJ0QXBwaXVtQW5kRW11bGF0b3JQYXJhbXMge1xuICAvKiogVGhlIEFwcGl1bSBzZXJ2ZXIgVVJMLiAqL1xuICBhcHBpdW1Vcmw6IFVSTDtcblxuICAvKiogQVZEIG5hbWUgdG8gYXV0by1zdGFydCBpZiB0aGUgZGV2aWNlIGlzIG5vdCBjb25uZWN0ZWQuICovXG4gIGF2ZE5hbWU/OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG5cbiAgLyoqIERldmljZSBVRElELiAqL1xuICBkZXZpY2VJZDogc3RyaW5nO1xuXG4gIC8qKiBUaGUgQXBwaXVtIHNlcnZlciBwb3J0LiAqL1xuICBwb3J0OiBudW1iZXI7XG5cbiAgLyoqIFdoZXRoZXIgQXBwaXVtIGF1dG8tc3RhcnQgaXMgYWxsb3dlZC4gKi9cbiAgc2hvdWxkQXV0b1N0YXJ0QXBwaXVtPzogYm9vbGVhbiB8IHVuZGVmaW5lZDtcbn1cblxubGV0IGNhY2hlZFRyYW5zcG9ydDogT2JzaWRpYW5UcmFuc3BvcnQgfCB1bmRlZmluZWQ7XG5cbi8qKlxuICogQ3JlYXRlcyBhIG5ldyB0cmFuc3BvcnQgaW5zdGFuY2UgZnJvbSB0aGUgZ2l2ZW4gb3B0aW9ucy5cbiAqXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFRyYW5zcG9ydCBjb25maWd1cmF0aW9uLiBEZWZhdWx0cyB0byBDTEkgdHJhbnNwb3J0LlxuICogQHJldHVybnMgQSBuZXcgdHJhbnNwb3J0IGluc3RhbmNlLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlVHJhbnNwb3J0RnJvbU9wdGlvbnMob3B0aW9ucz86IE9ic2lkaWFuVHJhbnNwb3J0T3B0aW9ucyk6IFByb21pc2U8T2JzaWRpYW5UcmFuc3BvcnQ+IHtcbiAgaWYgKCFvcHRpb25zIHx8IG9wdGlvbnMudHlwZSA9PT0gJ29ic2lkaWFuLWNsaScpIHtcbiAgICBsb2coJ1t0cmFuc3BvcnQtZmFjdG9yeV0gQ3JlYXRpbmcgRGVza3RvcENsaVRyYW5zcG9ydCAobm8gb3B0aW9ucyBvciB0eXBlPW9ic2lkaWFuLWNsaSknKTtcbiAgICByZXR1cm4gbmV3IERlc2t0b3BDbGlUcmFuc3BvcnQoKTtcbiAgfVxuXG4gIGlmIChvcHRpb25zLnR5cGUgPT09ICdvYnNpZGlhbi1jZHAnKSB7XG4gICAgbG9nKGBbdHJhbnNwb3J0LWZhY3RvcnldIENyZWF0aW5nIERlc2t0b3BDZHBUcmFuc3BvcnQgKGhvc3Q9JHtvcHRpb25zLmhvc3QgPz8gJ2xvY2FsaG9zdCd9LCBwb3J0PSR7U3RyaW5nKG9wdGlvbnMucG9ydCA/PyBDRFBfREVGQVVMVF9QT1JUKX0pYCk7XG4gICAgcmV0dXJuIG5ldyBEZXNrdG9wQ2RwVHJhbnNwb3J0KHtcbiAgICAgIC4uLihvcHRpb25zLmhvc3QgIT09IHVuZGVmaW5lZCAmJiB7IGNkcEhvc3Q6IG9wdGlvbnMuaG9zdCB9KSxcbiAgICAgIC4uLihvcHRpb25zLnBvcnQgIT09IHVuZGVmaW5lZCAmJiB7IGNkcFBvcnQ6IG9wdGlvbnMucG9ydCB9KSxcbiAgICAgIC4uLihvcHRpb25zLmNvbW1hbmRUaW1lb3V0SW5NaWxsaXNlY29uZHMgIT09IHVuZGVmaW5lZCAmJiB7IGNvbW1hbmRUaW1lb3V0SW5NaWxsaXNlY29uZHM6IG9wdGlvbnMuY29tbWFuZFRpbWVvdXRJbk1pbGxpc2Vjb25kcyB9KVxuICAgIH0pO1xuICB9XG5cbiAgbG9nKGBbdHJhbnNwb3J0LWZhY3RvcnldIENyZWF0aW5nIEFwcGl1bVRyYW5zcG9ydCAodXJsPSR7b3B0aW9ucy5hcHBpdW1Vcmx9LCBkZXZpY2U9JHtvcHRpb25zLmRldmljZUlkfSlgKTtcbiAgcmV0dXJuIGNyZWF0ZUFwcGl1bVRyYW5zcG9ydChvcHRpb25zKTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGEgY2FjaGVkIHRyYW5zcG9ydCBpbnN0YW5jZSwgY3JlYXRpbmcgb25lIGZyb20gdGhlIGdpdmVuIG9wdGlvbnNcbiAqIGlmIG5vdCBhbHJlYWR5IGNhY2hlZC5cbiAqXG4gKiBUaGUgdHJhbnNwb3J0IGlzIGNhY2hlZCBwZXIgd29ya2VyIHByb2Nlc3Mgc28gV2ViU29ja2V0L0FwcGl1bSBzZXNzaW9uc1xuICogYXJlIHJldXNlZCBhY3Jvc3MgY2FsbHMgd2l0aGluIHRoZSBzYW1lIHRlc3Qgd29ya2VyLlxuICpcbiAqIEBwYXJhbSBvcHRpb25zIC0gVHJhbnNwb3J0IGNvbmZpZ3VyYXRpb24uIERlZmF1bHRzIHRvIENMSSB0cmFuc3BvcnQuXG4gKiBAcmV0dXJucyBUaGUgY2FjaGVkIG9yIG5ld2x5IGNyZWF0ZWQgdHJhbnNwb3J0LlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0T3JDcmVhdGVUcmFuc3BvcnQob3B0aW9ucz86IE9ic2lkaWFuVHJhbnNwb3J0T3B0aW9ucyk6IFByb21pc2U8T2JzaWRpYW5UcmFuc3BvcnQ+IHtcbiAgaWYgKGNhY2hlZFRyYW5zcG9ydCkge1xuICAgIHJldHVybiBjYWNoZWRUcmFuc3BvcnQ7XG4gIH1cblxuICBjb25zdCByZXN1bHQgPSBhd2FpdCBjcmVhdGVUcmFuc3BvcnRGcm9tT3B0aW9ucyhvcHRpb25zKTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIHJlcXVpcmUtYXRvbWljLXVwZGF0ZXMgLS0gU2luZ2xlLXRocmVhZGVkIHdvcmtlcjsgbm8gY29uY3VycmVudCB3cml0ZXMuXG4gIGNhY2hlZFRyYW5zcG9ydCA9IHJlc3VsdDtcbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLyoqXG4gKiBQZXJmb3JtcyBhIHF1aWNrIEhUVFAgY2hlY2sgYWdhaW5zdCB0aGUgQXBwaXVtIHNlcnZlcidzIHN0YXR1cyBlbmRwb2ludC5cbiAqXG4gKiBGYWlscyBmYXN0ICh3aXRoaW4ge0BsaW5rIEFQUElVTV9QUkVGTElHSFRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFN9KSB3aXRoXG4gKiBhIGNsZWFyIGVycm9yIG1lc3NhZ2UgaW5zdGVhZCBvZiBsZXR0aW5nIFdlYkRyaXZlcklPIHJldHJ5IHNpbGVudGx5IGZvciBtaW51dGVzLlxuICpcbiAqIEBwYXJhbSB1cmwgLSBUaGUgcGFyc2VkIEFwcGl1bSBVUkwuXG4gKi9cbmZ1bmN0aW9uIGNoZWNrQXBwaXVtUmVhY2hhYmxlKHVybDogVVJMKTogUHJvbWlzZTx2b2lkPiB7XG4gIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgY29uc3Qgc3RhdHVzVXJsID0gbmV3IFVSTCgnL3N0YXR1cycsIHVybCk7XG4gICAgY29uc3QgcmVxID0gaHR0cC5nZXQoc3RhdHVzVXJsLCB7IHRpbWVvdXQ6IEFQUElVTV9QUkVGTElHSFRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgfSwgKHJlcykgPT4ge1xuICAgICAgcmVzLnJlc3VtZSgpO1xuICAgICAgcmVzb2x2ZSgpO1xuICAgIH0pO1xuICAgIHJlcS5vbigndGltZW91dCcsICgpID0+IHtcbiAgICAgIHJlcS5kZXN0cm95KCk7XG4gICAgICByZWplY3QoXG4gICAgICAgIG5ldyBFcnJvcihcbiAgICAgICAgICBgQXBwaXVtIHNlcnZlciBhdCAke3VybC5vcmlnaW59IGRpZCBub3QgcmVzcG9uZCB3aXRoaW4gJHtTdHJpbmcoQVBQSVVNX1BSRUZMSUdIVF9USU1FT1VUX0lOX01JTExJU0VDT05EUyl9bXMuIElzIHRoZSBBcHBpdW0gc2VydmVyIHJ1bm5pbmc/YFxuICAgICAgICApXG4gICAgICApO1xuICAgIH0pO1xuICAgIHJlcS5vbignZXJyb3InLCAoZXJyKSA9PiB7XG4gICAgICByZWplY3QoXG4gICAgICAgIG5ldyBFcnJvcihcbiAgICAgICAgICBgQ2Fubm90IHJlYWNoIEFwcGl1bSBzZXJ2ZXIgYXQgJHt1cmwub3JpZ2lufTogJHtlcnIubWVzc2FnZX0uIElzIHRoZSBBcHBpdW0gc2VydmVyIHJ1bm5pbmc/YFxuICAgICAgICApXG4gICAgICApO1xuICAgIH0pO1xuICB9KTtcbn1cblxuLyoqXG4gKiBDaGVja3Mgd2hldGhlciB0aGUgc3BlY2lmaWVkIGRldmljZSBpcyBjb25uZWN0ZWQgdmlhIEFEQi5cbiAqXG4gKiBAcGFyYW0gZGV2aWNlSWQgLSBUaGUgZGV2aWNlIFVESUQgdG8gY2hlY2sgKGUuZy4gYCdlbXVsYXRvci01NTU0J2ApLlxuICogQHJldHVybnMgYHRydWVgIGlmIHRoZSBkZXZpY2UgaXMgY29ubmVjdGVkIGFuZCBpbiB0aGUgYGRldmljZWAgc3RhdGUuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGNoZWNrRGV2aWNlQ29ubmVjdGVkKGRldmljZUlkOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgY29uc3Qgb3V0cHV0ID0gYXdhaXQgbmV3IFByb21pc2U8c3RyaW5nPigocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgZXhlY0ZpbGUoJ2FkYicsIFsnZGV2aWNlcyddLCB7IHRpbWVvdXQ6IEFEQl9ERVZJQ0VfQ0hFQ0tfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgfSwgKGVycm9yLCBzdGRvdXQsIHN0ZGVycikgPT4ge1xuICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgIHJlamVjdChuZXcgRXJyb3IoYEZhaWxlZCB0byBydW4gJ2FkYiBkZXZpY2VzJzogJHtlcnJvci5tZXNzYWdlfS4gSXMgQURCIGluc3RhbGxlZCBhbmQgaW4gUEFUSD9gKSk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGlmIChzdGRlcnIpIHtcbiAgICAgICAgbG9nKGBbdHJhbnNwb3J0LWZhY3RvcnldIEFEQiBzdGRlcnI6ICR7c3RkZXJyLnRyaW0oKX1gKTtcbiAgICAgIH1cbiAgICAgIHJlc29sdmUoc3Rkb3V0KTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgY29uc3QgbGluZXMgPSBvdXRwdXQuc3BsaXQoJ1xcbicpLm1hcCgobGluZSkgPT4gbGluZS50cmltKCkpLmZpbHRlcigobGluZSkgPT4gbGluZS5sZW5ndGggPiAwKTtcbiAgY29uc3QgZGV2aWNlTGluZSA9IGxpbmVzLmZpbmQoKGxpbmUpID0+IGxpbmUuc3RhcnRzV2l0aChkZXZpY2VJZCkpO1xuICByZXR1cm4gZGV2aWNlTGluZT8uaW5jbHVkZXMoJ1xcdGRldmljZScpID8/IGZhbHNlO1xufVxuXG4vKipcbiAqIENyZWF0ZXMgYW4gQXBwaXVtIHRyYW5zcG9ydCBieSBlc3RhYmxpc2hpbmcgYSBXZWJEcml2ZXJJTyBzZXNzaW9uLlxuICpcbiAqIEBwYXJhbSBvcHRpb25zIC0gQW5kcm9pZCBBcHBpdW0gdHJhbnNwb3J0IG9wdGlvbnMuXG4gKiBAcmV0dXJucyBBIGNvbmZpZ3VyZWQgQXBwaXVtIHRyYW5zcG9ydC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY3JlYXRlQXBwaXVtVHJhbnNwb3J0KG9wdGlvbnM6IE9ic2lkaWFuQW5kcm9pZEFwcGl1bVRyYW5zcG9ydE9wdGlvbnMpOiBQcm9taXNlPE9ic2lkaWFuVHJhbnNwb3J0PiB7XG4gIGNvbnN0IHVybCA9IG5ldyBVUkwob3B0aW9ucy5hcHBpdW1VcmwpO1xuXG4gIGNvbnN0IHBvcnQgPSBOdW1iZXIodXJsLnBvcnQpO1xuICBpZiAoaXNOYU4ocG9ydCkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYEludmFsaWQgcG9ydCBpbiBhcHBpdW1Vcmw6ICR7dXJsLnBvcnR9YCk7XG4gIH1cblxuICBjb25zdCBhcHBJZCA9IG9wdGlvbnMuYXBwSWQgPz8gQVBQX1BBQ0tBR0U7XG5cbiAgbGV0IGFwcGl1bVByb2Nlc3M6IENoaWxkUHJvY2VzcyB8IHVuZGVmaW5lZDtcbiAgbGV0IGVtdWxhdG9yUHJvY2VzczogQ2hpbGRQcm9jZXNzIHwgdW5kZWZpbmVkO1xuXG4gIHRyeSB7XG4gICAgW2FwcGl1bVByb2Nlc3MsIGVtdWxhdG9yUHJvY2Vzc10gPSBhd2FpdCBzdGFydEFwcGl1bUFuZEVtdWxhdG9yKHtcbiAgICAgIGFwcGl1bVVybDogdXJsLFxuICAgICAgYXZkTmFtZTogb3B0aW9ucy5hdmROYW1lLFxuICAgICAgZGV2aWNlSWQ6IG9wdGlvbnMuZGV2aWNlSWQsXG4gICAgICBwb3J0LFxuICAgICAgc2hvdWxkQXV0b1N0YXJ0QXBwaXVtOiBvcHRpb25zLnNob3VsZEF1dG9TdGFydEFwcGl1bVxuICAgIH0pO1xuXG4gICAgbG9nKFxuICAgICAgYFt0cmFuc3BvcnQtZmFjdG9yeV0gQ29ubmVjdGluZyB0byBBcHBpdW0gKGRldmljZT0ke29wdGlvbnMuZGV2aWNlSWR9LCBhcHA9JHthcHBJZH0sIHJldHJ5VGltZW91dDogJHtcbiAgICAgICAgU3RyaW5nKEFQUElVTV9DT05ORUNUSU9OX1JFVFJZX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTKVxuICAgICAgfW1zLCByZXRyaWVzOiAke1N0cmluZyhBUFBJVU1fQ09OTkVDVElPTl9SRVRSWV9DT1VOVCl9KS4uLmBcbiAgICApO1xuICAgIGNvbnN0IGJyb3dzZXIgPSBhd2FpdCByZW1vdGUoe1xuICAgICAgY2FwYWJpbGl0aWVzOiB7XG4gICAgICAgICdhcHBpdW06YXBwQWN0aXZpdHknOiBBUFBfQUNUSVZJVFksXG4gICAgICAgICdhcHBpdW06YXBwUGFja2FnZSc6IGFwcElkLFxuICAgICAgICAnYXBwaXVtOmF1dG9HcmFudFBlcm1pc3Npb25zJzogdHJ1ZSxcbiAgICAgICAgJ2FwcGl1bTphdXRvbWF0aW9uTmFtZSc6ICdVaUF1dG9tYXRvcjInLFxuICAgICAgICAnYXBwaXVtOm5ld0NvbW1hbmRUaW1lb3V0JzogQ09NTUFORF9USU1FT1VUX0lOX01JTExJU0VDT05EUyxcbiAgICAgICAgJ2FwcGl1bTpub1Jlc2V0JzogdHJ1ZSxcbiAgICAgICAgJ2FwcGl1bTpzZXR0aW5ncyc6IHtcbiAgICAgICAgICAnYXBwaXVtOmNocm9tZWRyaXZlckF1dG9kb3dubG9hZCc6IHRydWVcbiAgICAgICAgfSxcbiAgICAgICAgJ2FwcGl1bTpza2lwU2VydmVySW5zdGFsbGF0aW9uJzogdHJ1ZSxcbiAgICAgICAgJ2FwcGl1bTp1ZGlkJzogb3B0aW9ucy5kZXZpY2VJZCxcbiAgICAgICAgJ2FwcGl1bTp1aWF1dG9tYXRvcjJTZXJ2ZXJMYXVuY2hUaW1lb3V0JzogU0VSVkVSX0xBVU5DSF9USU1FT1VUX0lOX01JTExJU0VDT05EUyxcbiAgICAgICAgJ3BsYXRmb3JtTmFtZSc6ICdBbmRyb2lkJ1xuICAgICAgfSxcbiAgICAgIGNvbm5lY3Rpb25SZXRyeUNvdW50OiBBUFBJVU1fQ09OTkVDVElPTl9SRVRSWV9DT1VOVCxcbiAgICAgIGNvbm5lY3Rpb25SZXRyeVRpbWVvdXQ6IEFQUElVTV9DT05ORUNUSU9OX1JFVFJZX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTLFxuICAgICAgaG9zdG5hbWU6IHVybC5ob3N0bmFtZSxcbiAgICAgIGxvZ0xldmVsOiAnd2FybicsXG4gICAgICBwYXRoOiB1cmwucGF0aG5hbWUsXG4gICAgICBwb3J0XG4gICAgfSk7XG5cbiAgICBsb2coJ1t0cmFuc3BvcnQtZmFjdG9yeV0gQXBwaXVtIHNlc3Npb24gZXN0YWJsaXNoZWQuJyk7XG4gICAgY29uc3QgYXBwaXVtVHJhbnNwb3J0ID0gbmV3IEFwcGl1bVRyYW5zcG9ydCh7XG4gICAgICBhcHBJZCxcbiAgICAgIGJyb3dzZXIsXG4gICAgICBwbGF0Zm9ybTogJ2FuZHJvaWQnLFxuICAgICAgLi4uKG9wdGlvbnMudmF1bHRCYXNlUGF0aCAhPT0gdW5kZWZpbmVkICYmIHsgdmF1bHRCYXNlUGF0aDogb3B0aW9ucy52YXVsdEJhc2VQYXRoIH0pXG4gICAgfSk7XG5cbiAgICBjb25zdCBvcmlnaW5hbERpc3Bvc2UgPSBhcHBpdW1UcmFuc3BvcnQuZGlzcG9zZS5iaW5kKGFwcGl1bVRyYW5zcG9ydCk7XG4gICAgY29uc3QgdHJhbnNwb3J0OiBPYnNpZGlhblRyYW5zcG9ydCA9IGFwcGl1bVRyYW5zcG9ydDtcbiAgICB0cmFuc3BvcnQuZGlzcG9zZSA9IGFzeW5jICgpOiBQcm9taXNlPHZvaWQ+ID0+IHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IG9yaWdpbmFsRGlzcG9zZSgpO1xuICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAga2lsbEF1dG9TdGFydGVkUHJvY2Vzc2VzKCk7XG4gICAgICB9XG4gICAgfTtcblxuICAgIHRyYW5zcG9ydC5kaXNwb3NlU3luYyA9ICgpOiB2b2lkID0+IHtcbiAgICAgIGtpbGxBdXRvU3RhcnRlZFByb2Nlc3NlcygpO1xuICAgIH07XG5cbiAgICByZXR1cm4gdHJhbnNwb3J0O1xuXG4gICAgZnVuY3Rpb24ga2lsbEF1dG9TdGFydGVkUHJvY2Vzc2VzKCk6IHZvaWQge1xuICAgICAgaWYgKGFwcGl1bVByb2Nlc3MpIHtcbiAgICAgICAgYXBwaXVtUHJvY2Vzcy5raWxsKCk7XG4gICAgICAgIGxvZygnW3RyYW5zcG9ydC1mYWN0b3J5XSBBdXRvLXN0YXJ0ZWQgQXBwaXVtIHNlcnZlciBzdG9wcGVkLicpO1xuICAgICAgfVxuICAgICAgaWYgKGVtdWxhdG9yUHJvY2Vzcykge1xuICAgICAgICBlbXVsYXRvclByb2Nlc3Mua2lsbCgpO1xuICAgICAgICBsb2coJ1t0cmFuc3BvcnQtZmFjdG9yeV0gQXV0by1zdGFydGVkIGVtdWxhdG9yIHN0b3BwZWQuJyk7XG4gICAgICB9XG4gICAgfVxuICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgIGlmIChhcHBpdW1Qcm9jZXNzKSB7XG4gICAgICBhcHBpdW1Qcm9jZXNzLmtpbGwoKTtcbiAgICAgIGxvZygnW3RyYW5zcG9ydC1mYWN0b3J5XSBLaWxsZWQgYXV0by1zdGFydGVkIEFwcGl1bSBzZXJ2ZXIgYWZ0ZXIgY29ubmVjdGlvbiBmYWlsdXJlLicpO1xuICAgIH1cbiAgICBpZiAoZW11bGF0b3JQcm9jZXNzKSB7XG4gICAgICBlbXVsYXRvclByb2Nlc3Mua2lsbCgpO1xuICAgICAgbG9nKCdbdHJhbnNwb3J0LWZhY3RvcnldIEtpbGxlZCBhdXRvLXN0YXJ0ZWQgZW11bGF0b3IgYWZ0ZXIgY29ubmVjdGlvbiBmYWlsdXJlLicpO1xuICAgIH1cbiAgICB0aHJvdyBlcnJvcjtcbiAgfVxufVxuXG4vKipcbiAqIEVuc3VyZXMgdGhlIHNwZWNpZmllZCBkZXZpY2UgaXMgY29ubmVjdGVkLCBvcHRpb25hbGx5IGF1dG8tc3RhcnRpbmcgYW5cbiAqIGVtdWxhdG9yIGlmIGFuIEFWRCBuYW1lIGlzIHByb3ZpZGVkLlxuICpcbiAqIEBwYXJhbSBwYXJhbXMgLSBEZXZpY2UgYW5kIG9wdGlvbmFsIEFWRCBjb25maWd1cmF0aW9uLlxuICogQHBhcmFtIHBhcmFtcy5hdmROYW1lIC0gQVZEIG5hbWUgdG8gYXV0by1zdGFydCBpZiB0aGUgZGV2aWNlIGlzIG5vdCBjb25uZWN0ZWQuXG4gKiBAcGFyYW0gcGFyYW1zLmRldmljZUlkIC0gRGV2aWNlIFVESUQgdG8gY2hlY2suXG4gKiBAcmV0dXJucyBUaGUgZW11bGF0b3IgY2hpbGQgcHJvY2VzcyBpZiBvbmUgd2FzIGF1dG8tc3RhcnRlZCwgb3IgYHVuZGVmaW5lZGAuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGVuc3VyZURldmljZUNvbm5lY3RlZChwYXJhbXM6IEVuc3VyZURldmljZUNvbm5lY3RlZFBhcmFtcyk6IFByb21pc2U8Q2hpbGRQcm9jZXNzIHwgdW5kZWZpbmVkPiB7XG4gIGNvbnN0IHsgYXZkTmFtZSwgZGV2aWNlSWQgfSA9IHBhcmFtcztcbiAgbG9nKGBbdHJhbnNwb3J0LWZhY3RvcnldIENoZWNraW5nIGRldmljZSAke2RldmljZUlkfSBpcyBjb25uZWN0ZWQgdmlhIEFEQi4uLmApO1xuXG4gIGlmIChhd2FpdCBjaGVja0RldmljZUNvbm5lY3RlZChkZXZpY2VJZCkpIHtcbiAgICBsb2coYFt0cmFuc3BvcnQtZmFjdG9yeV0gRGV2aWNlICR7ZGV2aWNlSWR9IGlzIGNvbm5lY3RlZC5gKTtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG5cbiAgaWYgKCFhdmROYW1lKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYERldmljZSBcIiR7ZGV2aWNlSWR9XCIgaXMgbm90IGNvbm5lY3RlZC4gU3RhcnQgdGhlIGVtdWxhdG9yIGJlZm9yZSBydW5uaW5nIGludGVncmF0aW9uIHRlc3RzLGBcbiAgICAgICAgKyAnIG9yIHNldCBhdmROYW1lIGluIHRyYW5zcG9ydCBvcHRpb25zIHRvIGF1dG8tc3RhcnQgaXQuJ1xuICAgICk7XG4gIH1cblxuICBsb2coYFt0cmFuc3BvcnQtZmFjdG9yeV0gRGV2aWNlICR7ZGV2aWNlSWR9IG5vdCBmb3VuZCwgc3RhcnRpbmcgZW11bGF0b3IgQVZEIFwiJHthdmROYW1lfVwiLi4uYCk7XG4gIGNvbnN0IGVtdWxhdG9yUHJvY2VzcyA9IHN0YXJ0RW11bGF0b3IoYXZkTmFtZSk7XG4gIGF3YWl0IHdhaXRGb3JEZXZpY2UoZGV2aWNlSWQpO1xuICBsb2coYFt0cmFuc3BvcnQtZmFjdG9yeV0gRW11bGF0b3IgXCIke2F2ZE5hbWV9XCIgc3RhcnRlZCwgZGV2aWNlICR7ZGV2aWNlSWR9IGlzIGNvbm5lY3RlZC5gKTtcbiAgcmV0dXJuIGVtdWxhdG9yUHJvY2Vzcztcbn1cblxuLyoqXG4gKiBSZXNvbHZlcyB0aGUgcGF0aCB0byB0aGUgQW5kcm9pZCBlbXVsYXRvciBiaW5hcnkuXG4gKlxuICogVXNlcyBgQU5EUk9JRF9IT01FYCBvciBgQU5EUk9JRF9TREtfUk9PVGAgZW52aXJvbm1lbnQgdmFyaWFibGUgaWYgc2V0LFxuICogb3RoZXJ3aXNlIGZhbGxzIGJhY2sgdG8gYGVtdWxhdG9yYCAoYXNzdW1lcyBpdCdzIGluIFBBVEgpLlxuICpcbiAqIEByZXR1cm5zIFRoZSBwYXRoIHRvIHRoZSBlbXVsYXRvciBiaW5hcnkuXG4gKi9cbmZ1bmN0aW9uIHJlc29sdmVFbXVsYXRvckJpbmFyeSgpOiBzdHJpbmcge1xuICBjb25zdCBzZGtSb290ID0gcHJvY2Vzcy5lbnZbJ0FORFJPSURfSE9NRSddID8/IHByb2Nlc3MuZW52WydBTkRST0lEX1NES19ST09UJ107XG4gIGlmICghc2RrUm9vdCkge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICdDYW5ub3QgZmluZCBBbmRyb2lkIGVtdWxhdG9yOiBuZWl0aGVyIEFORFJPSURfSE9NRSBub3IgQU5EUk9JRF9TREtfUk9PVCBlbnZpcm9ubWVudCB2YXJpYWJsZSBpcyBzZXQuJ1xuICAgICk7XG4gIH1cbiAgcmV0dXJuIGpvaW4oc2RrUm9vdCwgJ2VtdWxhdG9yJywgJ2VtdWxhdG9yJyk7XG59XG5cbi8qKlxuICogU2VuZHMgYSBrZXkgZXZlbnQgdG8gYSBkZXZpY2UgdmlhIEFEQi4gRmFpbHVyZXMgYXJlIGxvZ2dlZCBhcyB3YXJuaW5nc1xuICogaW5zdGVhZCBvZiB0aHJvd2luZywgc2luY2Uga2V5IGV2ZW50cyBhcmUgYmVzdC1lZmZvcnQgKGUuZy4sIHRoZSBzY3JlZW5cbiAqIG1heSBhbHJlYWR5IGJlIG9uKS5cbiAqXG4gKiBAcGFyYW0gZGV2aWNlSWQgLSBUaGUgZGV2aWNlIFVESUQuXG4gKiBAcGFyYW0ga2V5Q29kZSAtIFRoZSBBbmRyb2lkIGtleWNvZGUgdG8gc2VuZC5cbiAqIEBwYXJhbSBkZXNjcmlwdGlvbiAtIEh1bWFuLXJlYWRhYmxlIGRlc2NyaXB0aW9uIGZvciBsb2cgbWVzc2FnZXMuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHNlbmRLZXlFdmVudChkZXZpY2VJZDogc3RyaW5nLCBrZXlDb2RlOiBudW1iZXIsIGRlc2NyaXB0aW9uOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHtcbiAgICBleGVjRmlsZShcbiAgICAgICdhZGInLFxuICAgICAgWyctcycsIGRldmljZUlkLCAnc2hlbGwnLCAnaW5wdXQnLCAna2V5ZXZlbnQnLCBTdHJpbmcoa2V5Q29kZSldLFxuICAgICAgeyB0aW1lb3V0OiBBREJfREVWSUNFX0NIRUNLX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTIH0sXG4gICAgICAoZXJyb3IpID0+IHtcbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgbG9nKFxuICAgICAgICAgICAgYFt0cmFuc3BvcnQtZmFjdG9yeV0gV2FybmluZzogZmFpbGVkIHRvICR7ZGVzY3JpcHRpb259IChrZXlldmVudCAke1N0cmluZyhrZXlDb2RlKX0pOiAke2Vycm9yIGluc3RhbmNlb2YgRXJyb3IgPyBlcnJvci5tZXNzYWdlIDogJ3Vua25vd24gZXJyb3InfWBcbiAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgfVxuICAgICk7XG4gIH0pO1xufVxuXG4vKipcbiAqIFN0YXJ0cyB0aGUgQXBwaXVtIHNlcnZlciBhbmQgZW11bGF0b3IgaW4gcGFyYWxsZWwgd2hlbiBib3RoIG5lZWQgYXV0by1zdGFydGluZy5cbiAqXG4gKiBAcGFyYW0gcGFyYW1zIC0gQ29uZmlndXJhdGlvbiBmb3IgQXBwaXVtIGFuZCBlbXVsYXRvciBzdGFydHVwLlxuICogQHJldHVybnMgQSB0dXBsZSBvZiBgW2FwcGl1bVByb2Nlc3MsIGVtdWxhdG9yUHJvY2Vzc11gLCBlaXRoZXIgb2Ygd2hpY2ggbWF5IGJlIGB1bmRlZmluZWRgLlxuICovXG5hc3luYyBmdW5jdGlvbiBzdGFydEFwcGl1bUFuZEVtdWxhdG9yKHBhcmFtczogU3RhcnRBcHBpdW1BbmRFbXVsYXRvclBhcmFtcyk6IFByb21pc2U8W0NoaWxkUHJvY2VzcyB8IHVuZGVmaW5lZCwgQ2hpbGRQcm9jZXNzIHwgdW5kZWZpbmVkXT4ge1xuICBjb25zdCB7IGFwcGl1bVVybCwgYXZkTmFtZSwgZGV2aWNlSWQsIHBvcnQsIHNob3VsZEF1dG9TdGFydEFwcGl1bSB9ID0gcGFyYW1zO1xuXG4gIGxldCBuZWVkc0FwcGl1bVN0YXJ0ID0gZmFsc2U7XG5cbiAgbG9nKGBbdHJhbnNwb3J0LWZhY3RvcnldIENoZWNraW5nIEFwcGl1bSBzZXJ2ZXIgYXQgJHthcHBpdW1VcmwuaHJlZn0uLi5gKTtcbiAgdHJ5IHtcbiAgICBhd2FpdCBjaGVja0FwcGl1bVJlYWNoYWJsZShhcHBpdW1VcmwpO1xuICAgIGxvZygnW3RyYW5zcG9ydC1mYWN0b3J5XSBBcHBpdW0gc2VydmVyIGlzIHJlYWNoYWJsZS4nKTtcbiAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICBpZiAoc2hvdWxkQXV0b1N0YXJ0QXBwaXVtID09PSBmYWxzZSkge1xuICAgICAgdGhyb3cgZXJyb3I7XG4gICAgfVxuICAgIG5lZWRzQXBwaXVtU3RhcnQgPSB0cnVlO1xuICB9XG5cbiAgbGV0IGFwcGl1bVByb2Nlc3M6IENoaWxkUHJvY2VzcyB8IHVuZGVmaW5lZDtcblxuICBpZiAobmVlZHNBcHBpdW1TdGFydCkge1xuICAgIGxvZyhgW3RyYW5zcG9ydC1mYWN0b3J5XSBBcHBpdW0gbm90IHJlYWNoYWJsZSwgYXV0by1zdGFydGluZyBvbiBwb3J0ICR7U3RyaW5nKHBvcnQpfS4uLmApO1xuICAgIGFwcGl1bVByb2Nlc3MgPSBzdGFydEFwcGl1bVNlcnZlcihwb3J0KTtcbiAgfVxuXG4gIGNvbnN0IFssIGVtdWxhdG9yUHJvY2Vzc10gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgbmVlZHNBcHBpdW1TdGFydFxuICAgICAgPyB3YWl0Rm9yQXBwaXVtUmVhZHkoYXBwaXVtVXJsKS50aGVuKCgpID0+IHtcbiAgICAgICAgbG9nKCdbdHJhbnNwb3J0LWZhY3RvcnldIEF1dG8tc3RhcnRlZCBBcHBpdW0gc2VydmVyIGlzIHJlYWR5LicpO1xuICAgICAgfSlcbiAgICAgIDogUHJvbWlzZS5yZXNvbHZlKCksXG4gICAgZW5zdXJlRGV2aWNlQ29ubmVjdGVkKHsgYXZkTmFtZSwgZGV2aWNlSWQgfSlcbiAgXSk7XG5cbiAgcmV0dXJuIFthcHBpdW1Qcm9jZXNzLCBlbXVsYXRvclByb2Nlc3NdO1xufVxuXG4vKipcbiAqIFNwYXducyBhbiBBcHBpdW0gc2VydmVyIGFzIGEgZGV0YWNoZWQgYmFja2dyb3VuZCBwcm9jZXNzLlxuICpcbiAqIEBwYXJhbSBwb3J0IC0gVGhlIHBvcnQgdG8gc3RhcnQgQXBwaXVtIG9uLlxuICogQHJldHVybnMgVGhlIHNwYXduZWQgY2hpbGQgcHJvY2Vzcy5cbiAqL1xuZnVuY3Rpb24gc3RhcnRBcHBpdW1TZXJ2ZXIocG9ydDogbnVtYmVyKTogQ2hpbGRQcm9jZXNzIHtcbiAgY29uc3QgY2hpbGQgPSBzcGF3bihgbnB4IGFwcGl1bSAtLWxvZy10aW1lc3RhbXAgLS1wb3J0ICR7U3RyaW5nKHBvcnQpfWAsIHtcbiAgICBkZXRhY2hlZDogdHJ1ZSxcbiAgICBzaGVsbDogdHJ1ZSxcbiAgICBzdGRpbzogWydpZ25vcmUnLCAnaW5oZXJpdCcsICdpbmhlcml0J11cbiAgfSk7XG5cbiAgY2hpbGQudW5yZWYoKTtcbiAgcmV0dXJuIGNoaWxkO1xufVxuXG4vKipcbiAqIFNwYXducyBhbiBBbmRyb2lkIGVtdWxhdG9yIGFzIGEgYmFja2dyb3VuZCBwcm9jZXNzLlxuICpcbiAqIEBwYXJhbSBhdmROYW1lIC0gVGhlIEFWRCBuYW1lIHRvIHN0YXJ0LlxuICogQHJldHVybnMgVGhlIHNwYXduZWQgY2hpbGQgcHJvY2Vzcy5cbiAqL1xuZnVuY3Rpb24gc3RhcnRFbXVsYXRvcihhdmROYW1lOiBzdHJpbmcpOiBDaGlsZFByb2Nlc3Mge1xuICBjb25zdCBlbXVsYXRvckJpbmFyeSA9IHJlc29sdmVFbXVsYXRvckJpbmFyeSgpO1xuICBsb2coYFt0cmFuc3BvcnQtZmFjdG9yeV0gUnVubmluZzogJHtlbXVsYXRvckJpbmFyeX0gLWF2ZCAke2F2ZE5hbWV9IC1uby1zbmFwc2hvdC1zYXZlYCk7XG4gIGNvbnN0IGNoaWxkID0gc3Bhd24oZW11bGF0b3JCaW5hcnksIFsnLWF2ZCcsIGF2ZE5hbWUsICctbm8tc25hcHNob3Qtc2F2ZSddLCB7XG4gICAgZGV0YWNoZWQ6IHRydWUsXG4gICAgc3RkaW86IFsnaWdub3JlJywgJ2lnbm9yZScsICdpZ25vcmUnXVxuICB9KTtcblxuICBjaGlsZC51bnJlZigpO1xuICByZXR1cm4gY2hpbGQ7XG59XG5cbi8qKlxuICogUG9sbHMgdGhlIEFwcGl1bSBzZXJ2ZXIgdW50aWwgaXQgcmVzcG9uZHMgdG8gYC9zdGF0dXNgLlxuICpcbiAqIEBwYXJhbSB1cmwgLSBUaGUgQXBwaXVtIHNlcnZlciBVUkwuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHdhaXRGb3JBcHBpdW1SZWFkeSh1cmw6IFVSTCk6IFByb21pc2U8dm9pZD4ge1xuICBsb2coXG4gICAgYFt0cmFuc3BvcnQtZmFjdG9yeV0gV2FpdGluZyBmb3IgQXBwaXVtIGF0ICR7dXJsLmhyZWZ9ICh0aW1lb3V0OiAke1N0cmluZyhBUFBJVU1fU1RBUlRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMpfW1zLCBwb2xsOiAke1xuICAgICAgU3RyaW5nKEFQUElVTV9TVEFSVF9QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUylcbiAgICB9bXMpLi4uYFxuICApO1xuICBjb25zdCBkZWFkbGluZSA9IERhdGUubm93KCkgKyBBUFBJVU1fU1RBUlRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFM7XG5cbiAgd2hpbGUgKERhdGUubm93KCkgPCBkZWFkbGluZSkge1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCBjaGVja0FwcGl1bVJlYWNoYWJsZSh1cmwpO1xuICAgICAgcmV0dXJuO1xuICAgIH0gY2F0Y2gge1xuICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgc2V0VGltZW91dChyZXNvbHZlLCBBUFBJVU1fU1RBUlRfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMpO1xuICAgICAgfSk7XG4gICAgfVxuICB9XG5cbiAgdGhyb3cgbmV3IEVycm9yKFxuICAgIGBBdXRvLXN0YXJ0ZWQgQXBwaXVtIHNlcnZlciBkaWQgbm90IGJlY29tZSByZWFkeSB3aXRoaW4gJHtTdHJpbmcoQVBQSVVNX1NUQVJUX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTKX1tc2BcbiAgKTtcbn1cblxuLyoqXG4gKiBXYWl0cyBmb3IgdGhlIGRldmljZSB0byBmaW5pc2ggYm9vdGluZyBieSBwb2xsaW5nIGBzeXMuYm9vdF9jb21wbGV0ZWRgLlxuICpcbiAqIEBwYXJhbSBkZXZpY2VJZCAtIFRoZSBkZXZpY2UgVURJRC5cbiAqIEBwYXJhbSBkZWFkbGluZSAtIEFic29sdXRlIHRpbWVzdGFtcCBkZWFkbGluZS5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gd2FpdEZvckJvb3QoZGV2aWNlSWQ6IHN0cmluZywgZGVhZGxpbmU6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICBjb25zdCByZW1haW5pbmdNcyA9IE1hdGgubWF4KDAsIGRlYWRsaW5lIC0gRGF0ZS5ub3coKSk7XG4gIGxvZyhcbiAgICBgW3RyYW5zcG9ydC1mYWN0b3J5XSBXYWl0aW5nIGZvciBkZXZpY2UgJHtkZXZpY2VJZH0gdG8gZmluaXNoIGJvb3RpbmcgKHJlbWFpbmluZzogJHtTdHJpbmcocmVtYWluaW5nTXMpfW1zLCBwb2xsOiAke1xuICAgICAgU3RyaW5nKEVNVUxBVE9SX0JPT1RfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMpXG4gICAgfW1zKS4uLmBcbiAgKTtcblxuICB3aGlsZSAoRGF0ZS5ub3coKSA8IGRlYWRsaW5lKSB7XG4gICAgY29uc3QgaXNCb290ZWQgPSBhd2FpdCBuZXcgUHJvbWlzZTxib29sZWFuPigocmVzb2x2ZSkgPT4ge1xuICAgICAgZXhlY0ZpbGUoXG4gICAgICAgICdhZGInLFxuICAgICAgICBbJy1zJywgZGV2aWNlSWQsICdzaGVsbCcsICdnZXRwcm9wJywgJ3N5cy5ib290X2NvbXBsZXRlZCddLFxuICAgICAgICB7IHRpbWVvdXQ6IEFEQl9ERVZJQ0VfQ0hFQ0tfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgfSxcbiAgICAgICAgKF9lcnJvciwgc3Rkb3V0KSA9PiB7XG4gICAgICAgICAgcmVzb2x2ZShzdGRvdXQudHJpbSgpID09PSAnMScpO1xuICAgICAgICB9XG4gICAgICApO1xuICAgIH0pO1xuXG4gICAgaWYgKGlzQm9vdGVkKSB7XG4gICAgICBsb2coYFt0cmFuc3BvcnQtZmFjdG9yeV0gRGV2aWNlICR7ZGV2aWNlSWR9IGJvb3QgY29tcGxldGVkLmApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICBzZXRUaW1lb3V0KHJlc29sdmUsIEVNVUxBVE9SX0JPT1RfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMpO1xuICAgIH0pO1xuICB9XG5cbiAgdGhyb3cgbmV3IEVycm9yKFxuICAgIGBEZXZpY2UgXCIke2RldmljZUlkfVwiIGNvbm5lY3RlZCBidXQgZGlkIG5vdCBmaW5pc2ggYm9vdGluZyB3aXRoaW4gJHtTdHJpbmcoRU1VTEFUT1JfQk9PVF9USU1FT1VUX0lOX01JTExJU0VDT05EUyl9bXMuYFxuICApO1xufVxuXG4vKipcbiAqIFBvbGxzIGBhZGIgZGV2aWNlc2AgdW50aWwgdGhlIHNwZWNpZmllZCBkZXZpY2UgYXBwZWFycyBpbiB0aGUgYGRldmljZWAgc3RhdGUuXG4gKlxuICogQWxzbyB3YWl0cyBmb3IgdGhlIGRldmljZSB0byBmaW5pc2ggYm9vdGluZyBieSBjaGVja2luZyBgc3lzLmJvb3RfY29tcGxldGVkYC5cbiAqXG4gKiBAcGFyYW0gZGV2aWNlSWQgLSBUaGUgZGV2aWNlIFVESUQgdG8gd2FpdCBmb3IuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHdhaXRGb3JEZXZpY2UoZGV2aWNlSWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBsb2coXG4gICAgYFt0cmFuc3BvcnQtZmFjdG9yeV0gV2FpdGluZyBmb3IgZGV2aWNlICR7ZGV2aWNlSWR9IHRvIGFwcGVhciBpbiBBREIgKHRpbWVvdXQ6ICR7U3RyaW5nKEVNVUxBVE9SX0JPT1RfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMpfW1zLCBwb2xsOiAke1xuICAgICAgU3RyaW5nKEVNVUxBVE9SX0JPT1RfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMpXG4gICAgfW1zKS4uLmBcbiAgKTtcbiAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgRU1VTEFUT1JfQk9PVF9USU1FT1VUX0lOX01JTExJU0VDT05EUztcblxuICB3aGlsZSAoRGF0ZS5ub3coKSA8IGRlYWRsaW5lKSB7XG4gICAgaWYgKGF3YWl0IGNoZWNrRGV2aWNlQ29ubmVjdGVkKGRldmljZUlkKSkge1xuICAgICAgbG9nKGBbdHJhbnNwb3J0LWZhY3RvcnldIERldmljZSAke2RldmljZUlkfSBhcHBlYXJlZCBpbiBBREIsIHdhaXRpbmcgZm9yIGJvb3QgdG8gY29tcGxldGUuLi5gKTtcbiAgICAgIGF3YWl0IHdhaXRGb3JCb290KGRldmljZUlkLCBkZWFkbGluZSk7XG4gICAgICBhd2FpdCB3YWtlU2NyZWVuKGRldmljZUlkKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgc2V0VGltZW91dChyZXNvbHZlLCBFTVVMQVRPUl9CT09UX1BPTExfSU5URVJWQUxfSU5fTUlMTElTRUNPTkRTKTtcbiAgICB9KTtcbiAgfVxuXG4gIHRocm93IG5ldyBFcnJvcihcbiAgICBgRW11bGF0b3IgZGV2aWNlIFwiJHtkZXZpY2VJZH1cIiBkaWQgbm90IGFwcGVhciB3aXRoaW4gJHtTdHJpbmcoRU1VTEFUT1JfQk9PVF9USU1FT1VUX0lOX01JTExJU0VDT05EUyl9bXMuYFxuICApO1xufVxuXG4vKipcbiAqIFdha2VzIHRoZSBlbXVsYXRvciBzY3JlZW4gYW5kIGRpc21pc3NlcyB0aGUgbG9jayBzY3JlZW4uXG4gKlxuICogQWZ0ZXIgYm9vdCwgdGhlIGVtdWxhdG9yIHNjcmVlbiBtYXkgYmUgb2ZmLiBTZW5kcyBgS0VZQ09ERV9XQUtFVVBgIHRvIHR1cm5cbiAqIG9uIHRoZSBkaXNwbGF5IGFuZCBgS0VZQ09ERV9NRU5VYCB0byBkaXNtaXNzIHRoZSBsb2NrIHNjcmVlbi5cbiAqXG4gKiBAcGFyYW0gZGV2aWNlSWQgLSBUaGUgZGV2aWNlIFVESUQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHdha2VTY3JlZW4oZGV2aWNlSWQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICBsb2coYFt0cmFuc3BvcnQtZmFjdG9yeV0gV2FraW5nIHNjcmVlbiBvbiBkZXZpY2UgJHtkZXZpY2VJZH0uLi5gKTtcblxuICBhd2FpdCBzZW5kS2V5RXZlbnQoZGV2aWNlSWQsIEtFWUNPREVfV0FLRVVQLCAnd2FrZSBzY3JlZW4nKTtcbiAgYXdhaXQgc2VuZEtleUV2ZW50KGRldmljZUlkLCBLRVlDT0RFX01FTlUsICdkaXNtaXNzIGxvY2sgc2NyZWVuJyk7XG5cbiAgbG9nKGBbdHJhbnNwb3J0LWZhY3RvcnldIFNjcmVlbiB3YWtlIGNvbXBsZXRlIG9uIGRldmljZSAke2RldmljZUlkfS5gKTtcbn1cblxuLyogdjggaWdub3JlIHN0b3AgKi9cbiJdLAogICJtYXBwaW5ncyI6ICI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFVQSxnQ0FHTztBQUNQLHVCQUFpQjtBQUNqQix1QkFBcUI7QUFDckIsMEJBQW9CO0FBQ3BCLHlCQUF1QjtBQVF2QixpQkFBb0I7QUFDcEIsOEJBQWdDO0FBQ2hDLG1DQUFvQztBQUNwQyxtQ0FBb0M7QUFFcEMsTUFBTSxjQUFjO0FBQ3BCLE1BQU0sZUFBZSxHQUFHLFdBQVc7QUFDbkMsTUFBTSwyQ0FBMkM7QUFDakQsTUFBTSxnQ0FBZ0M7QUFDdEMsTUFBTSxrREFBa0Q7QUFDeEQsTUFBTSwyQ0FBMkM7QUFDakQsTUFBTSw2Q0FBNkM7QUFDbkQsTUFBTSx1Q0FBdUM7QUFDN0MsTUFBTSxtQkFBbUI7QUFDekIsTUFBTSxrQ0FBa0M7QUFDeEMsTUFBTSw4Q0FBOEM7QUFDcEQsTUFBTSx3Q0FBd0M7QUFDOUMsTUFBTSxlQUFlO0FBQ3JCLE1BQU0saUJBQWlCO0FBQ3ZCLE1BQU0sd0NBQXdDO0FBaUM5QyxJQUFJO0FBUUosZUFBc0IsMkJBQTJCLFNBQWdFO0FBQy9HLE1BQUksQ0FBQyxXQUFXLFFBQVEsU0FBUyxnQkFBZ0I7QUFDL0Msd0JBQUksb0ZBQW9GO0FBQ3hGLFdBQU8sSUFBSSxpREFBb0I7QUFB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QkFBSSw4QkFBOEIsUUFBUSxtREFBbUQ7QUFDN0YsWUFBTSxZQUFZLFVBQVUsUUFBUTtBQUNwQyxZQUFNLFdBQVcsUUFBUTtBQUN6QjtBQUFBLElBQ0Y7QUFFQSxVQUFNLElBQUksUUFBUSxDQUFDLFlBQVk7QUFDN0IsaUJBQVcsU0FBUywyQ0FBMkM7QUFBQSxJQUNqRSxDQUFDO0FBQUEsRUFDSDtBQUVBLFFBQU0sSUFBSTtBQUFBLElBQ1Isb0JBQW9CLFFBQVEsMkJBQTJCLE9BQU8scUNBQXFDLENBQUM7QUFBQSxFQUN0RztBQUNGO0FBVUEsZUFBZSxXQUFXLFVBQWlDO0FBQ3pELHNCQUFJLCtDQUErQyxRQUFRLEtBQUs7QUFFaEUsUUFBTSxhQUFhLFVBQVUsZ0JBQWdCLGFBQWE7QUFDMUQsUUFBTSxhQUFhLFVBQVUsY0FBYyxxQkFBcUI7QUFFaEUsc0JBQUksc0RBQXNELFFBQVEsR0FBRztBQUN2RTsiLAogICJuYW1lcyI6IFsiaHR0cCIsICJraWxsQXV0b1N0YXJ0ZWRQcm9jZXNzZXMiLCAicHJvY2VzcyJdCn0K
411
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL3RyYW5zcG9ydC1mYWN0b3J5LnRzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIvKipcbiAqIEBmaWxlXG4gKlxuICogRmFjdG9yeSBmb3IgY3JlYXRpbmcgdHJhbnNwb3J0IGluc3RhbmNlcyBmcm9tIHtAbGluayBPYnNpZGlhblRyYW5zcG9ydE9wdGlvbnN9LlxuICovXG5cbi8qIHY4IGlnbm9yZSBzdGFydCAtLSBJbnRlZ3JhdGlvbi10aW1lIGZhY3RvcnkgY292ZXJlZCBieSBpbnRlZ3JhdGlvbiB0ZXN0cywgbm90IHVuaXQgdGVzdHMuICovXG5cbmltcG9ydCB0eXBlIHsgQ2hpbGRQcm9jZXNzIH0gZnJvbSAnbm9kZTpjaGlsZF9wcm9jZXNzJztcblxuaW1wb3J0IHtcbiAgZXhlY0ZpbGUsXG4gIGV4ZWNGaWxlU3luYyxcbiAgc3Bhd25cbn0gZnJvbSAnbm9kZTpjaGlsZF9wcm9jZXNzJztcbmltcG9ydCBodHRwIGZyb20gJ25vZGU6aHR0cCc7XG5pbXBvcnQgeyBqb2luIH0gZnJvbSAnbm9kZTpwYXRoJztcbmltcG9ydCBwcm9jZXNzIGZyb20gJ25vZGU6cHJvY2Vzcyc7XG5pbXBvcnQgeyByZW1vdGUgfSBmcm9tICd3ZWJkcml2ZXJpbyc7XG5cbmltcG9ydCB0eXBlIHtcbiAgT2JzaWRpYW5BbmRyb2lkQXBwaXVtVHJhbnNwb3J0T3B0aW9ucyxcbiAgT2JzaWRpYW5UcmFuc3BvcnRPcHRpb25zXG59IGZyb20gJy4vdHJhbnNwb3J0LW9wdGlvbnMuY2pzJztcbmltcG9ydCB0eXBlIHsgT2JzaWRpYW5UcmFuc3BvcnQgfSBmcm9tICcuL3RyYW5zcG9ydC5janMnO1xuXG5pbXBvcnQgeyBsb2cgfSBmcm9tICcuL2xvZy5janMnO1xuaW1wb3J0IHsgQXBwaXVtVHJhbnNwb3J0IH0gZnJvbSAnLi90cmFuc3BvcnQtYXBwaXVtLmNqcyc7XG5pbXBvcnQgeyBEZXNrdG9wQ2RwVHJhbnNwb3J0IH0gZnJvbSAnLi90cmFuc3BvcnQtZGVza3RvcC1jZHAuY2pzJztcbmltcG9ydCB7IERlc2t0b3BDbGlUcmFuc3BvcnQgfSBmcm9tICcuL3RyYW5zcG9ydC1kZXNrdG9wLWNsaS5janMnO1xuXG5jb25zdCBBUFBfUEFDS0FHRSA9ICdtZC5vYnNpZGlhbic7XG5jb25zdCBBUFBfQUNUSVZJVFkgPSBgJHtBUFBfUEFDS0FHRX0uTWFpbkFjdGl2aXR5YDtcbmNvbnN0IEFEQl9ERVZJQ0VfQ0hFQ0tfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSA1MDAwO1xuY29uc3QgQVBQSVVNX0NPTk5FQ1RJT05fUkVUUllfQ09VTlQgPSAxO1xuY29uc3QgQVBQSVVNX0NPTk5FQ1RJT05fUkVUUllfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSA2MDAwMDtcbmNvbnN0IEFQUElVTV9QUkVGTElHSFRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSA1MDAwO1xuY29uc3QgQVBQSVVNX1NUQVJUX1BPTExfSU5URVJWQUxfSU5fTUlMTElTRUNPTkRTID0gNTAwO1xuY29uc3QgQVBQSVVNX1NUQVJUX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gNjAwMDA7XG5jb25zdCBDRFBfREVGQVVMVF9QT1JUID0gODMxNTtcbmNvbnN0IENPTU1BTkRfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSAzMDA7XG5jb25zdCBERUZBVUxUX1RSQU5TUE9SVF9UWVBFID0gJ29ic2lkaWFuLWNsaSc7XG5jb25zdCBFTVVMQVRPUl9CT09UX1BPTExfSU5URVJWQUxfSU5fTUlMTElTRUNPTkRTID0gMjAwMDtcbmNvbnN0IEVNVUxBVE9SX0JPT1RfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSAxMjAwMDA7XG5jb25zdCBLRVlDT0RFX01FTlUgPSA4MjtcbmNvbnN0IEtFWUNPREVfV0FLRVVQID0gMjI0O1xuY29uc3QgU0VSVkVSX0xBVU5DSF9USU1FT1VUX0lOX01JTExJU0VDT05EUyA9IDMwMDAwO1xuXG4vKipcbiAqIFBhcmFtZXRlcnMgZm9yIHtAbGluayBBcHBpdW1UcmFuc3BvcnRGYWN0b3J5LnN0YXJ0QXBwaXVtQW5kRW11bGF0b3J9LlxuICovXG5pbnRlcmZhY2UgU3RhcnRBcHBpdW1BbmRFbXVsYXRvclBhcmFtcyB7XG4gIC8qKiBUaGUgQXBwaXVtIHNlcnZlciBVUkwuICovXG4gIGFwcGl1bVVybDogVVJMO1xuXG4gIC8qKiBBVkQgbmFtZSB0byBhdXRvLXN0YXJ0IGlmIHRoZSBkZXZpY2UgaXMgbm90IGNvbm5lY3RlZC4gKi9cbiAgYXZkTmFtZT86IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAvKiogRGV2aWNlIFVESUQuICovXG4gIGRldmljZUlkOiBzdHJpbmc7XG5cbiAgLyoqIFRoZSBBcHBpdW0gc2VydmVyIHBvcnQuICovXG4gIHBvcnQ6IG51bWJlcjtcblxuICAvKiogV2hldGhlciBBcHBpdW0gYXV0by1zdGFydCBpcyBhbGxvd2VkLiAqL1xuICBzaG91bGRBdXRvU3RhcnRBcHBpdW0/OiBib29sZWFuIHwgdW5kZWZpbmVkO1xufVxuXG5sZXQgY2FjaGVkVHJhbnNwb3J0OiBPYnNpZGlhblRyYW5zcG9ydCB8IHVuZGVmaW5lZDtcblxuLyoqXG4gKiBFbmNhcHN1bGF0ZXMgYWxsIEFwcGl1bSB0cmFuc3BvcnQgY3JlYXRpb24gbG9naWMsIGluY2x1ZGluZyBBcHBpdW0gc2VydmVyXG4gKiBzdGFydHVwLCBlbXVsYXRvciBtYW5hZ2VtZW50LCBhbmQgV2ViRHJpdmVySU8gc2Vzc2lvbiBlc3RhYmxpc2htZW50LlxuICpcbiAqIFVzaW5nIGEgY2xhc3MgYXZvaWRzIHRocmVhZGluZyB0aGUgdHJhbnNwb3J0IGB0eXBlYCBsYWJlbCB0aHJvdWdoIGV2ZXJ5XG4gKiBoZWxwZXIgZnVuY3Rpb24gXHUyMDE0IGB0aGlzLmxvZygpYCBhdXRvbWF0aWNhbGx5IHByZWZpeGVzIGl0LlxuICovXG5jbGFzcyBBcHBpdW1UcmFuc3BvcnRGYWN0b3J5IHtcbiAgcHJpdmF0ZSByZWFkb25seSB0eXBlOiBzdHJpbmc7XG5cbiAgcHVibGljIGNvbnN0cnVjdG9yKHR5cGU6IHN0cmluZykge1xuICAgIHRoaXMudHlwZSA9IHR5cGU7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhbiBBcHBpdW0gdHJhbnNwb3J0IGJ5IGVzdGFibGlzaGluZyBhIFdlYkRyaXZlcklPIHNlc3Npb24uXG4gICAqXG4gICAqIEBwYXJhbSBvcHRpb25zIC0gQW5kcm9pZCBBcHBpdW0gdHJhbnNwb3J0IG9wdGlvbnMuXG4gICAqIEByZXR1cm5zIEEgY29uZmlndXJlZCBBcHBpdW0gdHJhbnNwb3J0LlxuICAgKi9cbiAgcHVibGljIGFzeW5jIGNyZWF0ZShvcHRpb25zOiBPYnNpZGlhbkFuZHJvaWRBcHBpdW1UcmFuc3BvcnRPcHRpb25zKTogUHJvbWlzZTxPYnNpZGlhblRyYW5zcG9ydD4ge1xuICAgIHRoaXMubG9nKGBDcmVhdGluZyBBcHBpdW1UcmFuc3BvcnQgKHVybD0ke29wdGlvbnMuYXBwaXVtVXJsfSwgZGV2aWNlPSR7b3B0aW9ucy5kZXZpY2VJZH0pYCk7XG5cbiAgICBjb25zdCB1cmwgPSBuZXcgVVJMKG9wdGlvbnMuYXBwaXVtVXJsKTtcblxuICAgIGNvbnN0IHBvcnQgPSBOdW1iZXIodXJsLnBvcnQpO1xuICAgIGlmIChpc05hTihwb3J0KSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHBvcnQgaW4gYXBwaXVtVXJsOiAke3VybC5wb3J0fWApO1xuICAgIH1cblxuICAgIGNvbnN0IGFwcElkID0gb3B0aW9ucy5hcHBJZCA/PyBBUFBfUEFDS0FHRTtcblxuICAgIGxldCBhcHBpdW1Qcm9jZXNzOiBDaGlsZFByb2Nlc3MgfCB1bmRlZmluZWQ7XG4gICAgbGV0IGVtdWxhdG9yUHJvY2VzczogQ2hpbGRQcm9jZXNzIHwgdW5kZWZpbmVkO1xuXG4gICAgdHJ5IHtcbiAgICAgIFthcHBpdW1Qcm9jZXNzLCBlbXVsYXRvclByb2Nlc3NdID0gYXdhaXQgdGhpcy5zdGFydEFwcGl1bUFuZEVtdWxhdG9yKHtcbiAgICAgICAgYXBwaXVtVXJsOiB1cmwsXG4gICAgICAgIGF2ZE5hbWU6IG9wdGlvbnMuYXZkTmFtZSxcbiAgICAgICAgZGV2aWNlSWQ6IG9wdGlvbnMuZGV2aWNlSWQsXG4gICAgICAgIHBvcnQsXG4gICAgICAgIHNob3VsZEF1dG9TdGFydEFwcGl1bTogb3B0aW9ucy5zaG91bGRBdXRvU3RhcnRBcHBpdW1cbiAgICAgIH0pO1xuXG4gICAgICB0aGlzLmxvZyhcbiAgICAgICAgYENvbm5lY3RpbmcgdG8gQXBwaXVtIChkZXZpY2U9JHtvcHRpb25zLmRldmljZUlkfSwgYXBwPSR7YXBwSWR9LCByZXRyeVRpbWVvdXQ6ICR7U3RyaW5nKEFQUElVTV9DT05ORUNUSU9OX1JFVFJZX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTKX1tcywgcmV0cmllczogJHtcbiAgICAgICAgICBTdHJpbmcoQVBQSVVNX0NPTk5FQ1RJT05fUkVUUllfQ09VTlQpXG4gICAgICAgIH0pLi4uYFxuICAgICAgKTtcbiAgICAgIGNvbnN0IGJyb3dzZXIgPSBhd2FpdCByZW1vdGUoe1xuICAgICAgICBjYXBhYmlsaXRpZXM6IHtcbiAgICAgICAgICAnYXBwaXVtOmFwcEFjdGl2aXR5JzogQVBQX0FDVElWSVRZLFxuICAgICAgICAgICdhcHBpdW06YXBwUGFja2FnZSc6IGFwcElkLFxuICAgICAgICAgICdhcHBpdW06YXV0b0dyYW50UGVybWlzc2lvbnMnOiB0cnVlLFxuICAgICAgICAgICdhcHBpdW06YXV0b21hdGlvbk5hbWUnOiAnVWlBdXRvbWF0b3IyJyxcbiAgICAgICAgICAnYXBwaXVtOm5ld0NvbW1hbmRUaW1lb3V0JzogQ09NTUFORF9USU1FT1VUX0lOX01JTExJU0VDT05EUyxcbiAgICAgICAgICAnYXBwaXVtOm5vUmVzZXQnOiB0cnVlLFxuICAgICAgICAgICdhcHBpdW06c2V0dGluZ3MnOiB7XG4gICAgICAgICAgICAnYXBwaXVtOmNocm9tZWRyaXZlckF1dG9kb3dubG9hZCc6IHRydWVcbiAgICAgICAgICB9LFxuICAgICAgICAgICdhcHBpdW06c2tpcFNlcnZlckluc3RhbGxhdGlvbic6IHRydWUsXG4gICAgICAgICAgJ2FwcGl1bTp1ZGlkJzogb3B0aW9ucy5kZXZpY2VJZCxcbiAgICAgICAgICAnYXBwaXVtOnVpYXV0b21hdG9yMlNlcnZlckxhdW5jaFRpbWVvdXQnOiBTRVJWRVJfTEFVTkNIX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTLFxuICAgICAgICAgICdwbGF0Zm9ybU5hbWUnOiAnQW5kcm9pZCdcbiAgICAgICAgfSxcbiAgICAgICAgY29ubmVjdGlvblJldHJ5Q291bnQ6IEFQUElVTV9DT05ORUNUSU9OX1JFVFJZX0NPVU5ULFxuICAgICAgICBjb25uZWN0aW9uUmV0cnlUaW1lb3V0OiBBUFBJVU1fQ09OTkVDVElPTl9SRVRSWV9USU1FT1VUX0lOX01JTExJU0VDT05EUyxcbiAgICAgICAgaG9zdG5hbWU6IHVybC5ob3N0bmFtZSxcbiAgICAgICAgbG9nTGV2ZWw6ICd3YXJuJyxcbiAgICAgICAgcGF0aDogdXJsLnBhdGhuYW1lLFxuICAgICAgICBwb3J0XG4gICAgICB9KTtcblxuICAgICAgdGhpcy5sb2coJ0FwcGl1bSBzZXNzaW9uIGVzdGFibGlzaGVkLicpO1xuICAgICAgY29uc3QgYXBwaXVtVHJhbnNwb3J0ID0gbmV3IEFwcGl1bVRyYW5zcG9ydCh7XG4gICAgICAgIGFwcElkLFxuICAgICAgICBicm93c2VyLFxuICAgICAgICBwbGF0Zm9ybTogJ2FuZHJvaWQnLFxuICAgICAgICAuLi4ob3B0aW9ucy52YXVsdEJhc2VQYXRoICE9PSB1bmRlZmluZWQgJiYgeyB2YXVsdEJhc2VQYXRoOiBvcHRpb25zLnZhdWx0QmFzZVBhdGggfSlcbiAgICAgIH0pO1xuXG4gICAgICBjb25zdCBvcmlnaW5hbERpc3Bvc2UgPSBhcHBpdW1UcmFuc3BvcnQuZGlzcG9zZS5iaW5kKGFwcGl1bVRyYW5zcG9ydCk7XG4gICAgICBjb25zdCB0cmFuc3BvcnQ6IE9ic2lkaWFuVHJhbnNwb3J0ID0gYXBwaXVtVHJhbnNwb3J0O1xuICAgICAgdHJhbnNwb3J0LmRpc3Bvc2UgPSBhc3luYyAoKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgYXdhaXQgb3JpZ2luYWxEaXNwb3NlKCk7XG4gICAgICAgIH0gZmluYWxseSB7XG4gICAgICAgICAga2lsbEF1dG9TdGFydGVkUHJvY2Vzc2VzKCk7XG4gICAgICAgIH1cbiAgICAgIH07XG5cbiAgICAgIHRyYW5zcG9ydC5kaXNwb3NlU3luYyA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAga2lsbEF1dG9TdGFydGVkUHJvY2Vzc2VzKCk7XG4gICAgICB9O1xuXG4gICAgICByZXR1cm4gdHJhbnNwb3J0O1xuXG4gICAgICBmdW5jdGlvbiBraWxsQXV0b1N0YXJ0ZWRQcm9jZXNzZXMoKTogdm9pZCB7XG4gICAgICAgIGlmIChhcHBpdW1Qcm9jZXNzKSB7XG4gICAgICAgICAga2lsbFByb2Nlc3NUcmVlKGFwcGl1bVByb2Nlc3MpO1xuICAgICAgICAgIC8vIENhbm5vdCB1c2UgdGhpcy5sb2cgaW5zaWRlIG5lc3RlZCBmdW5jdGlvbiBcdTIwMTQgYHRoaXNgIGlzIG5vdCBjYXB0dXJlZC5cbiAgICAgICAgICBsb2coYFt0cmFuc3BvcnQtZmFjdG9yeToke29wdGlvbnMudHlwZX1dIEF1dG8tc3RhcnRlZCBBcHBpdW0gc2VydmVyIHN0b3BwZWQuYCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGVtdWxhdG9yUHJvY2Vzcykge1xuICAgICAgICAgIGtpbGxQcm9jZXNzVHJlZShlbXVsYXRvclByb2Nlc3MpO1xuICAgICAgICAgIGxvZyhgW3RyYW5zcG9ydC1mYWN0b3J5OiR7b3B0aW9ucy50eXBlfV0gQXV0by1zdGFydGVkIGVtdWxhdG9yIHN0b3BwZWQuYCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGNhdGNoIChlcnJvcjogdW5rbm93bikge1xuICAgICAgaWYgKGFwcGl1bVByb2Nlc3MpIHtcbiAgICAgICAga2lsbFByb2Nlc3NUcmVlKGFwcGl1bVByb2Nlc3MpO1xuICAgICAgICB0aGlzLmxvZygnS2lsbGVkIGF1dG8tc3RhcnRlZCBBcHBpdW0gc2VydmVyIGFmdGVyIGNvbm5lY3Rpb24gZmFpbHVyZS4nKTtcbiAgICAgIH1cbiAgICAgIGlmIChlbXVsYXRvclByb2Nlc3MpIHtcbiAgICAgICAga2lsbFByb2Nlc3NUcmVlKGVtdWxhdG9yUHJvY2Vzcyk7XG4gICAgICAgIHRoaXMubG9nKCdLaWxsZWQgYXV0by1zdGFydGVkIGVtdWxhdG9yIGFmdGVyIGNvbm5lY3Rpb24gZmFpbHVyZS4nKTtcbiAgICAgIH1cbiAgICAgIHRocm93IGVycm9yO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY2hlY2tBcHBpdW1SZWFjaGFibGUodXJsOiBVUkwpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3Qgc3RhdHVzVXJsID0gbmV3IFVSTCgnL3N0YXR1cycsIHVybCk7XG4gICAgICBjb25zdCByZXEgPSBodHRwLmdldChzdGF0dXNVcmwsIHsgdGltZW91dDogQVBQSVVNX1BSRUZMSUdIVF9USU1FT1VUX0lOX01JTExJU0VDT05EUyB9LCAocmVzKSA9PiB7XG4gICAgICAgIHJlcy5yZXN1bWUoKTtcbiAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgfSk7XG4gICAgICByZXEub24oJ3RpbWVvdXQnLCAoKSA9PiB7XG4gICAgICAgIHJlcS5kZXN0cm95KCk7XG4gICAgICAgIHJlamVjdChcbiAgICAgICAgICBuZXcgRXJyb3IoXG4gICAgICAgICAgICBgQXBwaXVtIHNlcnZlciBhdCAke3VybC5vcmlnaW59IGRpZCBub3QgcmVzcG9uZCB3aXRoaW4gJHtTdHJpbmcoQVBQSVVNX1BSRUZMSUdIVF9USU1FT1VUX0lOX01JTExJU0VDT05EUyl9bXMuIElzIHRoZSBBcHBpdW0gc2VydmVyIHJ1bm5pbmc/YFxuICAgICAgICAgIClcbiAgICAgICAgKTtcbiAgICAgIH0pO1xuICAgICAgcmVxLm9uKCdlcnJvcicsIChlcnIpID0+IHtcbiAgICAgICAgcmVqZWN0KFxuICAgICAgICAgIG5ldyBFcnJvcihcbiAgICAgICAgICAgIGBDYW5ub3QgcmVhY2ggQXBwaXVtIHNlcnZlciBhdCAke3VybC5vcmlnaW59OiAke2Vyci5tZXNzYWdlfS4gSXMgdGhlIEFwcGl1bSBzZXJ2ZXIgcnVubmluZz9gXG4gICAgICAgICAgKVxuICAgICAgICApO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIGNoZWNrRGV2aWNlQ29ubmVjdGVkKGRldmljZUlkOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCBvdXRwdXQgPSBhd2FpdCBuZXcgUHJvbWlzZTxzdHJpbmc+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIGV4ZWNGaWxlKCdhZGInLCBbJ2RldmljZXMnXSwgeyB0aW1lb3V0OiBBREJfREVWSUNFX0NIRUNLX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTIH0sIChlcnJvciwgc3Rkb3V0LCBzdGRlcnIpID0+IHtcbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIHJ1biAnYWRiIGRldmljZXMnOiAke2Vycm9yLm1lc3NhZ2V9LiBJcyBBREIgaW5zdGFsbGVkIGFuZCBpbiBQQVRIP2ApKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHN0ZGVycikge1xuICAgICAgICAgIHRoaXMubG9nKGBBREIgc3RkZXJyOiAke3N0ZGVyci50cmltKCl9YCk7XG4gICAgICAgIH1cbiAgICAgICAgcmVzb2x2ZShzdGRvdXQpO1xuICAgICAgfSk7XG4gICAgfSk7XG5cbiAgICBjb25zdCBsaW5lcyA9IG91dHB1dC5zcGxpdCgnXFxuJykubWFwKChsaW5lKSA9PiBsaW5lLnRyaW0oKSkuZmlsdGVyKChsaW5lKSA9PiBsaW5lLmxlbmd0aCA+IDApO1xuICAgIGNvbnN0IGRldmljZUxpbmUgPSBsaW5lcy5maW5kKChsaW5lKSA9PiBsaW5lLnN0YXJ0c1dpdGgoZGV2aWNlSWQpKTtcbiAgICByZXR1cm4gZGV2aWNlTGluZT8uaW5jbHVkZXMoJ1xcdGRldmljZScpID8/IGZhbHNlO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyBlbnN1cmVEZXZpY2VDb25uZWN0ZWQoYXZkTmFtZTogc3RyaW5nIHwgdW5kZWZpbmVkLCBkZXZpY2VJZDogc3RyaW5nKTogUHJvbWlzZTxDaGlsZFByb2Nlc3MgfCB1bmRlZmluZWQ+IHtcbiAgICB0aGlzLmxvZyhgQ2hlY2tpbmcgZGV2aWNlICR7ZGV2aWNlSWR9IGlzIGNvbm5lY3RlZCB2aWEgQURCLi4uYCk7XG5cbiAgICBpZiAoYXdhaXQgdGhpcy5jaGVja0RldmljZUNvbm5lY3RlZChkZXZpY2VJZCkpIHtcbiAgICAgIHRoaXMubG9nKGBEZXZpY2UgJHtkZXZpY2VJZH0gaXMgY29ubmVjdGVkLmApO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBpZiAoIWF2ZE5hbWUpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgYERldmljZSBcIiR7ZGV2aWNlSWR9XCIgaXMgbm90IGNvbm5lY3RlZC4gU3RhcnQgdGhlIGVtdWxhdG9yIGJlZm9yZSBydW5uaW5nIGludGVncmF0aW9uIHRlc3RzLGBcbiAgICAgICAgICArICcgb3Igc2V0IGF2ZE5hbWUgaW4gdHJhbnNwb3J0IG9wdGlvbnMgdG8gYXV0by1zdGFydCBpdC4nXG4gICAgICApO1xuICAgIH1cblxuICAgIHRoaXMubG9nKGBEZXZpY2UgJHtkZXZpY2VJZH0gbm90IGZvdW5kLCBzdGFydGluZyBlbXVsYXRvciBBVkQgXCIke2F2ZE5hbWV9XCIuLi5gKTtcbiAgICBjb25zdCBlbXVsYXRvclByb2Nlc3MgPSB0aGlzLnN0YXJ0RW11bGF0b3IoYXZkTmFtZSk7XG4gICAgYXdhaXQgdGhpcy53YWl0Rm9yRGV2aWNlKGRldmljZUlkKTtcbiAgICB0aGlzLmxvZyhgRW11bGF0b3IgXCIke2F2ZE5hbWV9XCIgc3RhcnRlZCwgZGV2aWNlICR7ZGV2aWNlSWR9IGlzIGNvbm5lY3RlZC5gKTtcbiAgICByZXR1cm4gZW11bGF0b3JQcm9jZXNzO1xuICB9XG5cbiAgcHJpdmF0ZSBsb2cobWVzc2FnZTogc3RyaW5nKTogdm9pZCB7XG4gICAgbG9nKGBbdHJhbnNwb3J0LWZhY3Rvcnk6JHt0aGlzLnR5cGV9XSAke21lc3NhZ2V9YCk7XG4gIH1cblxuICBwcml2YXRlIHJlc29sdmVFbXVsYXRvckJpbmFyeSgpOiBzdHJpbmcge1xuICAgIGNvbnN0IHNka1Jvb3QgPSBwcm9jZXNzLmVudlsnQU5EUk9JRF9IT01FJ10gPz8gcHJvY2Vzcy5lbnZbJ0FORFJPSURfU0RLX1JPT1QnXTtcbiAgICBpZiAoIXNka1Jvb3QpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ0Nhbm5vdCBmaW5kIEFuZHJvaWQgZW11bGF0b3I6IG5laXRoZXIgQU5EUk9JRF9IT01FIG5vciBBTkRST0lEX1NES19ST09UIGVudmlyb25tZW50IHZhcmlhYmxlIGlzIHNldC4nXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gam9pbihzZGtSb290LCAnZW11bGF0b3InLCAnZW11bGF0b3InKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgc2VuZEtleUV2ZW50KGRldmljZUlkOiBzdHJpbmcsIGtleUNvZGU6IG51bWJlciwgZGVzY3JpcHRpb246IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGF3YWl0IG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB7XG4gICAgICBleGVjRmlsZShcbiAgICAgICAgJ2FkYicsXG4gICAgICAgIFsnLXMnLCBkZXZpY2VJZCwgJ3NoZWxsJywgJ2lucHV0JywgJ2tleWV2ZW50JywgU3RyaW5nKGtleUNvZGUpXSxcbiAgICAgICAgeyB0aW1lb3V0OiBBREJfREVWSUNFX0NIRUNLX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTIH0sXG4gICAgICAgIChlcnJvcikgPT4ge1xuICAgICAgICAgIGlmIChlcnJvcikge1xuICAgICAgICAgICAgdGhpcy5sb2coXG4gICAgICAgICAgICAgIGBXYXJuaW5nOiBmYWlsZWQgdG8gJHtkZXNjcmlwdGlvbn0gKGtleWV2ZW50ICR7U3RyaW5nKGtleUNvZGUpfSk6ICR7ZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiAndW5rbm93biBlcnJvcid9YFxuICAgICAgICAgICAgKTtcbiAgICAgICAgICB9XG5cbiAgICAgICAgICByZXNvbHZlKCk7XG4gICAgICAgIH1cbiAgICAgICk7XG4gICAgfSk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHN0YXJ0QXBwaXVtQW5kRW11bGF0b3IocGFyYW1zOiBTdGFydEFwcGl1bUFuZEVtdWxhdG9yUGFyYW1zKTogUHJvbWlzZTxbQ2hpbGRQcm9jZXNzIHwgdW5kZWZpbmVkLCBDaGlsZFByb2Nlc3MgfCB1bmRlZmluZWRdPiB7XG4gICAgY29uc3QgeyBhcHBpdW1VcmwsIGF2ZE5hbWUsIGRldmljZUlkLCBwb3J0LCBzaG91bGRBdXRvU3RhcnRBcHBpdW0gfSA9IHBhcmFtcztcblxuICAgIGxldCBuZWVkc0FwcGl1bVN0YXJ0ID0gZmFsc2U7XG5cbiAgICB0aGlzLmxvZyhgQ2hlY2tpbmcgQXBwaXVtIHNlcnZlciBhdCAke2FwcGl1bVVybC5ocmVmfS4uLmApO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLmNoZWNrQXBwaXVtUmVhY2hhYmxlKGFwcGl1bVVybCk7XG4gICAgICB0aGlzLmxvZygnQXBwaXVtIHNlcnZlciBpcyByZWFjaGFibGUuJyk7XG4gICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgIGlmIChzaG91bGRBdXRvU3RhcnRBcHBpdW0gPT09IGZhbHNlKSB7XG4gICAgICAgIHRocm93IGVycm9yO1xuICAgICAgfVxuICAgICAgbmVlZHNBcHBpdW1TdGFydCA9IHRydWU7XG4gICAgfVxuXG4gICAgbGV0IGFwcGl1bVByb2Nlc3M6IENoaWxkUHJvY2VzcyB8IHVuZGVmaW5lZDtcblxuICAgIGlmIChuZWVkc0FwcGl1bVN0YXJ0KSB7XG4gICAgICB0aGlzLmxvZyhgQXBwaXVtIG5vdCByZWFjaGFibGUsIGF1dG8tc3RhcnRpbmcgb24gcG9ydCAke1N0cmluZyhwb3J0KX0uLi5gKTtcbiAgICAgIGFwcGl1bVByb2Nlc3MgPSB0aGlzLnN0YXJ0QXBwaXVtU2VydmVyKHBvcnQpO1xuICAgIH1cblxuICAgIGNvbnN0IFssIGVtdWxhdG9yUHJvY2Vzc10gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgICBuZWVkc0FwcGl1bVN0YXJ0XG4gICAgICAgID8gdGhpcy53YWl0Rm9yQXBwaXVtUmVhZHkoYXBwaXVtVXJsKS50aGVuKCgpID0+IHtcbiAgICAgICAgICB0aGlzLmxvZygnQXV0by1zdGFydGVkIEFwcGl1bSBzZXJ2ZXIgaXMgcmVhZHkuJyk7XG4gICAgICAgIH0pXG4gICAgICAgIDogUHJvbWlzZS5yZXNvbHZlKCksXG4gICAgICB0aGlzLmVuc3VyZURldmljZUNvbm5lY3RlZChhdmROYW1lLCBkZXZpY2VJZClcbiAgICBdKTtcblxuICAgIHJldHVybiBbYXBwaXVtUHJvY2VzcywgZW11bGF0b3JQcm9jZXNzXTtcbiAgfVxuXG4gIHByaXZhdGUgc3RhcnRBcHBpdW1TZXJ2ZXIocG9ydDogbnVtYmVyKTogQ2hpbGRQcm9jZXNzIHtcbiAgICBjb25zdCBjaGlsZCA9IHNwYXduKGBucHggYXBwaXVtIC0tbG9nLXRpbWVzdGFtcCAtLXBvcnQgJHtTdHJpbmcocG9ydCl9YCwge1xuICAgICAgZGV0YWNoZWQ6IHRydWUsXG4gICAgICBzaGVsbDogdHJ1ZSxcbiAgICAgIHN0ZGlvOiBbJ2lnbm9yZScsICdpbmhlcml0JywgJ2luaGVyaXQnXVxuICAgIH0pO1xuXG4gICAgY2hpbGQudW5yZWYoKTtcbiAgICByZXR1cm4gY2hpbGQ7XG4gIH1cblxuICBwcml2YXRlIHN0YXJ0RW11bGF0b3IoYXZkTmFtZTogc3RyaW5nKTogQ2hpbGRQcm9jZXNzIHtcbiAgICBjb25zdCBlbXVsYXRvckJpbmFyeSA9IHRoaXMucmVzb2x2ZUVtdWxhdG9yQmluYXJ5KCk7XG4gICAgdGhpcy5sb2coYFJ1bm5pbmc6ICR7ZW11bGF0b3JCaW5hcnl9IC1hdmQgJHthdmROYW1lfSAtbm8tc25hcHNob3Qtc2F2ZWApO1xuICAgIGNvbnN0IGNoaWxkID0gc3Bhd24oZW11bGF0b3JCaW5hcnksIFsnLWF2ZCcsIGF2ZE5hbWUsICctbm8tc25hcHNob3Qtc2F2ZSddLCB7XG4gICAgICBkZXRhY2hlZDogdHJ1ZSxcbiAgICAgIHN0ZGlvOiBbJ2lnbm9yZScsICdpZ25vcmUnLCAnaWdub3JlJ11cbiAgICB9KTtcblxuICAgIGNoaWxkLnVucmVmKCk7XG4gICAgcmV0dXJuIGNoaWxkO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB3YWl0Rm9yQXBwaXVtUmVhZHkodXJsOiBVUkwpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmxvZyhcbiAgICAgIGBXYWl0aW5nIGZvciBBcHBpdW0gYXQgJHt1cmwuaHJlZn0gKHRpbWVvdXQ6ICR7U3RyaW5nKEFQUElVTV9TVEFSVF9USU1FT1VUX0lOX01JTExJU0VDT05EUyl9bXMsIHBvbGw6ICR7XG4gICAgICAgIFN0cmluZyhBUFBJVU1fU1RBUlRfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMpXG4gICAgICB9bXMpLi4uYFxuICAgICk7XG4gICAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgQVBQSVVNX1NUQVJUX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTO1xuXG4gICAgd2hpbGUgKERhdGUubm93KCkgPCBkZWFkbGluZSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgdGhpcy5jaGVja0FwcGl1bVJlYWNoYWJsZSh1cmwpO1xuICAgICAgICByZXR1cm47XG4gICAgICB9IGNhdGNoIHtcbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgICAgICBzZXRUaW1lb3V0KHJlc29sdmUsIEFQUElVTV9TVEFSVF9QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyk7XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH1cblxuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBBdXRvLXN0YXJ0ZWQgQXBwaXVtIHNlcnZlciBkaWQgbm90IGJlY29tZSByZWFkeSB3aXRoaW4gJHtTdHJpbmcoQVBQSVVNX1NUQVJUX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTKX1tc2BcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB3YWl0Rm9yQm9vdChkZXZpY2VJZDogc3RyaW5nLCBkZWFkbGluZTogbnVtYmVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgcmVtYWluaW5nTXMgPSBNYXRoLm1heCgwLCBkZWFkbGluZSAtIERhdGUubm93KCkpO1xuICAgIHRoaXMubG9nKFxuICAgICAgYFdhaXRpbmcgZm9yIGRldmljZSAke2RldmljZUlkfSB0byBmaW5pc2ggYm9vdGluZyAocmVtYWluaW5nOiAke1N0cmluZyhyZW1haW5pbmdNcyl9bXMsIHBvbGw6ICR7XG4gICAgICAgIFN0cmluZyhFTVVMQVRPUl9CT09UX1BPTExfSU5URVJWQUxfSU5fTUlMTElTRUNPTkRTKVxuICAgICAgfW1zKS4uLmBcbiAgICApO1xuXG4gICAgd2hpbGUgKERhdGUubm93KCkgPCBkZWFkbGluZSkge1xuICAgICAgY29uc3QgaXNCb290ZWQgPSBhd2FpdCBuZXcgUHJvbWlzZTxib29sZWFuPigocmVzb2x2ZSkgPT4ge1xuICAgICAgICBleGVjRmlsZShcbiAgICAgICAgICAnYWRiJyxcbiAgICAgICAgICBbJy1zJywgZGV2aWNlSWQsICdzaGVsbCcsICdnZXRwcm9wJywgJ3N5cy5ib290X2NvbXBsZXRlZCddLFxuICAgICAgICAgIHsgdGltZW91dDogQURCX0RFVklDRV9DSEVDS19USU1FT1VUX0lOX01JTExJU0VDT05EUyB9LFxuICAgICAgICAgIChfZXJyb3IsIHN0ZG91dCkgPT4ge1xuICAgICAgICAgICAgcmVzb2x2ZShzdGRvdXQudHJpbSgpID09PSAnMScpO1xuICAgICAgICAgIH1cbiAgICAgICAgKTtcbiAgICAgIH0pO1xuXG4gICAgICBpZiAoaXNCb290ZWQpIHtcbiAgICAgICAgdGhpcy5sb2coYERldmljZSAke2RldmljZUlkfSBib290IGNvbXBsZXRlZC5gKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICBzZXRUaW1lb3V0KHJlc29sdmUsIEVNVUxBVE9SX0JPT1RfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMpO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYERldmljZSBcIiR7ZGV2aWNlSWR9XCIgY29ubmVjdGVkIGJ1dCBkaWQgbm90IGZpbmlzaCBib290aW5nIHdpdGhpbiAke1N0cmluZyhFTVVMQVRPUl9CT09UX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTKX1tcy5gXG4gICAgKTtcbiAgfVxuXG4gIHByaXZhdGUgYXN5bmMgd2FpdEZvckRldmljZShkZXZpY2VJZDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5sb2coXG4gICAgICBgV2FpdGluZyBmb3IgZGV2aWNlICR7ZGV2aWNlSWR9IHRvIGFwcGVhciBpbiBBREIgKHRpbWVvdXQ6ICR7U3RyaW5nKEVNVUxBVE9SX0JPT1RfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMpfW1zLCBwb2xsOiAke1xuICAgICAgICBTdHJpbmcoRU1VTEFUT1JfQk9PVF9QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUylcbiAgICAgIH1tcykuLi5gXG4gICAgKTtcbiAgICBjb25zdCBkZWFkbGluZSA9IERhdGUubm93KCkgKyBFTVVMQVRPUl9CT09UX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTO1xuXG4gICAgd2hpbGUgKERhdGUubm93KCkgPCBkZWFkbGluZSkge1xuICAgICAgaWYgKGF3YWl0IHRoaXMuY2hlY2tEZXZpY2VDb25uZWN0ZWQoZGV2aWNlSWQpKSB7XG4gICAgICAgIHRoaXMubG9nKGBEZXZpY2UgJHtkZXZpY2VJZH0gYXBwZWFyZWQgaW4gQURCLCB3YWl0aW5nIGZvciBib290IHRvIGNvbXBsZXRlLi4uYCk7XG4gICAgICAgIGF3YWl0IHRoaXMud2FpdEZvckJvb3QoZGV2aWNlSWQsIGRlYWRsaW5lKTtcbiAgICAgICAgYXdhaXQgdGhpcy53YWtlU2NyZWVuKGRldmljZUlkKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuXG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZSgocmVzb2x2ZSkgPT4ge1xuICAgICAgICBzZXRUaW1lb3V0KHJlc29sdmUsIEVNVUxBVE9SX0JPT1RfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMpO1xuICAgICAgfSk7XG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEVtdWxhdG9yIGRldmljZSBcIiR7ZGV2aWNlSWR9XCIgZGlkIG5vdCBhcHBlYXIgd2l0aGluICR7U3RyaW5nKEVNVUxBVE9SX0JPT1RfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMpfW1zLmBcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB3YWtlU2NyZWVuKGRldmljZUlkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0aGlzLmxvZyhgV2FraW5nIHNjcmVlbiBvbiBkZXZpY2UgJHtkZXZpY2VJZH0uLi5gKTtcblxuICAgIGF3YWl0IHRoaXMuc2VuZEtleUV2ZW50KGRldmljZUlkLCBLRVlDT0RFX1dBS0VVUCwgJ3dha2Ugc2NyZWVuJyk7XG4gICAgYXdhaXQgdGhpcy5zZW5kS2V5RXZlbnQoZGV2aWNlSWQsIEtFWUNPREVfTUVOVSwgJ2Rpc21pc3MgbG9jayBzY3JlZW4nKTtcblxuICAgIHRoaXMubG9nKGBTY3JlZW4gd2FrZSBjb21wbGV0ZSBvbiBkZXZpY2UgJHtkZXZpY2VJZH0uYCk7XG4gIH1cbn1cblxuLyoqXG4gKiBDcmVhdGVzIGEgbmV3IHRyYW5zcG9ydCBpbnN0YW5jZSBmcm9tIHRoZSBnaXZlbiBvcHRpb25zLlxuICpcbiAqIEBwYXJhbSBvcHRpb25zIC0gVHJhbnNwb3J0IGNvbmZpZ3VyYXRpb24uIERlZmF1bHRzIHRvIENMSSB0cmFuc3BvcnQuXG4gKiBAcmV0dXJucyBBIG5ldyB0cmFuc3BvcnQgaW5zdGFuY2UuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVUcmFuc3BvcnRGcm9tT3B0aW9ucyhvcHRpb25zPzogT2JzaWRpYW5UcmFuc3BvcnRPcHRpb25zKTogUHJvbWlzZTxPYnNpZGlhblRyYW5zcG9ydD4ge1xuICBjb25zdCB0eXBlID0gb3B0aW9ucz8udHlwZSA/PyBERUZBVUxUX1RSQU5TUE9SVF9UWVBFO1xuXG4gIGlmICghb3B0aW9ucyB8fCBvcHRpb25zLnR5cGUgPT09ICdvYnNpZGlhbi1jbGknKSB7XG4gICAgbG9nKGBbdHJhbnNwb3J0LWZhY3Rvcnk6JHt0eXBlfV0gQ3JlYXRpbmcgRGVza3RvcENsaVRyYW5zcG9ydGApO1xuICAgIHJldHVybiBuZXcgRGVza3RvcENsaVRyYW5zcG9ydCgpO1xuICB9XG5cbiAgaWYgKG9wdGlvbnMudHlwZSA9PT0gJ29ic2lkaWFuLWNkcCcpIHtcbiAgICBsb2coYFt0cmFuc3BvcnQtZmFjdG9yeToke3R5cGV9XSBDcmVhdGluZyBEZXNrdG9wQ2RwVHJhbnNwb3J0IChob3N0PSR7b3B0aW9ucy5ob3N0ID8/ICdsb2NhbGhvc3QnfSwgcG9ydD0ke1N0cmluZyhvcHRpb25zLnBvcnQgPz8gQ0RQX0RFRkFVTFRfUE9SVCl9KWApO1xuICAgIHJldHVybiBuZXcgRGVza3RvcENkcFRyYW5zcG9ydCh7XG4gICAgICAuLi4ob3B0aW9ucy5ob3N0ICE9PSB1bmRlZmluZWQgJiYgeyBjZHBIb3N0OiBvcHRpb25zLmhvc3QgfSksXG4gICAgICAuLi4ob3B0aW9ucy5wb3J0ICE9PSB1bmRlZmluZWQgJiYgeyBjZHBQb3J0OiBvcHRpb25zLnBvcnQgfSksXG4gICAgICAuLi4ob3B0aW9ucy5jb21tYW5kVGltZW91dEluTWlsbGlzZWNvbmRzICE9PSB1bmRlZmluZWQgJiYgeyBjb21tYW5kVGltZW91dEluTWlsbGlzZWNvbmRzOiBvcHRpb25zLmNvbW1hbmRUaW1lb3V0SW5NaWxsaXNlY29uZHMgfSlcbiAgICB9KTtcbiAgfVxuXG4gIGNvbnN0IGZhY3RvcnkgPSBuZXcgQXBwaXVtVHJhbnNwb3J0RmFjdG9yeSh0eXBlKTtcbiAgcmV0dXJuIGZhY3RvcnkuY3JlYXRlKG9wdGlvbnMpO1xufVxuXG4vKipcbiAqIFJldHVybnMgYSBjYWNoZWQgdHJhbnNwb3J0IGluc3RhbmNlLCBjcmVhdGluZyBvbmUgZnJvbSB0aGUgZ2l2ZW4gb3B0aW9uc1xuICogaWYgbm90IGFscmVhZHkgY2FjaGVkLlxuICpcbiAqIFRoZSB0cmFuc3BvcnQgaXMgY2FjaGVkIHBlciB3b3JrZXIgcHJvY2VzcyBzbyBXZWJTb2NrZXQvQXBwaXVtIHNlc3Npb25zXG4gKiBhcmUgcmV1c2VkIGFjcm9zcyBjYWxscyB3aXRoaW4gdGhlIHNhbWUgdGVzdCB3b3JrZXIuXG4gKlxuICogQHBhcmFtIG9wdGlvbnMgLSBUcmFuc3BvcnQgY29uZmlndXJhdGlvbi4gRGVmYXVsdHMgdG8gQ0xJIHRyYW5zcG9ydC5cbiAqIEByZXR1cm5zIFRoZSBjYWNoZWQgb3IgbmV3bHkgY3JlYXRlZCB0cmFuc3BvcnQuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXRPckNyZWF0ZVRyYW5zcG9ydChvcHRpb25zPzogT2JzaWRpYW5UcmFuc3BvcnRPcHRpb25zKTogUHJvbWlzZTxPYnNpZGlhblRyYW5zcG9ydD4ge1xuICBpZiAoY2FjaGVkVHJhbnNwb3J0KSB7XG4gICAgcmV0dXJuIGNhY2hlZFRyYW5zcG9ydDtcbiAgfVxuXG4gIGNvbnN0IHJlc3VsdCA9IGF3YWl0IGNyZWF0ZVRyYW5zcG9ydEZyb21PcHRpb25zKG9wdGlvbnMpO1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgcmVxdWlyZS1hdG9taWMtdXBkYXRlcyAtLSBTaW5nbGUtdGhyZWFkZWQgd29ya2VyOyBubyBjb25jdXJyZW50IHdyaXRlcy5cbiAgY2FjaGVkVHJhbnNwb3J0ID0gcmVzdWx0O1xuICByZXR1cm4gcmVzdWx0O1xufVxuXG4vKipcbiAqIEtpbGxzIGEgY2hpbGQgcHJvY2VzcyBhbmQgaXRzIGVudGlyZSBwcm9jZXNzIHRyZWUuXG4gKlxuICogT24gV2luZG93cywgYGNoaWxkLmtpbGwoKWAgb25seSBzZW5kcyBTSUdURVJNIHRvIHRoZSBkaXJlY3QgcHJvY2VzcyxcbiAqIGxlYXZpbmcgc3Bhd25lZCBncmFuZGNoaWxkcmVuIChlLmcuIFFFTVUsIFVpQXV0b21hdG9yKSBhbGl2ZS5cbiAqIGB0YXNra2lsbCAvRiAvVCAvUElEYCBmb3JjZWZ1bGx5IHRlcm1pbmF0ZXMgdGhlIGVudGlyZSB0cmVlLlxuICpcbiAqIEBwYXJhbSBjaGlsZCAtIFRoZSBjaGlsZCBwcm9jZXNzIHRvIGtpbGwuXG4gKi9cbmZ1bmN0aW9uIGtpbGxQcm9jZXNzVHJlZShjaGlsZDogQ2hpbGRQcm9jZXNzKTogdm9pZCB7XG4gIGlmIChjaGlsZC5waWQgPT09IHVuZGVmaW5lZCkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGlmIChwcm9jZXNzLnBsYXRmb3JtID09PSAnd2luMzInKSB7XG4gICAgdHJ5IHtcbiAgICAgIGV4ZWNGaWxlU3luYygndGFza2tpbGwnLCBbJy9GJywgJy9UJywgJy9QSUQnLCBTdHJpbmcoY2hpbGQucGlkKV0sIHsgc3RkaW86ICdpZ25vcmUnIH0pO1xuICAgIH0gY2F0Y2ggKGVycm9yOiB1bmtub3duKSB7XG4gICAgICBsb2coXG4gICAgICAgIGBbdHJhbnNwb3J0LWZhY3RvcnldIHRhc2traWxsIGZvciBQSUQgJHtTdHJpbmcoY2hpbGQucGlkKX0gZmFpbGVkIChtYXkgaGF2ZSBhbHJlYWR5IGV4aXRlZCk6ICR7XG4gICAgICAgICAgZXJyb3IgaW5zdGFuY2VvZiBFcnJvciA/IGVycm9yLm1lc3NhZ2UgOiAndW5rbm93biBlcnJvcidcbiAgICAgICAgfWBcbiAgICAgICk7XG4gICAgfVxuICB9IGVsc2Uge1xuICAgIGNoaWxkLmtpbGwoJ1NJR0tJTEwnKTtcbiAgfVxufVxuXG4vKiB2OCBpZ25vcmUgc3RvcCAqL1xuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQVVBLGdDQUlPO0FBQ1AsdUJBQWlCO0FBQ2pCLHVCQUFxQjtBQUNyQiwwQkFBb0I7QUFDcEIseUJBQXVCO0FBUXZCLGlCQUFvQjtBQUNwQiw4QkFBZ0M7QUFDaEMsbUNBQW9DO0FBQ3BDLG1DQUFvQztBQUVwQyxNQUFNLGNBQWM7QUFDcEIsTUFBTSxlQUFlLEdBQUcsV0FBVztBQUNuQyxNQUFNLDJDQUEyQztBQUNqRCxNQUFNLGdDQUFnQztBQUN0QyxNQUFNLGtEQUFrRDtBQUN4RCxNQUFNLDJDQUEyQztBQUNqRCxNQUFNLDZDQUE2QztBQUNuRCxNQUFNLHVDQUF1QztBQUM3QyxNQUFNLG1CQUFtQjtBQUN6QixNQUFNLGtDQUFrQztBQUN4QyxNQUFNLHlCQUF5QjtBQUMvQixNQUFNLDhDQUE4QztBQUNwRCxNQUFNLHdDQUF3QztBQUM5QyxNQUFNLGVBQWU7QUFDckIsTUFBTSxpQkFBaUI7QUFDdkIsTUFBTSx3Q0FBd0M7QUFzQjlDLElBQUk7QUFTSixNQUFNLHVCQUF1QjtBQUFBLEVBQ1Y7QUFBQSxFQUVWLFlBQVksTUFBYztBQUMvQixTQUFLLE9BQU87QUFBQSxFQUNkO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFRQSxNQUFhLE9BQU8sU0FBNEU7QUFDOUYsU0FBSyxJQUFJLGlDQUFpQyxRQUFRLFNBQVMsWUFBWSxRQUFRLFFBQVEsR0FBRztBQUUxRixVQUFNLE1BQU0sSUFBSSxJQUFJLFFBQVEsU0FBUztBQUVyQyxVQUFNLE9BQU8sT0FBTyxJQUFJLElBQUk7QUFDNUIsUUFBSSxNQUFNLElBQUksR0FBRztBQUNmLFlBQU0sSUFBSSxNQUFNLDhCQUE4QixJQUFJLElBQUksRUFBRTtBQUFBLElBQzFEO0FBRUEs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QXFCO0FBRXJFLFNBQUssSUFBSSxrQ0FBa0MsUUFBUSxHQUFHO0FBQUEsRUFDeEQ7QUFDRjtBQVFBLGVBQXNCLDJCQUEyQixTQUFnRTtBQUMvRyxRQUFNLE9BQU8sU0FBUyxRQUFRO0FBRTlCLE1BQUksQ0FBQyxXQUFXLFFBQVEsU0FBUyxnQkFBZ0I7QUFDL0Msd0JBQUksc0JBQXNCLElBQUksZ0NBQWdDO0FBQzlELFdBQU8sSUFBSSxpREFBb0I7QUFBQSxFQUNqQztBQUVBLE1BQUksUUFBUSxTQUFTLGdCQUFnQjtBQUNuQyx3QkFBSSxzQkFBc0IsSUFBSSx3Q0FBd0MsUUFBUSxRQUFRLFdBQVcsVUFBVSxPQUFPLFFBQVEsUUFBUSxnQkFBZ0IsQ0FBQyxHQUFHO0FBQ3RKLFdBQU8sSUFBSSxpREFBb0I7QUFBQSxNQUM3QixHQUFJLFFBQVEsU0FBUyxVQUFhLEVBQUUsU0FBUyxRQUFRLEtBQUs7QUFBQSxNQUMxRCxHQUFJLFFBQVEsU0FBUyxVQUFhLEVBQUUsU0FBUyxRQUFRLEtBQUs7QUFBQSxNQUMxRCxHQUFJLFFBQVEsaUNBQWlDLFVBQWEsRUFBRSw4QkFBOEIsUUFBUSw2QkFBNkI7QUFBQSxJQUNqSSxDQUFDO0FBQUEsRUFDSDtBQUVBLFFBQU0sVUFBVSxJQUFJLHVCQUF1QixJQUFJO0FBQy9DLFNBQU8sUUFBUSxPQUFPLE9BQU87QUFDL0I7QUFZQSxlQUFzQixxQkFBcUIsU0FBZ0U7QUFDekcsTUFBSSxpQkFBaUI7QUFDbkIsV0FBTztBQUFBLEVBQ1Q7QUFFQSxRQUFNLFNBQVMsTUFBTSwyQkFBMkIsT0FBTztBQUV2RCxvQkFBa0I7QUFDbEIsU0FBTztBQUNUO0FBV0EsU0FBUyxnQkFBZ0IsT0FBMkI7QUFDbEQsTUFBSSxNQUFNLFFBQVEsUUFBVztBQUMzQjtBQUFBLEVBQ0Y7QUFFQSxNQUFJLG9CQUFBQSxRQUFRLGFBQWEsU0FBUztBQUNoQyxRQUFJO0FBQ0Ysa0RBQWEsWUFBWSxDQUFDLE1BQU0sTUFBTSxRQUFRLE9BQU8sTUFBTSxHQUFHLENBQUMsR0FBRyxFQUFFLE9BQU8sU0FBUyxDQUFDO0FBQUEsSUFDdkYsU0FBUyxPQUFnQjtBQUN2QjtBQUFBLFFBQ0Usd0NBQXdDLE9BQU8sTUFBTSxHQUFHLENBQUMsc0NBQ3ZELGlCQUFpQixRQUFRLE1BQU0sVUFBVSxlQUMzQztBQUFBLE1BQ0Y7QUFBQSxJQUNGO0FBQUEsRUFDRixPQUFPO0FBQ0wsVUFBTSxLQUFLLFNBQVM7QUFBQSxFQUN0QjtBQUNGOyIsCiAgIm5hbWVzIjogWyJraWxsQXV0b1N0YXJ0ZWRQcm9jZXNzZXMiLCAiaHR0cCIsICJwcm9jZXNzIl0KfQo=