obsidian-integration-testing 12.1.2 → 12.2.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 (63) hide show
  1. package/dist/lib/cjs/app-config.cjs +137 -0
  2. package/dist/lib/cjs/app-config.d.cts +190 -0
  3. package/dist/lib/cjs/appium-session-config.cjs +9 -1
  4. package/dist/lib/cjs/appium-session-config.d.cts +23 -0
  5. package/dist/lib/cjs/cdp-command-timeout-error.cjs +50 -0
  6. package/dist/lib/cjs/cdp-command-timeout-error.d.cts +48 -0
  7. package/dist/lib/cjs/emulator-arguments.cjs +19 -3
  8. package/dist/lib/cjs/emulator-arguments.d.cts +44 -0
  9. package/dist/lib/cjs/emulator-marker.cjs +169 -0
  10. package/dist/lib/cjs/emulator-marker.d.cts +224 -0
  11. package/dist/lib/cjs/emulator-reaper.cjs +248 -0
  12. package/dist/lib/cjs/emulator-reaper.d.cts +225 -0
  13. package/dist/lib/cjs/emulator-reclaim.cjs +312 -0
  14. package/dist/lib/cjs/emulator-reclaim.d.cts +179 -0
  15. package/dist/lib/cjs/eval-cap-exceeded-error.cjs +61 -0
  16. package/dist/lib/cjs/eval-cap-exceeded-error.d.cts +92 -0
  17. package/dist/lib/cjs/global-setup-core.cjs +2 -3
  18. package/dist/lib/cjs/index.cjs +18 -1
  19. package/dist/lib/cjs/index.d.cts +6 -0
  20. package/dist/lib/cjs/library.cjs +1 -1
  21. package/dist/lib/cjs/poll-in-obsidian.cjs +1 -1
  22. package/dist/lib/cjs/poll-in-obsidian.d.cts +6 -2
  23. package/dist/lib/cjs/setup-lock.cjs +56 -8
  24. package/dist/lib/cjs/setup-lock.d.cts +53 -0
  25. package/dist/lib/cjs/transport-appium.cjs +69 -4
  26. package/dist/lib/cjs/transport-appium.d.cts +35 -0
  27. package/dist/lib/cjs/transport-desktop-cdp.cjs +22 -7
  28. package/dist/lib/cjs/transport-factory.cjs +297 -190
  29. package/dist/lib/cjs/transport-factory.d.cts +15 -0
  30. package/dist/lib/cjs/transport-options.cjs +1 -1
  31. package/dist/lib/cjs/transport-options.d.cts +27 -0
  32. package/dist/lib/esm/app-config.d.mts +190 -0
  33. package/dist/lib/esm/app-config.mjs +110 -0
  34. package/dist/lib/esm/appium-session-config.d.mts +23 -0
  35. package/dist/lib/esm/appium-session-config.mjs +7 -1
  36. package/dist/lib/esm/cdp-command-timeout-error.d.mts +48 -0
  37. package/dist/lib/esm/cdp-command-timeout-error.mjs +26 -0
  38. package/dist/lib/esm/emulator-arguments.d.mts +44 -0
  39. package/dist/lib/esm/emulator-arguments.mjs +16 -2
  40. package/dist/lib/esm/emulator-marker.d.mts +224 -0
  41. package/dist/lib/esm/emulator-marker.mjs +134 -0
  42. package/dist/lib/esm/emulator-reaper.d.mts +225 -0
  43. package/dist/lib/esm/emulator-reaper.mjs +211 -0
  44. package/dist/lib/esm/emulator-reclaim.d.mts +179 -0
  45. package/dist/lib/esm/emulator-reclaim.mjs +291 -0
  46. package/dist/lib/esm/eval-cap-exceeded-error.d.mts +92 -0
  47. package/dist/lib/esm/eval-cap-exceeded-error.mjs +36 -0
  48. package/dist/lib/esm/global-setup-core.mjs +6 -4
  49. package/dist/lib/esm/index.d.mts +6 -0
  50. package/dist/lib/esm/index.mjs +19 -1
  51. package/dist/lib/esm/library.mjs +1 -1
  52. package/dist/lib/esm/poll-in-obsidian.d.mts +6 -2
  53. package/dist/lib/esm/poll-in-obsidian.mjs +1 -1
  54. package/dist/lib/esm/setup-lock.d.mts +53 -0
  55. package/dist/lib/esm/setup-lock.mjs +52 -7
  56. package/dist/lib/esm/transport-appium.d.mts +35 -0
  57. package/dist/lib/esm/transport-appium.mjs +72 -4
  58. package/dist/lib/esm/transport-desktop-cdp.mjs +22 -7
  59. package/dist/lib/esm/transport-factory.d.mts +15 -0
  60. package/dist/lib/esm/transport-factory.mjs +305 -184
  61. package/dist/lib/esm/transport-options.d.mts +27 -0
  62. package/dist/lib/esm/transport-options.mjs +1 -1
  63. package/package.json +4 -4
