obsidian-integration-testing 12.1.2 → 12.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +1 -5
  2. package/dist/lib/cjs/app-config.cjs +137 -0
  3. package/dist/lib/cjs/app-config.d.cts +190 -0
  4. package/dist/lib/cjs/appium-session-config.cjs +7 -1
  5. package/dist/lib/cjs/appium-session-config.d.cts +20 -0
  6. package/dist/lib/cjs/cdp-command-timeout-error.cjs +50 -0
  7. package/dist/lib/cjs/cdp-command-timeout-error.d.cts +48 -0
  8. package/dist/lib/cjs/connect-to-cdp.cjs +1 -1
  9. package/dist/lib/cjs/connect-to-cdp.d.cts +5 -0
  10. package/dist/lib/cjs/emulator-arguments.cjs +19 -3
  11. package/dist/lib/cjs/emulator-arguments.d.cts +44 -0
  12. package/dist/lib/cjs/emulator-device-id.cjs +1 -1
  13. package/dist/lib/cjs/emulator-device-id.d.cts +7 -6
  14. package/dist/lib/cjs/emulator-marker.cjs +169 -0
  15. package/dist/lib/cjs/emulator-marker.d.cts +224 -0
  16. package/dist/lib/cjs/emulator-reaper.cjs +248 -0
  17. package/dist/lib/cjs/emulator-reaper.d.cts +225 -0
  18. package/dist/lib/cjs/emulator-reclaim.cjs +312 -0
  19. package/dist/lib/cjs/emulator-reclaim.d.cts +179 -0
  20. package/dist/lib/cjs/eval-cap-exceeded-error.cjs +61 -0
  21. package/dist/lib/cjs/eval-cap-exceeded-error.d.cts +92 -0
  22. package/dist/lib/cjs/eval-cap.cjs +29 -0
  23. package/dist/lib/cjs/eval-cap.d.cts +43 -0
  24. package/dist/lib/cjs/global-setup-core.cjs +2 -3
  25. package/dist/lib/cjs/index.cjs +21 -1
  26. package/dist/lib/cjs/index.d.cts +7 -0
  27. package/dist/lib/cjs/library.cjs +1 -1
  28. package/dist/lib/cjs/poll-in-obsidian.cjs +1 -1
  29. package/dist/lib/cjs/poll-in-obsidian.d.cts +7 -2
  30. package/dist/lib/cjs/setup-lock.cjs +56 -8
  31. package/dist/lib/cjs/setup-lock.d.cts +53 -0
  32. package/dist/lib/cjs/soft-keyboard-geometry.cjs +17 -8
  33. package/dist/lib/cjs/soft-keyboard-geometry.d.cts +46 -9
  34. package/dist/lib/cjs/soft-keyboard.cjs +7 -4
  35. package/dist/lib/cjs/soft-keyboard.d.cts +16 -2
  36. package/dist/lib/cjs/transport-appium.cjs +70 -5
  37. package/dist/lib/cjs/transport-appium.d.cts +35 -0
  38. package/dist/lib/cjs/transport-desktop-cdp.cjs +98 -19
  39. package/dist/lib/cjs/transport-desktop-cdp.d.cts +44 -1
  40. package/dist/lib/cjs/transport-factory.cjs +297 -190
  41. package/dist/lib/cjs/transport-factory.d.cts +15 -0
  42. package/dist/lib/cjs/transport-options.cjs +1 -1
  43. package/dist/lib/cjs/transport-options.d.cts +38 -1
  44. package/dist/lib/cjs/webview-cdp.cjs +45 -14
  45. package/dist/lib/cjs/webview-cdp.d.cts +17 -0
  46. package/dist/lib/esm/app-config.d.mts +190 -0
  47. package/dist/lib/esm/app-config.mjs +110 -0
  48. package/dist/lib/esm/appium-session-config.d.mts +20 -0
  49. package/dist/lib/esm/appium-session-config.mjs +6 -1
  50. package/dist/lib/esm/cdp-command-timeout-error.d.mts +48 -0
  51. package/dist/lib/esm/cdp-command-timeout-error.mjs +26 -0
  52. package/dist/lib/esm/connect-to-cdp.d.mts +5 -0
  53. package/dist/lib/esm/connect-to-cdp.mjs +1 -1
  54. package/dist/lib/esm/emulator-arguments.d.mts +44 -0
  55. package/dist/lib/esm/emulator-arguments.mjs +16 -2
  56. package/dist/lib/esm/emulator-device-id.d.mts +7 -6
  57. package/dist/lib/esm/emulator-device-id.mjs +1 -1
  58. package/dist/lib/esm/emulator-marker.d.mts +224 -0
  59. package/dist/lib/esm/emulator-marker.mjs +134 -0
  60. package/dist/lib/esm/emulator-reaper.d.mts +225 -0
  61. package/dist/lib/esm/emulator-reaper.mjs +211 -0
  62. package/dist/lib/esm/emulator-reclaim.d.mts +179 -0
  63. package/dist/lib/esm/emulator-reclaim.mjs +291 -0
  64. package/dist/lib/esm/eval-cap-exceeded-error.d.mts +92 -0
  65. package/dist/lib/esm/eval-cap-exceeded-error.mjs +36 -0
  66. package/dist/lib/esm/eval-cap.d.mts +43 -0
  67. package/dist/lib/esm/eval-cap.mjs +5 -0
  68. package/dist/lib/esm/global-setup-core.mjs +6 -4
  69. package/dist/lib/esm/index.d.mts +7 -0
  70. package/dist/lib/esm/index.mjs +21 -1
  71. package/dist/lib/esm/library.mjs +1 -1
  72. package/dist/lib/esm/poll-in-obsidian.d.mts +7 -2
  73. package/dist/lib/esm/poll-in-obsidian.mjs +1 -1
  74. package/dist/lib/esm/setup-lock.d.mts +53 -0
  75. package/dist/lib/esm/setup-lock.mjs +52 -7
  76. package/dist/lib/esm/soft-keyboard-geometry.d.mts +46 -9
  77. package/dist/lib/esm/soft-keyboard-geometry.mjs +17 -8
  78. package/dist/lib/esm/soft-keyboard.d.mts +16 -2
  79. package/dist/lib/esm/soft-keyboard.mjs +7 -4
  80. package/dist/lib/esm/transport-appium.d.mts +35 -0
  81. package/dist/lib/esm/transport-appium.mjs +73 -5
  82. package/dist/lib/esm/transport-desktop-cdp.d.mts +44 -1
  83. package/dist/lib/esm/transport-desktop-cdp.mjs +98 -19
  84. package/dist/lib/esm/transport-factory.d.mts +15 -0
  85. package/dist/lib/esm/transport-factory.mjs +305 -184
  86. package/dist/lib/esm/transport-options.d.mts +38 -1
  87. package/dist/lib/esm/transport-options.mjs +1 -1
  88. package/dist/lib/esm/webview-cdp.d.mts +17 -0
  89. package/dist/lib/esm/webview-cdp.mjs +45 -14
  90. package/package.json +4 -4
@@ -0,0 +1,225 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Stops an emulator this harness started once its run is gone for good — the
5
+ * case nothing else covers, **L56**'s path B with no later run: the runner is
6
+ * killed (SIGKILL, Task Manager, an IDE stop button), no teardown runs, and no
7
+ * other Android run ever comes along to reclaim the leftover from its marker.
8
+ * On 2026-09-10 an emulator left like that kept `netsimd` spinning for six hours.
9
+ *
10
+ * The reaper is a small detached Node process spawned beside every emulator the
11
+ * harness starts or takes over. It holds no handle to the emulator and needs
12
+ * none: the marker (`emulator-marker.ts`) says what to stop, and the verified
13
+ * stop (`emulator-reclaim.ts`) is the one the run's own teardown uses.
14
+ *
15
+ * ## It watches the `android` setup lock, not the process that started it
16
+ *
17
+ * The process that starts the emulator is not always the run. A project with no
18
+ * transport global setup boots it from a **test worker** (**L56** path A), and
19
+ * Vitest ends workers during a perfectly healthy run — so a reaper tied to its
20
+ * spawner's lifetime (the **L33** liveness socket, the first idea) would stop
21
+ * the emulator mid-run, or race the next worker taking it over. The `android`
22
+ * setup lock (**L7**) is held by the run's main process for the whole run, and
23
+ * its holder being alive is exactly what "an Android run is in flight" means.
24
+ *
25
+ * So the reaper polls that lock, and once it can **take** it — no live run
26
+ * holds it — it stops the harness's leftovers while holding it, which is the
27
+ * precondition every leftover stop documents: no run can adopt an emulator that
28
+ * is mid-shutdown. That is precisely what the next run's preflight would do,
29
+ * done at the first moment that run could have done it.
30
+ *
31
+ * A dead same-host holder is abandoned at once. A live-PID holder that has gone
32
+ * silent is only abandoned after
33
+ * {@link EMULATOR_REAPER_LOCK_SILENCE_IN_MILLISECONDS}, far wider than the
34
+ * two minutes a waiting run steals on: a reaper is always waiting, so at two
35
+ * minutes a live run that blocked its event loop would lose its emulator.
36
+ *
37
+ * ## Fail-open, like the renderer watchdog
38
+ *
39
+ * A reaper is armed only when a live run holds the lock at the moment the
40
+ * emulator is recorded. A run that takes no lock — a hand-wired
41
+ * `createTransportFromOptions` — gets none, and says so, rather than a reaper
42
+ * that would read its missing lock as "the run is over" and stop an emulator
43
+ * somebody is using. A spawn that fails is logged and never fails the launch.
44
+ *
45
+ * ## How it runs
46
+ *
47
+ * `process.execPath -e <bootstrap> <this module's URL> <avdName> <startedAt>`:
48
+ * the bootstrap `import()`s this very module and calls {@link runEmulatorReaper}.
49
+ * The URL is the built `.mjs` / `.cjs` in a consumer's install, and the `.ts`
50
+ * source in this repo's own suites, which Node's type stripping runs as-is — so
51
+ * nothing this module imports may reach a module that reads the build-time
52
+ * `OBSIDIAN_METADATA` global. Its stdout/stderr go to a per-AVD log file, capped
53
+ * at {@link EMULATOR_REAPER_LOG_MAX_SIZE_IN_BYTES}, so the verdict lines the stop
54
+ * prints are on record even though no terminal is left to show them.
55
+ *
56
+ * ## It is started through a relay, so a process-tree kill cannot take it
57
+ *
58
+ * `detached` does not reparent on Windows: a child spawned straight from the run
59
+ * stays in the run's process tree, so the kill shapes that walk that tree take
60
+ * the safety net along with the thing it was guarding — Task Manager's *End
61
+ * task*, `taskkill /T`, an IDE stop button, a `ParentProcessId` descendant
62
+ * sweep. Measured on 2026-09-11: a reaper died with the run it was watching, and
63
+ * the emulator it would have stopped idled on for ten minutes.
64
+ *
65
+ * So the spawn goes through a relay — {@link buildEmulatorReaperRelayBootstrap} —
66
+ * which starts the reaper and exits at once. By the time any tree-walker takes
67
+ * its snapshot the relay is gone, so there is no edge from the run to the reaper
68
+ * left to follow. This is the double-fork idiom, and it holds on POSIX for the
69
+ * same reason: once the relay exits, the reaper belongs to `init`.
70
+ */
71
+ import type { EmulatorMarker } from './emulator-marker.cjs';
72
+ /**
73
+ * How often the reaper looks at the marker and the lock — the lock's own
74
+ * heartbeat interval, so a killed run is noticed within one beat.
75
+ */
76
+ export declare const EMULATOR_REAPER_POLL_INTERVAL_IN_MILLISECONDS = 5000;
77
+ /**
78
+ * How long a same-host lock holder whose PID is still alive may stay silent
79
+ * before the reaper treats its run as gone: 30 minutes, the threshold the lock
80
+ * already uses across hosts. Only a recycled PID should ever reach it.
81
+ */
82
+ export declare const EMULATOR_REAPER_LOCK_SILENCE_IN_MILLISECONDS = 1800000;
83
+ /**
84
+ * The size past which the reaper log is started afresh rather than appended to.
85
+ */
86
+ export declare const EMULATOR_REAPER_LOG_MAX_SIZE_IN_BYTES = 1048576;
87
+ /**
88
+ * The export the bootstrap calls. Named here so the bootstrap and the export
89
+ * cannot drift apart.
90
+ */
91
+ export declare const EMULATOR_REAPER_ENTRY_NAME = "runEmulatorReaper";
92
+ /**
93
+ * What the reaper watches for: the emulator a marker recorded when it was armed.
94
+ */
95
+ export interface EmulatorReaperArguments {
96
+ /**
97
+ The AVD whose marker it watches.
98
+ */
99
+ readonly avdName: string;
100
+ /**
101
+ * When the watched emulator was launched. A takeover keeps it, so it names one
102
+ * emulator across every run that owns it, and tells a later emulator of the
103
+ * same AVD apart.
104
+ */
105
+ readonly startedAtInMilliseconds: number;
106
+ }
107
+ /**
108
+ * What the marker says the reaper should do next.
109
+ *
110
+ * - `emulator-stopped` — the marker is gone, or none of its PIDs is alive: the
111
+ * emulator was stopped, so there is nothing left to watch.
112
+ * - `superseded` — the marker now records a different emulator of the same AVD,
113
+ * which has its own reaper.
114
+ * - `watch` — the emulator is still up.
115
+ */
116
+ export type EmulatorReaperWatchVerdict = 'emulator-stopped' | 'superseded' | 'watch';
117
+ /**
118
+ * Parameters for {@link resolveEmulatorReaperWatch}.
119
+ */
120
+ export interface ResolveEmulatorReaperWatchParams {
121
+ /**
122
+ The launch time of the emulator this reaper was armed for.
123
+ */
124
+ readonly armedStartedAtInMilliseconds: number;
125
+ /**
126
+ Whether a PID is still running.
127
+ */
128
+ readonly checkIsPidAlive: (pid: number) => boolean;
129
+ /**
130
+ The AVD's marker as it reads now, if any.
131
+ */
132
+ readonly marker: EmulatorMarker | undefined;
133
+ }
134
+ /**
135
+ * Parameters for {@link spawnEmulatorReaper}.
136
+ */
137
+ export interface SpawnEmulatorReaperParams {
138
+ /**
139
+ The AVD whose emulator was just recorded.
140
+ */
141
+ readonly avdName: string;
142
+ /**
143
+ Receives the armed / not-armed line.
144
+ */
145
+ readonly log: (message: string) => void;
146
+ /**
147
+ The recorded emulator's launch time, as written into its marker.
148
+ */
149
+ readonly startedAtInMilliseconds: number;
150
+ }
151
+ /**
152
+ * Encodes the reaper's arguments for its command line.
153
+ *
154
+ * @param reaperArguments - What the reaper watches for.
155
+ * @returns The command-line arguments, in the order {@link parseEmulatorReaperArguments} reads them.
156
+ */
157
+ export declare function buildEmulatorReaperArguments(reaperArguments: EmulatorReaperArguments): string[];
158
+ /**
159
+ * Builds the script `node -e` runs to start the reaper.
160
+ *
161
+ * It `import()`s the module whose URL is its first argument and hands the rest
162
+ * to {@link runEmulatorReaper}. `import()` rather than `require`, because it
163
+ * loads all three forms the module ships as — ESM, CJS, and this repo's own
164
+ * `.ts` source under Node's type stripping. A CJS module's named exports are
165
+ * found by Node's static analysis; `default` is the fallback when they are not.
166
+ *
167
+ * @returns The script.
168
+ */
169
+ export declare function buildEmulatorReaperBootstrap(): string;
170
+ /**
171
+ * Builds the script `node -e` runs as the **relay**: it starts the reaper and
172
+ * exits at once, so the reaper is left with a dead parent and no tree-walking
173
+ * kill of the run can reach it. See the relay section in this module's header
174
+ * for why that indirection exists at all.
175
+ *
176
+ * Its own arguments are the reaper's whole command line — the bootstrap script
177
+ * first, then everything that bootstrap reads — so the relay never has to know
178
+ * what any of them mean. It hands the reaper its own stdout and stderr, which
179
+ * are the capped log file the spawner opened, so the log is unaffected by the
180
+ * extra hop.
181
+ *
182
+ * @returns The script.
183
+ */
184
+ export declare function buildEmulatorReaperRelayBootstrap(): string;
185
+ /**
186
+ * Decodes the reaper's command-line arguments.
187
+ *
188
+ * @param argv - The arguments after the module URL.
189
+ * @returns The arguments, or `undefined` when they are not the two {@link buildEmulatorReaperArguments} writes.
190
+ */
191
+ export declare function parseEmulatorReaperArguments(argv: readonly string[]): EmulatorReaperArguments | undefined;
192
+ /**
193
+ * Decides how the reaper log is opened, so it can never grow without bound.
194
+ *
195
+ * @param sizeInBytes - The log's current size, or `undefined` when there is none.
196
+ * @returns `'a'` to append, or `'w'` to start it afresh once it has passed the cap.
197
+ */
198
+ export declare function resolveEmulatorReaperLogOpenFlag(sizeInBytes: number | undefined): 'a' | 'w';
199
+ /**
200
+ * Decides what the marker says the reaper should do next.
201
+ *
202
+ * @param params - The marker as it reads now, and the emulator the reaper was armed for.
203
+ * @returns The verdict.
204
+ */
205
+ export declare function resolveEmulatorReaperWatch(params: ResolveEmulatorReaperWatchParams): EmulatorReaperWatchVerdict;
206
+ /**
207
+ * The reaper process's body: watches the marker and the lock until either the
208
+ * emulator is gone or no run holds the lock, and in the second case stops the
209
+ * harness's leftovers while holding it.
210
+ *
211
+ * Exported for the bootstrap, which is its only caller.
212
+ *
213
+ * @param argv - The reaper's arguments, as {@link buildEmulatorReaperArguments} wrote them.
214
+ */
215
+ export declare function runEmulatorReaper(argv: readonly string[]): Promise<void>;
216
+ /**
217
+ * Starts the reaper for an emulator the harness just recorded as its own, when
218
+ * a live run holds the `android` setup lock — and says which it did.
219
+ *
220
+ * Never throws: a reaper that cannot be spawned is a lost safety net, not a
221
+ * reason to fail the launch it would have guarded.
222
+ *
223
+ * @param params - The recorded emulator, and where to report.
224
+ */
225
+ export declare function spawnEmulatorReaper(params: SpawnEmulatorReaperParams): void;
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var emulator_reclaim_exports = {};
30
+ __export(emulator_reclaim_exports, {
31
+ ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS: () => ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS,
32
+ ADB_DUMPSYS_MAX_BUFFER_IN_BYTES: () => ADB_DUMPSYS_MAX_BUFFER_IN_BYTES,
33
+ EmulatorReclaimer: () => EmulatorReclaimer,
34
+ HOST_PROCESS_QUERY_TIMEOUT_IN_MILLISECONDS: () => HOST_PROCESS_QUERY_TIMEOUT_IN_MILLISECONDS
35
+ });
36
+ module.exports = __toCommonJS(emulator_reclaim_exports);
37
+ var import_node_child_process = require("node:child_process");
38
+ var import_node_process = __toESM(require("node:process"), 1);
39
+ var import_adb_device_list = require("./adb-device-list.cjs");
40
+ var import_emulator_backend = require("./emulator-backend.cjs");
41
+ var import_emulator_marker = require("./emulator-marker.cjs");
42
+ var import_kill_process_tree = require("./kill-process-tree.cjs");
43
+ var import_process_liveness = require("./process-liveness.cjs");
44
+ var import_teardown_verdict = require("./teardown-verdict.cjs");
45
+ const ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS = 5e3;
46
+ const ADB_DUMPSYS_MAX_BUFFER_IN_BYTES = 8388608;
47
+ const HOST_PROCESS_QUERY_TIMEOUT_IN_MILLISECONDS = 3e4;
48
+ const EMULATOR_ESCALATED_STOP_TIMEOUT_IN_MILLISECONDS = 5e3;
49
+ const EMULATOR_STOP_POLL_INTERVAL_IN_MILLISECONDS = 500;
50
+ const EMULATOR_STOP_TIMEOUT_IN_MILLISECONDS = 2e4;
51
+ const MILLISECONDS_PER_SECOND = 1e3;
52
+ class EmulatorReclaimer {
53
+ log;
54
+ /**
55
+ * Creates a reclaimer that reports under the caller's own log prefix.
56
+ *
57
+ * @param log - Receives every progress and verdict line.
58
+ */
59
+ constructor(log) {
60
+ this.log = log;
61
+ }
62
+ /**
63
+ * Runs `adb devices` and returns its raw stdout.
64
+ *
65
+ * The raw listing is what teardown needs: `getConnectedDeviceIds` keeps only
66
+ * the `device` state, and an emulator on its way out answers `offline` while
67
+ * still holding the AVD — see `adb-device-list.ts`.
68
+ *
69
+ * @returns The raw `adb devices` output.
70
+ * @throws If adb could not be run at all.
71
+ */
72
+ getDevicesOutput() {
73
+ return new Promise((resolve, reject) => {
74
+ (0, import_node_child_process.execFile)("adb", ["devices"], { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS }, (error, stdout, stderr) => {
75
+ if (error) {
76
+ reject(new Error(`Failed to run 'adb devices': ${error.message}. Is ADB installed and in PATH?`));
77
+ return;
78
+ }
79
+ if (stderr) {
80
+ this.log(`ADB stderr: ${stderr.trim()}`);
81
+ }
82
+ resolve(stdout);
83
+ });
84
+ });
85
+ }
86
+ /**
87
+ * Lists every process on the host.
88
+ *
89
+ * A host always has processes, so a listing that parses to **zero** rows is a
90
+ * failed query however it exited, and comes back as `undefined` exactly like
91
+ * one that did not run — never as an empty list a caller could read as "no
92
+ * emulator is running".
93
+ *
94
+ * @returns The host's processes, or `undefined` when the listing failed.
95
+ */
96
+ async queryHostProcesses() {
97
+ const query = buildHostProcessQuery();
98
+ const output = await new Promise((resolve) => {
99
+ (0, import_node_child_process.execFile)(
100
+ query.command,
101
+ query.commandArguments,
102
+ { maxBuffer: ADB_DUMPSYS_MAX_BUFFER_IN_BYTES, timeout: HOST_PROCESS_QUERY_TIMEOUT_IN_MILLISECONDS },
103
+ (error, stdout) => {
104
+ if (error) {
105
+ this.log(`Warning: could not list host processes (\`${query.command}\`): ${error.message}`);
106
+ }
107
+ resolve(error ? "" : stdout);
108
+ }
109
+ );
110
+ });
111
+ const processes = parseHostProcessList(output);
112
+ if (processes.length === 0) {
113
+ this.log(`Warning: \`${query.command}\` listed no processes.`);
114
+ return void 0;
115
+ }
116
+ return processes;
117
+ }
118
+ /**
119
+ * Stops the emulators this harness started that no live run is responsible
120
+ * for, and drops the markers that no longer describe a running emulator.
121
+ *
122
+ * Only ever acts on a **marker-verified** emulator: one whose marker names a
123
+ * PID that is still a live emulator process. An emulator without a marker —
124
+ * booted by hand, by CI, or by another tool — is never touched, which keeps
125
+ * the L46 line: never a `qemu*` sweep.
126
+ *
127
+ * Callers must hold the `android` setup lock (L7), which is what makes a
128
+ * leftover safe to stop: no other Android run can be mid-flight on it.
129
+ *
130
+ * @param params - Which AVD to leave alone, and whether this run is ending.
131
+ */
132
+ async reclaimLeftoverEmulators(params) {
133
+ const markers = (0, import_emulator_marker.listEmulatorMarkers)().filter((marker) => marker.avdName !== params.exceptAvdName);
134
+ if (markers.length === 0) {
135
+ return;
136
+ }
137
+ const processes = await this.queryHostProcesses();
138
+ if (processes === void 0) {
139
+ this.log(`Cannot judge ${String(markers.length)} emulator marker(s) without a host process listing; leaving them for a later run.`);
140
+ return;
141
+ }
142
+ const liveEmulatorPids = (0, import_emulator_backend.selectEmulatorBackendPids)({ knownPids: [], processes });
143
+ for (const marker of markers) {
144
+ await this.reclaimLeftoverEmulator(marker, liveEmulatorPids, params.scope);
145
+ }
146
+ }
147
+ /**
148
+ * Stops an emulator this harness owns, and **verifies** it stopped.
149
+ *
150
+ * The console shutdown goes first because it is the only path that releases
151
+ * the AVD's `multiinstance.lock`; a `taskkill` leaves the lock behind, and a
152
+ * stale lock is what makes the next run fail with `Running multiple emulators
153
+ * with the same AVD` — a FATAL the emulator writes to its own stdout, where
154
+ * nobody sees it.
155
+ *
156
+ * Works without a launcher handle too: a leftover this run took over has only
157
+ * its PIDs, which is all the escalation below ever needed. The marker goes
158
+ * only with a **verified** stop, so an emulator that outlived this attempt
159
+ * stays convictable by the next one.
160
+ *
161
+ * @param params - The emulator process, the device it serves and the PIDs this run owns.
162
+ */
163
+ async stopEmulator(params) {
164
+ if (params.deviceId !== void 0) {
165
+ await this.killEmulatorConsole(params.deviceId);
166
+ }
167
+ if (params.emulatorProcess) {
168
+ (0, import_kill_process_tree.killProcessTree)(params.emulatorProcess);
169
+ }
170
+ const waitParams = {
171
+ deviceId: params.deviceId,
172
+ ownedEmulatorPids: params.ownedEmulatorPids,
173
+ timeoutInMilliseconds: EMULATOR_STOP_TIMEOUT_IN_MILLISECONDS
174
+ };
175
+ let hasEscalated = false;
176
+ let isStopped = await this.waitForEmulatorStopped(waitParams);
177
+ if (!isStopped) {
178
+ const survivingPids = params.ownedEmulatorPids.filter((pid) => (0, import_process_liveness.checkIsProcessAlive)(pid));
179
+ if (survivingPids.length > 0) {
180
+ hasEscalated = true;
181
+ this.log(
182
+ `Auto-started emulator outlived its console shutdown and the launcher's process tree; escalating to the emulator PID(s) this run owns: [${survivingPids.join(", ")}].`
183
+ );
184
+ for (const pid of survivingPids) {
185
+ (0, import_kill_process_tree.killProcessTreeByPid)(pid);
186
+ }
187
+ isStopped = await this.waitForEmulatorStopped({ ...waitParams, timeoutInMilliseconds: EMULATOR_ESCALATED_STOP_TIMEOUT_IN_MILLISECONDS });
188
+ }
189
+ }
190
+ this.log((0, import_teardown_verdict.buildTeardownMessage)({
191
+ evidence: params.deviceId === void 0 ? `AVD "${params.avdName}"` : `AVD "${params.avdName}" on device ${params.deviceId}`,
192
+ outcome: (0, import_teardown_verdict.resolveTeardownOutcome)({ hasEscalated, isStopped }),
193
+ subject: "Auto-started emulator",
194
+ timeoutInMilliseconds: EMULATOR_STOP_TIMEOUT_IN_MILLISECONDS
195
+ }));
196
+ if (isStopped) {
197
+ (0, import_emulator_marker.clearEmulatorMarkerIfStopped)(params.avdName);
198
+ }
199
+ }
200
+ /**
201
+ * Decides whether the emulator this run started is really gone.
202
+ *
203
+ * Two independent proofs, cheapest first: none of the PIDs this run owns is
204
+ * alive, and the device no longer appears in `adb devices` **in any state** (a
205
+ * dying emulator answers `offline` while it still holds the AVD).
206
+ *
207
+ * @param params - The device and the PIDs this run owns.
208
+ * @returns `true` when nothing of this run's emulator is left.
209
+ */
210
+ async checkIsEmulatorGone(params) {
211
+ if (params.ownedEmulatorPids.some((pid) => (0, import_process_liveness.checkIsProcessAlive)(pid))) {
212
+ return false;
213
+ }
214
+ if (params.deviceId === void 0) {
215
+ return true;
216
+ }
217
+ try {
218
+ return !(0, import_adb_device_list.checkIsDeviceListed)({ deviceId: params.deviceId, devicesOutput: await this.getDevicesOutput() });
219
+ } catch (error) {
220
+ this.log(`Could not re-check connected devices while stopping the emulator: ${error instanceof Error ? error.message : String(error)}`);
221
+ return true;
222
+ }
223
+ }
224
+ /**
225
+ * Asks the emulator to shut itself down over its console.
226
+ *
227
+ * Preferred over killing the process outright because it is the path that
228
+ * releases the AVD's `multiinstance.lock`. Best-effort: a console that does
229
+ * not answer is reported and the caller falls through to the kill.
230
+ *
231
+ * @param deviceId - The emulator's device ID.
232
+ */
233
+ killEmulatorConsole(deviceId) {
234
+ return new Promise((resolve) => {
235
+ (0, import_node_child_process.execFile)("adb", ["-s", deviceId, "emu", "kill"], { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS }, (error, stdout) => {
236
+ this.log(
237
+ error ? `Emulator console shutdown for ${deviceId} did not answer: ${error.message}` : `Emulator console shutdown for ${deviceId}: ${stdout.trim()}`
238
+ );
239
+ resolve();
240
+ });
241
+ });
242
+ }
243
+ /**
244
+ * Acts on one emulator marker for {@link reclaimLeftoverEmulators}.
245
+ *
246
+ * @param marker - The marker to judge.
247
+ * @param liveEmulatorPids - The emulator processes currently running on the host.
248
+ * @param scope - Whether an emulator another live harness process owns is spared.
249
+ */
250
+ async reclaimLeftoverEmulator(marker, liveEmulatorPids, scope) {
251
+ const verdict = (0, import_emulator_marker.resolveEmulatorMarkerVerdict)({
252
+ currentPid: import_node_process.default.pid,
253
+ isOwnerAlive: (0, import_process_liveness.checkIsProcessAlive)(marker.ownerPid),
254
+ liveEmulatorPids,
255
+ marker
256
+ });
257
+ if (verdict === "stale-marker") {
258
+ this.log(`Dropping the emulator marker for AVD "${marker.avdName}": none of its PIDs [${marker.ownedEmulatorPids.join(", ")}] is a running emulator any more.`);
259
+ (0, import_emulator_marker.clearEmulatorMarker)(marker.avdName);
260
+ return;
261
+ }
262
+ const evidence = describeMarkedEmulator(marker);
263
+ if (verdict === "in-use-by-live-run" && scope === "preflight") {
264
+ this.log(`Leaving the emulator for ${evidence} alone: the harness process that owns it (PID ${String(marker.ownerPid)}) is still running.`);
265
+ return;
266
+ }
267
+ const ageInSeconds = Math.round((Date.now() - marker.startedAtInMilliseconds) / MILLISECONDS_PER_SECOND);
268
+ this.log(
269
+ `Stopping the emulator for ${evidence}: this harness started it ${String(ageInSeconds)}s ago, and no live run is left to stop it.`
270
+ );
271
+ await this.stopEmulator({
272
+ avdName: marker.avdName,
273
+ deviceId: marker.deviceId,
274
+ ownedEmulatorPids: (0, import_emulator_marker.selectLiveMarkedPids)(marker, liveEmulatorPids)
275
+ });
276
+ }
277
+ /**
278
+ * Polls until this run's emulator is gone, or the budget elapses.
279
+ *
280
+ * @param params - The device, the PIDs this run owns, and the budget.
281
+ * @returns `true` when the emulator disappeared within the budget.
282
+ */
283
+ async waitForEmulatorStopped(params) {
284
+ const deadline = Date.now() + params.timeoutInMilliseconds;
285
+ while (Date.now() < deadline) {
286
+ if (await this.checkIsEmulatorGone(params)) {
287
+ return true;
288
+ }
289
+ await new Promise((resolve) => {
290
+ setTimeout(resolve, EMULATOR_STOP_POLL_INTERVAL_IN_MILLISECONDS);
291
+ });
292
+ }
293
+ return false;
294
+ }
295
+ }
296
+ function buildHostProcessQuery() {
297
+ return import_node_process.default.platform === "win32" ? { command: "tasklist", commandArguments: ["/FO", "CSV", "/NH"] } : { command: "ps", commandArguments: ["-eo", "pid=,comm="] };
298
+ }
299
+ function describeMarkedEmulator(marker) {
300
+ return marker.deviceId === void 0 ? `AVD "${marker.avdName}" (no device: it was still booting when it was recorded)` : `AVD "${marker.avdName}" on device ${marker.deviceId}`;
301
+ }
302
+ function parseHostProcessList(output) {
303
+ return import_node_process.default.platform === "win32" ? (0, import_emulator_backend.parseWindowsTaskList)(output) : (0, import_emulator_backend.parsePosixProcessList)(output);
304
+ }
305
+ // Annotate the CommonJS export names for ESM import in node:
306
+ 0 && (module.exports = {
307
+ ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS,
308
+ ADB_DUMPSYS_MAX_BUFFER_IN_BYTES,
309
+ EmulatorReclaimer,
310
+ HOST_PROCESS_QUERY_TIMEOUT_IN_MILLISECONDS
311
+ });
312
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2VtdWxhdG9yLXJlY2xhaW0udHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qKlxuICogQGZpbGVcbiAqXG4gKiBTdG9wcyB0aGUgZW11bGF0b3JzIHRoaXMgaGFybmVzcyBzdGFydGVkLCBhbmQgKip2ZXJpZmllcyoqIGVhY2ggc3RvcCBcdTIwMTQgdGhlXG4gKiBtYXJrZXItZHJpdmVuIGhhbGYgb2YgZW11bGF0b3Igb3duZXJzaGlwIChgZW11bGF0b3ItbWFya2VyLnRzYCwgKipMNTYqKikuXG4gKlxuICogSXQgbGl2ZXMgYXBhcnQgZnJvbSBgdHJhbnNwb3J0LWZhY3RvcnkudHNgIHNvIHRoYXQgYSBwcm9jZXNzIHdpdGggbm8gYnVzaW5lc3NcbiAqIGxvYWRpbmcgdGhlIHdob2xlIHRyYW5zcG9ydCBzdGFjayBjYW4gc3RpbGwgc3RvcCBhbiBlbXVsYXRvciBjb3JyZWN0bHkuIFRoYXRcbiAqIHByb2Nlc3MgaXMgdGhlIGVtdWxhdG9yIHJlYXBlciAoYGVtdWxhdG9yLXJlYXBlci50c2ApOiBpdCBydW5zIGRldGFjaGVkIGZyb21cbiAqIGFueSB0ZXN0IHJ1bm5lciwgYW5kIGluIHRoaXMgcmVwbydzIG93biBzdWl0ZXMgaXQgcnVucyBzdHJhaWdodCBmcm9tIHNvdXJjZVxuICogdW5kZXIgTm9kZSdzIHR5cGUgc3RyaXBwaW5nLCB3aGVyZSBhIG1vZHVsZSByZWFkaW5nIHRoZSBidWlsZC10aW1lXG4gKiBgT0JTSURJQU5fTUVUQURBVEFgIGdsb2JhbCBcdTIwMTQgd2hpY2ggdGhlIGZhY3RvcnkncyBpbXBvcnQgZ3JhcGggcmVhY2hlcyBcdTIwMTRcbiAqIGNhbm5vdCBsb2FkIGF0IGFsbC4gU28gbm90aGluZyB0aGlzIG1vZHVsZSBpbXBvcnRzIG1heSByZWFjaCBpdCBlaXRoZXIuXG4gKi9cblxuLyogdjggaWdub3JlIHN0YXJ0IC0tIEludGVncmF0aW9uLXRpbWUgZW11bGF0b3IgbWFuYWdlbWVudCBjb3ZlcmVkIGJ5IHRoZSBBbmRyb2lkIGludGVncmF0aW9uIHN1aXRlLCBub3QgdW5pdCB0ZXN0cy4gKi9cblxuaW1wb3J0IHR5cGUgeyBDaGlsZFByb2Nlc3MgfSBmcm9tICdub2RlOmNoaWxkX3Byb2Nlc3MnO1xuXG5pbXBvcnQgeyBleGVjRmlsZSB9IGZyb20gJ25vZGU6Y2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgcHJvY2VzcyBmcm9tICdub2RlOnByb2Nlc3MnO1xuXG5pbXBvcnQgdHlwZSB7IFByb2Nlc3NMaXN0RW50cnkgfSBmcm9tICcuL2VtdWxhdG9yLWJhY2tlbmQuY2pzJztcbmltcG9ydCB0eXBlIHsgRW11bGF0b3JNYXJrZXIgfSBmcm9tICcuL2VtdWxhdG9yLW1hcmtlci5janMnO1xuXG5pbXBvcnQgeyBjaGVja0lzRGV2aWNlTGlzdGVkIH0gZnJvbSAnLi9hZGItZGV2aWNlLWxpc3QuY2pzJztcbmltcG9ydCB7XG4gIHBhcnNlUG9zaXhQcm9jZXNzTGlzdCxcbiAgcGFyc2VXaW5kb3dzVGFza0xpc3QsXG4gIHNlbGVjdEVtdWxhdG9yQmFja2VuZFBpZHNcbn0gZnJvbSAnLi9lbXVsYXRvci1iYWNrZW5kLmNqcyc7XG5pbXBvcnQge1xuICBjbGVhckVtdWxhdG9yTWFya2VyLFxuICBjbGVhckVtdWxhdG9yTWFya2VySWZTdG9wcGVkLFxuICBsaXN0RW11bGF0b3JNYXJrZXJzLFxuICByZXNvbHZlRW11bGF0b3JNYXJrZXJWZXJkaWN0LFxuICBzZWxlY3RMaXZlTWFya2VkUGlkc1xufSBmcm9tICcuL2VtdWxhdG9yLW1hcmtlci5janMnO1xuaW1wb3J0IHtcbiAga2lsbFByb2Nlc3NUcmVlLFxuICBraWxsUHJvY2Vzc1RyZWVCeVBpZFxufSBmcm9tICcuL2tpbGwtcHJvY2Vzcy10cmVlLmNqcyc7XG5pbXBvcnQgeyBjaGVja0lzUHJvY2Vzc0FsaXZlIH0gZnJvbSAnLi9wcm9jZXNzLWxpdmVuZXNzLmNqcyc7XG5pbXBvcnQge1xuICBidWlsZFRlYXJkb3duTWVzc2FnZSxcbiAgcmVzb2x2ZVRlYXJkb3duT3V0Y29tZVxufSBmcm9tICcuL3RlYXJkb3duLXZlcmRpY3QuY2pzJztcblxuLyoqXG4gKiBCdWRnZXQgZm9yIG9uZSBxdWljayBgYWRiYCBjYWxsIFx1MjAxNCBgYWRiIGRldmljZXNgLCBhIGNvbnNvbGUgY29tbWFuZC5cbiAqL1xuZXhwb3J0IGNvbnN0IEFEQl9ERVZJQ0VfQ0hFQ0tfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSA1MDAwO1xuXG4vKlxuICogYGR1bXBzeXMgY29ubmVjdGl2aXR5YCBpcyBmYXIgbGFyZ2VyIHRoYW4gdGhlIG90aGVyIHByb2Jlcycgb3V0cHV0LCBhbmQgTm9kZSdzXG4gKiBkZWZhdWx0IDEgTWlCIGBtYXhCdWZmZXJgIHdvdWxkIGZhaWwgdGhlIGNhbGwgcmF0aGVyIHRoYW4gdHJ1bmNhdGUgaXQgXHUyMDE0IHdoaWNoXG4gKiB3b3VsZCByZWFkIGFzIFwibm90IHJlYWR5XCIgZm9yZXZlciBhbmQgYnVybiB0aGUgd2hvbGUgbmV0d29yayBidWRnZXQuIEEgaG9zdFxuICogcHJvY2VzcyBsaXN0aW5nIHNoYXJlcyB0aGUgc2FtZSBjZWlsaW5nLlxuICovXG5leHBvcnQgY29uc3QgQURCX0RVTVBTWVNfTUFYX0JVRkZFUl9JTl9CWVRFUyA9IDhfMzg4XzYwODtcblxuLypcbiAqIDMwcywgbm90IHRoZSAxMHMgYSBgdGFza2xpc3RgIGNvc3RzIG9uIGFuIGlkbGUgaG9zdDogdGhpcyBxdWVyeSBydW5zIGluIHRoZVxuICogc2FtZSBwb3N0LWJvb3QgY29udGVudGlvbiB3aW5kb3cgdGhhdCBpbmZsYXRlcyBldmVyeSBgYWRiYCByb3VuZC10cmlwIDI1LTUweFxuICogKEw0NSksIGFuZCB0aGUgZmlyc3QgZW5kLXRvLWVuZCBydW4gb3ZlcnJhbiBhIDEwcyBidWRnZXQgdGhlcmUgXHUyMDE0IHNpbGVudGx5XG4gKiBkaXNhcm1pbmcgdGhlIHRlYXJkb3duIGVzY2FsYXRpb24uIFNpemVkIGxpa2UgYEFEQl9EVU1QU1lTX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTYCxcbiAqIGZvciB0aGUgc2FtZSByZWFzb24uXG4gKi9cbmV4cG9ydCBjb25zdCBIT1NUX1BST0NFU1NfUVVFUllfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSAzMF8wMDA7XG5cbmNvbnN0IEVNVUxBVE9SX0VTQ0FMQVRFRF9TVE9QX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gNTAwMDtcbmNvbnN0IEVNVUxBVE9SX1NUT1BfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMgPSA1MDA7XG5jb25zdCBFTVVMQVRPUl9TVE9QX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gMjBfMDAwO1xuY29uc3QgTUlMTElTRUNPTkRTX1BFUl9TRUNPTkQgPSAxMDAwO1xuXG4vKipcbiAqIEEgaG9zdCBjb21tYW5kIGFuZCBpdHMgYXJndW1lbnRzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEhvc3RDb21tYW5kUXVlcnkge1xuICAvKipcbiAgVGhlIGV4ZWN1dGFibGUgdG8gcnVuLlxuICAgKi9cbiAgcmVhZG9ubHkgY29tbWFuZDogc3RyaW5nO1xuXG4gIC8qKlxuICBUaGUgY29tbWFuZCdzIGFyZ3VtZW50cy5cbiAgICovXG4gIHJlYWRvbmx5IGNvbW1hbmRBcmd1bWVudHM6IHN0cmluZ1tdO1xufVxuXG4vKipcbiAqIFBhcmFtZXRlcnMgZm9yIHtAbGluayBFbXVsYXRvclJlY2xhaW1lci5yZWNsYWltTGVmdG92ZXJFbXVsYXRvcnN9LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlY2xhaW1MZWZ0b3ZlckVtdWxhdG9yc1BhcmFtcyB7XG4gIC8qKlxuICBBbiBBVkQgd2hvc2UgbGVmdG92ZXIgaXMgbGVmdCBhbG9uZSBcdTIwMTQgdGhlIG9uZSBhIHByZWZsaWdodCBpcyBhYm91dCB0byBhZG9wdCBpbnN0ZWFkLlxuICAgKi9cbiAgcmVhZG9ubHkgZXhjZXB0QXZkTmFtZT86IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgICogYCdwcmVmbGlnaHQnYCBzcGFyZXMgYW4gZW11bGF0b3IgYW5vdGhlciBsaXZlIGhhcm5lc3MgcHJvY2VzcyBzdGlsbCBvd25zLlxuICAgKiBgJ2VuZC1vZi1ydW4nYCBkb2VzIG5vdDogdGhlIGNhbGxlciBob2xkcyB0aGUgYGFuZHJvaWRgIGxvY2sgd2l0aCBubyBydW4gaW5cbiAgICogZmxpZ2h0IFx1MjAxNCB0aGUgcnVuJ3Mgb3duIGdsb2JhbCB0ZWFyZG93biwgb3IgdGhlIGVtdWxhdG9yIHJlYXBlciBhZnRlciB0aGVcbiAgICogcnVuIGlzIGdvbmUgXHUyMDE0IHNvIHRoYXQgb3duZXIgY2FuIG9ubHkgYmUgb25lIG9mIHRoZSBmaW5pc2hlZCBydW4nc1xuICAgKiBwcm9jZXNzZXMsIHdoaWNoIG5vIGxvbmdlciBoYXMgYSB0dXJuIHRvIHN0b3AgaXQuXG4gICAqL1xuICByZWFkb25seSBzY29wZTogJ2VuZC1vZi1ydW4nIHwgJ3ByZWZsaWdodCc7XG59XG5cbi8qKlxuICogUGFyYW1ldGVycyBmb3Ige0BsaW5rIEVtdWxhdG9yUmVjbGFpbWVyLnN0b3BFbXVsYXRvcn0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3RvcEVtdWxhdG9yUGFyYW1zIHtcbiAgLyoqXG4gIFRoZSBBVkQgbmFtZSwgbmFtZWQgaW4gdGhlIHdhcm5pbmcgc28gdGhlIGxlZnRvdmVyIGlzIGlkZW50aWZpYWJsZSwgYW5kIHRoZSBrZXkgb2YgaXRzIG1hcmtlci5cbiAgICovXG4gIHJlYWRvbmx5IGF2ZE5hbWU6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIGRldmljZSB0aGUgZW11bGF0b3IgaXMgc2VydmluZywgc2h1dCBkb3duIG92ZXIgaXRzIGNvbnNvbGUgYW5kIHBvbGxlZFxuICAgKiB0byBkZWNpZGUgd2hldGhlciBpdCBhY3R1YWxseSBzdG9wcGVkLiBgdW5kZWZpbmVkYCB3aGVuIHRoZSBlbXVsYXRvciBmYWlsZWRcbiAgICogYmVmb3JlIGFueSBkZXZpY2UgYXBwZWFyZWQuXG4gICAqL1xuICByZWFkb25seSBkZXZpY2VJZD86IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgICogVGhlIGVtdWxhdG9yIGxhdW5jaGVyIHByb2Nlc3MgdGhpcyBydW4gc3Bhd25lZC4gYHVuZGVmaW5lZGAgZm9yIGEgbGVmdG92ZXJcbiAgICogdGhpcyBydW4gdG9vayBvdmVyLCB3aG9zZSBsYXVuY2hlciBiZWxvbmdlZCB0byBhbm90aGVyIHByb2Nlc3MuXG4gICAqL1xuICByZWFkb25seSBlbXVsYXRvclByb2Nlc3M/OiBDaGlsZFByb2Nlc3MgfCB1bmRlZmluZWQ7XG5cbiAgLyoqXG4gIFRoZSBlbXVsYXRvciBQSURzIHRoaXMgcnVuIG93bnMsIGVzY2FsYXRlZCB0byB3aGVuIHRoZSBjb25zb2xlIGFuZCB0aGUgbGF1bmNoZXIncyB0cmVlIGtpbGwgbGVhdmUgb25lIGJlaGluZC5cbiAgICovXG4gIHJlYWRvbmx5IG93bmVkRW11bGF0b3JQaWRzOiByZWFkb25seSBudW1iZXJbXTtcbn1cblxuaW50ZXJmYWNlIENoZWNrSXNFbXVsYXRvckdvbmVQYXJhbXMge1xuICAvKipcbiAgVGhlIGRldmljZSB0aGUgZW11bGF0b3Igc2VydmVzLCBvciBgdW5kZWZpbmVkYCB3aGVuIG5vbmUgZXZlciBhcHBlYXJlZCBcdTIwMTQgdGhlIFBJRHMgYXJlIHRoZW4gdGhlIG9ubHkgcHJvb2YuXG4gICAqL1xuICByZWFkb25seSBkZXZpY2VJZD86IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgVGhlIGVtdWxhdG9yIFBJRHMgdGhpcyBydW4gb3ducy5cbiAgICovXG4gIHJlYWRvbmx5IG93bmVkRW11bGF0b3JQaWRzOiByZWFkb25seSBudW1iZXJbXTtcbn1cblxuaW50ZXJmYWNlIFdhaXRGb3JFbXVsYXRvclN0b3BwZWRQYXJhbXMge1xuICAvKipcbiAgVGhlIGRldmljZSB0aGUgZW11bGF0b3Igc2VydmVzLCBvciBgdW5kZWZpbmVkYCB3aGVuIG5vbmUgZXZlciBhcHBlYXJlZC5cbiAgICovXG4gIHJlYWRvbmx5IGRldmljZUlkPzogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4gIC8qKlxuICBUaGUgZW11bGF0b3IgUElEcyB0aGlzIHJ1biBvd25zLlxuICAgKi9cbiAgcmVhZG9ubHkgb3duZWRFbXVsYXRvclBpZHM6IHJlYWRvbmx5IG51bWJlcltdO1xuXG4gIC8qKlxuICBIb3cgbG9uZyB0byB3YWl0IGZvciB0aGUgZW11bGF0b3IgdG8gZGlzYXBwZWFyLCBpbiBtaWxsaXNlY29uZHMuXG4gICAqL1xuICByZWFkb25seSB0aW1lb3V0SW5NaWxsaXNlY29uZHM6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBGaW5kcywgc3RvcHMgYW5kIHZlcmlmaWVzIHRoZSBoYXJuZXNzJ3MgZW11bGF0b3JzLCByZXBvcnRpbmcgdGhyb3VnaCB0aGVcbiAqIGNhbGxlcidzIGxvZyBzbyBlYWNoIGNhbGxlciBrZWVwcyBpdHMgb3duIHByZWZpeC5cbiAqL1xuZXhwb3J0IGNsYXNzIEVtdWxhdG9yUmVjbGFpbWVyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBsb2c6IChtZXNzYWdlOiBzdHJpbmcpID0+IHZvaWQ7XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSByZWNsYWltZXIgdGhhdCByZXBvcnRzIHVuZGVyIHRoZSBjYWxsZXIncyBvd24gbG9nIHByZWZpeC5cbiAgICpcbiAgICogQHBhcmFtIGxvZyAtIFJlY2VpdmVzIGV2ZXJ5IHByb2dyZXNzIGFuZCB2ZXJkaWN0IGxpbmUuXG4gICAqL1xuICBwdWJsaWMgY29uc3RydWN0b3IobG9nOiAobWVzc2FnZTogc3RyaW5nKSA9PiB2b2lkKSB7XG4gICAgdGhpcy5sb2cgPSBsb2c7XG4gIH1cblxuICAvKipcbiAgICogUnVucyBgYWRiIGRldmljZXNgIGFuZCByZXR1cm5zIGl0cyByYXcgc3Rkb3V0LlxuICAgKlxuICAgKiBUaGUgcmF3IGxpc3RpbmcgaXMgd2hhdCB0ZWFyZG93biBuZWVkczogYGdldENvbm5lY3RlZERldmljZUlkc2Aga2VlcHMgb25seVxuICAgKiB0aGUgYGRldmljZWAgc3RhdGUsIGFuZCBhbiBlbXVsYXRvciBvbiBpdHMgd2F5IG91dCBhbnN3ZXJzIGBvZmZsaW5lYCB3aGlsZVxuICAgKiBzdGlsbCBob2xkaW5nIHRoZSBBVkQgXHUyMDE0IHNlZSBgYWRiLWRldmljZS1saXN0LnRzYC5cbiAgICpcbiAgICogQHJldHVybnMgVGhlIHJhdyBgYWRiIGRldmljZXNgIG91dHB1dC5cbiAgICogQHRocm93cyBJZiBhZGIgY291bGQgbm90IGJlIHJ1biBhdCBhbGwuXG4gICAqL1xuICBwdWJsaWMgZ2V0RGV2aWNlc091dHB1dCgpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZTxzdHJpbmc+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIGV4ZWNGaWxlKCdhZGInLCBbJ2RldmljZXMnXSwgeyB0aW1lb3V0OiBBREJfREVWSUNFX0NIRUNLX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTIH0sIChlcnJvciwgc3Rkb3V0LCBzdGRlcnIpID0+IHtcbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIHJ1biAnYWRiIGRldmljZXMnOiAke2Vycm9yLm1lc3NhZ2V9LiBJcyBBREIgaW5zdGFsbGVkIGFuZCBpbiBQQVRIP2ApKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHN0ZGVycikge1xuICAgICAgICAgIHRoaXMubG9nKGBBREIgc3RkZXJyOiAke3N0ZGVyci50cmltKCl9YCk7XG4gICAgICAgIH1cbiAgICAgICAgcmVzb2x2ZShzdGRvdXQpO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogTGlzdHMgZXZlcnkgcHJvY2VzcyBvbiB0aGUgaG9zdC5cbiAgICpcbiAgICogQSBob3N0IGFsd2F5cyBoYXMgcHJvY2Vzc2VzLCBzbyBhIGxpc3RpbmcgdGhhdCBwYXJzZXMgdG8gKip6ZXJvKiogcm93cyBpcyBhXG4gICAqIGZhaWxlZCBxdWVyeSBob3dldmVyIGl0IGV4aXRlZCwgYW5kIGNvbWVzIGJhY2sgYXMgYHVuZGVmaW5lZGAgZXhhY3RseSBsaWtlXG4gICAqIG9uZSB0aGF0IGRpZCBub3QgcnVuIFx1MjAxNCBuZXZlciBhcyBhbiBlbXB0eSBsaXN0IGEgY2FsbGVyIGNvdWxkIHJlYWQgYXMgXCJub1xuICAgKiBlbXVsYXRvciBpcyBydW5uaW5nXCIuXG4gICAqXG4gICAqIEByZXR1cm5zIFRoZSBob3N0J3MgcHJvY2Vzc2VzLCBvciBgdW5kZWZpbmVkYCB3aGVuIHRoZSBsaXN0aW5nIGZhaWxlZC5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBxdWVyeUhvc3RQcm9jZXNzZXMoKTogUHJvbWlzZTxQcm9jZXNzTGlzdEVudHJ5W10gfCB1bmRlZmluZWQ+IHtcbiAgICBjb25zdCBxdWVyeSA9IGJ1aWxkSG9zdFByb2Nlc3NRdWVyeSgpO1xuICAgIGNvbnN0IG91dHB1dCA9IGF3YWl0IG5ldyBQcm9taXNlPHN0cmluZz4oKHJlc29sdmUpID0+IHtcbiAgICAgIGV4ZWNGaWxlKFxuICAgICAgICBxdWVyeS5jb21tYW5kLFxuICAgICAgICBxdWVyeS5jb21tYW5kQXJndW1lbnRzLFxuICAgICAgICB7IG1heEJ1ZmZlcjogQURCX0RVTVBTWVNfTUFYX0JVRkZFUl9JTl9CWVRFUywgdGltZW91dDogSE9TVF9QUk9DRVNTX1FVRVJZX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTIH0sXG4gICAgICAgIChlcnJvciwgc3Rkb3V0KSA9PiB7XG4gICAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICB0aGlzLmxvZyhgV2FybmluZzogY291bGQgbm90IGxpc3QgaG9zdCBwcm9jZXNzZXMgKFxcYCR7cXVlcnkuY29tbWFuZH1cXGApOiAke2Vycm9yLm1lc3NhZ2V9YCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJlc29sdmUoZXJyb3IgPyAnJyA6IHN0ZG91dCk7XG4gICAgICAgIH1cbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBjb25zdCBwcm9jZXNzZXMgPSBwYXJzZUhvc3RQcm9jZXNzTGlzdChvdXRwdXQpO1xuICAgIGlmIChwcm9jZXNzZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aGlzLmxvZyhgV2FybmluZzogXFxgJHtxdWVyeS5jb21tYW5kfVxcYCBsaXN0ZWQgbm8gcHJvY2Vzc2VzLmApO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICByZXR1cm4gcHJvY2Vzc2VzO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0b3BzIHRoZSBlbXVsYXRvcnMgdGhpcyBoYXJuZXNzIHN0YXJ0ZWQgdGhhdCBubyBsaXZlIHJ1biBpcyByZXNwb25zaWJsZVxuICAgKiBmb3IsIGFuZCBkcm9wcyB0aGUgbWFya2VycyB0aGF0IG5vIGxvbmdlciBkZXNjcmliZSBhIHJ1bm5pbmcgZW11bGF0b3IuXG4gICAqXG4gICAqIE9ubHkgZXZlciBhY3RzIG9uIGEgKiptYXJrZXItdmVyaWZpZWQqKiBlbXVsYXRvcjogb25lIHdob3NlIG1hcmtlciBuYW1lcyBhXG4gICAqIFBJRCB0aGF0IGlzIHN0aWxsIGEgbGl2ZSBlbXVsYXRvciBwcm9jZXNzLiBBbiBlbXVsYXRvciB3aXRob3V0IGEgbWFya2VyIFx1MjAxNFxuICAgKiBib290ZWQgYnkgaGFuZCwgYnkgQ0ksIG9yIGJ5IGFub3RoZXIgdG9vbCBcdTIwMTQgaXMgbmV2ZXIgdG91Y2hlZCwgd2hpY2gga2VlcHNcbiAgICogdGhlIEw0NiBsaW5lOiBuZXZlciBhIGBxZW11KmAgc3dlZXAuXG4gICAqXG4gICAqIENhbGxlcnMgbXVzdCBob2xkIHRoZSBgYW5kcm9pZGAgc2V0dXAgbG9jayAoTDcpLCB3aGljaCBpcyB3aGF0IG1ha2VzIGFcbiAgICogbGVmdG92ZXIgc2FmZSB0byBzdG9wOiBubyBvdGhlciBBbmRyb2lkIHJ1biBjYW4gYmUgbWlkLWZsaWdodCBvbiBpdC5cbiAgICpcbiAgICogQHBhcmFtIHBhcmFtcyAtIFdoaWNoIEFWRCB0byBsZWF2ZSBhbG9uZSwgYW5kIHdoZXRoZXIgdGhpcyBydW4gaXMgZW5kaW5nLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHJlY2xhaW1MZWZ0b3ZlckVtdWxhdG9ycyhwYXJhbXM6IFJlY2xhaW1MZWZ0b3ZlckVtdWxhdG9yc1BhcmFtcyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IG1hcmtlcnMgPSBsaXN0RW11bGF0b3JNYXJrZXJzKCkuZmlsdGVyKChtYXJrZXIpID0+IG1hcmtlci5hdmROYW1lICE9PSBwYXJhbXMuZXhjZXB0QXZkTmFtZSk7XG4gICAgaWYgKG1hcmtlcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgcHJvY2Vzc2VzID0gYXdhaXQgdGhpcy5xdWVyeUhvc3RQcm9jZXNzZXMoKTtcbiAgICBpZiAocHJvY2Vzc2VzID09PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIFdpdGhvdXQgYSBsaXN0aW5nIGV2ZXJ5IG1hcmtlciB3b3VsZCByZWFkIGFzIHN0YWxlLCBhbmQgZGVsZXRpbmcgdGhlbSB3b3VsZCBsZWF2ZSB0aGUgbGVmdG92ZXJzIGltcG9zc2libGUgdG8gY29udmljdC5cbiAgICAgIHRoaXMubG9nKGBDYW5ub3QganVkZ2UgJHtTdHJpbmcobWFya2Vycy5sZW5ndGgpfSBlbXVsYXRvciBtYXJrZXIocykgd2l0aG91dCBhIGhvc3QgcHJvY2VzcyBsaXN0aW5nOyBsZWF2aW5nIHRoZW0gZm9yIGEgbGF0ZXIgcnVuLmApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGxpdmVFbXVsYXRvclBpZHMgPSBzZWxlY3RFbXVsYXRvckJhY2tlbmRQaWRzKHsga25vd25QaWRzOiBbXSwgcHJvY2Vzc2VzIH0pO1xuICAgIGZvciAoY29uc3QgbWFya2VyIG9mIG1hcmtlcnMpIHtcbiAgICAgIGF3YWl0IHRoaXMucmVjbGFpbUxlZnRvdmVyRW11bGF0b3IobWFya2VyLCBsaXZlRW11bGF0b3JQaWRzLCBwYXJhbXMuc2NvcGUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wcyBhbiBlbXVsYXRvciB0aGlzIGhhcm5lc3Mgb3ducywgYW5kICoqdmVyaWZpZXMqKiBpdCBzdG9wcGVkLlxuICAgKlxuICAgKiBUaGUgY29uc29sZSBzaHV0ZG93biBnb2VzIGZpcnN0IGJlY2F1c2UgaXQgaXMgdGhlIG9ubHkgcGF0aCB0aGF0IHJlbGVhc2VzXG4gICAqIHRoZSBBVkQncyBgbXVsdGlpbnN0YW5jZS5sb2NrYDsgYSBgdGFza2tpbGxgIGxlYXZlcyB0aGUgbG9jayBiZWhpbmQsIGFuZCBhXG4gICAqIHN0YWxlIGxvY2sgaXMgd2hhdCBtYWtlcyB0aGUgbmV4dCBydW4gZmFpbCB3aXRoIGBSdW5uaW5nIG11bHRpcGxlIGVtdWxhdG9yc1xuICAgKiB3aXRoIHRoZSBzYW1lIEFWRGAgXHUyMDE0IGEgRkFUQUwgdGhlIGVtdWxhdG9yIHdyaXRlcyB0byBpdHMgb3duIHN0ZG91dCwgd2hlcmVcbiAgICogbm9ib2R5IHNlZXMgaXQuXG4gICAqXG4gICAqIFdvcmtzIHdpdGhvdXQgYSBsYXVuY2hlciBoYW5kbGUgdG9vOiBhIGxlZnRvdmVyIHRoaXMgcnVuIHRvb2sgb3ZlciBoYXMgb25seVxuICAgKiBpdHMgUElEcywgd2hpY2ggaXMgYWxsIHRoZSBlc2NhbGF0aW9uIGJlbG93IGV2ZXIgbmVlZGVkLiBUaGUgbWFya2VyIGdvZXNcbiAgICogb25seSB3aXRoIGEgKip2ZXJpZmllZCoqIHN0b3AsIHNvIGFuIGVtdWxhdG9yIHRoYXQgb3V0bGl2ZWQgdGhpcyBhdHRlbXB0XG4gICAqIHN0YXlzIGNvbnZpY3RhYmxlIGJ5IHRoZSBuZXh0IG9uZS5cbiAgICpcbiAgICogQHBhcmFtIHBhcmFtcyAtIFRoZSBlbXVsYXRvciBwcm9jZXNzLCB0aGUgZGV2aWNlIGl0IHNlcnZlcyBhbmQgdGhlIFBJRHMgdGhpcyBydW4gb3ducy5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBzdG9wRW11bGF0b3IocGFyYW1zOiBTdG9wRW11bGF0b3JQYXJhbXMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAocGFyYW1zLmRldmljZUlkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGF3YWl0IHRoaXMua2lsbEVtdWxhdG9yQ29uc29sZShwYXJhbXMuZGV2aWNlSWQpO1xuICAgIH1cbiAgICBpZiAocGFyYW1zLmVtdWxhdG9yUHJvY2Vzcykge1xuICAgICAga2lsbFByb2Nlc3NUcmVlKHBhcmFtcy5lbXVsYXRvclByb2Nlc3MpO1xuICAgIH1cblxuICAgIGNvbnN0IHdhaXRQYXJhbXM6IFdhaXRGb3JFbXVsYXRvclN0b3BwZWRQYXJhbXMgPSB7XG4gICAgICBkZXZpY2VJZDogcGFyYW1zLmRldmljZUlkLFxuICAgICAgb3duZWRFbXVsYXRvclBpZHM6IHBhcmFtcy5vd25lZEVtdWxhdG9yUGlkcyxcbiAgICAgIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogRU1VTEFUT1JfU1RPUF9USU1FT1VUX0lOX01JTExJU0VDT05EU1xuICAgIH07XG5cbiAgICBsZXQgaGFzRXNjYWxhdGVkID0gZmFsc2U7XG4gICAgbGV0IGlzU3RvcHBlZCA9IGF3YWl0IHRoaXMud2FpdEZvckVtdWxhdG9yU3RvcHBlZCh3YWl0UGFyYW1zKTtcblxuICAgIGlmICghaXNTdG9wcGVkKSB7XG4gICAgICBjb25zdCBzdXJ2aXZpbmdQaWRzID0gcGFyYW1zLm93bmVkRW11bGF0b3JQaWRzLmZpbHRlcigocGlkKSA9PiBjaGVja0lzUHJvY2Vzc0FsaXZlKHBpZCkpO1xuICAgICAgaWYgKHN1cnZpdmluZ1BpZHMubGVuZ3RoID4gMCkge1xuICAgICAgICBoYXNFc2NhbGF0ZWQgPSB0cnVlO1xuICAgICAgICB0aGlzLmxvZyhcbiAgICAgICAgICBgQXV0by1zdGFydGVkIGVtdWxhdG9yIG91dGxpdmVkIGl0cyBjb25zb2xlIHNodXRkb3duIGFuZCB0aGUgbGF1bmNoZXIncyBwcm9jZXNzIHRyZWU7IGVzY2FsYXRpbmcgdG8gdGhlIGVtdWxhdG9yIFBJRChzKSB0aGlzIHJ1biBvd25zOiBbJHtzdXJ2aXZpbmdQaWRzLmpvaW4oJywgJyl9XS5gXG4gICAgICAgICk7XG4gICAgICAgIGZvciAoY29uc3QgcGlkIG9mIHN1cnZpdmluZ1BpZHMpIHtcbiAgICAgICAgICBraWxsUHJvY2Vzc1RyZWVCeVBpZChwaWQpO1xuICAgICAgICB9XG4gICAgICAgIGlzU3RvcHBlZCA9IGF3YWl0IHRoaXMud2FpdEZvckVtdWxhdG9yU3RvcHBlZCh7IC4uLndhaXRQYXJhbXMsIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogRU1VTEFUT1JfRVNDQUxBVEVEX1NUT1BfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5sb2coYnVpbGRUZWFyZG93bk1lc3NhZ2Uoe1xuICAgICAgZXZpZGVuY2U6IHBhcmFtcy5kZXZpY2VJZCA9PT0gdW5kZWZpbmVkID8gYEFWRCBcIiR7cGFyYW1zLmF2ZE5hbWV9XCJgIDogYEFWRCBcIiR7cGFyYW1zLmF2ZE5hbWV9XCIgb24gZGV2aWNlICR7cGFyYW1zLmRldmljZUlkfWAsXG4gICAgICBvdXRjb21lOiByZXNvbHZlVGVhcmRvd25PdXRjb21lKHsgaGFzRXNjYWxhdGVkLCBpc1N0b3BwZWQgfSksXG4gICAgICBzdWJqZWN0OiAnQXV0by1zdGFydGVkIGVtdWxhdG9yJyxcbiAgICAgIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogRU1VTEFUT1JfU1RPUF9USU1FT1VUX0lOX01JTExJU0VDT05EU1xuICAgIH0pKTtcblxuICAgIGlmIChpc1N0b3BwZWQpIHtcbiAgICAgIGNsZWFyRW11bGF0b3JNYXJrZXJJZlN0b3BwZWQocGFyYW1zLmF2ZE5hbWUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEZWNpZGVzIHdoZXRoZXIgdGhlIGVtdWxhdG9yIHRoaXMgcnVuIHN0YXJ0ZWQgaXMgcmVhbGx5IGdvbmUuXG4gICAqXG4gICAqIFR3byBpbmRlcGVuZGVudCBwcm9vZnMsIGNoZWFwZXN0IGZpcnN0OiBub25lIG9mIHRoZSBQSURzIHRoaXMgcnVuIG93bnMgaXNcbiAgICogYWxpdmUsIGFuZCB0aGUgZGV2aWNlIG5vIGxvbmdlciBhcHBlYXJzIGluIGBhZGIgZGV2aWNlc2AgKippbiBhbnkgc3RhdGUqKiAoYVxuICAgKiBkeWluZyBlbXVsYXRvciBhbnN3ZXJzIGBvZmZsaW5lYCB3aGlsZSBpdCBzdGlsbCBob2xkcyB0aGUgQVZEKS5cbiAgICpcbiAgICogQHBhcmFtIHBhcmFtcyAtIFRoZSBkZXZpY2UgYW5kIHRoZSBQSURzIHRoaXMgcnVuIG93bnMuXG4gICAqIEByZXR1cm5zIGB0cnVlYCB3aGVuIG5vdGhpbmcgb2YgdGhpcyBydW4ncyBlbXVsYXRvciBpcyBsZWZ0LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBjaGVja0lzRW11bGF0b3JHb25lKHBhcmFtczogQ2hlY2tJc0VtdWxhdG9yR29uZVBhcmFtcyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGlmIChwYXJhbXMub3duZWRFbXVsYXRvclBpZHMuc29tZSgocGlkKSA9PiBjaGVja0lzUHJvY2Vzc0FsaXZlKHBpZCkpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgaWYgKHBhcmFtcy5kZXZpY2VJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgcmV0dXJuICFjaGVja0lzRGV2aWNlTGlzdGVkKHsgZGV2aWNlSWQ6IHBhcmFtcy5kZXZpY2VJZCwgZGV2aWNlc091dHB1dDogYXdhaXQgdGhpcy5nZXREZXZpY2VzT3V0cHV0KCkgfSk7XG4gICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgIC8qXG4gICAgICAgKiBBbiBhZGIgdGhhdCBjYW5ub3QgcnVuIGlzIG5vdCBldmlkZW5jZSBhYm91dCB0aGUgZW11bGF0b3IgXHUyMDE0IGFuZCB0aGUgUElEXG4gICAgICAgKiBwcm9iZSBhYm92ZSBoYXMgYWxyZWFkeSBzYWlkIHRoaXMgcnVuJ3MgcHJvY2Vzc2VzIGFyZSBnb25lLCB3aGljaCBpcyB0aGVcbiAgICAgICAqIHN0cm9uZ2VyIG9mIHRoZSB0d28gcHJvb2ZzLlxuICAgICAgICovXG4gICAgICB0aGlzLmxvZyhgQ291bGQgbm90IHJlLWNoZWNrIGNvbm5lY3RlZCBkZXZpY2VzIHdoaWxlIHN0b3BwaW5nIHRoZSBlbXVsYXRvcjogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9YCk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQXNrcyB0aGUgZW11bGF0b3IgdG8gc2h1dCBpdHNlbGYgZG93biBvdmVyIGl0cyBjb25zb2xlLlxuICAgKlxuICAgKiBQcmVmZXJyZWQgb3ZlciBraWxsaW5nIHRoZSBwcm9jZXNzIG91dHJpZ2h0IGJlY2F1c2UgaXQgaXMgdGhlIHBhdGggdGhhdFxuICAgKiByZWxlYXNlcyB0aGUgQVZEJ3MgYG11bHRpaW5zdGFuY2UubG9ja2AuIEJlc3QtZWZmb3J0OiBhIGNvbnNvbGUgdGhhdCBkb2VzXG4gICAqIG5vdCBhbnN3ZXIgaXMgcmVwb3J0ZWQgYW5kIHRoZSBjYWxsZXIgZmFsbHMgdGhyb3VnaCB0byB0aGUga2lsbC5cbiAgICpcbiAgICogQHBhcmFtIGRldmljZUlkIC0gVGhlIGVtdWxhdG9yJ3MgZGV2aWNlIElELlxuICAgKi9cbiAgcHJpdmF0ZSBraWxsRW11bGF0b3JDb25zb2xlKGRldmljZUlkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHtcbiAgICAgIGV4ZWNGaWxlKCdhZGInLCBbJy1zJywgZGV2aWNlSWQsICdlbXUnLCAna2lsbCddLCB7IHRpbWVvdXQ6IEFEQl9ERVZJQ0VfQ0hFQ0tfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgfSwgKGVycm9yLCBzdGRvdXQpID0+IHtcbiAgICAgICAgdGhpcy5sb2coXG4gICAgICAgICAgZXJyb3JcbiAgICAgICAgICAgID8gYEVtdWxhdG9yIGNvbnNvbGUgc2h1dGRvd24gZm9yICR7ZGV2aWNlSWR9IGRpZCBub3QgYW5zd2VyOiAke2Vycm9yLm1lc3NhZ2V9YFxuICAgICAgICAgICAgOiBgRW11bGF0b3IgY29uc29sZSBzaHV0ZG93biBmb3IgJHtkZXZpY2VJZH06ICR7c3Rkb3V0LnRyaW0oKX1gXG4gICAgICAgICk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFjdHMgb24gb25lIGVtdWxhdG9yIG1hcmtlciBmb3Ige0BsaW5rIHJlY2xhaW1MZWZ0b3ZlckVtdWxhdG9yc30uXG4gICAqXG4gICAqIEBwYXJhbSBtYXJrZXIgLSBUaGUgbWFya2VyIHRvIGp1ZGdlLlxuICAgKiBAcGFyYW0gbGl2ZUVtdWxhdG9yUGlkcyAtIFRoZSBlbXVsYXRvciBwcm9jZXNzZXMgY3VycmVudGx5IHJ1bm5pbmcgb24gdGhlIGhvc3QuXG4gICAqIEBwYXJhbSBzY29wZSAtIFdoZXRoZXIgYW4gZW11bGF0b3IgYW5vdGhlciBsaXZlIGhhcm5lc3MgcHJvY2VzcyBvd25zIGlzIHNwYXJlZC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVjbGFpbUxlZnRvdmVyRW11bGF0b3IobWFya2VyOiBFbXVsYXRvck1hcmtlciwgbGl2ZUVtdWxhdG9yUGlkczogcmVhZG9ubHkgbnVtYmVyW10sIHNjb3BlOiBSZWNsYWltTGVmdG92ZXJFbXVsYXRvcnNQYXJhbXNbJ3Njb3BlJ10pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB2ZXJkaWN0ID0gcmVzb2x2ZUVtdWxhdG9yTWFya2VyVmVyZGljdCh7XG4gICAgICBjdXJyZW50UGlkOiBwcm9jZXNzLnBpZCxcbiAgICAgIGlzT3duZXJBbGl2ZTogY2hlY2tJc1Byb2Nlc3NBbGl2ZShtYXJrZXIub3duZXJQaWQpLFxuICAgICAgbGl2ZUVtdWxhdG9yUGlkcyxcbiAgICAgIG1hcmtlclxuICAgIH0pO1xuXG4gICAgaWYgKHZlcmRpY3QgPT09ICdzdGFsZS1tYXJrZXInKSB7XG4gICAgICB0aGlzLmxvZyhgRHJvcHBpbmcgdGhlIGVtdWxhdG9yIG1hcmtlciBmb3IgQVZEIFwiJHttYXJrZXIuYXZkTmFtZX1cIjogbm9uZSBvZiBpdHMgUElEcyBbJHttYXJrZXIub3duZWRFbXVsYXRvclBpZHMuam9pbignLCAnKX1dIGlzIGEgcnVubmluZyBlbXVsYXRvciBhbnkgbW9yZS5gKTtcbiAgICAgIGNsZWFyRW11bGF0b3JNYXJrZXIobWFya2VyLmF2ZE5hbWUpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8qXG4gICAgICogQSBtYXJrZXIgd3JpdHRlbiBhdCBsYXVuY2ggaGFzIG5vIGRldmljZSB5ZXQ6IHRoZSBlbXVsYXRvciB3YXMgc3RpbGxcbiAgICAgKiBib290aW5nIHdoZW4gd2hhdGV2ZXIgcmVjb3JkZWQgaXQgc3RvcHBlZCBsb29raW5nLiBUaGUgc3RvcCBiZWxvdyByZWFkc1xuICAgICAqIHRoYXQgYWJzZW5jZSBjb3JyZWN0bHkgXHUyMDE0IG5vIGNvbnNvbGUgdG8gc2h1dCBkb3duLCB0aGUgb3duZWQgUElEcyBhcmUgdGhlXG4gICAgICogd2hvbGUgb2YgdGhlIGV2aWRlbmNlIFx1MjAxNCBzbyBpdCBpcyBkZXNjcmliZWQgcmF0aGVyIHRoYW4gcGFwZXJlZCBvdmVyLlxuICAgICAqL1xuICAgIGNvbnN0IGV2aWRlbmNlID0gZGVzY3JpYmVNYXJrZWRFbXVsYXRvcihtYXJrZXIpO1xuXG4gICAgaWYgKHZlcmRpY3QgPT09ICdpbi11c2UtYnktbGl2ZS1ydW4nICYmIHNjb3BlID09PSAncHJlZmxpZ2h0Jykge1xuICAgICAgdGhpcy5sb2coYExlYXZpbmcgdGhlIGVtdWxhdG9yIGZvciAke2V2aWRlbmNlfSBhbG9uZTogdGhlIGhhcm5lc3MgcHJvY2VzcyB0aGF0IG93bnMgaXQgKFBJRCAke1N0cmluZyhtYXJrZXIub3duZXJQaWQpfSkgaXMgc3RpbGwgcnVubmluZy5gKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBhZ2VJblNlY29uZHMgPSBNYXRoLnJvdW5kKChEYXRlLm5vdygpIC0gbWFya2VyLnN0YXJ0ZWRBdEluTWlsbGlzZWNvbmRzKSAvIE1JTExJU0VDT05EU19QRVJfU0VDT05EKTtcbiAgICB0aGlzLmxvZyhcbiAgICAgIGBTdG9wcGluZyB0aGUgZW11bGF0b3IgZm9yICR7ZXZpZGVuY2V9OiB0aGlzIGhhcm5lc3Mgc3RhcnRlZCBpdCAke1N0cmluZyhhZ2VJblNlY29uZHMpfXMgYWdvLCBhbmQgbm8gbGl2ZSBydW4gaXMgbGVmdCB0byBzdG9wIGl0LmBcbiAgICApO1xuICAgIGF3YWl0IHRoaXMuc3RvcEVtdWxhdG9yKHtcbiAgICAgIGF2ZE5hbWU6IG1hcmtlci5hdmROYW1lLFxuICAgICAgZGV2aWNlSWQ6IG1hcmtlci5kZXZpY2VJZCxcbiAgICAgIG93bmVkRW11bGF0b3JQaWRzOiBzZWxlY3RMaXZlTWFya2VkUGlkcyhtYXJrZXIsIGxpdmVFbXVsYXRvclBpZHMpXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUG9sbHMgdW50aWwgdGhpcyBydW4ncyBlbXVsYXRvciBpcyBnb25lLCBvciB0aGUgYnVkZ2V0IGVsYXBzZXMuXG4gICAqXG4gICAqIEBwYXJhbSBwYXJhbXMgLSBUaGUgZGV2aWNlLCB0aGUgUElEcyB0aGlzIHJ1biBvd25zLCBhbmQgdGhlIGJ1ZGdldC5cbiAgICogQHJldHVybnMgYHRydWVgIHdoZW4gdGhlIGVtdWxhdG9yIGRpc2FwcGVhcmVkIHdpdGhpbiB0aGUgYnVkZ2V0LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB3YWl0Rm9yRW11bGF0b3JTdG9wcGVkKHBhcmFtczogV2FpdEZvckVtdWxhdG9yU3RvcHBlZFBhcmFtcyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IGRlYWRsaW5lID0gRGF0ZS5ub3coKSArIHBhcmFtcy50aW1lb3V0SW5NaWxsaXNlY29uZHM7XG5cbiAgICB3aGlsZSAoRGF0ZS5ub3coKSA8IGRlYWRsaW5lKSB7XG4gICAgICBpZiAoYXdhaXQgdGhpcy5jaGVja0lzRW11bGF0b3JHb25lKHBhcmFtcykpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG5cbiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgIHNldFRpbWVvdXQocmVzb2x2ZSwgRU1VTEFUT1JfU1RPUF9QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiBCdWlsZHMgdGhlIHBsYXRmb3JtJ3MgXCJsaXN0IGV2ZXJ5IHByb2Nlc3NcIiBxdWVyeS5cbiAqXG4gKiBAcmV0dXJucyBUaGUgY29tbWFuZCBhbmQgYXJndW1lbnRzIHRvIHJ1bi5cbiAqL1xuZnVuY3Rpb24gYnVpbGRIb3N0UHJvY2Vzc1F1ZXJ5KCk6IEhvc3RDb21tYW5kUXVlcnkge1xuICByZXR1cm4gcHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJ1xuICAgID8geyBjb21tYW5kOiAndGFza2xpc3QnLCBjb21tYW5kQXJndW1lbnRzOiBbJy9GTycsICdDU1YnLCAnL05IJ10gfVxuICAgIDogeyBjb21tYW5kOiAncHMnLCBjb21tYW5kQXJndW1lbnRzOiBbJy1lbycsICdwaWQ9LGNvbW09J10gfTtcbn1cblxuLyoqXG4gKiBOYW1lcyB0aGUgZW11bGF0b3IgYSBtYXJrZXIgZGVzY3JpYmVzLCBmb3IgdGhlIGxvZy5cbiAqXG4gKiBAcGFyYW0gbWFya2VyIC0gVGhlIG1hcmtlci5cbiAqIEByZXR1cm5zIFRoZSBBVkQgYW5kIGl0cyBkZXZpY2UsIG9yIHRoZSBBVkQgYWxvbmUgZm9yIGEgbGF1bmNoIHRoYXQgaGFkIG5vdCBwcm9kdWNlZCBhIGRldmljZSB5ZXQuXG4gKi9cbmZ1bmN0aW9uIGRlc2NyaWJlTWFya2VkRW11bGF0b3IobWFya2VyOiBFbXVsYXRvck1hcmtlcik6IHN0cmluZyB7XG4gIHJldHVybiBtYXJrZXIuZGV2aWNlSWQgPT09IHVuZGVmaW5lZFxuICAgID8gYEFWRCBcIiR7bWFya2VyLmF2ZE5hbWV9XCIgKG5vIGRldmljZTogaXQgd2FzIHN0aWxsIGJvb3Rpbmcgd2hlbiBpdCB3YXMgcmVjb3JkZWQpYFxuICAgIDogYEFWRCBcIiR7bWFya2VyLmF2ZE5hbWV9XCIgb24gZGV2aWNlICR7bWFya2VyLmRldmljZUlkfWA7XG59XG5cbi8qKlxuICogUGFyc2VzIGEgaG9zdCBwcm9jZXNzIGxpc3Rpbmcgd2l0aCB0aGUgcGxhdGZvcm0ncyBwYXJzZXIuXG4gKlxuICogQHBhcmFtIG91dHB1dCAtIFJhdyBzdGRvdXQgb2Yge0BsaW5rIGJ1aWxkSG9zdFByb2Nlc3NRdWVyeX0ncyBjb21tYW5kLlxuICogQHJldHVybnMgVGhlIGxpc3RlZCBwcm9jZXNzZXMuXG4gKi9cbmZ1bmN0aW9uIHBhcnNlSG9zdFByb2Nlc3NMaXN0KG91dHB1dDogc3RyaW5nKTogUHJvY2Vzc0xpc3RFbnRyeVtdIHtcbiAgcmV0dXJuIHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicgPyBwYXJzZVdpbmRvd3NUYXNrTGlzdChvdXRwdXQpIDogcGFyc2VQb3NpeFByb2Nlc3NMaXN0KG91dHB1dCk7XG59XG5cbi8qIHY4IGlnbm9yZSBzdG9wICovXG4iXSwKICAibWFwcGluZ3MiOiAiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQW1CQSxnQ0FBeUI7QUFDekIsMEJBQW9CO0FBS3BCLDZCQUFvQztBQUNwQyw4QkFJTztBQUNQLDZCQU1PO0FBQ1AsK0JBR087QUFDUCw4QkFBb0M7QUFDcEMsOEJBR087QUFLQSxNQUFNLDJDQUEyQztBQVFqRCxNQUFNLGtDQUFrQztBQVN4QyxNQUFNLDZDQUE2QztBQUUxRCxNQUFNLGtEQUFrRDtBQUN4RCxNQUFNLDhDQUE4QztBQUNwRCxNQUFNLHdDQUF3QztBQUM5QyxNQUFNLDBCQUEwQjtBQWlHekIsTUFBTSxrQkFBa0I7QUFBQSxFQUNaO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBT1YsWUFBWSxLQUFnQztBQUNqRCxTQUFLLE1BQU07QUFBQSxFQUNiO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQVlPLG1CQUFvQztBQUN6QyxXQUFPLElBQUksUUFBZ0IsQ0FBQyxTQUFTLFdBQVc7QUFDOUMsOENBQVMsT0FBTyxDQUFDLFNBQVMsR0FBRyxFQUFFLFNBQVMseUNBQXlDLEdBQUcsQ0FBQyxPQUFPLFFBQVEsV0FBVztBQUM3RyxZQUFJLE9BQU87QUFDVCxpQkFBTyxJQUFJLE1BQU0sZ0NBQWdDLE1BQU0sT0FBTyxpQ0FBaUMsQ0FBQztBQUNoRztBQUFBLFFBQ0Y7QUFDQSxZQUFJLFFBQVE7QUFDVixlQUFLLElBQUksZUFBZSxPQUFPLEtBQUssQ0FBQyxFQUFFO0FBQUEsUUFDekM7QUFDQSxnQkFBUSxNQUFNO0FBQUEsTUFDaEIsQ0FBQztBQUFBLElBQ0gsQ0FBQztBQUFBLEVBQ0g7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBWUEsTUFBYSxxQkFBOEQ7QUFDekUsVUFBTSxRQUFRLHNCQUFzQjtBQUNwQyxVQUFNLFNBQVMsTUFBTSxJQUFJLFFBQWdCLENBQUMsWUFBWTtBQUNwRDtBQUFBLFFBQ0UsTUFBTTtBQUFBLFFBQ04sTUFBTTtBQUFBLFFBQ04sRUFBRSxXQUFXLGlDQUFpQyxTQUFTLDJDQUEyQztBQUFBLFFBQ2xHLENBQUMsT0FBTyxXQUFXO0FBQ2pCLGNBQUksT0FBTztBQUNULGlCQUFLLElBQUksNkNBQTZDLE1BQU0sT0FBTyxRQUFRLE1BQU0sT0FBTyxFQUFFO0FBQUEsVUFDNUY7QUFDQSxrQkFBUSxRQUFRLEtBQUssTUFBTTtBQUFBLFFBQzdCO0FBQUEsTUFDRjtBQUFBLElBQ0YsQ0FBQztBQUVELFVBQU0sWUFBWSxxQkFBcUIsTUFBTTtBQUM3QyxRQUFJLFVBQVUsV0FBVyxHQUFHO0FBQzFCLFdBQUssSUFBSSxjQUFjLE1BQU0sT0FBTyx5QkFBeUI7QUFDN0QsYUFBTztBQUFBLElBQ1Q7QUFFQSxXQUFPO0FBQUEsRUFDVDtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQWdCQSxNQUFhLHlCQUF5QixRQUF1RDtBQUMzRixVQUFNLGNBQVUsNENBQW9CLEVBQUUsT0FBTyxDQUFDLFdBQVcsT0FBTyxZQUFZLE9BQU8sYUFBYTtBQUNoRyxRQUFJLFFBQVEsV0FBVyxHQUFHO0FBQ3hCO0FBQUEsSUFDRjtBQUVBLFVBQU0sWUFBWSxNQUFNLEtBQUssbUJBQW1CO0FBQ2hELFFBQUksY0FBYyxRQUFXO0FBRTNCLFdBQUssSUFBSSxnQkFBZ0IsT0FBTyxRQUFRLE1BQU0sQ0FBQyxtRkFBbUY7QUFDbEk7QUFBQSxJQUNGO0FBRUEsVUFBTSx1QkFBbUIsbURBQTBCLEVBQUUsV0FBVyxDQUFDLEdBQUcsVUFBVSxDQUFDO0FBQy9FLGVBQVcsVUFBVSxTQUFTO0FBQzVCLFlBQU0sS0FBSyx3QkFBd0IsUUFBUSxrQkFBa0IsT0FBTyxLQUFLO0FBQUEsSUFDM0U7QUFBQSxFQUNGO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQWtCQSxNQUFhLGFBQWEsUUFBMkM7QUFDbkUsUUFBSSxPQUFPLGFBQWEsUUFBVztBQUNqQyxZQUFNLEtBQUssb0JBQW9CLE9BQU8sUUFBUTtBQUFBLElBQ2hEO0FBQ0EsUUFBSSxPQUFPLGlCQUFpQjtBQUMxQixvREFBZ0IsT0FBTyxlQUFlO0FBQUEsSUFDeEM7QUFFQSxVQUFNLGFBQTJDO0FBQUEsTUFDL0MsVUFBVSxPQUFPO0FBQUEsTUFDakIsbUJBQW1CLE9BQU87QUFBQSxNQUMxQix1QkFBdUI7QUFBQSxJQUN6QjtBQUVBLFFBQUksZUFBZTtBQUNuQixRQUFJLFlBQVksTUFBTSxLQUFLLHVCQUF1QixVQUFVO0FBRTVELFFBQUksQ0FBQyxXQUFXO0FBQ2QsWUFBTSxnQkFBZ0IsT0FBTyxrQkFBa0IsT0FBTyxDQUFDLFlBQVEsNkNBQW9CLEdBQUcsQ0FBQztBQUN2RixVQUFJLGNBQWMsU0FBUyxHQUFHO0FBQzVCLHVCQUFlO0FBQ2YsYUFBSztBQUFBLFVBQ0gsMElBQTBJLGNBQWMsS0FBSyxJQUFJLENBQUM7QUFBQSxRQUNwSztBQUNBLG1CQUFXLE9BQU8sZUFBZTtBQUMvQiw2REFBcUIsR0FBRztBQUFBLFFBQzFCO0FBQ0Esb0JBQVksTUFBTSxLQUFLLHVCQUF1QixFQUFFLEdBQUcsWUFBWSx1QkFBdUIsZ0RBQWdELENBQUM7QUFBQSxNQUN6STtBQUFBLElBQ0Y7QUFFQSxTQUFLLFFBQUksOENBQXFCO0FBQUEsTUFDNUIsVUFBVSxPQUFPLGFBQWEsU0FBWSxRQUFRLE9BQU8sT0FBTyxNQUFNLFFBQVEsT0FBTyxPQUFPLGVBQWUsT0FBTyxRQUFRO0FBQUEsTUFDMUgsYUFBUyxnREFBdUIsRUFBRSxjQUFjLFVBQVUsQ0FBQztBQUFBLE1BQzNELFNBQVM7QUFBQSxNQUNULHVCQUF1QjtBQUFBLElBQ3pCLENBQUMsQ0FBQztBQUVGLFFBQUksV0FBVztBQUNiLCtEQUE2QixPQUFPLE9BQU87QUFBQSxJQUM3QztBQUFBLEVBQ0Y7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBWUEsTUFBYyxvQkFBb0IsUUFBcUQ7QUFDckYsUUFBSSxPQUFPLGtCQUFrQixLQUFLLENBQUMsWUFBUSw2Q0FBb0IsR0FBRyxDQUFDLEdBQUc7QUFDcEUsYUFBTztBQUFBLElBQ1Q7QUFFQSxRQUFJLE9BQU8sYUFBYSxRQUFXO0FBQ2pDLGFBQU87QUFBQSxJQUNUO0FBRUEsUUFBSTtBQUNGLGFBQU8sS0FBQyw0Q0FBb0IsRUFBRSxVQUFVLE9BQU8sVUFBVSxlQUFlLE1BQU0sS0FBSyxpQkFBaUIsRUFBRSxDQUFDO0FBQUEsSUFDekcsU0FBUyxPQUFnQjtBQU12QixXQUFLLElBQUkscUVBQXFFLGlCQUFpQixRQUFRLE1BQU0sVUFBVSxPQUFPLEtBQUssQ0FBQyxFQUFFO0FBQ3RJLGFBQU87QUFBQSxJQUNUO0FBQUEsRUFDRjtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBV1Esb0JBQW9CLFVBQWlDO0FBQzNELFdBQU8sSUFBSSxRQUFjLENBQUMsWUFBWTtBQUNwQyw4Q0FBUyxPQUFPLENBQUMsTUFBTSxVQUFVLE9BQU8sTUFBTSxHQUFHLEVBQUUsU0FBUyx5Q0FBeUMsR0FBRyxDQUFDLE9BQU8sV0FBVztBQUN6SCxhQUFLO0FBQUEsVUFDSCxRQUNJLGlDQUFpQyxRQUFRLG9CQUFvQixNQUFNLE9BQU8sS0FDMUUsaUNBQWlDLFFBQVEsS0FBSyxPQUFPLEtBQUssQ0FBQztBQUFBLFFBQ2pFO0FBQ0EsZ0JBQVE7QUFBQSxNQUNWLENBQUM7QUFBQSxJQUNILENBQUM7QUFBQSxFQUNIO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQSxFQVNBLE1BQWMsd0JBQXdCLFFBQXdCLGtCQUFxQyxPQUErRDtBQUNoSyxVQUFNLGNBQVUscURBQTZCO0FBQUEsTUFDM0MsWUFBWSxvQkFBQUEsUUFBUTtBQUFBLE1BQ3BCLGtCQUFjLDZDQUFvQixPQUFPLFFBQVE7QUFBQSxNQUNqRDtBQUFBLE1BQ0E7QUFBQSxJQUNGLENBQUM7QUFFRCxRQUFJLFlBQVksZ0JBQWdCO0FBQzlCLFdBQUssSUFBSSx5Q0FBeUMsT0FBTyxPQUFPLHdCQUF3QixPQUFPLGtCQUFrQixLQUFLLElBQUksQ0FBQyxtQ0FBbUM7QUFDOUosc0RBQW9CLE9BQU8sT0FBTztBQUNsQztBQUFBLElBQ0Y7QUFRQSxVQUFNLFdBQVcsdUJBQXVCLE1BQU07QUFFOUMsUUFBSSxZQUFZLHdCQUF3QixVQUFVLGFBQWE7QUFDN0QsV0FBSyxJQUFJLDRCQUE0QixRQUFRLGlEQUFpRCxPQUFPLE9BQU8sUUFBUSxDQUFDLHFCQUFxQjtBQUMxSTtBQUFBLElBQ0Y7QUFFQSxVQUFNLGVBQWUsS0FBSyxPQUFPLEtBQUssSUFBSSxJQUFJLE9BQU8sMkJBQTJCLHVCQUF1QjtBQUN2RyxTQUFLO0FBQUEsTUFDSCw2QkFBNkIsUUFBUSw2QkFBNkIsT0FBTyxZQUFZLENBQUM7QUFBQSxJQUN4RjtBQUNBLFVBQU0sS0FBSyxhQUFhO0FBQUEsTUFDdEIsU0FBUyxPQUFPO0FBQUEsTUFDaEIsVUFBVSxPQUFPO0FBQUEsTUFDakIsdUJBQW1CLDZDQUFxQixRQUFRLGdCQUFnQjtBQUFBLElBQ2xFLENBQUM7QUFBQSxFQUNIO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUEsRUFRQSxNQUFjLHVCQUF1QixRQUF3RDtBQUMzRixVQUFNLFdBQVcsS0FBSyxJQUFJLElBQUksT0FBTztBQUVyQyxXQUFPLEtBQUssSUFBSSxJQUFJLFVBQVU7QUFDNUIsVUFBSSxNQUFNLEtBQUssb0JBQW9CLE1BQU0sR0FBRztBQUMxQyxlQUFPO0FBQUEsTUFDVDtBQUVBLFlBQU0sSUFBSSxRQUFRLENBQUMsWUFBWTtBQUM3QixtQkFBVyxTQUFTLDJDQUEyQztBQUFBLE1BQ2pFLENBQUM7QUFBQSxJQUNIO0FBRUEsV0FBTztBQUFBLEVBQ1Q7QUFDRjtBQU9BLFNBQVMsd0JBQTBDO0FBQ2pELFNBQU8sb0JBQUFBLFFBQVEsYUFBYSxVQUN4QixFQUFFLFNBQVMsWUFBWSxrQkFBa0IsQ0FBQyxPQUFPLE9BQU8sS0FBSyxFQUFFLElBQy9ELEVBQUUsU0FBUyxNQUFNLGtCQUFrQixDQUFDLE9BQU8sWUFBWSxFQUFFO0FBQy9EO0FBUUEsU0FBUyx1QkFBdUIsUUFBZ0M7QUFDOUQsU0FBTyxPQUFPLGFBQWEsU0FDdkIsUUFBUSxPQUFPLE9BQU8sNkRBQ3RCLFFBQVEsT0FBTyxPQUFPLGVBQWUsT0FBTyxRQUFRO0FBQzFEO0FBUUEsU0FBUyxxQkFBcUIsUUFBb0M7QUFDaEUsU0FBTyxvQkFBQUEsUUFBUSxhQUFhLGNBQVUsOENBQXFCLE1BQU0sUUFBSSwrQ0FBc0IsTUFBTTtBQUNuRzsiLAogICJuYW1lcyI6IFsicHJvY2VzcyJdCn0K