@@ -0,0 +1,179 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * Stops the emulators this harness started, and **verifies** each stop — the
5
+ * marker-driven half of emulator ownership (`emulator-marker.ts`, **L56**).
6
+ *
7
+ * It lives apart from `transport-factory.ts` so that a process with no business
8
+ * loading the whole transport stack can still stop an emulator correctly. That
9
+ * process is the emulator reaper (`emulator-reaper.ts`): it runs detached from
10
+ * any test runner, and in this repo's own suites it runs straight from source
11
+ * under Node's type stripping, where a module reading the build-time
12
+ * `OBSIDIAN_METADATA` global — which the factory's import graph reaches —
13
+ * cannot load at all. So nothing this module imports may reach it either.
14
+ */
15
+ import type { ChildProcess } from 'node:child_process';
16
+ import type { ProcessListEntry } from './emulator-backend.mjs';
17
+ /**
18
+ * Budget for one quick `adb` call — `adb devices`, a console command.
19
+ */
20
+ export declare const ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS = 5000;
21
+ export declare const ADB_DUMPSYS_MAX_BUFFER_IN_BYTES = 8388608;
22
+ export declare const HOST_PROCESS_QUERY_TIMEOUT_IN_MILLISECONDS = 30000;
23
+ /**
24
+ * A host command and its arguments.
25
+ */
26
+ export interface HostCommandQuery {
27
+ /**
28
+ The executable to run.
29
+ */
30
+ readonly command: string;
31
+ /**
32
+ The command's arguments.
33
+ */
34
+ readonly commandArguments: string[];
35
+ }
36
+ /**
37
+ * Parameters for {@link EmulatorReclaimer.reclaimLeftoverEmulators}.
38
+ */
39
+ export interface ReclaimLeftoverEmulatorsParams {
40
+ /**
41
+ An AVD whose leftover is left alone — the one a preflight is about to adopt instead.
42
+ */
43
+ readonly exceptAvdName?: string | undefined;
44
+ /**
45
+ * `'preflight'` spares an emulator another live harness process still owns.
46
+ * `'end-of-run'` does not: the caller holds the `android` lock with no run in
47
+ * flight — the run's own global teardown, or the emulator reaper after the
48
+ * run is gone — so that owner can only be one of the finished run's
49
+ * processes, which no longer has a turn to stop it.
50
+ */
51
+ readonly scope: 'end-of-run' | 'preflight';
52
+ }
53
+ /**
54
+ * Parameters for {@link EmulatorReclaimer.stopEmulator}.
55
+ */
56
+ export interface StopEmulatorParams {
57
+ /**
58
+ The AVD name, named in the warning so the leftover is identifiable, and the key of its marker.
59
+ */
60
+ readonly avdName: string;
61
+ /**
62
+ * The device the emulator is serving, shut down over its console and polled
63
+ * to decide whether it actually stopped. `undefined` when the emulator failed
64
+ * before any device appeared.
65
+ */
66
+ readonly deviceId?: string | undefined;
67
+ /**
68
+ * The emulator launcher process this run spawned. `undefined` for a leftover
69
+ * this run took over, whose launcher belonged to another process.
70
+ */
71
+ readonly emulatorProcess?: ChildProcess | undefined;
72
+ /**
73
+ The emulator PIDs this run owns, escalated to when the console and the launcher's tree kill leave one behind.
74
+ */
75
+ readonly ownedEmulatorPids: readonly number[];
76
+ }
77
+ /**
78
+ * Finds, stops and verifies the harness's emulators, reporting through the
79
+ * caller's log so each caller keeps its own prefix.
80
+ */
81
+ export declare class EmulatorReclaimer {
82
+ private readonly log;
83
+ /**
84
+ * Creates a reclaimer that reports under the caller's own log prefix.
85
+ *
86
+ * @param log - Receives every progress and verdict line.
87
+ */
88
+ constructor(log: (message: string) => void);
89
+ /**
90
+ * Runs `adb devices` and returns its raw stdout.
91
+ *
92
+ * The raw listing is what teardown needs: `getConnectedDeviceIds` keeps only
93
+ * the `device` state, and an emulator on its way out answers `offline` while
94
+ * still holding the AVD — see `adb-device-list.ts`.
95
+ *
96
+ * @returns The raw `adb devices` output.
97
+ * @throws If adb could not be run at all.
98
+ */
99
+ getDevicesOutput(): Promise<string>;
100
+ /**
101
+ * Lists every process on the host.
102
+ *
103
+ * A host always has processes, so a listing that parses to **zero** rows is a
104
+ * failed query however it exited, and comes back as `undefined` exactly like
105
+ * one that did not run — never as an empty list a caller could read as "no
106
+ * emulator is running".
107
+ *
108
+ * @returns The host's processes, or `undefined` when the listing failed.
109
+ */
110
+ queryHostProcesses(): Promise<ProcessListEntry[] | undefined>;
111
+ /**
112
+ * Stops the emulators this harness started that no live run is responsible
113
+ * for, and drops the markers that no longer describe a running emulator.
114
+ *
115
+ * Only ever acts on a **marker-verified** emulator: one whose marker names a
116
+ * PID that is still a live emulator process. An emulator without a marker —
117
+ * booted by hand, by CI, or by another tool — is never touched, which keeps
118
+ * the L46 line: never a `qemu*` sweep.
119
+ *
120
+ * Callers must hold the `android` setup lock (L7), which is what makes a
121
+ * leftover safe to stop: no other Android run can be mid-flight on it.
122
+ *
123
+ * @param params - Which AVD to leave alone, and whether this run is ending.
124
+ */
125
+ reclaimLeftoverEmulators(params: ReclaimLeftoverEmulatorsParams): Promise<void>;
126
+ /**
127
+ * Stops an emulator this harness owns, and **verifies** it stopped.
128
+ *
129
+ * The console shutdown goes first because it is the only path that releases
130
+ * the AVD's `multiinstance.lock`; a `taskkill` leaves the lock behind, and a
131
+ * stale lock is what makes the next run fail with `Running multiple emulators
132
+ * with the same AVD` — a FATAL the emulator writes to its own stdout, where
133
+ * nobody sees it.
134
+ *
135
+ * Works without a launcher handle too: a leftover this run took over has only
136
+ * its PIDs, which is all the escalation below ever needed. The marker goes
137
+ * only with a **verified** stop, so an emulator that outlived this attempt
138
+ * stays convictable by the next one.
139
+ *
140
+ * @param params - The emulator process, the device it serves and the PIDs this run owns.
141
+ */
142
+ stopEmulator(params: StopEmulatorParams): Promise<void>;
143
+ /**
144
+ * Decides whether the emulator this run started is really gone.
145
+ *
146
+ * Two independent proofs, cheapest first: none of the PIDs this run owns is
147
+ * alive, and the device no longer appears in `adb devices` **in any state** (a
148
+ * dying emulator answers `offline` while it still holds the AVD).
149
+ *
150
+ * @param params - The device and the PIDs this run owns.
151
+ * @returns `true` when nothing of this run's emulator is left.
152
+ */
153
+ private checkIsEmulatorGone;
154
+ /**
155
+ * Asks the emulator to shut itself down over its console.
156
+ *
157
+ * Preferred over killing the process outright because it is the path that
158
+ * releases the AVD's `multiinstance.lock`. Best-effort: a console that does
159
+ * not answer is reported and the caller falls through to the kill.
160
+ *
161
+ * @param deviceId - The emulator's device ID.
162
+ */
163
+ private killEmulatorConsole;
164
+ /**
165
+ * Acts on one emulator marker for {@link reclaimLeftoverEmulators}.
166
+ *
167
+ * @param marker - The marker to judge.
168
+ * @param liveEmulatorPids - The emulator processes currently running on the host.
169
+ * @param scope - Whether an emulator another live harness process owns is spared.
170
+ */
171
+ private reclaimLeftoverEmulator;
172
+ /**
173
+ * Polls until this run's emulator is gone, or the budget elapses.
174
+ *
175
+ * @param params - The device, the PIDs this run owns, and the budget.
176
+ * @returns `true` when the emulator disappeared within the budget.
177
+ */
178
+ private waitForEmulatorStopped;
179
+ }
@@ -0,0 +1,291 @@
1
+ import { execFile } from "node:child_process";
2
+ import process from "node:process";
3
+ import { checkIsDeviceListed } from "./adb-device-list.mjs";
4
+ import {
5
+ parsePosixProcessList,
6
+ parseWindowsTaskList,
7
+ selectEmulatorBackendPids
8
+ } from "./emulator-backend.mjs";
9
+ import {
10
+ clearEmulatorMarker,
11
+ clearEmulatorMarkerIfStopped,
12
+ listEmulatorMarkers,
13
+ resolveEmulatorMarkerVerdict,
14
+ selectLiveMarkedPids
15
+ } from "./emulator-marker.mjs";
16
+ import {
17
+ killProcessTree,
18
+ killProcessTreeByPid
19
+ } from "./kill-process-tree.mjs";
20
+ import { checkIsProcessAlive } from "./process-liveness.mjs";
21
+ import {
22
+ buildTeardownMessage,
23
+ resolveTeardownOutcome
24
+ } from "./teardown-verdict.mjs";
25
+ const ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS = 5e3;
26
+ const ADB_DUMPSYS_MAX_BUFFER_IN_BYTES = 8388608;
27
+ const HOST_PROCESS_QUERY_TIMEOUT_IN_MILLISECONDS = 3e4;
28
+ const EMULATOR_ESCALATED_STOP_TIMEOUT_IN_MILLISECONDS = 5e3;
29
+ const EMULATOR_STOP_POLL_INTERVAL_IN_MILLISECONDS = 500;
30
+ const EMULATOR_STOP_TIMEOUT_IN_MILLISECONDS = 2e4;
31
+ const MILLISECONDS_PER_SECOND = 1e3;
32
+ class EmulatorReclaimer {
33
+ log;
34
+ /**
35
+ * Creates a reclaimer that reports under the caller's own log prefix.
36
+ *
37
+ * @param log - Receives every progress and verdict line.
38
+ */
39
+ constructor(log) {
40
+ this.log = log;
41
+ }
42
+ /**
43
+ * Runs `adb devices` and returns its raw stdout.
44
+ *
45
+ * The raw listing is what teardown needs: `getConnectedDeviceIds` keeps only
46
+ * the `device` state, and an emulator on its way out answers `offline` while
47
+ * still holding the AVD — see `adb-device-list.ts`.
48
+ *
49
+ * @returns The raw `adb devices` output.
50
+ * @throws If adb could not be run at all.
51
+ */
52
+ getDevicesOutput() {
53
+ return new Promise((resolve, reject) => {
54
+ execFile("adb", ["devices"], { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS }, (error, stdout, stderr) => {
55
+ if (error) {
56
+ reject(new Error(`Failed to run 'adb devices': ${error.message}. Is ADB installed and in PATH?`));
57
+ return;
58
+ }
59
+ if (stderr) {
60
+ this.log(`ADB stderr: ${stderr.trim()}`);
61
+ }
62
+ resolve(stdout);
63
+ });
64
+ });
65
+ }
66
+ /**
67
+ * Lists every process on the host.
68
+ *
69
+ * A host always has processes, so a listing that parses to **zero** rows is a
70
+ * failed query however it exited, and comes back as `undefined` exactly like
71
+ * one that did not run — never as an empty list a caller could read as "no
72
+ * emulator is running".
73
+ *
74
+ * @returns The host's processes, or `undefined` when the listing failed.
75
+ */
76
+ async queryHostProcesses() {
77
+ const query = buildHostProcessQuery();
78
+ const output = await new Promise((resolve) => {
79
+ execFile(
80
+ query.command,
81
+ query.commandArguments,
82
+ { maxBuffer: ADB_DUMPSYS_MAX_BUFFER_IN_BYTES, timeout: HOST_PROCESS_QUERY_TIMEOUT_IN_MILLISECONDS },
83
+ (error, stdout) => {
84
+ if (error) {
85
+ this.log(`Warning: could not list host processes (\`${query.command}\`): ${error.message}`);
86
+ }
87
+ resolve(error ? "" : stdout);
88
+ }
89
+ );
90
+ });
91
+ const processes = parseHostProcessList(output);
92
+ if (processes.length === 0) {
93
+ this.log(`Warning: \`${query.command}\` listed no processes.`);
94
+ return void 0;
95
+ }
96
+ return processes;
97
+ }
98
+ /**
99
+ * Stops the emulators this harness started that no live run is responsible
100
+ * for, and drops the markers that no longer describe a running emulator.
101
+ *
102
+ * Only ever acts on a **marker-verified** emulator: one whose marker names a
103
+ * PID that is still a live emulator process. An emulator without a marker —
104
+ * booted by hand, by CI, or by another tool — is never touched, which keeps
105
+ * the L46 line: never a `qemu*` sweep.
106
+ *
107
+ * Callers must hold the `android` setup lock (L7), which is what makes a
108
+ * leftover safe to stop: no other Android run can be mid-flight on it.
109
+ *
110
+ * @param params - Which AVD to leave alone, and whether this run is ending.
111
+ */
112
+ async reclaimLeftoverEmulators(params) {
113
+ const markers = listEmulatorMarkers().filter((marker) => marker.avdName !== params.exceptAvdName);
114
+ if (markers.length === 0) {
115
+ return;
116
+ }
117
+ const processes = await this.queryHostProcesses();
118
+ if (processes === void 0) {
119
+ this.log(`Cannot judge ${String(markers.length)} emulator marker(s) without a host process listing; leaving them for a later run.`);
120
+ return;
121
+ }
122
+ const liveEmulatorPids = selectEmulatorBackendPids({ knownPids: [], processes });
123
+ for (const marker of markers) {
124
+ await this.reclaimLeftoverEmulator(marker, liveEmulatorPids, params.scope);
125
+ }
126
+ }
127
+ /**
128
+ * Stops an emulator this harness owns, and **verifies** it stopped.
129
+ *
130
+ * The console shutdown goes first because it is the only path that releases
131
+ * the AVD's `multiinstance.lock`; a `taskkill` leaves the lock behind, and a
132
+ * stale lock is what makes the next run fail with `Running multiple emulators
133
+ * with the same AVD` — a FATAL the emulator writes to its own stdout, where
134
+ * nobody sees it.
135
+ *
136
+ * Works without a launcher handle too: a leftover this run took over has only
137
+ * its PIDs, which is all the escalation below ever needed. The marker goes
138
+ * only with a **verified** stop, so an emulator that outlived this attempt
139
+ * stays convictable by the next one.
140
+ *
141
+ * @param params - The emulator process, the device it serves and the PIDs this run owns.
142
+ */
143
+ async stopEmulator(params) {
144
+ if (params.deviceId !== void 0) {
145
+ await this.killEmulatorConsole(params.deviceId);
146
+ }
147
+ if (params.emulatorProcess) {
148
+ killProcessTree(params.emulatorProcess);
149
+ }
150
+ const waitParams = {
151
+ deviceId: params.deviceId,
152
+ ownedEmulatorPids: params.ownedEmulatorPids,
153
+ timeoutInMilliseconds: EMULATOR_STOP_TIMEOUT_IN_MILLISECONDS
154
+ };
155
+ let hasEscalated = false;
156
+ let isStopped = await this.waitForEmulatorStopped(waitParams);
157
+ if (!isStopped) {
158
+ const survivingPids = params.ownedEmulatorPids.filter((pid) => checkIsProcessAlive(pid));
159
+ if (survivingPids.length > 0) {
160
+ hasEscalated = true;
161
+ this.log(
162
+ `Auto-started emulator outlived its console shutdown and the launcher's process tree; escalating to the emulator PID(s) this run owns: [${survivingPids.join(", ")}].`
163
+ );
164
+ for (const pid of survivingPids) {
165
+ killProcessTreeByPid(pid);
166
+ }
167
+ isStopped = await this.waitForEmulatorStopped({ ...waitParams, timeoutInMilliseconds: EMULATOR_ESCALATED_STOP_TIMEOUT_IN_MILLISECONDS });
168
+ }
169
+ }
170
+ this.log(buildTeardownMessage({
171
+ evidence: params.deviceId === void 0 ? `AVD "${params.avdName}"` : `AVD "${params.avdName}" on device ${params.deviceId}`,
172
+ outcome: resolveTeardownOutcome({ hasEscalated, isStopped }),
173
+ subject: "Auto-started emulator",
174
+ timeoutInMilliseconds: EMULATOR_STOP_TIMEOUT_IN_MILLISECONDS
175
+ }));
176
+ if (isStopped) {
177
+ clearEmulatorMarkerIfStopped(params.avdName);
178
+ }
179
+ }
180
+ /**
181
+ * Decides whether the emulator this run started is really gone.
182
+ *
183
+ * Two independent proofs, cheapest first: none of the PIDs this run owns is
184
+ * alive, and the device no longer appears in `adb devices` **in any state** (a
185
+ * dying emulator answers `offline` while it still holds the AVD).
186
+ *
187
+ * @param params - The device and the PIDs this run owns.
188
+ * @returns `true` when nothing of this run's emulator is left.
189
+ */
190
+ async checkIsEmulatorGone(params) {
191
+ if (params.ownedEmulatorPids.some((pid) => checkIsProcessAlive(pid))) {
192
+ return false;
193
+ }
194
+ if (params.deviceId === void 0) {
195
+ return true;
196
+ }
197
+ try {
198
+ return !checkIsDeviceListed({ deviceId: params.deviceId, devicesOutput: await this.getDevicesOutput() });
199
+ } catch (error) {
200
+ this.log(`Could not re-check connected devices while stopping the emulator: ${error instanceof Error ? error.message : String(error)}`);
201
+ return true;
202
+ }
203
+ }
204
+ /**
205
+ * Asks the emulator to shut itself down over its console.
206
+ *
207
+ * Preferred over killing the process outright because it is the path that
208
+ * releases the AVD's `multiinstance.lock`. Best-effort: a console that does
209
+ * not answer is reported and the caller falls through to the kill.
210
+ *
211
+ * @param deviceId - The emulator's device ID.
212
+ */
213
+ killEmulatorConsole(deviceId) {
214
+ return new Promise((resolve) => {
215
+ execFile("adb", ["-s", deviceId, "emu", "kill"], { timeout: ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS }, (error, stdout) => {
216
+ this.log(
217
+ error ? `Emulator console shutdown for ${deviceId} did not answer: ${error.message}` : `Emulator console shutdown for ${deviceId}: ${stdout.trim()}`
218
+ );
219
+ resolve();
220
+ });
221
+ });
222
+ }
223
+ /**
224
+ * Acts on one emulator marker for {@link reclaimLeftoverEmulators}.
225
+ *
226
+ * @param marker - The marker to judge.
227
+ * @param liveEmulatorPids - The emulator processes currently running on the host.
228
+ * @param scope - Whether an emulator another live harness process owns is spared.
229
+ */
230
+ async reclaimLeftoverEmulator(marker, liveEmulatorPids, scope) {
231
+ const verdict = resolveEmulatorMarkerVerdict({
232
+ currentPid: process.pid,
233
+ isOwnerAlive: checkIsProcessAlive(marker.ownerPid),
234
+ liveEmulatorPids,
235
+ marker
236
+ });
237
+ if (verdict === "stale-marker") {
238
+ this.log(`Dropping the emulator marker for AVD "${marker.avdName}": none of its PIDs [${marker.ownedEmulatorPids.join(", ")}] is a running emulator any more.`);
239
+ clearEmulatorMarker(marker.avdName);
240
+ return;
241
+ }
242
+ const evidence = describeMarkedEmulator(marker);
243
+ if (verdict === "in-use-by-live-run" && scope === "preflight") {
244
+ this.log(`Leaving the emulator for ${evidence} alone: the harness process that owns it (PID ${String(marker.ownerPid)}) is still running.`);
245
+ return;
246
+ }
247
+ const ageInSeconds = Math.round((Date.now() - marker.startedAtInMilliseconds) / MILLISECONDS_PER_SECOND);
248
+ this.log(
249
+ `Stopping the emulator for ${evidence}: this harness started it ${String(ageInSeconds)}s ago, and no live run is left to stop it.`
250
+ );
251
+ await this.stopEmulator({
252
+ avdName: marker.avdName,
253
+ deviceId: marker.deviceId,
254
+ ownedEmulatorPids: selectLiveMarkedPids(marker, liveEmulatorPids)
255
+ });
256
+ }
257
+ /**
258
+ * Polls until this run's emulator is gone, or the budget elapses.
259
+ *
260
+ * @param params - The device, the PIDs this run owns, and the budget.
261
+ * @returns `true` when the emulator disappeared within the budget.
262
+ */
263
+ async waitForEmulatorStopped(params) {
264
+ const deadline = Date.now() + params.timeoutInMilliseconds;
265
+ while (Date.now() < deadline) {
266
+ if (await this.checkIsEmulatorGone(params)) {
267
+ return true;
268
+ }
269
+ await new Promise((resolve) => {
270
+ setTimeout(resolve, EMULATOR_STOP_POLL_INTERVAL_IN_MILLISECONDS);
271
+ });
272
+ }
273
+ return false;
274
+ }
275
+ }
276
+ function buildHostProcessQuery() {
277
+ return process.platform === "win32" ? { command: "tasklist", commandArguments: ["/FO", "CSV", "/NH"] } : { command: "ps", commandArguments: ["-eo", "pid=,comm="] };
278
+ }
279
+ function describeMarkedEmulator(marker) {
280
+ 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}`;
281
+ }
282
+ function parseHostProcessList(output) {
283
+ return process.platform === "win32" ? parseWindowsTaskList(output) : parsePosixProcessList(output);
284
+ }
285
+ export {
286
+ ADB_DEVICE_CHECK_TIMEOUT_IN_MILLISECONDS,
287
+ ADB_DUMPSYS_MAX_BUFFER_IN_BYTES,
288
+ EmulatorReclaimer,
289
+ HOST_PROCESS_QUERY_TIMEOUT_IN_MILLISECONDS
290
+ };
291
+ //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vc3JjL2VtdWxhdG9yLXJlY2xhaW0udHMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qKlxuICogQGZpbGVcbiAqXG4gKiBTdG9wcyB0aGUgZW11bGF0b3JzIHRoaXMgaGFybmVzcyBzdGFydGVkLCBhbmQgKip2ZXJpZmllcyoqIGVhY2ggc3RvcCBcdTIwMTQgdGhlXG4gKiBtYXJrZXItZHJpdmVuIGhhbGYgb2YgZW11bGF0b3Igb3duZXJzaGlwIChgZW11bGF0b3ItbWFya2VyLnRzYCwgKipMNTYqKikuXG4gKlxuICogSXQgbGl2ZXMgYXBhcnQgZnJvbSBgdHJhbnNwb3J0LWZhY3RvcnkudHNgIHNvIHRoYXQgYSBwcm9jZXNzIHdpdGggbm8gYnVzaW5lc3NcbiAqIGxvYWRpbmcgdGhlIHdob2xlIHRyYW5zcG9ydCBzdGFjayBjYW4gc3RpbGwgc3RvcCBhbiBlbXVsYXRvciBjb3JyZWN0bHkuIFRoYXRcbiAqIHByb2Nlc3MgaXMgdGhlIGVtdWxhdG9yIHJlYXBlciAoYGVtdWxhdG9yLXJlYXBlci50c2ApOiBpdCBydW5zIGRldGFjaGVkIGZyb21cbiAqIGFueSB0ZXN0IHJ1bm5lciwgYW5kIGluIHRoaXMgcmVwbydzIG93biBzdWl0ZXMgaXQgcnVucyBzdHJhaWdodCBmcm9tIHNvdXJjZVxuICogdW5kZXIgTm9kZSdzIHR5cGUgc3RyaXBwaW5nLCB3aGVyZSBhIG1vZHVsZSByZWFkaW5nIHRoZSBidWlsZC10aW1lXG4gKiBgT0JTSURJQU5fTUVUQURBVEFgIGdsb2JhbCBcdTIwMTQgd2hpY2ggdGhlIGZhY3RvcnkncyBpbXBvcnQgZ3JhcGggcmVhY2hlcyBcdTIwMTRcbiAqIGNhbm5vdCBsb2FkIGF0IGFsbC4gU28gbm90aGluZyB0aGlzIG1vZHVsZSBpbXBvcnRzIG1heSByZWFjaCBpdCBlaXRoZXIuXG4gKi9cblxuLyogdjggaWdub3JlIHN0YXJ0IC0tIEludGVncmF0aW9uLXRpbWUgZW11bGF0b3IgbWFuYWdlbWVudCBjb3ZlcmVkIGJ5IHRoZSBBbmRyb2lkIGludGVncmF0aW9uIHN1aXRlLCBub3QgdW5pdCB0ZXN0cy4gKi9cblxuaW1wb3J0IHR5cGUgeyBDaGlsZFByb2Nlc3MgfSBmcm9tICdub2RlOmNoaWxkX3Byb2Nlc3MnO1xuXG5pbXBvcnQgeyBleGVjRmlsZSB9IGZyb20gJ25vZGU6Y2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgcHJvY2VzcyBmcm9tICdub2RlOnByb2Nlc3MnO1xuXG5pbXBvcnQgdHlwZSB7IFByb2Nlc3NMaXN0RW50cnkgfSBmcm9tICcuL2VtdWxhdG9yLWJhY2tlbmQubWpzJztcbmltcG9ydCB0eXBlIHsgRW11bGF0b3JNYXJrZXIgfSBmcm9tICcuL2VtdWxhdG9yLW1hcmtlci5tanMnO1xuXG5pbXBvcnQgeyBjaGVja0lzRGV2aWNlTGlzdGVkIH0gZnJvbSAnLi9hZGItZGV2aWNlLWxpc3QubWpzJztcbmltcG9ydCB7XG4gIHBhcnNlUG9zaXhQcm9jZXNzTGlzdCxcbiAgcGFyc2VXaW5kb3dzVGFza0xpc3QsXG4gIHNlbGVjdEVtdWxhdG9yQmFja2VuZFBpZHNcbn0gZnJvbSAnLi9lbXVsYXRvci1iYWNrZW5kLm1qcyc7XG5pbXBvcnQge1xuICBjbGVhckVtdWxhdG9yTWFya2VyLFxuICBjbGVhckVtdWxhdG9yTWFya2VySWZTdG9wcGVkLFxuICBsaXN0RW11bGF0b3JNYXJrZXJzLFxuICByZXNvbHZlRW11bGF0b3JNYXJrZXJWZXJkaWN0LFxuICBzZWxlY3RMaXZlTWFya2VkUGlkc1xufSBmcm9tICcuL2VtdWxhdG9yLW1hcmtlci5tanMnO1xuaW1wb3J0IHtcbiAga2lsbFByb2Nlc3NUcmVlLFxuICBraWxsUHJvY2Vzc1RyZWVCeVBpZFxufSBmcm9tICcuL2tpbGwtcHJvY2Vzcy10cmVlLm1qcyc7XG5pbXBvcnQgeyBjaGVja0lzUHJvY2Vzc0FsaXZlIH0gZnJvbSAnLi9wcm9jZXNzLWxpdmVuZXNzLm1qcyc7XG5pbXBvcnQge1xuICBidWlsZFRlYXJkb3duTWVzc2FnZSxcbiAgcmVzb2x2ZVRlYXJkb3duT3V0Y29tZVxufSBmcm9tICcuL3RlYXJkb3duLXZlcmRpY3QubWpzJztcblxuLyoqXG4gKiBCdWRnZXQgZm9yIG9uZSBxdWljayBgYWRiYCBjYWxsIFx1MjAxNCBgYWRiIGRldmljZXNgLCBhIGNvbnNvbGUgY29tbWFuZC5cbiAqL1xuZXhwb3J0IGNvbnN0IEFEQl9ERVZJQ0VfQ0hFQ0tfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSA1MDAwO1xuXG4vKlxuICogYGR1bXBzeXMgY29ubmVjdGl2aXR5YCBpcyBmYXIgbGFyZ2VyIHRoYW4gdGhlIG90aGVyIHByb2Jlcycgb3V0cHV0LCBhbmQgTm9kZSdzXG4gKiBkZWZhdWx0IDEgTWlCIGBtYXhCdWZmZXJgIHdvdWxkIGZhaWwgdGhlIGNhbGwgcmF0aGVyIHRoYW4gdHJ1bmNhdGUgaXQgXHUyMDE0IHdoaWNoXG4gKiB3b3VsZCByZWFkIGFzIFwibm90IHJlYWR5XCIgZm9yZXZlciBhbmQgYnVybiB0aGUgd2hvbGUgbmV0d29yayBidWRnZXQuIEEgaG9zdFxuICogcHJvY2VzcyBsaXN0aW5nIHNoYXJlcyB0aGUgc2FtZSBjZWlsaW5nLlxuICovXG5leHBvcnQgY29uc3QgQURCX0RVTVBTWVNfTUFYX0JVRkZFUl9JTl9CWVRFUyA9IDhfMzg4XzYwODtcblxuLypcbiAqIDMwcywgbm90IHRoZSAxMHMgYSBgdGFza2xpc3RgIGNvc3RzIG9uIGFuIGlkbGUgaG9zdDogdGhpcyBxdWVyeSBydW5zIGluIHRoZVxuICogc2FtZSBwb3N0LWJvb3QgY29udGVudGlvbiB3aW5kb3cgdGhhdCBpbmZsYXRlcyBldmVyeSBgYWRiYCByb3VuZC10cmlwIDI1LTUweFxuICogKEw0NSksIGFuZCB0aGUgZmlyc3QgZW5kLXRvLWVuZCBydW4gb3ZlcnJhbiBhIDEwcyBidWRnZXQgdGhlcmUgXHUyMDE0IHNpbGVudGx5XG4gKiBkaXNhcm1pbmcgdGhlIHRlYXJkb3duIGVzY2FsYXRpb24uIFNpemVkIGxpa2UgYEFEQl9EVU1QU1lTX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTYCxcbiAqIGZvciB0aGUgc2FtZSByZWFzb24uXG4gKi9cbmV4cG9ydCBjb25zdCBIT1NUX1BST0NFU1NfUVVFUllfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgPSAzMF8wMDA7XG5cbmNvbnN0IEVNVUxBVE9SX0VTQ0FMQVRFRF9TVE9QX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gNTAwMDtcbmNvbnN0IEVNVUxBVE9SX1NUT1BfUE9MTF9JTlRFUlZBTF9JTl9NSUxMSVNFQ09ORFMgPSA1MDA7XG5jb25zdCBFTVVMQVRPUl9TVE9QX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTID0gMjBfMDAwO1xuY29uc3QgTUlMTElTRUNPTkRTX1BFUl9TRUNPTkQgPSAxMDAwO1xuXG4vKipcbiAqIEEgaG9zdCBjb21tYW5kIGFuZCBpdHMgYXJndW1lbnRzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEhvc3RDb21tYW5kUXVlcnkge1xuICAvKipcbiAgVGhlIGV4ZWN1dGFibGUgdG8gcnVuLlxuICAgKi9cbiAgcmVhZG9ubHkgY29tbWFuZDogc3RyaW5nO1xuXG4gIC8qKlxuICBUaGUgY29tbWFuZCdzIGFyZ3VtZW50cy5cbiAgICovXG4gIHJlYWRvbmx5IGNvbW1hbmRBcmd1bWVudHM6IHN0cmluZ1tdO1xufVxuXG4vKipcbiAqIFBhcmFtZXRlcnMgZm9yIHtAbGluayBFbXVsYXRvclJlY2xhaW1lci5yZWNsYWltTGVmdG92ZXJFbXVsYXRvcnN9LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFJlY2xhaW1MZWZ0b3ZlckVtdWxhdG9yc1BhcmFtcyB7XG4gIC8qKlxuICBBbiBBVkQgd2hvc2UgbGVmdG92ZXIgaXMgbGVmdCBhbG9uZSBcdTIwMTQgdGhlIG9uZSBhIHByZWZsaWdodCBpcyBhYm91dCB0byBhZG9wdCBpbnN0ZWFkLlxuICAgKi9cbiAgcmVhZG9ubHkgZXhjZXB0QXZkTmFtZT86IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgICogYCdwcmVmbGlnaHQnYCBzcGFyZXMgYW4gZW11bGF0b3IgYW5vdGhlciBsaXZlIGhhcm5lc3MgcHJvY2VzcyBzdGlsbCBvd25zLlxuICAgKiBgJ2VuZC1vZi1ydW4nYCBkb2VzIG5vdDogdGhlIGNhbGxlciBob2xkcyB0aGUgYGFuZHJvaWRgIGxvY2sgd2l0aCBubyBydW4gaW5cbiAgICogZmxpZ2h0IFx1MjAxNCB0aGUgcnVuJ3Mgb3duIGdsb2JhbCB0ZWFyZG93biwgb3IgdGhlIGVtdWxhdG9yIHJlYXBlciBhZnRlciB0aGVcbiAgICogcnVuIGlzIGdvbmUgXHUyMDE0IHNvIHRoYXQgb3duZXIgY2FuIG9ubHkgYmUgb25lIG9mIHRoZSBmaW5pc2hlZCBydW4nc1xuICAgKiBwcm9jZXNzZXMsIHdoaWNoIG5vIGxvbmdlciBoYXMgYSB0dXJuIHRvIHN0b3AgaXQuXG4gICAqL1xuICByZWFkb25seSBzY29wZTogJ2VuZC1vZi1ydW4nIHwgJ3ByZWZsaWdodCc7XG59XG5cbi8qKlxuICogUGFyYW1ldGVycyBmb3Ige0BsaW5rIEVtdWxhdG9yUmVjbGFpbWVyLnN0b3BFbXVsYXRvcn0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgU3RvcEVtdWxhdG9yUGFyYW1zIHtcbiAgLyoqXG4gIFRoZSBBVkQgbmFtZSwgbmFtZWQgaW4gdGhlIHdhcm5pbmcgc28gdGhlIGxlZnRvdmVyIGlzIGlkZW50aWZpYWJsZSwgYW5kIHRoZSBrZXkgb2YgaXRzIG1hcmtlci5cbiAgICovXG4gIHJlYWRvbmx5IGF2ZE5hbWU6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIGRldmljZSB0aGUgZW11bGF0b3IgaXMgc2VydmluZywgc2h1dCBkb3duIG92ZXIgaXRzIGNvbnNvbGUgYW5kIHBvbGxlZFxuICAgKiB0byBkZWNpZGUgd2hldGhlciBpdCBhY3R1YWxseSBzdG9wcGVkLiBgdW5kZWZpbmVkYCB3aGVuIHRoZSBlbXVsYXRvciBmYWlsZWRcbiAgICogYmVmb3JlIGFueSBkZXZpY2UgYXBwZWFyZWQuXG4gICAqL1xuICByZWFkb25seSBkZXZpY2VJZD86IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgICogVGhlIGVtdWxhdG9yIGxhdW5jaGVyIHByb2Nlc3MgdGhpcyBydW4gc3Bhd25lZC4gYHVuZGVmaW5lZGAgZm9yIGEgbGVmdG92ZXJcbiAgICogdGhpcyBydW4gdG9vayBvdmVyLCB3aG9zZSBsYXVuY2hlciBiZWxvbmdlZCB0byBhbm90aGVyIHByb2Nlc3MuXG4gICAqL1xuICByZWFkb25seSBlbXVsYXRvclByb2Nlc3M/OiBDaGlsZFByb2Nlc3MgfCB1bmRlZmluZWQ7XG5cbiAgLyoqXG4gIFRoZSBlbXVsYXRvciBQSURzIHRoaXMgcnVuIG93bnMsIGVzY2FsYXRlZCB0byB3aGVuIHRoZSBjb25zb2xlIGFuZCB0aGUgbGF1bmNoZXIncyB0cmVlIGtpbGwgbGVhdmUgb25lIGJlaGluZC5cbiAgICovXG4gIHJlYWRvbmx5IG93bmVkRW11bGF0b3JQaWRzOiByZWFkb25seSBudW1iZXJbXTtcbn1cblxuaW50ZXJmYWNlIENoZWNrSXNFbXVsYXRvckdvbmVQYXJhbXMge1xuICAvKipcbiAgVGhlIGRldmljZSB0aGUgZW11bGF0b3Igc2VydmVzLCBvciBgdW5kZWZpbmVkYCB3aGVuIG5vbmUgZXZlciBhcHBlYXJlZCBcdTIwMTQgdGhlIFBJRHMgYXJlIHRoZW4gdGhlIG9ubHkgcHJvb2YuXG4gICAqL1xuICByZWFkb25seSBkZXZpY2VJZD86IHN0cmluZyB8IHVuZGVmaW5lZDtcblxuICAvKipcbiAgVGhlIGVtdWxhdG9yIFBJRHMgdGhpcyBydW4gb3ducy5cbiAgICovXG4gIHJlYWRvbmx5IG93bmVkRW11bGF0b3JQaWRzOiByZWFkb25seSBudW1iZXJbXTtcbn1cblxuaW50ZXJmYWNlIFdhaXRGb3JFbXVsYXRvclN0b3BwZWRQYXJhbXMge1xuICAvKipcbiAgVGhlIGRldmljZSB0aGUgZW11bGF0b3Igc2VydmVzLCBvciBgdW5kZWZpbmVkYCB3aGVuIG5vbmUgZXZlciBhcHBlYXJlZC5cbiAgICovXG4gIHJlYWRvbmx5IGRldmljZUlkPzogc3RyaW5nIHwgdW5kZWZpbmVkO1xuXG4gIC8qKlxuICBUaGUgZW11bGF0b3IgUElEcyB0aGlzIHJ1biBvd25zLlxuICAgKi9cbiAgcmVhZG9ubHkgb3duZWRFbXVsYXRvclBpZHM6IHJlYWRvbmx5IG51bWJlcltdO1xuXG4gIC8qKlxuICBIb3cgbG9uZyB0byB3YWl0IGZvciB0aGUgZW11bGF0b3IgdG8gZGlzYXBwZWFyLCBpbiBtaWxsaXNlY29uZHMuXG4gICAqL1xuICByZWFkb25seSB0aW1lb3V0SW5NaWxsaXNlY29uZHM6IG51bWJlcjtcbn1cblxuLyoqXG4gKiBGaW5kcywgc3RvcHMgYW5kIHZlcmlmaWVzIHRoZSBoYXJuZXNzJ3MgZW11bGF0b3JzLCByZXBvcnRpbmcgdGhyb3VnaCB0aGVcbiAqIGNhbGxlcidzIGxvZyBzbyBlYWNoIGNhbGxlciBrZWVwcyBpdHMgb3duIHByZWZpeC5cbiAqL1xuZXhwb3J0IGNsYXNzIEVtdWxhdG9yUmVjbGFpbWVyIHtcbiAgcHJpdmF0ZSByZWFkb25seSBsb2c6IChtZXNzYWdlOiBzdHJpbmcpID0+IHZvaWQ7XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSByZWNsYWltZXIgdGhhdCByZXBvcnRzIHVuZGVyIHRoZSBjYWxsZXIncyBvd24gbG9nIHByZWZpeC5cbiAgICpcbiAgICogQHBhcmFtIGxvZyAtIFJlY2VpdmVzIGV2ZXJ5IHByb2dyZXNzIGFuZCB2ZXJkaWN0IGxpbmUuXG4gICAqL1xuICBwdWJsaWMgY29uc3RydWN0b3IobG9nOiAobWVzc2FnZTogc3RyaW5nKSA9PiB2b2lkKSB7XG4gICAgdGhpcy5sb2cgPSBsb2c7XG4gIH1cblxuICAvKipcbiAgICogUnVucyBgYWRiIGRldmljZXNgIGFuZCByZXR1cm5zIGl0cyByYXcgc3Rkb3V0LlxuICAgKlxuICAgKiBUaGUgcmF3IGxpc3RpbmcgaXMgd2hhdCB0ZWFyZG93biBuZWVkczogYGdldENvbm5lY3RlZERldmljZUlkc2Aga2VlcHMgb25seVxuICAgKiB0aGUgYGRldmljZWAgc3RhdGUsIGFuZCBhbiBlbXVsYXRvciBvbiBpdHMgd2F5IG91dCBhbnN3ZXJzIGBvZmZsaW5lYCB3aGlsZVxuICAgKiBzdGlsbCBob2xkaW5nIHRoZSBBVkQgXHUyMDE0IHNlZSBgYWRiLWRldmljZS1saXN0LnRzYC5cbiAgICpcbiAgICogQHJldHVybnMgVGhlIHJhdyBgYWRiIGRldmljZXNgIG91dHB1dC5cbiAgICogQHRocm93cyBJZiBhZGIgY291bGQgbm90IGJlIHJ1biBhdCBhbGwuXG4gICAqL1xuICBwdWJsaWMgZ2V0RGV2aWNlc091dHB1dCgpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZTxzdHJpbmc+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgIGV4ZWNGaWxlKCdhZGInLCBbJ2RldmljZXMnXSwgeyB0aW1lb3V0OiBBREJfREVWSUNFX0NIRUNLX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTIH0sIChlcnJvciwgc3Rkb3V0LCBzdGRlcnIpID0+IHtcbiAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgRmFpbGVkIHRvIHJ1biAnYWRiIGRldmljZXMnOiAke2Vycm9yLm1lc3NhZ2V9LiBJcyBBREIgaW5zdGFsbGVkIGFuZCBpbiBQQVRIP2ApKTtcbiAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHN0ZGVycikge1xuICAgICAgICAgIHRoaXMubG9nKGBBREIgc3RkZXJyOiAke3N0ZGVyci50cmltKCl9YCk7XG4gICAgICAgIH1cbiAgICAgICAgcmVzb2x2ZShzdGRvdXQpO1xuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogTGlzdHMgZXZlcnkgcHJvY2VzcyBvbiB0aGUgaG9zdC5cbiAgICpcbiAgICogQSBob3N0IGFsd2F5cyBoYXMgcHJvY2Vzc2VzLCBzbyBhIGxpc3RpbmcgdGhhdCBwYXJzZXMgdG8gKip6ZXJvKiogcm93cyBpcyBhXG4gICAqIGZhaWxlZCBxdWVyeSBob3dldmVyIGl0IGV4aXRlZCwgYW5kIGNvbWVzIGJhY2sgYXMgYHVuZGVmaW5lZGAgZXhhY3RseSBsaWtlXG4gICAqIG9uZSB0aGF0IGRpZCBub3QgcnVuIFx1MjAxNCBuZXZlciBhcyBhbiBlbXB0eSBsaXN0IGEgY2FsbGVyIGNvdWxkIHJlYWQgYXMgXCJub1xuICAgKiBlbXVsYXRvciBpcyBydW5uaW5nXCIuXG4gICAqXG4gICAqIEByZXR1cm5zIFRoZSBob3N0J3MgcHJvY2Vzc2VzLCBvciBgdW5kZWZpbmVkYCB3aGVuIHRoZSBsaXN0aW5nIGZhaWxlZC5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBxdWVyeUhvc3RQcm9jZXNzZXMoKTogUHJvbWlzZTxQcm9jZXNzTGlzdEVudHJ5W10gfCB1bmRlZmluZWQ+IHtcbiAgICBjb25zdCBxdWVyeSA9IGJ1aWxkSG9zdFByb2Nlc3NRdWVyeSgpO1xuICAgIGNvbnN0IG91dHB1dCA9IGF3YWl0IG5ldyBQcm9taXNlPHN0cmluZz4oKHJlc29sdmUpID0+IHtcbiAgICAgIGV4ZWNGaWxlKFxuICAgICAgICBxdWVyeS5jb21tYW5kLFxuICAgICAgICBxdWVyeS5jb21tYW5kQXJndW1lbnRzLFxuICAgICAgICB7IG1heEJ1ZmZlcjogQURCX0RVTVBTWVNfTUFYX0JVRkZFUl9JTl9CWVRFUywgdGltZW91dDogSE9TVF9QUk9DRVNTX1FVRVJZX1RJTUVPVVRfSU5fTUlMTElTRUNPTkRTIH0sXG4gICAgICAgIChlcnJvciwgc3Rkb3V0KSA9PiB7XG4gICAgICAgICAgaWYgKGVycm9yKSB7XG4gICAgICAgICAgICB0aGlzLmxvZyhgV2FybmluZzogY291bGQgbm90IGxpc3QgaG9zdCBwcm9jZXNzZXMgKFxcYCR7cXVlcnkuY29tbWFuZH1cXGApOiAke2Vycm9yLm1lc3NhZ2V9YCk7XG4gICAgICAgICAgfVxuICAgICAgICAgIHJlc29sdmUoZXJyb3IgPyAnJyA6IHN0ZG91dCk7XG4gICAgICAgIH1cbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBjb25zdCBwcm9jZXNzZXMgPSBwYXJzZUhvc3RQcm9jZXNzTGlzdChvdXRwdXQpO1xuICAgIGlmIChwcm9jZXNzZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aGlzLmxvZyhgV2FybmluZzogXFxgJHtxdWVyeS5jb21tYW5kfVxcYCBsaXN0ZWQgbm8gcHJvY2Vzc2VzLmApO1xuICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICByZXR1cm4gcHJvY2Vzc2VzO1xuICB9XG5cbiAgLyoqXG4gICAqIFN0b3BzIHRoZSBlbXVsYXRvcnMgdGhpcyBoYXJuZXNzIHN0YXJ0ZWQgdGhhdCBubyBsaXZlIHJ1biBpcyByZXNwb25zaWJsZVxuICAgKiBmb3IsIGFuZCBkcm9wcyB0aGUgbWFya2VycyB0aGF0IG5vIGxvbmdlciBkZXNjcmliZSBhIHJ1bm5pbmcgZW11bGF0b3IuXG4gICAqXG4gICAqIE9ubHkgZXZlciBhY3RzIG9uIGEgKiptYXJrZXItdmVyaWZpZWQqKiBlbXVsYXRvcjogb25lIHdob3NlIG1hcmtlciBuYW1lcyBhXG4gICAqIFBJRCB0aGF0IGlzIHN0aWxsIGEgbGl2ZSBlbXVsYXRvciBwcm9jZXNzLiBBbiBlbXVsYXRvciB3aXRob3V0IGEgbWFya2VyIFx1MjAxNFxuICAgKiBib290ZWQgYnkgaGFuZCwgYnkgQ0ksIG9yIGJ5IGFub3RoZXIgdG9vbCBcdTIwMTQgaXMgbmV2ZXIgdG91Y2hlZCwgd2hpY2gga2VlcHNcbiAgICogdGhlIEw0NiBsaW5lOiBuZXZlciBhIGBxZW11KmAgc3dlZXAuXG4gICAqXG4gICAqIENhbGxlcnMgbXVzdCBob2xkIHRoZSBgYW5kcm9pZGAgc2V0dXAgbG9jayAoTDcpLCB3aGljaCBpcyB3aGF0IG1ha2VzIGFcbiAgICogbGVmdG92ZXIgc2FmZSB0byBzdG9wOiBubyBvdGhlciBBbmRyb2lkIHJ1biBjYW4gYmUgbWlkLWZsaWdodCBvbiBpdC5cbiAgICpcbiAgICogQHBhcmFtIHBhcmFtcyAtIFdoaWNoIEFWRCB0byBsZWF2ZSBhbG9uZSwgYW5kIHdoZXRoZXIgdGhpcyBydW4gaXMgZW5kaW5nLlxuICAgKi9cbiAgcHVibGljIGFzeW5jIHJlY2xhaW1MZWZ0b3ZlckVtdWxhdG9ycyhwYXJhbXM6IFJlY2xhaW1MZWZ0b3ZlckVtdWxhdG9yc1BhcmFtcyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IG1hcmtlcnMgPSBsaXN0RW11bGF0b3JNYXJrZXJzKCkuZmlsdGVyKChtYXJrZXIpID0+IG1hcmtlci5hdmROYW1lICE9PSBwYXJhbXMuZXhjZXB0QXZkTmFtZSk7XG4gICAgaWYgKG1hcmtlcnMubGVuZ3RoID09PSAwKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgcHJvY2Vzc2VzID0gYXdhaXQgdGhpcy5xdWVyeUhvc3RQcm9jZXNzZXMoKTtcbiAgICBpZiAocHJvY2Vzc2VzID09PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIFdpdGhvdXQgYSBsaXN0aW5nIGV2ZXJ5IG1hcmtlciB3b3VsZCByZWFkIGFzIHN0YWxlLCBhbmQgZGVsZXRpbmcgdGhlbSB3b3VsZCBsZWF2ZSB0aGUgbGVmdG92ZXJzIGltcG9zc2libGUgdG8gY29udmljdC5cbiAgICAgIHRoaXMubG9nKGBDYW5ub3QganVkZ2UgJHtTdHJpbmcobWFya2Vycy5sZW5ndGgpfSBlbXVsYXRvciBtYXJrZXIocykgd2l0aG91dCBhIGhvc3QgcHJvY2VzcyBsaXN0aW5nOyBsZWF2aW5nIHRoZW0gZm9yIGEgbGF0ZXIgcnVuLmApO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGNvbnN0IGxpdmVFbXVsYXRvclBpZHMgPSBzZWxlY3RFbXVsYXRvckJhY2tlbmRQaWRzKHsga25vd25QaWRzOiBbXSwgcHJvY2Vzc2VzIH0pO1xuICAgIGZvciAoY29uc3QgbWFya2VyIG9mIG1hcmtlcnMpIHtcbiAgICAgIGF3YWl0IHRoaXMucmVjbGFpbUxlZnRvdmVyRW11bGF0b3IobWFya2VyLCBsaXZlRW11bGF0b3JQaWRzLCBwYXJhbXMuc2NvcGUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wcyBhbiBlbXVsYXRvciB0aGlzIGhhcm5lc3Mgb3ducywgYW5kICoqdmVyaWZpZXMqKiBpdCBzdG9wcGVkLlxuICAgKlxuICAgKiBUaGUgY29uc29sZSBzaHV0ZG93biBnb2VzIGZpcnN0IGJlY2F1c2UgaXQgaXMgdGhlIG9ubHkgcGF0aCB0aGF0IHJlbGVhc2VzXG4gICAqIHRoZSBBVkQncyBgbXVsdGlpbnN0YW5jZS5sb2NrYDsgYSBgdGFza2tpbGxgIGxlYXZlcyB0aGUgbG9jayBiZWhpbmQsIGFuZCBhXG4gICAqIHN0YWxlIGxvY2sgaXMgd2hhdCBtYWtlcyB0aGUgbmV4dCBydW4gZmFpbCB3aXRoIGBSdW5uaW5nIG11bHRpcGxlIGVtdWxhdG9yc1xuICAgKiB3aXRoIHRoZSBzYW1lIEFWRGAgXHUyMDE0IGEgRkFUQUwgdGhlIGVtdWxhdG9yIHdyaXRlcyB0byBpdHMgb3duIHN0ZG91dCwgd2hlcmVcbiAgICogbm9ib2R5IHNlZXMgaXQuXG4gICAqXG4gICAqIFdvcmtzIHdpdGhvdXQgYSBsYXVuY2hlciBoYW5kbGUgdG9vOiBhIGxlZnRvdmVyIHRoaXMgcnVuIHRvb2sgb3ZlciBoYXMgb25seVxuICAgKiBpdHMgUElEcywgd2hpY2ggaXMgYWxsIHRoZSBlc2NhbGF0aW9uIGJlbG93IGV2ZXIgbmVlZGVkLiBUaGUgbWFya2VyIGdvZXNcbiAgICogb25seSB3aXRoIGEgKip2ZXJpZmllZCoqIHN0b3AsIHNvIGFuIGVtdWxhdG9yIHRoYXQgb3V0bGl2ZWQgdGhpcyBhdHRlbXB0XG4gICAqIHN0YXlzIGNvbnZpY3RhYmxlIGJ5IHRoZSBuZXh0IG9uZS5cbiAgICpcbiAgICogQHBhcmFtIHBhcmFtcyAtIFRoZSBlbXVsYXRvciBwcm9jZXNzLCB0aGUgZGV2aWNlIGl0IHNlcnZlcyBhbmQgdGhlIFBJRHMgdGhpcyBydW4gb3ducy5cbiAgICovXG4gIHB1YmxpYyBhc3luYyBzdG9wRW11bGF0b3IocGFyYW1zOiBTdG9wRW11bGF0b3JQYXJhbXMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAocGFyYW1zLmRldmljZUlkICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGF3YWl0IHRoaXMua2lsbEVtdWxhdG9yQ29uc29sZShwYXJhbXMuZGV2aWNlSWQpO1xuICAgIH1cbiAgICBpZiAocGFyYW1zLmVtdWxhdG9yUHJvY2Vzcykge1xuICAgICAga2lsbFByb2Nlc3NUcmVlKHBhcmFtcy5lbXVsYXRvclByb2Nlc3MpO1xuICAgIH1cblxuICAgIGNvbnN0IHdhaXRQYXJhbXM6IFdhaXRGb3JFbXVsYXRvclN0b3BwZWRQYXJhbXMgPSB7XG4gICAgICBkZXZpY2VJZDogcGFyYW1zLmRldmljZUlkLFxuICAgICAgb3duZWRFbXVsYXRvclBpZHM6IHBhcmFtcy5vd25lZEVtdWxhdG9yUGlkcyxcbiAgICAgIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogRU1VTEFUT1JfU1RPUF9USU1FT1VUX0lOX01JTExJU0VDT05EU1xuICAgIH07XG5cbiAgICBsZXQgaGFzRXNjYWxhdGVkID0gZmFsc2U7XG4gICAgbGV0IGlzU3RvcHBlZCA9IGF3YWl0IHRoaXMud2FpdEZvckVtdWxhdG9yU3RvcHBlZCh3YWl0UGFyYW1zKTtcblxuICAgIGlmICghaXNTdG9wcGVkKSB7XG4gICAgICBjb25zdCBzdXJ2aXZpbmdQaWRzID0gcGFyYW1zLm93bmVkRW11bGF0b3JQaWRzLmZpbHRlcigocGlkKSA9PiBjaGVja0lzUHJvY2Vzc0FsaXZlKHBpZCkpO1xuICAgICAgaWYgKHN1cnZpdmluZ1BpZHMubGVuZ3RoID4gMCkge1xuICAgICAgICBoYXNFc2NhbGF0ZWQgPSB0cnVlO1xuICAgICAgICB0aGlzLmxvZyhcbiAgICAgICAgICBgQXV0by1zdGFydGVkIGVtdWxhdG9yIG91dGxpdmVkIGl0cyBjb25zb2xlIHNodXRkb3duIGFuZCB0aGUgbGF1bmNoZXIncyBwcm9jZXNzIHRyZWU7IGVzY2FsYXRpbmcgdG8gdGhlIGVtdWxhdG9yIFBJRChzKSB0aGlzIHJ1biBvd25zOiBbJHtzdXJ2aXZpbmdQaWRzLmpvaW4oJywgJyl9XS5gXG4gICAgICAgICk7XG4gICAgICAgIGZvciAoY29uc3QgcGlkIG9mIHN1cnZpdmluZ1BpZHMpIHtcbiAgICAgICAgICBraWxsUHJvY2Vzc1RyZWVCeVBpZChwaWQpO1xuICAgICAgICB9XG4gICAgICAgIGlzU3RvcHBlZCA9IGF3YWl0IHRoaXMud2FpdEZvckVtdWxhdG9yU3RvcHBlZCh7IC4uLndhaXRQYXJhbXMsIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogRU1VTEFUT1JfRVNDQUxBVEVEX1NUT1BfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy5sb2coYnVpbGRUZWFyZG93bk1lc3NhZ2Uoe1xuICAgICAgZXZpZGVuY2U6IHBhcmFtcy5kZXZpY2VJZCA9PT0gdW5kZWZpbmVkID8gYEFWRCBcIiR7cGFyYW1zLmF2ZE5hbWV9XCJgIDogYEFWRCBcIiR7cGFyYW1zLmF2ZE5hbWV9XCIgb24gZGV2aWNlICR7cGFyYW1zLmRldmljZUlkfWAsXG4gICAgICBvdXRjb21lOiByZXNvbHZlVGVhcmRvd25PdXRjb21lKHsgaGFzRXNjYWxhdGVkLCBpc1N0b3BwZWQgfSksXG4gICAgICBzdWJqZWN0OiAnQXV0by1zdGFydGVkIGVtdWxhdG9yJyxcbiAgICAgIHRpbWVvdXRJbk1pbGxpc2Vjb25kczogRU1VTEFUT1JfU1RPUF9USU1FT1VUX0lOX01JTExJU0VDT05EU1xuICAgIH0pKTtcblxuICAgIGlmIChpc1N0b3BwZWQpIHtcbiAgICAgIGNsZWFyRW11bGF0b3JNYXJrZXJJZlN0b3BwZWQocGFyYW1zLmF2ZE5hbWUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBEZWNpZGVzIHdoZXRoZXIgdGhlIGVtdWxhdG9yIHRoaXMgcnVuIHN0YXJ0ZWQgaXMgcmVhbGx5IGdvbmUuXG4gICAqXG4gICAqIFR3byBpbmRlcGVuZGVudCBwcm9vZnMsIGNoZWFwZXN0IGZpcnN0OiBub25lIG9mIHRoZSBQSURzIHRoaXMgcnVuIG93bnMgaXNcbiAgICogYWxpdmUsIGFuZCB0aGUgZGV2aWNlIG5vIGxvbmdlciBhcHBlYXJzIGluIGBhZGIgZGV2aWNlc2AgKippbiBhbnkgc3RhdGUqKiAoYVxuICAgKiBkeWluZyBlbXVsYXRvciBhbnN3ZXJzIGBvZmZsaW5lYCB3aGlsZSBpdCBzdGlsbCBob2xkcyB0aGUgQVZEKS5cbiAgICpcbiAgICogQHBhcmFtIHBhcmFtcyAtIFRoZSBkZXZpY2UgYW5kIHRoZSBQSURzIHRoaXMgcnVuIG93bnMuXG4gICAqIEByZXR1cm5zIGB0cnVlYCB3aGVuIG5vdGhpbmcgb2YgdGhpcyBydW4ncyBlbXVsYXRvciBpcyBsZWZ0LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyBjaGVja0lzRW11bGF0b3JHb25lKHBhcmFtczogQ2hlY2tJc0VtdWxhdG9yR29uZVBhcmFtcyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGlmIChwYXJhbXMub3duZWRFbXVsYXRvclBpZHMuc29tZSgocGlkKSA9PiBjaGVja0lzUHJvY2Vzc0FsaXZlKHBpZCkpKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgaWYgKHBhcmFtcy5kZXZpY2VJZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgcmV0dXJuICFjaGVja0lzRGV2aWNlTGlzdGVkKHsgZGV2aWNlSWQ6IHBhcmFtcy5kZXZpY2VJZCwgZGV2aWNlc091dHB1dDogYXdhaXQgdGhpcy5nZXREZXZpY2VzT3V0cHV0KCkgfSk7XG4gICAgfSBjYXRjaCAoZXJyb3I6IHVua25vd24pIHtcbiAgICAgIC8qXG4gICAgICAgKiBBbiBhZGIgdGhhdCBjYW5ub3QgcnVuIGlzIG5vdCBldmlkZW5jZSBhYm91dCB0aGUgZW11bGF0b3IgXHUyMDE0IGFuZCB0aGUgUElEXG4gICAgICAgKiBwcm9iZSBhYm92ZSBoYXMgYWxyZWFkeSBzYWlkIHRoaXMgcnVuJ3MgcHJvY2Vzc2VzIGFyZSBnb25lLCB3aGljaCBpcyB0aGVcbiAgICAgICAqIHN0cm9uZ2VyIG9mIHRoZSB0d28gcHJvb2ZzLlxuICAgICAgICovXG4gICAgICB0aGlzLmxvZyhgQ291bGQgbm90IHJlLWNoZWNrIGNvbm5lY3RlZCBkZXZpY2VzIHdoaWxlIHN0b3BwaW5nIHRoZSBlbXVsYXRvcjogJHtlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvcil9YCk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQXNrcyB0aGUgZW11bGF0b3IgdG8gc2h1dCBpdHNlbGYgZG93biBvdmVyIGl0cyBjb25zb2xlLlxuICAgKlxuICAgKiBQcmVmZXJyZWQgb3ZlciBraWxsaW5nIHRoZSBwcm9jZXNzIG91dHJpZ2h0IGJlY2F1c2UgaXQgaXMgdGhlIHBhdGggdGhhdFxuICAgKiByZWxlYXNlcyB0aGUgQVZEJ3MgYG11bHRpaW5zdGFuY2UubG9ja2AuIEJlc3QtZWZmb3J0OiBhIGNvbnNvbGUgdGhhdCBkb2VzXG4gICAqIG5vdCBhbnN3ZXIgaXMgcmVwb3J0ZWQgYW5kIHRoZSBjYWxsZXIgZmFsbHMgdGhyb3VnaCB0byB0aGUga2lsbC5cbiAgICpcbiAgICogQHBhcmFtIGRldmljZUlkIC0gVGhlIGVtdWxhdG9yJ3MgZGV2aWNlIElELlxuICAgKi9cbiAgcHJpdmF0ZSBraWxsRW11bGF0b3JDb25zb2xlKGRldmljZUlkOiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHtcbiAgICAgIGV4ZWNGaWxlKCdhZGInLCBbJy1zJywgZGV2aWNlSWQsICdlbXUnLCAna2lsbCddLCB7IHRpbWVvdXQ6IEFEQl9ERVZJQ0VfQ0hFQ0tfVElNRU9VVF9JTl9NSUxMSVNFQ09ORFMgfSwgKGVycm9yLCBzdGRvdXQpID0+IHtcbiAgICAgICAgdGhpcy5sb2coXG4gICAgICAgICAgZXJyb3JcbiAgICAgICAgICAgID8gYEVtdWxhdG9yIGNvbnNvbGUgc2h1dGRvd24gZm9yICR7ZGV2aWNlSWR9IGRpZCBub3QgYW5zd2VyOiAke2Vycm9yLm1lc3NhZ2V9YFxuICAgICAgICAgICAgOiBgRW11bGF0b3IgY29uc29sZSBzaHV0ZG93biBmb3IgJHtkZXZpY2VJZH06ICR7c3Rkb3V0LnRyaW0oKX1gXG4gICAgICAgICk7XG4gICAgICAgIHJlc29sdmUoKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFjdHMgb24gb25lIGVtdWxhdG9yIG1hcmtlciBmb3Ige0BsaW5rIHJlY2xhaW1MZWZ0b3ZlckVtdWxhdG9yc30uXG4gICAqXG4gICAqIEBwYXJhbSBtYXJrZXIgLSBUaGUgbWFya2VyIHRvIGp1ZGdlLlxuICAgKiBAcGFyYW0gbGl2ZUVtdWxhdG9yUGlkcyAtIFRoZSBlbXVsYXRvciBwcm9jZXNzZXMgY3VycmVudGx5IHJ1bm5pbmcgb24gdGhlIGhvc3QuXG4gICAqIEBwYXJhbSBzY29wZSAtIFdoZXRoZXIgYW4gZW11bGF0b3IgYW5vdGhlciBsaXZlIGhhcm5lc3MgcHJvY2VzcyBvd25zIGlzIHNwYXJlZC5cbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVjbGFpbUxlZnRvdmVyRW11bGF0b3IobWFya2VyOiBFbXVsYXRvck1hcmtlciwgbGl2ZUVtdWxhdG9yUGlkczogcmVhZG9ubHkgbnVtYmVyW10sIHNjb3BlOiBSZWNsYWltTGVmdG92ZXJFbXVsYXRvcnNQYXJhbXNbJ3Njb3BlJ10pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB2ZXJkaWN0ID0gcmVzb2x2ZUVtdWxhdG9yTWFya2VyVmVyZGljdCh7XG4gICAgICBjdXJyZW50UGlkOiBwcm9jZXNzLnBpZCxcbiAgICAgIGlzT3duZXJBbGl2ZTogY2hlY2tJc1Byb2Nlc3NBbGl2ZShtYXJrZXIub3duZXJQaWQpLFxuICAgICAgbGl2ZUVtdWxhdG9yUGlkcyxcbiAgICAgIG1hcmtlclxuICAgIH0pO1xuXG4gICAgaWYgKHZlcmRpY3QgPT09ICdzdGFsZS1tYXJrZXInKSB7XG4gICAgICB0aGlzLmxvZyhgRHJvcHBpbmcgdGhlIGVtdWxhdG9yIG1hcmtlciBmb3IgQVZEIFwiJHttYXJrZXIuYXZkTmFtZX1cIjogbm9uZSBvZiBpdHMgUElEcyBbJHttYXJrZXIub3duZWRFbXVsYXRvclBpZHMuam9pbignLCAnKX1dIGlzIGEgcnVubmluZyBlbXVsYXRvciBhbnkgbW9yZS5gKTtcbiAgICAgIGNsZWFyRW11bGF0b3JNYXJrZXIobWFya2VyLmF2ZE5hbWUpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8qXG4gICAgICogQSBtYXJrZXIgd3JpdHRlbiBhdCBsYXVuY2ggaGFzIG5vIGRldmljZSB5ZXQ6IHRoZSBlbXVsYXRvciB3YXMgc3RpbGxcbiAgICAgKiBib290aW5nIHdoZW4gd2hhdGV2ZXIgcmVjb3JkZWQgaXQgc3RvcHBlZCBsb29raW5nLiBUaGUgc3RvcCBiZWxvdyByZWFkc1xuICAgICAqIHRoYXQgYWJzZW5jZSBjb3JyZWN0bHkgXHUyMDE0IG5vIGNvbnNvbGUgdG8gc2h1dCBkb3duLCB0aGUgb3duZWQgUElEcyBhcmUgdGhlXG4gICAgICogd2hvbGUgb2YgdGhlIGV2aWRlbmNlIFx1MjAxNCBzbyBpdCBpcyBkZXNjcmliZWQgcmF0aGVyIHRoYW4gcGFwZXJlZCBvdmVyLlxuICAgICAqL1xuICAgIGNvbnN0IGV2aWRlbmNlID0gZGVzY3JpYmVNYXJrZWRFbXVsYXRvcihtYXJrZXIpO1xuXG4gICAgaWYgKHZlcmRpY3QgPT09ICdpbi11c2UtYnktbGl2ZS1ydW4nICYmIHNjb3BlID09PSAncHJlZmxpZ2h0Jykge1xuICAgICAgdGhpcy5sb2coYExlYXZpbmcgdGhlIGVtdWxhdG9yIGZvciAke2V2aWRlbmNlfSBhbG9uZTogdGhlIGhhcm5lc3MgcHJvY2VzcyB0aGF0IG93bnMgaXQgKFBJRCAke1N0cmluZyhtYXJrZXIub3duZXJQaWQpfSkgaXMgc3RpbGwgcnVubmluZy5gKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG5cbiAgICBjb25zdCBhZ2VJblNlY29uZHMgPSBNYXRoLnJvdW5kKChEYXRlLm5vdygpIC0gbWFya2VyLnN0YXJ0ZWRBdEluTWlsbGlzZWNvbmRzKSAvIE1JTExJU0VDT05EU19QRVJfU0VDT05EKTtcbiAgICB0aGlzLmxvZyhcbiAgICAgIGBTdG9wcGluZyB0aGUgZW11bGF0b3IgZm9yICR7ZXZpZGVuY2V9OiB0aGlzIGhhcm5lc3Mgc3RhcnRlZCBpdCAke1N0cmluZyhhZ2VJblNlY29uZHMpfXMgYWdvLCBhbmQgbm8gbGl2ZSBydW4gaXMgbGVmdCB0byBzdG9wIGl0LmBcbiAgICApO1xuICAgIGF3YWl0IHRoaXMuc3RvcEVtdWxhdG9yKHtcbiAgICAgIGF2ZE5hbWU6IG1hcmtlci5hdmROYW1lLFxuICAgICAgZGV2aWNlSWQ6IG1hcmtlci5kZXZpY2VJZCxcbiAgICAgIG93bmVkRW11bGF0b3JQaWRzOiBzZWxlY3RMaXZlTWFya2VkUGlkcyhtYXJrZXIsIGxpdmVFbXVsYXRvclBpZHMpXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUG9sbHMgdW50aWwgdGhpcyBydW4ncyBlbXVsYXRvciBpcyBnb25lLCBvciB0aGUgYnVkZ2V0IGVsYXBzZXMuXG4gICAqXG4gICAqIEBwYXJhbSBwYXJhbXMgLSBUaGUgZGV2aWNlLCB0aGUgUElEcyB0aGlzIHJ1biBvd25zLCBhbmQgdGhlIGJ1ZGdldC5cbiAgICogQHJldHVybnMgYHRydWVgIHdoZW4gdGhlIGVtdWxhdG9yIGRpc2FwcGVhcmVkIHdpdGhpbiB0aGUgYnVkZ2V0LlxuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyB3YWl0Rm9yRW11bGF0b3JTdG9wcGVkKHBhcmFtczogV2FpdEZvckVtdWxhdG9yU3RvcHBlZFBhcmFtcyk6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIGNvbnN0IGRlYWRsaW5lID0gRGF0ZS5ub3coKSArIHBhcmFtcy50aW1lb3V0SW5NaWxsaXNlY29uZHM7XG5cbiAgICB3aGlsZSAoRGF0ZS5ub3coKSA8IGRlYWRsaW5lKSB7XG4gICAgICBpZiAoYXdhaXQgdGhpcy5jaGVja0lzRW11bGF0b3JHb25lKHBhcmFtcykpIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICB9XG5cbiAgICAgIGF3YWl0IG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICAgIHNldFRpbWVvdXQocmVzb2x2ZSwgRU1VTEFUT1JfU1RPUF9QT0xMX0lOVEVSVkFMX0lOX01JTExJU0VDT05EUyk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuLyoqXG4gKiBCdWlsZHMgdGhlIHBsYXRmb3JtJ3MgXCJsaXN0IGV2ZXJ5IHByb2Nlc3NcIiBxdWVyeS5cbiAqXG4gKiBAcmV0dXJucyBUaGUgY29tbWFuZCBhbmQgYXJndW1lbnRzIHRvIHJ1bi5cbiAqL1xuZnVuY3Rpb24gYnVpbGRIb3N0UHJvY2Vzc1F1ZXJ5KCk6IEhvc3RDb21tYW5kUXVlcnkge1xuICByZXR1cm4gcHJvY2Vzcy5wbGF0Zm9ybSA9PT0gJ3dpbjMyJ1xuICAgID8geyBjb21tYW5kOiAndGFza2xpc3QnLCBjb21tYW5kQXJndW1lbnRzOiBbJy9GTycsICdDU1YnLCAnL05IJ10gfVxuICAgIDogeyBjb21tYW5kOiAncHMnLCBjb21tYW5kQXJndW1lbnRzOiBbJy1lbycsICdwaWQ9LGNvbW09J10gfTtcbn1cblxuLyoqXG4gKiBOYW1lcyB0aGUgZW11bGF0b3IgYSBtYXJrZXIgZGVzY3JpYmVzLCBmb3IgdGhlIGxvZy5cbiAqXG4gKiBAcGFyYW0gbWFya2VyIC0gVGhlIG1hcmtlci5cbiAqIEByZXR1cm5zIFRoZSBBVkQgYW5kIGl0cyBkZXZpY2UsIG9yIHRoZSBBVkQgYWxvbmUgZm9yIGEgbGF1bmNoIHRoYXQgaGFkIG5vdCBwcm9kdWNlZCBhIGRldmljZSB5ZXQuXG4gKi9cbmZ1bmN0aW9uIGRlc2NyaWJlTWFya2VkRW11bGF0b3IobWFya2VyOiBFbXVsYXRvck1hcmtlcik6IHN0cmluZyB7XG4gIHJldHVybiBtYXJrZXIuZGV2aWNlSWQgPT09IHVuZGVmaW5lZFxuICAgID8gYEFWRCBcIiR7bWFya2VyLmF2ZE5hbWV9XCIgKG5vIGRldmljZTogaXQgd2FzIHN0aWxsIGJvb3Rpbmcgd2hlbiBpdCB3YXMgcmVjb3JkZWQpYFxuICAgIDogYEFWRCBcIiR7bWFya2VyLmF2ZE5hbWV9XCIgb24gZGV2aWNlICR7bWFya2VyLmRldmljZUlkfWA7XG59XG5cbi8qKlxuICogUGFyc2VzIGEgaG9zdCBwcm9jZXNzIGxpc3Rpbmcgd2l0aCB0aGUgcGxhdGZvcm0ncyBwYXJzZXIuXG4gKlxuICogQHBhcmFtIG91dHB1dCAtIFJhdyBzdGRvdXQgb2Yge0BsaW5rIGJ1aWxkSG9zdFByb2Nlc3NRdWVyeX0ncyBjb21tYW5kLlxuICogQHJldHVybnMgVGhlIGxpc3RlZCBwcm9jZXNzZXMuXG4gKi9cbmZ1bmN0aW9uIHBhcnNlSG9zdFByb2Nlc3NMaXN0KG91dHB1dDogc3RyaW5nKTogUHJvY2Vzc0xpc3RFbnRyeVtdIHtcbiAgcmV0dXJuIHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicgPyBwYXJzZVdpbmRvd3NUYXNrTGlzdChvdXRwdXQpIDogcGFyc2VQb3NpeFByb2Nlc3NMaXN0KG91dHB1dCk7XG59XG5cbi8qIHY4IGlnbm9yZSBzdG9wICovXG4iXSwKICAibWFwcGluZ3MiOiAiQUFtQkEsU0FBUyxnQkFBZ0I7QUFDekIsT0FBTyxhQUFhO0FBS3BCLFNBQVMsMkJBQTJCO0FBQ3BDO0FBQUEsRUFDRTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsT0FDSztBQUNQO0FBQUEsRUFDRTtBQUFBLEVBQ0E7QUFBQSxFQUNBO0FBQUEsRUFDQTtBQUFBLEVBQ0E7QUFBQSxPQUNLO0FBQ1A7QUFBQSxFQUNFO0FBQUEsRUFDQTtBQUFBLE9BQ0s7QUFDUCxTQUFTLDJCQUEyQjtBQUNwQztBQUFBLEVBQ0U7QUFBQSxFQUNBO0FBQUEsT0FDSztBQUtBLE1BQU0sMkNBQTJDO0FBUWpELE1BQU0sa0NBQWtDO0FBU3hDLE1BQU0sNkNBQTZDO0FBRTFELE1BQU0sa0RBQWtEO0FBQ3hELE1BQU0sOENBQThDO0FBQ3BELE1BQU0sd0NBQXdDO0FBQzlDLE1BQU0sMEJBQTBCO0FBaUd6QixN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
@@ -0,0 +1,92 @@
1
+ /**
2
+ * @file
3
+ *
4
+ * The one error both transports raise when a single `evalInObsidian` closure
5
+ * outruns the per-eval cap, and the predicate that recognizes the raw transport
6
+ * error it replaces.
7
+ *
8
+ * The cap itself is not the problem this module solves — the diagnosis is.
9
+ * Desktop surfaces the overrun as a generic
10
+ * `CDP command timed out ... : Runtime.evaluate`, which never names the test's
11
+ * own wait and so reads as a broken device or a wedged app — a plugin release was
12
+ * once held for two days by that reading, with a healthy emulator blamed for
13
+ * having no network.
14
+ *
15
+ * Android is worse, because it surfaces NOTHING. On a live emulator the closure
16
+ * was measured completing in the guest on schedule — timers armed at 30s and 40s fired within
17
+ * ~13ms of nominal on a visible, focused page — while its Execute Script response
18
+ * never reached the client, so the call hung until whichever outer budget gave up
19
+ * first. A hang names nothing at all, which is why the Android cap is enforced by
20
+ * the transport on the Node side rather than by the `timeouts.script` capability
21
+ * it also declares and that nothing acts on.
22
+ *
23
+ * Both transports therefore funnel into the message below, which says what the cap
24
+ * is, which transport enforced it, and what to do instead.
25
+ *
26
+ * A closure that needs to wait longer than the cap should not be waiting inside
27
+ * Obsidian at all. `pollInObsidian` is the sanctioned shape: short closures, and
28
+ * the waiting done from Node.
29
+ */
30
+ /**
31
+ * Parameters for {@link EvalCapExceededError}'s constructor.
32
+ */
33
+ export interface EvalCapExceededErrorConstructorParams {
34
+ /**
35
+ The per-eval cap the closure outran, in milliseconds.
36
+ */
37
+ readonly capInMilliseconds: number;
38
+ /**
39
+ The underlying transport error, kept as the `cause` so the raw diagnosis is not lost.
40
+ */
41
+ readonly cause: unknown;
42
+ /**
43
+ The name of the option that sets the cap on this transport, so the message can name the real knob.
44
+ */
45
+ readonly optionName: string;
46
+ /**
47
+ How to refer to the transport that enforced the cap, e.g. `'Android (Appium)'`.
48
+ */
49
+ readonly transportName: string;
50
+ }
51
+ /**
52
+ * Thrown when one `evalInObsidian` closure ran longer than the transport's
53
+ * per-eval cap.
54
+ *
55
+ * Carries the cap and the transport so a caller can `instanceof`-match, and the
56
+ * original transport error as `cause`.
57
+ */
58
+ export declare class EvalCapExceededError extends Error {
59
+ /**
60
+ The per-eval cap the closure outran, in milliseconds.
61
+ */
62
+ readonly capInMilliseconds: number;
63
+ /**
64
+ How the transport that enforced the cap was named in the message.
65
+ */
66
+ readonly transportName: string;
67
+ /**
68
+ * Creates the error from the cap, the transport, and the raw error it replaces.
69
+ *
70
+ * @param params - The cap, the transport, the option that sets the cap, and the underlying error.
71
+ */
72
+ constructor(params: EvalCapExceededErrorConstructorParams);
73
+ }
74
+ /**
75
+ * Whether an error is a WebDriver script timeout — the W3C error a driver would
76
+ * raise if it enforced `timeouts.script`.
77
+ *
78
+ * Matched on the message rather than on a type, because the error arrives as a
79
+ * generic `WebDriverError` whose only distinguishing mark is the W3C error code
80
+ * in its text.
81
+ *
82
+ * **This driver never raises it.** UiAutomator2 was measured accepting the
83
+ * capability, reporting it back, and enforcing nothing. The predicate is kept
84
+ * because it costs nothing, it is the right translation if a future driver does
85
+ * enforce the capability, and it keeps one error covering one condition on both
86
+ * transports — but the Android cap is enforced Node-side, and this is not what
87
+ * enforces it.
88
+ *
89
+ * @param error - The error thrown by the transport.
90
+ * @returns `true` when the error is a script timeout.
91
+ */
92
+ export declare function isScriptTimeoutError(error: unknown): boolean;